Search Unity

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

Discussion in 'Multiplayer' started by cyunide, Sep 27, 2017.

  1. cyunide

    cyunide

    Joined:
    Sep 20, 2017
    Posts:
    9
    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:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Networking;
    3. using System.Collections;
    4.  
    5. public class PlayerMove : NetworkBehaviour {
    6.     private InputManager inputManagerDatabase;
    7.     public StorageInventory STI;
    8.     // Use this for initialization
    9.     void Start () {
    10.         if (!isLocalPlayer)          
    11.         return;
    12.         if (inputManagerDatabase == null)
    13.             inputManagerDatabase = (InputManager)Resources.Load("InputManager");
    14.     }
    15.  
    16.     public override void OnStartLocalPlayer() {
    17.         if (isServer) {
    18.             GetComponent<MeshRenderer> ().material.color = Color.red;
    19.         } else {
    20.             GetComponent<MeshRenderer> ().material.color = Color.blue;
    21.         }
    22.     }
    23.  
    24.     // Update is called once per frame
    25.     void Update () {  
    26.         if (!isLocalPlayer)
    27.             return;      
    28.         var x = Input.GetAxis("Horizontal")*0.1f;
    29.         var z = Input.GetAxis("Vertical")*0.1f;      
    30.         transform.Translate(x, 0, z);
    31.         RaycastHit hit;
    32.         float theDistance;
    33.  
    34.         Vector3 forward = transform.TransformDirection (Vector3.forward) * 10;
    35.         // E Key Handler
    36.         if (Input.GetKeyDown (inputManagerDatabase.StorageKeyCode)) {
    37.             if (Physics.Raycast (transform.position, (forward), out hit)) {
    38.                 theDistance = hit.distance;
    39.  
    40.                 if (theDistance <= 1.6f && hit.collider.gameObject.tag == "StorageBoxGeneric") {
    41.                     CmdStorageBoxGenericBusy(hit.collider.gameObject);
    42.                 }
    43.             }
    44.         }
    45.         // End E Key Handler
    46.     }
    47.  
    48.  
    49.     [Command]
    50.     void CmdStorageBoxGenericBusy(GameObject g) {
    51.         STI = g.GetComponent<StorageInventory> ();
    52.         if (STI.isBusy == true) {
    53.             STI.CmdSetBusy (false);
    54.             StartCoroutine(STI.OpenInventoryWithTimer());
    55.         } else {
    56.             STI.CmdSetBusy (true);
    57.             StartCoroutine(STI.OpenInventoryWithTimer());
    58.         }
    59.     }
    60.  
    61. }
    62.  
    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?
     
    Last edited: Sep 27, 2017
  2. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    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.
     
    cyunide likes this.
  3. cyunide

    cyunide

    Joined:
    Sep 20, 2017
    Posts:
    9
    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:
    Code (CSharp):
    1.  
    2. public void OpenInventoryWithTimer() {
    3.         if (showStorage == true && isBusy == true) {
    4.             Debug.Log ("Opening");
    5.             //startTimer = Time.time;
    6.             //showTimer = true;
    7.             //yield return new WaitForSeconds (timeToOpenStorage);
    8.             inv.ItemsInInventory.Clear ();
    9.             inventory.SetActive (true);
    10.             addItemsToInventory ();
    11.             //showTimer = false;
    12.             //if (timer != null)
    13.                 //timer.SetActive (false);
    14.         } else {
    15.             Debug.Log ("Closing");
    16.             storageItems.Clear ();
    17.             setListofStorage ();
    18.             inventory.SetActive (false);
    19.             inv.deleteAllItems ();
    20.             tooltip.deactivateTooltip ();
    21.         }
    22.     }
    23.  
    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!
     
    Last edited: Sep 27, 2017
  4. cyunide

    cyunide

    Joined:
    Sep 20, 2017
    Posts:
    9
    Joe I also wanted to ask you, would I be better off using authority like this: http://answers.unity3d.com/questions/1245341/unet-how-do-i-properly-handle-client-authority-wit.html ?

    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?
     
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    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
     
  6. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    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.
     
  7. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    ^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.
     
  8. Njb_

    Njb_

    Joined:
    Jun 3, 2015
    Posts:
    20
    use targetRPC