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

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?

1 Like

Commands can be in a NetworkBehaviour on something with a NetworkIdentity. You don’t have to check anything on the NetworkIdentity component.

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

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):

// My modified NetworkManager class
    public override void OnStartServer ()
    {
        base.OnStartServer ();
        GameObject ga = (GameObject)Instantiate (networkController);
        ga.name = networkController.name; // Not necessary, but removes "(clone)" from name
        NetworkServer.Spawn (ga);
    }
1 Like

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:

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class LootBoxScript : NetworkBehaviour {

    // Use this for initialization
    void Start () {
   
    }
   
    // Update is called once per frame
    void Update () {
        if(Input.GetKeyDown(KeyCode.L)){
            CmdGenerateLoot();
        }
    }
   
    [Command]
    void CmdGenerateLoot(){
        GameObject loot = (GameObject) GameObject.Instantiate(Resources.Load("Loot"), transform.position, Quaternion.identity);
        NetworkServer.Spawn(loot);
    }
}

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.

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.

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?

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.

    public void StartPickUp(){
        Debug.Log("Client Sent Pickup Request to Server");
        CmdRequestPickUp(GameObject.FindGameObjectWithTag("HUD").GetComponent<FindLocalPlayerScript>().player.GetComponent<NetworkIdentity>().netId.Value);
    }
   
    [Command]
    public void CmdRequestPickUp(uint requesterID){
        Debug.Log("Server Recieved Pickup Request");
        if(picked == false){
            picked = true;
            visible = false;
            RpcApprovePickUp(requesterID);
            Debug.Log("Server Approved Pickup");
        }
    }
   
    [ClientRpc]
    public void RpcApprovePickUp(uint requesterID){
        if(GameObject.FindGameObjectWithTag("HUD").GetComponent<FindLocalPlayerScript>().player.GetComponent<NetworkIdentity>().netId.Value == requesterID){
            grabbed = true;
        }
        Debug.Log("Client Recieved Pickup Approval");
    }

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.

From the manual “Commands are sent from player objects on the client to player objects on the server”. Only player objects can send commands.

1 Like

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…

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.

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.

without any client authority how do any client interact with an non-player object of the scene. I am having this issue.

[

](c# - one object control by all network players - Stack Overflow)

void Update () {

    CmdMove();

}

[Command]
void CmdMove() {
    transform.Translate(new Vector3(Input.GetAxis("Vertical") * 5 * Time.deltaTime, 0, Input.GetAxis("Horizontal") * 5 * Time.deltaTime));
}

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).

1 Like

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.

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.

1 Like

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

Yepp, that should work.

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 :smile: 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.