Skip to content

Commit

Permalink
Fix the "assist to upgrade" and "assist to unpause" settings conflict…
Browse files Browse the repository at this point in the history
…ing and not pausing upgraded units during lag (#6446)
  • Loading branch information
lL1l1 authored Sep 22, 2024
1 parent c4bc803 commit 39710e7
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 46 deletions.
1 change: 1 addition & 0 deletions changelog/snippets/fix.6446.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- (#6446) Fix the "assist to upgrade" and "assist to unpause" game options conflicting and causing upgrading units to unpause during lag spikes.
105 changes: 59 additions & 46 deletions lua/ui/game/commandmode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -406,61 +406,74 @@ local function OnGuardUpgrade(guardees, unit)
end
end

--- Thread to keep track of when to unpause,
--- logic is a bit convoluted but guarantees that we still have access to the user units as the game progresses
---@param targetId EntityId
local function UnpauseThread(targetId)
WaitTicks(10)
local target = GetUnitById(targetId)
while target do
local candidates = target.ThreadUnpauseCandidates
if (candidates and not table.empty(candidates)) then
for id, _ in candidates do
local engineer = GetUnitById(id)
-- check if it is idle instead of its guarded entity to allow queuing orders before the assist command
if engineer and not engineer:IsIdle() then
-- ensure the target focus exists, since this thread may be targeted at something that is not building anything,
-- but might start to build something after some network delay, which you won't want to unpause due to `nil == nil`
local targetFocus = target:GetFocus()
if targetFocus and targetFocus == engineer:GetFocus() then
target.ThreadUnpauseCandidates = nil
target.ThreadUnpause = nil
SetPaused({ target }, false)
return
end
else
-- engineer is idle, died, we switch armies, ...
candidates[id] = nil
end
end
else
target.ThreadUnpauseCandidates = nil
target.ThreadUnpause = nil
return
end

WaitTicks(10)
target = GetUnitById(targetId)
end
end

---@param guardees UserUnit[]
---@param target UserUnit
local function OnGuardUnpause(guardees, target)
local prefs = Prefs.GetFieldFromCurrentProfile('options').assist_to_unpause
if prefs == 'On' or
(
prefs == 'ExtractorsAndRadars' and
EntityCategoryContains((categories.MASSEXTRACTION + categories.RADAR) * categories.STRUCTURE, target))
then

-- start a single thread to keep track of when to unpause, logic feels a bit convoluted
-- but that is purely to guarantee that we still have access to the user units as the
-- game progresses
if not target.ThreadUnpause then
local id = target:GetEntityId()
target.ThreadUnpause = ForkThread(
function()
WaitSeconds(1.0)
local target = GetUnitById(id)
while target do
local candidates = target.ThreadUnpauseCandidates
if (candidates and not table.empty(candidates)) then
for id, _ in candidates do
local engineer = GetUnitById(id)
if engineer and not engineer:IsIdle() then
local focus = engineer:GetFocus()
if focus == target:GetFocus() then
target.ThreadUnpauseCandidates = nil
target.ThreadUnpause = nil
SetPaused({ target }, false)
break
end
-- engineer is idle, died, we switch armies, ...
else
candidates[id] = nil
end
end
else
target.ThreadUnpauseCandidates = nil
target.ThreadUnpause = nil
break
end

WaitSeconds(1.0)
target = GetUnitById(id)
end
end
local bp = __blueprints[target:GetUnitId()]
-- only create the unpause thread for units that have the ability to unpause
if (
prefs == 'On' and
(
EntityCategoryContains(categories.REPAIR + categories.FACTORY + categories.SILO, target) -- REPAIR includes mantis and harbs, compared to ENGINEER category
or (bp.General.UpgradesTo and bp.General.UpgradesTo ~= '') -- upgradeables can also be assisted
)
end

-- add these to keep track
)
or
(
prefs == 'ExtractorsAndRadars'
and EntityCategoryContains((categories.MASSEXTRACTION + categories.RADAR) * categories.STRUCTURE, target)
and (bp.General.UpgradesTo and bp.General.UpgradesTo ~= '') -- use `and` to make sure the mex/radar is upgradeable
)
then
-- save the guardees' entity ids to keep track of in the unpause thread
target.ThreadUnpauseCandidates = target.ThreadUnpauseCandidates or {}
for k, guardee in guardees do
target.ThreadUnpauseCandidates[guardee:GetEntityId()] = true
end

-- start a single thread to keep track of when to unpause
if not target.ThreadUnpause then
target.ThreadUnpause = ForkThread(UnpauseThread, target:GetEntityId())
end
end
end

Expand Down

0 comments on commit 39710e7

Please sign in to comment.