Search Unity

(Solved)Networking: Spawn an item on client only

Discussion in 'Scripting' started by tijanikun, Jan 19, 2019.

  1. tijanikun

    tijanikun

    Joined:
    Aug 10, 2017
    Posts:
    129
    Hello,
    I got some trouble with an ability that allow you to spawn an item when warp to it's location by pressing again:
    It works on host side, i can warp to the item with a "Findwithtag", i trrying to set a client only spawnable item because i don't want to other player to warp on wrong item cause of tag.

    Actually with this script the item spawn on host only:

    Spell script:
    Code (CSharp):
    1. void Update()
    2. {
    3. if (Input.GetButtonDown("Ability4"))
    4.                 {
    5.                     if (!isLocalPlayer)
    6.                         return;
    7.  
    8.                     if (teleport == false)
    9.                     {
    10.                         CmdBaliseDrop();
    11.                     }
    12.                     if (teleport == true)
    13.                     {
    14.                         teleport = false;
    15.                     }
    16.                     else
    17.                     {
    18.                         teleport = true;
    19.                     }
    20.                                    }
    21. }
    22. [Command]
    23.     void CmdBaliseDrop()
    24.     {
    25.         GameObject instance = Instantiate(Balise, magicspawn1.position, cameratest.GetComponent<Transform>().rotation) as GameObject;
    26.     }
    27.  
    28.  
    Player script:
    Code (CSharp):
    1. if (Input.GetButtonDown("Ability4"))
    2.                 {
    3.                     if (teleport == false)
    4.                     {
    5.                         teleport = true;
    6.                     }
    7.                     if (teleport == true)
    8.                     {
    9.  
    10.                         GameObject balise = GameObject.FindWithTag("balise");
    11.  
    12.                         playerminato.transform.position = balise.transform.position;
    13.                         WarpSound.Play();
    14.                     }
    15.                     isCoolDown4 = true;
    16.                     nextFireTime10 = Time.time + cooldowntime10;
    17.                 }
    Can you help me please ?
     
  2. tijanikun

    tijanikun

    Joined:
    Aug 10, 2017
    Posts:
    129
  3. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    You're instantiating an object on the server only.

    If you want to create a tracker for the client to return to, do something like this:
    (Tracker creation):
    1. Send a Cmd to the server.
    2. Make server spawn / create object to track the position.
    3. Keep a track for the created tracker - something like Dictionary<clientId, Tracker>;
    4. Add the tracker based on the clientId which has sent this command;

    clientId - is just an (u)int that can be fetched from the network manager / player object
    (I think. Haven't used UNet / Mirror in a while. Just an identifier to determine which player controls which object).
    Tracker can be a struct which contains current player's position / rotation (Vector3, Quaternion);

    4. Send a targeted RPC to the client, to notify it that tracker has been spawned.

    When client X requests a teleport:
    (Teleport request)
    1. Send a Cmd to the server.
    2. Check on the server if the tracker for the specific player exists (Dictionary.TryGetValue(clientId, out var Tracker)).
    3. Move position of the player on the server or do nothing if the tracker doesn't exist.
    4. (Optional) Send targeted rpc to that client to update position / rotation.

    Also, Don't use .FindWithTag, as that may be inconsistent. Especially over network.
    Also, forget about "host" architecture. There's always only client(s), and a server.
    Host is just server and a client running on the same instance.
    By doing so, you'll get less confused on how things should communicate.
     
    Last edited: Jan 21, 2019
  4. tijanikun

    tijanikun

    Joined:
    Aug 10, 2017
    Posts:
    129
    I never used dictionary until now, can u explain a bit more the part about Dictionary<clientId, Tracker> please ?
     
  5. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
  6. tijanikun

    tijanikun

    Joined:
    Aug 10, 2017
    Posts:
    129
    I didn't understand with that link, so many information, i don't know what to do o_o'
    How do i lock the balise on the dictionary ? Please

    "3. Keep a track for the created tracker - something like Dictionary<clientId, Tracker>;"
     
  7. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    // Server context / Pseudo-code
    Code (CSharp):
    1. private Dictionary<int, Tracker> _lookup = new Dictionary<int, Tracker>();
    2.  
    3. private void OnCmdSetTracker(){
    4.     // get a client id first
    5.     // int clientId = ...;
    6.     // Get client position and rotation
    7.     Tracker tracker = new Tracker();
    8.      // Set position and rotation of the player to the tracker
    9.     _lookup[clientId] = tracker;
    10. }
    // On teleport request
    Code (CSharp):
    1. private void OnCmdTeleportRequest() {
    2.     // Fetch the client id
    3.     int clientId = ...;
    4.     // Grab the previous tracker:
    5.     Tracker tracker;
    6.     if (!_lookup.TryGetValue(clientId, out tracker)) {
    7.           // No tracker available. Do nothing, or send no tracker rpc to the client
    8.           return;    
    9.     }
    10.  
    11.     // Grab the player and set the position / rotation based on the tracker object
    12. }
    It's not that difficult. Just read the manual.
     
  8. tijanikun

    tijanikun

    Joined:
    Aug 10, 2017
    Posts:
    129
    Code (CSharp):
    1.  
    2.     private void OnCmdSetTracker()
    3.     {
    4.         m_Identity = GetComponent<NetworkIdentity>();
    5.         int clientID = m_Identity.connectionToClient;
    6.         // get a client id first
    7.         // int clientId = ...;
    8.         // Get client position and rotation
    9.         Vector3 BalisePosition = gameObject.transform.position;
    10.         GameObject tracker = new GameObject();
    11.         // Set position and rotation of the player to the tracker
    12.         _lookup[clientId] = tracker;
    13.     }
    I got an error with client id int: cannot convert to int

    lookup[clientId] = tracker; : clientID doesn't exist in this context

    private Dictionary<int, GameObject> _lookup = new Dictionary<int, GameObject>();
    i replaced Traker by GameObject because it said that doesn't exist too
    I think that won't work with gameobject...
     
  9. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    It is a pseudo-code after all. :p

    Tracker is just an example.
    Note that you don't need gameobjects for this. Something like struct with position / rotation will do.

    Also, NetworkIdentity.connectionToClient is not an int. It's a NetworkIdentity.connectionToClient.connectionId that is int.
     
  10. tijanikun

    tijanikun

    Joined:
    Aug 10, 2017
    Posts:
    129
    What should i write in that

    struct Tracker { } ?

    Code (CSharp):
    1.  
    2.     private void OnCmdSetTracker()
    3.     {
    4.        NetworkIdentity m_Identity = GetComponent<NetworkIdentity>();
    5.         int clientID = m_Identity.connectionToClient.connectionId;
    6.         // get a client id first
    7.         // int clientId = ...;
    8.         // Get client position and rotation
    9.        Vector3 BalisePosition = gameObject.transform.position;
    10.         GameObject tracker = new GameObject();
    11.         // Set position and rotation of the player to the tracker
    12.         _lookup[clientId] = tracker;
    13.     }
    Thank you for your help, but i still need information please
     
  11. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Something like this:
    Code (CSharp):
    1. public struct Tracker {
    2.      public Vector3 Position;
    3.      public Quaternion Rotation;
    4. }
    Then assign the position / rotation of the player to it:
    Code (CSharp):
    1. Tracker tracker = new Tracker {
    2.                   Position = // player position,
    3.                   Rotation = // player rotation
    4. };
     
  12. tijanikun

    tijanikun

    Joined:
    Aug 10, 2017
    Posts:
    129
    I had major problem on other script sorry for late answer,
    On Spell script:
    Code (CSharp):
    1.  
    2. if (Input.GetButtonDown("Ability4"))
    3.                 {
    4.                     if (!isLocalPlayer)
    5.                         return;
    6.  
    7.                     if (teleport == false)
    8.                     {
    9.                         OnCmdSetTracker();
    10.                        // CmdBaliseDrop();
    11.                     }
    12.                     if (teleport == true)
    13.                     {
    14.                         teleport = false;
    15.                     }
    16.                     else
    17.                     {
    18.                         teleport = true;
    19.                     }
    20.                     nextFireTime2 = Time.time + cooldowntime2;
    21.                 }
    22. public void OnCmdSetTracker()
    23.     {
    24.  
    25.         // get a client id first
    26.         NetworkIdentity m_Identity = GetComponent<NetworkIdentity>();
    27.         // int clientId = ...;
    28.         int clientID = m_Identity.connectionToClient.connectionId;
    29.         // Get client position and rotation
    30.         Tracker tracker = new Tracker
    31.         {
    32.             Position = gameObject.transform.position,
    33.             Rotation = gameObject.transform.rotation
    34.         };
    35.         // Set position and rotation of the player to the tracker
    36.         _lookup[clientID] = tracker;
    37.     }
    38.     public void OnCmdTeleportRequest()
    39.     {
    40.         NetworkIdentity m_Identity = GetComponent<NetworkIdentity>();
    41.         // int clientId = ...;
    42.         int clientID = m_Identity.connectionToClient.connectionId;
    43.         // Fetch the client id
    44.         // Grab the previous tracker:
    45.         Tracker tracker;
    46.         if (!_lookup.TryGetValue(clientID, out tracker))
    47.         {
    48.             // No tracker available. Do nothing, or send no tracker rpc to the client
    49.             return;
    50.         }
    51.         gameObject.transform.position = tracker.Position;
    52.         // Grab the player and set the position / rotation based on the tracker object
    53.     }
    On PlayerController script(they are both on same gameobject):
    Code (CSharp):
    1. if (Input.GetButtonDown("Ability4"))
    2.                 {
    3.                     if (teleport == false)
    4.                     {
    5.                         teleport = true;
    6.                     }
    7.                     if (teleport == true)
    8.                     {
    9.                         gameObject.GetComponent<ShotSpellMinato>().OnCmdTeleportRequest();
    10.                         WarpSound.Play();
    11.                     }
    12.                     isCoolDown4 = true;
    13.                     nextFireTime10 = Time.time + cooldowntime10;
    14.                 }
    It's working perfectly for the "host"(client who create the room, note that it's a matchmaking system), but for client who join i got this error: (look screenshot)

    It's the same error as my other thread here: https://forum.unity.com/threads/rep...an-instance-of-an-object.617566/#post-4136566

    Have you any idea?
    Thank you for your help until now by the way
     

    Attached Files:

  13. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    It's seems like null is either NetworkIdentity, or the lookup itself.
    Hard to tell without seeing actual script, as all line numbers are incorrect.

    Are you sure you're not calling the method directly on the client? It supposed to be called only on server.
    (Does it have [Command] on it?)
     
    Last edited: Jan 23, 2019
  14. tijanikun

    tijanikun

    Joined:
    Aug 10, 2017
    Posts:
    129
    Code (CSharp):
    1. [Command]
    2.     public void CmdSetTracker()
    3.     {
    4.  
    5.         // get a client id first
    6.         NetworkIdentity m_Identity = GetComponent<NetworkIdentity>();
    7.         // int clientId = ...;
    8.         int clientID = m_Identity.connectionToClient.connectionId;
    9.         // Get client position and rotation
    10.         Tracker tracker = new Tracker
    11.         {
    12.             Position = gameObject.transform.position,
    13.             Rotation = gameObject.transform.rotation
    14.         };
    15.         // Set position and rotation of the player to the tracker
    16.         _lookup[clientID] = tracker;
    17.     }
    18.     [Command]
    19.     public void CmdTeleportRequest()
    20.     {
    21.         NetworkIdentity m_Identity = GetComponent<NetworkIdentity>();
    22.         // int clientId = ...;
    23.         int clientID = m_Identity.connectionToClient.connectionId;
    24.         // Fetch the client id
    25.         // Grab the previous tracker:
    26.         Tracker tracker;
    27.         if (!_lookup.TryGetValue(clientID, out tracker))
    28.         {
    29.             // No tracker available. Do nothing, or send no tracker rpc to the client
    30.             return;
    31.         }
    32.         gameObject.transform.position = tracker.Position;
    33.         // Grab the player and set the position / rotation based on the tracker object
    34.     }
    With that i got no error on console but the client doesn't warp :/

    Since some days i got this error
    A connection has already been set as ready. There can only be one.
    UnityEngine.Networking.NetworkIdentity:UNetStaticUpdate()
    Check screenshot, i don't know why I have that, can it make some problem for the client?
    I heard that can be a problem with the networkmanager script, but i didn't find out the solution...
    Here NetworkManager script:
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.Networking;
    6. using UnityEngine.Networking.Match;
    7.  
    8. public class MyLobby : NetworkManager {
    9.     public GameObject LobbyOption;
    10.     public GameObject TitleScreen;
    11.     private bool TitleBool;
    12.     public enum Selection { none, minato, saitama, goku, war };
    13.     public Selection currentSelect;
    14.     private float nextRefreshTime;
    15.     //  public GameObject MinatoButton;
    16.     // public bool minato;
    17.     GameObject playerselected;
    18.     public GameObject MinatoPlayer;
    19. //   public GameObject SaitamaButton;
    20.    // public bool Saitama;
    21.     public GameObject SaitamaPlayer;
    22. //   public GameObject GokuButton;
    23.   //  public bool Goku;
    24.     public GameObject GokuPlayer;
    25. //    public GameObject WarButton;
    26.    // public bool War;
    27.     public GameObject WarPlayer;
    28.  
    29.  
    30.    
    31.     public void StartHosting()
    32.     {
    33.         StartMatchMaker();
    34.         matchMaker.CreateMatch("Jasons Match", 4, true, "", "", "", 0, 0, OnMatchCreated);
    35.        
    36.     }
    37.  
    38.     private void OnMatchCreated(bool success, string extendedInfo, MatchInfo responseData)
    39.     {
    40.         base.StartHost(responseData);
    41.  
    42.         RefreshMatches();
    43.     }
    44.     private void Update()
    45.     {
    46.         if (Time.time >= nextRefreshTime)
    47.         {
    48.             RefreshMatches();
    49.         }
    50.        
    51.         if(currentSelect == Selection.war)
    52.         {
    53.             playerselected = WarPlayer;
    54.         }
    55.         if (currentSelect == Selection.minato)
    56.         {
    57.             playerselected = MinatoPlayer;
    58.         }
    59.         if (currentSelect == Selection.saitama)
    60.         {
    61.             playerselected = SaitamaPlayer;
    62.         }
    63.         if (currentSelect == Selection.goku)
    64.         {
    65.             playerselected = GokuPlayer;
    66.         }
    67.         // currentSelect = MinatoButton.GetComponent<CharacterSelectionScreen>().currentSelect;
    68.         //  Goku = GokuButton.GetComponent<CharacterSelectionScreen>().GokuSelected;
    69.         // Saitama = SaitamaButton.GetComponent<CharacterSelectionScreen>().SaitamaSelected;
    70.         // War = WarButton.GetComponent<CharacterSelectionScreen>().WarSelected;
    71.     }
    72.  
    73.     private void RefreshMatches()
    74.     {
    75.         nextRefreshTime = Time.time + 5f;
    76.         if (matchMaker == null);
    77.         StartMatchMaker();
    78.  
    79.         matchMaker.ListMatches(0, 10, "", true, 0, 0, HandleListMatchesComplete);
    80.  
    81.     }
    82.  
    83.     public void JoinMatch(MatchInfoSnapshot match)
    84.     {
    85.         if (matchMaker == null)
    86.             StartMatchMaker();
    87.  
    88.         matchMaker.JoinMatch(match.networkId, "", "", "", 0, 0, HandleJoinedMatch);
    89.     }
    90.  
    91.     private void HandleJoinedMatch(bool success, string extendedInfo, MatchInfo responseData)
    92.     {
    93.         StartClient(responseData);
    94.     }
    95.  
    96.     private void HandleListMatchesComplete(bool success, string extendedInfo, List<MatchInfoSnapshot> responseData)
    97.     {
    98.         AvaibleMatchesList.HandleNewMatchList(responseData);
    99.     }
    100.     public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
    101.     {
    102.         TitleScreen.SetActive(false);
    103.        LobbyOption.SetActive(true);
    104.             GameObject player = (GameObject)Instantiate(playerPrefab, Vector3.zero, Quaternion.identity);
    105.          
    106.             NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
    107.  
    108.     }
    109.     // if (team == 1)
    110.     //    { player.GetComponent<MinatoController_Net>().team = 1; }
    111.     //int team = TeamHandler.GetComponent<TeamHandler>().team;
    112.  
    113.    
    114.     public void Wargreymon()
    115.     {
    116.         //  LobbySelect.GetComponent<MyLobby>().currentSelect = war;
    117.         currentSelect = Selection.war; // changes currentSel to Up.
    118.  
    119.     }
    120.  
    121.     //  MinatoButton.GetComponent<CharacterSelectionScreen>().MinatoSelected = false;
    122.     //    SaitamaButton.GetComponent<CharacterSelectionScreen>().SaitamaSelected = false;
    123.     //   GokuButton.GetComponent<CharacterSelectionScreen>().GokuSelected = false;
    124.     //   WarSelected = true;
    125.  
    126.     public void Minato()
    127.     {
    128.         currentSelect = Selection.minato; // changes currentSel to Up.
    129.                                           //  WarButton.GetComponent<CharacterSelectionScreen>().WarSelected = false;
    130.                                           //  SaitamaButton.GetComponent<CharacterSelectionScreen>().SaitamaSelected = false;
    131.                                           //   GokuButton.GetComponent<CharacterSelectionScreen>().GokuSelected = false;
    132.                                           //   MinatoSelected = true;
    133.     }
    134.     public void Goku()
    135.     {
    136.         currentSelect = Selection.goku; // changes currentSel to Up.
    137.                                         //  WarButton.GetComponent<CharacterSelectionScreen>().WarSelected = false;      
    138.                                         //   MinatoButton.GetComponent<CharacterSelectionScreen>().MinatoSelected = false;
    139.                                         //   SaitamaButton.GetComponent<CharacterSelectionScreen>().SaitamaSelected = false;
    140.                                         //   GokuSelected = true;
    141.     }
    142.     public void Saitama()
    143.     {
    144.         currentSelect = Selection.saitama; // changes currentSel to Up.
    145.                                            //  WarButton.GetComponent<CharacterSelectionScreen>().WarSelected = false;
    146.                                            //  MinatoButton.GetComponent<CharacterSelectionScreen>().MinatoSelected = false;
    147.                                            ////  GokuButton.GetComponent<CharacterSelectionScreen>().GokuSelected = false;
    148.                                            //SaitamaSelected = true;
    149.     }
    150.         public void PlayerChoosed(NetworkIdentity player)
    151.         {
    152.  
    153.             var conn = player.connectionToClient;
    154.  
    155.             var newPlayer = Instantiate<GameObject>(playerselected);
    156.  
    157.         LobbyOption.SetActive(false);
    158.             Destroy(player.gameObject);
    159.             NetworkServer.ReplacePlayerForConnection(conn, newPlayer, 0);
    160.         }
    161.    
    162. }
    163.  
     

    Attached Files:

    Last edited: Jan 23, 2019
  15. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Try outputing a debug inside this block, it might be that you're fetching invalid client it:
    Code (CSharp):
    1. if (!_lookup.TryGetValue(clientID, out tracker))
    2.         {
    3.             Debug.LogWarning("No tracker found for "+clientId);
    4.             // No tracker available. Do nothing, or send no tracker rpc to the client
    5.             return;
    6.         }
    It should print it out on the server / host. Otherwise see if the command is called on the server. Debug until it works :)
     
  16. tijanikun

    tijanikun

    Joined:
    Aug 10, 2017
    Posts:
    129
    I have no Debug either server or client
    I just noticed from scene server view, that when i use teleport request, i see the player warping, but in client view he stays on same position and walk from that position when he moves (in scene view he warp back to normal position with move input)
    I also notied that when a client join, the server spawn 2 playerPrefabs, one is only on the server view and doesn't move but got collider.
     
  17. tijanikun

    tijanikun

    Joined:
    Aug 10, 2017
    Posts:
    129
  18. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Make sure you've enabled warnings in the console (previous screenshot has them disabled).

    That piece of code is supposed to work on the server, so I suggest looking at the console on the host / server side after client has performed an action. Moreover, I suggest asking specific questions about synchronization on the multiplayer subforum.