Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/snippets/fix.7153.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- (#7153) Add a delay before ACUs explode when their player disconnects. This should fix replays cuttting off at the first disconnected player.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the changelog typo.

cuttting should be cutting.

Proposed fix
-- (`#7153`) Add a delay before ACUs explode when their player disconnects. This should fix replays cuttting off at the first disconnected player.
+- (`#7153`) Add a delay before ACUs explode when their player disconnects. This should fix replays cutting off at the first disconnected player.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- (#7153) Add a delay before ACUs explode when their player disconnects. This should fix replays cuttting off at the first disconnected player.
- (`#7153`) Add a delay before ACUs explode when their player disconnects. This should fix replays cutting off at the first disconnected player.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@changelog/snippets/fix.7153.md` at line 1, Fix the typo in the changelog
entry in the fix.7153.md file by changing the word "cuttting" to "cutting" in
the sentence that reads "This should fix replays cuttting off at the first
disconnected player."

37 changes: 29 additions & 8 deletions lua/SimUtils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -992,8 +992,19 @@ end
-- shortly thereafter due to the army being defeated (as is common), we then
-- have an opportunity to see the final game state as observers before everything
-- would have blown up.
--
-- This should be longer than `AbandonedArmyGracePeriod`
EndGameGracePeriod = 10

-- Seconds to wait after an army has been abandoned (the player disconnected)
-- before processing its units. This gives a small opportunity to avoid the ACU
-- explosion, and prevents disconnecting players from providing the server with
-- a cut-off replay due to the sim on their client instantly killing all other ACUs
-- and reporting a game over state.
--
-- This should be shorter than `EndGameGracePeriod`
AbandonedArmyGracePeriod = 3

-- Set to true in `AbstractVictoryCondition.EndGame` to prevent killing units after a
-- team is victorious but before the sim is stopped.
GameIsEnding = false
Expand All @@ -1010,6 +1021,16 @@ local function KillUnits(toKill)
end
end

--- Kills all given units after a delay, if not already dead.
---@param toKill Entity[]
---@param delaySec number
local function DelayedKillUnits(toKill, delaySec)
ForkThread(function ()
WaitSeconds(delaySec)
KillUnits(toKill)
end)
end

---@param self AIBrain
local function KillWalls(self)
KillUnits(self:GetListOfUnits(categories.WALL, false))
Expand Down Expand Up @@ -1251,13 +1272,17 @@ MinimumShareTime = 5 * 60 * 10 -- 5 minutes
function KillUnsafeCommanders(commanders, tick)
tick = tick or GetGameTick()
local safeCommanders = {}
local unsafeCommanders = {}
for _, com in commanders do
if com.LastTickDamaged and com.LastTickDamaged + CommanderSafeTime > tick then
com:Kill()
table.insert(unsafeCommanders, com)
else
table.insert(safeCommanders, com)
end
end

DelayedKillUnits(unsafeCommanders, AbandonedArmyGracePeriod)

return safeCommanders
end

Expand Down Expand Up @@ -1305,8 +1330,7 @@ function KillArmyOnDelayedRecall(self, shareOption, shareTime)
local safeCommanders = KillUnsafeCommanders(sharedCommanders)

if not table.empty(safeCommanders) then
-- note: this adds 3 seconds to the grace period
FakeTeleportUnits(safeCommanders, true)
ForkThread(FakeTeleportUnits, safeCommanders, true)
end
end
end
Expand Down Expand Up @@ -1345,16 +1369,14 @@ function KillAbandonedArmy(self, shareOption, shareAcuOption, victoryOption)
shareOption = ScenarioInfo.Options.Share
end

-- Don't apply instant-effect disconnect rules for players/ACUs that might be defeated soon,
-- and might have intentionally disconnected.
if shareAcuOption == 'Explode' or shareAcuOption == 'Recall' then
local safeCommanders
local commanders = self:GetListOfUnits(categories.COMMAND, false)
if shareAcuOption == 'Recall' then
safeCommanders = KillUnsafeCommanders(commanders)
else
-- explode all the ACUs so they don't get shared
KillUnits(commanders)
DelayedKillUnits(commanders, AbandonedArmyGracePeriod)
end

-- Only handle Assassination victory, as in other settings the player is unlikely to be defeated soon
Expand All @@ -1364,8 +1386,7 @@ function KillAbandonedArmy(self, shareOption, shareAcuOption, victoryOption)

-- non-assassination modes can have armies abandon without commanders
if shareAcuOption == 'Recall' and not table.empty(safeCommanders) then
-- note: this adds 3 seconds to the grace period
FakeTeleportUnits(safeCommanders, true)
ForkThread(FakeTeleportUnits, safeCommanders, true)
end

KillArmy(self, shareOption)
Expand Down
Loading