Facepunch / sbox-issues

175 stars 12 forks source link

Joining as a `Player` does not load components in the same order as the `Host` #5443

Closed PolSpock closed 6 months ago

PolSpock commented 6 months ago

Describe the bug

Hi,

I'm still continuing my investigation regarding component order, etc., and i discovered that a player who joins a session will not have the same component order as the host

Let's go through the reproduction steps:

To Reproduce

1) Download https://github.com/Facepunch/sbox-scenestaging 2) Create 3 components: OrderOneComponent & OrderTwoComponent & OrderThreeComponent Add a simple log to each of them:

protected override void OnStart()
{
    base.OnStart();
    Log.Info( GetType().Name + " OnStart (" + Network.OwnerConnection.DisplayName + ") : IsProxy " + IsProxy );
}

edit : same issue with OnAwake or OnEnabled 3) Replace the OnActive function in GameNetworkManage.cs with:

public void OnActive( Connection channel )
{
    Log.Info( "OnActive" );
    var player = new GameObject();
    player.Components.Create<NetworkTest>();

    var body = new GameObject();
    body.SetParent( player );

    var bodySkinnedModelRenderer = body.Components.Create<SkinnedModelRenderer>();
    bodySkinnedModelRenderer.Model = Model.Load( "models/citizen/citizen.vmdl" );
    body.Components.Create<OrderOneComponent>();

    var characterController = player.Components.Create<CharacterController>();
    var playerController = player.Components.Create<PlayerController>();
    playerController.Body = body;

    var citizenAnimationHelper = player.Components.Create<CitizenAnimationHelper>();
    citizenAnimationHelper.Target = bodySkinnedModelRenderer;

    player.Components.Create<OrderTwoComponent>();
    player.Components.Create<OrderThreeComponent>();

    playerController.AnimationHelper = citizenAnimationHelper;

    player.NetworkSpawn( channel );
}

4) Start the game, and join with another player Open the consoles and observe that the component loading differs between the host and the player

As you can see, the OrderOneComponent is not loaded in the same order for the player cause it's attached to a sub-GameObject called Body. But why does this differ from the Host?

Expected behavior

The same order

Media/Files

Reproduction steps in video

https://github.com/Facepunch/sbox-issues/assets/5229571/73ab2ad1-7f09-430d-a728-d762dcdc0363

Additional context

No response

kurozael commented 6 months ago

This kind of makes sense. When you create the object and components there, you are creating the components in a specific order. So the callbacks will be made in that order.

When a client receives a snapshot of a GameObject, it receives the entire serialized JSON of the GameObject - it cannot know what order Components.Create was called.

It'll almost never be the same. When a GameObject is deserialized these callbacks would be called in the order of creation of GameObjects in the tree. For example, it's gonna create the components on the root first, then the children - that's why OrderOneComponent is called last because it's a child of the root at that point.

This is actually also what you want, I think, because in any callback for a component you'd like to expect any components in the parent have been initialized first.

PolSpock commented 6 months ago

So it's normal! Thanks for the clarification