RPC calling a client to block its movement

Hi everyone,
I’m fairly new in Godot & working on a mechanism and need some advice / insights.

In my Multiplayer game I have two types of players being a Survivor and a Blob.
I need a mechanism where the Blob can temporarily “absorb” a survivor.

This would mean that if the blob gets close enough to the survivor, he starts absorbing, and does damage to the survivor. both the survivor and blob should be temporarily blocked from movement.
Currently I have the following code, but this does not really seem to work. My RPC calls aren’t correctly sent/received for some reason…

... code to check if the monster is close enough to a survivor.

 // If a survivor is found within range, request to absorb
        if (closestSurvivor != null && !_isAbsorbing)
        {
            if (!Multiplayer.IsServer())
            {
                GD.Print("Requesting to absorb survivor: " + closestSurvivor.Name);
                RpcId(SERVER_ID, nameof(RequestAbsorbSurvivor), closestSurvivor.GetPath());
            }
            else
            {
                AbsorbSurvivor(closestSurvivor, SERVER_ID);
            }
        }
    [Rpc(MultiplayerApi.RpcMode.AnyPeer)]
    public void RequestAbsorbSurvivor(NodePath survivorPath)
    {
        if (!Multiplayer.IsServer())
            return;

        Survivor survivor = GetNode<Survivor>(survivorPath);
        if (survivor != null && !_isAbsorbing)
        {
            AbsorbSurvivor(survivor, Multiplayer.GetRemoteSenderId());
        }
    }
    public async void AbsorbSurvivor(Survivor targetSurvivor, int clientBlobId)
    {
        _isAbsorbing = true;

        BlockOwnMovementOrRpcToClients(targetSurvivor, clientBlobId, true);

        // Wait for the eating duration
        await Task.Delay((int)(absorbDuration * 1000));


        targetSurvivor.TakeDamage(absorbDamage);
        BlockOwnMovementOrRpcToClients(targetSurvivor, clientBlobId, false);
        
        _isAbsorbing = false;
    }
    private void BlockOwnMovementOrRpcToClients(Survivor survivor, int clientBlobId, bool block)
    {
        // Immobilize both Blob and Survivor. Survivor.Name is set to the clientId on player spawning
        int survivorClientId = int.Parse(survivor.Name);

        if (survivorClientId == SERVER_ID)
        {
            survivor.BlockMovement(block);
            RpcId(clientBlobId, nameof(BlockMovement), block);
        }
        else if (clientBlobId == SERVER_ID)
        {
            this.BlockMovement(block);
            RpcId(survivorClientId, nameof(BlockMovement), block);
        }
        else
        {
            RpcId(survivorClientId, nameof(BlockMovement), block);
            RpcId(clientBlobId, nameof(BlockMovement), block);
        }
    }

So basically I’m checking if the blob/survivor is the server, and if it is, block the movement of the player locally and otherwise send out an RPC call to the specific clientId of the blob/player.
For some reason, RPC calls seem to be correctly sent out but are not received on the clients while the clientIDs are correct.

This whole mess made me realise that I might just be doing this mechanic wrong so that’s why I need some input from the professionals themselves :slight_smile:
If you see any mistakes in my code, feel free to correct me! I’m here to learn :slight_smile:

Don’t worry to ask for more questions if you feel like this is not enough information.
Thanks! <3

I would avoid rpc completely and setup behavior that runs locally, all while synchronizing state with a MultiplayerSynchronizer.

The authority ( the server ) will detect the interaction. You just need to get the state to synchronize on the other end.

The client could do its own detection just don’t allow it to actually change the state and delete the object. Just have it update any visual aspects. And let the server authority do the real work.

Hi!
Thanks for your answer!

How should I go about syncrhonizing the state with a MultiplayerSyncrhonizer?
I give the local client authority over its own player node and thus also the MultiplayerSynchronizer.

Is there a way for the server to “override” the authority and force-set the state?
Or should I temporarily give the server authority, change the state and give it back?

Both the “blob” and “survivor” will be seperate clients that both need their movement blocked.

I usually think central authority is easier, and more fair than a peer-to-peer setup, to code. I’m not sure what path you took but i will try to envision what i think should happen.

Survivor and blob are both clients that we will call characters generically. The authority is a dedicated server.

The characters are spawned as a scene with two MultiplayerSynchronizers each.

The first is an input node that is authed to the client which sends player input to the server. To optimize this you can send a bit mask that represents the character controls. (Up, down, left, right, dodge, etc.)

The second is the character state. ( Position, rotation, velocity, health, state(alive, dead, disabled, etc.)) This will be sent from the server, and will change depending on the client input and what is happening in the world.

As is there may be an inherent delay from client input to server response. This can be mitigated with client side prediction, but is out of scope with this example.

When a blob touches a survivor you want both to be disabled. But if the blob thinks he got the survivor, and the survivor thinks he got away you have a conflict. Network games have a limitations of time delay and what each client sees will be different for each client so we need the authority to make the call.

The authority will adjust the state of the blob and survivor to be “disabled” when it detects the collision. And ignore the input coming in, sending the state changes all back to the clients.

Clients will see the character internal state change and play different animations like damage and what not.

You should be able to use the exact same code on the client side that the server will also use you will just need to feel out some edge cases. Like you don’t want to stop sending input if you the the client detected a collision and should be disabled, let the authority make the call and always send the input.

You should be able to allow the client change the state, as long as you don’t delete anything. The server will set it to the correct value when the data arrives.