[Command] Running on wrong player. (Code included)

So I have Player1 which is the host and player. Player 2 is the client. This script is attached to the player prefab. Here is my code:

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

public class PlayerMove : NetworkBehaviour {
    private InputManager inputManagerDatabase;
    public StorageInventory STI;
    // Use this for initialization
    void Start () {
        if (!isLocalPlayer)          
        return;
        if (inputManagerDatabase == null)
            inputManagerDatabase = (InputManager)Resources.Load("InputManager");
    }

    public override void OnStartLocalPlayer() {
        if (isServer) {
            GetComponent<MeshRenderer> ().material.color = Color.red;
        } else {
            GetComponent<MeshRenderer> ().material.color = Color.blue;
        }
    }
  
    // Update is called once per frame
    void Update () {  
        if (!isLocalPlayer)
            return;      
        var x = Input.GetAxis("Horizontal")*0.1f;
        var z = Input.GetAxis("Vertical")*0.1f;      
        transform.Translate(x, 0, z);
        RaycastHit hit;
        float theDistance;

        Vector3 forward = transform.TransformDirection (Vector3.forward) * 10;
        // E Key Handler
        if (Input.GetKeyDown (inputManagerDatabase.StorageKeyCode)) {
            if (Physics.Raycast (transform.position, (forward), out hit)) {
                theDistance = hit.distance;

                if (theDistance <= 1.6f && hit.collider.gameObject.tag == "StorageBoxGeneric") {
                    CmdStorageBoxGenericBusy(hit.collider.gameObject);
                }
            }
        }
        // End E Key Handler
    }


    [Command]
    void CmdStorageBoxGenericBusy(GameObject g) {
        STI = g.GetComponent<StorageInventory> ();
        if (STI.isBusy == true) {
            STI.CmdSetBusy (false);
            StartCoroutine(STI.OpenInventoryWithTimer());
        } else {
            STI.CmdSetBusy (true);
            StartCoroutine(STI.OpenInventoryWithTimer());
        }
    }

}

The problem is whether player1 or player2 press E on the box, the variable STI is set on player1. And when the inventory of the box is opened, its always opened on player1.

What SHOULD happen is when player1 presses e on the box, it sets STI on the player1 prefab and opens / closes on player1 screen. When player2 presses e on the box, it sets STI on the player2 prefab and opens / closes on player2 screen.

So it appears to work from player1 which is the host. But player2, it sets STI on player1 and opens / closes on player1 screen.

What am I doing wrong?

So when player2 presses E, it calls the command on the host, and only the host. I’m going to guess your issue is in your STI.OpenInventoryWithTimer coroutine, where it probably doesn’t call a ClientRPC to open the UI window, instead opening it on the host directly.

Are you sure STI gets set on Player1 when Player2 presses E, and STI getting set isn’t just left over from the last time you pressed E on Player1? I don’t see how that would happen otherwise.

1 Like

Oh ok, yes you are correct. STI is getting set on the proper prefab, I mustve seen it wrong.

Correct, STI.OpenInventoryWithTimer isnt using ClientRPC at all. I was thinking ClientRPC was to send things to all clients.

So can you please help me with the ClientRPC? How would I use that to make it work on the proper player? For reference here is the code it calls:

public void OpenInventoryWithTimer() {
        if (showStorage == true && isBusy == true) {
            Debug.Log ("Opening");
            //startTimer = Time.time;
            //showTimer = true;
            //yield return new WaitForSeconds (timeToOpenStorage);
            inv.ItemsInInventory.Clear ();
            inventory.SetActive (true);
            addItemsToInventory ();
            //showTimer = false;
            //if (timer != null)
                //timer.SetActive (false);
        } else {
            Debug.Log ("Closing");
            storageItems.Clear ();
            setListofStorage ();
            inventory.SetActive (false);
            inv.deleteAllItems ();
            tooltip.deactivateTooltip ();
        }
    }

NOTE: I changed the OpenInventoryWithTimer from an Ienumerator to a void. Startcoroutine is no longer used. Just STI.OpenInventoryWithTimer, commented out some things to make it work.

I will read up on ClientRPC, but Im almost a week into just getting this one thing to work, so any help with the ClientRPC would be amazing! Thank you!

Joe I also wanted to ask you, would I be better off using authority like this: UNET: How do I properly handle Client Authority with interactable Objects? - Questions & Answers - Unity Discussions ?

I mean, it really shouldnt be this hard to have a simple storage container in the world, have players be able to open it, take things, put things, and simply lock when one player has it open.

Could you PLEASE advise me on whether I could continue with the [Command] and [ClientRPC] method, or perhaps change to the Authority method?

So you are correct that ClientRPC goes to all clients. The easy way code wise would be to just wrap everything in that RPC with a check for isLocalPlayer. So the RPC is called on every client but the code in the RPC is only run on the client that owns the player object. Alternatively you could be far more network efficient with a TargetRPC, but just to get things working I would just do the ClientRPC first and check for local player and note down to change it later.

https://docs.unity3d.com/ScriptReference/Networking.TargetRpcAttribute.html

This comes down to preference.

Personally I’m not a big fan of giving clients authority of anything, since it opens up hacking opportunities. I also haven’t investigated what happens to objects with client authority if the client disconnects (maybe its fine, I haven’t looked into it, but seems like a potential issue). Also if you want to support multiple players interacting with the same container at the same time then assigning client authority would probably be the wrong approach.

^This

Also, you can always use LLAPI messaging system to pass data specificly how you want. Though it requires a bit more reading, it’s well worth it, since you’re not bind to the player class when receiving messages from server. Just my two cents.

use targetRPC