#Advandtage / Disadvantage for Network Transform Authority Mode

1 messages · Page 1 of 1 (latest)

agile hemlock
#

Hi, I'm making a simple spawning system but encountered an issue with transporting the player. I fixed it by switching the Authority mode to server, however now my movement code doesn't work since I wrote it as client side (owner).
So I'm wondering what I should do, should I find another fix or should I rewrite the movement code to work server side?
What benefit or loss is there?

obtuse girder
#

Server Authority's downside is increased input latency, which you will have to deal with somehow.

With owner Auth, the server will have to tell the client where to spawn in an RPC. The downside there is less security, as the client could cheat.

agile hemlock
obtuse girder
# agile hemlock How severe is the latency and is it just plain advised to use owner?

The latency depends entirely on the client network connection. Could be 25ms or 250ms. What is acceptable depends on your game type. A card game is not going to matter as much as a fighting game.

Which one you use depends on how sensitive you are to potential cheating. A Co Op game with friends can be all client auth. But a competitive shooter would need to be server auth for fairness

agile hemlock
obtuse girder
agile hemlock
obtuse girder
agile hemlock
#
public class SpawnManager : MonoBehaviour
{
    private readonly List<SpawnPoint> spawnPoints = new();

    public void Awake()
    {
        spawnPoints.Clear();
        foreach (Transform child in transform)
        {
            spawnPoints.Add(new SpawnPoint
            {
                spTransform = child,
                IsUsed = false,
                OwnerClientId = null
            });
        }
    }

    public Transform ReserveSpawn(ulong clientId)
    {
        if (spawnPoints.Count == 0) return null;

        var sp = spawnPoints.Find(p => !p.IsUsed);
        if (sp == null)
            sp = spawnPoints[clientId.GetHashCode() % spawnPoints.Count];

        sp.IsUsed = true;
        sp.OwnerClientId = clientId;
        return sp.spTransform;
    }

    public void ReleaseSpawn(ulong clientId)
    {
        var sp = spawnPoints.Find(p => p.OwnerClientId == clientId);
        if (sp == null) return;

        sp.IsUsed = false;
        sp.OwnerClientId = null;
    }
}

public class SpawnPoint
{
    public Transform spTransform;

    public bool IsUsed;

    public ulong? OwnerClientId;

    //public MapType mapType { get; set; } // ADD ENUM

    //public float Radius { get; private set; } // ADD RADIUS

    // add rotation

    // 
}
#

And in the playermovement script

#
public override void OnNetworkSpawn()
{
    state = GetComponent<PlayerState>();

    IARefs.Clear();
    if (moveAction != null) IARefs.Add(moveAction);
    if (jumpAction != null) IARefs.Add(jumpAction);
    if (lookAction != null) IARefs.Add(lookAction);

    if (!IsOwner)
    {
        if (playerCamera) playerCamera.gameObject.SetActive(false);
        SetInputsEnabled(false);
        return;
    }
    if (IsServer)
    {
        var sp = GameManager.Instance.spawnManager.ReserveSpawn(OwnerClientId);
        if (sp != null) transform.SetPositionAndRotation(sp.position, sp.rotation);
    }

    Cursor.lockState = CursorLockMode.Locked;

    yaw = transform.eulerAngles.y;
    if (playerCamera) camLocalPos = playerCamera.localPosition;

    if (state != null)
    {
        state.IsAlive.OnValueChanged += OnAliveChanged;
        OnAliveChanged(true, state.IsAlive.Value);
    }
    else
    {
        SetInputsEnabled(true);
    }

    Debug.Log($"Spawned {gameObject.name} | IsOwner={IsOwner} IsClient={IsClient} IsServer={IsServer}");
}
#
public class GameManager : NetworkBehaviour
{
    public static GameManager Instance { get; private set; }

    public SpawnManager spawnManager;

    private readonly Dictionary<ulong, PlayerState> players = new();
    public IReadOnlyDictionary<ulong, PlayerState> Players => players;

    void Awake()
    {
        if (Instance != null && Instance != this) { Destroy(gameObject); return; }
        Instance = this;

        DontDestroyOnLoad(gameObject);
    }

    void Start()
    {
        NetworkManager.Singleton.OnServerStarted += OnServerStarted;
    }

