Search Unity

Pattern for ServerAuthority Variables and Validation when changing those

Discussion in 'Netcode for GameObjects' started by imakemygame, Oct 27, 2022.

  1. imakemygame

    imakemygame

    Joined:
    Mar 3, 2013
    Posts:
    38
    Hi folks,
    I am quite confused with all the videos and tutorials. My endgoal for now is to have the Multiplayer just for anti cheat purpose, so it is important that the clients actions are validated by the host/server

    When looking into this I am super confused. I do see that there are NetworkVariables and ServerRpcs

    Now my idea was that when some value has to change, the client has no rights to just change the value itself. So I have declared a NetworkVariable and when the client needs to change that the client calls a ServerRpc where I then pass the ClientId, and do something like:
    Code (CSharp):
    1. var player = NetworkManager.Singleton.ConnectedClients[clientId].PlayerObject.GetComponent<MultiplayerPlayerscript>();
    2. player.randomNumber.Value = i;
    in this case "randomNumber" is my NetworkVariable

    while having this function I am able on the server to do some validation and just return if this is not valid or if so, set this value, like above

    But now I have to questions:
    1) is this a best practice approach for doing stuff like this?
    2) subscribing to the .OnValueChanged for this NetworkVariable is super confusing, since this is called on every client with the same value, even that I have set it for a very specific gameobject. I thought that every <MultiplayerPlayerscript> has its own variables and values and that this is not the same for all clients

    Thank you. And maybe some of you have a good documentation, I take this with love ;)
     
  2. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    A network variable is a variable that is the same for every clinet/server in the same game. This means that if you have a <MultiplayerPlayerscript> script and it has a network variable, the network variable will be synced across all players and all <MultiplayerPlayerscript> scripts and have the same value. This can be usefull if for example you have a player's health and you want all the other players to be able to see it (So you make a network variable called player1Health and it will sync across all clients).
    Changing a value on serverRPC like you did is the best solution I know. This way the server can check if the input is valid and block hackers(as long as the Host player which is a server and a client won't be a hacker himself).
     
    imakemygame and wanglong-2016 like this.
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,882
    What type of game is it? And what type of cheats are you trying to prevent?

    If this is a single-player only game that only requires connecting to a server in order to do cheat protection, then I can only advise against that. Players won't like the lag, or having to connect to an online server for a single-player game, and in the end, hey, it's a single player game - let them cheat any way they want! ;)

    This would also require a good amount of development time that's better spent making the game, publishing it, and in case it goes through the roof and becomes popular THEN you can take some measures against the cheats that are actually harming the game.
     
    wanglong-2016 likes this.
  4. imakemygame

    imakemygame

    Joined:
    Mar 3, 2013
    Posts:
    38
    it is going to be a web3 game and it makes no sense without anti cheat since it is kinda P2E

    @lavagoatGG thanks for giving me this hint. So I guess I need a a custom struct representing all userdata for each user in a List<struct> - now it is a bit clearer to me. Thanks
     
  5. imakemygame

    imakemygame

    Joined:
    Mar 3, 2013
    Posts:
    38
    @lavagoatGG now I found the time to play around with it again and I must say that this works not like that. Have we cross talked?

    I just print the value for testing like this:

    Code (CSharp):
    1. Debug.log($"client with id  {NetworkManager.Singleton.LocalClientId}  ({OwnerClientId})  has currently value = {randomNumber.Value}");        
    and this gives me different values for each client since I set them on the specific GameObject Component. So it seems that every client component holds his own NetworkVariable.Value

    I thought I must have a NetworkList for all the properties of the different Clients but it seems that I just need a List with all the References to the clients to cache them and not having this GetComponent each time

    Have I misunderstood what you posted?

    I guess the NetworkVariable is only the same for all if they grab it from the server, if the server sets the value on his own script/component or if setting the value is not limited to server execution like ServerRpc
     
  6. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    Wierd... Is the debug log called on each client or on the server? From what I know the variable should be the same if it is on a networkBehavior script on a spawned network object. What do you mean by "I set them on the specific GameObject Component"?
     
  7. imakemygame

    imakemygame

    Joined:
    Mar 3, 2013
    Posts:
    38

    for testing I just have a button that prints the NetworkVariable.Value
    and another button that sets the NetworkVariable.Value
    with "I set them on the specific GameObject Component" I mean that if you look to my ServerRpc method I use the clientId and use that with the NetworkManager.Singleton.ConnectedClients Array to get the .PlayerObject and on this the <MultiplayerPlayerscript> that points straight to my very specific NetworkVariable

    When printing the values with my button I get n rows with respect to the client count. And each client has its own value for randomNumber. So I still wonder how this is usually solved. I am kinda confused since there are so much different approaches everywhere. I keep struggling with this whole concept and architecture and lack of good documentation

    Code (CSharp):
    1. public class MultiplayerPlayerscript : NetworkBehaviour
    2. {
    3.     private Button buttonPrint;
    4.     private Button buttonTest;
    5.  
    6.     private NetworkVariable<int> randomNumber = new NetworkVariable<int>(1);
    7.  
    8.     private void Start()
    9.     {
    10.          buttonPrint = GameObject.Find("Button - print").GetComponent<Button>();
    11.          buttonPrint.onClick.AddListener(() =>
    12.         {
    13.             PrintNetworkVariableValue();
    14.         });
    15.  
    16.         buttonTest = GameObject.Find("Button - Test").GetComponent<Button>();
    17.         buttonTest.onClick.AddListener(() =>
    18.         {
    19.             TestServerRpc(UnityEngine.Random.Range(0, 5),                    NetworkManager.Singleton.LocalClientId);
    20.         });
    21.     }
    22.  
    23.     [ServerRpc(RequireOwnership = false)]
    24.     private void TestServerRpc(int i, ulong clientId)
    25.     {  
    26.         var player = NetworkManager.Singleton.ConnectedClients[clientId].PlayerObject.GetComponent<MultiplayerPlayerscript>();
    27.         player.randomNumber.Value = i;
    28.  
    29.      }
    30. }
     
  8. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    If you want to do it the way you do now, you should use regular variables and not network ones. This way each client will have different values to their variables. If you want the variables to be synced across all players, you shuld use :

    [LIST=1]
    [*] [ServerRpc(RequireOwnership = false)]
    [*] private void TestServerRpc(int i, ulong clientId)
    [*] {
    [*] randomNumber.Value = i;
    [*]

    [*] }
    [/LIST]

    This will change the value of the network variable acroos all clients and trigger the OnValueChanged callback.
     
  9. imakemygame

    imakemygame

    Joined:
    Mar 3, 2013
    Posts:
    38
    if I use regular variables they are not server authorized? And that is my main aim since otherwise they are no safe from being memory hacked on the client, right?
     
  10. lavagoatGG

    lavagoatGG

    Joined:
    Apr 16, 2022
    Posts:
    229
    If you have onlt one player then or a fixed amount of players then you can use network variables but else I don't know any way netcode for gameobject can help you vertify players actions...
     
  11. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    As you discovered changes to network variables only apply to that particular object, so multiple objects of the same type will have different values.
    1. This does seem to be the most common approach. You can give the client permission to access network variables but that comes with its own caveats.
    2. You'll want to filter out the change events you're not interested in, for example conditionally check for IsLocalPlayer or IsOwner if you only want to act on those. There are other options but that's the simplest one.
     
  12. imakemygame

    imakemygame

    Joined:
    Mar 3, 2013
    Posts:
    38
    I tried now what you mentioned earlier in your post with just having "randomNumber.Value = i;" in the ServerRpc call but the behaviour is exactly the same
    still every 'OwnerClientId' has its own value when printing the value of the NetworkVariable
    thank you very much for this confirmation and answers
    One last question please: I learned from a yt video that code using the clientId and then get the component with "NetworkManager.Singleton.ConnectedClients[clientId].PlayerObject.GetComponent<MultiplayerPlayerscript>()" to change the value of this NetworkVariable. Now when trying something around this I just updated the value on this NetworkVariable without reference to this GameObject from this client but just inside the ServerRpc like this

    Code (CSharp):
    1.  [ServerRpc(RequireOwnership = false)]
    2. private void TestServerRpc(int i, ulong clientId)
    3. {
    4.       randomNumber.Value = i;
    5. }
    and still magically it sets the value of the client that called this ServerRpc. Is this working implicitly because inside this ServerRpc every usage of variables are pointing to the component reference of the client that has called this ServerRpc?
     
  13. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    The ServerRpc code will run on the server-side counterpart of that object so that is the correct way to do it. I don't know the technical details but the network manager takes care of it using the networkId to find the correct object.
     
    imakemygame likes this.