mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-25 23:11:15 +02:00
Implement session teardown and resource management for in-process transitions
- Introduced `teardownMountedSession` to handle cleanup of mounted versions, generated data, and mod states during transitions between the editor and launcher. - Added `flushEditorPackageLoaded` to evict save-editor modules from `package.loaded` dynamically, ensuring a clean state for subsequent sessions. - Implemented `Game:reset` and `Game2:reset` methods to clear session-specific fields, allowing for a fresh start when returning to the launcher. - Enhanced `Renderer` and `TileRenderer` to release GPU resources immediately, preventing memory leaks during rapid transitions. - Updated `MagnetTrainRide` to support OAM priority overlays and manage background rendering with new shader functionality. This commit improves the stability and performance of the application during in-process transitions, particularly on Android. fixes Yellow color palette for title screen fixes #1662 #1643 #1536 and finishes fixing #1597
This commit is contained in:
@@ -77,4 +77,26 @@ do
|
||||
check(Runtime.modRequire == nil, "Runtime.reset clears modRequire")
|
||||
end
|
||||
|
||||
-- 4. returnToLauncher / closeEditor also clear Assets.loader (orphaned Loader)
|
||||
do
|
||||
local Assets = require("src.render.Assets")
|
||||
Assets.installLoader({
|
||||
overrideOrder = function() return { { id = "x", path = "mods/x" } } end,
|
||||
derivedPath = function() return nil end,
|
||||
})
|
||||
check(Assets.loader ~= nil, "Assets.loader installed for the session")
|
||||
Assets.installLoader(nil)
|
||||
check(Assets.loader == nil, "session teardown clears Assets.loader")
|
||||
end
|
||||
|
||||
-- 5. Gen1 Game:reset exists so main.lua need not guess field names
|
||||
do
|
||||
local Game = require("src.core.Game")
|
||||
check(type(Game.reset) == "function", "Game:reset is defined for in-process return")
|
||||
Game.mods = { stale = true }
|
||||
Game:reset()
|
||||
check(Game.mods == nil, "Game:reset clears session fields")
|
||||
check(type(Game.load) == "function", "Game:reset keeps methods")
|
||||
end
|
||||
|
||||
T.finish("android_exit_to_launcher_test")
|
||||
|
||||
@@ -57,6 +57,36 @@ eq(love.filesystem.read("assets/generated/fonts/font.png"), "font-bytes",
|
||||
eq(love.filesystem.read("data/generated/constants.lua"), "return {}",
|
||||
"post-mount probe reads red data at the un-prefixed path")
|
||||
|
||||
-- unmountVersion peels generated overlays (LIFO) then the version folder, so
|
||||
-- a later Play/Edit on another game cannot resolve this version's files.
|
||||
do
|
||||
local mounts = love.filesystem._mounts
|
||||
local archives = {}
|
||||
for _, m in ipairs(mounts) do archives[#archives + 1] = m.archive end
|
||||
local function indexOf(name)
|
||||
for i, a in ipairs(archives) do if a == name then return i end end
|
||||
end
|
||||
local dataIdx = indexOf("red/data/generated")
|
||||
local assetsIdx = indexOf("red/assets/generated")
|
||||
check(dataIdx and assetsIdx, "mountVersion recorded generated-tree overlays")
|
||||
check(assetsIdx > dataIdx,
|
||||
"assets/generated was mounted after data/generated (LIFO unmount peels it first)")
|
||||
end
|
||||
check(CacheFs.unmountVersion("red") == true, "unmountVersion(red) returns true")
|
||||
check(love.filesystem.read("assets/generated/fonts/font.png") == nil,
|
||||
"post-unmount probe no longer sees red assets at the un-prefixed path")
|
||||
check(love.filesystem.read("data/generated/constants.lua") == nil,
|
||||
"post-unmount probe no longer sees red data at the un-prefixed path")
|
||||
do
|
||||
local left = {}
|
||||
for _, m in ipairs(love.filesystem._mounts) do
|
||||
if tostring(m.archive):find("red", 1, true) then
|
||||
left[#left + 1] = m.archive
|
||||
end
|
||||
end
|
||||
eq(#left, 0, "no red/ mounts remain after unmountVersion")
|
||||
end
|
||||
|
||||
-- no legacy cache at all: migration is a no-op, not an error
|
||||
love.filesystem.remove("red/rom-cache.complete")
|
||||
love.filesystem.remove("red/data/generated/maps.lua")
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
-- In-process launcher session teardown: Game:reset, Renderer canvas release,
|
||||
-- Runtime/Assets/LegacyCompat cleanup, and editor package.loaded discovery flush.
|
||||
-- luajit tests/engine/launcher_session_teardown_test.lua
|
||||
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
love = love or require("tests.love_stub")
|
||||
|
||||
local T = require("tests.harness")
|
||||
local check, eq = T.check, T.eq
|
||||
|
||||
local Runtime = require("src.mods.Runtime")
|
||||
local Assets = require("src.render.Assets")
|
||||
local LegacyCompat = require("src.mods.LegacyCompat")
|
||||
local Game = require("src.core.Game")
|
||||
local Renderer = require("src.render.Renderer")
|
||||
local StateStack = require("src.core.StateStack")
|
||||
|
||||
-- ---- Game:reset drops instance state, keeps methods ----------------------
|
||||
do
|
||||
StateStack:init()
|
||||
StateStack:push({ name = "stale" })
|
||||
Game.mods = { id = "orphan" }
|
||||
Game.save = { money = 1 }
|
||||
Game.network = { live = true } -- future field: must not need a whitelist
|
||||
Game.stack = StateStack
|
||||
Game.renderer = Renderer
|
||||
Renderer.canvas = love.graphics.newCanvas(8, 8)
|
||||
|
||||
Game:reset()
|
||||
|
||||
check(type(Game.load) == "function", "Game:reset keeps methods")
|
||||
check(type(Game.reset) == "function", "Game:reset keeps itself")
|
||||
check(Game.mods == nil, "Game:reset clears mods")
|
||||
check(Game.save == nil, "Game:reset clears save")
|
||||
check(Game.network == nil, "Game:reset clears arbitrary future fields")
|
||||
check(Game.stack == nil, "Game:reset clears stack reference")
|
||||
check(Game.renderer == nil, "Game:reset clears renderer reference")
|
||||
check(StateStack:top() == nil, "Game:reset cleared the shared StateStack")
|
||||
end
|
||||
|
||||
-- ---- Renderer:init releases prior canvases before realloc ----------------
|
||||
do
|
||||
local first = love.graphics.newCanvas(16, 16)
|
||||
Renderer.canvas = first
|
||||
Renderer.battleHUDCanvas = love.graphics.newCanvas(16, 16)
|
||||
Renderer.worldCanvas = love.graphics.newCanvas(16, 16)
|
||||
Renderer.uprightCanvas = love.graphics.newCanvas(16, 16)
|
||||
Renderer:init()
|
||||
check(first.released == true,
|
||||
"Renderer:init Object:release()s the previous primary canvas")
|
||||
check(Renderer.canvas ~= nil and Renderer.canvas ~= first,
|
||||
"Renderer:init allocates a fresh primary canvas")
|
||||
check(Renderer.canvas.released ~= true,
|
||||
"the new primary canvas is not released")
|
||||
-- second init also releases the one just created
|
||||
local second = Renderer.canvas
|
||||
Renderer:init()
|
||||
check(second.released == true,
|
||||
"a second Renderer:init releases the canvas from the prior init")
|
||||
end
|
||||
|
||||
-- ---- Shared singleton teardown contract (closeEditor / returnToLauncher)
|
||||
do
|
||||
Runtime.install({ emit = function() end }, { call = function() end }, { "e" })
|
||||
Assets.installLoader({
|
||||
overrideOrder = function() return {} end,
|
||||
derivedPath = function() return nil end,
|
||||
})
|
||||
LegacyCompat.reports = { some_mod = { order = {} } }
|
||||
|
||||
-- Mirrors main.lua teardownMountedSession without mounting CacheFs.
|
||||
require("src.core.Data"):unloadGenerated()
|
||||
Runtime.reset()
|
||||
Assets.installLoader(nil)
|
||||
LegacyCompat.reset()
|
||||
|
||||
check(Runtime.errors == nil, "teardown clears Runtime.errors")
|
||||
check(Assets.loader == nil, "teardown clears Assets.loader")
|
||||
eq(next(LegacyCompat.reports), nil, "teardown clears LegacyCompat.reports")
|
||||
end
|
||||
|
||||
-- ---- Editor package.loaded discovery flush (no panel whitelist) ---------
|
||||
do
|
||||
love.filesystem.write("tools/save-editor/App.lua", "return {}")
|
||||
love.filesystem.write("tools/save-editor/panels/NewPanel.lua", "return {}")
|
||||
package.loaded["App"] = { stale = true }
|
||||
package.loaded["NewPanel"] = { stale = true }
|
||||
package.loaded["src.core.Data"] = package.loaded["src.core.Data"] -- keep
|
||||
|
||||
local function isEditorFlat(name)
|
||||
if name:find("[./]") then return false end
|
||||
return love.filesystem.getInfo("tools/save-editor/" .. name .. ".lua") ~= nil
|
||||
or love.filesystem.getInfo("tools/save-editor/panels/" .. name .. ".lua") ~= nil
|
||||
end
|
||||
for k in pairs(package.loaded) do
|
||||
if type(k) == "string"
|
||||
and (k:find("save%-editor", 1, false) or isEditorFlat(k)) then
|
||||
package.loaded[k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
check(package.loaded["App"] == nil, "discovery flush drops flat App")
|
||||
check(package.loaded["NewPanel"] == nil,
|
||||
"discovery flush drops a new panel without a hardcoded list")
|
||||
check(package.loaded["src.core.Data"] ~= nil,
|
||||
"discovery flush leaves engine modules alone")
|
||||
love.filesystem.remove("tools/save-editor/App.lua")
|
||||
love.filesystem.remove("tools/save-editor/panels/NewPanel.lua")
|
||||
end
|
||||
|
||||
T.finish("launcher_session_teardown_test")
|
||||
Reference in New Issue
Block a user