    private void OnDestroy()
    {
        if (NetworkManager.Singleton == null) return;
        NetworkManager.Singleton.OnServerStarted -= OnServerStarted;
        NetworkManager.Singleton.OnClientConnectedCallback -= OnClientConnected;
    }

    private void OnServerStarted()
    {
        NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected;
        NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnected;

        foreach (var clientId in NetworkManager.Singleton.ConnectedClientsIds)
            OnClientConnected(clientId);

    }

    private void OnClientConnected(ulong clientId)
    {
        if (!IsServer) return;

        var playerObj = NetworkManager.Singleton.ConnectedClients[clientId].PlayerObject;
        if (playerObj == null) return;

        var sp = spawnManager.ReserveSpawn(clientId);
        if (sp == null) return;

        playerObj.transform.SetPositionAndRotation(sp.position, sp.rotation);
    }

    private void OnClientDisconnected(ulong clientId)
    {
        if (!IsServer) return;
        spawnManager.ReleaseSpawn(clientId);
    }


    public void RegisterPlayer(ulong clientId, PlayerState player)
    {
        if (!IsServer) return;
        players[clientId] = player;
    }

    public void UnregisterPlayer(ulong clientId)
    {
        if (!IsServer) return;
        players.Remove(clientId);
    }

}
obtuse girder
#

The server will not be able to set the player transform when you are using Owner Auth

agile hemlock
#

Ohhh right

#

So I replace IsServer with IsOwner

obtuse girder
#

You'll need to use an RPC for the server to tell the client which spawnpoint to use

agile hemlock
obtuse girder
#

ReserveSpawn is happening on the server. When it get a spawnpoint, it can send an RPC to the client with the spawnpoint position. That client can then set its player position

agile hemlock
#

Ahh right. I'll try that.

Would you say I'm doing server stuff at the moment on the player movement manager that I shouldn't be doing?

#

Should I be splitting some of this up?

obtuse girder
#

Should probably split it up. It should only be handling player movement. Since its Owner Auth there is nothing the server should be doing in there

agile hemlock
obtuse girder
agile hemlock
#

Does this seem correct?

#

Hmm, doesn't seem to work.
When the host connects it can move and look.
But when a client connects it freezes the host and the client can only look around

obtuse girder
#

Make sure there is only one active camera in the scene. Only the local player should have the camera

#

You should probably also disable the player input on the non local players if you are not already

agile hemlock
#

Should there not be a camera per player?

#

It is disabling the other camera actually

#

But I'm unsure what you mean with disabling input on non local players?

#

Found an easy way of sharing it

obtuse girder
#

Yea. whatever script is handling inputs should only be enabled for the local player.

agile hemlock
#

Which happens in OnNetworkSpawn like this:
[SerializeField] private PlayerSelfControlManager control;

state.IsAlive.OnValueChanged += OnAliveChanged;
OnAliveChanged(true, state.IsAlive.Value);
private void OnAliveChanged(bool oldVal, bool newVal)
{
    if (!IsOwner) return;

    if (control) control.enabled = newVal;

    Cursor.lockState = newVal ? CursorLockMode.Locked : CursorLockMode.None;
}
obtuse girder
agile hemlock
#
private void SetLocalOnly(bool enabled)
{
    if (playerCamera) playerCamera.gameObject.SetActive(enabled);
    if (audioListener) audioListener.enabled = enabled;
    if (control) control.enabled = enabled;
}
obtuse girder
#

for the remote players !IsOwner will be true

#

that's why you should check for IsLocalPlayer instead

agile hemlock
#

From Host view on host player

#

From client view of client player

#

But I'll try IsLocalPlayer

#

Same result

obtuse girder
agile hemlock
#

This is the hosts console after host joins

#

This is the clients console after client joins

agile hemlock
#

Should they not

obtuse girder
agile hemlock
#

@obtuse girder Hi, thanks for all the help, I got it working fully now.
I was wondering, since I'm making a prototype game to 1. learn unity more than I did 2. to get an idea if the game is fun, if I wanted the ability to setup a small playtest with another person or two, how would I do that?
I'm guessing first make a build of the game.
And I need to port forward my router, but the steps in between or after I'm a bit confused on