using System; using UnityEngine; namespace LobbyRelaySample { /// /// Current state of the user in the lobby. /// This is a Flags enum to allow for the Inspector to select multiples for various UI features. /// [Flags] public enum UserStatus { None = 0, Connecting = 1, // User has joined a lobby but has not yet connected to Relay. Lobby = 2, // User is in a lobby and connected to Relay. Ready = 4, // User has selected the ready button, to ready for the "game" to start. InGame = 8, // User is part of a "game" that has started. Menu = 16 // User is not in a lobby, in one of the main menus. } /// /// Data for a local player instance. This will update data and is observed to know when to push local player changes to the entire lobby. /// [Serializable] public class LobbyUser : Observed { public LobbyUser(bool isHost = false, string displayName = null, string id = null, EmoteType emote = EmoteType.None, UserStatus userStatus = UserStatus.Menu, bool isApproved = false) { m_data = new UserData(isHost, displayName, id, emote, userStatus, isApproved); } #region Local UserData public struct UserData { public bool IsHost { get; set; } public string DisplayName { get; set; } public string ID { get; set; } public EmoteType Emote { get; set; } public UserStatus UserStatus { get; set; } public bool IsApproved { get; set; } public UserData(bool isHost, string displayName, string id, EmoteType emote, UserStatus userStatus, bool isApproved) { IsHost = isHost; DisplayName = displayName; ID = id; Emote = emote; UserStatus = userStatus; IsApproved = isApproved; } } private UserData m_data; public void ResetState() { m_data = new UserData(false, m_data.DisplayName, m_data.ID, EmoteType.None, UserStatus.Menu, false); // ID and DisplayName should persist since this might be the local user. } #endregion /// /// Used for limiting costly OnChanged actions to just the members which actually changed. /// [Flags] public enum UserMembers { IsHost = 1, DisplayName = 2, Emote = 4, ID = 8, UserStatus = 16, IsApproved = 32 } private UserMembers m_lastChanged; public UserMembers LastChanged => m_lastChanged; public bool IsHost { get { return m_data.IsHost; } set { if (m_data.IsHost != value) { m_data.IsHost = value; m_lastChanged = UserMembers.IsHost; OnChanged(this); if (value) IsApproved = true; } } } public string DisplayName { get => m_data.DisplayName; set { if (m_data.DisplayName != value) { m_data.DisplayName = value; m_lastChanged = UserMembers.DisplayName; OnChanged(this); } } } public EmoteType Emote { get => m_data.Emote; set { if (m_data.Emote != value) { m_data.Emote = value; m_lastChanged = UserMembers.Emote; OnChanged(this); } } } public string ID { get => m_data.ID; set { if (m_data.ID != value) { m_data.ID = value; m_lastChanged = UserMembers.ID; OnChanged(this); } } } UserStatus m_userStatus = UserStatus.Menu; public UserStatus UserStatus { get => m_userStatus; set { if (m_userStatus != value) { m_userStatus = value; m_lastChanged = UserMembers.UserStatus; OnChanged(this); } } } public bool IsApproved // Clients joining the lobby should be approved by the host before they can interact. { get => m_data.IsApproved; set { if (!m_data.IsApproved && value) // Don't be un-approved except by a call to ResetState. { m_data.IsApproved = value; m_lastChanged = UserMembers.IsApproved; OnChanged(this); Locator.Get.Messenger.OnReceiveMessage(MessageType.ClientUserApproved, null); } } } public override void CopyObserved(LobbyUser observed) { UserData data = observed.m_data; int lastChanged = // Set flags just for the members that will be changed. (m_data.IsHost == data.IsHost ? 0 : (int)UserMembers.IsHost) | (m_data.DisplayName == data.DisplayName ? 0 : (int)UserMembers.DisplayName) | (m_data.ID == data.ID ? 0 : (int)UserMembers.ID) | (m_data.Emote == data.Emote ? 0 : (int)UserMembers.Emote) | (m_data.UserStatus == data.UserStatus ? 0 : (int)UserMembers.UserStatus); if (lastChanged == 0) // Ensure something actually changed. return; m_data = data; m_lastChanged = (UserMembers)lastChanged; OnChanged(this); } } }