Clientside parenting smoothing issue (Netcode)

I’m working on a feature where players are able to pick up a dolly and move boxes around with it.

This works fine as a host:
host

But has a very delayed feel as a client:
client

This is the code I’m using on the dolly object.

[ServerRpc(RequireOwnership = false)]
private void UseServerRpc(ServerRpcParams rpcParams = default)
{
    if (IsBeingUsed)
    {
        Debug.LogWarning($"Player {_player.Value.AsPlayer()} tried to use the dolly, but it is already being used by {_player.Value.AsPlayer()}");
        return;
    }

    var player = rpcParams.Receive.GetPlayerController();
    _player.Value = player.gameObject;
    NetworkObject.ChangeOwnership(player.NetworkClientId);
    NetworkObject.TrySetParent(player.gameObject);
    GetComponent<Rigidbody>().isKinematic = true;
    transform.SetPositionAndRotation(player.DollyTransform.position, player.DollyTransform.rotation);
}

[ServerRpc]
private void StopUsingServerRpc(ServerRpcParams rpcParams = default)
{
    if (!IsBeingUsed)
    {
        Debug.LogWarning($"Player {_player.Value.AsPlayer()} tried to stop using the dolly, but it is not being used");
        return;
    }

    _player.MakeInvalid();
    GetComponent<Rigidbody>().isKinematic = false;
    NetworkObject.TryRemoveParent();
}

Would love to hear what I’m doing wrong, and/or even hearing alternative approaches on how to do this :slight_smile:

I bet you have the Interpolation checkbox enabled on NetworkTransform. Turn that off!

Interpolation is terrible. To be honest, this should be hidden away and locked deep down inside the framework. The effect that interpolation has is that everything on the client side moves smoothly, but it will do so only with additional latency.

Basically this happens:

  • object starts at X = 0
  • Client gets new object position at X = 10
  • Client starts interpolating the movement from 0 to 10 over time
  • a couple frames later, object is at X = 10 …
  • … or not because client got a new position and started interpolating towards that

Interpolation is the opposite effect that you want to have in such a situation. If disabled interpolation feels too jerky, try increasing your network tick rate (caution: this will also increase traffic and server load). Default is 30 Hz, 60 Hz should feel fine, 120 Hz is about the most I would try.

The better solution would be transferring ownership of that cart to the player who currently grabs it. Prior to NGO 2.0 you would also have use a NetworkTransform subclass where the IsAuthority property returns IsOwner, so only the owner has authority and is allowed to update the cart’s position.

By doing that, every time someone grabs the cart it is updated instantly for the handler and everyone else gets these position updates from that client.

Or you could do the simple trick and not make the cart a separate item that follows the player. Instead, the standalone networked cart gets hidden, and one that’s fixed to the player’s hands appears on the client (not networked).

Every client gets a corresponding RPC that instructs them to enable the fixed cart for that player. The cart then moves along with the player and you spare yourself the extra NetworkTransform traffic.

If player let’s go of the cart, you teleport the standalone cart to the new position, make it visible again and send an RPC to hide the fixed cart on the player.

Basically you are swapping objects. Perhaps even the standalone cart needn’t be networked either if you trust the clients.

I’d go with the swap option since that’s cleanest, most reliable, and doesn’t send extra traffic.

Which is what I am doing, as per code above. At least thats what I THINK it does.:face_with_head_bandage:

EDIT: Okay turns out I’m on 1.10 for some reason. I guess that is my issue then, thanks.

Yea I’m actually doing that approach somewhere else. Based on an answer you gave in another thread. :stuck_out_tongue:

For me that was having a system for the player to be able to carry boxes around. I do dislike this tho because of the extra “boilerplate” dev work required:

  • A prefab for the box visuals
  • A prefab for the server, containing an instance of the visuals, aswell as server scripts, which hold some NetworkVariables
  • A prefab for the client, also containing an instance of the visuals, aswell was as a client script which has some pickup logic.

So basically, for having 1 object, with synced data, support being carried, I have to implement 2 classes and 3 prefabs and connect them together carefully.

Typing this out makes me think I’m massively overthinking something here…

I’d love if you could point me into some direction on this issue aswell :smiley:

Offtopic:
I actually tried looking for a place to tip you for your great assistance, which you gave me multiple times indirectly without knowing (given you’re all over the netcode forum), but you don’t seem to find any place to do that …

Ouch :smiley:
1.9 is the most current, you can also try 2.0 since it’s close to going out of preview. It adds distributed authority above all else but also some convenience features.

I’m never shy to repeat myself. :smiley:

Quite the opposite. Pulling it apart will make it easier to develop and debug individual features.

My network player prefab used to look like this:

It was painful to add support for more camera perspectives, different controller behaviours, changing avatars and so on.

Now my network player prefab looks like this:

image

Everything else gets instantiated into the player instance on demand. None of the additional aspects are networked. They are all driven by the base player components which pick up data to synchronize and inform the subsystems via events or interfaces. For a mode switch, I just activate/deactivate objects (Camera, Controller) or instantiate a new one and destroy the previous one (Avatar).

Many of the components no longer reside in the player at all. Looks like this at runtime:

Now I have all the cameras in one place so I can see at a glance what’s going on.

The main separation between server/client is just two scripts which host the client and server side RPCs. I want to avoid putting RPCs in any other script. The callstack goes like this: when I want to spawn a player I call m_Client.SpawnPlayer() which then calls m_Server.SpawnPlayerServerRPC() so there’s an additional routing but it makes separation of concerns clearer. All RPCs in the server script are server RPCs, all RPCs in the client script are client RPCs. And the player’s components must only call methods in the client script.

I actually tried looking for a place to tip you for your great assistance, which you gave me multiple times indirectly without knowing (given you’re all over the netcode forum), but you don’t seem to find any place to do that …

Thank you, I appreciate that! :slight_smile:
There is no such place yet. Closest is my Patreon which only has a free tier for now. For whatever reason I thought it’ll be good to not add a paid tier until I have 100+ subscribers. For now, just spread the word. :hugs:

So am I understanding correctly: You’re still using a singular prefab, but are enabling/disabling features depending on if they’re created on the server or client?

If so, what is responsible for doing that? The scripts themselfs? That would not work with third party components. :thinking:

It’s a single prefab. The components like PlayerAvatar then spawn an Avatar based on the already available index. This instantiates a prefab provided by a ScriptableObject which has a list of possible Avatar prefabs. So the list and types of Avatars can be changed at any time with any Avatar.

Same for character controllers, except each controller gets instantiated but only one is SetActive. You could have a custom 3rd party controller provided that it doesn’t strictly operate on the platform it is on - which I bet most do. In such a case you’d have to have a secondary component on the controller child object that simply applies the child components transform values to the player’s root. Just a bit of extra work.

If the 3rd party asset takes control of the whole thing, eg the object’s collider and physics and camera and possibly even animations, it wouldn’t be compatible. But it likely wouldn’t be networkable either if its such a monolithic thing.

I very much prefer modular controllers like this one.