// ----------------------------------------------------------------------------
//
// Loadbalancing Framework for Photon - Copyright (C) 2016 Exit Games GmbH
//
//
// Provides operations to use the LoadBalancing and Cloud photon servers.
// No logic is implemented here.
//
// developer@photonengine.com
// ----------------------------------------------------------------------------
#define UNITY
namespace ExitGames.Client.Photon.LoadBalancing
{
using System;
using System.Collections;
using System.Collections.Generic;
using ExitGames.Client.Photon;
#if UNITY
using UnityEngine;
using Debug = UnityEngine.Debug;
#endif
#if UNITY || NETFX_CORE
using Hashtable = ExitGames.Client.Photon.Hashtable;
using SupportClass = ExitGames.Client.Photon.SupportClass;
#endif
///
/// A LoadbalancingPeer provides the operations and enum definitions needed to use the loadbalancing server application which is also used in Photon Cloud.
///
///
/// Internally used by PUN.
/// The LoadBalancingPeer does not keep a state, instead this is done by a LoadBalancingClient.
///
public class LoadBalancingPeer : PhotonPeer
{
protected internal static Type PingImplementation = null;
private readonly Dictionary opParameters = new Dictionary(); // used in OpRaiseEvent() (avoids lots of new Dictionary() calls)
///
/// Creates a Peer with specified connection protocol. You need to set the Listener before using the peer.
///
/// Each connection protocol has it's own default networking ports for Photon.
/// The preferred option is UDP.
public LoadBalancingPeer(ConnectionProtocol protocolType) : base(protocolType)
{
// this does not require a Listener, so:
// make sure to set this.Listener before using a peer!
this.ConfigUnitySockets();
}
///
/// Creates a Peer with specified connection protocol and a Listener for callbacks.
///
public LoadBalancingPeer(IPhotonPeerListener listener, ConnectionProtocol protocolType) : this(protocolType)
{
this.Listener = listener;
}
// this sets up the socket implementations to use, depending on export
[System.Diagnostics.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);
PingImplementation = typeof(PingNativeDynamic);
#elif !UNITY_EDITOR && (UNITY_IPHONE || UNITY_SWITCH)
this.SocketImplementationConfig[ConnectionProtocol.Udp] = typeof(SocketUdpNativeStatic);
PingImplementation = typeof(PingNativeStatic);
#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
PingImplementation = typeof(PingMono);
#if UNITY_WEBGL
Type pingType = Type.GetType("ExitGames.Client.Photon.PingHttp, Assembly-CSharp", false);
PingImplementation = pingType ?? Type.GetType("ExitGames.Client.Photon.PingHttp, Assembly-CSharp-firstpass", false);
#endif
#if !UNITY_EDITOR && UNITY_WINRT
PingImplementation = typeof(PingWindowsStore);
#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;
}
}
public virtual bool OpGetRegions(string appId)
{
Dictionary parameters = new Dictionary();
parameters[(byte)ParameterCode.ApplicationId] = appId;
return this.OpCustom(OperationCode.GetRegions, parameters, true, 0, true);
}
///
/// Joins the lobby on the Master Server, where you get a list of RoomInfos of currently open rooms.
/// This is an async request which triggers a OnOperationResponse() call.
///
/// The lobby join to.
/// If the operation could be sent (has to be connected).
public virtual bool OpJoinLobby(TypedLobby lobby = null)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpJoinLobby()");
}
Dictionary parameters = null;
if (lobby != null && !lobby.IsDefault)
{
parameters = new Dictionary();
parameters[(byte)ParameterCode.LobbyName] = lobby.Name;
parameters[(byte)ParameterCode.LobbyType] = (byte)lobby.Type;
}
return this.OpCustom(OperationCode.JoinLobby, parameters, true);
}
///
/// Leaves the lobby on the Master Server.
/// This is an async request which triggers a OnOperationResponse() call.
///
/// If the operation could be sent (requires connection).
public virtual bool OpLeaveLobby()
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpLeaveLobby()");
}
return this.OpCustom(OperationCode.LeaveLobby, null, true);
}
/// Used in the RoomOptionFlags parameter, this bitmask toggles options in the room.
enum RoomOptionBit : int
{
CheckUserOnJoin = 0x01, // toggles a check of the UserId when joining (enabling returning to a game)
DeleteCacheOnLeave = 0x02, // deletes cache on leave
SuppressRoomEvents = 0x04, // suppresses all room events
PublishUserId = 0x08, // signals that we should publish userId
DeleteNullProps = 0x10, // signals that we should remove property if its value was set to null. see RoomOption to Delete Null Properties
BroadcastPropsChangeToAll = 0x20, // signals that we should send PropertyChanged event to all room players including initiator
}
private void RoomOptionsToOpParameters(Dictionary op, RoomOptions roomOptions)
{
if (roomOptions == null)
{
roomOptions = new RoomOptions();
}
Hashtable gameProperties = new Hashtable();
gameProperties[GamePropertyKey.IsOpen] = roomOptions.IsOpen;
gameProperties[GamePropertyKey.IsVisible] = roomOptions.IsVisible;
gameProperties[GamePropertyKey.PropsListedInLobby] = (roomOptions.CustomRoomPropertiesForLobby == null) ? new string[0] : roomOptions.CustomRoomPropertiesForLobby;
gameProperties.MergeStringKeys(roomOptions.CustomRoomProperties);
if (roomOptions.MaxPlayers > 0)
{
gameProperties[GamePropertyKey.MaxPlayers] = roomOptions.MaxPlayers;
}
op[ParameterCode.GameProperties] = gameProperties;
int flags = 0; // a new way to send the room options as bitwise-flags
op[ParameterCode.CleanupCacheOnLeave] = roomOptions.CleanupCacheOnLeave; // this is actually setting the room's config
flags = flags | (int)RoomOptionBit.DeleteCacheOnLeave;
if (!roomOptions.CleanupCacheOnLeave)
{
gameProperties[GamePropertyKey.CleanupCacheOnLeave] = false; // this is only informational for the clients which join
}
if (roomOptions.CheckUserOnJoin)
{
flags = flags | (int)RoomOptionBit.CheckUserOnJoin;
op[ParameterCode.CheckUserOnJoin] = true; //TURNBASED
}
if (roomOptions.PlayerTtl > 0 || roomOptions.PlayerTtl == -1)
{
flags = flags | (int)RoomOptionBit.CheckUserOnJoin;
op[ParameterCode.CheckUserOnJoin] = true; // this affects rejoining a room. requires a userId to be used. added in v1.67
op[ParameterCode.PlayerTTL] = roomOptions.PlayerTtl; // TURNBASED
}
if (roomOptions.EmptyRoomTtl > 0)
{
op[ParameterCode.EmptyRoomTTL] = roomOptions.EmptyRoomTtl; //TURNBASED
}
if (roomOptions.SuppressRoomEvents)
{
flags = flags | (int)RoomOptionBit.SuppressRoomEvents;
op[ParameterCode.SuppressRoomEvents] = true;
}
if (roomOptions.Plugins != null)
{
op[ParameterCode.Plugins] = roomOptions.Plugins;
}
if (roomOptions.PublishUserId)
{
flags = flags | (int)RoomOptionBit.PublishUserId;
op[ParameterCode.PublishUserId] = true;
}
if (roomOptions.DeleteNullProperties)
{
flags = flags | (int)RoomOptionBit.DeleteNullProps; // this is only settable as flag
}
op[ParameterCode.RoomOptionFlags] = flags;
}
///
/// Creates a room (on either Master or Game Server).
/// The OperationResponse depends on the server the peer is connected to:
/// Master will return a Game Server to connect to.
/// Game Server will return the joined Room's data.
/// This is an async request which triggers a OnOperationResponse() call.
///
///
/// If the room is already existing, the OperationResponse will have a returnCode of ErrorCode.GameAlreadyExists.
///
public virtual bool OpCreateRoom(EnterRoomParams opParams)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpCreateRoom()");
}
Dictionary op = new Dictionary();
if (!string.IsNullOrEmpty(opParams.RoomName))
{
op[ParameterCode.RoomName] = opParams.RoomName;
}
if (opParams.Lobby != null && !string.IsNullOrEmpty(opParams.Lobby.Name))
{
op[ParameterCode.LobbyName] = opParams.Lobby.Name;
op[ParameterCode.LobbyType] = (byte)opParams.Lobby.Type;
}
if (opParams.ExpectedUsers != null && opParams.ExpectedUsers.Length > 0)
{
op[ParameterCode.Add] = opParams.ExpectedUsers;
}
if (opParams.OnGameServer)
{
if (opParams.PlayerProperties != null && opParams.PlayerProperties.Count > 0)
{
op[ParameterCode.PlayerProperties] = opParams.PlayerProperties;
op[ParameterCode.Broadcast] = true; // TODO: check if this also makes sense when creating a room?! // broadcast actor properties
}
this.RoomOptionsToOpParameters(op, opParams.RoomOptions);
}
//this.Listener.DebugReturn(DebugLevel.INFO, "CreateGame: " + SupportClass.DictionaryToString(op));
return this.OpCustom(OperationCode.CreateGame, op, true);
}
///
/// Joins a room by name or creates new room if room with given name not exists.
/// The OperationResponse depends on the server the peer is connected to:
/// Master will return a Game Server to connect to.
/// Game Server will return the joined Room's data.
/// This is an async request which triggers a OnOperationResponse() call.
///
///
/// If the room is not existing (anymore), the OperationResponse will have a returnCode of ErrorCode.GameDoesNotExist.
/// Other possible ErrorCodes are: GameClosed, GameFull.
///
/// If the operation could be sent (requires connection).
public virtual bool OpJoinRoom(EnterRoomParams opParams)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpJoinRoom()");
}
Dictionary op = new Dictionary();
if (!string.IsNullOrEmpty(opParams.RoomName))
{
op[ParameterCode.RoomName] = opParams.RoomName;
}
if (opParams.CreateIfNotExists)
{
op[ParameterCode.JoinMode] = (byte)JoinMode.CreateIfNotExists;
if (opParams.Lobby != null)
{
op[ParameterCode.LobbyName] = opParams.Lobby.Name;
op[ParameterCode.LobbyType] = (byte)opParams.Lobby.Type;
}
}
if (opParams.RejoinOnly)
{
op[ParameterCode.JoinMode] = (byte)JoinMode.RejoinOnly; // changed from JoinMode.JoinOrRejoin
}
if (opParams.ExpectedUsers != null && opParams.ExpectedUsers.Length > 0)
{
op[ParameterCode.Add] = opParams.ExpectedUsers;
}
if (opParams.OnGameServer)
{
if (opParams.PlayerProperties != null && opParams.PlayerProperties.Count > 0)
{
op[ParameterCode.PlayerProperties] = opParams.PlayerProperties;
op[ParameterCode.Broadcast] = true; // broadcast actor properties
}
if (opParams.CreateIfNotExists)
{
this.RoomOptionsToOpParameters(op, opParams.RoomOptions);
}
}
//UnityEngine.Debug.Log("JoinGame: " + SupportClass.DictionaryToString(op));
return this.OpCustom(OperationCode.JoinGame, op, true);
}
///
/// Operation to join a random, available room. Overloads take additional player properties.
/// This is an async request which triggers a OnOperationResponse() call.
/// If all rooms are closed or full, the OperationResponse will have a returnCode of ErrorCode.NoRandomMatchFound.
/// If successful, the OperationResponse contains a gameserver address and the name of some room.
///
/// If the operation could be sent currently (requires connection).
public virtual bool OpJoinRandomRoom(OpJoinRandomRoomParams opJoinRandomRoomParams)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpJoinRandomRoom()");
}
Hashtable expectedRoomProperties = new Hashtable();
expectedRoomProperties.MergeStringKeys(opJoinRandomRoomParams.ExpectedCustomRoomProperties);
if (opJoinRandomRoomParams.ExpectedMaxPlayers > 0)
{
expectedRoomProperties[GamePropertyKey.MaxPlayers] = opJoinRandomRoomParams.ExpectedMaxPlayers;
}
Dictionary opParameters = new Dictionary();
if (expectedRoomProperties.Count > 0)
{
opParameters[ParameterCode.GameProperties] = expectedRoomProperties;
}
if (opJoinRandomRoomParams.MatchingType != MatchmakingMode.FillRoom)
{
opParameters[ParameterCode.MatchMakingType] = (byte)opJoinRandomRoomParams.MatchingType;
}
if (opJoinRandomRoomParams.TypedLobby != null && !string.IsNullOrEmpty(opJoinRandomRoomParams.TypedLobby.Name))
{
opParameters[ParameterCode.LobbyName] = opJoinRandomRoomParams.TypedLobby.Name;
opParameters[ParameterCode.LobbyType] = (byte)opJoinRandomRoomParams.TypedLobby.Type;
}
if (!string.IsNullOrEmpty(opJoinRandomRoomParams.SqlLobbyFilter))
{
opParameters[ParameterCode.Data] = opJoinRandomRoomParams.SqlLobbyFilter;
}
if (opJoinRandomRoomParams.ExpectedUsers != null && opJoinRandomRoomParams.ExpectedUsers.Length > 0)
{
opParameters[ParameterCode.Add] = opJoinRandomRoomParams.ExpectedUsers;
}
//this.Listener.DebugReturn(DebugLevel.INFO, "OpJoinRandom: " + SupportClass.DictionaryToString(opParameters));
return this.OpCustom(OperationCode.JoinRandomGame, opParameters, true);
}
///
/// Leaves a room with option to come back later or "for good".
///
/// Async games can be re-joined (loaded) later on. Set to false, if you want to abandon a game entirely.
/// If the opteration can be send currently.
public virtual bool OpLeaveRoom(bool becomeInactive)
{
Dictionary opParameters = new Dictionary();
if (becomeInactive)
{
opParameters[ParameterCode.IsInactive] = becomeInactive;
}
return this.OpCustom(OperationCode.Leave, opParameters, true);
}
/// Gets a list of games matching a SQL-like where clause.
///
/// Operation is only available in lobbies of type SqlLobby.
/// This is an async request which triggers a OnOperationResponse() call.
/// Returned game list is stored in RoomInfoList.
///
///
/// The lobby to query. Has to be of type SqlLobby.
/// The sql query statement.
/// If the operation could be sent (has to be connected).
public virtual bool OpGetGameList(TypedLobby lobby, string queryData)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpGetGameList()");
}
if (lobby == null)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpGetGameList not sent. Lobby cannot be null.");
}
return false;
}
if (lobby.Type != LobbyType.SqlLobby)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpGetGameList not sent. LobbyType must be SqlLobby.");
}
return false;
}
Dictionary opParameters = new Dictionary();
opParameters[(byte)ParameterCode.LobbyName] = lobby.Name;
opParameters[(byte)ParameterCode.LobbyType] = (byte)lobby.Type;
opParameters[(byte)ParameterCode.Data] = queryData;
return this.OpCustom(OperationCode.GetGameList, opParameters, true);
}
///
/// Request the rooms and online status for a list of friends (each client must set a unique username via OpAuthenticate).
///
///
/// Used on Master Server to find the rooms played by a selected list of users.
/// Users identify themselves by using OpAuthenticate with a unique username.
/// The list of usernames must be fetched from some other source (not provided by Photon).
///
/// The server response includes 2 arrays of info (each index matching a friend from the request):
/// ParameterCode.FindFriendsResponseOnlineList = bool[] of online states
/// ParameterCode.FindFriendsResponseRoomIdList = string[] of room names (empty string if not in a room)
///
/// Array of friend's names (make sure they are unique).
/// If the operation could be sent (requires connection).
public virtual bool OpFindFriends(string[] friendsToFind)
{
Dictionary opParameters = new Dictionary();
if (friendsToFind != null && friendsToFind.Length > 0)
{
opParameters[ParameterCode.FindFriendsRequestList] = friendsToFind;
}
return this.OpCustom(OperationCode.FindFriends, opParameters, true);
}
public bool OpSetCustomPropertiesOfActor(int actorNr, Hashtable actorProperties)
{
return this.OpSetPropertiesOfActor(actorNr, actorProperties.StripToStringKeys(), null);
}
///
/// Sets properties of a player / actor.
/// Internally this uses OpSetProperties, which can be used to either set room or player properties.
///
/// The payer ID (a.k.a. actorNumber) of the player to attach these properties to.
/// The properties to add or update.
/// If set, these must be in the current properties-set (on the server) to set actorProperties: CAS.
/// Set these to forward the properties to a WebHook as defined for this app (in Dashboard).
/// If the operation could be sent (requires connection).
protected internal bool OpSetPropertiesOfActor(int actorNr, Hashtable actorProperties, Hashtable expectedProperties = null, WebFlags webflags = null)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpSetPropertiesOfActor()");
}
if (actorNr <= 0 || actorProperties == null)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpSetPropertiesOfActor not sent. ActorNr must be > 0 and actorProperties != null.");
}
return false;
}
Dictionary opParameters = new Dictionary();
opParameters.Add(ParameterCode.Properties, actorProperties);
opParameters.Add(ParameterCode.ActorNr, actorNr);
opParameters.Add(ParameterCode.Broadcast, true);
if (expectedProperties != null && expectedProperties.Count != 0)
{
opParameters.Add(ParameterCode.ExpectedValues, expectedProperties);
}
if (webflags != null && webflags.HttpForward)
{
opParameters[ParameterCode.EventForward] = webflags.WebhookFlags;
}
return this.OpCustom((byte)OperationCode.SetProperties, opParameters, true, 0, false);
}
protected void OpSetPropertyOfRoom(byte propCode, object value)
{
Hashtable properties = new Hashtable();
properties[propCode] = value;
this.OpSetPropertiesOfRoom(properties);
}
public bool OpSetCustomPropertiesOfRoom(Hashtable gameProperties)
{
return this.OpSetPropertiesOfRoom(gameProperties.StripToStringKeys());
}
///
/// Sets properties of a room.
/// Internally this uses OpSetProperties, which can be used to either set room or player properties.
///
/// The properties to add or update.
/// The properties expected when update occurs. (CAS : "Check And Swap")
/// WebFlag to indicate if request should be forwarded as "PathProperties" webhook or not.
/// If the operation could be sent (has to be connected).
protected internal bool OpSetPropertiesOfRoom(Hashtable gameProperties, Hashtable expectedProperties = null, WebFlags webflags = null)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpSetPropertiesOfRoom()");
}
Dictionary opParameters = new Dictionary();
opParameters.Add(ParameterCode.Properties, gameProperties);
opParameters.Add(ParameterCode.Broadcast, true);
if (expectedProperties != null && expectedProperties.Count != 0)
{
opParameters.Add(ParameterCode.ExpectedValues, expectedProperties);
}
if (webflags!=null && webflags.HttpForward)
{
opParameters[ParameterCode.EventForward] = webflags.WebhookFlags;
}
return this.OpCustom((byte)OperationCode.SetProperties, opParameters, true, 0, false);
}
///
/// Sends this app's appId and appVersion to identify this application server side.
/// This is an async request which triggers a OnOperationResponse() call.
///
///
/// This operation makes use of encryption, if that is established before.
/// See: EstablishEncryption(). Check encryption with IsEncryptionAvailable.
/// This operation is allowed only once per connection (multiple calls will have ErrorCode != Ok).
///
/// Your application's name or ID to authenticate. This is assigned by Photon Cloud (webpage).
/// The client's version (clients with differing client appVersions are separated and players don't meet).
/// Contains all values relevant for authentication. Even without account system (external Custom Auth), the clients are allowed to identify themselves.
/// Optional region code, if the client should connect to a specific Photon Cloud Region.
/// Set to true on Master Server to receive "Lobby Statistics" events.
/// If the operation could be sent (has to be connected).
public virtual bool OpAuthenticate(string appId, string appVersion, AuthenticationValues authValues, string regionCode, bool getLobbyStatistics)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpAuthenticate()");
}
Dictionary opParameters = new Dictionary();
if (getLobbyStatistics)
{
// must be sent in operation, even if a Token is available
opParameters[ParameterCode.LobbyStats] = true;
}
// shortcut, if we have a Token
if (authValues != null && authValues.Token != null)
{
opParameters[ParameterCode.Secret] = authValues.Token;
return this.OpCustom(OperationCode.Authenticate, opParameters, true, (byte)0, false); // we don't have to encrypt, when we have a token (which is encrypted)
}
// without a token, we send a complete op auth
opParameters[ParameterCode.AppVersion] = appVersion;
opParameters[ParameterCode.ApplicationId] = appId;
if (!string.IsNullOrEmpty(regionCode))
{
opParameters[ParameterCode.Region] = regionCode;
}
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 (!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(OperationCode.Authenticate, opParameters, true, (byte)0, this.IsEncryptionAvailable);
}
///
/// Sends this app's appId and appVersion to identify this application server side.
/// This is an async request which triggers a OnOperationResponse() call.
///
///
/// This operation makes use of encryption, if that is established before.
/// See: EstablishEncryption(). Check encryption with IsEncryptionAvailable.
/// This operation is allowed only once per connection (multiple calls will have ErrorCode != Ok).
///
/// Your application's name or ID to authenticate. This is assigned by Photon Cloud (webpage).
/// The client's version (clients with differing client appVersions are separated and players don't meet).
/// Optional authentication values. The client can set no values or a UserId or some parameters for Custom Authentication by a server.
/// Optional region code, if the client should connect to a specific Photon Cloud Region.
///
///
/// If the operation could be sent (has to be connected).
public virtual bool OpAuthenticateOnce(string appId, string appVersion, AuthenticationValues authValues, string regionCode, EncryptionMode encryptionMode, ConnectionProtocol expectedProtocol)
{
if (this.DebugOut >= DebugLevel.INFO)
{
this.Listener.DebugReturn(DebugLevel.INFO, "OpAuthenticate()");
}
var opParameters = new Dictionary();
// shortcut, if we have a Token
if (authValues != null && authValues.Token != null)
{
opParameters[ParameterCode.Secret] = authValues.Token;
return this.OpCustom(OperationCode.AuthenticateOnce, opParameters, true, (byte)0, false); // we don't have to encrypt, when we have a token (which is encrypted)
}
opParameters[ParameterCode.ExpectedProtocol] = (byte)expectedProtocol;
opParameters[ParameterCode.EncryptionMode] = (byte)encryptionMode;
opParameters[ParameterCode.AppVersion] = appVersion;
opParameters[ParameterCode.ApplicationId] = appId;
if (!string.IsNullOrEmpty(regionCode))
{
opParameters[ParameterCode.Region] = regionCode;
}
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 (!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(OperationCode.AuthenticateOnce, opParameters, true, (byte)0, this.IsEncryptionAvailable);
}
///
/// Operation to handle this client's interest groups (for events in room).
///
///
/// Note the difference between passing null and byte[0]:
/// null won't add/remove any groups.
/// byte[0] will add/remove all (existing) groups.
/// First, removing groups is executed. This way, you could leave all groups and join only the ones provided.
///
/// Changes become active not immediately but when the server executes this operation (approximately RTT/2).
///
/// Groups to remove from interest. Null will not remove any. A byte[0] will remove all.
/// Groups to add to interest. Null will not add any. A byte[0] will add all current.
/// If operation could be enqueued for sending. Sent when calling: Service or SendOutgoingCommands.
public virtual bool OpChangeGroups(byte[] groupsToRemove, byte[] groupsToAdd)
{
if (this.DebugOut >= DebugLevel.ALL)
{
this.Listener.DebugReturn(DebugLevel.ALL, "OpChangeGroups()");
}
Dictionary opParameters = new Dictionary();
if (groupsToRemove != null)
{
opParameters[(byte)ParameterCode.Remove] = groupsToRemove;
}
if (groupsToAdd != null)
{
opParameters[(byte)ParameterCode.Add] = groupsToAdd;
}
return this.OpCustom((byte)OperationCode.ChangeGroups, opParameters, true, 0);
}
///
/// Send an event with custom code/type and any content to the other players in the same room.
///
/// This override explicitly uses another parameter order to not mix it up with the implementation for Hashtable only.
/// Identifies this type of event (and the content). Your game's event codes can start with 0.
/// Any serializable datatype (including Hashtable like the other OpRaiseEvent overloads).
/// If this event has to arrive reliably (potentially repeated if it's lost).
/// Contains (slightly) less often used options. If you pass null, the default options will be used.
/// If operation could be enqueued for sending. Sent when calling: Service or SendOutgoingCommands.
public virtual bool OpRaiseEvent(byte eventCode, object customEventContent, bool sendReliable, RaiseEventOptions raiseEventOptions)
{
this.opParameters.Clear(); // re-used private variable to avoid many new Dictionary() calls (garbage collection)
this.opParameters[(byte)ParameterCode.Code] = (byte)eventCode;
if (customEventContent != null)
{
this.opParameters[(byte) ParameterCode.Data] = customEventContent;
}
if (raiseEventOptions == null)
{
raiseEventOptions = RaiseEventOptions.Default;
}
else
{
if (raiseEventOptions.CachingOption != EventCaching.DoNotCache)
{
this.opParameters[(byte) ParameterCode.Cache] = (byte) raiseEventOptions.CachingOption;
}
if (raiseEventOptions.Receivers != ReceiverGroup.Others)
{
this.opParameters[(byte) ParameterCode.ReceiverGroup] = (byte) raiseEventOptions.Receivers;
}
if (raiseEventOptions.InterestGroup != 0)
{
this.opParameters[(byte) ParameterCode.Group] = (byte) raiseEventOptions.InterestGroup;
}
if (raiseEventOptions.TargetActors != null)
{
this.opParameters[(byte) ParameterCode.ActorList] = raiseEventOptions.TargetActors;
}
if (raiseEventOptions.Flags.HttpForward)
{
this.opParameters[(byte) ParameterCode.EventForward] = raiseEventOptions.Flags.WebhookFlags; //TURNBASED
}
}
return this.OpCustom((byte) OperationCode.RaiseEvent, this.opParameters, sendReliable, raiseEventOptions.SequenceChannel, false);
}
///
/// Internally used operation to set some "per server" settings. This is for the Master Server.
///
/// Set to true, to get Lobby Statistics (lists of existing lobbies).
/// False if the operation could not be sent.
public virtual bool OpSettings(bool receiveLobbyStats)
{
if (this.DebugOut >= DebugLevel.ALL)
{
this.Listener.DebugReturn(DebugLevel.ALL, "OpSettings()");
}
// re-used private variable to avoid many new Dictionary() calls (garbage collection)
this.opParameters.Clear();
// implementation for Master Server:
if (receiveLobbyStats)
{
this.opParameters[(byte)0] = receiveLobbyStats;
}
if (this.opParameters.Count == 0)
{
// no need to send op in case we set the default values
return true;
}
return this.OpCustom((byte)OperationCode.ServerSettings, this.opParameters, true);
}
}
public class OpJoinRandomRoomParams
{
public Hashtable ExpectedCustomRoomProperties;
public byte ExpectedMaxPlayers;
public MatchmakingMode MatchingType;
public TypedLobby TypedLobby;
public string SqlLobbyFilter;
public string[] ExpectedUsers;
}
public class EnterRoomParams
{
public string RoomName;
public RoomOptions RoomOptions;
public TypedLobby Lobby;
public Hashtable PlayerProperties;
public bool OnGameServer = true; // defaults to true! better send more parameter than too few (GS needs all)
public bool CreateIfNotExists;
public bool RejoinOnly;
public string[] ExpectedUsers;
}
///
/// 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 ConnectedToMasterserver (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.
[Obsolete("Use InvalidOperation.")]
public const int InvalidOperationCode = -2;
/// (-2) The operation you called could not be executed on the server.
///
/// Make sure you are connected to the server you expect.
///
/// This code is used in several cases:
/// The arguments/parameters of the operation might be out of range, missing entirely or conflicting.
/// The operation you called is not implemented on the server (application). Server-side plugins affect the available operations.
///
public const int InvalidOperation = -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;
[Obsolete("No longer used, cause random matchmaking is no longer a process.")]
public const int AlreadyMatched = 0x7FFF - 4;
/// (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://www.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;
///
/// (32752) A server-side plugin (or webhook) failed to execute and reported an error. Check the OperationResponse.DebugMessage.
///
public const int PluginReportedError = 0x7FFF - 15;
///
/// (32751) CreateGame/JoinGame/Join operation fails if expected plugin does not correspond to loaded one.
///
public const int PluginMismatch = 0x7FFF - 16;
///
/// (32750) for join requests. Indicates the current peer already called join and is joined to the room.
///
public const int JoinFailedPeerAlreadyJoined = 32750; // 0x7FFF - 17,
///
/// (32749) for join requests. Indicates the list of InactiveActors already contains an actor with the requested ActorNr or UserId.
///
public const int JoinFailedFoundInactiveJoiner = 32749; // 0x7FFF - 18,
///
/// (32748) for join requests. Indicates the list of Actors (active and inactive) did not contain an actor with the requested ActorNr or UserId.
///
public const int JoinFailedWithRejoinerNotFound = 32748; // 0x7FFF - 19,
///
/// (32747) for join requests. Note: for future use - Indicates the requested UserId was found in the ExcludedList.
///
public const int JoinFailedFoundExcludedUserId = 32747; // 0x7FFF - 20,
///
/// (32746) for join requests. Indicates the list of ActiveActors already contains an actor with the requested ActorNr or UserId.
///
public const int JoinFailedFoundActiveJoiner = 32746; // 0x7FFF - 21,
///
/// (32745) for SetProerties and Raisevent (if flag HttpForward is true) requests. Indicates the maximum allowd http requests per minute was reached.
///
public const int HttpLimitReached = 32745; // 0x7FFF - 22,
///
/// (32744) for WebRpc requests. Indicates the the call to the external service failed.
///
public const int ExternalHttpCallFailed = 32744; // 0x7FFF - 23,
///
/// (32742) Server error during matchmaking with slot reservation. E.g. the reserved slots can not exceed MaxPlayers.
///
public const int SlotError = 32742; // 0x7FFF - 25,
///
/// (32741) Server will react with this error if invalid encryption parameters provided by token
///
public const int InvalidEncryptionParameters = 32741; // 0x7FFF - 24,
}
///
/// Class for constants. These (byte) values define "well known" properties for an Actor / Player.
///
///
/// Pun uses these constants internally.
/// "Custom properties" have to use a string-type as key. They can be assigned at will.
///
public class ActorProperties
{
/// (255) Name of a player/actor.
public const byte PlayerName = 255; // was: 1
/// (254) Tells you if the player is currently in this game (getting events live).
/// A server-set value for async games, where players can leave the game and return later.
public const byte IsInactive = 254;
/// (253) UserId of the player. Sent when room gets created with RoomOptions.PublishUserId = true.
public const byte UserId = 253;
}
///
/// Class for constants. These (byte) values are for "well known" room/game properties used in Photon Loadbalancing.
///
///
/// Pun uses these constants internally.
/// "Custom properties" have to use a string-type as key. They can be assigned at will.
///
public class GamePropertyKey
{
/// (255) Max number of players that "fit" into this room. 0 is for "unlimited".
public const byte MaxPlayers = 255;
/// (254) Makes this room listed or not in the lobby on master.
public const byte IsVisible = 254;
/// (253) Allows more players to join a room (or not).
public const byte IsOpen = 253;
/// (252) Current count of players in the room. Used only in the lobby on master.
public const byte PlayerCount = 252;
/// (251) True if the room is to be removed from room listing (used in update to room list in lobby on master)
public const byte Removed = 251;
/// (250) A list of the room properties to pass to the RoomInfo list in a lobby. This is used in CreateRoom, which defines this list once per room.
public const byte PropsListedInLobby = 250;
/// (249) Equivalent of Operation Join parameter CleanupCacheOnLeave.
public const byte CleanupCacheOnLeave = 249;
/// (248) Code for MasterClientId, which is synced by server. When sent as op-parameter this is (byte)203. As room property this is (byte)248.
/// Tightly related to ParameterCode.MasterClientId.
public const byte MasterClientId = (byte)248;
/// (247) Code for ExpectedUsers in a room. Matchmaking keeps a slot open for the players with these userIDs.
public const byte ExpectedUsers = (byte)247;
}
///
/// Class for constants. These values are for events defined by Photon Loadbalancing.
///
/// They start at 255 and go DOWN. Your own in-game events can start at 0. Pun uses these constants internally.
public class EventCode
{
/// (230) Initial list of RoomInfos (in lobby on Master)
public const byte GameList = 230;
/// (229) Update of RoomInfos to be merged into "initial" list (in lobby on Master)
public const byte GameListUpdate = 229;
/// (228) Currently not used. State of queueing in case of server-full
public const byte QueueState = 228;
/// (227) Currently not used. Event for matchmaking
public const byte Match = 227;
/// (226) Event with stats about this application (players, rooms, etc)
public const byte AppStats = 226;
/// (224) This event provides a list of lobbies with their player and game counts.
public const byte LobbyStats = 224;
/// (210) Internally used in case of hosting by Azure
[Obsolete("TCP routing was removed after becoming obsolete.")]
public const byte AzureNodeInfo = 210;
/// (255) Event Join: someone joined the game. The new actorNumber is provided as well as the properties of that actor (if set in OpJoin).
public const byte Join = (byte)255;
/// (254) Event Leave: The player who left the game can be identified by the actorNumber.
public const byte Leave = (byte)254;
/// (253) When you call OpSetProperties with the broadcast option "on", this event is fired. It contains the properties being set.
public const byte PropertiesChanged = (byte)253;
/// (253) When you call OpSetProperties with the broadcast option "on", this event is fired. It contains the properties being set.
[Obsolete("Use PropertiesChanged now.")]
public const byte SetProperties = (byte)253;
/// (252) When player left game unexpected and the room has a playerTtl > 0, this event is fired to let everyone know about the timeout.
/// Obsolete. Replaced by Leave. public const byte Disconnect = LiteEventCode.Disconnect;
/// (251) Sent by Photon Cloud when a plugin-call or webhook-call failed. Usually, the execution on the server continues, despite the issue. Contains: ParameterCode.Info.
///
public const byte ErrorInfo = 251;
/// (250) Sent by Photon whent he event cache slice was changed. Done by OpRaiseEvent.
public const byte CacheSliceChanged = 250;
/// (223) Sent by Photon to update a token before it times out.
public const byte AuthEvent = 223;
}
/// Class for constants. Codes for parameters of Operations and Events.
/// Pun uses these constants internally.
public class ParameterCode
{
/// (237) A bool parameter for creating games. If set to true, no room events are sent to the clients on join and leave. Default: false (and not sent).
public const byte SuppressRoomEvents = 237;
/// (236) Time To Live (TTL) for a room when the last player leaves. Keeps room in memory for case a player re-joins soon. In milliseconds.
public const byte EmptyRoomTTL = 236;
/// (235) Time To Live (TTL) for an 'actor' in a room. If a client disconnects, this actor is inactive first and removed after this timeout. In milliseconds.
public const byte PlayerTTL = 235;
/// (234) Optional parameter of OpRaiseEvent and OpSetCustomProperties to forward the event/operation to a web-service.
public const byte EventForward = 234;
/// (233) Optional parameter of OpLeave in async games. If false, the player does abandons the game (forever). By default players become inactive and can re-join.
[Obsolete("Use: IsInactive")]
public const byte IsComingBack = (byte)233;
/// (233) Used in EvLeave to describe if a user is inactive (and might come back) or not. In rooms with PlayerTTL, becoming inactive is the default case.
public const byte IsInactive = (byte)233;
/// (232) Used when creating rooms to define if any userid can join the room only once.
public const byte CheckUserOnJoin = (byte)232;
/// (231) Code for "Check And Swap" (CAS) when changing properties.
public const byte ExpectedValues = (byte)231;
/// (230) Address of a (game) server to use.
public const byte Address = 230;
/// (229) Count of players in this application in a rooms (used in stats event)
public const byte PeerCount = 229;
/// (228) Count of games in this application (used in stats event)
public const byte GameCount = 228;
/// (227) Count of players on the master server (in this app, looking for rooms)
public const byte MasterPeerCount = 227;
/// (225) User's ID
public const byte UserId = 225;
/// (224) Your application's ID: a name on your own Photon or a GUID on the Photon Cloud
public const byte ApplicationId = 224;
/// (223) Not used currently (as "Position"). If you get queued before connect, this is your position
public const byte Position = 223;
/// (223) Modifies the matchmaking algorithm used for OpJoinRandom. Allowed parameter values are defined in enum MatchmakingMode.
public const byte MatchMakingType = 223;
/// (222) List of RoomInfos about open / listed rooms
public const byte GameList = 222;
/// (221) Internally used to establish encryption
public const byte Secret = 221;
/// (220) Version of your application
public const byte AppVersion = 220;
/// (210) Internally used in case of hosting by Azure
[Obsolete("TCP routing was removed after becoming obsolete.")]
public const byte AzureNodeInfo = 210; // only used within events, so use: EventCode.AzureNodeInfo
/// (209) Internally used in case of hosting by Azure
[Obsolete("TCP routing was removed after becoming obsolete.")]
public const byte AzureLocalNodeId = 209;
/// (208) Internally used in case of hosting by Azure
[Obsolete("TCP routing was removed after becoming obsolete.")]
public const byte AzureMasterNodeId = 208;
/// (255) Code for the gameId/roomName (a unique name per room). Used in OpJoin and similar.
public const byte RoomName = (byte)255;
/// (250) Code for broadcast parameter of OpSetProperties method.
public const byte Broadcast = (byte)250;
/// (252) Code for list of players in a room. Currently not used.
public const byte ActorList = (byte)252;
/// (254) Code of the Actor of an operation. Used for property get and set.
public const byte ActorNr = (byte)254;
/// (249) Code for property set (Hashtable).
public const byte PlayerProperties = (byte)249;
/// (245) Code of data/custom content of an event. Used in OpRaiseEvent.
public const byte CustomEventContent = (byte)245;
/// (245) Code of data of an event. Used in OpRaiseEvent.
public const byte Data = (byte)245;
/// (244) Code used when sending some code-related parameter, like OpRaiseEvent's event-code.
/// This is not the same as the Operation's code, which is no longer sent as part of the parameter Dictionary in Photon 3.
public const byte Code = (byte)244;
/// (248) Code for property set (Hashtable).
public const byte GameProperties = (byte)248;
///
/// (251) Code for property-set (Hashtable). This key is used when sending only one set of properties.
/// If either ActorProperties or GameProperties are used (or both), check those keys.
///
public const byte Properties = (byte)251;
/// (253) Code of the target Actor of an operation. Used for property set. Is 0 for game
public const byte TargetActorNr = (byte)253;
/// (246) Code to select the receivers of events (used in Lite, Operation RaiseEvent).
public const byte ReceiverGroup = (byte)246;
/// (247) Code for caching events while raising them.
public const byte Cache = (byte)247;
/// (241) Bool parameter of CreateGame Operation. If true, server cleans up roomcache of leaving players (their cached events get removed).
public const byte CleanupCacheOnLeave = (byte)241;
/// (240) Code for "group" operation-parameter (as used in Op RaiseEvent).
public const byte Group = 240;
/// (239) The "Remove" operation-parameter can be used to remove something from a list. E.g. remove groups from player's interest groups.
public const byte Remove = 239;
/// (239) Used in Op Join to define if UserIds of the players are broadcast in the room. Useful for FindFriends and reserving slots for expected users.
public const byte PublishUserId = 239;
/// (238) The "Add" operation-parameter can be used to add something to some list or set. E.g. add groups to player's interest groups.
public const byte Add = 238;
/// (218) Content for EventCode.ErrorInfo and internal debug operations.
public const byte Info = 218;
/// (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;
/// (215) Makes the server create a room if it doesn't exist. OpJoin uses this to always enter a room, unless it exists and is full/closed.
// public const byte CreateIfNotExists = 215;
/// (215) The JoinMode enum defines which variant of joining a room will be executed: Join only if available, create if not exists or re-join.
/// Replaces CreateIfNotExists which was only a bool-value.
public const byte JoinMode = 215;
/// (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;
/// (203) Code for MasterClientId, which is synced by server. When sent as op-parameter this is code 203.
/// Tightly related to GamePropertyKey.MasterClientId.
public const byte MasterClientId = (byte)203;
/// (1) Used in Op FindFriends request. Value must be string[] of friends to look up.
public const byte FindFriendsRequestList = (byte)1;
/// (1) Used in Op FindFriends response. Contains bool[] list of online states (false if not online).
public const byte FindFriendsResponseOnlineList = (byte)1;
/// (2) Used in Op FindFriends response. Contains string[] of room names ("" where not known or no room joined).
public const byte FindFriendsResponseRoomIdList = (byte)2;
/// (213) Used in matchmaking-related methods and when creating a room to name a lobby (to join or to attach a room to).
public const byte LobbyName = (byte)213;
/// (212) Used in matchmaking-related methods and when creating a room to define the type of a lobby. Combined with the lobby name this identifies the lobby.
public const byte LobbyType = (byte)212;
/// (211) This (optional) parameter can be sent in Op Authenticate to turn on Lobby Stats (info about lobby names and their user- and game-counts). See: PhotonNetwork.Lobbies
public const byte LobbyStats = (byte)211;
/// (210) Used for region values in OpAuth and OpGetRegions.
public const byte Region = (byte)210;
/// (209) Path of the WebRPC that got called. Also known as "WebRpc Name". Type: string.
public const byte UriPath = 209;
/// (208) Parameters for a WebRPC as: Dictionary<string, object>. This will get serialized to JSon.
public const byte WebRpcParameters = 208;
/// (207) ReturnCode for the WebRPC, as sent by the web service (not by Photon, which uses ErrorCode). Type: byte.
public const byte WebRpcReturnCode = 207;
/// (206) Message returned by WebRPC server. Analog to Photon's debug message. Type: string.
public const byte WebRpcReturnMessage = 206;
/// (205) Used to define a "slice" for cached events. Slices can easily be removed from cache. Type: int.
public const byte CacheSliceIndex = 205;
/// (204) Informs the server of the expected plugin setup.
///
/// The operation will fail in case of a plugin mismatch returning error code PluginMismatch 32751(0x7FFF - 16).
/// Setting string[]{} means the client expects no plugin to be setup.
/// Note: for backwards compatibility null omits any check.
///
public const byte Plugins = 204;
/// (202) Used by the server in Operation Responses, when it sends the nickname of the client (the user's nickname).
public const byte NickName = 202;
/// (201) Informs user about name of plugin load to game
public const byte PluginName = 201;
/// (200) Informs user about version of plugin load to game
public const byte PluginVersion = 200;
/// (195) Protocol which will be used by client to connect master/game servers. Used for nameserver.
public const byte ExpectedProtocol = 195;
/// (194) Set of custom parameters which are sent in auth request.
public const byte CustomInitData = 194;
/// (193) How are we going to encrypt data.
public const byte EncryptionMode = 193;
/// (192) Parameter of Authentication, which contains encryption keys (depends on AuthMode and EncryptionMode).
public const byte EncryptionData = 192;
/// (191) An int parameter summarizing several boolean room-options with bit-flags.
public const byte RoomOptionFlags = 191;
}
///
/// Class for constants. Contains operation codes.
/// Pun uses these constants internally.
///
public class OperationCode
{
[Obsolete("Exchanging encrpytion keys is done internally in the lib now. Don't expect this operation-result.")]
public const byte ExchangeKeysForEncryption = 250;
/// (255) Code for OpJoin, to get into a room.
[Obsolete]
public const byte Join = 255;
/// (231) Authenticates this peer and connects to a virtual application
public const byte AuthenticateOnce = 231;
/// (230) Authenticates this peer and connects to a virtual application
public const byte Authenticate = 230;
/// (229) Joins lobby (on master)
public const byte JoinLobby = 229;
/// (228) Leaves lobby (on master)
public const byte LeaveLobby = 228;
/// (227) Creates a game (or fails if name exists)
public const byte CreateGame = 227;
/// (226) Join game (by name)
public const byte JoinGame = 226;
/// (225) Joins random game (on master)
public const byte JoinRandomGame = 225;
// public const byte CancelJoinRandom = 224; // obsolete, cause JoinRandom no longer is a "process". now provides result immediately
/// (254) Code for OpLeave, to get out of a room.
public const byte Leave = (byte)254;
/// (253) Raise event (in a room, for other actors/players)
public const byte RaiseEvent = (byte)253;
/// (252) Set Properties (of room or actor/player)
public const byte SetProperties = (byte)252;
/// (251) Get Properties
public const byte GetProperties = (byte)251;
/// (248) Operation code to change interest groups in Rooms (Lite application and extending ones).
public const byte ChangeGroups = (byte)248;
/// (222) Request the rooms and online status for a list of friends (by name, which should be unique).
public const byte FindFriends = 222;
/// (221) Request statistics about a specific list of lobbies (their user and game count).
public const byte GetLobbyStats = 221;
/// (220) Get list of regional servers from a NameServer.
public const byte GetRegions = 220;
/// (219) WebRpc Operation.
public const byte WebRpc = 219;
/// (218) Operation to set some server settings. Used with different parameters on various servers.
public const byte ServerSettings = 218;
/// (217) Get the game list matching a supplied sql filter (SqlListLobby only)
public const byte GetGameList = 217;
}
/// Defines possible values for OpJoinRoom and OpJoinOrCreate. It tells the server if the room can be only be joined normally, created implicitly or found on a web-service for Turnbased games.
/// These values are not directly used by a game but implicitly set.
public enum JoinMode : byte
{
/// Regular join. The room must exist.
Default = 0,
/// Join or create the room if it's not existing. Used for OpJoinOrCreate for example.
CreateIfNotExists = 1,
/// The room might be out of memory and should be loaded (if possible) from a Turnbased web-service.
JoinOrRejoin = 2,
/// Only re-join will be allowed. If the user is not yet in the room, this will fail.
RejoinOnly = 3,
}
///
/// Options for matchmaking rules for OpJoinRandom.
///
public enum MatchmakingMode : byte
{
/// Fills up rooms (oldest first) to get players together as fast as possible. Default.
/// Makes most sense with MaxPlayers > 0 and games that can only start with more players.
FillRoom = 0,
/// Distributes players across available rooms sequentially but takes filter into account. Without filter, rooms get players evenly distributed.
SerialMatching = 1,
/// Joins a (fully) random room. Expected properties must match but aside from this, any available room might be selected.
RandomMatching = 2
}
///
/// Lite - OpRaiseEvent lets you chose which actors in the room should receive events.
/// By default, events are sent to "Others" but you can overrule this.
///
public enum ReceiverGroup : byte
{
/// Default value (not sent). Anyone else gets my event.
Others = 0,
/// Everyone in the current room (including this peer) will get this event.
All = 1,
/// The server sends this event only to the actor with the lowest actorNumber.
/// The "master client" does not have special rights but is the one who is in this room the longest time.
MasterClient = 2,
}
///
/// Lite - OpRaiseEvent allows you to cache events and automatically send them to joining players in a room.
/// Events are cached per event code and player: Event 100 (example!) can be stored once per player.
/// Cached events can be modified, replaced and removed.
///
///
/// Caching works only combination with ReceiverGroup options Others and All.
///
public enum EventCaching : byte
{
/// Default value (not sent).
DoNotCache = 0,
/// Will merge this event's keys with those already cached.
[Obsolete]
MergeCache = 1,
/// Replaces the event cache for this eventCode with this event's content.
[Obsolete]
ReplaceCache = 2,
/// Removes this event (by eventCode) from the cache.
[Obsolete]
RemoveCache = 3,
/// Adds an event to the room's cache
AddToRoomCache = 4,
/// Adds this event to the cache for actor 0 (becoming a "globally owned" event in the cache).
AddToRoomCacheGlobal = 5,
/// Remove fitting event from the room's cache.
RemoveFromRoomCache = 6,
/// Removes events of players who already left the room (cleaning up).
RemoveFromRoomCacheForActorsLeft = 7,
/// Increase the index of the sliced cache.
SliceIncreaseIndex = 10,
/// Set the index of the sliced cache. You must set RaiseEventOptions.CacheSliceIndex for this.
SliceSetIndex = 11,
/// Purge cache slice with index. Exactly one slice is removed from cache. You must set RaiseEventOptions.CacheSliceIndex for this.
SlicePurgeIndex = 12,
/// Purge cache slices with specified index and anything lower than that. You must set RaiseEventOptions.CacheSliceIndex for this.
SlicePurgeUpToIndex = 13,
}
///
/// Flags for "types of properties", being used as filter in OpGetProperties.
///
[Flags]
public enum PropertyTypeFlag : byte
{
/// (0x00) Flag type for no property type.
None = 0x00,
/// (0x01) Flag type for game-attached properties.
Game = 0x01,
/// (0x02) Flag type for actor related propeties.
Actor = 0x02,
/// (0x01) Flag type for game AND actor properties. Equal to 'Game'
GameAndActor = Game | Actor
}
/// Wraps up common room properties needed when you create rooms. Read the individual entries for more details.
/// This directly maps to the fields in the Room class.
public class RoomOptions
{
/// Defines if this room is listed in the lobby. If not, it also is not joined randomly.
///
/// A room that is not visible will be excluded from the room lists that are sent to the clients in lobbies.
/// An invisible room can be joined by name but is excluded from random matchmaking.
///
/// Use this to "hide" a room and simulate "private rooms". Players can exchange a roomname and create it
/// invisble to avoid anyone else joining it.
///
public bool IsVisible { get { return this.isVisible; } set { this.isVisible = value; } }
private bool isVisible = true;
/// Defines if this room can be joined at all.
///
/// If a room is closed, no player can join this. As example this makes sense when 3 of 4 possible players
/// start their gameplay early and don't want anyone to join during the game.
/// The room can still be listed in the lobby (set isVisible to control lobby-visibility).
///
public bool IsOpen { get { return this.isOpen; } set { this.isOpen = value; } }
private bool isOpen = true;
/// Max number of players that can be in the room at any time. 0 means "no limit".
public byte MaxPlayers;
/// Time To Live (TTL) for an 'actor' in a room. If a client disconnects, this actor is inactive first and removed after this timeout. In milliseconds.
public int PlayerTtl;
/// Time To Live (TTL) for a room when the last player leaves. Keeps room in memory for case a player re-joins soon. In milliseconds.
public int EmptyRoomTtl;
/// Activates UserId checks on joining - allowing a users to be only once in the room.
///
/// Turnbased rooms should be created with this check turned on! They should also use custom authentication.
/// Disabled by default for backwards-compatibility.
///
public bool CheckUserOnJoin { get; set; }
/// Removes a user's events and properties from the room when a user leaves.
///
/// This makes sense when in rooms where players can't place items in the room and just vanish entirely.
/// When you disable this, the event history can become too long to load if the room stays in use indefinitely.
/// Default: true. Cleans up the cache and props of leaving users.
///
public bool CleanupCacheOnLeave { get { return this.cleanupCacheOnLeave; } set { this.cleanupCacheOnLeave = value; } }
private bool cleanupCacheOnLeave = true;
/// The room's custom properties to set. Use string keys!
///
/// Custom room properties are any key-values you need to define the game's setup.
/// The shorter your keys are, the better.
/// Example: Map, Mode (could be "m" when used with "Map"), TileSet (could be "t").
///
public Hashtable CustomRoomProperties;
/// Defines the custom room properties that get listed in the lobby.
///
/// Name the custom room properties that should be available to clients that are in a lobby.
/// Use with care. Unless a custom property is essential for matchmaking or user info, it should
/// not be sent to the lobby, which causes traffic and delays for clients in the lobby.
///
/// Default: No custom properties are sent to the lobby.
///
public string[] CustomRoomPropertiesForLobby = new string[0];
/// Informs the server of the expected plugin setup.
///
/// The operation will fail in case of a plugin missmatch returning error code PluginMismatch 32757(0x7FFF - 10).
/// Setting string[]{} means the client expects no plugin to be setup.
/// Note: for backwards compatibility null omits any check.
///
public string[] Plugins;
///
/// Tells the server to skip room events for joining and leaving players.
///
///
/// Using this makes the client unaware of the other players in a room.
/// That can save some traffic if you have some server logic that updates players
/// but it can also limit the client's usability.
///
public bool SuppressRoomEvents { get; set; }
///
/// Defines if the UserIds of players get "published" in the room. Useful for FindFriends, if players want to play another game together.
///
///
/// When you set this to true, Photon will publish the UserIds of the players in that room.
/// In that case, you can use PhotonPlayer.userId, to access any player's userID.
/// This is useful for FindFriends and to set "expected users" to reserve slots in a room (see PhotonNetwork.JoinRoom e.g.).
///
public bool PublishUserId { get; set; }
/// Optionally, properties get deleted, when null gets assigned as value. Defaults to off / false.
///
/// When Op SetProperties is setting a key's value to null, the server and clients should remove the key/value from the Custom Properties.
/// By default, the server keeps the keys (and null values) and sends them to joining players.
///
/// Important: Only when SetProperties does a "broadcast", the change (key, value = null) is sent to clients to update accordingly.
/// This applies to Custom Properties for rooms and actors/players.
///
public bool DeleteNullProperties { get; set; }
}
/// Aggregates several less-often used options for operation RaiseEvent. See field descriptions for usage details.
public class RaiseEventOptions
{
/// Default options: CachingOption: DoNotCache, InterestGroup: 0, targetActors: null, receivers: Others, sequenceChannel: 0.
public readonly static RaiseEventOptions Default = new RaiseEventOptions();
/// Defines if the server should simply send the event, put it in the cache or remove events that are like this one.
///
/// When using option: SliceSetIndex, SlicePurgeIndex or SlicePurgeUpToIndex, set a CacheSliceIndex. All other options except SequenceChannel get ignored.
///
public EventCaching CachingOption;
/// The number of the Interest Group to send this to. 0 goes to all users but to get 1 and up, clients must subscribe to the group first.
public byte InterestGroup;
/// A list of PhotonPlayer.IDs to send this event to. You can implement events that just go to specific users this way.
public int[] TargetActors;
/// Sends the event to All, MasterClient or Others (default). Be careful with MasterClient, as the client might disconnect before it got the event and it gets lost.
public ReceiverGroup Receivers;
/// Events are ordered per "channel". If you have events that are independent of others, they can go into another sequence or channel.
public byte SequenceChannel;
/// Optional flags to be used in Photon client SDKs with Op RaiseEvent and Op SetProperties.
/// Introduced mainly for webhooks 1.2 to control behavior of forwarded HTTP requests.
public WebFlags Flags = WebFlags.Default;
///// Used along with CachingOption SliceSetIndex, SlicePurgeIndex or SlicePurgeUpToIndex if you want to set or purge a specific cache-slice.
//public int CacheSliceIndex;
//public bool Encrypt;
}
///
/// Options of lobby types available. Lobby types might be implemented in certain Photon versions and won't be available on older servers.
///
public enum LobbyType :byte
{
/// This lobby is used unless another is defined by game or JoinRandom. Room-lists will be sent and JoinRandomRoom can filter by matching properties.
Default = 0,
/// This lobby type lists rooms like Default but JoinRandom has a parameter for SQL-like "where" clauses for filtering. This allows bigger, less, or and and combinations.
SqlLobby = 2,
/// This lobby does not send lists of games. It is only used for OpJoinRandomRoom. It keeps rooms available for a while when there are only inactive users left.
AsyncRandomLobby = 3
}
/// Refers to a specific lobby (and type) on the server.
///
/// The name and type are the unique identifier for a lobby.
/// Join a lobby via PhotonNetwork.JoinLobby(TypedLobby lobby).
/// The current lobby is stored in PhotonNetwork.lobby.
///
public class TypedLobby
{
/// Name of the lobby this game gets added to. Default: null, attached to default lobby. Lobbies are unique per lobbyName plus lobbyType, so the same name can be used when several types are existing.
public string Name;
/// Type of the (named)lobby this game gets added to
public LobbyType Type;
public static readonly TypedLobby Default = new TypedLobby();
public bool IsDefault { get { return this.Type == LobbyType.Default && string.IsNullOrEmpty(this.Name); } }
public TypedLobby()
{
this.Name = string.Empty;
this.Type = LobbyType.Default;
}
public TypedLobby(string name, LobbyType type)
{
this.Name = name;
this.Type = type;
}
public override string ToString()
{
return String.Format((string) "lobby '{0}'[{1}]", (object) this.Name, (object) this.Type);
}
}
public class TypedLobbyInfo : TypedLobby
{
public int PlayerCount;
public int RoomCount;
public override string ToString()
{
return string.Format("TypedLobbyInfo '{0}'[{1}] rooms: {2} players: {3}", this.Name, this.Type, this.RoomCount, this.PlayerCount);
}
}
///
/// Options for authentication modes. From "classic" auth on each server to AuthOnce (on NameServer).
///
public enum AuthModeOption { Auth, AuthOnce, AuthOnceWss }
///
/// 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 AuthValues are sent in OpAuthenticate when you connect, so they must be set before you connect.
/// Should you not set any AuthValues, PUN will create them and set the playerName as userId in them.
/// 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://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..
/// 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.
/// 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}", this.UserId, this.AuthGetParameters, this.Token != null);
}
}
}