Skip to content

Fix adjacency error caused by dummy unit from drones/external factories - #6863

Open
lL1l1 wants to merge 13 commits into
FAForever:developfrom
lL1l1:fix-adjacency-error
Open

Fix adjacency error caused by dummy unit from drones/external factories#6863
lL1l1 wants to merge 13 commits into
FAForever:developfrom
lL1l1:fix-adjacency-error

Conversation

@lL1l1

@lL1l1 lL1l1 commented Jun 16, 2025

Copy link
Copy Markdown
Contributor

Description of the proposed changes

Fixes #6425.

  • Adds the dummy unit OnCreate to zxa0003's script. This fixes EntityId being missing from the unit.
  • Disable doing adjacency for dummy units since it can allocate many tables considering that it occurs for all UEF construction drones which may be flying around building many structures.

Testing done on the proposed changes

The t2 mass fab is placed here so that it triggers the adjacency for the dummy unit of the fatboy. Using debug logging it shows that the dummy unit is ignored correctly.
You can also trigger the adjacency by having the UEF engineer SACU build any structures.

   CreateUnitAtMouse('uel0301_engineer', 0,    1.93,   -0.48, -0.00000)
   CreateUnitAtMouse('uel0301_engineer', 0,   -2.00,   -0.48, -0.00000)
   CreateUnitAtMouse('uel0301_engineer', 0,   -3.32,   -0.48, -0.00000)
   CreateUnitAtMouse('uel0301_engineer', 0,   -4.84,   -0.48, -0.00000)
   CreateUnitAtMouse('uel0301_engineer', 0,   -0.72,   -0.48, -0.00000)
   CreateUnitAtMouse('uel0301_engineer', 0,    5.19,   -0.48, -0.00000)
   CreateUnitAtMouse('uel0301_engineer', 0,    3.34,   -0.48, -0.00000)
   CreateUnitAtMouse('uel0301_engineer', 0,    0.40,   -0.48, -0.00000)
   CreateUnitAtMouse('ueb1104', 0,   -2.32,    3.52,  0.00000)
   CreateUnitAtMouse('uel0401ef', 0,   -1.49,    2.89,  1.35140)
   CreateUnitAtMouse('uel0401', 0,    3.15,    3.92,  1.35140)
   CreateUnitAtMouse('xab1401', 0,    0.68,   -6.48, -0.00000)

Checklist

Summary by CodeRabbit

  • Bug Fixes

    • Fixed OnAdjacentTo errors involving dummy units attached to external factories and UEF engineering drones.
    • Improved construction pod attachment behavior when connecting to transports.
  • Documentation

    • Clarified unit motion types, motion states, capture behavior, and motion-state callbacks.
    • Documented construction pod guard-dummy behavior.
  • Diagnostics

    • Added improved logging for unit state changes and adjacency checks during construction.

@lL1l1
lL1l1 requested review from Garanas, clyfordv and speed2CZ June 16, 2025 20:27
@lL1l1 lL1l1 added type: bug area: sim Area that is affected by the Simulation of the Game labels Jun 16, 2025
@lL1l1
lL1l1 marked this pull request as ready for review June 16, 2025 20:28
@Garanas

Garanas commented Jun 17, 2025

Copy link
Copy Markdown
Member

Great catch on the missing call to OnCreate.

I do not fully understand the context about UEF construction drones (from kennels), these generate calls to OnAdjacentTo? That would be unexpected, I thought that was a structure-only engine event.

@lL1l1

lL1l1 commented Jun 17, 2025

Copy link
Copy Markdown
Contributor Author

It is, but we have a "guard dummy" unit (zxa0003) which uses the RULEUMT_None motion type and is attached to the drone itself. When the drone finishes construction, the attached dummy unit can be in an ogrid where it is considered adjacent.

The dummy is used to save assist orders onto the drone after a drone docks.

Comment thread lua/sim/units/StructureUnit.lua Outdated
-- make sure we're both finished building
if self:IsBeingBuilt() or adjacentUnit:IsBeingBuilt() then
if self:IsBeingBuilt() or adjacentUnit:IsBeingBuilt()
or adjacentUnit.Blueprint.CategoriesHash["DUMMYUNIT"]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

By the suggested logic the dummy unit should not have adjacency.

Whats the reason to put this check into Structure?

Why not to add OnAdjacentTo OnNotAdjacentTo to the Dummy class that would return?

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.

^Second this suggestion. Keeping things modular means that any future changes/removals don't leave lingering logic in the base class. Additionally, as the original implementer of zxa0003 I'll say that I knew enough to get it working, but not enough to know all the details exhaustively. Somebody with more experience in the unit blueprints could remove the structure-ness of it and address the problem at the source (add a movement type, maybe?).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Why not to add OnAdjacentTo OnNotAdjacentTo to the Dummy class that would return?

The error pops up in the script of the structure that is adjacent. Returning in the dummy class does nothing to prevent the adjacent structure unit script from running.

don't leave lingering logic in the base class

I agree, I'll try to find a different solution.

remove the structure-ness of it and address the problem at the source (add a movement type, maybe?).

