Skip to content

Request: Entity attachment in replication (like FiveM's AttachEntityToEntity) #310

Description

@Kheartz

Games keep needing "entity B rides on entity A at a fixed offset": a passenger in a car, a cuffed player escorted by an officer, a carried object. The Framework has no concept of this, so each game builds its own. The obvious version (send the child's position like any other entity) can't stay in sync while they move.

What exists today

GetInterestDependency() covers the streaming half: a child pulls its parent into the viewer's interest set.

// --- Game extension points ---
// Server interest: an entity this one makes no sense without (a seated ped needs its car).
// Survivors of a per-type budget pull it in with them, uncounted. Followed one level.
virtual NetworkEntity *GetInterestDependency() {
return nullptr;
}

Everything else is up to each game: where the relationship is stored, whether the child's own transform is still sent, and what happens when the parent goes away.

Why sending the child's position doesn't work

In Hogwarts Legacy MP we hold an escorted player next to the officer. The server clamps the prisoner's streamed position near the officer, and the prisoner's own client attaches its pawn to its local copy of the officer. On the prisoner's screen it looks right. On everyone else's screen the prisoner trails the officer and swings wide in turns.

Tuning can't fix this. Observers draw the prisoner from the prisoner's streamed transform, which is:

  1. computed from the prisoner client's copy of the officer, which is already one relay old (officer → server → prisoner), then
  2. relayed a second time (prisoner → server → observer).

So observers get two separate transform streams sampled at different times, and the child arrives about one relay behind its parent. No clamping or smoothing makes two separately sampled streams line up in a turn.

How FiveM does it

FiveM replicates the relationship, not the child's position. CPhysicalAttachDataNode carries the parent id, an offset, an orientation and bone indices:

https://github.com/citizenfx/fivem/blob/ab3ee4506333e02e4beb847c3db5467ae263ee6e/code/components/citizen-server-impl/include/state/SyncTrees_Five.h#L587-L646

Every client attaches the child to its own copy of the parent and works out the child's pose each frame. The parent may lag on a given screen, but the child is placed from that same lagged copy, so the pair always moves as one body. That's why GTA RP escorts stay glued together through turns.

Suggested design

Attachment as server-written state on NetworkEntity, applied by the game on every client:

  • State: { parent NetworkID, offset vec3, rotation quat, socket string } as ServerFields, sent on construction (for late joiners) and as a delta when it changes. The Framework never interprets socket; it's the game's bone or socket name.
  • Streaming: the default GetInterestDependency() returns the attachment parent, so games don't need to override it for this case.
  • Transform: while attached, the server ignores the owner's upstream transform and stores parent pose × offset as the child's position, so server-side range, interest and proximity checks stay correct. Observers don't interpolate the child.
  • Game hook: a client virtual such as OnAttachmentChanged(), where the game performs the real attach (UE AttachToActor, a vehicle seat native, …). If the parent isn't constructed on that client yet, the Framework defers the call until it is.
  • Lifecycle: detach automatically when the parent is destroyed; refuse cycles (with a depth cap) at attach time.
  • Scripting: entity.attachTo(parent, { offset, rotation, socket }), entity.detach(), entity.getAttachment(). Server-only in v1, so a client can't attach itself to another player or slip out of cuffs.

Open questions

  • Should an owner be able to attach its own entity? FiveM allows it, and some cases are naturally owner-initiated, such as a player getting into a car. This could come later behind the delegation policy.
  • Is a rigid attach enough for v1, or do we also need a "soft" mode where the child keeps its own transform relative to the parent (walking on a moving platform)?

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions