Question on SetCurrentFactoryForQueueDisplay engine method
-
Short Question
What does calling SetCurrentFactoryForQueueDisplay() do beside return the build queue of the factory passed as a parameter? What does ClearCurrentFactoryForQueueDisplay() do? Is there a better way to do a simple check for the existence of a factory queue in the background other than the former?Background
I'm working on a UI mod to extend control groups inheritance from factories (it's actually mostly complete, aside from this issue). Adding a control group while a unit is under construction updates the control group on that unit. A factory assisting another factory will inherit the source factories control group and apply it to the unit under construction.Problem
Trying to chase down a bug in a UI mod where:A factory (FacA) is assisting another factory (FacB), which in turn is assisting another factory (FacC).
The build queue's of FacA and FacC are empty (so only FacB, the middle factory in the assist chain, is building units).
Unusual behavior: When FacB is given an order to build more than one unit, FacA (the assisting factory) will begin construction before FacB, and the total number of units built will be roughly twice the number of units added to the queue (pseudo double dip situation). Additionally, if FacB is constructing a single unit (so one unit added to the queue originally) and another unit is added to the queue, FacB will restart construction on its own unit after FacA (the assisting factory) begins construction. This only happens when FacB is assisting the (idle) FacC.
Suffice to say I don't really understand what's going on under the hood, but process of elimination suggests SetCurrentFactoryForQueueDisplay is somehow responsible. I'm using it in a very basic sense to check if a factory has a build queue, but experimentation suggests it has other effects on the UI when called. If anybody has any insight on those effects I would be very thankful to hear them.
Code/Hooks
selection.lualocal modpath = ('/mods/ControlGroupOverhaul') local SmartPop = import(modpath .. '/modules/utilities.lua').SmartPop local Dummy = import(modpath .. '/modules/utilities.lua').Dummy local LogTable = import(modpath .. '/modules/LogTable.lua').LogTable local Units = import('/mods/UMT/modules/units.lua') --local Selection = import('/lua/ui/game/selection.lua') --local Units = import('/mods/common/units.lua') do local unitId = nil local factory = nil local sets = {} local assistedFactory = nil local oldAddUnitToSelectionSet = AddUnitToSelectionSet function AddUnitToSelectionSet(name, unit) if unitId == nil or unit:GetEntityId() ~= unitId then if unit:HasSelectionSet(Dummy()) then unit:RemoveSelectionSet(Dummy()) end unitId = unit:GetEntityId() LOG('AddUnitToSelectionSet: New unit ID : ' .. unitId) factory = unit:GetCreator() LOG('AddUnitToSelectionSet: Factory focus : ' .. tostring(factory:GetFocus():GetEntityId())) sets = factory:GetSelectionSets() assistedFactory = FindFacTargetRecursive(factory) if sets == {Dummy()} and assistedFactory ~= nil then return elseif assistedFactory ~= nil and not GetFactoryQueue(factory) then sets = assistedFactory:GetSelectionSets() end for _, name in sets do if name ~= Dummy() then LOG('AddUnitToSelectionSet/name: '..name) oldAddUnitToSelectionSet(name, unit) end end end LOG('AddUnitToSelectionSet/End') end local oldAddSelectionSet = AddSelectionSet function AddSelectionSet(name, unitArray) LOG('AddSelectionSet/name: '..name) local aOutgoingFactories = EntityCategoryFilterDown(categories.FACTORY, ProcessSelectionSet(name)) or {} local aIncomingFactories = EntityCategoryFilterDown(categories.FACTORY, unitArray) or {} --remove dummy group in advance of oldAddSelectionSet so we don't jack up selectionSets[other] if next(aIncomingFactories) then for _, factory in aIncomingFactories do if factory:HasSelectionSet(Dummy()) then factory:RemoveSelectionSet(Dummy()) end end end --call oldAddSelectionSet as usual LOG('AddSelectionSet/oldAddSelectionSet') oldAddSelectionSet(name, unitArray) --add dummy selection set to outgoing factories LOG('AddSelectionSet/aOutgoingFactories') if next(aOutgoingFactories) then for _, factory in aOutgoingFactories do if not next(factory:GetSelectionSets()) then factory:AddSelectionSet(Dummy()) end end end --add selection set to unit under construction --then find relevant assisting factories and add selection set to those units under construction LOG('AddSelectionSet/aIncomingFactories') if next(aIncomingFactories) then for _, factory in aIncomingFactories do if factory:GetFocus() ~= nil and not next(factory:GetFocus():GetSelectionSets()) then if GetFactoryQueue(factory) then oldAddUnitToSelectionSet(name, factory:GetFocus()) end end if factory.AssistingFactories ~= nil and next(factory.AssistingFactories) then LOG('AddSelectionSet/ Factory is assisted (potentially)') for assistingFactory in factory.AssistingFactories do if assistingFactory.RecursiveGuardedEntity == factory and assistingFactory:GetFocus() then LOG(' assistingFactory.GetEntityId: '..assistingFactory:GetEntityId()) LOG(' assistingFactory.GetFocus:GetEntityId: '..assistingFactory:GetFocus():GetEntityId()) LOG(' assistingFactory.RecursiveGuardedEntity:GetEntityId: '..assistingFactory.RecursiveGuardedEntity:GetEntityId()) oldAddUnitToSelectionSet(name, assistingFactory:GetFocus()) end end else LOG('AddSelectionSet/ No assisting factories') end end end LOG('AddSelectionSet/End') end end function FindFacTargetRecursive(factory) LOG('FindFacTargetRecursive') local recursiveFac = factory while recursiveFac:GetGuardedEntity() and not GetFactoryQueue(recursiveFac) do recursiveFac = recursiveFac:GetGuardedEntity() end if recursiveFac:GetEntityId() ~= factory:GetEntityId() then recursiveFac.AssistingFactories = recursiveFac.AssistingFactories or {} recursiveFac.AssistingFactories[factory] = true if GetFactoryQueue(recursiveFac) then LogTable(GetFactoryQueue(recursiveFac)) end else recursiveFac = nil end factory.RecursiveGuardedEntity = recursiveFac LOG('FindFacTargetRecursive/End') return recursiveFac end function GetFactoryQueue(factory) local queue = SetCurrentFactoryForQueueDisplay(factory) or nil local selectedFactories = EntityCategoryFilterDown(categories.FACTORY, GetSelectedUnits()) LOG('Num of selected factories: '..tostring(TableLength(selectedFactories))) if TableLength(selectedFactories) == 1 then SetCurrentFactoryForQueueDisplay(selectedFactories[1]) else ClearCurrentFactoryForQueueDisplay() end return queue end function TableLength(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end
commandmode.lua
local modpath = ('/mods/ControlGroupOverhaul') local Dummy = import(modpath .. '/modules/utilities.lua').Dummy oldOnGuard = OnGuard function OnGuard(guardees, unit) if EntityCategoryContains(categories.FACTORY, unit) then local guardingFactories = EntityCategoryFilterDown(categories.FACTORY, guardees) for _, factory in guardingFactories do if not next(factory:GetSelectionSets()) then factory:AddSelectionSet(Dummy()) end end end oldOnGuard(guardees, unit) end
-
I'd suggest to move into discord server and discuss it there. Take a modder role and go to #modding-general
-
Having asked a number of questions in that channel I felt this one was more suited to a forum post.
-
Update: Testing with no mods and this appears to be an engine bug. A set of factories (in which one is assisting a factory which is assisting another factory outside the set) will produce some factor of the original number of units in the build queue.
Update 2.0: The factory with the build queue, assisting another idle factory, doesn't subtract from its own build queue (except for the last unit it builds). Appears it gets confused about where it's getting the unit from.
-
@slicknixon We are also aware of a rare issue that can cause a complete lock-up when factory assists form a complete circle - A >> B >>> C >> A
-
I investigated that in the course of the above and found that it automatically broke the chain when the circle was completed (might not apply in all cases).