mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-17 11:11:10 +02:00
CLOSES #455, CLOSES #487, CLOSES #501, CLOSES #540, CLOSES #585, CLOSES #591, CLOSES #593, CLOSES #595, CLOSES #597, CLOSES #599, CLOSES #600, CLOSES #606, CLOSES #607, CLOSES #610, CLOSES #613, CLOSES #616, CLOSES #620, CLOSES #626, CLOSES #632, CLOSES #633, CLOSES #647
This commit is contained in:
+74
-6
@@ -40,7 +40,8 @@ function Handshake.mods(game)
|
||||
if not ok or not status then return mods end
|
||||
for _, manifest in ipairs(status.loaded or {}) do
|
||||
mods[#mods + 1] = { id = manifest.id, version = manifest.version,
|
||||
affectsLink = manifest.affects_link ~= false }
|
||||
affectsLink = manifest.affects_link ~= false,
|
||||
language = manifest.language == true }
|
||||
end
|
||||
table.sort(mods, function(a, b) return tostring(a.id) < tostring(b.id) end)
|
||||
return mods
|
||||
@@ -67,15 +68,82 @@ function Handshake.linkModified(game)
|
||||
return false
|
||||
end
|
||||
|
||||
-- #501: the registries a declared translation may write, and nothing else.
|
||||
-- `text` is the ROM's dialogue, `strings` the engine's own authored text
|
||||
-- (src/mods/Schemas.lua R.text / R.strings) and `font` the glyphs a
|
||||
-- language with accents needs. None of the three is hashed into the
|
||||
-- fingerprint (src/link/Fingerprint.lua header), so two peers reading the
|
||||
-- same game in different languages stay in lockstep -- which is also what
|
||||
-- the cable did: TradeCenter_PrintPartyListNames (pokered
|
||||
-- engine/link/cable_club.asm) names the peer's party out of the local
|
||||
-- ROM's table, only the trainer name and the party bytes travel.
|
||||
-- text_pointers is deliberately out: its rows carry mart inventories and
|
||||
-- nurse/pc flags, which are gameplay, not language.
|
||||
local LANGUAGE_REGISTRIES = { text = true, strings = true, font = true }
|
||||
|
||||
Handshake.LANGUAGE_REGISTRIES = LANGUAGE_REGISTRIES
|
||||
|
||||
-- The manifest's `language = true` is the author's claim; this is the
|
||||
-- check. A mod counts as a translation only if every op it appended
|
||||
-- landed in a language registry, it subscribed no code (a hook or listener
|
||||
-- runs inside the battle the two peers are lockstepping) and it asked for
|
||||
-- no permission. Online play meets strangers, so nothing here may rest on
|
||||
-- the manifest alone. A patched client can still lie about its own mods --
|
||||
-- the fingerprint, not this, is what keeps the shared simulation honest;
|
||||
-- this gate is what keeps an honest install from being told to turn its
|
||||
-- language off.
|
||||
local function translationOnly(mod, id)
|
||||
local record = mod.mods and mod.mods[id]
|
||||
local manifest = record and record.manifest
|
||||
if manifest and #(manifest.permissions or {}) > 0 then return false end
|
||||
for name, registry in pairs(mod.content or {}) do
|
||||
if not LANGUAGE_REGISTRIES[name] then
|
||||
for _, list in pairs(registry.ops or {}) do
|
||||
for _, entry in ipairs(list) do
|
||||
if entry.owner == id then return false end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, chain in pairs((mod.hooks and mod.hooks.chains) or {}) do
|
||||
for _, entry in ipairs(chain) do
|
||||
if entry.owner == id then return false end
|
||||
end
|
||||
end
|
||||
for _, list in pairs((mod.events and mod.events.listeners) or {}) do
|
||||
for _, entry in ipairs(list) do
|
||||
if entry.owner == id then return false end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- the enabled mods that keep this install out of online play, in the
|
||||
-- id order Handshake.mods sorts: everything except verified translations.
|
||||
-- LinkState:offerVanillaRestart names these and switches off exactly these.
|
||||
function Handshake.onlineBlockers(game)
|
||||
local mod = loader(game)
|
||||
local blockers = {}
|
||||
for _, entry in ipairs(Handshake.mods(game)) do
|
||||
local allowed = entry.language and not entry.affectsLink
|
||||
and mod ~= nil and translationOnly(mod, entry.id)
|
||||
if not allowed then blockers[#blockers + 1] = entry end
|
||||
end
|
||||
return blockers
|
||||
end
|
||||
|
||||
-- online play (the relay-based online match / tournament flows in
|
||||
-- LinkState/Tournament) meets strangers, not a coordinating friend, so it
|
||||
-- skips the LAN path's per-peer compatibility negotiation entirely and
|
||||
-- just requires vanilla on both ends: no mod-added Pokemon, no surprises.
|
||||
-- Mods only ever get baked in at boot (Loader:load), so this is a gate on
|
||||
-- attempting to go online, not a live mod toggle -- the player disables
|
||||
-- mods via the mod manager and relaunches.
|
||||
-- just requires a vanilla simulation on both ends: no mod-added Pokemon, no
|
||||
-- surprises. #501 carves out translations, because a language is not a
|
||||
-- simulation: a mod that only rewrites text is invisible to the wire and
|
||||
-- may stay on, so an English player and a Spanish one can meet the way two
|
||||
-- regional carts always could. Mods only ever get baked in at boot
|
||||
-- (Loader:load), so this is a gate on attempting to go online, not a live
|
||||
-- mod toggle -- the player disables mods via the mod manager and relaunches.
|
||||
function Handshake.onlineAllowed(game)
|
||||
return #Handshake.mods(game) == 0
|
||||
return #Handshake.onlineBlockers(game) == 0
|
||||
end
|
||||
|
||||
-- mode is nil on the guest: it pairs and announces itself before the host
|
||||
|
||||
@@ -110,17 +110,19 @@ function LinkState:exitWith(message, reason)
|
||||
end
|
||||
end
|
||||
|
||||
-- Online play meets strangers, so it requires vanilla on both ends
|
||||
-- (Handshake.onlineAllowed). Mods merge into the shared Data registries at
|
||||
-- boot and there is no unmerge, so switching them off has to go through a
|
||||
-- relaunch -- but the player should not have to go find the mod manager and
|
||||
-- work out which mods count. This turns every enabled mod off, records
|
||||
-- them so the mod manager can put them back, and relaunches. The restart
|
||||
-- Online play meets strangers, so it requires a vanilla simulation on both
|
||||
-- ends (Handshake.onlineAllowed). Mods merge into the shared Data
|
||||
-- registries at boot and there is no unmerge, so switching them off has to
|
||||
-- go through a relaunch -- but the player should not have to go find the
|
||||
-- mod manager and work out which mods count. This turns the blocking mods
|
||||
-- off, records them so the mod manager can put them back, and relaunches.
|
||||
-- Verified translations are not blockers (#501), so a player keeps their
|
||||
-- language across the restart and only the gameplay mods go. The restart
|
||||
-- is confirmed rather than silent: it drops unsaved progress.
|
||||
function LinkState:offerVanillaRestart()
|
||||
local game = self.game
|
||||
local loader = game.mods
|
||||
local mods = Handshake.mods(game)
|
||||
local mods = Handshake.onlineBlockers(game)
|
||||
local names = {}
|
||||
for i, mod in ipairs(mods) do
|
||||
if i > 2 then break end
|
||||
|
||||
Reference in New Issue
Block a user