using System.Collections; using System.Collections.Generic; using UnityEngine; using ExitGames.Client.Photon.LoadBalancing; using ExitGames.Client.Photon; using Hashtable = ExitGames.Client.Photon.Hashtable; using System.Linq; public class NetworkManager : MonoBehaviour { #region Inspector public string serverAddress, appID, gameVersion; [Header("Debug")] public ParticleSystem NetworkDebugParticles; public bool expectedOnline = false; public byte expectedMaxPlayers = 2; #endregion #region Particles public static bool visParticles = true; // Spawn a particle on an entity, used for visualizing updates public static void netParticle(EntityBase eb, Color pColor) { if (!visParticles) return; if (instance.NetworkDebugParticles) { var pparams = new ParticleSystem.EmitParams(); pparams.position = eb.transform.position; pparams.startColor = pColor; instance.NetworkDebugParticles.Emit(pparams, 1); } } #endregion #region Network Helpers public static NetLogic net { get; private set; } /// /// A time value that is 'approximately' (+/- 10ms most of the time) synced across clients /// public static double serverTime { get { if (net == null || net.loadBalancingPeer == null) return Time.realtimeSinceStartup; return net.loadBalancingPeer.ServerTimeInMilliSeconds * (1d/1000d); } } /// /// On non WebSocketSecure platforms, encryption handshake must occur before opCustom can be sent. /// This is important in cases such as getting the room or region list. /// public static bool delayForEncrypt { get { // We use secure websockets in UnityGL, so no encrypt handshake needs to occur #if UNITY_WEBGL return true; #else return !net.loadBalancingPeer.IsEncryptionAvailable; #endif } } /// /// Returns true if able to send network connections. /// public static bool isReady { get { if (net == null) return false; return net.IsConnectedAndReady; } } /// /// If this client is considered owner of the room. /// public static bool isMaster { get { // If have no networking, we're the owner. if (net == null) return true; if (!inRoom) return true; return net.LocalPlayer.IsMasterClient; } } /// /// Boolean for if we are on the name server. Used for awaiting the name server connection. /// Can be set to true to connect to name server. /// /// true if on name server; otherwise, false.// public static bool onNameServer { get { if (net == null) return false; return net.State.Equals(ClientState.ConnectedToNameServer); } set { if (value) net.ConnectToNameServer(); } } /// /// Boolean to check if the network is in the master lobby, will be true after we've found a region. /// /// true if on master lobby; otherwise, false. public static bool onMasterLobby { get { if (net == null) return false; return net.State.Equals(ClientState.JoinedLobby); } } /// /// Boolean to check if we're in a room or not. /// /// true if in room; otherwise, false. public static bool inRoom{ get { if (net == null) return false; return net.State.Equals(ClientState.Joined); } } /// /// Returns all players in the current room sorted by . /// public static Player[] getSortedPlayers{ get { if (!inRoom) return new Player[0]; var players = NetworkManager.net.CurrentRoom.Players.Values; var playersSorted = players.OrderBy(p => p.ID); return playersSorted.ToArray(); } } /// /// Enqueue a network update to be sent. Network events are processed both on Update and LateUpdate timings. /// public static bool netMessage(byte eventCode, object eventContent, bool sendReliable = false, RaiseEventOptions options = null) { if (!inRoom || !isReady) return false; // Only actually send messages when in game and ready if (options == null) options = RaiseEventOptions.Default; return net.OpRaiseEvent(eventCode, eventContent, sendReliable, options); } /// /// Get the local player id. if isOnline isn't true, this will be -1 /// public static int localID { get { if (net == null) return 0; return net.LocalPlayer.ID; } } #endregion public static event System.Action netHook; public static event System.Action onPropChanged; public static event System.Action onLeave; public static event System.Action onJoin; private static NetworkManager _instance; public static NetworkManager instance { get { if (_instance) return _instance; _instance = FindObjectOfType(); if (_instance) return _instance; throw new System.Exception("Network manager not instanced in scene"); } set { _instance = value; } } public void Awake() { if (_instance != null) { Destroy(gameObject); return; } // Debug.Log("Aweak") instance = this; DontDestroyOnLoad(gameObject); // Initialize network net = new NetLogic(); } void OnDestroy() { if (net == null) return; // Only do service if (_instance == this) { net.Service(); // Service before disconnect to clear any blockers net.Disconnect(); } } void Update() { net.Service(); } void LateUpdate () { net.Service(); } public class NetLogic : LoadBalancingClient { public NetLogic() { // Setup and launch network service AppId = NetworkManager.instance.appID; AppVersion = NetworkManager.instance.gameVersion; AutoJoinLobby = true; // Register custom type handlers StreamCustomTypes.Register(); #if UNITY_WEBGL Debug.Log("Using secure websockets"); this.TransportProtocol = ConnectionProtocol.WebSocketSecure; #endif } public event System.Action GamelistRefresh; public override void OnEvent(EventData photonEvent) { base.OnEvent(photonEvent); switch(photonEvent.Code) { case EventCode.GameList: case EventCode.GameListUpdate: Debug.Log("Server List recieved"); if (GamelistRefresh != null) GamelistRefresh(); break; case EventCode.PropertiesChanged: onPropChanged?.Invoke(photonEvent); break; case EventCode.Join: onJoin?.Invoke(photonEvent); break; case EventCode.Leave: onLeave?.Invoke(photonEvent); break; } netHook?.Invoke(photonEvent); } /// /// Joins a specific room by name. If the room doesn't exist (yet), it will be created implicitiy. /// Creates custom properties automatically for the room. /// /// /// /// /// /// public bool OpJoinOrCreateRoomWithProperties(string roomName, RoomOptions options, TypedLobby lobby) { PlayerProperties.CreatePlayerHashtable(); options.CustomRoomProperties = RoomProperties.GetRoomHashtable(); return OpJoinOrCreateRoom(roomName, options, lobby); } public bool OpCreateRoomWithProperties(string roomName, RoomOptions options, TypedLobby lobby) { PlayerProperties.CreatePlayerHashtable(); options.CustomRoomProperties = RoomProperties.GetRoomHashtable(); return OpCreateRoom(roomName, options, lobby); } public bool OpJoinRoomWithProperties(string roomName) { PlayerProperties.CreatePlayerHashtable(); return OpJoinRoom(roomName); } } }