Search Unity

Unity Multiplayer [Solved] Client's list of game object prefabs not shown in the Server. How do I solve this?

Discussion in 'Multiplayer' started by asperatology, Sep 5, 2015.

  1. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    976
    I am using Unity 5.1.3f1, and the new UNET Networking API. I am making a simple RTS game in Unity.

    I used a game object prefab to spawn in via Network Manager's Spawn Info properties in the Inspector. This game object prefab then spawns in a list of game object prefabs, which are managers that manage player actions, for my game.

    The problem is that when the client connects to the server, the server does not see the client's list of game object prefabs. See picture below:



    The left side is the client, and the right side is the server. I am pretty sure I registered all of the list of game object prefabs in the Network Manager. So, I can only isolate the problem down to "client's game objects are not shown in the server."

    This is the Prefab Initialization code:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Networking;
    3. using System;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6. using Common;
    7.  
    8. public class PrefabInitialization : NetworkBehaviour {
    9.     public static PrefabInitialization Instance;
    10.  
    11.     public List<GameObject> gameObjectList;
    12.     public CommonUnitManager unitManager;
    13.     public CommonInputManager inputManager;
    14.     public CommonAttackManager attackManager;
    15.     public CommonMergeManager mergeManager;
    16.     public CommonSplitManager splitManager;
    17.  
    18.     public void Awake() {
    19.         PrefabInitialization.Instance = this;
    20.     }
    21.  
    22.     public override void OnStartLocalPlayer() {
    23.         if (this.isClient) {
    24.             this.SpawningInitialization();
    25.         }
    26.     }
    27.  
    28.     public void SpawningInitialization() {
    29.         for (int i = 0; i < this.gameObjectList.Count; i++) {
    30.             this.gameObjectList[i].SetActive(true);
    31.             SpawnObject(i);
    32.         }
    33.     }
    34.  
    35.     private void SpawnObject(int i) {
    36.         GameObject obj = GameObject.Instantiate(this.gameObjectList[i], Vector3.zero, Quaternion.identity) as GameObject;
    37.         obj.name = obj.name.Substring(0, obj.name.Length - "(Clone)".Length);
    38.         if (obj.tag.Equals("Input_Manager")) {
    39.             this.inputManager = obj.GetComponent<CommonInputManager>();
    40.             this.inputManager.SetUnitManager(this.unitManager);
    41.             this.inputManager.SetMergeManager(this.mergeManager);
    42.             this.inputManager.SetAttackManager(this.attackManager);
    43.             this.inputManager.SetSplitManager(this.splitManager);
    44.         }
    45.         else if (obj.tag.Equals("Merge_Manager")) {
    46.             this.mergeManager = obj.GetComponent<CommonMergeManager>();
    47.             this.mergeManager.unitManager = this.unitManager;
    48.         }
    49.         else if (obj.tag.Equals("Split_Manager")) {
    50.             this.splitManager = obj.GetComponent<CommonSplitManager>();
    51.         }
    52.         else if (obj.tag.Equals("Attack_Manager")) {
    53.             this.attackManager = obj.GetComponent<CommonAttackManager>();
    54.         }
    55.         else if (obj.tag.Equals("Unit_Manager")) {
    56.             this.unitManager = obj.GetComponent<CommonUnitManager>();
    57.         }
    58.         else if (obj.tag.Equals("RPCLess_Unit")) {
    59.             CommonUnit commonUnit = obj.GetComponent<CommonUnit>();
    60.             commonUnit.transform.position = this.transform.position;
    61.             commonUnit.unitManager = this.unitManager;
    62.             if (!this.unitManager.getAllObjects().Contains(obj)) {
    63.                 this.unitManager.getAllObjects().Add(obj);
    64.             }
    65.         }
    66.         NetworkServer.Spawn(obj);
    67.     }
    68. }
    69.  
    What am I missing? Thanks in advance.
     
  2. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    976
    SSCCE version. I can reproduce this result using a more simpler build and simpler C# code. Every game object is completely primitive. Only the "Spawner" prefab has the code given below attached as a script component.

    Below image shows the same scenario as the one I have above. The "Spawner" prefab has unsuccessfully spawned a "Sphere" game object for the server, but successfully spawned a "Sphere" game object for the client.



    Note: I don't know what happened to the name of the "Sphere" game object. After connecting as server and client, both of the "Sphere" prefab names in the editors have "(Server)" and "(Client)" added to them. Shouldn't cause much problems though, I believe.

    Below image shows what I have set in the Inspector for both the Network Manager (left) and the Spawner prefab (right). The "Sphere" prefab is just a generic "Create 3D > Sphere" game object in the scene.



    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Networking;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class Spawner : NetworkBehaviour {
    7.     public List<GameObject> prefabsToSpawn;
    8.  
    9.     public override void OnStartLocalPlayer() {
    10.         for (int i = 0; i < this.prefabsToSpawn.Count; i++) {
    11.             this.CmdSpawnObject(i);
    12.         }
    13.     }
    14.  
    15.     [Command]
    16.     public void CmdSpawnObject(int i) {
    17.         this.RpcSpawnObject(i);
    18.     }
    19.  
    20.     [ClientRpc]
    21.     public void RpcSpawnObject(int i) {
    22.         GameObject obj = this.prefabsToSpawn[i];
    23.         if (this.isServer) {
    24.             obj.name = "(Server) " + obj.name;
    25.         }
    26.         else if (this.isClient) {
    27.             obj.name = "(Client) " + obj.name;
    28.         }
    29.         NetworkServer.Spawn(obj);
    30.     }
    31. }
    32.  
    Once I figured out how to fix this, then I will be able to understand how to work with the new Networking API.

    I thought that [ClientRpc] is executed on the Server. When the Server executes "NetworkServer.Spawn()", it should spawn game objects on the network for both the client and the server. Was I wrong? Been reading the docs again and again, maybe it hasn't clicked yet.
     
  3. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    976
    Updated RpcSpawnObject():

    Code (CSharp):
    1.     [ClientRpc]
    2.     public void RpcSpawnObject(int i) {
    3.         GameObject obj = GameObject.Instantiate(this.prefabsToSpawn[i]) as GameObject;
    4.         if (this.isServer) {
    5.             obj.name = "(Server) " + obj.name;
    6.         }
    7.         else if (this.isClient) {
    8.             obj.name = "(Client) " + obj.name;
    9.         }
    10.         NetworkServer.Spawn(obj);
    11.     }
    If I tried this, the client has 1 more Sphere game object than the server. (Left: Server, Right: Client)

     
    Last edited: Sep 5, 2015
  4. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    976
    Alright, I found something. It may or may not be the solution for this.

    Context:

    Since this is an RTS game, my main intention is to have a "Spawner" prefab that initializes a game object prefab (Unit) at where the Network Start Location is located for all players. Because there is no known way of using Network Start Locations through code, I can only use Network Manager to help me out. This "Spawner" prefab is then used as a way to fetch the Network Start Locations' starting location and pass the Transforms and Positions to the (Unit) prefab. I wanted to find a way how to set it up so that when the players join the multiplayer session, the game will automatically set their start locations to wherever it was set to in the map. I don't know how to do it through code, hence I made a compromise.

    Description:

    In order to spawn in prefabs that are referenced by 1 main prefab, which is handled by Network Manager in the "Spawn Info", and that these spawned prefabs need to have the servers and the clients see them, they first need to be registered to the ClientScene before calling on NetworkServer.Spawn() on them.

    Here's a short code:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Networking;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System;
    6.  
    7. public class Spawner : NetworkBehaviour {
    8.     public List<GameObject> prefabsToSpawn;
    9.  
    10.     public override void OnStartLocalPlayer() {
    11.         CmdTest();
    12.     }
    13.  
    14.     [Command]
    15.     public void CmdTest() {
    16.         for (int i = 0; i < this.prefabsToSpawn.Count; i++) {
    17.             GameObject obj = NetworkBehaviour.Instantiate(this.prefabsToSpawn[i]);
    18.             ClientScene.RegisterPrefab(obj);
    19.             obj.transform.position = this.transform.position;
    20.             NetworkServer.Spawn(obj);
    21.         }
    22.     }
    23. }
    24.  

    Pros
    :
    • Allows the main prefab to initialize all prefabs the game requires.
    • Same or similar code can be used to initialize all prefabs dynamically.
    Cons:
    • You need to set up a list of prefabs that the game is going to use.
    • Servers and clients both do not have authority on all of these dynamically spawned prefabs.

    I cannot think of any other ways to fix this. I feel UNET itself is still unusable at this stage for real time strategy genre games. I don't know what Unity 5.2 will bring, but for now, I really don't know where to go from here.
     
    Bioinformatizer likes this.
unityunity