/Assets/Photon Unity Networking/Plugins/PhotonNetwork/NetworkingPeer.cs
C# | 3079 lines | 2336 code | 426 blank | 317 comment | 609 complexity | 7d0a804d894b99ec16e5bca6ed79f367 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- // --------------------------------------------------------------------------------------------------------------------
- // <copyright file="NetworkingPeer.cs" company="Exit Games GmbH">
- // Part of: Photon Unity Networking (PUN)
- // </copyright>
- // --------------------------------------------------------------------------------------------------------------------
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Reflection;
- using ExitGames.Client.Photon;
- using ExitGames.Client.Photon.Lite;
- using UnityEngine;
- /// <summary>
- /// Implements Photon LoadBalancing used in PUN.
- /// This class is used internally by PhotonNetwork and not intended as public API.
- /// </summary>
- internal class NetworkingPeer : LoadbalancingPeer, IPhotonPeerListener
- {
- // game properties must be cached, because the game is created on the master and then "re-created" on the game server
- // both must use the same props for the game
- public string mAppVersion;
- private string mAppId;
- private string masterServerAddress;
- private string playername = "";
- private IPhotonPeerListener externalListener;
- private JoinType mLastJoinType;
- private bool mPlayernameHasToBeUpdated;
- public string PlayerName
- {
- get
- {
- return this.playername;
- }
- set
- {
- if (string.IsNullOrEmpty(value) || value.Equals(this.playername))
- {
- return;
- }
- if (this.mLocalActor != null)
- {
- this.mLocalActor.name = value;
- }
- this.playername = value;
- if (this.mCurrentGame != null)
- {
- // Only when in a room
- this.SendPlayerName();
- }
- }
- }
- public PeerState State { get; internal set; }
- // "public" access to the current game - is null unless a room is joined on a gameserver
- public Room mCurrentGame
- {
- get
- {
- if (this.mRoomToGetInto != null && this.mRoomToGetInto.isLocalClientInside)
- {
- return this.mRoomToGetInto;
- }
- return null;
- }
- }
- /// <summary>
- /// keeps the custom properties, gameServer address and anything else about the room we want to get into
- /// </summary>
- internal Room mRoomToGetInto { get; set; }
- public Dictionary<int, PhotonPlayer> mActors = new Dictionary<int, PhotonPlayer>();
- public PhotonPlayer[] mOtherPlayerListCopy = new PhotonPlayer[0];
- public PhotonPlayer[] mPlayerListCopy = new PhotonPlayer[0];
- public PhotonPlayer mLocalActor { get; internal set; }
- public PhotonPlayer mMasterClient = null;
- public string mGameserver { get; internal set; }
- public bool requestSecurity = true;
- private Dictionary<Type, List<MethodInfo>> monoRPCMethodsCache = new Dictionary<Type, List<MethodInfo>>();
- public static bool UsePrefabCache = true;
- public static Dictionary<string, GameObject> PrefabCache = new Dictionary<string, GameObject>();
- public Dictionary<string, RoomInfo> mGameList = new Dictionary<string, RoomInfo>();
- public RoomInfo[] mGameListCopy = new RoomInfo[0];
- public int mQueuePosition { get; internal set; }
- public bool insideLobby = false;
- /// <summary>Stat value: Count of players on Master (looking for rooms)</summary>
- public int mPlayersOnMasterCount { get; internal set; }
- /// <summary>Stat value: Count of Rooms</summary>
- public int mGameCount { get; internal set; }
- /// <summary>Stat value: Count of Players in rooms</summary>
- public int mPlayersInRoomsCount { get; internal set; }
- /// <summary>
- /// Instantiated objects by their instantiationId. The id (key) is per actor.
- /// </summary>
- public Dictionary<int, GameObject> instantiatedObjects = new Dictionary<int, GameObject>();
- private HashSet<int> allowedReceivingGroups = new HashSet<int>();
- private HashSet<int> blockSendingGroups = new HashSet<int>();
- internal protected Dictionary<int, PhotonView> photonViewList = new Dictionary<int, PhotonView>(); //TODO: make private again
- internal protected short currentLevelPrefix = 0;
- private readonly Dictionary<Type, Dictionary<PhotonNetworkingMessage, MethodInfo>> cachedMethods = new Dictionary<Type, Dictionary<PhotonNetworkingMessage, MethodInfo>>();
- private readonly Dictionary<string, int> rpcShortcuts; // lookup "table" for the index (shortcut) of an RPC name
- public NetworkingPeer(IPhotonPeerListener listener, string playername, ConnectionProtocol connectionProtocol) : base(listener, connectionProtocol)
- {
- this.Listener = this;
- // don't set the field directly! the listener is passed on to other classes, which get updated by the property set method
- this.externalListener = listener;
- this.PlayerName = playername;
- this.mLocalActor = new PhotonPlayer(true, -1, this.playername);
- this.AddNewPlayer(this.mLocalActor.ID, this.mLocalActor);
- // RPC shortcut lookup creation (from list of RPCs, which is updated by Editor scripts)
- rpcShortcuts = new Dictionary<string, int>(PhotonNetwork.PhotonServerSettings.RpcList.Count);
- for (int index = 0; index < PhotonNetwork.PhotonServerSettings.RpcList.Count; index++)
- {
- var name = PhotonNetwork.PhotonServerSettings.RpcList[index];
- rpcShortcuts[name] = index;
- }
- this.State = global::PeerState.PeerCreated;
- }
- #region Operations and Connection Methods
- public override bool Connect(string serverAddress, string appID)
- {
- if (PhotonNetwork.connectionStateDetailed == global::PeerState.Disconnecting)
- {
- Debug.LogError("ERROR: Cannot connect to Photon while Disconnecting. Connection failed.");
- return false;
- }
- if (string.IsNullOrEmpty(this.masterServerAddress))
- {
- this.masterServerAddress = serverAddress;
- }
- this.mAppId = appID.Trim();
- // connect might fail, if the DNS name can't be resolved or if no network connection is available
- bool connecting = base.Connect(serverAddress, "");
- this.State = connecting ? global::PeerState.Connecting : global::PeerState.Disconnected;
- return connecting;
- }
- /// <summary>
- /// Complete disconnect from photon (and the open master OR game server)
- /// </summary>
- public override void Disconnect()
- {
- if (this.PeerState == PeerStateValue.Disconnected)
- {
- if (this.DebugOut >= DebugLevel.WARNING)
- {
- this.DebugReturn(DebugLevel.WARNING, string.Format("Can't execute Disconnect() while not connected. Nothing changed. State: {0}", this.State));
- }
- return;
- }
- base.Disconnect();
- this.State = global::PeerState.Disconnecting;
- this.LeftRoomCleanup();
- this.LeftLobbyCleanup();
- }
- // just switches servers(Master->Game). don't remove the room, actors, etc
- private void DisconnectFromMaster()
- {
- base.Disconnect();
- this.State = global::PeerState.DisconnectingFromMasterserver;
- LeftLobbyCleanup();
- }
- // switches back from gameserver to master and removes the room, actors, etc
- private void DisconnectFromGameServer()
- {
- base.Disconnect();
- this.State = global::PeerState.DisconnectingFromGameserver;
- this.LeftRoomCleanup();
- }
- /// <summary>
- /// Called at disconnect/leavelobby etc. This CAN also be called when we are not in a lobby (e.g. disconnect from room)
- /// </summary>
- private void LeftLobbyCleanup()
- {
- if (!insideLobby)
- {
- return;
- }
- SendMonoMessage(PhotonNetworkingMessage.OnLeftLobby);
- this.insideLobby = false;
- }
- /// <summary>
- /// Called when "this client" left a room to clean up.
- /// </summary>
- private void LeftRoomCleanup()
- {
- bool wasInRoom = mRoomToGetInto != null;
- // when leaving a room, we clean up depending on that room's settings.
- bool autoCleanupSettingOfRoom = (this.mRoomToGetInto != null) ? this.mRoomToGetInto.autoCleanUp : PhotonNetwork.autoCleanUpPlayerObjects;
- this.mRoomToGetInto = null;
- this.mActors = new Dictionary<int, PhotonPlayer>();
- mPlayerListCopy = new PhotonPlayer[0];
- mOtherPlayerListCopy = new PhotonPlayer[0];
- this.mMasterClient = null;
- this.allowedReceivingGroups = new HashSet<int>();
- this.blockSendingGroups = new HashSet<int>();
- this.mGameList = new Dictionary<string, RoomInfo>();
- mGameListCopy = new RoomInfo[0];
- this.ChangeLocalID(-1);
- // Cleanup all network objects (all spawned PhotonViews, local and remote)
- if (autoCleanupSettingOfRoom)
- {
- // Fill list with Instantiated objects
- List<GameObject> goList = new List<GameObject>(this.instantiatedObjects.Values);
- // Fill list with other PhotonViews (contains doubles from Instantiated GO's)
- foreach (PhotonView view in this.photonViewList.Values)
- {
- if (view != null && !view.isSceneView && view.gameObject != null)
- {
- goList.Add(view.gameObject);
- }
- }
- // Destroy GO's
- for (int i = goList.Count - 1; i >= 0; i--)
- {
- GameObject go = goList[i];
- if (go != null)
- {
- if (this.DebugOut >= DebugLevel.ALL)
- {
- this.DebugReturn(DebugLevel.ALL, "Network destroy Instantiated GO: " + go.name);
- }
- this.DestroyGO(go);
- }
- }
- this.instantiatedObjects = new Dictionary<int, GameObject>();
- PhotonNetwork.manuallyAllocatedViewIds = new List<int>();
- PhotonNetwork.lastUsedViewSubId = 0;
- PhotonNetwork.lastUsedViewSubIdStatic = 0;
- }
- if (wasInRoom)
- {
- SendMonoMessage(PhotonNetworkingMessage.OnLeftRoom);
- }
- }
- /// <summary>
- /// This is a safe way to delete GO's as it makes sure to cleanup our PhotonViews instead of relying on "OnDestroy" which is called at the end of the current frame only.
- /// </summary>
- /// <param name="go">GameObject to destroy.</param>
- void DestroyGO(GameObject go)
- {
- PhotonView[] views = go.GetComponentsInChildren<PhotonView>();
- foreach (PhotonView view in views)
- {
- if (view != null)
- {
- view.destroyedByPhotonNetworkOrQuit = true;
- this.RemovePhotonView(view);
- }
- }
- GameObject.Destroy(go);
- }
- // gameID can be null (optional). The server assigns a unique name if no name is set
- // joins a room and sets your current username as custom actorproperty (will broadcast that)
- #endregion
- #region Helpers
- private void readoutStandardProperties(Hashtable gameProperties, Hashtable pActorProperties, int targetActorNr)
- {
- // Debug.LogWarning("readoutStandardProperties game=" + gameProperties + " actors(" + pActorProperties + ")=" + pActorProperties + " " + targetActorNr);
- // read game properties and cache them locally
- if (this.mCurrentGame != null && gameProperties != null)
- {
- this.mCurrentGame.CacheProperties(gameProperties);
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonCustomRoomPropertiesChanged);
- if (PhotonNetwork.automaticallySyncScene)
- {
- this.AutomaticallySyncScene(); // will load new scene if sceneName was changed
- }
- }
- if (pActorProperties != null && pActorProperties.Count > 0)
- {
- if (targetActorNr > 0)
- {
- // we have a single entry in the pActorProperties with one
- // user's name
- // targets MUST exist before you set properties
- PhotonPlayer target = this.GetPlayerWithID(targetActorNr);
- if (target != null)
- {
- target.InternalCacheProperties(this.GetActorPropertiesForActorNr(pActorProperties, targetActorNr));
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonPlayerPropertiesChanged, target);
- }
- }
- else
- {
- // in this case, we've got a key-value pair per actor (each
- // value is a hashtable with the actor's properties then)
- int actorNr;
- Hashtable props;
- string newName;
- PhotonPlayer target;
- foreach (object key in pActorProperties.Keys)
- {
- actorNr = (int)key;
- props = (Hashtable)pActorProperties[key];
- newName = (string)props[ActorProperties.PlayerName];
- target = this.GetPlayerWithID(actorNr);
- if (target == null)
- {
- target = new PhotonPlayer(false, actorNr, newName);
- this.AddNewPlayer(actorNr, target);
- }
- target.InternalCacheProperties(props);
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonPlayerPropertiesChanged, target);
- }
- }
- }
- }
- private void AddNewPlayer(int ID, PhotonPlayer player)
- {
- if (!this.mActors.ContainsKey(ID))
- {
- this.mActors[ID] = player;
- RebuildPlayerListCopies();
- }
- else
- {
- Debug.LogError("Adding player twice: " + ID);
- }
- }
- void RemovePlayer(int ID, PhotonPlayer player)
- {
- this.mActors.Remove(ID);
- if (!player.isLocal)
- {
- RebuildPlayerListCopies();
- }
- }
- void RebuildPlayerListCopies()
- {
- this.mPlayerListCopy = new PhotonPlayer[this.mActors.Count];
- this.mActors.Values.CopyTo(this.mPlayerListCopy, 0);
- List<PhotonPlayer> otherP = new List<PhotonPlayer>();
- foreach (PhotonPlayer player in this.mPlayerListCopy)
- {
- if (!player.isLocal)
- {
- otherP.Add(player);
- }
- }
- this.mOtherPlayerListCopy = otherP.ToArray();
- }
- /// <summary>
- /// Resets the PhotonView "lastOnSerializeDataSent" so that "OnReliable" synched PhotonViews send a complete state to new clients (if the state doesnt change, no messages would be send otherwise!).
- /// Note that due to this reset, ALL other players will receive the full OnSerialize.
- /// </summary>
- private void ResetPhotonViewsOnSerialize()
- {
- foreach (PhotonView photonView in this.photonViewList.Values)
- {
- photonView.lastOnSerializeDataSent = null;
- }
- }
- /// <summary>
- /// Called when the event Leave (of some other player) arrived.
- /// Cleans game objects, views locally. The master will also clean the
- /// </summary>
- /// <param name="actorID">ID of player who left.</param>
- private void HandleEventLeave(int actorID)
- {
- if (this.DebugOut >= DebugLevel.INFO)
- {
- this.DebugReturn(DebugLevel.INFO, "HandleEventLeave actorNr: " + actorID);
- }
- // actorNr is fetched out of event above
- if (actorID < 0 || !this.mActors.ContainsKey(actorID))
- {
- if (this.DebugOut >= DebugLevel.ERROR)
- {
- this.DebugReturn(DebugLevel.ERROR, string.Format("Received event Leave for unknown actorNumber: {0}", actorID));
- }
- return;
- }
- PhotonPlayer player = this.GetPlayerWithID(actorID);
- if (player == null)
- {
- Debug.LogError("Error: HandleEventLeave for actorID=" + actorID + " has no PhotonPlayer!");
- }
- // 1: Elect new masterclient, ignore the leaving player (as it's still in playerlists)
- if (this.mMasterClient != null && this.mMasterClient.ID == actorID)
- {
- this.mMasterClient = null;
- }
- this.CheckMasterClient(actorID);
- // 2: Destroy objects & buffered messages
- if (this.mCurrentGame != null && this.mCurrentGame.autoCleanUp)
- {
- this.DestroyPlayerObjects(player, true);
- }
- RemovePlayer(actorID, player);
- // 4: Finally, send notification (the playerList and masterclient are now updated)
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonPlayerDisconnected, player);
- }
- /// <summary>
- /// Chooses the new master client. Supply ignoreActorID to ignore a specific actor (e.g. when this actor has just left)
- /// </summary>
- /// <param name="ignoreActorID"></param>
- private void CheckMasterClient(int ignoreActorID)
- {
- int lowestActorNumber = int.MaxValue;
- if (this.mMasterClient != null && this.mActors.ContainsKey(this.mMasterClient.ID))
- {
- // the current masterClient is still in the list of players, so it can't change
- return;
- }
- // the master is unknown. find lowest actornumber == master
- foreach (int actorNumber in this.mActors.Keys)
- {
- if (ignoreActorID != -1 && ignoreActorID == actorNumber)
- {
- continue; //Skip this actor as it's leaving.
- }
- if (actorNumber < lowestActorNumber)
- {
- lowestActorNumber = actorNumber;
- }
- }
- if (this.mMasterClient == null || this.mMasterClient.ID != lowestActorNumber)
- {
- this.mMasterClient = this.mActors[lowestActorNumber];
- bool leavingPlayerWasMaster = ignoreActorID > 0; // that value is the playerID who's leaving or -1
- if (leavingPlayerWasMaster)
- {
- SendMonoMessage(PhotonNetworkingMessage.OnMasterClientSwitched, this.mMasterClient);
- }
- }
- }
- private Hashtable GetActorPropertiesForActorNr(Hashtable actorProperties, int actorNr)
- {
- if (actorProperties.ContainsKey(actorNr))
- {
- return (Hashtable)actorProperties[actorNr];
- }
- return actorProperties;
- }
- private PhotonPlayer GetPlayerWithID(int number)
- {
- if (this.mActors != null && this.mActors.ContainsKey(number))
- {
- return this.mActors[number];
- }
- return null;
- }
- private void SendPlayerName()
- {
- if (this.State == global::PeerState.Joining)
- {
- // this means, the join on the gameServer is sent (with an outdated name). send the new when in game
- this.mPlayernameHasToBeUpdated = true;
- return;
- }
- if (this.mLocalActor != null)
- {
- this.mLocalActor.name = this.PlayerName;
- Hashtable properties = new Hashtable();
- properties[ActorProperties.PlayerName] = this.PlayerName;
- this.OpSetPropertiesOfActor(this.mLocalActor.ID, properties, true, (byte)0);
- this.mPlayernameHasToBeUpdated = false;
- }
- }
- private void GameEnteredOnGameServer(OperationResponse operationResponse)
- {
- if (operationResponse.ReturnCode != 0)
- {
- switch (operationResponse.OperationCode)
- {
- case OperationCode.CreateGame:
- this.DebugReturn(DebugLevel.ERROR, "Create failed on GameServer. Changing back to MasterServer. Msg: " + operationResponse.DebugMessage);
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonCreateRoomFailed);
- break;
- case OperationCode.JoinGame:
- this.DebugReturn(DebugLevel.WARNING, "Join failed on GameServer. Changing back to MasterServer. Msg: " + operationResponse.DebugMessage);
- if (operationResponse.ReturnCode == ErrorCode.GameDoesNotExist)
- {
- Debug.Log("Most likely the game became empty during the switch to GameServer.");
- }
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonJoinRoomFailed);
- break;
- case OperationCode.JoinRandomGame:
- this.DebugReturn(DebugLevel.WARNING, "Join failed on GameServer. Changing back to MasterServer. Msg: " + operationResponse.DebugMessage);
- if (operationResponse.ReturnCode == ErrorCode.GameDoesNotExist)
- {
- Debug.Log("Most likely the game became empty during the switch to GameServer.");
- }
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonRandomJoinFailed);
- break;
- }
- this.DisconnectFromGameServer();
- return;
- }
- this.State = global::PeerState.Joined;
- this.mRoomToGetInto.isLocalClientInside = true;
- Hashtable actorProperties = (Hashtable)operationResponse[ParameterCode.PlayerProperties];
- Hashtable gameProperties = (Hashtable)operationResponse[ParameterCode.GameProperties];
- this.readoutStandardProperties(gameProperties, actorProperties, 0);
- // the local player's actor-properties are not returned in join-result. add this player to the list
- int localActorNr = (int)operationResponse[ParameterCode.ActorNr];
- this.ChangeLocalID(localActorNr);
- this.CheckMasterClient(-1);
- if (this.mPlayernameHasToBeUpdated)
- {
- this.SendPlayerName();
- }
- switch (operationResponse.OperationCode)
- {
- case OperationCode.CreateGame:
- SendMonoMessage(PhotonNetworkingMessage.OnCreatedRoom);
- break;
- case OperationCode.JoinGame:
- case OperationCode.JoinRandomGame:
- // the mono message for this is sent at another place
- break;
- }
- }
- private Hashtable GetLocalActorProperties()
- {
- if (PhotonNetwork.player != null)
- {
- return PhotonNetwork.player.allProperties;
- }
- Hashtable actorProperties = new Hashtable();
- actorProperties[ActorProperties.PlayerName] = this.PlayerName;
- return actorProperties;
- }
- public void ChangeLocalID(int newID)
- {
- if (this.mLocalActor == null)
- {
- Debug.LogWarning(
- string.Format(
- "Local actor is null or not in mActors! mLocalActor: {0} mActors==null: {1} newID: {2}",
- this.mLocalActor,
- this.mActors == null,
- newID));
- }
- if (this.mActors.ContainsKey(this.mLocalActor.ID))
- {
- this.mActors.Remove(this.mLocalActor.ID);
- }
- this.mLocalActor.InternalChangeLocalID(newID);
- this.mActors[this.mLocalActor.ID] = this.mLocalActor;
- this.RebuildPlayerListCopies();
- }
- #endregion
- #region Operations
- public bool OpCreateGame(string gameID, bool isVisible, bool isOpen, byte maxPlayers, bool autoCleanUp, Hashtable customGameProperties, string[] propsListedInLobby)
- {
- this.mRoomToGetInto = new Room(gameID, customGameProperties, isVisible, isOpen, maxPlayers, autoCleanUp, propsListedInLobby);
- return base.OpCreateRoom(gameID, isVisible, isOpen, maxPlayers, autoCleanUp, customGameProperties, this.GetLocalActorProperties(), propsListedInLobby);
- }
- public bool OpJoin(string gameID)
- {
- this.mRoomToGetInto = new Room(gameID, null);
- return this.OpJoinRoom(gameID, this.GetLocalActorProperties());
- }
- // this override just makes sure we have a mRoomToGetInto, even if it's blank (the properties provided in this method are filters. they are not set when we join the game)
- public override bool OpJoinRandomRoom(Hashtable expectedCustomRoomProperties, byte expectedMaxPlayers, Hashtable playerProperties, MatchmakingMode matchingType)
- {
- this.mRoomToGetInto = new Room(null, null);
- return base.OpJoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers, playerProperties, matchingType);
- }
- /// <summary>
- /// Operation Leave will exit any current room.
- /// </summary>
- /// <remarks>
- /// This also happens when you disconnect from the server.
- /// Disconnect might be a step less if you don't want to create a new room on the same server.
- /// </remarks>
- /// <returns></returns>
- public virtual bool OpLeave()
- {
- if (this.State != global::PeerState.Joined)
- {
- this.DebugReturn(DebugLevel.ERROR, "NetworkingPeer::leaveGame() - ERROR: no game is currently joined");
- return false;
- }
- return this.OpCustom((byte)OperationCode.Leave, null, true, 0);
- }
- public override bool OpRaiseEvent(byte eventCode, byte interestGroup, Hashtable evData, bool sendReliable, byte channelId)
- {
- if (PhotonNetwork.offlineMode)
- {
- return false;
- }
- return base.OpRaiseEvent(eventCode, interestGroup, evData, sendReliable, channelId);
- }
- public override bool OpRaiseEvent(byte eventCode, Hashtable evData, bool sendReliable, byte channelId, int[] targetActors, EventCaching cache)
- {
- if (PhotonNetwork.offlineMode)
- {
- return false;
- }
- return base.OpRaiseEvent(eventCode, evData, sendReliable, channelId, targetActors, cache);
- }
- public override bool OpRaiseEvent(byte eventCode, Hashtable evData, bool sendReliable, byte channelId, EventCaching cache, ReceiverGroup receivers)
- {
- if (PhotonNetwork.offlineMode)
- {
- return false;
- }
- return base.OpRaiseEvent(eventCode, evData, sendReliable, channelId, cache, receivers);
- }
- #endregion
- #region Implementation of IPhotonPeerListener
- public void DebugReturn(DebugLevel level, string message)
- {
- this.externalListener.DebugReturn(level, message);
- }
- public void OnOperationResponse(OperationResponse operationResponse)
- {
- if (PhotonNetwork.networkingPeer.State == global::PeerState.Disconnecting)
- {
- if (this.DebugOut >= DebugLevel.INFO)
- {
- this.DebugReturn(DebugLevel.INFO, "OperationResponse ignored while disconnecting: " + operationResponse.OperationCode);
- }
- return;
- }
- // extra logging for error debugging (helping developers with a bit of automated analysis)
- if (operationResponse.ReturnCode == 0)
- {
- if (this.DebugOut >= DebugLevel.INFO)
- {
- this.DebugReturn(DebugLevel.INFO, operationResponse.ToString());
- }
- }
- else
- {
- if (this.DebugOut >= DebugLevel.WARNING)
- {
- if (operationResponse.ReturnCode == ErrorCode.OperationNotAllowedInCurrentState)
- {
- this.DebugReturn(DebugLevel.WARNING, "Operation could not be executed yet. Wait for state JoinedLobby or ConnectedToMaster and their respective callbacks before calling OPs. Client must be authorized.");
- }
- this.DebugReturn(DebugLevel.WARNING, operationResponse.ToStringFull());
- }
- }
- switch (operationResponse.OperationCode)
- {
- case OperationCode.Authenticate:
- {
- // PeerState oldState = this.State;
- if (operationResponse.ReturnCode != 0)
- {
- if (this.DebugOut >= DebugLevel.ERROR)
- {
- this.DebugReturn(DebugLevel.ERROR, string.Format("Authentication failed: '{0}' Code: {1}", operationResponse.DebugMessage, operationResponse.ReturnCode));
- }
- if (operationResponse.ReturnCode == ErrorCode.InvalidOperationCode)
- {
- this.DebugReturn(DebugLevel.ERROR, string.Format("If you host Photon yourself, make sure to start the 'Instance LoadBalancing'"));
- }
- if (operationResponse.ReturnCode == ErrorCode.InvalidAuthentication)
- {
- this.DebugReturn(DebugLevel.ERROR, string.Format("The appId this client sent is unknown on the server (Cloud). Check settings. If using the Cloud, check account."));
- }
- this.Disconnect();
- this.State = global::PeerState.Disconnecting;
- if (operationResponse.ReturnCode == ErrorCode.MaxCcuReached)
- {
- this.DebugReturn(DebugLevel.ERROR, string.Format("Currently, the limit of users is reached for this title. Try again later. Disconnecting"));
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonMaxCccuReached);
- SendMonoMessage(PhotonNetworkingMessage.OnConnectionFail, DisconnectCause.MaxCcuReached);
- }
- else if (operationResponse.ReturnCode == ErrorCode.InvalidRegion)
- {
- this.DebugReturn(DebugLevel.ERROR, string.Format("The used master server address is not available with the subscription currently used. Got to Photon Cloud Dashboard or change URL. Disconnecting"));
- SendMonoMessage(PhotonNetworkingMessage.OnConnectionFail, DisconnectCause.InvalidRegion);
- }
- break;
- }
- else
- {
- if (this.State == global::PeerState.Connected || this.State == global::PeerState.ConnectedComingFromGameserver)
- {
- if (operationResponse.Parameters.ContainsKey(ParameterCode.Position))
- {
- this.mQueuePosition = (int)operationResponse[ParameterCode.Position];
- // returnValues for Authenticate always include this value!
- if (this.mQueuePosition > 0)
- {
- // should only happen, if just out of nowhere the
- // amount of players going online at the same time
- // is increasing faster, than automatically started
- // additional gameservers could have been booten up
- if (this.State == global::PeerState.ConnectedComingFromGameserver)
- {
- this.State = global::PeerState.QueuedComingFromGameserver;
- }
- else
- {
- this.State = global::PeerState.Queued;
- }
- // we break here (not joining the lobby, etc) as this client is queued
- // the EventCode.QueueState will eventually resolve this state
- break;
- }
- }
- if (PhotonNetwork.autoJoinLobby)
- {
- this.OpJoinLobby();
- this.State = global::PeerState.Authenticated;
- }
- else
- {
- this.State = global::PeerState.ConnectedToMaster;
- NetworkingPeer.SendMonoMessage(PhotonNetworkingMessage.OnConnectedToMaster);
- }
- }
- else if (this.State == global::PeerState.ConnectedToGameserver)
- {
- this.State = global::PeerState.Joining;
- if (this.mLastJoinType == JoinType.JoinGame || this.mLastJoinType == JoinType.JoinRandomGame)
- {
- // if we just "join" the game, do so
- this.OpJoin(this.mRoomToGetInto.name);
- }
- else if (this.mLastJoinType == JoinType.CreateGame)
- {
- // on the game server, we have to apply the room properties that were chosen for creation of the room, so we use this.mRoomToGetInto
- this.OpCreateGame(
- this.mRoomToGetInto.name,
- this.mRoomToGetInto.visible,
- this.mRoomToGetInto.open,
- (byte)this.mRoomToGetInto.maxPlayers,
- this.mRoomToGetInto.autoCleanUp,
- this.mRoomToGetInto.customProperties,
- this.mRoomToGetInto.propertiesListedInLobby);
- }
- break;
- }
- }
- break;
- }
- case OperationCode.CreateGame:
- {
- if (this.State != global::PeerState.Joining)
- {
- if (operationResponse.ReturnCode != 0)
- {
- if (this.DebugOut >= DebugLevel.ERROR)
- {
- this.DebugReturn(DebugLevel.ERROR, string.Format("createGame failed, client stays on masterserver: {0}.", operationResponse.ToStringFull()));
- }
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonCreateRoomFailed);
- break;
- }
- string gameID = (string)operationResponse[ParameterCode.RoomName];
- if (!string.IsNullOrEmpty(gameID))
- {
- // is only sent by the server's response, if it has not been
- // sent with the client's request before!
- this.mRoomToGetInto.name = gameID;
- }
- this.mGameserver = (string)operationResponse[ParameterCode.Address];
- this.DisconnectFromMaster();
- this.mLastJoinType = JoinType.CreateGame;
- }
- else
- {
- this.GameEnteredOnGameServer(operationResponse);
- }
- break;
- }
- case OperationCode.JoinGame:
- {
- if (this.State != global::PeerState.Joining)
- {
- if (operationResponse.ReturnCode != 0)
- {
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonJoinRoomFailed);
- if (this.DebugOut >= DebugLevel.WARNING)
- {
- this.DebugReturn(DebugLevel.WARNING, string.Format("JoinRoom failed (room maybe closed by now). Client stays on masterserver: {0}. State: {1}", operationResponse.ToStringFull(), this.State));
- }
- // this.mListener.joinGameReturn(0, null, null, returnCode, debugMsg);
- break;
- }
- this.mGameserver = (string)operationResponse[ParameterCode.Address];
- this.DisconnectFromMaster();
- this.mLastJoinType = JoinType.JoinGame;
- }
- else
- {
- this.GameEnteredOnGameServer(operationResponse);
- }
- break;
- }
- case OperationCode.JoinRandomGame:
- {
- // happens only on master. on gameserver, this is a regular join (we don't need to find a random game again)
- // the operation OpJoinRandom either fails (with returncode 8) or returns game-to-join information
- if (operationResponse.ReturnCode != 0)
- {
- if (operationResponse.ReturnCode == ErrorCode.NoRandomMatchFound)
- {
- this.DebugReturn(DebugLevel.WARNING, "JoinRandom failed: No open game. Client stays in lobby.");
- }
- else if (this.DebugOut >= DebugLevel.ERROR)
- {
- this.DebugReturn(DebugLevel.ERROR, string.Format("JoinRandom failed: {0}.", operationResponse.ToStringFull()));
- }
- SendMonoMessage(PhotonNetworkingMessage.OnPhotonRandomJoinFailed);
- // this.mListener.createGameReturn(0, null, null, returnCode, debugMsg);
- break;
- }
- string gameID = (string)operationResponse[ParameterCode.RoomName];
- this.mRoomToGetInto.name = gameID;
- this.mGameserver = (string)operationResponse[ParameterCode.Address];
- this.DisconnectFromMaster();
- this.mLastJoinType = JoinType.JoinRandomGame;
- break;
- }
- case OperationCode.JoinLobby:
- this.State = global::PeerState.JoinedLobby;
- this.insideLobby = true;
- SendMonoMessage(PhotonNetworkingMessage.OnJoinedLobby);
- // this.mListener.joinLobbyReturn();
- break;
- case OperationCode.LeaveLobby:
- this.State = global::PeerState.Authenticated;
- this.LeftLobbyCleanup();
- break;
- case OperationCode.Leave:
- this.DisconnectFromGameServer();
- break;
- case OperationCode.SetProperties:
- // this.mListener.setPropertiesReturn(returnCode, debugMsg);
- break;
- case OperationCode.GetProperties:
- {
- Hashtable actorProperties = (Hashtable)operationResponse[ParameterCode.PlayerProperties];
- Hashtable gameProperties = (Hashtable)operationResponse[ParameterCode.GameProperties];
- this.readoutStandardProperties(gameProperties, actorProperties, 0);
- // RemoveByteTypedPropertyKeys(actorProperties, false);
- // RemoveByteTypedPropertyKeys(gameProperties, false);
- // this.mListener.getPropertiesReturn(gameProperties, actorProperties, returnCode, debugMsg);
- break;
- }
- case OperationCode.RaiseEvent:
- // this usually doesn't give us a result. only if the caching is affected the server will send one.
- break;
- default:
- if (this.DebugOut >= DebugLevel.ERROR)
- {
- this.DebugReturn(DebugLevel.ERROR, string.Format("operationResponse unhandled: {0}", operationResponse.ToString()));
- }
- break;
- }
- this.externalListener.OnOperationResponse(operationResponse);
- }
- public void OnStatusChanged(StatusCode statusCode)
- {
- if (this.DebugOut >= DebugLevel.INFO)
- {
- this.DebugReturn(DebugLevel.INFO, string.Format("OnStatusChanged: {0}", statusCode.ToString()));
- }
- switch (statusCode)
- {
- case StatusCode.Connect:
- if (this.State == global::PeerState.ConnectingToGameserver)
- {
- if (this.DebugOut >= DebugLevel.ALL)
- {
- this.DebugReturn(DebugLevel.ALL, "Connected to gameserver.");
- }
- this.State = global::PeerState.ConnectedToGameserver;
- }
- else
- {
- if (this.DebugOut >= DebugLevel.ALL)
- {
- this.DebugReturn(DebugLevel.ALL, "Connected to masterserver.");
- }
- if (this.State == global::PeerState.Connecting)
- {
- SendMonoMessage(PhotonNetworkingMessage.OnConnectedToPhoton);
- this.State = global::PeerState.Connected;
- }
- else
- {
- this.State = global::PeerState.ConnectedComingFromGameserver;
- }
- }
- if (this.requestSecurity)
- {
- this.EstablishEncryption();
- }
- else
- {
- if (!this.OpAuthenticate(this.mAppId, this.mAppVersion))
- {
- this.externalListener.DebugReturn(DebugLevel.ERROR, "Error Authenticating! Did not work.");
- }
- }
- break;
- case StatusCode.Disconnect:
- if (this.State == global::PeerState.DisconnectingFromMasterserver)
- {
- if (this.Connect(this.mGameserver, this.mAppId))
- {
- this.State = global::PeerState.ConnectingToGameserver;
- }
- }
- else if (this.State == global::PeerState.DisconnectingFromGameserver)
- {
- if (this.Connect(this.masterServerAddress, this.mAppId))
- {
- this.State = global::PeerState.ConnectingToMasterserver;
- }
- }
- else
- {
- this.LeftRoomCleanup();
- this.State = global::PeerState.PeerCreated;
- SendMonoMessage(PhotonNetworkingMessage.OnDisconnectedFromPhoton);
- }
- break;
- case StatusCode.SecurityExceptionOnConnect:
- case StatusCode.ExceptionOnConnect:
- this.State = global::PeerState.PeerCreated;
- DisconnectCause cause = (DisconnectCause)statusCode;
- SendMonoMessage(PhotonNetworkingMessage.OnFailedToConnectToPhoton, cause);
- break;
- case StatusCode.Exception:
- if (this.State == global::PeerState.Connecting)
- {
- this.DebugReturn(DebugLevel.WARNING, "Exception while connecting to: " + this.ServerAddress + ". Check if the server is available.");
- if (this.ServerAddress == null || this.ServerAddress.StartsWith("127.0.0.1"))
- {
- this.DebugReturn(DebugLevel.WARNING, "The server address is 127.0.0.1 (localhost): Make sure the server is running on this machine. Android and iOS emulators have their own localhost.");
- if (this.ServerAddress == this.mGameserver)
- {
- this.DebugReturn(DebugLevel.WARNING, "This might be a misconfiguration in the game server config. You need to edit it to a (public) address.");
- }
- }
- this.State = global::PeerState.PeerCreated;
- cause = (DisconnectCause)statusCode;
- SendMonoMessage(PhotonNetworkingMessage.OnFailedToConnectToPhoton, cause);
- }
- else
- {
- this.State = global::PeerState.PeerCreated;
- cause = (DisconnectCause)statusCode;
- SendMonoMessage(PhotonNetworkingMessage.OnConnectionFail, cause);
- }
- this.Disconnect();
- break;
- case StatusCode.TimeoutDisconnect:
- case StatusCode.InternalReceiveException:
- case StatusCode.DisconnectByServer:
- case StatusCode.DisconnectByServerLogic:
- case StatusCode.DisconnectByServerUserLimit:
- if (this.State == global::PeerState.Connecting)
- {
- this.DebugReturn(DebugLevel.WARNING, statusCode + " while connecting to: " + this.ServerAddress + ". Check if the server is available.");
- this.State = global::PeerState.PeerCreated;
- cause = (DisconnectCause)statusCode;
- SendMonoMessage(PhotonNetworkingMessage.OnFailedToConnectToPhoton, cause);
- }
- else
- {
- this.State = global::PeerState.PeerCreated;
- cause = (DisconnectCause)statusCode;
- SendMonoMessage(PhotonNetworkingMessage.OnConnectionFail, cause);
- }
- this.Disconnect();
- break;
- case StatusCode.SendError:
- // this.mListener.clientErrorReturn(statusCode);
- break;
- case StatusCode.QueueOutgoingReliableWarning:
- case StatusCode.QueueOutgoingUnreliableWarning:
- case StatusCode.QueueOutgoingAcksWarning:
- case StatusCode.QueueSentWarning:
- // this.mListener.warningReturn(statusCode);
- break;
- case StatusCode.EncryptionEstablished:
- if (!this.OpAuthenticate(this.mAppId, this.mAppVersion))
- {
- this.externalListener.DebugReturn(DebugLevel.ERROR, "Error Authenticating! Did not work.");
- }
- break;
- case StatusCode.EncryptionFailedToEstablish:
- this.externalListener.DebugReturn(DebugLevel.ERROR, "Encryption wasn't established: " + statusCode + ". Going to authenticate anyways.");
- if (!this.OpAuthenticate(this.mAppId, this.mAppVersion))
- {
- this.externalListener.DebugReturn(DebugLevel.ERROR, "Error Authenticating! Did not work.");
- }
- break;
- // // TCP "routing" is an option of Photon that's not currently needed (or supported) by PUN
- //case StatusCode.TcpRouterResponseOk:
- // break;
- //case StatusCode.TcpRouterResponseEndpointUnknown:
- //case StatusCode.TcpRouterResponseNodeIdUnknown:
- //case StatusCode.TcpRouterResponseNodeNotReady:
- // this.DebugReturn(DebugLevel.ERROR, "Unexpected router response: " + statusCode);
- // break;
- default:
- // this.mListener.serverErrorReturn(statusCode.value());
- this.DebugReturn(DebugLevel.ERROR, "Received unknown status code: " + statusCode);
- break;
- }
- this.externalListener.OnStatusChanged(statusCode);
- }
- public void OnEvent(EventData photonEvent)
- {
- if (this.DebugOut >= DebugLevel.INFO)
- {
- this.DebugReturn(DebugLevel.INFO, string.Format("OnEvent: {0}", photonEvent.ToString()));
- }
- int actorNr = -1;
- PhotonPlayer originatingPlayer = null;
- if (photonEvent.Parameters.ContainsKey(ParameterCode.ActorNr))
- {
- actorNr = (int)photonEvent[ParameterCode.ActorNr];
- if (this.mActors.ContainsKey(actorNr))
- {
- originatingPlayer = (PhotonPlayer)this.mActors[actorNr];
- }
- //else
- //{
- // // the actor sending this event is not in actorlist. this is usually no problem
- // if (photonEvent.Code != (byte)LiteOpCode.Join)
- // {
- // Debug.LogWarning("Received event, but we do not have this actor: " + actorNr);
- // }
- //}
- }
- switch (photonEvent.Code)
- {
- case EventCode.GameList:
- {
- this.mGameList = new Dictionary<string, RoomInfo>();
- Hashtable games = (Hashtable)photonEvent[ParameterCode.GameList];
- foreach (DictionaryEntry game in games)
- {
- string gameName = (string)game.Key;
- this.mGameList[gameName] = new RoomInfo(gameName, (Hashtable)game.Value);
- }
- mGameListCopy = new RoomInfo[mGameList.Count];
- mGameList.Values.CopyTo(mGameListCopy, 0);
- SendMonoMessage(PhotonNetworkingMessage.OnReceivedRoomListUpdate);
- break;
- }
- case EventCode.GameListUpdate:
- {
- Hashtable games = (Hashtable)photonEvent[ParameterCode.GameList];
- foreach (DictionaryEntry room in games)
- {
- string gameName = (string)room.Key;
- Room game = new Room(gameName, (Hashtable)room.Value);
- if (game.removedFromList)
- {
- this.mGameList.Remo…
Large files files are truncated, but you can click here to view the full file