I tried other motion types but the engine removes the assist order once the drone is attached to the SACU (and the dummy unit is finally attached indirectly to the SACU).
Fortunately, upon some investigation, it seems related to the "Attached" state of the unit, so we can do self:SetUnitState("Attached", false) in OnAttachedToTransport and then remove all the guard re-assignment behavior, since assist orders no longer get removed.

I haven't yet figured out how to deal with fatboy detaching its external factory when you give the dummy unit a motion type.

@Garanas

Garanas commented Jun 26, 2025

Copy link
Copy Markdown
Member

Now I understand the context more, the guard unit is introduced with: #6176

And it only applies to the construction pods of the (S)ACUs. The drones from Kennels do not use this class. They are 'regular' construction units.

@BlackYps BlackYps removed this from the Development Iteration III of 2025 milestone Aug 15, 2025
@lL1l1
lL1l1 force-pushed the fix-adjacency-error branch from fe1b3c7 to fa75f69 Compare August 4, 2026 01:37
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change documents motion and attachment states, adds diagnostics for adjacency and active unit states, and updates UEF construction pod transport attachment handling. It also adds a changelog entry for the OnAdjacentTo fix.

Changes

Motion and adjacency behavior

Layer / File(s) Summary
Motion state contracts
engine/Core/Blueprints/UnitBlueprint.lua, engine/Core/Categories.lua, engine/Enums.lua, lua/sim/Unit.lua
Documentation now covers motion defaults, attached-unit behavior, motion states, and the OnMotionStateChange callback signature.
Adjacency diagnostics
lua/sim/units/StructureUnit.lua, lua/sim/units/components/DebugUnitComponent.lua, changelog/snippets/fix.6863md
Adjacency callbacks log early returns during construction. DebugActiveStates reports active unit states. The changelog records the related fix.
Construction pod attachment handling
lua/sim/units/uef/TConstructionPodUnit.lua
The construction pod clears its "Attached" state before transport attachment handling. Its guard dummy documentation describes assist-order preservation.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • FAForever/fa#7190: Both changes update MotionType documentation, but the other functionality is unrelated.

Suggested reviewers: garanas, clyfordv, 4z0t

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The shown changes do not demonstrate the required zxa0003 OnCreate assignment or an explicit adjacency skip for dummy units, so issue #6425 is not fully addressed. Add and verify OnCreate for zxa0003 so it assigns self.EntityId, and ensure adjacency ignores that dummy unit before indexing it.
Out of Scope Changes check ⚠️ Warning Several documentation updates and the DebugActiveStates helper appear unrelated to the linked adjacency bug beyond the changelog and targeted diagnostic changes. Remove unrelated documentation and diagnostic changes, or link them to separate issues and keep this pull request focused on #6425.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the adjacency error and its dummy-unit cause, matching the primary pull request objective.
Description check ✅ Passed The description includes the changes, issue reference, testing evidence, commands, and completed checklist; only optional additional context is omitted.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
lua/sim/units/StructureUnit.lua (1)

778-778: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Gate and contextualize the adjacency diagnostics.

OnAdjacentTo and OnNotAdjacentTo call LOG without a debug guard. Construction-heavy games can write one message for every skipped adjacency. The messages also omit both entity IDs and the IsBeingBuilt() result. Use the existing debug logging path, or an equivalent guard, and include the skipped units and condition.

Proposed logging change
-            LOG('OnAdjacentTo return')
+            self:DebugLog(
+                'OnAdjacentTo return; adjacent=' .. tostring(adjacentUnit.EntityId)
+                    .. '; selfBeingBuilt=' .. tostring(self:IsBeingBuilt())
+                    .. '; adjacentBeingBuilt=' .. tostring(adjacentUnit:IsBeingBuilt())
+            )
...
-            LOG('OnNotAdjacentTo return')
+            self:DebugLog(
+                'OnNotAdjacentTo return; adjacent=' .. tostring(adjacentUnit.EntityId)
+                    .. '; selfBeingBuilt=' .. tostring(self:IsBeingBuilt())
+                    .. '; adjacentBeingBuilt=' .. tostring(adjacentUnit:IsBeingBuilt())
+            )

Also applies to: 821-821

