Skip to content

Commit ba5228f

Browse files
authored
fix: ebb and flow memory leak (#3528)
Fix memory leak in Ebb and Flow system causing server crashes • Fixed critical memory leak related to Ebb and Flow map transitions • Tracked and cleaned up event callbacks to prevent accumulation • Managed addEvent timers through a centralized pendingEvents table • Implemented clearPendingEvents() and unregisterEventCallbacks() • Ensured proper cleanup before each map change and on server save • Resolved lingering tile and scope references in anonymous functions • Maintains default 25 packet/s without lag or instability Fixes #3373
1 parent 10e8450 commit ba5228f

1 file changed

Lines changed: 63 additions & 7 deletions

File tree

data-otservbr-global/scripts/quests/soul_war/globalevent-ebb_and_flow_change_maps.lua

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,57 @@
1+
local eventCallbacks = {}
2+
13
local function updateWaterPoolsSize()
24
for _, pos in ipairs(SoulWarQuest.ebbAndFlow.poolPositions) do
35
local tile = Tile(pos)
46
if tile then
57
local item = tile:getItemById(SoulWarQuest.ebbAndFlow.smallPoolId)
68
if item then
79
item:transform(SoulWarQuest.ebbAndFlow.MediumPoolId)
8-
-- Starts another timer for filling after an additional 40 seconds
9-
addEvent(function()
10-
local item = tile:getItemById(SoulWarQuest.ebbAndFlow.MediumPoolId)
11-
if item then
12-
item:transform(SoulWarQuest.ebbAndFlow.smallPoolId)
10+
local eventId = addEvent(function()
11+
local tile = Tile(pos)
12+
if tile then
13+
local item = tile:getItemById(SoulWarQuest.ebbAndFlow.MediumPoolId)
14+
if item then
15+
item:transform(SoulWarQuest.ebbAndFlow.smallPoolId)
16+
end
1317
end
1418
end, 40000) -- 40 seconds
19+
20+
-- Store the event ID for new cleanup
21+
if not SoulWarQuest.ebbAndFlow.pendingEvents then
22+
SoulWarQuest.ebbAndFlow.pendingEvents = {}
23+
end
24+
table.insert(SoulWarQuest.ebbAndFlow.pendingEvents, eventId)
1525
end
1626
end
1727
end
1828
end
1929

30+
-- Helper function to clear all pending events
31+
local function clearPendingEvents()
32+
if SoulWarQuest.ebbAndFlow.pendingEvents then
33+
for _, eventId in ipairs(SoulWarQuest.ebbAndFlow.pendingEvents) do
34+
stopEvent(eventId)
35+
end
36+
SoulWarQuest.ebbAndFlow.pendingEvents = {}
37+
end
38+
end
39+
40+
-- Helper function to unregister all event callbacks
41+
local function unregisterEventCallbacks()
42+
for name, callback in pairs(eventCallbacks) do
43+
if callback and callback.unregister then
44+
callback:unregister()
45+
end
46+
end
47+
eventCallbacks = {}
48+
end
49+
2050
local function loadMapEmpty()
51+
-- Clean up previous events before loading a new map
52+
clearPendingEvents()
53+
unregisterEventCallbacks()
54+
2155
local playersInZone = SoulWarQuest.ebbAndFlow.getZone():countPlayers()
2256
local monstersInZone = SoulWarQuest.ebbAndFlow.getZone():countMonsters()
2357
if playersInZone > 0 or monstersInZone > 0 then
@@ -55,11 +89,17 @@ local function loadMapEmpty()
5589
end
5690

5791
updatePlayers:register()
92+
-- Store reference for later cleanup
93+
eventCallbacks["UpdatePlayersEmptyEbbFlowMap"] = updatePlayers
5894

59-
addEvent(function()
60-
-- Change the appearance of puddles to indicate the next filling
95+
local eventId = addEvent(function()
6196
updateWaterPoolsSize()
6297
end, 80000) -- 80 seconds
98+
99+
if not SoulWarQuest.ebbAndFlow.pendingEvents then
100+
SoulWarQuest.ebbAndFlow.pendingEvents = {}
101+
end
102+
table.insert(SoulWarQuest.ebbAndFlow.pendingEvents, eventId)
63103
end
64104

65105
local function getDistance(pos1, pos2)
@@ -80,6 +120,9 @@ local function findNearestRoomPosition(playerPosition)
80120
end
81121

82122
local function loadMapInundate()
123+
clearPendingEvents()
124+
unregisterEventCallbacks()
125+
83126
local playersInZone = SoulWarQuest.ebbAndFlow.getZone():countPlayers()
84127
local monstersInZone = SoulWarQuest.ebbAndFlow.getZone():countMonsters()
85128
if playersInZone > 0 or monstersInZone > 0 then
@@ -121,11 +164,14 @@ local function loadMapInundate()
121164
end
122165

123166
updatePlayers:register()
167+
eventCallbacks["UpdatePlayersInundateEbbFlowMap"] = updatePlayers
124168
end
125169

126170
local loadEmptyMap = GlobalEvent("SoulWarQuest.ebbAndFlow")
127171

128172
function loadEmptyMap.onStartup()
173+
SoulWarQuest.ebbAndFlow.pendingEvents = {}
174+
129175
Game.loadMap(SoulWarQuest.ebbAndFlow.mapsPath.ebbFlow)
130176
loadMapEmpty()
131177
SoulWarQuest.ebbAndFlow.updateZonePlayers()
@@ -149,3 +195,13 @@ end
149195

150196
eddAndFlowInundate:interval(SoulWarQuest.ebbAndFlow.intervalChangeMap * 60 * 1000)
151197
eddAndFlowInundate:register()
198+
199+
local cleanupOnSave = GlobalEvent("EbbFlowCleanupOnSave")
200+
201+
function cleanupOnSave.onSave()
202+
clearPendingEvents()
203+
unregisterEventCallbacks()
204+
return true
205+
end
206+
207+
cleanupOnSave:register()

0 commit comments

Comments
 (0)