Skip to content

CMD.REMOVE and CMD.BUILD interactions can result in dropped commands #2893

@Fx-Doo

Description

@Fx-Doo

Edit:: reformulated with chatGPT

This issue, mentioned by PtaQ, highlights a problem that appears to be engine-related (even though it is triggered by our widgets).

After investigating, I identified two scenarios that reliably reproduce the issue, along with an explanation of why it occurs.

Preliminary observations

Before describing the issue itself, here are two relevant behaviors:

Case 1:

In BAR (latest version), spawn an armmlv (or any unit with canAssist = false).
Give it a build command, then stop it once the nanoframe has been created.
Issue the exact same build command again.
→ The unit remains idle, as it cannot resume the build command.

Case 2:

Spawn an armconsul (also with canAssist = true).
Order it to build a mine, then stop it once the nanoframe has been created.
Place a new mine build command on the same spot.
→ The unit remains idle. Although it could resume the build via CMD.REPAIR, it instead attempts to start a new mine on top of the existing one.

Normal behavior comparison:

Using the same armconsul, build an armsolar.
Stop construction, then issue the same CMD.BUILD order again.
→ This time, the unit correctly resumes construction.

Why does this happen?

Builders with canAssist = false are not allowed to resume construction on an existing unit.

Additionally, non-blocking buildings do not trigger the “blocking detection” logic in StartBuild. This is the mechanism that normally allows a unit to assist an existing build when given a similar command.

As a result:

The builder cannot resume the existing structure.
The location is still considered unbuildable for a new structure.
→ The command is simply dropped.

CMD.REMOVE behavior

When a CMD.REMOVE order is issued while a unit is building, it appears to reset the unit’s command queue, regardless of whether it actually removes a command.

This causes the CMD.BUILD order to be effectively reissued, meaning the previously described rules apply again.

Standard builders will resume construction.
Builders with canAssist = false, or when building non-blocking structures, will drop the current command and proceed to the next one.

Reproduction via widget

This behavior can be reproduced with the following widget:

function widget:UnitCreated(unitID, _, _, builderID)
  if builderID then
    Spring.GiveOrderToUnit(builderID, CMD.REMOVE, {}, {})
    -- Empty params: no command tag is passed, so nothing is actually removed
  end
end

Test this with the scenarios above (armmlv, armconsul mine, and armsolar).
The results are consistent with the observations.

Impact in BAR

In BAR, this issue was uncovered indirectly.

The unit_clear_builder_queue widget sends a CMD.REMOVE order to builders when they finish constructing a building. Its purpose is to remove completed buildings from other builders’ queues.

However, due to its current design:

It systematically attempts to remove at least the current command that is still present in the builder’s queue.
Regardless of whether that command is actually removed or simply considered finished (depending on timing and latency), the “queue reset” effect still occurs.

If, due to lag and rapid transitions between build commands, the CMD.REMOVE reaches the server after the next build command has already started, it triggers the problematic scenario described above.

Proposed fixes:

When a uDef.canAssist = false unit starts (UnitCreated() ) a unit, make it able to resume building this unit by "remembering" it.

When an unfinished building of the same type is present underneath the current build command of a builder, attempt to resume it regardless of whether or not it is actually blocking. Only attempt to build over it if resuming it isn't possible.

And this might be a longer shot but, find out why CMD.REMOVE forces a "reset" of the command queue and simply make sure it does not... but i'm guessing it has do do with the fact that a cmd remove is instantly performed in front, takes precedence over all other commands, and the queue is only resumed once it's done.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions