Search Unity

[SOLVED] Tackling the issue of spawning non-player objects from non-player objects.

Discussion in 'Multiplayer' started by asperatology, Oct 2, 2015.

  1. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    I plan to have players spawn non-player objects from other non-player objects into the scene. The issue is that non-player objects do not have Client Authority Owners or specific Connection To Client or Connect to Server.

    Code (CSharp):
    1.     private void AddingNewSplitGroup() {
    2.         foreach (GameObject obj in this.selectionManager.selectedObjects) {
    3.             NetworkIdentity identity = obj.GetComponent<NetworkIdentity>();
    4.             Debug.Log("Calling on CmdSplit()");
    5.             CmdSplit(identity);
    6.         }
    7.     }
    8.  
    9.     [Command]
    10.     public void CmdSplit(NetworkIdentity obj) {
    11.         Debug.Log("It's being called...");
    12.  
    13.         //Just double-checking.
    14.         if (!this.localPlayerAuthority) {
    15.             return;
    16.         }
    17.         RpcSplit(obj.GetComponent<NetworkIdentity>());
    18.     }
    19.  
    20.     [ClientRpc]
    21.     [ServerCallback]
    22.     public void RpcSplit(NetworkIdentity owner) {
    23.         Debug.Log("Calling on RpcSplit()");
    24.  
    25.         if (!this.localPlayerAuthority) {
    26.             return;
    27.         }
    28.  
    29.         if (!this.hasAuthority) {
    30.             Debug.Log("No authority");
    31.             return;
    32.         }
    33.  
    34.         //When spawning non-player objects from non-player objects, the spawned objects must be assigned a client authority owner
    35.         //through NetworkIdentity. I don't think the order matters here...
    36.         GameObject mainObj = owner.gameObject;
    37.         GameObject splitObj = MonoBehaviour.Instantiate(mainObj) as GameObject;
    38.         NetworkIdentity splitIdentity = splitObj.GetComponent<NetworkIdentity>();
    39.         NetworkServer.SpawnWithClientAuthority(splitObj, owner.clientAuthorityOwner);
    40.  
    41.         //if (owner.clientAuthorityOwner != null) {
    42.         //    owner.AssignClientAuthority(owner.clientAuthorityOwner);
    43.         //    splitIdentity.AssignClientAuthority(owner.clientAuthorityOwner);
    44.         //}
    45.  
    46.         if (splitIdentity != null && owner != null) {
    47.             Debug.Log("Adding new split group. Owner: " + owner.ToString() + " Split: " + splitIdentity.ToString());
    48.             this.splitGroupList.Add(new SplitGroup(owner, splitIdentity, owner.clientAuthorityOwner));
    49.         }
    50.     }
    The code is not cleaned up because I'm still tackling this issue. And I don't know if the commented codes are going to be useful when it comes to assigning Client Authority Ownership. I leave them in so future readers can help brainstorm or come up with other ideas of assigning Client Authority Ownership on non-player objects.

    So far, we can only spawn player objects, and non-player objects from player objects. Can we go further and spawn non-player objects from non-player objects spawned from player objects? Transferable ownership is key to make this happen.

    Discuss! I wanted to have a discussion to see if what I'm doing is right or wrong, even if it's obviously trying to spawn non-player objects from non-player objects.
     
  2. Capn_Andy

    Capn_Andy

    Joined:
    Nov 20, 2013
    Posts:
    80
    Can't you just send a Command from the player object, and have it create non-player-objects on the server?
     
  3. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    I was about to update on this. Nice timing!

    No. The player object cannot keep track of other non-player objects when the player is always interacting with non-player object.

    Since the game is an RTS game, the player object is the Starting Location game object, which spawns in Capsules, a type of non-player game objects, that the player controls. Starting Location game object also spawns in Selection Manager and Split Manager non-player objects. Selection Manager handles selecting the Capsules and keep track of which Capsules is the player. Split Manager handles spawning non-player objects from Capsules, which themselves are also non-player objects. The issue lies within Split Manager.

    This is what happens:



    The left is the client, the right is the server.

    First thing it did was the client called on CmdSplit().
    Next, it checked that this.localPlayerAuthority is true.
    Then it caled on RpcSplit(), and we shift to the server console.
    On the server, RpcSplit is being called.
    But, then, we get that there's no authority for the class, ending with nothing happening.

    I send a Command to the server, but the server ate it up because there's no client authority on the non-player object, Split Manager, that was to spawn the non-player object, Capsule. Still wondering why when it should already have client authority when I spawned the Split Manager game object.

    Code (CSharp):
    1.     [ServerCallback]
    2.     public void ServerInitialize() {
    3.         //Server code
    4.         //This is run for spawning new non-player objects. Since it is a server calling to all clients (local and remote), it needs to pass in a
    5.         //NetworkConnection that connects from server to THAT PARTICULAR client, who is going to own client authority on the spawned object.
    6.  
    7.         //Player unit
    8.         GameObject obj = MonoBehaviour.Instantiate(this.spawnPrefab) as GameObject;
    9.         NetworkServer.SpawnWithClientAuthority(obj, this.connectionToClient);
    10.  
    11.         //Player selection manager
    12.         GameObject manager = MonoBehaviour.Instantiate(this.selectionManagerPrefab) as GameObject;
    13.         NetworkServer.SpawnWithClientAuthority(manager, this.connectionToClient);
    14.         SelectionManager selectionManager = manager.GetComponent<SelectionManager>();
    15.         if (selectionManager != null) {
    16.             selectionManager.allObjects.Add(obj);
    17.         }
    18.  
    19.         //Player split manager
    20.         manager = MonoBehaviour.Instantiate(this.splitManagerPrefab) as GameObject;
    21.         NetworkServer.SpawnWithClientAuthority(manager, this.connectionToClient);
    22.         SplitManager splitManager = manager.GetComponent<SplitManager>();
    23.         if (splitManager != null) {
    24.             splitManager.selectionManager = selectionManager;
    25.         }
    26.     }
     
  4. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Code (CSharp):
    1.     [ServerCallback]
    2.     public void ServerInitialize() {
    3.         //Server code
    4.         //This is run for spawning new non-player objects. Since it is a server calling to all clients (local and remote), it needs to pass in a
    5.         //NetworkConnection that connects from server to THAT PARTICULAR client, who is going to own client authority on the spawned object.
    6.  
    7.         //Player unit
    8.         //-------------CHANGED-----------------------
    9.         GameObject obj = MonoBehaviour.Instantiate(this.spawnPrefab) as GameObject;
    10.         NetworkIdentity objIdentity = obj.GetComponent<NetworkIdentity>();
    11.         if (objIdentity != null) {
    12.             objIdentity.AssignClientAuthority(this.connectionToClient);
    13.         }
    14.         NetworkServer.SpawnWithClientAuthority(obj, this.connectionToClient);
    15.         //-------------END OF CHANGES MADE------------
    16.  
    17.         //Player selection manager
    18.         GameObject manager = MonoBehaviour.Instantiate(this.selectionManagerPrefab) as GameObject;
    19.         NetworkServer.SpawnWithClientAuthority(manager, this.connectionToClient);
    20.         SelectionManager selectionManager = manager.GetComponent<SelectionManager>();
    21.         if (selectionManager != null) {
    22.             selectionManager.allObjects.Add(obj);
    23.         }
    24.  
    25.         //Player split manager
    26.         manager = MonoBehaviour.Instantiate(this.splitManagerPrefab) as GameObject;
    27.         NetworkServer.SpawnWithClientAuthority(manager, this.connectionToClient);
    28.         SplitManager splitManager = manager.GetComponent<SplitManager>();
    29.         if (splitManager != null) {
    30.             splitManager.selectionManager = selectionManager;
    31.         }
    32.     }
    Even if I changed it so that tries to assign the NetworkConnection from server to client as the client authority owner, it still gives NULL for client authority owners.

    I don't know what to do from this point.
     
  5. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Sounds like a refactor and passing around id's would possibly fix it cleaner than trying to shoehorn it into this pattern. But have you ruled out bugs in Unet first? It's had quite a few bugs so I would not be surprised...
     
  6. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Maybe I can report that Unity is expected to but is unable to support spawn chaining, where non-player objects A, B, C, ... cannot let clients have authority after spawning them.

    A spawns B. B spawns C. C spawns D, and so on. The clients doesn't have authority over X, Y, and Z.

    This is problematic. I do not know what behaviors should I be expecting from UNET, so I cannot compare it with its current state. I know I cannot rule out the possibility of UNET bugs, but without knowing what to expect, I don't even know what to describe in the Bug Reporter.
    I'm currently refactoring as much as possible, to try to get the code to work step by step. The most obvious detail is in the NetworkIdentity.

    What properties can I use to have NetworkServer.SpawnWithClientAuthority() with the client? NetworkIdentity only has clientAuthorityOwner, yet it spits out an error saying it could not assign the game object to that owner.
     
  7. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Just after I posted this, I had an idea of checking to see who is the non-player object's NetworkIdentity's clientAuthorityOwner.

    I realized this may seems to be a bug, but I need your opinions on this.



    If a non-player object has authority, then who is the client authority owner? Or these are separate things?
     
  8. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    Update:

    So the editor just crashed. Cannot understand why game objects spawned using NetworkServer.SpawnWithClientAuthority() have hasAuthority = false, using the same owner that has authority on the original game object. All the time.

    I also don't know why hasAuthority = true, when there's no client authority owner.

    I have placed my repo on Github, open-source, Apache Licensed. This is the repo that may crash the editor.

    This is the following relevant code for spawning non-player objects from non-player objects.

    Code (CSharp):
    1.  
    2.     private void AddingNewSplitGroup() {
    3.         foreach (GameObject obj in this.selectionManager.selectedObjects) {
    4.             CmdSplit(obj);
    5.         }
    6.         return;
    7.     }
    8.  
    9.     [Command]
    10.     public void CmdSplit(GameObject obj) {
    11.         //Place the RPC call here.
    12.         Debug.Log("CMD split was called. Calling RPC.");
    13.         GameObject split = MonoBehaviour.Instantiate(obj) as GameObject;
    14.         NetworkServer.SpawnWithClientAuthority(split, this.connectionToClient);
    15.         NetworkIdentity identity = split.GetComponent<NetworkIdentity>();
    16.         Debug.Log(identity); //For inspecting the properties in VS2015
    17.     }
    18.  
     
  9. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    hippocoder likes this.
  10. samjoly

    samjoly

    Joined:
    Apr 23, 2013
    Posts:
    30
    I have the same question than you: "why hasAuthority = true, when there's no client authority owner."
    Did you figure out why?
     
  11. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    This question was answered with the update to Unity 5.2, where they introduced a better way of implementing authoritative control over game objects for both client and server. In other words, Unity 5.2 allows spawning non-player controlled game objects on the network.

    When you assign a client authority owner to a game object, then spawning it, the client will be able to take control of the game object, without requiring the server to be the middleman in passing the client's commands over to the target game object.

    I think you can ask around if you have more questions.