Only the host player can be killed properly

I'm testing basic HP/death mechanics. The basic architecture is from the Fusion Outer Loop sample project: a Player is instantiated for each client, and can then spawn a Character as their avatar.

Right now, if the host dies, their character despawns, their death counter is incremented, and they respawn after 2 seconds, exactly as intended. But if a client dies, their character despawns, but they are never marked as dead, and so they never respawn.

I have the following in the Character script:

[Networked(OnChanged = nameof(OnHPChanged))]
public int hp { get; set; }

public void OnTakeDamage(int damage)
{
    hp -= damage;
    if (hp <= 0) Die();
}

void Die()
{
    if (Object.HasStateAuthority) App.Instance.Session.Map.KillPlayer(player);
}

And then the function in question on the Map manager:

public void KillPlayer(Player ply)
{
    ply.RPC_SetDead(true);
    ply.RPC_SetCanSpawn(false);
    ply.RPC_IncrementDeaths();
    DespawnAvatar(ply);
    if (ply == App.Instance.GetPlayer()) changeCharacterButton.SetActive(true);
    StartCoroutine(EnableRespawn(ply));
}

IEnumerator EnableRespawn(Player ply)
{
    yield return new WaitForSeconds(2f);
    ply.RPC_SetCanSpawn(true);
}

If I remove the check for state authority in Die(), clients can respawn, but client deaths are recorded twice (I assume because the RPCs in KillPlayer() are sent twice).

I'm sure there's a simple fix - what am I doing wrong here?

Answers

  • Hi,

    First thing, could you share the implementation or at least the filters of those RPCs?

    And also, try not to use a coroutine, a TickTimer is ideal for that.

    -----

    Isaac Augusto

    Photon Fusion Team

  • Sure thing. They're super simple.

    [Networked] public NetworkBool dead { get; set; }
    [Networked] public NetworkBool canSpawn { get; set; }
    [Networked] public int deaths { get; set; }
    
    [Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
    public void RPC_SetDead(bool isDead)
    {
        dead = isDead;
    }
    
    
    [Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
    public void RPC_SetCanSpawn(bool spawn)
    {
        canSpawn = spawn;
    }
    
    
    [Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
    public void RPC_IncrementDeaths()
    {
        deaths++;
    }
    

    To be quite honest, I hadn't considered the RPC filters. I hadn't been changing them from the basic example version.

  • Okay, As i can see, those RPCs are not necessary, since they only change a property that's networked anyway.

    Your problem probably lives on the RPC filters, you're saying that only the input authority should call the RPC, but you're culling to only the state authority call the function that call the RPCs. So when the client dies, the state authority will run the KillPlayer() but will not call the rpcs (because the state authority is only input authority of him own avatar)

    Replace those RPCs calls with their actual implementation and the problem should be fixed.

    -----

    Isaac Augusto

    Photon Fusion Team

  • I'll be honest, it took me a while to figure out what you meant, since I know networked properties can only be changed by the host. Eventually I figured out you meant I should leave in the cull to state authority, but make the RPCs local methods instead, and then it worked.

    I also got it working by changing the filters on those RPCs to RpcSources.StateAuthority as well, but I think I prefer your solution. It makes more sense to me if only the host is ever allowed to handle player death.

    Thanks a lot for replying, I really appreciate that you guys are here to help out with user questions.