[UNet] How to initialize non-local player?

I’d like to initialize differently local and other clients’ players. And apparently there is no easy way to do it.

For local player I just override OnStartLocalPlayer and everything is cool. But what about players from network? For example if I want a different instantiate animation for local players and network players? Where should I put “StartAwesomeNetworkBzzzztAnimation()”?

I cannot do it in OnStartClient because the isLocalPlayer flag is not set yet.

I think I more or less solved this by using the low level api to send a message to the server any time OnStartClient is called. The server listens for the message and sends a ClientRpc back that calls OnStartNonLocalPlayer:

In my Player script:

public override void OnStartClient()
{
  networkManager.client.Send(MsgType.RequestNonLocalPlayerInit, new NetIDMessage(netId));
}

[ClientRpc]
public void RpcStartNonOwner() 
{
  if (!isLocalPlayer)
  {
      OnStartNonOwner();
  }
}

public void OnStartNonOwner()
{
  // Do the thing
}

And then in my NetworkManager

public override void OnMatchCreate(CreateMatchResponse matchResponse)
{
  base.OnMatchCreate(matchResponse);
  if (matchResponse.success)
  {
    NetworkServer.RegisterHandler(MsgType.RequestNonLocalPlayerInit, OnRequestNonLocalPlayerInit);
  }
}

public void OnRequestNonLocalPlayerInit(NetworkMessage msg)
{
  NetIDMessage idMsg = msg.ReadMessage<NetIDMessage>();
  GameObject player = ClientScene.FindLocalObject(idMsg.netId);
  player.GetComponent<PlayerNetworked>().RpcStartNonOwner();
}

I think this may be a better solution than the one @seanr posted because it handles cases with more than 2 players. @seanr’s solution won’t work since ClientRpc’s aren’t buffered. When the first client connects to the server, the player spawns and the rpc is sent, but when a second client connects they will never receive the rpc to initialize the first non-local player since it was sent before they connected.

There is no built-in hook for that. The best solution is probable to invoke a ClientRpc after the player object is spawned, and run the instantiate animation in that handler - but NOT if isLocalPlayer is set.

I solved the problem by initializing a generic network player inside of OnStartClient() and then overriding the local player settings inside of onStartLocalplayer(), but I think this is also not a great workaround.

These are my solutions:

  1. In case you just want a call to non local players without receiving info from the actual localplayer:

    void Start()
    {
    if (!isLocalPlayer)
    InitNonLocalPlayer();
    }

    void InitNonLocalPlayer(){}

  2. In case you need information about the actual LocalPlayer on the nonLocalPlayers:

    void OnStartLocalPlayer()
    {
    CmdInfoToNonLocalPlayers();
    }

    [Command]
    void CmdInfoToNonLocalPlayers()
    {
    RpcInfoToNonLocalPlayers()
    }

    [ClientRpc]
    void RpcInfoToNonLocalPlayers()
    {
    if (isLocalPlayer)
    return;

    // Do what you need to do on nonLocalPlayers with info
    }

I think you can make the assumption OnStartLocalPlayer will happen fairly quickly after OnStartClient.
If you do that you can use coroutines.

private bool isAllSet = false

public override void OnStartLocalPlayer()
{
//reset parameters for local player
}
public override void OnStartClient()
{
//Assume that this is going to be a remote machine
//Set parameters for remote
StartCoroutine(AllSetup());
}

private IEnumerator AllSetup()
{
    //Need to wait to see if the local player function triggers
   yield return new WaitForSecodns(1); //or some other time.
   //Now determine if you are local player or not.
        if (!isLocalPlayer)
         {
            //Local Player Animation
         }
     else
     {
         //Remote Player Animation
       }
}

Also I think you could use async if you are using C# 5. However, I would have to play around with that one for a bit.