silksong-coop-mod 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/SSMP/SSMP.dll +0 -0
- package/UPDATE.bat +14 -0
- package/package.json +3 -1
- package/update.ps1 +249 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# silksong-coop-mod 0.4.
|
|
1
|
+
# silksong-coop-mod 0.4.2
|
|
2
2
|
|
|
3
3
|
The files a player needs to install a two-player co-op build of **SSMP**, packaged so they can be fetched from
|
|
4
4
|
a mirror that is reachable inside mainland China. This is not a Node library: nothing here is imported or run
|
|
@@ -14,4 +14,4 @@ The BepInEx loader is included, so the install needs no network access at all.
|
|
|
14
14
|
|
|
15
15
|
The multiplayer mod is the work of [Extremelyd1](https://github.com/Extremelyd1/SSMP). This is a modified copy
|
|
16
16
|
that adds a shared two-player save; please do not take questions about it to the original author. Distributed
|
|
17
|
-
under the GNU LGPL 2.1 - see `LICENSE`. Built from https://github.com/Candy-649/SSMP at `
|
|
17
|
+
under the GNU LGPL 2.1 - see `LICENSE`. Built from https://github.com/Candy-649/SSMP at `a5f6b1b`.
|
package/SSMP/SSMP.dll
CHANGED
|
Binary file
|
package/UPDATE.bat
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
rem Double-click this to pull the newest co-op build. It only replaces the plugin of an install that already
|
|
3
|
+
rem works; run INSTALL.bat first if the mod is not installed yet.
|
|
4
|
+
rem
|
|
5
|
+
rem -ExecutionPolicy Bypass applies to this one run only and changes no system setting. pwsh is preferred
|
|
6
|
+
rem because Windows PowerShell 5.1 mangles UTF-8 without a BOM.
|
|
7
|
+
where pwsh >nul 2>nul
|
|
8
|
+
if %errorlevel%==0 (
|
|
9
|
+
pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0update.ps1" %*
|
|
10
|
+
) else (
|
|
11
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0update.ps1" %*
|
|
12
|
+
)
|
|
13
|
+
echo.
|
|
14
|
+
pause
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silksong-coop-mod",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Offline installer payload for a two-player co-op build of the SSMP Silksong multiplayer mod. Not a Node library.",
|
|
5
5
|
"license": "LGPL-2.1-or-later",
|
|
6
6
|
"author": "Candy-649 (https://github.com/Candy-649)",
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
"INSTALL.bat",
|
|
20
20
|
"INSTALL.txt",
|
|
21
21
|
"install.ps1",
|
|
22
|
+
"UPDATE.bat",
|
|
23
|
+
"update.ps1",
|
|
22
24
|
"LICENSE",
|
|
23
25
|
"LICENSE-BouncyCastle.md",
|
|
24
26
|
"BepInEx_win_x64_5.4.23.4.zip",
|
package/update.ps1
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Updates an already-installed SSMP co-op plugin in place, by asking npm for the newest published build.
|
|
2
|
+
#
|
|
3
|
+
# Why this is separate from install.ps1: that script's whole point is that a normal install needs no network
|
|
4
|
+
# at all, and it says so at the top. Adding a download to it would quietly take that property away. This one
|
|
5
|
+
# is the opposite - it exists to go to the network - so it lives on its own and only ever touches the plugin
|
|
6
|
+
# DLLs of an install that already works.
|
|
7
|
+
#
|
|
8
|
+
# Why npm: the two players are on opposite sides of the Chinese firewall. npm's mainland mirror
|
|
9
|
+
# (registry.npmmirror.com) serves any published tarball anonymously, with no account and no filing on either
|
|
10
|
+
# side, and it rewrites dist.tarball to its own host - so whichever registry answers also tells us where to
|
|
11
|
+
# download from, and no URL is ever built by hand here.
|
|
12
|
+
#
|
|
13
|
+
# Console output is English for the same reason install.ps1's is: Windows PowerShell 5.1 reads a UTF-8 file
|
|
14
|
+
# without a BOM as ANSI and turns anything else into mojibake.
|
|
15
|
+
|
|
16
|
+
[CmdletBinding()]
|
|
17
|
+
param(
|
|
18
|
+
# Skip the search and use this game folder. Handy when the game lives somewhere unusual.
|
|
19
|
+
[string] $GameDir,
|
|
20
|
+
|
|
21
|
+
# Report what would happen and change nothing.
|
|
22
|
+
[switch] $WhatIfOnly
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
$ErrorActionPreference = 'Stop'
|
|
26
|
+
|
|
27
|
+
$GameExe = 'Hollow Knight Silksong.exe'
|
|
28
|
+
$GameDirName = 'Hollow Knight Silksong'
|
|
29
|
+
$PackageName = 'silksong-coop-mod'
|
|
30
|
+
|
|
31
|
+
# The mainland mirror first because that is the side this exists for; npm itself second, as the source of
|
|
32
|
+
# truth for anyone outside. Both were checked to return the same dist.shasum for the same version.
|
|
33
|
+
$Registries = @(
|
|
34
|
+
'https://registry.npmmirror.com',
|
|
35
|
+
'https://registry.npmjs.org'
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
function Write-Step($text) { Write-Host ""; Write-Host "==> $text" -ForegroundColor Cyan }
|
|
39
|
+
function Write-Ok($text) { Write-Host " $text" -ForegroundColor Green }
|
|
40
|
+
function Write-Warn2($text) { Write-Host " $text" -ForegroundColor Yellow }
|
|
41
|
+
|
|
42
|
+
# Deliberately a second copy of install.ps1's Find-Game rather than a shared file: install.ps1 already works
|
|
43
|
+
# and is what the other player depends on, so it is not worth editing to serve an optional extra. If the two
|
|
44
|
+
# ever drift the symptom is "the updater cannot find the game", which is loud and harmless.
|
|
45
|
+
function Find-Game {
|
|
46
|
+
$candidates = New-Object System.Collections.Generic.List[string]
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
$steamPath = (Get-ItemProperty 'HKCU:\Software\Valve\Steam' -ErrorAction Stop).SteamPath
|
|
50
|
+
if ($steamPath) {
|
|
51
|
+
$steamPath = $steamPath -replace '/', '\'
|
|
52
|
+
$candidates.Add((Join-Path $steamPath "steamapps\common\$GameDirName"))
|
|
53
|
+
|
|
54
|
+
$vdf = Join-Path $steamPath 'steamapps\libraryfolders.vdf'
|
|
55
|
+
if (Test-Path $vdf) {
|
|
56
|
+
foreach ($line in (Get-Content $vdf)) {
|
|
57
|
+
if ($line -match '"path"\s+"(.+?)"') {
|
|
58
|
+
$libPath = $matches[1] -replace '\\\\', '\'
|
|
59
|
+
$candidates.Add((Join-Path $libPath "steamapps\common\$GameDirName"))
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
# No Steam in the registry is not fatal; the guesses below still apply
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
foreach ($drive in (Get-PSDrive -PSProvider FileSystem).Name) {
|
|
69
|
+
$candidates.Add("${drive}:\STEAM\steamapps\common\$GameDirName")
|
|
70
|
+
$candidates.Add("${drive}:\SteamLibrary\steamapps\common\$GameDirName")
|
|
71
|
+
$candidates.Add("${drive}:\Steam\steamapps\common\$GameDirName")
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
foreach ($candidate in ($candidates | Select-Object -Unique)) {
|
|
75
|
+
if (Test-Path (Join-Path $candidate $GameExe)) { return $candidate }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return $null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# "0.3.1.0+<commit>" and "0.4.0.0" both reduce to the three parts npm publishes under.
|
|
82
|
+
function Get-ComparableVersion($raw) {
|
|
83
|
+
if ($raw -match '^([0-9]+\.[0-9]+\.[0-9]+)') { return [version] $matches[1] }
|
|
84
|
+
return $null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
Write-Host ""
|
|
89
|
+
Write-Host " Silksong co-op (SSMP) updater" -ForegroundColor White
|
|
90
|
+
Write-Host " -----------------------------"
|
|
91
|
+
|
|
92
|
+
# --- Where it is installed ---------------------------------------------------------------------------
|
|
93
|
+
Write-Step 'Looking for the install'
|
|
94
|
+
$game = if ($GameDir) { $GameDir.Trim('"', ' ') } else { Find-Game }
|
|
95
|
+
if (-not $game -or -not (Test-Path (Join-Path $game $GameExe))) {
|
|
96
|
+
throw ("Could not find $GameDirName. Pass the folder that contains ${GameExe}:`n" +
|
|
97
|
+
" .\update.ps1 -GameDir ""D:\path\to\$GameDirName""")
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
$pluginDir = Join-Path $game 'BepInEx\plugins\SSMP'
|
|
101
|
+
$installedDll = Join-Path $pluginDir 'SSMP.dll'
|
|
102
|
+
if (-not (Test-Path $installedDll)) {
|
|
103
|
+
throw ("The co-op plugin is not installed here: $installedDll`n" +
|
|
104
|
+
" This updates an existing install. Run INSTALL.bat first.")
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
$installedRaw = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($installedDll).ProductVersion
|
|
108
|
+
$installed = Get-ComparableVersion $installedRaw
|
|
109
|
+
if (-not $installed) { throw "Could not read a version from the installed DLL ('$installedRaw')." }
|
|
110
|
+
Write-Ok "Game: $game"
|
|
111
|
+
Write-Ok "Installed: $installed"
|
|
112
|
+
|
|
113
|
+
# --- What is published -------------------------------------------------------------------------------
|
|
114
|
+
Write-Step 'Asking what the newest build is'
|
|
115
|
+
$meta = $null
|
|
116
|
+
$usedRegistry = $null
|
|
117
|
+
foreach ($registry in $Registries) {
|
|
118
|
+
$url = "$registry/$PackageName/latest"
|
|
119
|
+
try {
|
|
120
|
+
$meta = Invoke-RestMethod -Uri $url -UseBasicParsing -TimeoutSec 30
|
|
121
|
+
$usedRegistry = $registry
|
|
122
|
+
break
|
|
123
|
+
} catch {
|
|
124
|
+
Write-Warn2 "$registry did not answer ($($_.Exception.Message))"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (-not $meta) {
|
|
128
|
+
throw ("No registry answered, so there is nothing to compare against. Your network may be blocking`n" +
|
|
129
|
+
" both of them. Nothing was changed.")
|
|
130
|
+
}
|
|
131
|
+
Write-Ok "Asked: $usedRegistry"
|
|
132
|
+
|
|
133
|
+
$latest = Get-ComparableVersion $meta.version
|
|
134
|
+
if (-not $latest) { throw "The registry returned a version this cannot read ('$($meta.version)')." }
|
|
135
|
+
# Taken from the answer rather than built, so a mirror that serves from its own host just works.
|
|
136
|
+
$tarballUrl = $meta.dist.tarball
|
|
137
|
+
$expectedSha1 = $meta.dist.shasum
|
|
138
|
+
if (-not $tarballUrl -or -not $expectedSha1) { throw 'The registry answer had no tarball or no checksum.' }
|
|
139
|
+
Write-Ok "Newest: $latest"
|
|
140
|
+
|
|
141
|
+
if ($installed -ge $latest) {
|
|
142
|
+
Write-Host ""
|
|
143
|
+
Write-Host " Already up to date. Nothing to do." -ForegroundColor Green
|
|
144
|
+
Write-Host ""
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if ($WhatIfOnly) {
|
|
149
|
+
Write-Host ""
|
|
150
|
+
Write-Host " Would update $installed -> $latest from $tarballUrl" -ForegroundColor Yellow
|
|
151
|
+
Write-Host " Nothing was changed (-WhatIfOnly)." -ForegroundColor Yellow
|
|
152
|
+
Write-Host ""
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# --- The game must not be running --------------------------------------------------------------------
|
|
157
|
+
# A loaded DLL cannot be overwritten on Windows. Catching it here gives a sentence someone can act on,
|
|
158
|
+
# instead of an access-denied part-way through replacing the plugin.
|
|
159
|
+
Write-Step 'Checking the game is closed'
|
|
160
|
+
$running = Get-Process -ErrorAction SilentlyContinue |
|
|
161
|
+
Where-Object { $_.Path -and $_.Path.StartsWith($game, [StringComparison]::OrdinalIgnoreCase) }
|
|
162
|
+
if ($running) {
|
|
163
|
+
throw ("The game is still running ($(($running | Select-Object -First 1).ProcessName)). Close it and" +
|
|
164
|
+
"`n run this again; Windows will not let a loaded plugin be replaced.")
|
|
165
|
+
}
|
|
166
|
+
Write-Ok 'Closed'
|
|
167
|
+
|
|
168
|
+
# --- Download ------------------------------------------------------------------------------------------
|
|
169
|
+
Write-Step 'Downloading'
|
|
170
|
+
$work = Join-Path ([System.IO.Path]::GetTempPath()) ("ssmp-update-" + [Guid]::NewGuid().ToString('N'))
|
|
171
|
+
New-Item -ItemType Directory -Path $work -Force | Out-Null
|
|
172
|
+
$tgz = Join-Path $work 'package.tgz'
|
|
173
|
+
|
|
174
|
+
$oldProgress = $ProgressPreference
|
|
175
|
+
# Not cosmetic: Invoke-WebRequest's progress bar makes a download several times slower.
|
|
176
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
177
|
+
try {
|
|
178
|
+
Invoke-WebRequest -Uri $tarballUrl -OutFile $tgz -UseBasicParsing -TimeoutSec 300
|
|
179
|
+
} catch {
|
|
180
|
+
throw "Could not download the update: $($_.Exception.Message)"
|
|
181
|
+
} finally {
|
|
182
|
+
$ProgressPreference = $oldProgress
|
|
183
|
+
}
|
|
184
|
+
Write-Ok "Got $([math]::Round((Get-Item $tgz).Length / 1MB, 2)) MB"
|
|
185
|
+
|
|
186
|
+
# --- Verify before anything is touched -----------------------------------------------------------------
|
|
187
|
+
# The realistic failure of a public mirror is not a forged file, it is a truncated one or an error page
|
|
188
|
+
# served with a 200. SHA1 is what the registry publishes, and it catches exactly that.
|
|
189
|
+
Write-Step 'Checking what was downloaded'
|
|
190
|
+
$actualSha1 = (Get-FileHash $tgz -Algorithm SHA1).Hash.ToLower()
|
|
191
|
+
if ($actualSha1 -ne $expectedSha1.ToLower()) {
|
|
192
|
+
throw ("The download does not match the checksum the registry published.`n" +
|
|
193
|
+
" expected $expectedSha1`n got $actualSha1`n" +
|
|
194
|
+
" Nothing was changed. Try again, or use a different network.")
|
|
195
|
+
}
|
|
196
|
+
Write-Ok 'Checksum matches'
|
|
197
|
+
|
|
198
|
+
# --- Unpack --------------------------------------------------------------------------------------------
|
|
199
|
+
# npm tarballs always wrap their contents in a folder called "package".
|
|
200
|
+
$extracted = Join-Path $work 'extracted'
|
|
201
|
+
New-Item -ItemType Directory -Path $extracted -Force | Out-Null
|
|
202
|
+
& tar -xzf $tgz -C $extracted
|
|
203
|
+
if ($LASTEXITCODE -ne 0) { throw 'Could not unpack the download (tar failed).' }
|
|
204
|
+
|
|
205
|
+
$newPluginDir = Join-Path $extracted 'package\SSMP'
|
|
206
|
+
$newDll = Join-Path $newPluginDir 'SSMP.dll'
|
|
207
|
+
if (-not (Test-Path $newDll)) { throw "The download did not contain SSMP.dll where expected ($newDll)." }
|
|
208
|
+
|
|
209
|
+
$newRaw = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($newDll).ProductVersion
|
|
210
|
+
$new = Get-ComparableVersion $newRaw
|
|
211
|
+
if ($new -ne $latest) {
|
|
212
|
+
throw ("The DLL inside the package says $new but the registry said $latest. Nothing was changed.")
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# --- Replace, keeping the old one ----------------------------------------------------------------------
|
|
216
|
+
Write-Step 'Replacing the plugin'
|
|
217
|
+
foreach ($dll in (Get-ChildItem $newPluginDir -Filter '*.dll')) {
|
|
218
|
+
$target = Join-Path $pluginDir $dll.Name
|
|
219
|
+
if (Test-Path $target) {
|
|
220
|
+
# Kept beside the plugin so going back is a rename, not another download. One per version, so
|
|
221
|
+
# repeated updates do not pile up copies of the same build.
|
|
222
|
+
$backup = "$target.$installed.bak"
|
|
223
|
+
Copy-Item -LiteralPath $target -Destination $backup -Force
|
|
224
|
+
}
|
|
225
|
+
Copy-Item -LiteralPath $dll.FullName -Destination $target -Force
|
|
226
|
+
Write-Ok "Updated $($dll.Name)"
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
# --- Verify the install, not the download ---------------------------------------------------------------
|
|
230
|
+
Write-Step 'Checking the result'
|
|
231
|
+
$nowRaw = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($installedDll).ProductVersion
|
|
232
|
+
$now = Get-ComparableVersion $nowRaw
|
|
233
|
+
if ($now -ne $latest) { throw "After copying, the installed DLL still says $now. Expected $latest." }
|
|
234
|
+
|
|
235
|
+
if (Test-Path $work) { Remove-Item $work -Recurse -Force -ErrorAction SilentlyContinue }
|
|
236
|
+
|
|
237
|
+
Write-Host ""
|
|
238
|
+
Write-Host " Updated $installed -> $latest." -ForegroundColor Green
|
|
239
|
+
Write-Host " The previous plugin is kept beside it as *.$installed.bak" -ForegroundColor Green
|
|
240
|
+
Write-Host " Both players need the same version to play together." -ForegroundColor Green
|
|
241
|
+
Write-Host ""
|
|
242
|
+
} catch {
|
|
243
|
+
Write-Host ""
|
|
244
|
+
Write-Host " FAILED: $($_.Exception.Message)" -ForegroundColor Red
|
|
245
|
+
Write-Host ""
|
|
246
|
+
Write-Host " Your install was not changed." -ForegroundColor Yellow
|
|
247
|
+
Write-Host ""
|
|
248
|
+
exit 1
|
|
249
|
+
}
|