diff --git a/game/engines/default/engine/Actor.lua b/game/engines/default/engine/Actor.lua index a8313e7dae252786572d85fa1d5c08cd0bd9c18e..8b1c8651b293d5b8d7bdfe8aa7a909fa0c297337 100644 --- a/game/engines/default/engine/Actor.lua +++ b/game/engines/default/engine/Actor.lua @@ -90,6 +90,13 @@ end function _M:defineDisplayCallback() if not self._mo then return end + -- Cunning trick here! + -- the callback we give to mo:displayCallback is a function that references self + -- but self contains mo so it would create a cyclic reference and prevent GC'ing + -- thus we store a reference to a weak table and put self into it + -- this way when self dies the weak reference dies and does not prevent GC'ing + local weak = setmetatable({[1]=self}, {__mode="v"}) + local ps = self:getParticlesList() local f_self = nil @@ -104,7 +111,7 @@ function _M:defineDisplayCallback() e = ps[i] e:checkDisplay() if e.ps:isAlive() then e.ps:toScreen(x + w / 2, y + h / 2, true, w / game.level.map.tile_w) - else self:removeParticles(e) + elseif weak[1] then weak[1]:removeParticles(e) end end end @@ -112,6 +119,8 @@ function _M:defineDisplayCallback() local function tactical(x, y, w, h) -- Tactical info if game.level and game.level.map.view_faction then + local self = weak[1] + if not self then return end local map = game.level.map if not f_self then diff --git a/game/engines/default/engine/Level.lua b/game/engines/default/engine/Level.lua index 8831460050ef275cc2ab05e032d93b6c96f216ef..3d2e575d443207babab91be5669950790d643a51 100644 --- a/game/engines/default/engine/Level.lua +++ b/game/engines/default/engine/Level.lua @@ -23,6 +23,11 @@ local Map = require "engine.Map" --- Define a level module(..., package.seeall, class.make) +-- Keep a list of currently existing maps +-- this is a weak table so it doesn't prevents GC +__level_store = {} +setmetatable(__level_store, {__mode="k"}) + --- Initializes the level with a "level" and a map function _M:init(level, map) self.level = level @@ -150,6 +155,8 @@ function _M:save() return class.save(self, {entities_list=true}) end function _M:loaded() + __level_store[self] = true + -- Loading the game has defined new uids for entities, yet we hard referenced the old ones -- So we fix it local nes = {} diff --git a/game/engines/default/engine/SavefilePipe.lua b/game/engines/default/engine/SavefilePipe.lua index 10149b5f42a88f9e7b6871ac056f5c625c8349c5..a4fcadaee6bad972ecd0d8d45231ef9f3f84ee19 100644 --- a/game/engines/default/engine/SavefilePipe.lua +++ b/game/engines/default/engine/SavefilePipe.lua @@ -118,7 +118,7 @@ function _M:push(savename, type, object, class, on_end) local Savefile = require(class) local id = Savefile["nameSave"..type:lower():capitalize()](Savefile, object) - if #self.pipe == 0 then savefile_pipe.current_nb = 0 self:steamCleanup() end + if #self.pipe == 0 then savefile_pipe.start_time = os.time() savefile_pipe.current_nb = 0 self:steamCleanup() end local clone, nb = object:cloneForSave() self.pipe_types[type] = self.pipe_types[type] or {} @@ -238,6 +238,10 @@ function _M:doThread() p.fct() table.remove(self.on_done, 1) end + + if savefile_pipe.start_time then + print("[SAVEFILE PIPE] saving took "..(os.time() - savefile_pipe.start_time).." seconds") + end end --- Force to wait for the saves diff --git a/game/engines/default/engine/Zone.lua b/game/engines/default/engine/Zone.lua index 6335ed3816cad996544e85049dee0c8d6d14748f..170d1411eaeb044926837db24c420b085eee4b8c 100644 --- a/game/engines/default/engine/Zone.lua +++ b/game/engines/default/engine/Zone.lua @@ -30,6 +30,10 @@ module(..., package.seeall, class.make) _no_save_fields = {temp_memory_levels=true, _tmp_data=true} +-- Keep a list of currently existing maps +-- this is a weak table so it doesn't prevents GC +__zone_store = {} +setmetatable(__zone_store, {__mode="k"}) --- List of rules to run through when applying an ego to an entity. _M.ego_rules = {} @@ -93,6 +97,8 @@ end --- Loads a zone definition -- @param short_name the short name of the zone to load, if should correspond to a directory in your module data/zones/short_name/ with a zone.lua, npcs.lua, grids.lua and objects.lua files inside function _M:init(short_name, dynamic) + __zone_store[self] = true + if _M.persist_last_zones and _M.persist_last_zones[short_name] then local zone = _M.persist_last_zones[short_name] forceprint("[ZONE] loading from last persistent", short_name, zone) @@ -647,6 +653,8 @@ end --- If we are loaded we need a new uid function _M:loaded() + __zone_store[self] = true + if type(self.reload_lists) ~= "boolean" or self.reload_lists then self:loadBaseLists() end diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua index eb2df64276bc956fa6486ad9cdf102ab1bcd7364..4fb29398ca25541130c9698bdaaa61dec236b49b 100644 --- a/game/engines/default/engine/utils.lua +++ b/game/engines/default/engine/utils.lua @@ -1782,6 +1782,49 @@ function util.loadfilemods(file, env) return prev end +--- Find a reference to the given value inside a table and all it contains +function util.findAllReferences(t, what) + local seen = {} + local function recurs(t, data) + if seen[t] then return end + seen[t] = true + for k, e in pairs(t) do + if type(k) == "table" then + local data = table.clone(data) + data[#data+1] = "k:"..tostring(k) + recurs(k, data) + end + if type(e) == "table" then + local data = table.clone(data) + data[#data+1] = "e:"..tostring(k) + recurs(e, data) + end + if type(k) == "function" then + local fenv = getfenv(k) + local data = table.clone(data) + data[#data+1] = "k:fenv:"..tostring(k) + recurs(fenv, data) + end + if type(e) == "function" then + local fenv = getfenv(e) + local data = table.clone(data) + data[#data+1] = "e:fenv:"..tostring(k) + recurs(fenv, data) + end + + if k == what then + print("KEY", table.concat(data, ", ")) + end + if e == what then + print("VAL", table.concat(data, ", ")) + end + end + end + + local data = {} + recurs(t, data) +end + -- if these functions are ever desired elsewhere, don't be shy to make these accessible beyond utils.lua local function deltaCoordsToReal(dx, dy, source_x, source_y) if util.isHex() then diff --git a/game/modules/tome/class/WorldNPC.lua b/game/modules/tome/class/WorldNPC.lua index bf591b09a35bb7b38cf18c55f5f86f598369de1e..ebd6443dcbbdb48c215e8612cc26de53f39a1d0f 100644 --- a/game/modules/tome/class/WorldNPC.lua +++ b/game/modules/tome/class/WorldNPC.lua @@ -70,23 +70,19 @@ end function _M:defineDisplayCallback() if not self._mo then return end - local ps = self:getParticlesList() + -- Cunning trick here! + -- the callback we give to mo:displayCallback is a function that references self + -- but self contains mo so it would create a cyclic reference and prevent GC'ing + -- thus we store a reference to a weak table and put self into it + -- this way when self dies the weak reference dies and does not prevent GC'ing + local weak = setmetatable({[1]=self}, {__mode="v"}) - local f_self = nil - local f_danger = nil - local f_powerful = nil - local f_friend = nil - local f_enemy = nil - local f_neutral = nil - local sf_self = nil - local sf_danger2 = nil - local sf_danger1 = nil - local sf_powerful = nil - local sf_friend = nil - local sf_enemy = nil - local sf_neutral = nil + local ps = self:getParticlesList() self._mo:displayCallback(function(x, y, w, h, zoom, on_map) + local self = weak[1] + if not self then return end + if game.level and game.level.map.view_faction and game.always_target and game.always_target ~= "old" then local map = game.level.map if on_map then diff --git a/game/modules/tome/data/general/objects/world-artifacts.lua b/game/modules/tome/data/general/objects/world-artifacts.lua index ad936842cd0b7914920c9c78f1980c4787dc90af..343024acb3ca4f5c664e066f8de088938d4f66e5 100644 --- a/game/modules/tome/data/general/objects/world-artifacts.lua +++ b/game/modules/tome/data/general/objects/world-artifacts.lua @@ -1938,8 +1938,8 @@ newEntity{ base = "BASE_ARROW", special_on_crit = {desc="dominate the target", fct=function(combat, who, target) if not target or target == self then return end if target:canBe("instakill") then - local check = math.max(src:combatSpellpower(), src:combatMindpower(), src:combatAttack()) - target:setEffect(target.EFF_DOMINATE_ENTHRALL, 3, {src=who, apply_power=check()}) + local check = math.max(who:combatSpellpower(), who:combatMindpower(), who:combatAttack()) + target:setEffect(target.EFF_DOMINATE_ENTHRALL, 3, {src=who, apply_power=check}) end end}, },