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 CHANGED
@@ -1,4 +1,4 @@
1
- # silksong-coop-mod 0.4.0
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 `deba6cf`.
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.0",
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
+ }