namespace Fusion { using System; using UnityEngine; using System.Collections.Generic; /// /// Companion component for . Automatically added as needed for rendering in-game networking IMGUI. /// [RequireComponent(typeof(FusionBootstrap))] [AddComponentMenu("Fusion/Fusion Boostrap Debug GUI")] [ScriptHelp(BackColor = ScriptHeaderBackColor.Steel)] public class FusionBootstrapDebugGUI : Fusion.Behaviour { /// /// When enabled, the in-game user interface buttons can be activated with the keys H (Host), S (Server) and C (Client). /// [InlineHelp] public bool EnableHotkeys; /// /// The GUISkin to use as the base for the scalable in-game UI. /// [InlineHelp] public GUISkin BaseSkin; FusionBootstrap _networkDebugStart; string _clientCount; bool _isMultiplePeerMode; Dictionary _nicifiedStageNames; #if UNITY_EDITOR protected virtual void Reset() { _networkDebugStart = EnsureNetworkDebugStartExists(); _clientCount = _networkDebugStart.AutoClients.ToString(); BaseSkin = GetAsset("e59b35dfeb4b6f54e9b2791b2a40a510"); } #endif protected virtual void OnValidate() { ValidateClientCount(); } protected void ValidateClientCount() { if (_clientCount == null) { _clientCount = "1"; } else { _clientCount = System.Text.RegularExpressions.Regex.Replace(_clientCount, "[^0-9]", ""); } } protected int GetClientCount() { try { return Convert.ToInt32(_clientCount); } catch { return 0; } } protected virtual void Awake() { _nicifiedStageNames = ConvertEnumToNicifiedNameLookup("Fusion Status: "); _networkDebugStart = EnsureNetworkDebugStartExists(); _clientCount = _networkDebugStart.AutoClients.ToString(); ValidateClientCount(); } protected virtual void Start() { _isMultiplePeerMode = NetworkProjectConfig.Global.PeerMode == NetworkProjectConfig.PeerModes.Multiple; } protected FusionBootstrap EnsureNetworkDebugStartExists() { if (_networkDebugStart) { if (_networkDebugStart.gameObject == gameObject) return _networkDebugStart; } if (TryGetBehaviour(out var found)) { _networkDebugStart = found; return found; } _networkDebugStart = AddBehaviour(); return _networkDebugStart; } private void Update() { var nds = EnsureNetworkDebugStartExists(); if (nds.StartMode != FusionBootstrap.StartModes.UserInterface) { return; } var currentstage = nds.CurrentStage; if (currentstage != FusionBootstrap.Stage.Disconnected) { return; } if (EnableHotkeys) { if (Input.GetKeyDown(KeyCode.I)) { _networkDebugStart.StartSinglePlayer(); } if (Input.GetKeyDown(KeyCode.H)) { if (_isMultiplePeerMode) { StartHostWithClients(_networkDebugStart); } else { _networkDebugStart.StartHost(); } } if (Input.GetKeyDown(KeyCode.S)) { if (_isMultiplePeerMode) { StartServerWithClients(_networkDebugStart); } else { _networkDebugStart.StartServer(); } } if (Input.GetKeyDown(KeyCode.C)) { if (_isMultiplePeerMode) { StartMultipleClients(nds); } else { nds.StartClient(); } } if (Input.GetKeyDown(KeyCode.A)) { if (_isMultiplePeerMode) { StartMultipleAutoClients(nds); } else { nds.StartAutoClient(); } } if (Input.GetKeyDown(KeyCode.P)) { if (_isMultiplePeerMode) { StartMultipleSharedClients(nds); } else { nds.StartSharedClient(); } } } } protected virtual void OnGUI() { var nds = EnsureNetworkDebugStartExists(); if (nds.StartMode != FusionBootstrap.StartModes.UserInterface) { return; } var currentstage = nds.CurrentStage; if (nds.AutoHideGUI && currentstage == FusionBootstrap.Stage.AllConnected) { return; } var holdskin = GUI.skin; GUI.skin = FusionScalableIMGUI.GetScaledSkin(BaseSkin, out var height, out var width, out var padding, out var margin, out var leftBoxMargin); GUILayout.BeginArea(new Rect(leftBoxMargin, margin, width, Screen.height)); { GUILayout.BeginVertical(GUI.skin.window); { GUILayout.BeginHorizontal(GUILayout.Height(height)); { var stagename = _nicifiedStageNames.TryGetValue(nds.CurrentStage, out var stage) ? stage : "Unrecognized Stage"; GUILayout.Label(stagename, new GUIStyle(GUI.skin.label) { fontSize = (int)(GUI.skin.label.fontSize * .8f), alignment = TextAnchor.UpperLeft }); // Add button to hide Shutdown option after all connect, which just enables AutoHide - so that interface will reappear after a disconnect. if (nds.AutoHideGUI == false && nds.CurrentStage == FusionBootstrap.Stage.AllConnected) { if (GUILayout.Button("X", GUILayout.ExpandHeight(true), GUILayout.Width(height))) { nds.AutoHideGUI = true; } } } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); GUILayout.BeginVertical(GUI.skin.window); { if (currentstage == FusionBootstrap.Stage.Disconnected) { GUILayout.BeginHorizontal(); { GUILayout.Label("Room:", GUILayout.Height(height), GUILayout.Width(width * .33f)); nds.DefaultRoomName = GUILayout.TextField(nds.DefaultRoomName, 25, GUILayout.Height(height)); } GUILayout.EndHorizontal(); if (GUILayout.Button(EnableHotkeys ? "Start Single Player (I)" : "Start Single Player", GUILayout.Height(height))) { nds.StartSinglePlayer(); } if (GUILayout.Button(EnableHotkeys ? "Start Shared Client (P)" : "Start Shared Client", GUILayout.Height(height))) { if (_isMultiplePeerMode) { StartMultipleSharedClients(nds); } else { nds.StartSharedClient(); } } if (GUILayout.Button(EnableHotkeys ? "Start Server (S)" : "Start Server", GUILayout.Height(height))) { if (_isMultiplePeerMode) { StartServerWithClients(nds); } else { nds.StartServer(); } } if (GUILayout.Button(EnableHotkeys ? "Start Host (H)" : "Start Host", GUILayout.Height(height))) { if (_isMultiplePeerMode) { StartHostWithClients(nds); } else { nds.StartHost(); } } if (GUILayout.Button(EnableHotkeys ? "Start Client (C)" : "Start Client", GUILayout.Height(height))) { if (_isMultiplePeerMode) { StartMultipleClients(nds); } else { nds.StartClient(); } } if (GUILayout.Button(EnableHotkeys ? "Start Auto Host Or Client (A)" : "Start Auto Host Or Client", GUILayout.Height(height))) { if (_isMultiplePeerMode) { StartMultipleAutoClients(nds); } else { nds.StartAutoClient(); } } if (_isMultiplePeerMode) { GUILayout.BeginHorizontal(/*GUI.skin.button*/); { GUILayout.Label("Client Count:", GUILayout.Height(height)); GUILayout.Label("", GUILayout.Width(4)); string newcount = GUILayout.TextField(_clientCount, 10, GUILayout.Width(width * .25f), GUILayout.Height(height)); if (_clientCount != newcount) { // Remove everything but numbers from our client count string. _clientCount = newcount; ValidateClientCount(); } } GUILayout.EndHorizontal(); } } else { if (GUILayout.Button("Shutdown", GUILayout.Height(height))) { _networkDebugStart.ShutdownAll(); } } GUILayout.EndVertical(); } } GUILayout.EndArea(); GUI.skin = holdskin; } private void StartHostWithClients(FusionBootstrap nds) { int count; try { count = Convert.ToInt32(_clientCount); } catch { count = 0; } nds.StartHostPlusClients(count); } private void StartServerWithClients(FusionBootstrap nds) { int count; try { count = Convert.ToInt32(_clientCount); } catch { count = 0; } nds.StartServerPlusClients(count); } private void StartMultipleClients(FusionBootstrap nds) { int count; try { count = Convert.ToInt32(_clientCount); } catch { count = 0; } nds.StartMultipleClients(count); } private void StartMultipleAutoClients(FusionBootstrap nds) { int.TryParse(_clientCount, out int count); nds.StartMultipleAutoClients(count); } private void StartMultipleSharedClients(FusionBootstrap nds) { int count; try { count = Convert.ToInt32(_clientCount); } catch { count = 0; } nds.StartMultipleSharedClients(count); } // TODO Move to a utility public static Dictionary ConvertEnumToNicifiedNameLookup(string prefix = null, Dictionary nonalloc = null) where T : System.Enum { System.Text.StringBuilder sb = new System.Text.StringBuilder(); if (nonalloc == null) { nonalloc = new Dictionary(); } else { nonalloc.Clear(); } var names = Enum.GetNames(typeof(T)); var values = Enum.GetValues(typeof(T)); for (int i = 0, cnt = names.Length; i < cnt; ++i) { sb.Clear(); if (prefix != null) { sb.Append(prefix); } var name = names[i]; for (int n = 0; n < name.Length; n++) { // If this character is a capital and it is not the first character add a space. // This is because we don't want a space before the word has even begun. if (char.IsUpper(name[n]) == true && n != 0) { sb.Append(" "); } // Add the character to our new string sb.Append(name[n]); } nonalloc.Add((T)values.GetValue(i), sb.ToString()); } return nonalloc; } #if UNITY_EDITOR public static T GetAsset(string Guid) where T : UnityEngine.Object { var path = UnityEditor.AssetDatabase.GUIDToAssetPath(Guid); if (string.IsNullOrEmpty(path)) { return null; } else { return UnityEditor.AssetDatabase.LoadAssetAtPath(path); } } #endif } }