Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Call function on clients from server?

Discussion in 'Multiplayer' started by Happy_Jingle, Dec 14, 2015.

  1. Happy_Jingle

    Happy_Jingle

    Joined:
    Mar 9, 2014
    Posts:
    103
    Code (CSharp):
    1.    
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine.UI;
    6. using UnityEngine.Networking;
    7. using System;
    8.  
    9. public class MapGen : NetworkBehaviour {
    10.  
    11. [Server] void DisplayMap() //creates prefabs
    12.     {
    13.         for (int y = 0; y < height; y++)
    14.         {
    15.             for (int x = 0; x < width; x++)
    16.             {
    17.                 count += 1;
    18.                 if(map [x,y] == 0){
    19.                     float holdX;
    20.                     float holdY;
    21.                     if (y % 2 == 0)
    22.                     {
    23.                         holdX = x + 0.5f;
    24.                     }
    25.                     else
    26.                     {
    27.                         holdX = x;
    28.                     }
    29.  
    30.                     holdY = y - ((float)y) / 8;
    31.  
    32.  
    33.                     GameObject cloneGameObject = (GameObject)Instantiate(hex, new Vector3(holdX, 0, holdY), Quaternion.Euler(270, 90, 0));
    34.  
    35.                     NetworkServer.Spawn(cloneGameObject);
    36.                     cloneGameObject.GetComponent<Tile>().SetHoleServer(); // SEEMS TO BE WORKING
    37.                     cloneGameObject.GetComponent<Tile>().RpcSetHoleClient();
    38.  
    39.  
    40.  
    41.  
    42.  
    43.  
    44.                      }
    45.                         }
    46.                 }
    47.         }
    48. }
    Code (CSharp):
    1. using System.Collections;
    2. using System;
    3. using System.Linq;
    4. using UnityEngine;
    5. using System.Collections.Generic;
    6. using UnityEngine.Networking;
    7. public class Tile : NetworkBehaviour
    8. {  
    9.     [SyncVar]
    10.     public Renderer rend;
    11.     public bool isHole = false;
    12.  
    13.     [ClientRpc]
    14.     public void RpcSetHoleClient(){
    15.         if (!isServer) {
    16.             Debug.Log ("THE CLIENT IS CALLING SETHOLE");
    17.             if(isHole == true){
    18.                 rend = GetComponent<Renderer> ();
    19.                 rend.enabled = false;
    20.             }
    21.         }
    22.     }
    23. }
    I'm not able to get the message THE CLIENT IS CALLING SETHOLE, any advice?
     
  2. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    use OnStartClient() ?
     
  3. Happy_Jingle

    Happy_Jingle

    Joined:
    Mar 9, 2014
    Posts:
    103
    I'm not super certain of how I'm to use OnStartClient(), I tried adding this to my Tile class:
    Code (CSharp):
    1. using System.Collections;
    2. using System;
    3. using System.Linq;
    4. using UnityEngine;
    5. using System.Collections.Generic;
    6. using UnityEngine.Networking;
    7. public class Tile : NetworkBehaviour
    8. {
    9.     [SyncVar]
    10.     public Renderer rend;
    11.     [SyncVar]
    12.     public bool isHole = false;
    13.    
    14.     public override void OnStartClient(){
    15.     RpcSetHoleClient ();
    16.     }
    17.     [ClientRpc]
    18.     public void RpcSetHoleClient(){
    19.         if (!isServer) {
    20.             Debug.Log ("THE CLIENT IS CALLING SETHOLE");
    21.             if(isHole == true){
    22.                 rend = GetComponent<Renderer> ();
    23.                 rend.enabled = false;
    24.             }
    25.         }
    26.     }
    27. }
    And then instead of calling RpcSetHoleClient() in my MapGen Class I did:
    Code (CSharp):
    1.                     cloneGameObject.GetComponent<Tile>().OnStartClient();
    Which didn't work
     
  4. Happy_Jingle

    Happy_Jingle

    Joined:
    Mar 9, 2014
    Posts:
    103
    Okay! I seem to have got it
    Code (CSharp):
    1.     public override void OnStartClient(){
    2.         if(isHole == true){
    3.             blocked = true;
    4.             rend = GetComponent<Renderer> ();
    5.             rend.enabled = false;
    6.         }
    7.     }
    Just have to do
    Code (CSharp):
    1. cloneGameObject.GetComponent<Tile>().isHole = true;
    Before spawning it
     
  5. ObliviousHarmony

    ObliviousHarmony

    Joined:
    Jul 3, 2014
    Posts:
    79
    Hello Jingle,

    To be clear, a client cannot call a ClientRpc method, only the server can. On the client, the method will do nothing, and on the server, it will cause it to be called on all of the clients that have it in scope.

    As an addition to this, since you're using a SyncVar for your "isHole" member, you could also use a hook method to perform the update that you desire, in case the value ever changes on the server. Lastly, I'm not aware, but does the SyncVar on the Renderer actually work for replicating the reference, if it's not attached to a NetworkIdentity object?
     
  6. jathrek

    jathrek

    Joined:
    Jul 16, 2015
    Posts:
    36
    I see you're generating a map server side.

    How big is it? I tried generating such a map, with a dimension like 128 * 32. At that point, whenever a real client (so not the local client from the host application) was connecting, I got some "Message too big" errors, seemingly due to the amount of "blocks" being sent from the server to the new client.

    Did you encounter this issue? How did you manage it?

    Also, it seems to me that your original strategy of using a RPC for each time was quite overkilling; indeed, the client representation of each tile contained all the data required to handle the rendering updates, so adding a communication overhead on top of that was unnecessary.

    Now, I'm wondering if there's any difference in you using "OnStartClient" or "OnStart" to handle the deactivation of the renderer? (Because I've been using "OnStart" until now; not knowing about "OnStartClient" ;-p)