Search Unity

  1. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  4. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

How to do Commands/RPCs/RemoteActions on non-player objects?

Discussion in 'Connected Games' started by N427, Jul 4, 2015.

  1. N427

    N427

    Joined:
    Aug 7, 2014
    Posts:
    5
    So I'd been making a multiplayer game in Photon but Ive decided to move it to UNET now that it's finally out. In Photon I was able to send RPC messages to and from basically any networked object, but this only works on "player" objects.

    The exact situation I have right now is this: I've got an item in-game which resides on the server and I want player to be able to pick it up and move it around. In Photon I had been using RPCs so that when a player clicks on the client-instance of the serverside object, the client-instance of the object sends RPCs to the server-instance and asks for permission to pick it up, and the server-instance replies and approves with another RPC. Then the player can move the item around on their client while it is invisible to all other players. When the player lets go and drops the item, more RPCs are used to tell the server that the player has stopped moving it. The client-instance tells the server-instance the new location of the item (a Vector2D of the coordinates where the player dropped it) and the item snaps to that position, becomes visible to everyone, and basically goes back to the state it was at in the very beginning.

    Since Commands are for player-objects only, I can't send an RPC from the client-instance of an object to the master-server-instance. Is there a way to do this within the object? I figure I could have them take a "detour" and go through the player-object's networking, but it seems to me like that'd be overcomplicated and Id like to know if theres a better way to do it.

    TL;DR: I want to send RPCs messages on a non-player object in UNET. Should I route the messages through Commands and ClientRPCs on the player object or is there a better way?
     
  2. rikey

    rikey

    Joined:
    Mar 3, 2015
    Posts:
    147
    Commands can be in a NetworkBehaviour on something with a NetworkIdentity. You don't have to check anything on the NetworkIdentity component.
     
  3. N427

    N427

    Joined:
    Aug 7, 2014
    Posts:
    5
    I have commands on a nonplayer object with a Network Behaviour with a Network Identity, but they dont work. Commands in the networkbehaviours of player objects work fine.

    I dont see any difference between the code for the working and nonworking commands.

    The documentation also states that ClientRPCs are for all objects while commands are for specifically player objects. See "Commands" at:http://docs.unity3d.com/Manual/UNetActions.html
     
  4. Vazix

    Vazix

    Joined:
    Jan 16, 2013
    Posts:
    9
    Can you elaborate on the setup of your scene? The player objects (or usually spawned prefabs) are spawned by network manager or manually with another high level function. So if you just have an object in your scene with network identity added then my guess is that the client and the server (not on the same instance, but 2 different clients lets say) have their own objects in their own scenes, but not a synchronized common object.

    For me it started working when I instantiated a prefab on the server and used Spawn method on that game object. Newly joined clients would also get the same object and the rpc's seems to work (even tho that object is not assigned to any 1 player):

    Code (CSharp):
    1.  
    2. // My modified NetworkManager class
    3.     public override void OnStartServer ()
    4.     {
    5.         base.OnStartServer ();
    6.         GameObject ga = (GameObject)Instantiate (networkController);
    7.         ga.name = networkController.name; // Not necessary, but removes "(clone)" from name
    8.         NetworkServer.Spawn (ga);
    9.     }
     
    yazan2241 likes this.
  5. N427

    N427

    Joined:
    Aug 7, 2014
    Posts:
    5
    Vazix:

    In my scene I have a "LootBox" which, when the L key is pressed, spawns in a "Loot" prefab which is the object in question. I've been testing this by pressing L on the host-client which is both a server and a client. The Loot prefab is registered in the list of Spawnable Prefabs in my NetworkManager object, which is just using the default NetworkManager component.

    This is the script that spawns the object:


    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.Networking;
    4.  
    5. public class LootBoxScript : NetworkBehaviour {
    6.  
    7.     // Use this for initialization
    8.     void Start () {
    9.    
    10.     }
    11.    
    12.     // Update is called once per frame
    13.     void Update () {
    14.         if(Input.GetKeyDown(KeyCode.L)){
    15.             CmdGenerateLoot();
    16.         }
    17.     }
    18.    
    19.     [Command]
    20.     void CmdGenerateLoot(){
    21.         GameObject loot = (GameObject) GameObject.Instantiate(Resources.Load("Loot"), transform.position, Quaternion.identity);
    22.         NetworkServer.Spawn(loot);
    23.     }
    24. }
     
  6. rikey

    rikey

    Joined:
    Mar 3, 2015
    Posts:
    147
    The documentation isn't the best. Technically, Commands can be sent from objects other than players, so it sounds like the documentation is saying one player can't send a Command from another player.
     
  7. N427

    N427

    Joined:
    Aug 7, 2014
    Posts:
    5
    So then why aren't objects receiving Commands in my game? My only idea is that somehow I'm not spawning the objects on the network properly but Im not sure where the error is there.
     
  8. rikey

    rikey

    Joined:
    Mar 3, 2015
    Posts:
    147
    A Command just says to run the code inside it only on the server and not on the clients. They're not for directly sending information between objects.

    From your LootBox script, what isn't happening right?
     
  9. N427

    N427

    Joined:
    Aug 7, 2014
    Posts:
    5
    Context: The LootBox object is a spawner that spawns Loot objects.

    In "LootScript", an extension of NetworkBehaviour, the script on Loot objects, I've got some Commands and ClientRPCs. The Loot object is spawned through the LootBoxScript on the server.

    Code (CSharp):
    1.  
    2.     public void StartPickUp(){
    3.         Debug.Log("Client Sent Pickup Request to Server");
    4.         CmdRequestPickUp(GameObject.FindGameObjectWithTag("HUD").GetComponent<FindLocalPlayerScript>().player.GetComponent<NetworkIdentity>().netId.Value);
    5.     }
    6.    
    7.     [Command]
    8.     public void CmdRequestPickUp(uint requesterID){
    9.         Debug.Log("Server Recieved Pickup Request");
    10.         if(picked == false){
    11.             picked = true;
    12.             visible = false;
    13.             RpcApprovePickUp(requesterID);
    14.             Debug.Log("Server Approved Pickup");
    15.         }
    16.     }
    17.    
    18.     [ClientRpc]
    19.     public void RpcApprovePickUp(uint requesterID){
    20.         if(GameObject.FindGameObjectWithTag("HUD").GetComponent<FindLocalPlayerScript>().player.GetComponent<NetworkIdentity>().netId.Value == requesterID){
    21.             grabbed = true;
    22.         }
    23.         Debug.Log("Client Recieved Pickup Approval");
    24.     }
    The problem is that Commands seem to not be sent correctly over the network when I call StartPickUp(). I have the "requesterID" stuff in there because that's my way of sending a message to one specific client-instance of the server-based loot object, because ClientRPC functions are activated on every client when called anywhere.

    When I call StartPickUp() on a host client which is both client and server, the debug output on the hostclient is this:

    "Client Sent Pickup Request to Server"
    "Server Recieved Pickup Request"
    "Server Approved Pickup"
    "Client Recieved Pickup Approval"

    When I call StartPickUp() on a remote client which is only a client and is connected to a hostclient server, the debug output on the remote client is only "Client Sent Pickup Request to Server" and there is no debug output on the hostclient. I expect calling StarPickUp() on a remote client to yield the same debug output as calling it on a host client but it doesn't.
     
  10. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    From the manual "Commands are sent from player objects on the client to player objects on the server". Only player objects can send commands.
     
  11. Piflik

    Piflik

    Joined:
    Sep 11, 2011
    Posts:
    214
    You need a Command on your player object to tell the server, which LootBox should spawn its content. You can't pass complex objects to a command, but you can identify the object by its NetworkIdentity.netID and you can pass this to a command. The server can then find the box by its ID (NetworkServer.FindLocalObject(netID)) and spawn a loot object at the desired location. This loot object will then be created on all clients.

    Basically I would implement a Useable Interface and a Use function on the player that casts a ray and checks for that interface and a NetworkIdentity on the object hit, before calling the use-command with the object's netID .

    At least that's how I do it, although I still have some problems with commands...
     
  12. patrusfarr

    patrusfarr

    Joined:
    Jun 18, 2014
    Posts:
    2
    Why are Commands only sent from players? It just complicates things having to route all the functions to the player then to the server then back again. The player object just gets filled up with scripts containing the functionality of everything in the game that needs to be interacted with.
     
  13. PhilippG

    PhilippG

    Joined:
    Jan 7, 2014
    Posts:
    237
    You could do it by implementing "ownerships". A client can gain network control by assigning authority: http://docs.unity3d.com/ScriptReference/Networking.NetworkIdentity.AssignClientAuthority.html

    This however needs to be done via command from the player object. But as soon as you have the authority of any netobject your code would work from within that class, like that lootbox.
    That means you would just have two functions in your player, a CmdRequestOwnership and a CmdReleaseOwnership.
    Me, I implemented an abstract "Ownable" class that also netsyncs the owners id so you can quickly lookup if and who owns this entity. That way, the server can also deny requests etc etc.
     
    Last edited: Jun 20, 2016
  14. MFKJ

    MFKJ

    Joined:
    May 13, 2015
    Posts:
    101
    without any client authority how do any client interact with an non-player object of the scene. I am having this issue.


    Code (CSharp):
    1. void Update () {
    2.  
    3.     CmdMove();
    4.  
    5. }
    6.  
    7. [Command]
    8. void CmdMove() {
    9.     transform.Translate(new Vector3(Input.GetAxis("Vertical") * 5 * Time.deltaTime, 0, Input.GetAxis("Horizontal") * 5 * Time.deltaTime));
    10. }
     
  15. moco2k

    moco2k

    Joined:
    Apr 29, 2015
    Posts:
    288
    Sure, the command must be called from a script attached to a player object (or from an object with local authority).
    However, when this command is executed on the server, there is nothing which prevents you from calling public functions on other networked objects from within this command. Such objects can be server-side objects without any client-authority (like monsters in a coop game).

    If the target object of the player interaction is dynamic and not always the same, you might need to pass some identifier argument so that the server knows which object the player wants to interact with (like NetworkInstanceId or something).
     
    Last edited: Nov 2, 2016
  16. MFKJ

    MFKJ

    Joined:
    May 13, 2015
    Posts:
    101
    but my question is that as my code suggested it is only taking first player input on the object. second player unable to move the same object altough general code have written. Any player who will press horizontal/vetical key will move the object.
     
  17. moco2k

    moco2k

    Joined:
    Apr 29, 2015
    Posts:
    288
    Okay. So, the reason why your particular code does not work is because it does not make sense. You try to fetch client input data within the command. The command is executed on the server. The server does not know the input of the particular clients unless you do transmit this data. You can do this by transmitting the input data as arguments for the command. With the code you've posted, it would only work for the host because in this case server and client is the same instance so that the get input can be retrieved even within the command. I recommend to further investigate tutorials and examples on UNET basics. For example, this one is pretty good.
     
    Last edited: Nov 2, 2016
    fghfghfg likes this.
  18. fghfghfg

    fghfghfg

    Joined:
    Apr 26, 2018
    Posts:
    2
    Just trying to crystallize this in my head, if I have a non player object, say a door with an RpcOpen function on it and a player object with a CmdUseDoor(some_id) function on it. To then use that door's rpc, I could get the networkID of the door then call CmdUseDoor(net_id_of_door) and inside the cmd call the rpc for that specific door?
    Sorry about the necro I'm just trying to wrap my head around non-player network interactions
     
  19. moco2k

    moco2k

    Joined:
    Apr 29, 2015
    Posts:
    288
    Yepp, that should work.
     
  20. fghfghfg

    fghfghfg

    Joined:
    Apr 26, 2018
    Posts:
    2
    I just finished implementing the system/integrated it into my game manager object so everything is tracked and accessible and sexy, thanks for participating in this thread :D without your post that I initially replied to I was a little lost despite the documentation... despite unity's documentation usually being very easy to absorb I feel a little bit silly when I can't wrap my head around some of this networking documentation.
     
  21. Riiich

    Riiich

    Joined:
    Sep 30, 2014
    Posts:
    4
    You wouldn't mind posting your code would you? I'm really struggling with the same concept :(