// ---------------------------------------------------------------------------- // // Loadbalancing Framework for Photon - Copyright (C) 2018 Exit Games GmbH // // // Implements callbacks of the Realtime API to logs selected information // for support cases. // // developer@photonengine.com // ---------------------------------------------------------------------------- #if UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER #define SUPPORTED_UNITY #endif namespace Photon.Realtime { using System.Text; using System.Collections; using System.Collections.Generic; using Stopwatch = System.Diagnostics.Stopwatch; using ExitGames.Client.Photon; #if SUPPORTED_UNITY using UnityEngine; #endif #if SUPPORTED_UNITY || NETFX_CORE using Hashtable = ExitGames.Client.Photon.Hashtable; using SupportClass = ExitGames.Client.Photon.SupportClass; #endif /// /// Helper class to debug log basic information about Photon client and vital traffic statistics. /// /// /// Set SupportLogger.Client for this to work. /// #if SUPPORTED_UNITY [DisallowMultipleComponent] [AddComponentMenu("")] // hide from Unity Menus and searches public class SupportLogger : MonoBehaviour, IConnectionCallbacks , IMatchmakingCallbacks , IInRoomCallbacks, ILobbyCallbacks, IErrorInfoCallback #else public class SupportLogger : IConnectionCallbacks, IInRoomCallbacks, IMatchmakingCallbacks , ILobbyCallbacks #endif { /// /// Toggle to enable or disable traffic statistics logging. /// public bool LogTrafficStats = true; private bool loggedStillOfflineMessage; private LoadBalancingClient client; private Stopwatch startStopwatch; private int pingMax; private int pingMin; /// /// Photon client to log information and statistics from. /// public LoadBalancingClient Client { get { return this.client; } set { if (this.client != value) { if (this.client != null) { this.client.RemoveCallbackTarget(this); } this.client = value; if (this.client != null) { this.client.AddCallbackTarget(this); } } } } #if SUPPORTED_UNITY protected void Start() { if (this.startStopwatch == null) { this.startStopwatch = new Stopwatch(); this.startStopwatch.Start(); } } protected void OnApplicationPause(bool pause) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnApplicationPause: " + pause + " connected: " + (this.client == null ? "no (client is null)" : this.client.IsConnected.ToString())); } protected void OnApplicationQuit() { this.CancelInvoke(); } #endif public void StartLogStats() { #if SUPPORTED_UNITY this.InvokeRepeating("LogStats", 10, 10); #else Debug.Log("Not implemented for non-Unity projects."); #endif } public void StopLogStats() { #if SUPPORTED_UNITY this.CancelInvoke("LogStats"); #else Debug.Log("Not implemented for non-Unity projects."); #endif } private void StartTrackValues() { #if SUPPORTED_UNITY this.InvokeRepeating("TrackValues", 0.5f, 0.5f); #else Debug.Log("Not implemented for non-Unity projects."); #endif } private void StopTrackValues() { #if SUPPORTED_UNITY this.CancelInvoke("TrackValues"); #else Debug.Log("Not implemented for non-Unity projects."); #endif } private string GetFormattedTimestamp() { if (this.startStopwatch == null) { this.startStopwatch = new Stopwatch(); this.startStopwatch.Start(); } return string.Format("[{0}.{1}]", this.startStopwatch.Elapsed.Seconds, this.startStopwatch.Elapsed.Milliseconds); } // called via InvokeRepeatedly private void TrackValues() { if (this.client != null) { int currentRtt = this.client.LoadBalancingPeer.RoundTripTime; if (currentRtt > this.pingMax) { this.pingMax = currentRtt; } if (currentRtt < this.pingMin) { this.pingMin = currentRtt; } } } /// /// Debug logs vital traffic statistics about the attached Photon Client. /// public void LogStats() { if (this.client == null || this.client.State == ClientState.PeerCreated) { return; } if (this.LogTrafficStats) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger " + this.client.LoadBalancingPeer.VitalStatsToString(false) + " Ping min/max: " + this.pingMin + "/" + this.pingMax); } } /// /// Debug logs basic information (AppId, AppVersion, PeerID, Server address, Region) about the attached Photon Client. /// private void LogBasics() { if (this.client != null) { List buildProperties = new List(10); #if SUPPORTED_UNITY buildProperties.Add(Application.unityVersion); buildProperties.Add(Application.platform.ToString()); #endif #if ENABLE_IL2CPP buildProperties.Add("ENABLE_IL2CPP"); #endif #if ENABLE_MONO buildProperties.Add("ENABLE_MONO"); #endif #if DEBUG buildProperties.Add("DEBUG"); #endif #if MASTER buildProperties.Add("MASTER"); #endif #if NET_4_6 buildProperties.Add("NET_4_6"); #endif #if NET_STANDARD_2_0 buildProperties.Add("NET_STANDARD_2_0"); #endif #if NETFX_CORE buildProperties.Add("NETFX_CORE"); #endif #if NET_LEGACY buildProperties.Add("NET_LEGACY"); #endif #if UNITY_64 buildProperties.Add("UNITY_64"); #endif StringBuilder sb = new StringBuilder(); string appIdShort = string.IsNullOrEmpty(this.client.AppId) || this.client.AppId.Length < 8 ? this.client.AppId : string.Concat(this.client.AppId.Substring(0, 8), "***"); sb.AppendFormat("{0} SupportLogger Info: ", this.GetFormattedTimestamp()); sb.AppendFormat("AppID: \"{0}\" AppVersion: \"{1}\" ClientVersion: {2} Build: {3} ", appIdShort, this.client.AppVersion, this.client.LoadBalancingPeer.ClientVersion, string.Join(", ", buildProperties.ToArray())); sb.AppendFormat("UserId: \"{0}\" AuthType: {1} {2} {3} PeerID: {4} ", this.client.UserId, (this.client.AuthValues != null) ? this.client.AuthValues.AuthType.ToString() : "N/A", this.client.AuthMode, this.client.EncryptionMode, this.client.LoadBalancingPeer.PeerID); //NOTE: this.client.LoadBalancingPeer.ServerIpAddress requires Photon3Unity3d.dll v4.1.2.5 and up sb.AppendFormat("NameServer: {0} Server: {1} IP: {2} Region: {3} Socket: {4} ", this.client.NameServerHost, this.client.CurrentServerAddress, this.client.LoadBalancingPeer.ServerIpAddress, this.client.CloudRegion, this.client.LoadBalancingPeer.SocketImplementation); Debug.Log(sb.ToString()); } } public void OnConnected() { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnConnected()."); this.pingMax = 0; this.pingMin = this.client.LoadBalancingPeer.RoundTripTime; this.LogBasics(); if (this.LogTrafficStats) { this.client.LoadBalancingPeer.TrafficStatsEnabled = false; this.client.LoadBalancingPeer.TrafficStatsEnabled = true; this.StartLogStats(); } this.StartTrackValues(); } public void OnConnectedToMaster() { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnConnectedToMaster()."); } public void OnFriendListUpdate(List friendList) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnFriendListUpdate(friendList)."); } public void OnJoinedLobby() { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnJoinedLobby(" + this.client.CurrentLobby + ")."); } public void OnLeftLobby() { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnLeftLobby()."); } public void OnCreateRoomFailed(short returnCode, string message) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnCreateRoomFailed(" + returnCode+","+message+")."); } public void OnJoinedRoom() { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnJoinedRoom(" + this.client.CurrentRoom + "). " + this.client.CurrentLobby + " GameServer:" + this.client.GameServerAddress); } public void OnJoinRoomFailed(short returnCode, string message) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnJoinRoomFailed(" + returnCode+","+message+")."); } public void OnJoinRandomFailed(short returnCode, string message) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnJoinRandomFailed(" + returnCode+","+message+")."); } public void OnCreatedRoom() { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnCreatedRoom(" + this.client.CurrentRoom + "). " + this.client.CurrentLobby + " GameServer:" + this.client.GameServerAddress); } public void OnLeftRoom() { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnLeftRoom()."); } public void OnDisconnected(DisconnectCause cause) { this.StopLogStats(); this.StopTrackValues(); Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnDisconnected(" + cause + ")."); this.LogBasics(); this.LogStats(); } public void OnRegionListReceived(RegionHandler regionHandler) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnRegionListReceived(regionHandler)."); this.LogBasics(); } public void OnRoomListUpdate(List roomList) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnRoomListUpdate(roomList). roomList.Count: " + roomList.Count); } public void OnPlayerEnteredRoom(Player newPlayer) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnPlayerEnteredRoom(" + newPlayer+")."); } public void OnPlayerLeftRoom(Player otherPlayer) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnPlayerLeftRoom(" + otherPlayer+")."); } public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnRoomPropertiesUpdate(propertiesThatChanged)."); } public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnPlayerPropertiesUpdate(targetPlayer,changedProps)."); } public void OnMasterClientSwitched(Player newMasterClient) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnMasterClientSwitched(" + newMasterClient+")."); } public void OnCustomAuthenticationResponse(Dictionary data) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnCustomAuthenticationResponse(" + data.ToStringFull()+")."); } public void OnCustomAuthenticationFailed (string debugMessage) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnCustomAuthenticationFailed(" + debugMessage+")."); } public void OnLobbyStatisticsUpdate(List lobbyStatistics) { Debug.Log(this.GetFormattedTimestamp() + " SupportLogger OnLobbyStatisticsUpdate(lobbyStatistics)."); } #if !SUPPORTED_UNITY private static class Debug { public static void Log(string msg) { System.Diagnostics.Debug.WriteLine(msg); } public static void LogWarning(string msg) { System.Diagnostics.Debug.WriteLine(msg); } public static void LogError(string msg) { System.Diagnostics.Debug.WriteLine(msg); } } #endif public void OnErrorInfo(ErrorInfo errorInfo) { Debug.LogError(errorInfo.ToString()); } } }