Full analysis committed as docs/entity-wiring-and-networking.md on live-ebx. This issue tracks the three High-severity gaps from it; G4-G11 are in the doc.
Findings are grounded in the Venice-EBX dumps, with verified vs inferred kept separate in the doc.
G1 — Level-bus connections are never authored (the headline)
Frostbite splits gameplay wiring in two:
- Inside the prefab —
CapturePointPrefab carries an InterfaceDescriptorData exposing CaptureRadius, OnCaptured/OnLost etc., plus internal PropertyConnections. This ships in the blueprint, so CreateEntitiesFromBlueprint rebuilds it for free.
- In the level/subworld bus — the connections that make a flag a Conquest flag.
Levels/MP_001/Conquest.txt (SubWorldData 539342BE-…) holds 49 PropertyConnections + 17+ EventConnections referencing the flag RODs (CQ_logic/8C5AF081-…, CQ_logic_US/10A70CA7-…). MP_001.txt (LevelData) has more, cross-partition into the world parts.
MapEditor only ever creates the object: LevelInjector:CreateWorldParts builds a WorldPartData() with only objects populated (no propertyConnections/linkConnections/eventConnections/descriptor), and InvokeBlueprintSpawn wires the new bus into nothing.
Result: spawn a CapturePointPrefab / vehicle spawner / MCOM and it renders, its internal wiring works, and the game mode never sees it. Flags never capture, spawners never spawn, MCOMs never arm. Note this is not addressed by parentRepresentative (#202) — that makes per-instance data unique, it doesn't wire anything into the level bus.
Also relevant: the networking realm of an event is carried on the connection (EventConnectionTargetType: Client / Server / NetworkedClient / …), not on the object.
G2 — Editing a networked object desyncs it
Any EBX edit re-instantiates via InvokeBlueprintSpawnFromClone, which hardcodes networked = false and runs client-only. CapturePointPrefab is NeedNetworkId True (verified). So the client ends up rendering a local non-networked copy while the server still owns the real entity.
networked can't simply be restored: the clone DC isn't registered in ResourceManager, so a networked spawn hands the peer an unresolvable blueprint guid and crashes it — which is exactly why it's false. Options: (a) keep client-only preview and re-sync authoritatively on save, (b) register the clone so peers can resolve it, (c) skip re-instantiation for needNetworkId objects and fall back to Disable/Enable. Needs a design decision.
G3 — Entity buses leak on every edit
GameObject:Destroy calls Disable() on its entities; GameEntity:Destroy has zero callers. Every (debounced) re-instantiation therefore abandons an entity bus.
Constraint that must survive any fix: destroying a vanilla entity crashes the game. That is why the code disables rather than destroys. It is also subtler than it looks — a vanilla object is re-registered as Custom after its first EBX edit, so from then on its entities descend from level data. A fix has to destroy only entities we created via CreateEntitiesFromBlueprint, e.g. by tagging them in the create hook. See §4.1 of the doc.
Useful adjacent finding: the field-id hash -> name table needed to make connections human-readable (838548383 -> Geometry, 2099208964 -> OnCaptured) is already shipped in data.zip as EventHashes.json and simply discarded at load — see #388.
Full analysis committed as
docs/entity-wiring-and-networking.mdonlive-ebx. This issue tracks the three High-severity gaps from it; G4-G11 are in the doc.Findings are grounded in the Venice-EBX dumps, with verified vs inferred kept separate in the doc.
G1 — Level-bus connections are never authored (the headline)
Frostbite splits gameplay wiring in two:
CapturePointPrefabcarries anInterfaceDescriptorDataexposingCaptureRadius,OnCaptured/OnLostetc., plus internal PropertyConnections. This ships in the blueprint, soCreateEntitiesFromBlueprintrebuilds it for free.Levels/MP_001/Conquest.txt(SubWorldData 539342BE-…) holds 49 PropertyConnections + 17+ EventConnections referencing the flag RODs (CQ_logic/8C5AF081-…,CQ_logic_US/10A70CA7-…).MP_001.txt(LevelData) has more, cross-partition into the world parts.MapEditor only ever creates the object:
LevelInjector:CreateWorldPartsbuilds aWorldPartData()with onlyobjectspopulated (nopropertyConnections/linkConnections/eventConnections/descriptor), andInvokeBlueprintSpawnwires the new bus into nothing.Result: spawn a CapturePointPrefab / vehicle spawner / MCOM and it renders, its internal wiring works, and the game mode never sees it. Flags never capture, spawners never spawn, MCOMs never arm. Note this is not addressed by
parentRepresentative(#202) — that makes per-instance data unique, it doesn't wire anything into the level bus.Also relevant: the networking realm of an event is carried on the connection (
EventConnectionTargetType:Client/Server/NetworkedClient/ …), not on the object.G2 — Editing a networked object desyncs it
Any EBX edit re-instantiates via
InvokeBlueprintSpawnFromClone, which hardcodesnetworked = falseand runs client-only.CapturePointPrefabisNeedNetworkId True(verified). So the client ends up rendering a local non-networked copy while the server still owns the real entity.networkedcan't simply be restored: the clone DC isn't registered inResourceManager, so a networked spawn hands the peer an unresolvable blueprint guid and crashes it — which is exactly why it'sfalse. Options: (a) keep client-only preview and re-sync authoritatively on save, (b) register the clone so peers can resolve it, (c) skip re-instantiation forneedNetworkIdobjects and fall back toDisable/Enable. Needs a design decision.G3 — Entity buses leak on every edit
GameObject:DestroycallsDisable()on its entities;GameEntity:Destroyhas zero callers. Every (debounced) re-instantiation therefore abandons an entity bus.Constraint that must survive any fix: destroying a vanilla entity crashes the game. That is why the code disables rather than destroys. It is also subtler than it looks — a vanilla object is re-registered as
Customafter its first EBX edit, so from then on its entities descend from level data. A fix has to destroy only entities we created viaCreateEntitiesFromBlueprint, e.g. by tagging them in the create hook. See §4.1 of the doc.Useful adjacent finding: the field-id hash -> name table needed to make connections human-readable (
838548383->Geometry,2099208964->OnCaptured) is already shipped indata.zipasEventHashes.jsonand simply discarded at load — see #388.