using System; using Unity.BossRoom.Gameplay.Configuration; using Unity.BossRoom.Utils; using Unity.Netcode; namespace Unity.BossRoom.Gameplay.GameState { /// /// Common data and RPCs for the CharSelect stage. /// public class NetworkCharSelection : NetworkBehaviour { public enum SeatState : byte { Inactive, Active, LockedIn, } /// /// Describes one of the players in the lobby, and their current character-select status. /// /// /// Putting FixedString inside an INetworkSerializeByMemcpy struct is not recommended because it will lose the /// bandwidth optimization provided by INetworkSerializable -- an empty FixedString128Bytes serialized normally /// or through INetworkSerializable will use 4 bytes of bandwidth, but inside an INetworkSerializeByMemcpy, that /// same empty value would consume 132 bytes of bandwidth. /// public struct LobbyPlayerState : INetworkSerializable, IEquatable { public ulong ClientId; private FixedPlayerName m_PlayerName; // I'm sad there's no 256Bytes fixed list :( public int PlayerNumber; // this player's assigned "P#". (0=P1, 1=P2, etc.) public int SeatIdx; // the latest seat they were in. -1 means none public float LastChangeTime; public SeatState SeatState; public LobbyPlayerState(ulong clientId, string name, int playerNumber, SeatState state, int seatIdx = -1, float lastChangeTime = 0) { ClientId = clientId; PlayerNumber = playerNumber; SeatState = state; SeatIdx = seatIdx; LastChangeTime = lastChangeTime; m_PlayerName = new FixedPlayerName(); PlayerName = name; } public string PlayerName { get => m_PlayerName; private set => m_PlayerName = value; } public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { serializer.SerializeValue(ref ClientId); serializer.SerializeValue(ref m_PlayerName); serializer.SerializeValue(ref PlayerNumber); serializer.SerializeValue(ref SeatState); serializer.SerializeValue(ref SeatIdx); serializer.SerializeValue(ref LastChangeTime); } public bool Equals(LobbyPlayerState other) { return ClientId == other.ClientId && m_PlayerName.Equals(other.m_PlayerName) && PlayerNumber == other.PlayerNumber && SeatIdx == other.SeatIdx && LastChangeTime.Equals(other.LastChangeTime) && SeatState == other.SeatState; } } private NetworkList m_LobbyPlayers; public Avatar[] AvatarConfiguration; private void Awake() { m_LobbyPlayers = new NetworkList(); } /// /// Current state of all players in the lobby. /// public NetworkList LobbyPlayers => m_LobbyPlayers; /// /// When this becomes true, the lobby is closed and in process of terminating (switching to gameplay). /// public NetworkVariable IsLobbyClosed { get; } = new NetworkVariable(false); /// /// Server notification when a client requests a different lobby-seat, or locks in their seat choice /// public event Action OnClientChangedSeat; /// /// RPC to notify the server that a client has chosen a seat. /// [Rpc(SendTo.Server, RequireOwnership = false)] public void ServerChangeSeatRpc(ulong clientId, int seatIdx, bool lockedIn) { OnClientChangedSeat?.Invoke(clientId, seatIdx, lockedIn); } } }