🤖 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 `@lua/sim/units/StructureUnit.lua` at line 778, Update the diagnostic LOG calls
in OnAdjacentTo and OnNotAdjacentTo to use the existing debug-logging
guard/path, preventing them from running during normal construction. When
enabled, include both involved entity IDs and each unit’s IsBeingBuilt() result
in the adjacency messages.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@engine/Enums.lua`:
- Line 56: Resolve the `RULEUMT_Special` entry in the alias by either
documenting its intended behavior with a definitive comment and corresponding
repository support, or removing it from the public alias if it is unused and not
part of the contract; do not leave the unresolved “Identical to None?” note.

---

Nitpick comments:
In `@lua/sim/units/StructureUnit.lua`:
- Line 778: Update the diagnostic LOG calls in OnAdjacentTo and OnNotAdjacentTo
to use the existing debug-logging guard/path, preventing them from running
during normal construction. When enabled, include both involved entity IDs and
each unit’s IsBeingBuilt() result in the adjacency messages.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d328a2ad-85f8-4c44-97e7-06d2d2d83909

📥 Commits

Reviewing files that changed from the base of the PR and between 658b338 and fa75f69.

📒 Files selected for processing (8)
  • changelog/snippets/fix.6863md
  • engine/Core/Blueprints/UnitBlueprint.lua
  • engine/Core/Categories.lua
  • engine/Enums.lua
  • lua/sim/Unit.lua
  • lua/sim/units/StructureUnit.lua
  • lua/sim/units/components/DebugUnitComponent.lua
  • lua/sim/units/uef/TConstructionPodUnit.lua

Comment thread engine/Enums.lua
---| "RULEUMT_Hover"
---| "RULEUMT_AmphibiousFloating"
---| "RULEUMT_Special"
---| "RULEUMT_Special" # Identical to "None"?

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C 4 'RULEUMT_(Special|None)' .

Repository: FAForever/fa

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Engine files likely containing RULEUMT handling =="
rg -n -C 3 'case[[:space:]]+UNIT_MOTION_TYPE|UnitMotionType|MotionType|RULEUMT_Special|UNIT_MOTION_TYPE_SPECIAL|UNIT_MOTION_TYPE_NONE' engine lua src include --glob '*.cpp' --glob '*.h' --glob '*.lua' --max-count 80

echo "== Enums.lua surrounding aliases =="
sed -n '40,60p' engine/Enums.lua

Repository: FAForever/fa

Length of output: 17395


🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files | rg '(^|/)([^/]+\.(cpp|h|hpp|cc|c|inc|lua)|[^/]*_unit\.bp)$' | {
  rg -n 'UNIT_MOTION_TYPE|RULEUMT_Special|RULEUMT_None|UnitMotionType|MotionType' || true
} | head -n 200

echo "== Lua direct comparisons =="
python3 - <<'PY'
import subprocess, pathlib, re
files=list(pathlib.Path('.').rglob('*.lua'))
for p in files:
    txt=p.read_text(errors='ignore')
    if re.search(r"['\"]RULEUMT_(Special|None)['\"]", txt):
        for i,line in enumerate(txt.splitlines(),1):
            if re.search(r"['\"]RULEUMT_(Special|None)['\"]", line):
                print(f"{p}:{i}:{line.strip()}")
PY

echo "== Blueprint direct comparisons =="
python3 - <<'PY'
import pathlib, re
for p in pathlib.Path('.').rglob('*_unit.bp'):
    txt=p.read_text(errors='ignore')
    if re.search(r"['\"]RULEUMT_(Special|None)['\"]", txt):
        for i,line in enumerate(txt.splitlines(),1):
            if re.search(r"['\"]RULEUMT_(Special|None)['\"]", line):
                print(f"{p}:{i}:{line.strip()}")
PY

Repository: FAForever/fa

Length of output: 26696


🌐 Web query:

FAForever RULEUMT_Special MotionType RULEUMT_None

💡 Result:

In the context of Forged Alliance Forever (FAF) and the underlying SupCom engine, RULEUMT refers to Unit Motion Types, which define how a unit moves within the game world [1][2]. While there is no standard MotionType named RULEUMT_Special or RULEUMT_None in the base engine, these terms often appear in custom modding contexts or specific blueprint configurations where developers are either defining custom behaviors or attempting to disable movement logic [1][3]. 1. MotionTypes (RULEUMT): The engine uses various constants prefixed with RULEUMT to govern physics movement, such as RULEUMT_Hover, RULEUMT_AmphibiousFloating, RULEUMT_Land, and RULEUMT_Air [2]. 2. RULEUMT_Special: This is not a native engine constant. If encountered, it is likely a custom identifier used by a specific mod to define a unique movement logic handled by custom Lua scripts. 3. RULEUMT_None: Similarly, this is not a native constant. In cases where developers want to indicate a unit has no movement capability (static structures), they typically ensure the MotionType is omitted or set to a type that the physics engine ignores, or they define a custom constant named RULEUMT_None in their local environment to explicitly force a 'no movement' state [1][3]. If you are encountering these terms in a specific mod or file, they are almost certainly custom definitions added by the mod author to override or extend the standard behavior [3]. You can verify this by checking the local Enums.lua or unit blueprint files within that specific mod repository [1][4].

Citations:


Document RULEUMT_Special before exposing it.

RULEUMT_Special is listed in this alias, but no repository blueprint or Lua comparison uses it, and the current comment reads as unresolved. Either describe its intended behavior or remove the alias if it is not part of the public contract.

🤖 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 `@engine/Enums.lua` at line 56, Resolve the `RULEUMT_Special` entry in the
alias by either documenting its intended behavior with a definitive comment and
corresponding repository support, or removing it from the public alias if it is
unused and not part of the contract; do not leave the unresolved “Identical to
None?” note.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: sim Area that is affected by the Simulation of the Game type: bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Frequent OnAdjacentTo error

5 participants