How to change the client variable value by the server

In my game every player can spawn a bomb, but they need to ask to the server, its like a bomberman game, where if you place a bomb on the ground you need to wait for it to explode so you can place another one, I’m doing this using an coroutine…

Checks for the input and the position, and ask to the server


   private void Update
   {
    CanPlaceBombServerRpc(x, y, z);
     ....
   }

    [Rpc(SendTo.Server)]
    private void CanPlaceBombServerRpc(int bombsRemaining, bool hasPressedAttack, Vector2 position)
    {
        (int x, int y) = ConvertPositionToGrid(position);

        bool hasBomb = _manageDrops.CheckBombs(x, y);

        if(bombsRemaining > 0 && hasPressedAttack && !hasBomb)
            StartCoroutine(WaitBomb(position, RpcTarget.Single(OwnerClientId, RpcTargetUse.Temp)));
    }

(Notice that in the CanPlaceBomb method I check for the number of bombs my client can use…)

And then the server spawn the bomb for the client, the bomb is being spawned and after a time it explode and destroy perfectly

    private IEnumerator WaitBomb(Vector2 position, RpcParams rpcParams) 
    {
        ulong clientId = rpcParams.Receive.SenderClientId;
        var clientObj = NetworkManager.Singleton.ConnectedClients[clientId].PlayerObject;
        PlayerBomb clientScript = clientObj.GetComponent<PlayerBomb>();

        clientScript._bombsRemaining.Value--;

        SpawnBombServerRpc(position);

        yield return new WaitForSeconds(_bombFuseTime);

        StartExplosionServerRpc(position, _explosionRadius);

        DestroyBombServerRpc(position);

        clientScript._bombsRemaining.Value++;
    }

The problem is that I’m not being able to change the client _bombsremaining variable inside the coroutine, Im doing a debug.log and it seems that the clientId is always 0 for the client and host.

At the moment the client can spawn infinite bombs because its variable never changes

How can I change the client variable in the correct way?

Ah, I dont want to check every frame if I can spawn a bomb, is there any other way to not check every single time?

That’s because your entire logic only runs on the server, since you start the coroutine from the Server RPC.

It’s not a good idea to use a Coroutine here, much less so is using WaitForSeconds which is a real-time. You need to measure time in server ticks otherwise some bombs explode earlier than others due to the number of frames not being a constant (eg framerate drops, but real time still ticks on at the same rate).

Instead, spawn a bomb prefab. The bomb itself ought to handle its destruction after var timeToDie = Time.time + _timeToLive; has elapsed. Then it spawns an explosion prefab.

The _bombsRemaining should be a NetworkVariable. You modify it on the clientScript instance but that’s the one on the server. The clientScript instance on the client will not get its _bombsRemaining changed unless it’s a NetworkVariable.

Aaaah I totally forgot about this XD, but I swear I tried using networkVariables, anyways Im going to try again!

i really didnt know that, I thought using coroutine is better than decreasing the time, mainly because the name WaitForSeconds implies that it does not depend on user frames

Going to refactor my code :student:

Ok, it worked fine :slight_smile:

I have some questions though.
The first one is about the networkVariables, when I ask the server to execute a method using server rpc call, and inside this method I change the value of a networkvariable, does the server understands who is the client asking for the execution ? It seems like this, but idk because I only tested with one client and 1 host so far.

    [Rpc(SendTo.Server)]
    public void AddRadiusServerRpc(int amount)
    {
        if (_explosionRadius.Value + amount < _maxRadius)
            _explosionRadius.Value += amount;
        else
            _explosionRadius.Value = _maxRadius;
    }

Another thing is about the measuring the time in server ticks, I tried to do this line of code:

But the variable timeToDie starts to get higher every second because of its nature.

The time at the beginning of the current frame in seconds since the start of the application (Read Only).

For know Im doing the basic
Idk if this is enough to guarantee a fair game system

if(TimeToDie < 0)
   Die()
else
   TimeToDie -= Time.deltaTime;

No it does not understand that. The NetworkVariable by default is only server-writable, or you could set it to owner-writable. I assume in this case it works because the variable is server-writable. In all other cases you’ll get a permission error.

It also doesn’t matter who requested that change. The net var will update for everyone.

When it comes to implementing timer, I have a strong preference to record the time something should happen or should stop happening as a point in future. Like so:

public float TimeToLive = 2f;
private float TimeToDie;

// when resetting that time, eg after spawn
TimeToDie = Time.time + TimeToLive;

// and then check if that time has elapsed every update:
if (Time.time >= TimeToDie)
    Die();

I would not accumulate (or subtract) delta times simply because this may deviate over longer periods of times due to inherent inaccuracies in floating point values. It’s likely negligible as is the add/sub operation but a simple check if the current time exceeds the event time is easier to read and a bit more elegant.

It also gives you more options ie you may decide that you do need realTime or unscaledTime.

So if Client 1 pick a powerup, both the Client 1 and Client 2 will get the powerup?

Perhaps I didnt explain it right, its weird to me because I would assume that when a server execute a method requested by a client the server would change the networkvariable for itself not for other clients. Thats why I asked that, but it seems the contrary by your explanation, if I have more than 1 client all the clients will have its variables changed when one client ask to change> Is this correct? Then even the host would have its variables changed when another player pick a powerup but this didnt happen.

Also thanks for ur explanation about a lot of other questions, ur help means a lot to me, and my game is getting better faster because of this foruns !

No becausse they are (supposed to be) separate instances. Client1 player object changes its own var and does not affect client 2 player object’s powerup netvar.

Think of having two objects in the scene with the same components. You can change the fields for one object in the Inspector without affecting the values of the other object’s scripts.