UnityJacob
4 年前
当前提交
85de15c7
共有 58 个文件被更改,包括 1761 次插入 和 1159 次删除
-
3.gitignore
-
16Assets/Prefabs/UI/LobbyCodeCanvas.prefab
-
53Assets/Scenes/mainScene.unity
-
135Assets/Scripts/Entities/GameStateManager.cs
-
4Assets/Scripts/Entities/LobbyServiceData.cs
-
542Assets/Scripts/Entities/LocalLobby.cs
-
24Assets/Scripts/Tests/Editor/LobbyTests.cs
-
4Assets/Scripts/UI/CountdownUI.cs
-
4Assets/Scripts/UI/CreateMenuUI.cs
-
2Assets/Scripts/UI/JoinCreateRoomUI.cs
-
37Assets/Scripts/UI/JoinMenuUI.cs
-
12Assets/Scripts/UI/LobbyButtonUI.cs
-
4Assets/Scripts/UI/LobbyStateVisibilityUI.cs
-
6Assets/Scripts/UI/LobbyUsersUI.cs
-
4Assets/Scripts/UI/RelayCodeUI.cs
-
8Assets/Scripts/UI/RoomCodeUI.cs
-
4Assets/Scripts/UI/ServerAddressUI.cs
-
4Assets/Scripts/UI/ServerNameUI.cs
-
4Assets/TempDeleteAllRooms.cs
-
200Assets/Scripts/Infrastructure/Locator.cs
-
126Assets/Scripts/Infrastructure/LogHandler.cs
-
176Assets/Scripts/Infrastructure/Messenger.cs
-
276Assets/Scripts/Infrastructure/UpdateSlow.cs
-
4Assets/Scripts/Entities/LocalLobbyObserver.cs
-
63Assets/Scripts/Entities/ReadyCheck.cs
-
130Assets/Scripts/Tests/PlayMode/LobbyReadyCheckTests.cs
-
163Assets/Scripts/Tests/PlayMode/LobbyRoundtripTests.cs
-
108Assets/Scripts/Lobby/LobbyAPIInterface.cs
-
218Assets/Scripts/Lobby/LobbyAsyncRequests.cs
-
109Assets/Scripts/Lobby/LobbyContentHeartbeat.cs
-
23Assets/Scripts/Lobby/LobbyListHeartbeat.cs
-
89Assets/Scripts/Lobby/ToLocalLobby.cs
-
4Assets/Scripts/Entities/LobbyDataObserver.cs
-
67Assets/Scripts/Entities/LobbyReadyCheck.cs
-
131Assets/Scripts/Tests/PlayMode/ReadyCheckTests.cs
-
163Assets/Scripts/Tests/PlayMode/RoomsRoundtripTests.cs
-
0/Assets/Scripts/Entities/LocalLobby.cs.meta
-
0/Assets/Scripts/Entities/LocalLobbyObserver.cs.meta
-
0/Assets/Scripts/Entities/ReadyCheck.cs.meta
-
0/Assets/Scripts/Entities/LocalLobby.cs
-
0/Assets/Scripts/Tests/PlayMode/LobbyReadyCheckTests.cs.meta
-
0/Assets/Scripts/Tests/PlayMode/LobbyRoundtripTests.cs.meta
-
0/Assets/Scripts/Lobby.meta
-
0/Assets/Scripts/Infrastructure/Locator.cs.meta
-
0/Assets/Scripts/Infrastructure/LogHandler.cs.meta
-
0/Assets/Scripts/Infrastructure/Messenger.cs.meta
-
0/Assets/Scripts/Infrastructure/UpdateSlow.cs.meta
-
0/Assets/Scripts/Infrastructure/Locator.cs
-
0/Assets/Scripts/Infrastructure/LogHandler.cs
-
0/Assets/Scripts/Infrastructure/Messenger.cs
-
0/Assets/Scripts/Infrastructure/UpdateSlow.cs
-
0/Assets/Scripts/Lobby/LobbyContentHeartbeat.cs.meta
-
0/Assets/Scripts/Lobby/LobbyAPIInterface.cs.meta
-
0/Assets/Scripts/Lobby/LobbyListHeartbeat.cs.meta
-
0/Assets/Scripts/Lobby/LobbyAsyncRequests.cs.meta
-
0/Assets/Scripts/Lobby/ToLocalLobby.cs.meta
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UnityEngine; |
|||
|
|||
namespace LobbyRelaySample |
|||
{ |
|||
[Flags] |
|||
public enum LobbyState |
|||
{ |
|||
Lobby = 1, |
|||
CountDown = 2, |
|||
InGame = 4 |
|||
} |
|||
|
|||
public struct LobbyInfo |
|||
{ |
|||
public string RoomID { get; set; } |
|||
public string RoomCode { get; set; } |
|||
public string RelayCode { get; set; } |
|||
public string LobbyName { get; set; } |
|||
public bool Private { get; set; } |
|||
public int MaxPlayerCount { get; set; } |
|||
public LobbyState State { get; set; } |
|||
public long? AllPlayersReadyTime { get; set; } |
|||
|
|||
public LobbyInfo(LobbyInfo existing) |
|||
{ |
|||
RoomID = existing.RoomID; |
|||
RoomCode = existing.RoomCode; |
|||
RelayCode = existing.RelayCode; |
|||
LobbyName = existing.LobbyName; |
|||
Private = existing.Private; |
|||
MaxPlayerCount = existing.MaxPlayerCount; |
|||
State = existing.State; |
|||
AllPlayersReadyTime = existing.AllPlayersReadyTime; |
|||
} |
|||
|
|||
public LobbyInfo(string roomCode) |
|||
{ |
|||
RoomID = null; |
|||
RoomCode = roomCode; |
|||
RelayCode = null; |
|||
LobbyName = null; |
|||
Private = false; |
|||
MaxPlayerCount = -1; |
|||
State = LobbyState.Lobby; |
|||
AllPlayersReadyTime = null; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The local lobby data that the game can observe
|
|||
/// </summary>
|
|||
[System.Serializable] |
|||
public class LobbyData : Observed<LobbyData> |
|||
{ |
|||
Dictionary<string, LobbyUser> m_LobbyUsers = new Dictionary<string, LobbyUser>(); |
|||
public Dictionary<string, LobbyUser> LobbyUsers => m_LobbyUsers; |
|||
|
|||
#region LocalLobbyData
|
|||
private LobbyInfo m_data; |
|||
public LobbyInfo Data |
|||
{ |
|||
get { return new LobbyInfo(m_data); } |
|||
} |
|||
|
|||
float m_CountDownTime; |
|||
|
|||
public float CountDownTime |
|||
{ |
|||
get { return m_CountDownTime; } |
|||
set |
|||
{ |
|||
m_CountDownTime = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
DateTime m_TargetEndTime; |
|||
|
|||
public DateTime TargetEndTime |
|||
{ |
|||
get => m_TargetEndTime; |
|||
set |
|||
{ |
|||
m_TargetEndTime = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
ServerAddress m_relayServer; |
|||
|
|||
public ServerAddress RelayServer |
|||
{ |
|||
get => m_relayServer; |
|||
set |
|||
{ |
|||
m_relayServer = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
public void AddPlayer(LobbyUser user) |
|||
{ |
|||
if (m_LobbyUsers.ContainsKey(user.ID)) |
|||
{ |
|||
Debug.LogError($"Cant add player {user.DisplayName}({user.ID}) to room: {RoomID} twice"); |
|||
return; |
|||
} |
|||
|
|||
DoAddPlayer(user); |
|||
OnChanged(this); |
|||
} |
|||
|
|||
private void DoAddPlayer(LobbyUser user) |
|||
{ |
|||
m_LobbyUsers.Add(user.ID, user); |
|||
user.onChanged += OnChangedUser; |
|||
} |
|||
|
|||
public void RemovePlayer(LobbyUser user) |
|||
{ |
|||
DoRemoveUser(user); |
|||
OnChanged(this); |
|||
} |
|||
|
|||
private void DoRemoveUser(LobbyUser user) |
|||
{ |
|||
if (!m_LobbyUsers.ContainsKey(user.ID)) |
|||
{ |
|||
Debug.LogWarning($"Player {user.DisplayName}({user.ID}) does not exist in room: {RoomID}"); |
|||
return; |
|||
} |
|||
|
|||
m_LobbyUsers.Remove(user.ID); |
|||
user.onChanged -= OnChangedUser; |
|||
} |
|||
|
|||
private void OnChangedUser(LobbyUser user) |
|||
{ |
|||
OnChanged(this); |
|||
} |
|||
|
|||
public string RoomID |
|||
{ |
|||
get => m_data.RoomID; |
|||
set |
|||
{ |
|||
m_data.RoomID = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public string RoomCode |
|||
{ |
|||
get => m_data.RoomCode; |
|||
set |
|||
{ |
|||
m_data.RoomCode = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public string RelayCode |
|||
{ |
|||
get => m_data.RelayCode; |
|||
set |
|||
{ |
|||
m_data.RelayCode = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public string LobbyName |
|||
{ |
|||
get => m_data.LobbyName; |
|||
set |
|||
{ |
|||
m_data.LobbyName = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public LobbyState State |
|||
{ |
|||
get => m_data.State; |
|||
set |
|||
{ |
|||
m_data.State = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public bool Private |
|||
{ |
|||
get => m_data.Private; |
|||
set |
|||
{ |
|||
m_data.Private = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public int PlayerCount => m_LobbyUsers.Count; |
|||
|
|||
public int MaxPlayerCount |
|||
{ |
|||
get => m_data.MaxPlayerCount; |
|||
set |
|||
{ |
|||
m_data.MaxPlayerCount = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public long? AllPlayersReadyTime => m_data.AllPlayersReadyTime; |
|||
|
|||
/// <summary>
|
|||
/// Checks if we have n players that have the Status.
|
|||
/// -1 Count means you need all Lobbyusers
|
|||
/// </summary>
|
|||
/// <returns>True if enough players are of the input status.</returns>
|
|||
public bool PlayersOfState(UserStatus status, int playersCount = -1) |
|||
{ |
|||
var statePlayers = m_LobbyUsers.Values.Count(user => user.UserStatus == status); |
|||
|
|||
if (playersCount < 0) |
|||
return statePlayers == m_LobbyUsers.Count; |
|||
return statePlayers == playersCount; |
|||
} |
|||
|
|||
public void CopyObserved(LobbyInfo info, Dictionary<string, LobbyUser> oldUsers) |
|||
{ |
|||
m_data = info; |
|||
if (oldUsers == null) |
|||
m_LobbyUsers = new Dictionary<string, LobbyUser>(); |
|||
else |
|||
{ |
|||
List<LobbyUser> toRemove = new List<LobbyUser>(); |
|||
foreach (var user in m_LobbyUsers) |
|||
{ |
|||
if (oldUsers.ContainsKey(user.Key)) |
|||
user.Value.CopyObserved(oldUsers[user.Key]); |
|||
else |
|||
toRemove.Add(user.Value); |
|||
} |
|||
|
|||
foreach (var remove in toRemove) |
|||
{ |
|||
DoRemoveUser(remove); |
|||
} |
|||
|
|||
foreach (var oldUser in oldUsers) |
|||
{ |
|||
if (!m_LobbyUsers.ContainsKey(oldUser.Key)) |
|||
DoAddPlayer(oldUser.Value); |
|||
} |
|||
} |
|||
|
|||
OnChanged(this); |
|||
} |
|||
|
|||
public override void CopyObserved(LobbyData oldObserved) |
|||
{ |
|||
CopyObserved(oldObserved.Data, oldObserved.m_LobbyUsers); |
|||
} |
|||
} |
|||
} |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using UnityEngine; |
|||
|
|||
namespace LobbyRelaySample |
|||
{ |
|||
[Flags] |
|||
public enum LobbyState |
|||
{ |
|||
Lobby = 1, |
|||
CountDown = 2, |
|||
InGame = 4 |
|||
} |
|||
|
|||
public struct LobbyInfo |
|||
{ |
|||
public string LobbyID { get; set; } |
|||
public string LobbyCode { get; set; } |
|||
public string RelayCode { get; set; } |
|||
public string LobbyName { get; set; } |
|||
public bool Private { get; set; } |
|||
public int MaxPlayerCount { get; set; } |
|||
public LobbyState State { get; set; } |
|||
public long? AllPlayersReadyTime { get; set; } |
|||
|
|||
public LobbyInfo(LobbyInfo existing) |
|||
{ |
|||
LobbyID = existing.LobbyID; |
|||
LobbyCode = existing.LobbyCode; |
|||
RelayCode = existing.RelayCode; |
|||
LobbyName = existing.LobbyName; |
|||
Private = existing.Private; |
|||
MaxPlayerCount = existing.MaxPlayerCount; |
|||
State = existing.State; |
|||
AllPlayersReadyTime = existing.AllPlayersReadyTime; |
|||
} |
|||
|
|||
public LobbyInfo(string lobbyCode) |
|||
{ |
|||
LobbyID = null; |
|||
LobbyCode = lobbyCode; |
|||
RelayCode = null; |
|||
LobbyName = null; |
|||
Private = false; |
|||
MaxPlayerCount = -1; |
|||
State = LobbyState.Lobby; |
|||
AllPlayersReadyTime = null; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The local lobby data that the game can observe
|
|||
/// </summary>
|
|||
[System.Serializable] |
|||
public class LocalLobby : Observed<LocalLobby> |
|||
{ |
|||
Dictionary<string, LobbyUser> m_LobbyUsers = new Dictionary<string, LobbyUser>(); |
|||
public Dictionary<string, LobbyUser> LobbyUsers => m_LobbyUsers; |
|||
|
|||
#region LocalLobbyData
|
|||
private LobbyInfo m_data; |
|||
public LobbyInfo Data |
|||
{ |
|||
get { return new LobbyInfo(m_data); } |
|||
} |
|||
|
|||
float m_CountDownTime; |
|||
|
|||
public float CountDownTime |
|||
{ |
|||
get { return m_CountDownTime; } |
|||
set |
|||
{ |
|||
m_CountDownTime = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
DateTime m_TargetEndTime; |
|||
|
|||
public DateTime TargetEndTime |
|||
{ |
|||
get => m_TargetEndTime; |
|||
set |
|||
{ |
|||
m_TargetEndTime = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
ServerAddress m_relayServer; |
|||
|
|||
public ServerAddress RelayServer |
|||
{ |
|||
get => m_relayServer; |
|||
set |
|||
{ |
|||
m_relayServer = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
public void AddPlayer(LobbyUser user) |
|||
{ |
|||
if (m_LobbyUsers.ContainsKey(user.ID)) |
|||
{ |
|||
Debug.LogError($"Cant add player {user.DisplayName}({user.ID}) to lobby: {LobbyID} twice"); |
|||
return; |
|||
} |
|||
|
|||
DoAddPlayer(user); |
|||
OnChanged(this); |
|||
} |
|||
|
|||
private void DoAddPlayer(LobbyUser user) |
|||
{ |
|||
m_LobbyUsers.Add(user.ID, user); |
|||
user.onChanged += OnChangedUser; |
|||
} |
|||
|
|||
public void RemovePlayer(LobbyUser user) |
|||
{ |
|||
DoRemoveUser(user); |
|||
OnChanged(this); |
|||
} |
|||
|
|||
private void DoRemoveUser(LobbyUser user) |
|||
{ |
|||
if (!m_LobbyUsers.ContainsKey(user.ID)) |
|||
{ |
|||
Debug.LogWarning($"Player {user.DisplayName}({user.ID}) does not exist in lobby: {LobbyID}"); |
|||
return; |
|||
} |
|||
|
|||
m_LobbyUsers.Remove(user.ID); |
|||
user.onChanged -= OnChangedUser; |
|||
} |
|||
|
|||
private void OnChangedUser(LobbyUser user) |
|||
{ |
|||
OnChanged(this); |
|||
} |
|||
|
|||
public string LobbyID |
|||
{ |
|||
get => m_data.LobbyID; |
|||
set |
|||
{ |
|||
m_data.LobbyID = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public string LobbyCode |
|||
{ |
|||
get => m_data.LobbyCode; |
|||
set |
|||
{ |
|||
m_data.LobbyCode = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public string RelayCode |
|||
{ |
|||
get => m_data.RelayCode; |
|||
set |
|||
{ |
|||
m_data.RelayCode = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public string LobbyName |
|||
{ |
|||
get => m_data.LobbyName; |
|||
set |
|||
{ |
|||
m_data.LobbyName = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public LobbyState State |
|||
{ |
|||
get => m_data.State; |
|||
set |
|||
{ |
|||
m_data.State = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public bool Private |
|||
{ |
|||
get => m_data.Private; |
|||
set |
|||
{ |
|||
m_data.Private = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public int PlayerCount => m_LobbyUsers.Count; |
|||
|
|||
public int MaxPlayerCount |
|||
{ |
|||
get => m_data.MaxPlayerCount; |
|||
set |
|||
{ |
|||
m_data.MaxPlayerCount = value; |
|||
OnChanged(this); |
|||
} |
|||
} |
|||
|
|||
public long? AllPlayersReadyTime => m_data.AllPlayersReadyTime; |
|||
|
|||
/// <summary>
|
|||
/// Checks if we have n players that have the Status.
|
|||
/// -1 Count means you need all Lobbyusers
|
|||
/// </summary>
|
|||
/// <returns>True if enough players are of the input status.</returns>
|
|||
public bool PlayersOfState(UserStatus status, int playersCount = -1) |
|||
{ |
|||
var statePlayers = m_LobbyUsers.Values.Count(user => user.UserStatus == status); |
|||
|
|||
if (playersCount < 0) |
|||
return statePlayers == m_LobbyUsers.Count; |
|||
return statePlayers == playersCount; |
|||
} |
|||
|
|||
public void CopyObserved(LobbyInfo info, Dictionary<string, LobbyUser> oldUsers) |
|||
{ |
|||
m_data = info; |
|||
if (oldUsers == null) |
|||
m_LobbyUsers = new Dictionary<string, LobbyUser>(); |
|||
else |
|||
{ |
|||
List<LobbyUser> toRemove = new List<LobbyUser>(); |
|||
foreach (var user in m_LobbyUsers) |
|||
{ |
|||
if (oldUsers.ContainsKey(user.Key)) |
|||
user.Value.CopyObserved(oldUsers[user.Key]); |
|||
else |
|||
toRemove.Add(user.Value); |
|||
} |
|||
|
|||
foreach (var remove in toRemove) |
|||
{ |
|||
DoRemoveUser(remove); |
|||
} |
|||
|
|||
foreach (var oldUser in oldUsers) |
|||
{ |
|||
if (!m_LobbyUsers.ContainsKey(oldUser.Key)) |
|||
DoAddPlayer(oldUser.Value); |
|||
} |
|||
} |
|||
|
|||
OnChanged(this); |
|||
} |
|||
|
|||
public override void CopyObserved(LocalLobby oldObserved) |
|||
{ |
|||
CopyObserved(oldObserved.Data, oldObserved.m_LobbyUsers); |
|||
} |
|||
} |
|||
} |
|
|||
using LobbyRelaySample.Auth; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LobbyRelaySample |
|||
{ |
|||
/// <summary>
|
|||
/// Allows Located services to transfer data to their replacements if needed.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The base interface type you want to Provide.</typeparam>
|
|||
public interface IProvidable<T> |
|||
{ |
|||
void OnReProvided(T previousProvider); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Base Locator behavior, without static access.
|
|||
/// </summary>
|
|||
public class LocatorBase |
|||
{ |
|||
private Dictionary<Type, object> m_provided = new Dictionary<Type, object>(); |
|||
|
|||
/// <summary>
|
|||
/// On construction, we can prepare default implementations of any services we expect to be required. This way, if for some reason the actual implementations
|
|||
/// are never Provided (e.g. for tests), nothing will break.
|
|||
/// </summary>
|
|||
public LocatorBase() |
|||
{ |
|||
Provide(new Messenger()); |
|||
Provide(new UpdateSlowNoop()); |
|||
Provide(new IdentityNoop()); |
|||
|
|||
FinishConstruction(); |
|||
} |
|||
|
|||
protected virtual void FinishConstruction() { } |
|||
|
|||
/// <summary>
|
|||
/// Call this to indicate that something is available for global access.
|
|||
/// </summary>
|
|||
private void ProvideAny<T>(T instance) where T : IProvidable<T> |
|||
{ |
|||
Type type = typeof(T); |
|||
if (m_provided.ContainsKey(type)) |
|||
{ |
|||
var previousProvision = (T)m_provided[type]; |
|||
instance.OnReProvided(previousProvision); |
|||
m_provided.Remove(type); |
|||
} |
|||
|
|||
m_provided.Add(type, instance); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// If a T has previously been Provided, this will retrieve it. Else, null is returned.
|
|||
/// </summary>
|
|||
private T Locate<T>() where T : class |
|||
{ |
|||
Type type = typeof(T); |
|||
if (!m_provided.ContainsKey(type)) |
|||
return null; |
|||
return m_provided[type] as T; |
|||
} |
|||
|
|||
// To limit global access to only components that should have it, and to reduce programmer error, we'll declare explicit flavors of Provide and getters for them.
|
|||
public IMessenger Messenger => Locate<IMessenger>(); |
|||
public void Provide(IMessenger messenger) { ProvideAny(messenger); } |
|||
|
|||
public IUpdateSlow UpdateSlow => Locate<IUpdateSlow>(); |
|||
public void Provide(IUpdateSlow updateSlow) { ProvideAny(updateSlow); } |
|||
|
|||
public IIdentity Identity => Locate<IIdentity>(); |
|||
public void Provide(IIdentity identity) { ProvideAny(identity); } |
|||
|
|||
// As you add more Provided types, be sure their default implementations are included in the constructor.
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Anything which provides itself to a Locator can then be globally accessed. This should be a single access point for things that *want* to be singleton (that is,
|
|||
/// when they want to be available for use by arbitrary, unknown clients) but might not always be available or might need alternate flavors for tests, logging, etc.
|
|||
/// </summary>
|
|||
public class Locator : LocatorBase |
|||
{ |
|||
private static Locator s_instance; |
|||
|
|||
public static Locator Get |
|||
{ |
|||
get |
|||
{ |
|||
if (s_instance == null) |
|||
s_instance = new Locator(); |
|||
return s_instance; |
|||
} |
|||
} |
|||
|
|||
protected override void FinishConstruction() |
|||
{ |
|||
s_instance = this; |
|||
} |
|||
} |
|||
using LobbyRelaySample.Auth; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LobbyRelaySample |
|||
{ |
|||
/// <summary>
|
|||
/// Allows Located services to transfer data to their replacements if needed.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The base interface type you want to Provide.</typeparam>
|
|||
public interface IProvidable<T> |
|||
{ |
|||
void OnReProvided(T previousProvider); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Base Locator behavior, without static access.
|
|||
/// </summary>
|
|||
public class LocatorBase |
|||
{ |
|||
private Dictionary<Type, object> m_provided = new Dictionary<Type, object>(); |
|||
|
|||
/// <summary>
|
|||
/// On construction, we can prepare default implementations of any services we expect to be required. This way, if for some reason the actual implementations
|
|||
/// are never Provided (e.g. for tests), nothing will break.
|
|||
/// </summary>
|
|||
public LocatorBase() |
|||
{ |
|||
Provide(new Messenger()); |
|||
Provide(new UpdateSlowNoop()); |
|||
Provide(new IdentityNoop()); |
|||
|
|||
FinishConstruction(); |
|||
} |
|||
|
|||
protected virtual void FinishConstruction() { } |
|||
|
|||
/// <summary>
|
|||
/// Call this to indicate that something is available for global access.
|
|||
/// </summary>
|
|||
private void ProvideAny<T>(T instance) where T : IProvidable<T> |
|||
{ |
|||
Type type = typeof(T); |
|||
if (m_provided.ContainsKey(type)) |
|||
{ |
|||
var previousProvision = (T)m_provided[type]; |
|||
instance.OnReProvided(previousProvision); |
|||
m_provided.Remove(type); |
|||
} |
|||
|
|||
m_provided.Add(type, instance); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// If a T has previously been Provided, this will retrieve it. Else, null is returned.
|
|||
/// </summary>
|
|||
private T Locate<T>() where T : class |
|||
{ |
|||
Type type = typeof(T); |
|||
if (!m_provided.ContainsKey(type)) |
|||
return null; |
|||
return m_provided[type] as T; |
|||
} |
|||
|
|||
// To limit global access to only components that should have it, and to reduce programmer error, we'll declare explicit flavors of Provide and getters for them.
|
|||
public IMessenger Messenger => Locate<IMessenger>(); |
|||
public void Provide(IMessenger messenger) { ProvideAny(messenger); } |
|||
|
|||
public IUpdateSlow UpdateSlow => Locate<IUpdateSlow>(); |
|||
public void Provide(IUpdateSlow updateSlow) { ProvideAny(updateSlow); } |
|||
|
|||
public IIdentity Identity => Locate<IIdentity>(); |
|||
public void Provide(IIdentity identity) { ProvideAny(identity); } |
|||
|
|||
// As you add more Provided types, be sure their default implementations are included in the constructor.
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Anything which provides itself to a Locator can then be globally accessed. This should be a single access point for things that *want* to be singleton (that is,
|
|||
/// when they want to be available for use by arbitrary, unknown clients) but might not always be available or might need alternate flavors for tests, logging, etc.
|
|||
/// </summary>
|
|||
public class Locator : LocatorBase |
|||
{ |
|||
private static Locator s_instance; |
|||
|
|||
public static Locator Get |
|||
{ |
|||
get |
|||
{ |
|||
if (s_instance == null) |
|||
s_instance = new Locator(); |
|||
return s_instance; |
|||
} |
|||
} |
|||
|
|||
protected override void FinishConstruction() |
|||
{ |
|||
s_instance = this; |
|||
} |
|||
} |
|||
} |
|
|||
using System; |
|||
using UnityEngine; |
|||
using Object = UnityEngine.Object; |
|||
|
|||
namespace LobbyRelaySample |
|||
{ |
|||
public enum LogMode |
|||
{ |
|||
Critical, // Errors only.
|
|||
Warnings, // Errors and Warnings
|
|||
Verbose // Everything
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Overrides the Default Unity Logging with our own
|
|||
/// </summary>
|
|||
public class LogHandler : ILogHandler |
|||
{ |
|||
public LogMode mode = LogMode.Critical; |
|||
|
|||
static LogHandler s_instance; |
|||
ILogHandler m_DefaultLogHandler = Debug.unityLogger.logHandler; //Store the unity default logger to print to console.
|
|||
|
|||
public static LogHandler Get() |
|||