// ----------------------------------------------------------------------------------------------------------------------
// 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_3_OR_NEWER
#define SUPPORTED_UNITY
#endif
namespace Photon.Chat
{
using System;
using System.Diagnostics;
using System.Collections.Generic;
using ExitGames.Client.Photon;
#if SUPPORTED_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 string NameServerHost = "ns.photonengine.io";
/// 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; } }
/// Chat Peer constructor.
/// Chat listener implementation.
/// Protocol to be used by the peer.
public ChatPeer(IPhotonPeerListener listener, ConnectionProtocol protocol) : base(listener, protocol)
{
this.ConfigUnitySockets();
}
// Sets up the socket implementations to use, depending on platform
[System.Diagnostics.Conditional("SUPPORTED_UNITY")]
private void ConfigUnitySockets()
{
Type websocketType = null;
#if (UNITY_XBOXONE || UNITY_GAMECORE) && !UNITY_EDITOR
websocketType = Type.GetType("ExitGames.Client.Photon.SocketNativeSource, Assembly-CSharp", false);
if (websocketType == null)
{
websocketType = Type.GetType("ExitGames.Client.Photon.SocketNativeSource, Assembly-CSharp-firstpass", false);
}
if (websocketType == null)
{
websocketType = Type.GetType("ExitGames.Client.Photon.SocketNativeSource, PhotonRealtime", false);
}
if (websocketType != null)
{
this.SocketImplementationConfig[ConnectionProtocol.Udp] = websocketType; // on Xbox, the native socket plugin supports UDP as well
}
#else
// to support WebGL export in Unity, we find and assign the SocketWebTcp class (if it's in the project).
// alternatively class SocketWebTcp might be in the Photon3Unity3D.dll
websocketType = Type.GetType("ExitGames.Client.Photon.SocketWebTcp, PhotonWebSocket", false);
if (websocketType == null)
{
websocketType = Type.GetType("ExitGames.Client.Photon.SocketWebTcp, Assembly-CSharp-firstpass", false);
}
if (websocketType == null)
{
websocketType = Type.GetType("ExitGames.Client.Photon.SocketWebTcp, Assembly-CSharp", false);
}
#endif
if (websocketType != null)
{
this.SocketImplementationConfig[ConnectionProtocol.WebSocket] = websocketType;
this.SocketImplementationConfig[ConnectionProtocol.WebSocketSecure] = websocketType;
}
#if NET_4_6 && (UNITY_EDITOR || !ENABLE_IL2CPP) && !NETFX_CORE
this.SocketImplementationConfig[ConnectionProtocol.Udp] = typeof(SocketUdpAsync);
this.SocketImplementationConfig[ConnectionProtocol.Tcp] = typeof(SocketTcpAsync);
#endif
}
/// If not zero, this is used for the name server port on connect. Independent of protocol (so this better matches). Set by ChatClient.ConnectUsingSettings.
/// This is reset when the protocol fallback is used.
public ushort NameServerPortOverride;
///
/// 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);
if (this.NameServerPortOverride != 0)
{
this.Listener.DebugReturn(DebugLevel.INFO, string.Format("Using NameServerPortInAppSettings as port for Name Server: {0}", this.NameServerPortOverride));
protocolPort = this.NameServerPortOverride;
}
switch (this.TransportProtocol)
{
case ConnectionProtocol.Udp:
case ConnectionProtocol.Tcp:
return string.Format("{0}:{1}", NameServerHost, protocolPort);
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();
}
}
/// Connects to NameServer.
/// If the connection attempt could be sent.
public bool Connect()
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "Connecting to nameserver " + this.NameServerAddress);
}
return this.Connect(this.NameServerAddress, "NameServer");
}
/// Authenticates on NameServer.
/// If the authentication operation request could be sent.
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.AuthType != CustomAuthenticationType.None)
{
opParameters[ParameterCode.ClientAuthenticationType] = (byte) authValues.AuthType;
if (authValues.Token != null)
{
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.SendOperation(ChatOperationCode.Authenticate, opParameters, new SendOptions() { Reliability = true, Encrypt = 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 authentication service. Currently the only implemented option.
Custom = 0,
/// Authenticates users by their Steam Account. Set Steam's ticket as "ticket" via AddAuthParameter().
Steam = 1,
/// Authenticates users by their Facebook Account. Set Facebooks's tocken as "token" via AddAuthParameter().
Facebook = 2,
/// Authenticates users by their Oculus Account and token. Set Oculus' userid as "userid" and nonce as "nonce" via AddAuthParameter().
Oculus = 3,
/// Authenticates users by their PSN Account and token on PS4. Set token as "token", env as "env" and userName as "userName" via AddAuthParameter().
PlayStation4 = 4,
[Obsolete("Use PlayStation4 or PlayStation5 as needed")]
PlayStation = 4,
/// Authenticates users by their Xbox Account. Pass the XSTS token via SetAuthPostData().
Xbox = 5,
/// Authenticates users by their HTC Viveport Account. Set userToken as "userToken" via AddAuthParameter().
Viveport = 10,
/// Authenticates users by their NSA ID. Set token as "token" and appversion as "appversion" via AddAuthParameter(). The appversion is optional.
NintendoSwitch = 11,
/// Authenticates users by their PSN Account and token on PS5. Set token as "token", env as "env" and userName as "userName" via AddAuthParameter().
PlayStation5 = 12,
[Obsolete("Use PlayStation4 or PlayStation5 as needed")]
Playstation5 = 12,
/// Authenticates users with Epic Online Services (EOS). Set token as "token" and ownershipToken as "ownershipToken" via AddAuthParameter(). The ownershipToken is optional.
Epic = 13,
/// Authenticates users with Facebook Gaming api. Set token as "token" via AddAuthParameter().
FacebookGaming = 15,
/// Disables custom authentication. 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 authentication: 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 AuthValues are sent in OpAuthenticate when you connect, so they must be set before you connect.
/// If the AuthValues.UserId is null or empty when it's sent to the server, then the Photon Server assigns a UserId!
///
/// The Photon Cloud Dashboard will let you enable this feature and set important server values for it.
/// https://dashboard.photonengine.com
///
public class AuthenticationValues
{
/// See AuthType.
private CustomAuthenticationType authType = CustomAuthenticationType.None;
/// The type of authentication provider that should be used. Defaults to None (no auth whatsoever).
/// Several auth providers are available and CustomAuthenticationType.Custom can be used if you build your own service.
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.
///
/// Maps to operation parameter 216.
/// 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).
/// Maps to operation parameter 214.
public object AuthPostData { get; private set; }
/// Internal Photon token. After initial authentication, Photon provides a token for this client, subsequently used as (cached) validation.
/// Any token for custom authentication should be set via SetAuthPostData or AddAuthParameter.
public object Token { get; protected internal set; }
/// The UserId should be a unique identifier per user. This is for finding friends, etc..
/// See remarks of AuthValues for info about how this is set and used.
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.
/// AuthPostData is just one value. Each SetAuthPostData replaces any previous value. It can be either a string, a byte[] or a dictionary.
/// 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.
/// AuthPostData is just one value. Each SetAuthPostData replaces any previous value. It can be either a string, a byte[] or a dictionary.
/// Binary token / auth-data to pass on.
public virtual void SetAuthPostData(byte[] byteData)
{
this.AuthPostData = byteData;
}
/// Sets data to be passed-on to the auth service as Json (Content-Type: "application/json") via Post.
/// AuthPostData is just one value. Each SetAuthPostData replaces any previous value. It can be either a string, a byte[] or a dictionary.
/// A authentication-data dictionary will be converted to Json and passed to the Auth webservice via HTTP Post.
public virtual void SetAuthPostData(Dictionary dictData)
{
this.AuthPostData = dictData;
}
/// Adds a key-value pair to the get-parameters used for Custom Auth (AuthGetParameters).
/// 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));
}
///
/// Transform this object into string.
///
/// string representation of this object.
public override string ToString()
{
return string.Format("AuthenticationValues Type: {3} UserId: {0}, GetParameters: {1} Token available: {2}", this.UserId, this.AuthGetParameters, this.Token != null, this.AuthType);
}
///
/// Make a copy of the current object.
///
/// The object to be copied into.
/// The copied object.
public AuthenticationValues CopyTo(AuthenticationValues copy)
{
copy.AuthType = this.AuthType;
copy.AuthGetParameters = this.AuthGetParameters;
copy.AuthPostData = this.AuthPostData;
copy.UserId = this.UserId;
return copy;
}
}
/// Class for constants. Codes for parameters of Operations and Events.
public class ParameterCode
{
/// (224) Your application's ID: a name on your own Photon or a GUID on the Photon Cloud
public const byte ApplicationId = 224;
/// (221) Internally used to establish encryption
public const byte Secret = 221;
/// (220) Version of your application
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 or ConnectedToMaster
///
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 that is 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 because 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;
/// (32753) The Authentication ticket expired. Usually, this is refreshed behind the scenes. Connect (and authorize) again.
public const int AuthenticationTicketExpired = 0x7FF1;
}
}