Skip to content

Commit d0da067

Browse files
authored
Fix command source not matching army index in AI games (#7038)
1 parent 7b602b0 commit d0da067

File tree

5 files changed

+71
-18
lines changed

5 files changed

+71
-18
lines changed

changelog/snippets/fix.7038.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- (#7038) Fix various sim callbacks detecting the incorrect army as the source of the callback in games with AI or observers. This fixes the issue where paintings were displayed to the incorrect players in AI games.

engine/Sim.lua

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -600,9 +600,13 @@ end
600600
function GetArmyUnitCostTotal(army)
601601
end
602602

603-
--- Returns the currently active command source in the sim state. This number is the army index
604-
--- of the army that sent the command.
605-
---@return number
603+
--- Returns the currently active command source in the sim state. This is the index of the client
604+
--- that issued the command, which is from a list including players (human armies) and observers.
605+
--- The list is in the order of the lobby slots, with observers at the end.
606+
---
607+
--- Use `GetCurrentCommandSourceArmy` from `SimUtils.lua` to reliably get the army index of the
608+
--- current command source.
609+
---@return integer
606610
function GetCurrentCommandSource()
607611
end
608612

lua/SimCallbacks.lua

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ local IssueAggressiveMove = IssueAggressiveMove
4343
local IssueGuard = IssueGuard
4444
local IssueFerry = IssueFerry
4545

46+
local GetCurrentCommandSourceArmy = SimUtils.GetCurrentCommandSourceArmy
47+
4648
-- upvalue categories for performance
4749
local CategoriesTransportation = categories.TRANSPORTATION
4850

@@ -64,7 +66,18 @@ function DoCallback(name, data, units)
6466

6567
local timeTaken = GetSystemTimeSecondsOnlyForProfileUse() - start
6668
if (timeTaken > 0.005) then
67-
SPEW(string.format("Time to process %s from %d: %f", name, timeTaken, GetCurrentCommandSource() or -2))
69+
local commandSourceNickname
70+
local commandSourceArmy = GetCurrentCommandSourceArmy()
71+
if commandSourceArmy then
72+
commandSourceNickname = tostring(ArmyBrains[commandSourceArmy].Nickname)
73+
else
74+
commandSourceNickname = string.format("Observer (source %d)", GetCurrentCommandSource())
75+
end
76+
SPEW(string.format("Time to process \"%s\" from %s: %f"
77+
, name
78+
, commandSourceNickname
79+
, timeTaken
80+
))
6881
end
6982
end
7083

@@ -640,7 +653,7 @@ do
640653
-- verify selection
641654
selection = SecureUnits(selection)
642655
if (not selection) or TableEmpty(selection) then
643-
if (GetFocusArmy() == GetCurrentCommandSource()) then
656+
if (GetFocusArmy() == GetCurrentCommandSourceArmy()) then
644657
print("Unable to interrupt path finding")
645658
end
646659

@@ -650,7 +663,7 @@ do
650663
-- only apply this to engineers
651664
local engineers = EntityCategoryFilterDown(categories.ENGINEER + categories.COMMAND, selection)
652665
if table.empty(engineers) then
653-
if (GetFocusArmy() == GetCurrentCommandSource()) then
666+
if (GetFocusArmy() == GetCurrentCommandSourceArmy()) then
654667
print("Unable to interrupt path finding")
655668
end
656669

@@ -663,7 +676,7 @@ do
663676
local commandSourceGuard = CommandSourceGuards[commandSource]
664677

665678
if commandSourceGuard and commandSourceGuard + 5 >= gameTick then
666-
if (GetFocusArmy() == GetCurrentCommandSource()) then
679+
if (GetFocusArmy() == GetCurrentCommandSourceArmy()) then
667680
print("Unable to interrupt path finding")
668681
end
669682

@@ -686,7 +699,7 @@ do
686699
-- verify selection
687700
selection = SecureUnits(selection)
688701
if (not selection) or TableEmpty(selection) then
689-
if (GetFocusArmy() == GetCurrentCommandSource()) then
702+
if (GetFocusArmy() == GetCurrentCommandSourceArmy()) then
690703
print("Unable to discharge")
691704
end
692705

@@ -705,7 +718,7 @@ do
705718
end
706719

707720
if table.empty(unitsWithShields) then
708-
if (GetFocusArmy() == GetCurrentCommandSource()) then
721+
if (GetFocusArmy() == GetCurrentCommandSourceArmy()) then
709722
print("Unable to discharge")
710723
end
711724

@@ -718,7 +731,7 @@ do
718731
local commandSourceGuard = CommandSourceGuards[commandSource]
719732

720733
if commandSourceGuard and commandSourceGuard + 5 >= gameTick then
721-
if (GetFocusArmy() == GetCurrentCommandSource()) then
734+
if (GetFocusArmy() == GetCurrentCommandSourceArmy()) then
722735
print("Unable to discharge")
723736
end
724737

@@ -837,7 +850,7 @@ do
837850
---@param data UIShareableBrushStrokeCallbackMessage
838851
local SyncPainting = function(data)
839852
-- used to determine the color of the painting
840-
data.ShareablePainting.PeerName = GetArmyBrain(GetCurrentCommandSource()).Nickname
853+
data.ShareablePainting.PeerName = GetArmyBrain(GetCurrentCommandSourceArmy()).Nickname
841854

842855
Sync.SharePaintingBrushStroke = Sync.SharePaintingBrushStroke or {}
843856
table.insert(Sync.SharePaintingBrushStroke, data)
@@ -846,7 +859,7 @@ do
846859
---@param data UIShareableBrushStrokeCallbackMessage
847860
Callbacks.SharePaintingBrushStroke = function(data)
848861
local focusArmy = GetFocusArmy()
849-
local currentCommandSource = GetCurrentCommandSource()
862+
local currentCommandSourceArmy = GetCurrentCommandSourceArmy()
850863

851864
-- spectators are able to see all paintings. We take into account
852865
-- the original focus army because spectators can change focus army
@@ -856,7 +869,7 @@ do
856869
end
857870

858871
-- allies are able to see each others paintings
859-
if IsAlly(focusArmy, currentCommandSource) then
872+
if IsAlly(focusArmy, currentCommandSourceArmy) then
860873
SyncPainting(data)
861874
return
862875
end
@@ -882,7 +895,6 @@ local function PassesAIAntiCheatCheck()
882895
return ScenarioInfo.GameHasAIs or PassesAntiCheatCheck()
883896
end
884897

885-
886898
local SpawnedMeshes = {}
887899

888900
local function SpawnUnitMesh(id, x, y, z, pitch, yaw, roll)
@@ -1417,8 +1429,14 @@ end
14171429
---@param data CallbackModeratorEventData
14181430
Callbacks.ModeratorEvent = function(data)
14191431
-- show up in the game logs
1420-
local brain = GetArmyBrain(GetCurrentCommandSource())
1421-
SPEW(string.format("Moderator event for %s: %s", tostring(brain.Nickname), tostring(data.Message)))
1432+
local commandSourceNickname
1433+
local commandSourceArmy = GetCurrentCommandSourceArmy()
1434+
if commandSourceArmy then
1435+
commandSourceNickname = tostring(GetArmyBrain(commandSourceArmy).Nickname)
1436+
else
1437+
commandSourceNickname = string.format("Observer (source %d)", GetCurrentCommandSource())
1438+
end
1439+
SPEW(string.format("Moderator event for %s: %s", commandSourceNickname, tostring(data.Message)))
14221440
end
14231441

14241442
--#endregion

lua/SimUtils.lua

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
-- upvalues for performance
66
local ArmyBrains = ArmyBrains
7+
local GetCurrentCommandSource = GetCurrentCommandSource
78

89
------------------------------------------------------------------------------------------------------------------------
910
--#region General Unit Transfer Scripts
@@ -1541,3 +1542,30 @@ function DrawBone(entity, bone, length)
15411542
-- Z axis
15421543
DrawLine(pos, pos + forward * length, '0000ff')
15431544
end
1545+
1546+
local CommandSourceToArmyMap
1547+
--- Retrieves the army index corresponding to the given command source index.
1548+
---@param source integer
1549+
---@return integer
1550+
function GetArmyOfCommandSource(source)
1551+
if not CommandSourceToArmyMap then
1552+
CommandSourceToArmyMap = {}
1553+
local commandSourceIndex = 1
1554+
for index, army in ArmyBrains do
1555+
if army.Human then
1556+
CommandSourceToArmyMap[commandSourceIndex] = index
1557+
commandSourceIndex = commandSourceIndex + 1
1558+
end
1559+
end
1560+
end
1561+
1562+
return CommandSourceToArmyMap[source]
1563+
end
1564+
1565+
--- Retrieves the army index corresponding to the current command source.
1566+
---@return integer
1567+
function GetCurrentCommandSourceArmy()
1568+
return GetArmyOfCommandSource(GetCurrentCommandSource())
1569+
end
1570+
1571+

lua/sim/commands/area-reclaim-order.lua

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ local StringFormat = string.format
3131

3232
local IssueReclaim = IssueReclaim
3333

34+
local GetCurrentCommandSourceArmy = import("/lua/SimUtils.lua").GetCurrentCommandSourceArmy
35+
3436
---@type table<EntityId, boolean>
3537
local distances = { }
3638

@@ -86,7 +88,7 @@ local function ReclaimAdjacentUnits (units, target, doPrint)
8688
end
8789
end
8890

89-
if doPrint and processed > 0 and (GetFocusArmy() == GetCurrentCommandSource()) then
91+
if doPrint and processed > 0 and (GetFocusArmy() == GetCurrentCommandSourceArmy()) then
9092
print(StringFormat("Reclaiming %d adjacent units", processed))
9193
end
9294
end
@@ -197,7 +199,7 @@ function AreaReclaimProps(units, ps, pe, width, doPrint)
197199
end
198200
end
199201

200-
if doPrint and (GetFocusArmy() == GetCurrentCommandSource()) then
202+
if doPrint and (GetFocusArmy() == GetCurrentCommandSourceArmy()) then
201203
if processed > 0 then
202204
print(StringFormat("Reclaiming %d additional props", processed))
203205
end

0 commit comments

Comments
 (0)