Search Unity

SyncVar Being set after OnDeserialize (How do I get around this issue?)

Discussion in 'Multiplayer' started by Jackk_, May 25, 2019.

  1. Jackk_

    Jackk_

    Joined:
    May 2, 2017
    Posts:
    49
    Context of the game I am making:
    I am creating a multiplayer 3d style pokemon game in which the player has a moveable character which he can catch pokemon with and use them for realtime battles (essentially the player needs to instantiate a pokemon prefab which the local client then has control over on the network) Similar project for reference:
    .

    Current Functionality:
    I successfully have players which have movement and inventories synced to the network, in the inventories they have a pokemon item slot, which on clicked calls the following function from the script PokemonItem.cs which instantiates the pokemon prefab assigned to the inventory slot and changes the values such as ownerID (the network identity attached to the player's gameobject, and owner (the player gameobject);


    Code (CSharp):
    1. public override void Use(PlayerInventory inventory, int inventoryIndex)
    2.     {
    3.         SpawnPokemon(inventory.gameObject);
    4.     }
    5.  
    6. void SpawnPokemon(GameObject player)
    7.     {
    8.         GameObject pokemon_go = (GameObject)Instantiate(pokemonPrefab.gameObject, player.transform.position, Quaternion.identity);
    9.         Pokemon pokemon = pokemon_go.GetComponent<Pokemon>();
    10.         pokemon.ownerID = player.GetComponent<NetworkIdentity>();
    11.         pokemon.owner = player.GetComponent<PlayerMovement>();
    12.         NetworkServer.SpawnWithClientAuthority(pokemon_go, player.GetComponent<NetworkIdentity>().connectionToClient);
    13.         if (player.GetComponent<PlayerMovement>().activePokemon == null)
    14.         {
    15.             player.GetComponent<PlayerMovement>().CmdSetPlayerActivePokemon(pokemon_go);
    16.         }
    17.     }
    The variables of the pokemon instance "ownerID" and "owner" are [SyncVar] variables whose values would be assigned and shown in the inspector (with the unity engine running as the host machine) however these SyncVar variables would not be shown when the unity engine was running as a client machine (with a unity build as the host). After searching around on google I found this as a solution:

    The Following code is from the Pokemon.cs script (Deriving from NetworkBehaviour and also having local player authority set to true (only way I could get the pokemon to send commands due to them not being player objects))

    Code (CSharp):
    1. public void Start()
    2.     {
    3.         CmdUpdateOwnerVar(_owner);
    4.     }
    5.  
    6.     [Command]
    7.     void CmdUpdateOwnerVar(GameObject pokemonOwner)
    8.     {
    9.         _owner = pokemonOwner;
    10.         ownerID = pokemonOwner.GetComponent<NetworkIdentity>();
    11.         RpcTellAllClientsToUpdatePokemonOwnerVar(pokemonOwner, ownerID);
    12.     }
    13.  
    14.     [ClientRpc]
    15.     void RpcTellAllClientsToUpdatePokemonOwnerVar(GameObject pokemonOwner, NetworkIdentity pokemonOwnerId)
    16.     {
    17.         _owner = pokemonOwner;
    18.         ownerID = pokemonOwnerId;
    19.     }
    The Problem:
    Now this code DOES work as a solution for variables, it updates the values of the network spawned pokemon game object both on the host and all client machines, however when it comes to registering movement, it only seems to work on the host machine, (pokemon can only move on the host machine but fails to do so on the client) which I think is because the OnDeserialize function relies on the value of ownerID to not be null so that it can check if the owner is the local player, and since the OnDeserialize function is being called before the Start function, I get this error: OnDeserialize didn't read the full 1 bytes for object:Ivysaur(Clone) component=Pokemon sceneId=0. Make sure that OnSerialize and OnDeserialize write/read the same amount of data in all cases.
    this has been CONFIRMED as whats causing the error through breakpoints that the value of ownerID is null during the first call of OnDeserialize. But ALSO it is entirely possible that this isn't affecting why the pokemon can't move on the clients, as I am not entirely sure if I need to put the controller.move function in a command (see pastebin link of the pokemon.cs script)

    On Deserialize Function:

    Code (CSharp):
    1. // client-side deserialization
    2.  
    3.     public override void OnDeserialize(NetworkReader reader, bool initialState)
    4.     {
    5.         if (ownerID == null) Debug.LogWarning("ON DESERIALIZE OWNER ID HAS NOT YET BEEN SET");
    6.         // always read so we don't mess up the stream.
    7.         // only assign if not local player (local player has authority over it)
    8.         // => essentially works like a [SyncVar] that ignores local player
    9.         PokemonMoveState readState = (PokemonMoveState)reader.ReadByte();
    10.         if (!ownerID.isLocalPlayer) state = readState;
    11.        
    12.     }
    For the complete Pokemon.cs script: https://pastebin.com/Gwnk00XN (update function is where the movement occurs)

    I thought of changing the start function to an Awake() function but the SyncVar values have yet to be assigned at that point during the script so ownerID is still null.

    If anyone could help me work out how I can sync pokemon movement across clients that would be great!

    If you wish to know anything further about the structure of the game as to get a better understanding as to how to solve this issue then feel free to ask!
     
    Last edited: May 26, 2019