mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-26 07:21:22 +02:00
fix(mods): stop the Gen 2 require facade answering the engine
bootGame's require("src.core.Game") was resolving to Gen2Compat's Game
facade after a Gold session, so the next Gen 1 boot got an empty proxy
whose rawget(load) is nil (iOS: "src.core.Game missing load after
reload"; Android: Game:load called on a nil value).
Two independent causes, both needed:
* callerIsMod decided "not under src/" meant "a mod", and main.lua is
not under src/, so the engine's own require was gated as a mod's.
Root chunks main.lua and conf.lua are now matched exactly, which a
mod's own mods/<id>/main.lua cannot collide with.
* devShim.generation was only ever set, never cleared, so the
generation Gold declared outlived Gold's session. Loader.endSession
drops it from SessionLifecycle.endMountedSession, and the facade
gate now requires generation == 2 rather than ~= 1.
Only platforms that return to the launcher in-process (Android, and iOS
since #1808) keep the shim alive across sessions; everywhere else the
process restart cleared it.
This commit is contained in:
@@ -30,6 +30,8 @@ function SessionLifecycle.endMountedSession(version)
|
|||||||
local Runtime = require("src.mods.Runtime")
|
local Runtime = require("src.mods.Runtime")
|
||||||
if Runtime.reset then Runtime.reset() end
|
if Runtime.reset then Runtime.reset() end
|
||||||
if Assets.installLoader then Assets.installLoader(nil) end
|
if Assets.installLoader then Assets.installLoader(nil) end
|
||||||
|
local Loader = package.loaded["src.mods.Loader"]
|
||||||
|
if Loader and Loader.endSession then Loader.endSession() end
|
||||||
local okCompat, LegacyCompat = pcall(require, "src.mods.LegacyCompat")
|
local okCompat, LegacyCompat = pcall(require, "src.mods.LegacyCompat")
|
||||||
if okCompat and LegacyCompat.reset then LegacyCompat.reset() end
|
if okCompat and LegacyCompat.reset then LegacyCompat.reset() end
|
||||||
end
|
end
|
||||||
|
|||||||
+14
-2
@@ -147,12 +147,19 @@ local SUPPORTED_REQUIRES = {
|
|||||||
local ENGINE_PREFIX = (debug.getinfo(1, "S").source or "")
|
local ENGINE_PREFIX = (debug.getinfo(1, "S").source or "")
|
||||||
:gsub("^@", ""):gsub("mods[/\\]Loader%.lua$", "")
|
:gsub("^@", ""):gsub("mods[/\\]Loader%.lua$", "")
|
||||||
|
|
||||||
|
local ENGINE_CHUNKS = {
|
||||||
|
["main.lua"] = true,
|
||||||
|
["conf.lua"] = true,
|
||||||
|
}
|
||||||
|
|
||||||
local function callerIsMod(level)
|
local function callerIsMod(level)
|
||||||
if ENGINE_PREFIX == "" then return false end
|
if ENGINE_PREFIX == "" then return false end
|
||||||
local info = debug.getinfo(level, "S")
|
local info = debug.getinfo(level, "S")
|
||||||
local source = info and info.source
|
local source = info and info.source
|
||||||
if not source or source:sub(1, 1) ~= "@" then return false end
|
if not source or source:sub(1, 1) ~= "@" then return false end
|
||||||
return source:sub(2, 1 + #ENGINE_PREFIX) ~= ENGINE_PREFIX
|
local path = source:sub(2)
|
||||||
|
if ENGINE_CHUNKS[path] then return false end
|
||||||
|
return path:sub(1, #ENGINE_PREFIX) ~= ENGINE_PREFIX
|
||||||
end
|
end
|
||||||
|
|
||||||
local function scanRequire(name)
|
local function scanRequire(name)
|
||||||
@@ -207,6 +214,11 @@ local function engineRequire(name)
|
|||||||
return module
|
return module
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Loader.endSession()
|
||||||
|
devShim.generation = nil
|
||||||
|
devShim.errors = nil
|
||||||
|
end
|
||||||
|
|
||||||
function Loader:_installDevShim()
|
function Loader:_installDevShim()
|
||||||
for id, mod in pairs(self.mods) do
|
for id, mod in pairs(self.mods) do
|
||||||
devShim.permissions[id] = mod.manifest.permissionSet
|
devShim.permissions[id] = mod.manifest.permissionSet
|
||||||
@@ -234,7 +246,7 @@ function Loader:_installDevShim()
|
|||||||
-- The Gen 1 name a mod asked for, answered by the Gen 2 arm behind it.
|
-- The Gen 1 name a mod asked for, answered by the Gen 2 arm behind it.
|
||||||
-- Engine code keeps the real module: src/render/PaletteFX.lua:776
|
-- Engine code keeps the real module: src/render/PaletteFX.lua:776
|
||||||
-- requires src.core.Game on both generations and means it.
|
-- requires src.core.Game on both generations and means it.
|
||||||
if devShim.generation ~= 1 and Gen2Compat.serves(name)
|
if devShim.generation == 2 and Gen2Compat.serves(name)
|
||||||
and (owner or callerIsMod(3)) then
|
and (owner or callerIsMod(3)) then
|
||||||
local adapter = Gen2Compat.resolve(name, Runtime.currentMod)
|
local adapter = Gen2Compat.resolve(name, Runtime.currentMod)
|
||||||
if adapter then
|
if adapter then
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||||
|
|
||||||
|
love = love or require("tests.love_stub")
|
||||||
|
|
||||||
|
local S = require("tests.harness").suite("gen2 shim engine require")
|
||||||
|
local check, eq = S.check, S.eq
|
||||||
|
|
||||||
|
local Loader = require("src.mods.Loader")
|
||||||
|
local Gen2Compat = require("src.mods.Gen2Compat")
|
||||||
|
local GameVersion = require("src.core.GameVersion")
|
||||||
|
local SessionLifecycle = require("src.core.SessionLifecycle")
|
||||||
|
|
||||||
|
local NAME = "src.core.Game"
|
||||||
|
local REAL = { load = function() end }
|
||||||
|
|
||||||
|
local function memfs(files)
|
||||||
|
return {
|
||||||
|
files = files,
|
||||||
|
read = function(path) return files[path] end,
|
||||||
|
write = function(path, content) files[path] = content return true end,
|
||||||
|
remove = function(path) files[path] = nil return true end,
|
||||||
|
getInfo = function(path)
|
||||||
|
if files[path] then return { type = "file" } end
|
||||||
|
local prefix = path .. "/"
|
||||||
|
for key in pairs(files) do
|
||||||
|
if key:sub(1, #prefix) == prefix then return { type = "directory" } end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end,
|
||||||
|
load = function(path)
|
||||||
|
if not files[path] then return nil, "no file: " .. path end
|
||||||
|
return load(files[path], path)
|
||||||
|
end,
|
||||||
|
createDirectory = function() return true end,
|
||||||
|
getDirectoryItems = function(path)
|
||||||
|
local seen, items = {}, {}
|
||||||
|
local prefix = path .. "/"
|
||||||
|
for key in pairs(files) do
|
||||||
|
if key:sub(1, #prefix) == prefix then
|
||||||
|
local child = key:sub(#prefix + 1):match("^[^/]+")
|
||||||
|
if child and not seen[child] then
|
||||||
|
seen[child] = true
|
||||||
|
items[#items + 1] = child
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(items)
|
||||||
|
return items
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function requireFrom(source)
|
||||||
|
local chunk = assert(loadstring(
|
||||||
|
"local name = ... local mod = require(name) return mod", "@" .. source))
|
||||||
|
return chunk(NAME)
|
||||||
|
end
|
||||||
|
|
||||||
|
local savedVersion = GameVersion.get()
|
||||||
|
local savedModule = package.loaded[NAME]
|
||||||
|
package.loaded[NAME] = REAL
|
||||||
|
|
||||||
|
GameVersion.set("gold")
|
||||||
|
local files = {
|
||||||
|
["mods/facade/manifest.json"] =
|
||||||
|
[[{"id":"facade","name":"facade","version":"1.0.0","entry":"main.lua",]]
|
||||||
|
.. [["gen2compat":true}]],
|
||||||
|
["mods/facade/main.lua"] = "return function(mod) end",
|
||||||
|
}
|
||||||
|
local loader = Loader.new({ fs = memfs(files) })
|
||||||
|
loader:load({ pokemon = {} })
|
||||||
|
|
||||||
|
eq(loader.generation, 2, "the fixture loader is a Gen 2 one")
|
||||||
|
check(Gen2Compat.serves(NAME), "the facade serves " .. NAME)
|
||||||
|
|
||||||
|
local facade = Gen2Compat.resolve(NAME)
|
||||||
|
eq(requireFrom("mods/facade/main.lua"), facade,
|
||||||
|
"a mod's require still resolves to the Gen 2 facade")
|
||||||
|
eq(requireFrom("main.lua"), REAL,
|
||||||
|
"main.lua's require is the engine's own, never the facade")
|
||||||
|
eq(requireFrom("conf.lua"), REAL, "conf.lua's require is the engine's own")
|
||||||
|
|
||||||
|
SessionLifecycle.endMountedSession()
|
||||||
|
eq(requireFrom("mods/facade/main.lua"), REAL,
|
||||||
|
"after the session ends the facade stops answering for the next boot")
|
||||||
|
|
||||||
|
package.loaded[NAME] = savedModule
|
||||||
|
GameVersion.set(savedVersion)
|
||||||
|
|
||||||
|
S.finish()
|
||||||
Reference in New Issue
Block a user