Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Network spawning of game objects not working

Discussion in 'Multiplayer' started by Vedrit, Mar 12, 2016.

  1. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    I've got a problem where players are not able to instantiate/spawn and object for all players connected. The objects have a network identity and are set to local authority. If a client is also the host, the object spawns just fine.
    If the client is not the host, it does the same except it'll now throw errors saying the client doesn't have authority to send the commands for object to the server.
    Do the commands need to originate from the player object? What needs be set to local authority? Can anyone point me to a good tutorial, because I'm having a heck of a time finding anything
     
  2. sovium

    sovium

    Joined:
    Mar 8, 2016
    Posts:
    27
    Only game objects with Local Player Authority can send Commands to the server. You need to set that to any game object that should execute a Command on the server. Could you share some code on what you are trying to do? This sounds like you are invoking a Command on a player object that is not yours.

    Example:

    Code (CSharp):
    1.  
    2. public class Player : NetworkBehaviour{
    3.     void MethodThatInvokesTheCommand(){
    4.  
    5.         // If you call the above method in a situation where it is not certain that this player object is your player object, it is likely that this is called on a player object that is not yours.
    6.         CmdDoStuff();
    7.  
    8.         // Instead you should probably check
    9.         if(isLocalPlayer)
    10.             CmdDoStuff();
    11.     }
    12.  
    13.     [Command]
    14.     void CmdDoStuff(){
    15.         Stuff();
    16.     }
    17. }
    18.  
    Networking can be confusing I know, but without seeing your code, this is the best I can do.

    Hope this helps!
     
    Last edited: Mar 13, 2016
  3. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    Here's the script I am using just to testing things out. It runs on an empty object placed in the scene
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Networking;
    4. using System.Collections;
    5.  
    6. public class spawner : NetworkBehaviour
    7. {
    8.  
    9.     public GameObject cubePrefab;
    10.     public Transform anchor;
    11.     public GameObject player;
    12.  
    13.     // Update is called once per frame
    14.     void Update()
    15.     {
    16.         if (Input.GetKeyDown(KeyCode.F) && player.GetComponent<NetworkIdentity>().isLocalPlayer)
    17.         {
    18.             ClientScene.RegisterPrefab(cubePrefab);
    19.          
    20.             CmdSpawnCube();
    21.         }
    22.     }
    23.  
    24.     [Command]
    25.     void CmdSpawnCube()
    26.     {
    27.         var build = Instantiate(cubePrefab, anchor.position, anchor.rotation) as GameObject;
    28.         NetworkServer.Spawn(build);
    29.     }
    30. }
    31.  
    The object is set up like so
    http://snag.gy/s9fda.jpg

    When a player spawns, they set the object in their instance to their player, and set the anchor transform so that the cube prefab spawns where I want it to. My testing indicates that all Commands must be run from the player object (The player that spawns when client connects)
     
  4. sovium

    sovium

    Joined:
    Mar 8, 2016
    Posts:
    27
    Yeah commands must be run from the player objects or objects that have local player authority set to true.

    You don't have to register your prefab every time you press F, just register it once.

    Since this class is also a network behaviour, you can just check isLocalPlayer instead of player.GetComponent<NetworkIdentity>().isLocalPlayer

    Since you are spawning a cube from pressing a key, you could just include this behaviour in your player object and get a reference to the anchor position there.
     
  5. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    I've told the Spawner to have local player authority, as shown in my previous screenshot. This is what it looks like for the host: http://snag.gy/fdu2C.jpg
    For the client: http://snag.gy/tUzoR.jpg

    This returns false since it's not attached to the player. See the earlier image links

    The objective of this test is to find a way to not have this behavior run from a script attached to the player. In my project, the player interacts with an object (I.E. NPC) which does permission checks and provides the player with options to spawn for the player to chose from. The object has all the information, and will be the parent of the spawned prefab.
     
  6. sovium

    sovium

    Joined:
    Mar 8, 2016
    Posts:
    27
    You are correct, sorry for my misleading answer.

    Is your player instantiated somewhere? Does your player.GetComponent<NetworkIdentity>().isLocalPlayer return true?
     
  7. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    The player is being instantiated by NetworkManager component upon connection. And yes, player.GetComponent<NetworkIdentity>().isLocalPlayer does return true
     
  8. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    Just to re-iterate and clarify:
    I need the client to have authority to send Commands that are in scripts on other objects.
    Checking the box "Local Client Authority" in NetworkIdentity does not appear to work
     
  9. sovium

    sovium

    Joined:
    Mar 8, 2016
    Posts:
    27
    If you look at the documentation on remote actions, it states that Commands can only be sent from your player object.

    http://docs.unity3d.com/Manual/UNetActions.html

    Why do you have to authorize other objects to send commands? If you need other objects to affect your player, you could do it directly on the server by using the [Server] or [ServerCallback] attributes or check if isServer is true. Similar to the way they do taking damage in tutorials (Where a bullet hits a player, the bullet calls player.TakeDamage(int amount), which is called only on the server).

    I'm sorry if this does not help you, I'm still having hard time understanding what exactly you are trying to achieve.
     
  10. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    The scenario is thus: There are locations players can go to interact with an object to create or demolish buildings. Any player may do so, so long as they have certain permissions that are checked before the player even interacts with the object (E.G. permissions within their guild)
    The object contains all the pertinent information for spawning the building; position, rotation, name, parent, prefab.
    So it is preferable to have the object send the command, rather than telling that object who the player is, passing the information to the player, then have the player send the command.
     
  11. sovium

    sovium

    Joined:
    Mar 8, 2016
    Posts:
    27
    I am not sure how you can do this with commands, but I managed to spawn objects from the spawner with following code:

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Networking;
    4. using UnityEngine.Networking.NetworkSystem;
    5.  
    6. public class SpawnBehaviour : NetworkBehaviour {
    7.  
    8.     const short SpawnMessageType = 1000;
    9.  
    10.     public GameObject spawnPrefab;
    11.     public NetworkBehaviour player;
    12.  
    13.     void OnEnable()
    14.     {
    15.         NetworkServer.RegisterHandler(SpawnMessageType, SpawnObject);
    16.     }
    17.  
    18.     void Update()
    19.     {
    20.         if (Input.GetKeyDown(KeyCode.Space) && player.isLocalPlayer)
    21.         {
    22.             NetworkManager.singleton.client.Send(SpawnMessageType, new EmptyMessage());
    23.         }
    24.     }
    25.  
    26.     void SpawnObject(NetworkMessage msg)
    27.     {
    28.         print("SERVER: Spawning object");
    29.         NetworkServer.Spawn(Instantiate(spawnPrefab));
    30.     }
    31. }
    32.  
    SpawnObject is only ran on the server because only the server has the handler for SpawnMessageType.

    Let me know if this helps you!
     
  12. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    I'll test this when I get home, but in the mean time, could you explain what is going on in the script? What are NetworkServer.RegisterHandler and NetworkManager.singleton.client.Send doing?
     
  13. sovium

    sovium

    Joined:
    Mar 8, 2016
    Posts:
    27
    NetworkServer.RegisterHandler registers a message handler on the server and takes two parameters:
    - short MessageType (the type of your message)
    - Callback method that is invoked when a message of type given has been received.​

    NetworkManager.singleton gets the instance of networkmanager you are using, and networkManager.client gets your NetworkClient.

    The NetworkClient has a method called Send which takes two parameters:
    - short MessageType (the type of your message)
    - MessageBase message (the actual message)​

    In this case we didn't need to send any data with the message because we want the spawner to spawn stuff that it already knows about, so we will just send a message of type SpawnMessageType and content of new EmptyMessage() which is a premade UNet message. When the server receives this message, it will see if any handlers have been registered for that MessageType. Since we registered a handler for it, it will invoke the callback method and give the message as a parameter (SpawnObject(NetworkMessage msg)). Now if we had a message with actual content, we could read that message in the callback method with
    EmptyMessage theMessage = msg.ReadMessage<EmptyMessage>();

    You can register handlers on the client side as well. So for example we could do something like
    send message from client -> server receive message and do stuff -> send message back to client -> client receive message and do stuff.

    This might sound confusing as I am not very good in explaining stuff like this, but see network messaging documentation if this does not make sense. (http://docs.unity3d.com/Manual/UNetMessages.html)
     
  14. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    Sounds fairly similar to how SmartFox events are handled,
     
    sovium likes this.
  15. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    Ok, I'm running into a new issue where when the client tries to spawn, the other clients
    NullReferenceException: Object reference not set to an instance of an object
    I'm trying to spawn a prefab that is already registered with the network manager, and has local player authority.
    The function I'm using is
    Code (csharp):
    1.  
    2. void house()
    3.     {
    4.         currentBuild = "House";
    5.         var currentStructure = gameObject.transform.FindChild(placeHolder.name).gameObject;
    6.         Destroy(currentStructure);
    7.         playrCmds.CmdDestroy(currentStructure);  //Calls the command from a script attached to the player object to remove a pre-placed placeholder
    8.         var build = Instantiate(housePrefab, anchor.position, anchor.rotation) as GameObject;
    9.         build.transform.parent = thisPlot.transform;
    10.         build.name = housePrefab.name;
    11.         building = build;
    12.         playrCmds.CmdBuild(build);  //Same as CmdDestroy, but to spawn.
    13.         buildScreen.SetActive(false);
    14.         NPCspeech = new string[1];
    15.         NPCspeech[0] = "Constructing a house.";
    16.         theManager.convoEnding = true;
    17.         sendUpdate();
    18.     }
    When the host spawns the object, it shows up for all clients.
     
  16. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    Do I have to re-register the object with the server since I've changed the name, location and rotation, and parent? The CmdDestroy seems to run without any problems.
     
  17. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    I've some digging and debugging and adjusting, but for some reason, the script on the client side isn't sending the prefab to the command even though the client side log shows that the prefab isn't null
     
  18. notseanr

    notseanr

    Joined:
    Jan 15, 2016
    Posts:
    22
    "So it is preferable to have the object send the command, rather than telling that object who the player is, passing the information to the player, then have the player send the command"

    No.

    The player should send the command. In the UNet HLAPI, players do things, not random objects in the scene.

    You can't pass an arbitrary non-spawned game object in a command - in this case a prefab. Unity does not have a stable-over-time way to reference assets (prefabs) that can be used to refer to them in network messages. You will have to pass a file path, or an index into a list of registered prefabs that you manage yourself.

    Sadly the type "GameObject" is not specific enough for the system to identify that you are trying to send a prefab and generate a meaningful error message.