// ---------------------------------------------------------------------------------------------------------------------- // The Photon Chat Api enables clients to connect to a chat server and communicate with other clients. // ChatClient is the main class of this api. // Photon Chat Api - Copyright (C) 2014 Exit Games GmbH // ---------------------------------------------------------------------------------------------------------------------- #if UNITY_4_7 || UNITY_5 || UNITY_5_0 || UNITY_5_1 || UNITY_2017 #define UNITY #endif namespace ExitGames.Client.Photon.Chat { using System; using System.Diagnostics; using System.Collections.Generic; using ExitGames.Client.Photon; #if UNITY || NETFX_CORE using Hashtable = ExitGames.Client.Photon.Hashtable; using SupportClass = ExitGames.Client.Photon.SupportClass; #endif /// /// Provides basic operations of the Photon Chat server. This internal class is used by public ChatClient. /// public class ChatPeer : PhotonPeer { /// Name Server Host Name for Photon Cloud. Without port and without any prefix. public const string NameServerHost = "ns.exitgames.com"; /// Name Server for HTTP connections to the Photon Cloud. Includes prefix and port. public const string NameServerHttp = "http://ns.exitgamescloud.com:80/photon/n"; /// Name Server port per protocol (the UDP port is different than TCP, etc). private static readonly Dictionary ProtocolToNameServerPort = new Dictionary() { { ConnectionProtocol.Udp, 5058 }, { ConnectionProtocol.Tcp, 4533 }, { ConnectionProtocol.WebSocket, 9093 }, { ConnectionProtocol.WebSocketSecure, 19093 } }; //, { ConnectionProtocol.RHttp, 6063 } }; /// Name Server Address for Photon Cloud (based on current protocol). You can use the default values and usually won't have to set this value. public string NameServerAddress { get { return this.GetNameServerAddress(); } } virtual internal bool IsProtocolSecure { get { return this.UsedProtocol == ConnectionProtocol.WebSocketSecure; } } public ChatPeer(IPhotonPeerListener listener, ConnectionProtocol protocol) : base(listener, protocol) { this.ConfigUnitySockets(); } [Conditional("UNITY")] private void ConfigUnitySockets() { #pragma warning disable 0162 // the library variant defines if we should use PUN's SocketUdp variant (at all) if (PhotonPeer.NoSocket) { #if !UNITY_EDITOR && (UNITY_PS3 || UNITY_ANDROID) this.SocketImplementationConfig[ConnectionProtocol.Udp] = typeof(SocketUdpNativeDynamic); #elif !UNITY_EDITOR && UNITY_IPHONE this.SocketImplementationConfig[ConnectionProtocol.Udp] = typeof(SocketUdpNativeStatic); #elif !UNITY_EDITOR && (UNITY_WINRT) // this automatically uses a separate assembly-file with Win8-style Socket usage (not possible in Editor) #else Type udpSocket = Type.GetType("ExitGames.Client.Photon.SocketUdp, Assembly-CSharp"); this.SocketImplementationConfig[ConnectionProtocol.Udp] = udpSocket; if (udpSocket == null) { #if UNITY UnityEngine.Debug.Log("Could not find a suitable C# socket class. This Photon3Unity3D.dll only supports native socket plugins."); #endif } #endif } #pragma warning restore 0162 #if UNITY_WEBGL if (this.TransportProtocol != ConnectionProtocol.WebSocket && this.TransportProtocol != ConnectionProtocol.WebSocketSecure) { UnityEngine.Debug.Log("For UNITY_WEBGL, use protocol WebSocketSecure. Overriding currently set protcol " + this.TransportProtocol + "."); this.TransportProtocol = ConnectionProtocol.WebSocketSecure; } #endif // to support WebGL export in Unity, we find and assign the SocketWebTcpThread or SocketWebTcpCoroutine class (if it's in the project). Type websocketType = Type.GetType("ExitGames.Client.Photon.SocketWebTcpThread, Assembly-CSharp", false); websocketType = websocketType ?? Type.GetType("ExitGames.Client.Photon.SocketWebTcpThread, Assembly-CSharp-firstpass", false); websocketType = websocketType ?? Type.GetType("ExitGames.Client.Photon.SocketWebTcpCoroutine, Assembly-CSharp", false); websocketType = websocketType ?? Type.GetType("ExitGames.Client.Photon.SocketWebTcpCoroutine, Assembly-CSharp-firstpass", false); if (websocketType != null) { this.SocketImplementationConfig[ConnectionProtocol.WebSocket] = websocketType; this.SocketImplementationConfig[ConnectionProtocol.WebSocketSecure] = websocketType; } } /// /// Gets the NameServer Address (with prefix and port), based on the set protocol (this.UsedProtocol). /// /// NameServer Address (with prefix and port). private string GetNameServerAddress() { var protocolPort = 0; ProtocolToNameServerPort.TryGetValue(this.TransportProtocol, out protocolPort); switch (this.TransportProtocol) { case ConnectionProtocol.Udp: case ConnectionProtocol.Tcp: return string.Format("{0}:{1}", NameServerHost, protocolPort); #if RHTTP case ConnectionProtocol.RHttp: return NameServerHttp; #endif case ConnectionProtocol.WebSocket: return string.Format("ws://{0}:{1}", NameServerHost, protocolPort); case ConnectionProtocol.WebSocketSecure: return string.Format("wss://{0}:{1}", NameServerHost, protocolPort); default: throw new ArgumentOutOfRangeException(); } } public bool Connect() { if (this.DebugOut >= DebugLevel.INFO) { this.Listener.DebugReturn(DebugLevel.INFO, "Connecting to nameserver " + this.NameServerAddress); } return this.Connect(this.NameServerAddress, "NameServer"); } public bool AuthenticateOnNameServer(string appId, string appVersion, string region, AuthenticationValues authValues) { if (this.DebugOut >= DebugLevel.INFO) { this.Listener.DebugReturn(DebugLevel.INFO, "OpAuthenticate()"); } var opParameters = new Dictionary(); opParameters[ParameterCode.AppVersion] = appVersion; opParameters[ParameterCode.ApplicationId] = appId; opParameters[ParameterCode.Region] = region; if (authValues != null) { if (!string.IsNullOrEmpty(authValues.UserId)) { opParameters[ParameterCode.UserId] = authValues.UserId; } if (authValues != null && authValues.AuthType != CustomAuthenticationType.None) { opParameters[ParameterCode.ClientAuthenticationType] = (byte) authValues.AuthType; if (!string.IsNullOrEmpty(authValues.Token)) { opParameters[ParameterCode.Secret] = authValues.Token; } else { if (!string.IsNullOrEmpty(authValues.AuthGetParameters)) { opParameters[ParameterCode.ClientAuthenticationParams] = authValues.AuthGetParameters; } if (authValues.AuthPostData != null) { opParameters[ParameterCode.ClientAuthenticationData] = authValues.AuthPostData; } } } } return this.OpCustom((byte)ChatOperationCode.Authenticate, opParameters, true, (byte)0, this.IsEncryptionAvailable); } } /// /// Options for optional "Custom Authentication" services used with Photon. Used by OpAuthenticate after connecting to Photon. /// public enum CustomAuthenticationType : byte { /// Use a custom authentification service. Currently the only implemented option. Custom = 0, /// Authenticates users by their Steam Account. Set auth values accordingly! Steam = 1, /// Authenticates users by their Facebook Account. Set auth values accordingly! Facebook = 2, /// Authenticates users by their Oculus Account and token. Oculus = 3, /// Authenticates users by their PSN Account and token. PlayStation = 4, /// Authenticates users by their Xbox Account and XSTS token. Xbox = 5, /// Disables custom authentification. Same as not providing any AuthenticationValues for connect (more precisely for: OpAuthenticate). None = byte.MaxValue } /// /// Container for user authentication in Photon. Set AuthValues before you connect - all else is handled. /// /// /// On Photon, user authentication is optional but can be useful in many cases. /// If you want to FindFriends, a unique ID per user is very practical. /// /// There are basically three options for user authentification: None at all, the client sets some UserId /// or you can use some account web-service to authenticate a user (and set the UserId server-side). /// /// Custom Authentication lets you verify end-users by some kind of login or token. It sends those /// values to Photon which will verify them before granting access or disconnecting the client. /// /// The Photon Cloud Dashboard will let you enable this feature and set important server values for it. /// https://www.photonengine.com/dashboard /// public class AuthenticationValues { /// See AuthType. private CustomAuthenticationType authType = CustomAuthenticationType.None; /// The type of custom authentication provider that should be used. Currently only "Custom" or "None" (turns this off). public CustomAuthenticationType AuthType { get { return authType; } set { authType = value; } } /// This string must contain any (http get) parameters expected by the used authentication service. By default, username and token. /// Standard http get parameters are used here and passed on to the service that's defined in the server (Photon Cloud Dashboard). public string AuthGetParameters { get; set; } /// Data to be passed-on to the auth service via POST. Default: null (not sent). Either string or byte[] (see setters). public object AuthPostData { get; private set; } /// After initial authentication, Photon provides a token for this client / user, which is subsequently used as (cached) validation. public string Token { get; set; } /// The UserId should be a unique identifier per user. This is for finding friends, etc.. public string UserId { get; set; } /// Creates empty auth values without any info. public AuthenticationValues() { } /// Creates minimal info about the user. If this is authenticated or not, depends on the set AuthType. /// Some UserId to set in Photon. public AuthenticationValues(string userId) { this.UserId = userId; } /// Sets the data to be passed-on to the auth service via POST. /// String data to be used in the body of the POST request. Null or empty string will set AuthPostData to null. public virtual void SetAuthPostData(string stringData) { this.AuthPostData = (string.IsNullOrEmpty(stringData)) ? null : stringData; } /// Sets the data to be passed-on to the auth service via POST. /// Binary token / auth-data to pass on. public virtual void SetAuthPostData(byte[] byteData) { this.AuthPostData = byteData; } /// Adds a key-value pair to the get-parameters used for Custom Auth. /// This method does uri-encoding for you. /// Key for the value to set. /// Some value relevant for Custom Authentication. public virtual void AddAuthParameter(string key, string value) { string ampersand = string.IsNullOrEmpty(this.AuthGetParameters) ? "" : "&"; this.AuthGetParameters = string.Format("{0}{1}{2}={3}", this.AuthGetParameters, ampersand, System.Uri.EscapeDataString(key), System.Uri.EscapeDataString(value)); } public override string ToString() { return string.Format("AuthenticationValues UserId: {0}, GetParameters: {1} Token available: {2}", UserId, this.AuthGetParameters, Token != null); } } public class ParameterCode { public const byte ApplicationId = 224; /// (221) Internally used to establish encryption public const byte Secret = 221; public const byte AppVersion = 220; /// (217) This key's (byte) value defines the target custom authentication type/service the client connects with. Used in OpAuthenticate public const byte ClientAuthenticationType = 217; /// (216) This key's (string) value provides parameters sent to the custom authentication type/service the client connects with. Used in OpAuthenticate public const byte ClientAuthenticationParams = 216; /// (214) This key's (string or byte[]) value provides parameters sent to the custom authentication service setup in Photon Dashboard. Used in OpAuthenticate public const byte ClientAuthenticationData = 214; /// (210) Used for region values in OpAuth and OpGetRegions. public const byte Region = 210; /// (230) Address of a (game) server to use. public const byte Address = 230; /// (225) User's ID public const byte UserId = 225; } /// /// ErrorCode defines the default codes associated with Photon client/server communication. /// public class ErrorCode { /// (0) is always "OK", anything else an error or specific situation. public const int Ok = 0; // server - Photon low(er) level: <= 0 /// /// (-3) Operation can't be executed yet (e.g. OpJoin can't be called before being authenticated, RaiseEvent cant be used before getting into a room). /// /// /// Before you call any operations on the Cloud servers, the automated client workflow must complete its authorization. /// In PUN, wait until State is: JoinedLobby (with AutoJoinLobby = true) or ConnectedToMaster (AutoJoinLobby = false) /// public const int OperationNotAllowedInCurrentState = -3; /// (-2) The operation you called is not implemented on the server (application) you connect to. Make sure you run the fitting applications. public const int InvalidOperationCode = -2; /// (-1) Something went wrong in the server. Try to reproduce and contact Exit Games. public const int InternalServerError = -1; // server - PhotonNetwork: 0x7FFF and down // logic-level error codes start with short.max /// (32767) Authentication failed. Possible cause: AppId is unknown to Photon (in cloud service). public const int InvalidAuthentication = 0x7FFF; /// (32766) GameId (name) already in use (can't create another). Change name. public const int GameIdAlreadyExists = 0x7FFF - 1; /// (32765) Game is full. This rarely happens when some player joined the room before your join completed. public const int GameFull = 0x7FFF - 2; /// (32764) Game is closed and can't be joined. Join another game. public const int GameClosed = 0x7FFF - 3; /// (32762) Not in use currently. public const int ServerFull = 0x7FFF - 5; /// (32761) Not in use currently. public const int UserBlocked = 0x7FFF - 6; /// (32760) Random matchmaking only succeeds if a room exists thats neither closed nor full. Repeat in a few seconds or create a new room. public const int NoRandomMatchFound = 0x7FFF - 7; /// (32758) Join can fail if the room (name) is not existing (anymore). This can happen when players leave while you join. public const int GameDoesNotExist = 0x7FFF - 9; /// (32757) Authorization on the Photon Cloud failed becaus the concurrent users (CCU) limit of the app's subscription is reached. /// /// Unless you have a plan with "CCU Burst", clients might fail the authentication step during connect. /// Affected client are unable to call operations. Please note that players who end a game and return /// to the master server will disconnect and re-connect, which means that they just played and are rejected /// in the next minute / re-connect. /// This is a temporary measure. Once the CCU is below the limit, players will be able to connect an play again. /// /// OpAuthorize is part of connection workflow but only on the Photon Cloud, this error can happen. /// Self-hosted Photon servers with a CCU limited license won't let a client connect at all. /// public const int MaxCcuReached = 0x7FFF - 10; /// (32756) Authorization on the Photon Cloud failed because the app's subscription does not allow to use a particular region's server. /// /// Some subscription plans for the Photon Cloud are region-bound. Servers of other regions can't be used then. /// Check your master server address and compare it with your Photon Cloud Dashboard's info. /// https://cloud.photonengine.com/dashboard /// /// OpAuthorize is part of connection workflow but only on the Photon Cloud, this error can happen. /// Self-hosted Photon servers with a CCU limited license won't let a client connect at all. /// public const int InvalidRegion = 0x7FFF - 11; /// /// (32755) Custom Authentication of the user failed due to setup reasons (see Cloud Dashboard) or the provided user data (like username or token). Check error message for details. /// public const int CustomAuthenticationFailed = 0x7FFF - 12; } }