PageRenderTime 56ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 1ms

/Assets/Photon Unity Networking/Plugins/PhotonNetwork/NetworkingPeer.cs

https://bitbucket.org/AgentCodeMonkey/gameframework-unity-project
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

  1. // --------------------------------------------------------------------------------------------------------------------
  2. // <copyright file="NetworkingPeer.cs" company="Exit Games GmbH">
  3. // Part of: Photon Unity Networking (PUN)
  4. // </copyright>
  5. // --------------------------------------------------------------------------------------------------------------------
  6. using System;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.Reflection;
  10. using ExitGames.Client.Photon;
  11. using ExitGames.Client.Photon.Lite;
  12. using UnityEngine;
  13. /// <summary>
  14. /// Implements Photon LoadBalancing used in PUN.
  15. /// This class is used internally by PhotonNetwork and not intended as public API.
  16. /// </summary>
  17. internal class NetworkingPeer : LoadbalancingPeer, IPhotonPeerListener
  18. {
  19. // game properties must be cached, because the game is created on the master and then "re-created" on the game server
  20. // both must use the same props for the game
  21. public string mAppVersion;
  22. private string mAppId;
  23. private string masterServerAddress;
  24. private string playername = "";
  25. private IPhotonPeerListener externalListener;
  26. private JoinType mLastJoinType;
  27. private bool mPlayernameHasToBeUpdated;
  28. public string PlayerName
  29. {
  30. get
  31. {
  32. return this.playername;
  33. }
  34. set
  35. {
  36. if (string.IsNullOrEmpty(value) || value.Equals(this.playername))
  37. {
  38. return;
  39. }
  40. if (this.mLocalActor != null)
  41. {
  42. this.mLocalActor.name = value;
  43. }
  44. this.playername = value;
  45. if (this.mCurrentGame != null)
  46. {
  47. // Only when in a room
  48. this.SendPlayerName();
  49. }
  50. }
  51. }
  52. public PeerState State { get; internal set; }
  53. // "public" access to the current game - is null unless a room is joined on a gameserver
  54. public Room mCurrentGame
  55. {
  56. get
  57. {
  58. if (this.mRoomToGetInto != null && this.mRoomToGetInto.isLocalClientInside)
  59. {
  60. return this.mRoomToGetInto;
  61. }
  62. return null;
  63. }
  64. }
  65. /// <summary>
  66. /// keeps the custom properties, gameServer address and anything else about the room we want to get into
  67. /// </summary>
  68. internal Room mRoomToGetInto { get; set; }
  69. public Dictionary<int, PhotonPlayer> mActors = new Dictionary<int, PhotonPlayer>();
  70. public PhotonPlayer[] mOtherPlayerListCopy = new PhotonPlayer[0];
  71. public PhotonPlayer[] mPlayerListCopy = new PhotonPlayer[0];
  72. public PhotonPlayer mLocalActor { get; internal set; }
  73. public PhotonPlayer mMasterClient = null;
  74. public string mGameserver { get; internal set; }
  75. public bool requestSecurity = true;
  76. private Dictionary<Type, List<MethodInfo>> monoRPCMethodsCache = new Dictionary<Type, List<MethodInfo>>();
  77. public static bool UsePrefabCache = true;
  78. public static Dictionary<string, GameObject> PrefabCache = new Dictionary<string, GameObject>();
  79. public Dictionary<string, RoomInfo> mGameList = new Dictionary<string, RoomInfo>();
  80. public RoomInfo[] mGameListCopy = new RoomInfo[0];
  81. public int mQueuePosition { get; internal set; }
  82. public bool insideLobby = false;
  83. /// <summary>Stat value: Count of players on Master (looking for rooms)</summary>
  84. public int mPlayersOnMasterCount { get; internal set; }
  85. /// <summary>Stat value: Count of Rooms</summary>
  86. public int mGameCount { get; internal set; }
  87. /// <summary>Stat value: Count of Players in rooms</summary>
  88. public int mPlayersInRoomsCount { get; internal set; }
  89. /// <summary>
  90. /// Instantiated objects by their instantiationId. The id (key) is per actor.
  91. /// </summary>
  92. public Dictionary<int, GameObject> instantiatedObjects = new Dictionary<int, GameObject>();
  93. private HashSet<int> allowedReceivingGroups = new HashSet<int>();
  94. private HashSet<int> blockSendingGroups = new HashSet<int>();
  95. internal protected Dictionary<int, PhotonView> photonViewList = new Dictionary<int, PhotonView>(); //TODO: make private again
  96. internal protected short currentLevelPrefix = 0;
  97. private readonly Dictionary<Type, Dictionary<PhotonNetworkingMessage, MethodInfo>> cachedMethods = new Dictionary<Type, Dictionary<PhotonNetworkingMessage, MethodInfo>>();
  98. private readonly Dictionary<string, int> rpcShortcuts; // lookup "table" for the index (shortcut) of an RPC name
  99. public NetworkingPeer(IPhotonPeerListener listener, string playername, ConnectionProtocol connectionProtocol) : base(listener, connectionProtocol)
  100. {
  101. this.Listener = this;
  102. // don't set the field directly! the listener is passed on to other classes, which get updated by the property set method
  103. this.externalListener = listener;
  104. this.PlayerName = playername;
  105. this.mLocalActor = new PhotonPlayer(true, -1, this.playername);
  106. this.AddNewPlayer(this.mLocalActor.ID, this.mLocalActor);
  107. // RPC shortcut lookup creation (from list of RPCs, which is updated by Editor scripts)
  108. rpcShortcuts = new Dictionary<string, int>(PhotonNetwork.PhotonServerSettings.RpcList.Count);
  109. for (int index = 0; index < PhotonNetwork.PhotonServerSettings.RpcList.Count; index++)
  110. {
  111. var name = PhotonNetwork.PhotonServerSettings.RpcList[index];
  112. rpcShortcuts[name] = index;
  113. }
  114. this.State = global::PeerState.PeerCreated;
  115. }
  116. #region Operations and Connection Methods
  117. public override bool Connect(string serverAddress, string appID)
  118. {
  119. if (PhotonNetwork.connectionStateDetailed == global::PeerState.Disconnecting)
  120. {
  121. Debug.LogError("ERROR: Cannot connect to Photon while Disconnecting. Connection failed.");
  122. return false;
  123. }
  124. if (string.IsNullOrEmpty(this.masterServerAddress))
  125. {
  126. this.masterServerAddress = serverAddress;
  127. }
  128. this.mAppId = appID.Trim();
  129. // connect might fail, if the DNS name can't be resolved or if no network connection is available
  130. bool connecting = base.Connect(serverAddress, "");
  131. this.State = connecting ? global::PeerState.Connecting : global::PeerState.Disconnected;
  132. return connecting;
  133. }
  134. /// <summary>
  135. /// Complete disconnect from photon (and the open master OR game server)
  136. /// </summary>
  137. public override void Disconnect()
  138. {
  139. if (this.PeerState == PeerStateValue.Disconnected)
  140. {
  141. if (this.DebugOut >= DebugLevel.WARNING)
  142. {
  143. this.DebugReturn(DebugLevel.WARNING, string.Format("Can't execute Disconnect() while not connected. Nothing changed. State: {0}", this.State));
  144. }
  145. return;
  146. }
  147. base.Disconnect();
  148. this.State = global::PeerState.Disconnecting;
  149. this.LeftRoomCleanup();
  150. this.LeftLobbyCleanup();
  151. }
  152. // just switches servers(Master->Game). don't remove the room, actors, etc
  153. private void DisconnectFromMaster()
  154. {
  155. base.Disconnect();
  156. this.State = global::PeerState.DisconnectingFromMasterserver;
  157. LeftLobbyCleanup();
  158. }
  159. // switches back from gameserver to master and removes the room, actors, etc
  160. private void DisconnectFromGameServer()
  161. {
  162. base.Disconnect();
  163. this.State = global::PeerState.DisconnectingFromGameserver;
  164. this.LeftRoomCleanup();
  165. }
  166. /// <summary>
  167. /// Called at disconnect/leavelobby etc. This CAN also be called when we are not in a lobby (e.g. disconnect from room)
  168. /// </summary>
  169. private void LeftLobbyCleanup()
  170. {
  171. if (!insideLobby)
  172. {
  173. return;
  174. }
  175. SendMonoMessage(PhotonNetworkingMessage.OnLeftLobby);
  176. this.insideLobby = false;
  177. }
  178. /// <summary>
  179. /// Called when "this client" left a room to clean up.
  180. /// </summary>
  181. private void LeftRoomCleanup()
  182. {
  183. bool wasInRoom = mRoomToGetInto != null;
  184. // when leaving a room, we clean up depending on that room's settings.
  185. bool autoCleanupSettingOfRoom = (this.mRoomToGetInto != null) ? this.mRoomToGetInto.autoCleanUp : PhotonNetwork.autoCleanUpPlayerObjects;
  186. this.mRoomToGetInto = null;
  187. this.mActors = new Dictionary<int, PhotonPlayer>();
  188. mPlayerListCopy = new PhotonPlayer[0];
  189. mOtherPlayerListCopy = new PhotonPlayer[0];
  190. this.mMasterClient = null;
  191. this.allowedReceivingGroups = new HashSet<int>();
  192. this.blockSendingGroups = new HashSet<int>();
  193. this.mGameList = new Dictionary<string, RoomInfo>();
  194. mGameListCopy = new RoomInfo[0];
  195. this.ChangeLocalID(-1);
  196. // Cleanup all network objects (all spawned PhotonViews, local and remote)
  197. if (autoCleanupSettingOfRoom)
  198. {
  199. // Fill list with Instantiated objects
  200. List<GameObject> goList = new List<GameObject>(this.instantiatedObjects.Values);
  201. // Fill list with other PhotonViews (contains doubles from Instantiated GO's)
  202. foreach (PhotonView view in this.photonViewList.Values)
  203. {
  204. if (view != null && !view.isSceneView && view.gameObject != null)
  205. {
  206. goList.Add(view.gameObject);
  207. }
  208. }
  209. // Destroy GO's
  210. for (int i = goList.Count - 1; i >= 0; i--)
  211. {
  212. GameObject go = goList[i];
  213. if (go != null)
  214. {
  215. if (this.DebugOut >= DebugLevel.ALL)
  216. {
  217. this.DebugReturn(DebugLevel.ALL, "Network destroy Instantiated GO: " + go.name);
  218. }
  219. this.DestroyGO(go);
  220. }
  221. }
  222. this.instantiatedObjects = new Dictionary<int, GameObject>();
  223. PhotonNetwork.manuallyAllocatedViewIds = new List<int>();
  224. PhotonNetwork.lastUsedViewSubId = 0;
  225. PhotonNetwork.lastUsedViewSubIdStatic = 0;
  226. }
  227. if (wasInRoom)
  228. {
  229. SendMonoMessage(PhotonNetworkingMessage.OnLeftRoom);
  230. }
  231. }
  232. /// <summary>
  233. /// 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.
  234. /// </summary>
  235. /// <param name="go">GameObject to destroy.</param>
  236. void DestroyGO(GameObject go)
  237. {
  238. PhotonView[] views = go.GetComponentsInChildren<PhotonView>();
  239. foreach (PhotonView view in views)
  240. {
  241. if (view != null)
  242. {
  243. view.destroyedByPhotonNetworkOrQuit = true;
  244. this.RemovePhotonView(view);
  245. }
  246. }
  247. GameObject.Destroy(go);
  248. }
  249. // gameID can be null (optional). The server assigns a unique name if no name is set
  250. // joins a room and sets your current username as custom actorproperty (will broadcast that)
  251. #endregion
  252. #region Helpers
  253. private void readoutStandardProperties(Hashtable gameProperties, Hashtable pActorProperties, int targetActorNr)
  254. {
  255. // Debug.LogWarning("readoutStandardProperties game=" + gameProperties + " actors(" + pActorProperties + ")=" + pActorProperties + " " + targetActorNr);
  256. // read game properties and cache them locally
  257. if (this.mCurrentGame != null && gameProperties != null)
  258. {
  259. this.mCurrentGame.CacheProperties(gameProperties);
  260. SendMonoMessage(PhotonNetworkingMessage.OnPhotonCustomRoomPropertiesChanged);
  261. if (PhotonNetwork.automaticallySyncScene)
  262. {
  263. this.AutomaticallySyncScene(); // will load new scene if sceneName was changed
  264. }
  265. }
  266. if (pActorProperties != null && pActorProperties.Count > 0)
  267. {
  268. if (targetActorNr > 0)
  269. {
  270. // we have a single entry in the pActorProperties with one
  271. // user's name
  272. // targets MUST exist before you set properties
  273. PhotonPlayer target = this.GetPlayerWithID(targetActorNr);
  274. if (target != null)
  275. {
  276. target.InternalCacheProperties(this.GetActorPropertiesForActorNr(pActorProperties, targetActorNr));
  277. SendMonoMessage(PhotonNetworkingMessage.OnPhotonPlayerPropertiesChanged, target);
  278. }
  279. }
  280. else
  281. {
  282. // in this case, we've got a key-value pair per actor (each
  283. // value is a hashtable with the actor's properties then)
  284. int actorNr;
  285. Hashtable props;
  286. string newName;
  287. PhotonPlayer target;
  288. foreach (object key in pActorProperties.Keys)
  289. {
  290. actorNr = (int)key;
  291. props = (Hashtable)pActorProperties[key];
  292. newName = (string)props[ActorProperties.PlayerName];
  293. target = this.GetPlayerWithID(actorNr);
  294. if (target == null)
  295. {
  296. target = new PhotonPlayer(false, actorNr, newName);
  297. this.AddNewPlayer(actorNr, target);
  298. }
  299. target.InternalCacheProperties(props);
  300. SendMonoMessage(PhotonNetworkingMessage.OnPhotonPlayerPropertiesChanged, target);
  301. }
  302. }
  303. }
  304. }
  305. private void AddNewPlayer(int ID, PhotonPlayer player)
  306. {
  307. if (!this.mActors.ContainsKey(ID))
  308. {
  309. this.mActors[ID] = player;
  310. RebuildPlayerListCopies();
  311. }
  312. else
  313. {
  314. Debug.LogError("Adding player twice: " + ID);
  315. }
  316. }
  317. void RemovePlayer(int ID, PhotonPlayer player)
  318. {
  319. this.mActors.Remove(ID);
  320. if (!player.isLocal)
  321. {
  322. RebuildPlayerListCopies();
  323. }
  324. }
  325. void RebuildPlayerListCopies()
  326. {
  327. this.mPlayerListCopy = new PhotonPlayer[this.mActors.Count];
  328. this.mActors.Values.CopyTo(this.mPlayerListCopy, 0);
  329. List<PhotonPlayer> otherP = new List<PhotonPlayer>();
  330. foreach (PhotonPlayer player in this.mPlayerListCopy)
  331. {
  332. if (!player.isLocal)
  333. {
  334. otherP.Add(player);
  335. }
  336. }
  337. this.mOtherPlayerListCopy = otherP.ToArray();
  338. }
  339. /// <summary>
  340. /// 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!).
  341. /// Note that due to this reset, ALL other players will receive the full OnSerialize.
  342. /// </summary>
  343. private void ResetPhotonViewsOnSerialize()
  344. {
  345. foreach (PhotonView photonView in this.photonViewList.Values)
  346. {
  347. photonView.lastOnSerializeDataSent = null;
  348. }
  349. }
  350. /// <summary>
  351. /// Called when the event Leave (of some other player) arrived.
  352. /// Cleans game objects, views locally. The master will also clean the
  353. /// </summary>
  354. /// <param name="actorID">ID of player who left.</param>
  355. private void HandleEventLeave(int actorID)
  356. {
  357. if (this.DebugOut >= DebugLevel.INFO)
  358. {
  359. this.DebugReturn(DebugLevel.INFO, "HandleEventLeave actorNr: " + actorID);
  360. }
  361. // actorNr is fetched out of event above
  362. if (actorID < 0 || !this.mActors.ContainsKey(actorID))
  363. {
  364. if (this.DebugOut >= DebugLevel.ERROR)
  365. {
  366. this.DebugReturn(DebugLevel.ERROR, string.Format("Received event Leave for unknown actorNumber: {0}", actorID));
  367. }
  368. return;
  369. }
  370. PhotonPlayer player = this.GetPlayerWithID(actorID);
  371. if (player == null)
  372. {
  373. Debug.LogError("Error: HandleEventLeave for actorID=" + actorID + " has no PhotonPlayer!");
  374. }
  375. // 1: Elect new masterclient, ignore the leaving player (as it's still in playerlists)
  376. if (this.mMasterClient != null && this.mMasterClient.ID == actorID)
  377. {
  378. this.mMasterClient = null;
  379. }
  380. this.CheckMasterClient(actorID);
  381. // 2: Destroy objects & buffered messages
  382. if (this.mCurrentGame != null && this.mCurrentGame.autoCleanUp)
  383. {
  384. this.DestroyPlayerObjects(player, true);
  385. }
  386. RemovePlayer(actorID, player);
  387. // 4: Finally, send notification (the playerList and masterclient are now updated)
  388. SendMonoMessage(PhotonNetworkingMessage.OnPhotonPlayerDisconnected, player);
  389. }
  390. /// <summary>
  391. /// Chooses the new master client. Supply ignoreActorID to ignore a specific actor (e.g. when this actor has just left)
  392. /// </summary>
  393. /// <param name="ignoreActorID"></param>
  394. private void CheckMasterClient(int ignoreActorID)
  395. {
  396. int lowestActorNumber = int.MaxValue;
  397. if (this.mMasterClient != null && this.mActors.ContainsKey(this.mMasterClient.ID))
  398. {
  399. // the current masterClient is still in the list of players, so it can't change
  400. return;
  401. }
  402. // the master is unknown. find lowest actornumber == master
  403. foreach (int actorNumber in this.mActors.Keys)
  404. {
  405. if (ignoreActorID != -1 && ignoreActorID == actorNumber)
  406. {
  407. continue; //Skip this actor as it's leaving.
  408. }
  409. if (actorNumber < lowestActorNumber)
  410. {
  411. lowestActorNumber = actorNumber;
  412. }
  413. }
  414. if (this.mMasterClient == null || this.mMasterClient.ID != lowestActorNumber)
  415. {
  416. this.mMasterClient = this.mActors[lowestActorNumber];
  417. bool leavingPlayerWasMaster = ignoreActorID > 0; // that value is the playerID who's leaving or -1
  418. if (leavingPlayerWasMaster)
  419. {
  420. SendMonoMessage(PhotonNetworkingMessage.OnMasterClientSwitched, this.mMasterClient);
  421. }
  422. }
  423. }
  424. private Hashtable GetActorPropertiesForActorNr(Hashtable actorProperties, int actorNr)
  425. {
  426. if (actorProperties.ContainsKey(actorNr))
  427. {
  428. return (Hashtable)actorProperties[actorNr];
  429. }
  430. return actorProperties;
  431. }
  432. private PhotonPlayer GetPlayerWithID(int number)
  433. {
  434. if (this.mActors != null && this.mActors.ContainsKey(number))
  435. {
  436. return this.mActors[number];
  437. }
  438. return null;
  439. }
  440. private void SendPlayerName()
  441. {
  442. if (this.State == global::PeerState.Joining)
  443. {
  444. // this means, the join on the gameServer is sent (with an outdated name). send the new when in game
  445. this.mPlayernameHasToBeUpdated = true;
  446. return;
  447. }
  448. if (this.mLocalActor != null)
  449. {
  450. this.mLocalActor.name = this.PlayerName;
  451. Hashtable properties = new Hashtable();
  452. properties[ActorProperties.PlayerName] = this.PlayerName;
  453. this.OpSetPropertiesOfActor(this.mLocalActor.ID, properties, true, (byte)0);
  454. this.mPlayernameHasToBeUpdated = false;
  455. }
  456. }
  457. private void GameEnteredOnGameServer(OperationResponse operationResponse)
  458. {
  459. if (operationResponse.ReturnCode != 0)
  460. {
  461. switch (operationResponse.OperationCode)
  462. {
  463. case OperationCode.CreateGame:
  464. this.DebugReturn(DebugLevel.ERROR, "Create failed on GameServer. Changing back to MasterServer. Msg: " + operationResponse.DebugMessage);
  465. SendMonoMessage(PhotonNetworkingMessage.OnPhotonCreateRoomFailed);
  466. break;
  467. case OperationCode.JoinGame:
  468. this.DebugReturn(DebugLevel.WARNING, "Join failed on GameServer. Changing back to MasterServer. Msg: " + operationResponse.DebugMessage);
  469. if (operationResponse.ReturnCode == ErrorCode.GameDoesNotExist)
  470. {
  471. Debug.Log("Most likely the game became empty during the switch to GameServer.");
  472. }
  473. SendMonoMessage(PhotonNetworkingMessage.OnPhotonJoinRoomFailed);
  474. break;
  475. case OperationCode.JoinRandomGame:
  476. this.DebugReturn(DebugLevel.WARNING, "Join failed on GameServer. Changing back to MasterServer. Msg: " + operationResponse.DebugMessage);
  477. if (operationResponse.ReturnCode == ErrorCode.GameDoesNotExist)
  478. {
  479. Debug.Log("Most likely the game became empty during the switch to GameServer.");
  480. }
  481. SendMonoMessage(PhotonNetworkingMessage.OnPhotonRandomJoinFailed);
  482. break;
  483. }
  484. this.DisconnectFromGameServer();
  485. return;
  486. }
  487. this.State = global::PeerState.Joined;
  488. this.mRoomToGetInto.isLocalClientInside = true;
  489. Hashtable actorProperties = (Hashtable)operationResponse[ParameterCode.PlayerProperties];
  490. Hashtable gameProperties = (Hashtable)operationResponse[ParameterCode.GameProperties];
  491. this.readoutStandardProperties(gameProperties, actorProperties, 0);
  492. // the local player's actor-properties are not returned in join-result. add this player to the list
  493. int localActorNr = (int)operationResponse[ParameterCode.ActorNr];
  494. this.ChangeLocalID(localActorNr);
  495. this.CheckMasterClient(-1);
  496. if (this.mPlayernameHasToBeUpdated)
  497. {
  498. this.SendPlayerName();
  499. }
  500. switch (operationResponse.OperationCode)
  501. {
  502. case OperationCode.CreateGame:
  503. SendMonoMessage(PhotonNetworkingMessage.OnCreatedRoom);
  504. break;
  505. case OperationCode.JoinGame:
  506. case OperationCode.JoinRandomGame:
  507. // the mono message for this is sent at another place
  508. break;
  509. }
  510. }
  511. private Hashtable GetLocalActorProperties()
  512. {
  513. if (PhotonNetwork.player != null)
  514. {
  515. return PhotonNetwork.player.allProperties;
  516. }
  517. Hashtable actorProperties = new Hashtable();
  518. actorProperties[ActorProperties.PlayerName] = this.PlayerName;
  519. return actorProperties;
  520. }
  521. public void ChangeLocalID(int newID)
  522. {
  523. if (this.mLocalActor == null)
  524. {
  525. Debug.LogWarning(
  526. string.Format(
  527. "Local actor is null or not in mActors! mLocalActor: {0} mActors==null: {1} newID: {2}",
  528. this.mLocalActor,
  529. this.mActors == null,
  530. newID));
  531. }
  532. if (this.mActors.ContainsKey(this.mLocalActor.ID))
  533. {
  534. this.mActors.Remove(this.mLocalActor.ID);
  535. }
  536. this.mLocalActor.InternalChangeLocalID(newID);
  537. this.mActors[this.mLocalActor.ID] = this.mLocalActor;
  538. this.RebuildPlayerListCopies();
  539. }
  540. #endregion
  541. #region Operations
  542. public bool OpCreateGame(string gameID, bool isVisible, bool isOpen, byte maxPlayers, bool autoCleanUp, Hashtable customGameProperties, string[] propsListedInLobby)
  543. {
  544. this.mRoomToGetInto = new Room(gameID, customGameProperties, isVisible, isOpen, maxPlayers, autoCleanUp, propsListedInLobby);
  545. return base.OpCreateRoom(gameID, isVisible, isOpen, maxPlayers, autoCleanUp, customGameProperties, this.GetLocalActorProperties(), propsListedInLobby);
  546. }
  547. public bool OpJoin(string gameID)
  548. {
  549. this.mRoomToGetInto = new Room(gameID, null);
  550. return this.OpJoinRoom(gameID, this.GetLocalActorProperties());
  551. }
  552. // 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)
  553. public override bool OpJoinRandomRoom(Hashtable expectedCustomRoomProperties, byte expectedMaxPlayers, Hashtable playerProperties, MatchmakingMode matchingType)
  554. {
  555. this.mRoomToGetInto = new Room(null, null);
  556. return base.OpJoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers, playerProperties, matchingType);
  557. }
  558. /// <summary>
  559. /// Operation Leave will exit any current room.
  560. /// </summary>
  561. /// <remarks>
  562. /// This also happens when you disconnect from the server.
  563. /// Disconnect might be a step less if you don't want to create a new room on the same server.
  564. /// </remarks>
  565. /// <returns></returns>
  566. public virtual bool OpLeave()
  567. {
  568. if (this.State != global::PeerState.Joined)
  569. {
  570. this.DebugReturn(DebugLevel.ERROR, "NetworkingPeer::leaveGame() - ERROR: no game is currently joined");
  571. return false;
  572. }
  573. return this.OpCustom((byte)OperationCode.Leave, null, true, 0);
  574. }
  575. public override bool OpRaiseEvent(byte eventCode, byte interestGroup, Hashtable evData, bool sendReliable, byte channelId)
  576. {
  577. if (PhotonNetwork.offlineMode)
  578. {
  579. return false;
  580. }
  581. return base.OpRaiseEvent(eventCode, interestGroup, evData, sendReliable, channelId);
  582. }
  583. public override bool OpRaiseEvent(byte eventCode, Hashtable evData, bool sendReliable, byte channelId, int[] targetActors, EventCaching cache)
  584. {
  585. if (PhotonNetwork.offlineMode)
  586. {
  587. return false;
  588. }
  589. return base.OpRaiseEvent(eventCode, evData, sendReliable, channelId, targetActors, cache);
  590. }
  591. public override bool OpRaiseEvent(byte eventCode, Hashtable evData, bool sendReliable, byte channelId, EventCaching cache, ReceiverGroup receivers)
  592. {
  593. if (PhotonNetwork.offlineMode)
  594. {
  595. return false;
  596. }
  597. return base.OpRaiseEvent(eventCode, evData, sendReliable, channelId, cache, receivers);
  598. }
  599. #endregion
  600. #region Implementation of IPhotonPeerListener
  601. public void DebugReturn(DebugLevel level, string message)
  602. {
  603. this.externalListener.DebugReturn(level, message);
  604. }
  605. public void OnOperationResponse(OperationResponse operationResponse)
  606. {
  607. if (PhotonNetwork.networkingPeer.State == global::PeerState.Disconnecting)
  608. {
  609. if (this.DebugOut >= DebugLevel.INFO)
  610. {
  611. this.DebugReturn(DebugLevel.INFO, "OperationResponse ignored while disconnecting: " + operationResponse.OperationCode);
  612. }
  613. return;
  614. }
  615. // extra logging for error debugging (helping developers with a bit of automated analysis)
  616. if (operationResponse.ReturnCode == 0)
  617. {
  618. if (this.DebugOut >= DebugLevel.INFO)
  619. {
  620. this.DebugReturn(DebugLevel.INFO, operationResponse.ToString());
  621. }
  622. }
  623. else
  624. {
  625. if (this.DebugOut >= DebugLevel.WARNING)
  626. {
  627. if (operationResponse.ReturnCode == ErrorCode.OperationNotAllowedInCurrentState)
  628. {
  629. 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.");
  630. }
  631. this.DebugReturn(DebugLevel.WARNING, operationResponse.ToStringFull());
  632. }
  633. }
  634. switch (operationResponse.OperationCode)
  635. {
  636. case OperationCode.Authenticate:
  637. {
  638. // PeerState oldState = this.State;
  639. if (operationResponse.ReturnCode != 0)
  640. {
  641. if (this.DebugOut >= DebugLevel.ERROR)
  642. {
  643. this.DebugReturn(DebugLevel.ERROR, string.Format("Authentication failed: '{0}' Code: {1}", operationResponse.DebugMessage, operationResponse.ReturnCode));
  644. }
  645. if (operationResponse.ReturnCode == ErrorCode.InvalidOperationCode)
  646. {
  647. this.DebugReturn(DebugLevel.ERROR, string.Format("If you host Photon yourself, make sure to start the 'Instance LoadBalancing'"));
  648. }
  649. if (operationResponse.ReturnCode == ErrorCode.InvalidAuthentication)
  650. {
  651. 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."));
  652. }
  653. this.Disconnect();
  654. this.State = global::PeerState.Disconnecting;
  655. if (operationResponse.ReturnCode == ErrorCode.MaxCcuReached)
  656. {
  657. this.DebugReturn(DebugLevel.ERROR, string.Format("Currently, the limit of users is reached for this title. Try again later. Disconnecting"));
  658. SendMonoMessage(PhotonNetworkingMessage.OnPhotonMaxCccuReached);
  659. SendMonoMessage(PhotonNetworkingMessage.OnConnectionFail, DisconnectCause.MaxCcuReached);
  660. }
  661. else if (operationResponse.ReturnCode == ErrorCode.InvalidRegion)
  662. {
  663. 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"));
  664. SendMonoMessage(PhotonNetworkingMessage.OnConnectionFail, DisconnectCause.InvalidRegion);
  665. }
  666. break;
  667. }
  668. else
  669. {
  670. if (this.State == global::PeerState.Connected || this.State == global::PeerState.ConnectedComingFromGameserver)
  671. {
  672. if (operationResponse.Parameters.ContainsKey(ParameterCode.Position))
  673. {
  674. this.mQueuePosition = (int)operationResponse[ParameterCode.Position];
  675. // returnValues for Authenticate always include this value!
  676. if (this.mQueuePosition > 0)
  677. {
  678. // should only happen, if just out of nowhere the
  679. // amount of players going online at the same time
  680. // is increasing faster, than automatically started
  681. // additional gameservers could have been booten up
  682. if (this.State == global::PeerState.ConnectedComingFromGameserver)
  683. {
  684. this.State = global::PeerState.QueuedComingFromGameserver;
  685. }
  686. else
  687. {
  688. this.State = global::PeerState.Queued;
  689. }
  690. // we break here (not joining the lobby, etc) as this client is queued
  691. // the EventCode.QueueState will eventually resolve this state
  692. break;
  693. }
  694. }
  695. if (PhotonNetwork.autoJoinLobby)
  696. {
  697. this.OpJoinLobby();
  698. this.State = global::PeerState.Authenticated;
  699. }
  700. else
  701. {
  702. this.State = global::PeerState.ConnectedToMaster;
  703. NetworkingPeer.SendMonoMessage(PhotonNetworkingMessage.OnConnectedToMaster);
  704. }
  705. }
  706. else if (this.State == global::PeerState.ConnectedToGameserver)
  707. {
  708. this.State = global::PeerState.Joining;
  709. if (this.mLastJoinType == JoinType.JoinGame || this.mLastJoinType == JoinType.JoinRandomGame)
  710. {
  711. // if we just "join" the game, do so
  712. this.OpJoin(this.mRoomToGetInto.name);
  713. }
  714. else if (this.mLastJoinType == JoinType.CreateGame)
  715. {
  716. // on the game server, we have to apply the room properties that were chosen for creation of the room, so we use this.mRoomToGetInto
  717. this.OpCreateGame(
  718. this.mRoomToGetInto.name,
  719. this.mRoomToGetInto.visible,
  720. this.mRoomToGetInto.open,
  721. (byte)this.mRoomToGetInto.maxPlayers,
  722. this.mRoomToGetInto.autoCleanUp,
  723. this.mRoomToGetInto.customProperties,
  724. this.mRoomToGetInto.propertiesListedInLobby);
  725. }
  726. break;
  727. }
  728. }
  729. break;
  730. }
  731. case OperationCode.CreateGame:
  732. {
  733. if (this.State != global::PeerState.Joining)
  734. {
  735. if (operationResponse.ReturnCode != 0)
  736. {
  737. if (this.DebugOut >= DebugLevel.ERROR)
  738. {
  739. this.DebugReturn(DebugLevel.ERROR, string.Format("createGame failed, client stays on masterserver: {0}.", operationResponse.ToStringFull()));
  740. }
  741. SendMonoMessage(PhotonNetworkingMessage.OnPhotonCreateRoomFailed);
  742. break;
  743. }
  744. string gameID = (string)operationResponse[ParameterCode.RoomName];
  745. if (!string.IsNullOrEmpty(gameID))
  746. {
  747. // is only sent by the server's response, if it has not been
  748. // sent with the client's request before!
  749. this.mRoomToGetInto.name = gameID;
  750. }
  751. this.mGameserver = (string)operationResponse[ParameterCode.Address];
  752. this.DisconnectFromMaster();
  753. this.mLastJoinType = JoinType.CreateGame;
  754. }
  755. else
  756. {
  757. this.GameEnteredOnGameServer(operationResponse);
  758. }
  759. break;
  760. }
  761. case OperationCode.JoinGame:
  762. {
  763. if (this.State != global::PeerState.Joining)
  764. {
  765. if (operationResponse.ReturnCode != 0)
  766. {
  767. SendMonoMessage(PhotonNetworkingMessage.OnPhotonJoinRoomFailed);
  768. if (this.DebugOut >= DebugLevel.WARNING)
  769. {
  770. this.DebugReturn(DebugLevel.WARNING, string.Format("JoinRoom failed (room maybe closed by now). Client stays on masterserver: {0}. State: {1}", operationResponse.ToStringFull(), this.State));
  771. }
  772. // this.mListener.joinGameReturn(0, null, null, returnCode, debugMsg);
  773. break;
  774. }
  775. this.mGameserver = (string)operationResponse[ParameterCode.Address];
  776. this.DisconnectFromMaster();
  777. this.mLastJoinType = JoinType.JoinGame;
  778. }
  779. else
  780. {
  781. this.GameEnteredOnGameServer(operationResponse);
  782. }
  783. break;
  784. }
  785. case OperationCode.JoinRandomGame:
  786. {
  787. // happens only on master. on gameserver, this is a regular join (we don't need to find a random game again)
  788. // the operation OpJoinRandom either fails (with returncode 8) or returns game-to-join information
  789. if (operationResponse.ReturnCode != 0)
  790. {
  791. if (operationResponse.ReturnCode == ErrorCode.NoRandomMatchFound)
  792. {
  793. this.DebugReturn(DebugLevel.WARNING, "JoinRandom failed: No open game. Client stays in lobby.");
  794. }
  795. else if (this.DebugOut >= DebugLevel.ERROR)
  796. {
  797. this.DebugReturn(DebugLevel.ERROR, string.Format("JoinRandom failed: {0}.", operationResponse.ToStringFull()));
  798. }
  799. SendMonoMessage(PhotonNetworkingMessage.OnPhotonRandomJoinFailed);
  800. // this.mListener.createGameReturn(0, null, null, returnCode, debugMsg);
  801. break;
  802. }
  803. string gameID = (string)operationResponse[ParameterCode.RoomName];
  804. this.mRoomToGetInto.name = gameID;
  805. this.mGameserver = (string)operationResponse[ParameterCode.Address];
  806. this.DisconnectFromMaster();
  807. this.mLastJoinType = JoinType.JoinRandomGame;
  808. break;
  809. }
  810. case OperationCode.JoinLobby:
  811. this.State = global::PeerState.JoinedLobby;
  812. this.insideLobby = true;
  813. SendMonoMessage(PhotonNetworkingMessage.OnJoinedLobby);
  814. // this.mListener.joinLobbyReturn();
  815. break;
  816. case OperationCode.LeaveLobby:
  817. this.State = global::PeerState.Authenticated;
  818. this.LeftLobbyCleanup();
  819. break;
  820. case OperationCode.Leave:
  821. this.DisconnectFromGameServer();
  822. break;
  823. case OperationCode.SetProperties:
  824. // this.mListener.setPropertiesReturn(returnCode, debugMsg);
  825. break;
  826. case OperationCode.GetProperties:
  827. {
  828. Hashtable actorProperties = (Hashtable)operationResponse[ParameterCode.PlayerProperties];
  829. Hashtable gameProperties = (Hashtable)operationResponse[ParameterCode.GameProperties];
  830. this.readoutStandardProperties(gameProperties, actorProperties, 0);
  831. // RemoveByteTypedPropertyKeys(actorProperties, false);
  832. // RemoveByteTypedPropertyKeys(gameProperties, false);
  833. // this.mListener.getPropertiesReturn(gameProperties, actorProperties, returnCode, debugMsg);
  834. break;
  835. }
  836. case OperationCode.RaiseEvent:
  837. // this usually doesn't give us a result. only if the caching is affected the server will send one.
  838. break;
  839. default:
  840. if (this.DebugOut >= DebugLevel.ERROR)
  841. {
  842. this.DebugReturn(DebugLevel.ERROR, string.Format("operationResponse unhandled: {0}", operationResponse.ToString()));
  843. }
  844. break;
  845. }
  846. this.externalListener.OnOperationResponse(operationResponse);
  847. }
  848. public void OnStatusChanged(StatusCode statusCode)
  849. {
  850. if (this.DebugOut >= DebugLevel.INFO)
  851. {
  852. this.DebugReturn(DebugLevel.INFO, string.Format("OnStatusChanged: {0}", statusCode.ToString()));
  853. }
  854. switch (statusCode)
  855. {
  856. case StatusCode.Connect:
  857. if (this.State == global::PeerState.ConnectingToGameserver)
  858. {
  859. if (this.DebugOut >= DebugLevel.ALL)
  860. {
  861. this.DebugReturn(DebugLevel.ALL, "Connected to gameserver.");
  862. }
  863. this.State = global::PeerState.ConnectedToGameserver;
  864. }
  865. else
  866. {
  867. if (this.DebugOut >= DebugLevel.ALL)
  868. {
  869. this.DebugReturn(DebugLevel.ALL, "Connected to masterserver.");
  870. }
  871. if (this.State == global::PeerState.Connecting)
  872. {
  873. SendMonoMessage(PhotonNetworkingMessage.OnConnectedToPhoton);
  874. this.State = global::PeerState.Connected;
  875. }
  876. else
  877. {
  878. this.State = global::PeerState.ConnectedComingFromGameserver;
  879. }
  880. }
  881. if (this.requestSecurity)
  882. {
  883. this.EstablishEncryption();
  884. }
  885. else
  886. {
  887. if (!this.OpAuthenticate(this.mAppId, this.mAppVersion))
  888. {
  889. this.externalListener.DebugReturn(DebugLevel.ERROR, "Error Authenticating! Did not work.");
  890. }
  891. }
  892. break;
  893. case StatusCode.Disconnect:
  894. if (this.State == global::PeerState.DisconnectingFromMasterserver)
  895. {
  896. if (this.Connect(this.mGameserver, this.mAppId))
  897. {
  898. this.State = global::PeerState.ConnectingToGameserver;
  899. }
  900. }
  901. else if (this.State == global::PeerState.DisconnectingFromGameserver)
  902. {
  903. if (this.Connect(this.masterServerAddress, this.mAppId))
  904. {
  905. this.State = global::PeerState.ConnectingToMasterserver;
  906. }
  907. }
  908. else
  909. {
  910. this.LeftRoomCleanup();
  911. this.State = global::PeerState.PeerCreated;
  912. SendMonoMessage(PhotonNetworkingMessage.OnDisconnectedFromPhoton);
  913. }
  914. break;
  915. case StatusCode.SecurityExceptionOnConnect:
  916. case StatusCode.ExceptionOnConnect:
  917. this.State = global::PeerState.PeerCreated;
  918. DisconnectCause cause = (DisconnectCause)statusCode;
  919. SendMonoMessage(PhotonNetworkingMessage.OnFailedToConnectToPhoton, cause);
  920. break;
  921. case StatusCode.Exception:
  922. if (this.State == global::PeerState.Connecting)
  923. {
  924. this.DebugReturn(DebugLevel.WARNING, "Exception while connecting to: " + this.ServerAddress + ". Check if the server is available.");
  925. if (this.ServerAddress == null || this.ServerAddress.StartsWith("127.0.0.1"))
  926. {
  927. 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.");
  928. if (this.ServerAddress == this.mGameserver)
  929. {
  930. this.DebugReturn(DebugLevel.WARNING, "This might be a misconfiguration in the game server config. You need to edit it to a (public) address.");
  931. }
  932. }
  933. this.State = global::PeerState.PeerCreated;
  934. cause = (DisconnectCause)statusCode;
  935. SendMonoMessage(PhotonNetworkingMessage.OnFailedToConnectToPhoton, cause);
  936. }
  937. else
  938. {
  939. this.State = global::PeerState.PeerCreated;
  940. cause = (DisconnectCause)statusCode;
  941. SendMonoMessage(PhotonNetworkingMessage.OnConnectionFail, cause);
  942. }
  943. this.Disconnect();
  944. break;
  945. case StatusCode.TimeoutDisconnect:
  946. case StatusCode.InternalReceiveException:
  947. case StatusCode.DisconnectByServer:
  948. case StatusCode.DisconnectByServerLogic:
  949. case StatusCode.DisconnectByServerUserLimit:
  950. if (this.State == global::PeerState.Connecting)
  951. {
  952. this.DebugReturn(DebugLevel.WARNING, statusCode + " while connecting to: " + this.ServerAddress + ". Check if the server is available.");
  953. this.State = global::PeerState.PeerCreated;
  954. cause = (DisconnectCause)statusCode;
  955. SendMonoMessage(PhotonNetworkingMessage.OnFailedToConnectToPhoton, cause);
  956. }
  957. else
  958. {
  959. this.State = global::PeerState.PeerCreated;
  960. cause = (DisconnectCause)statusCode;
  961. SendMonoMessage(PhotonNetworkingMessage.OnConnectionFail, cause);
  962. }
  963. this.Disconnect();
  964. break;
  965. case StatusCode.SendError:
  966. // this.mListener.clientErrorReturn(statusCode);
  967. break;
  968. case StatusCode.QueueOutgoingReliableWarning:
  969. case StatusCode.QueueOutgoingUnreliableWarning:
  970. case StatusCode.QueueOutgoingAcksWarning:
  971. case StatusCode.QueueSentWarning:
  972. // this.mListener.warningReturn(statusCode);
  973. break;
  974. case StatusCode.EncryptionEstablished:
  975. if (!this.OpAuthenticate(this.mAppId, this.mAppVersion))
  976. {
  977. this.externalListener.DebugReturn(DebugLevel.ERROR, "Error Authenticating! Did not work.");
  978. }
  979. break;
  980. case StatusCode.EncryptionFailedToEstablish:
  981. this.externalListener.DebugReturn(DebugLevel.ERROR, "Encryption wasn't established: " + statusCode + ". Going to authenticate anyways.");
  982. if (!this.OpAuthenticate(this.mAppId, this.mAppVersion))
  983. {
  984. this.externalListener.DebugReturn(DebugLevel.ERROR, "Error Authenticating! Did not work.");
  985. }
  986. break;
  987. // // TCP "routing" is an option of Photon that's not currently needed (or supported) by PUN
  988. //case StatusCode.TcpRouterResponseOk:
  989. // break;
  990. //case StatusCode.TcpRouterResponseEndpointUnknown:
  991. //case StatusCode.TcpRouterResponseNodeIdUnknown:
  992. //case StatusCode.TcpRouterResponseNodeNotReady:
  993. // this.DebugReturn(DebugLevel.ERROR, "Unexpected router response: " + statusCode);
  994. // break;
  995. default:
  996. // this.mListener.serverErrorReturn(statusCode.value());
  997. this.DebugReturn(DebugLevel.ERROR, "Received unknown status code: " + statusCode);
  998. break;
  999. }
  1000. this.externalListener.OnStatusChanged(statusCode);
  1001. }
  1002. public void OnEvent(EventData photonEvent)
  1003. {
  1004. if (this.DebugOut >= DebugLevel.INFO)
  1005. {
  1006. this.DebugReturn(DebugLevel.INFO, string.Format("OnEvent: {0}", photonEvent.ToString()));
  1007. }
  1008. int actorNr = -1;
  1009. PhotonPlayer originatingPlayer = null;
  1010. if (photonEvent.Parameters.ContainsKey(ParameterCode.ActorNr))
  1011. {
  1012. actorNr = (int)photonEvent[ParameterCode.ActorNr];
  1013. if (this.mActors.ContainsKey(actorNr))
  1014. {
  1015. originatingPlayer = (PhotonPlayer)this.mActors[actorNr];
  1016. }
  1017. //else
  1018. //{
  1019. // // the actor sending this event is not in actorlist. this is usually no problem
  1020. // if (photonEvent.Code != (byte)LiteOpCode.Join)
  1021. // {
  1022. // Debug.LogWarning("Received event, but we do not have this actor: " + actorNr);
  1023. // }
  1024. //}
  1025. }
  1026. switch (photonEvent.Code)
  1027. {
  1028. case EventCode.GameList:
  1029. {
  1030. this.mGameList = new Dictionary<string, RoomInfo>();
  1031. Hashtable games = (Hashtable)photonEvent[ParameterCode.GameList];
  1032. foreach (DictionaryEntry game in games)
  1033. {
  1034. string gameName = (string)game.Key;
  1035. this.mGameList[gameName] = new RoomInfo(gameName, (Hashtable)game.Value);
  1036. }
  1037. mGameListCopy = new RoomInfo[mGameList.Count];
  1038. mGameList.Values.CopyTo(mGameListCopy, 0);
  1039. SendMonoMessage(PhotonNetworkingMessage.OnReceivedRoomListUpdate);
  1040. break;
  1041. }
  1042. case EventCode.GameListUpdate:
  1043. {
  1044. Hashtable games = (Hashtable)photonEvent[ParameterCode.GameList];
  1045. foreach (DictionaryEntry room in games)
  1046. {
  1047. string gameName = (string)room.Key;
  1048. Room game = new Room(gameName, (Hashtable)room.Value);
  1049. if (game.removedFromList)
  1050. {
  1051. this.mGameList.Remo

Large files files are truncated, but you can click here to view the full file