PhotonView.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. // ----------------------------------------------------------------------------
  2. // <copyright file="PhotonView.cs" company="Exit Games GmbH">
  3. // PhotonNetwork Framework for Unity - Copyright (C) 2018 Exit Games GmbH
  4. // </copyright>
  5. // <summary>
  6. // Contains the PhotonView class.
  7. // </summary>
  8. // <author>developer@exitgames.com</author>
  9. // ----------------------------------------------------------------------------
  10. namespace Photon.Pun
  11. {
  12. using System;
  13. using UnityEngine;
  14. using UnityEngine.Serialization;
  15. using System.Collections.Generic;
  16. using Photon.Realtime;
  17. #if UNITY_EDITOR
  18. using UnityEditor;
  19. #endif
  20. /// <summary>
  21. /// A PhotonView identifies an object across the network (viewID) and configures how the controlling client updates remote instances.
  22. /// </summary>
  23. /// \ingroup publicApi
  24. [AddComponentMenu("Photon Networking/Photon View")]
  25. public class PhotonView : MonoBehaviour
  26. {
  27. #if UNITY_EDITOR
  28. [UnityEditor.InitializeOnLoadMethod]
  29. private static void SetPhotonViewExecutionOrder()
  30. {
  31. int photonViewExecutionOrder = -16000;
  32. GameObject go = new GameObject();
  33. PhotonView pv = go.AddComponent<PhotonView>();
  34. MonoScript monoScript = MonoScript.FromMonoBehaviour(pv);
  35. if (photonViewExecutionOrder != MonoImporter.GetExecutionOrder(monoScript))
  36. {
  37. MonoImporter.SetExecutionOrder(monoScript, photonViewExecutionOrder); // very early but allows other scripts to run even earlier...
  38. }
  39. DestroyImmediate(go);
  40. }
  41. #endif
  42. #if UNITY_EDITOR
  43. [ContextMenu("Open PUN Wizard")]
  44. void OpenPunWizard()
  45. {
  46. EditorApplication.ExecuteMenuItem("Window/Photon Unity Networking/PUN Wizard");
  47. }
  48. #endif
  49. #if UNITY_EDITOR
  50. // Suppressing compiler warning "this variable is never used". Only used in the CustomEditor, only in Editor
  51. #pragma warning disable 0414
  52. [SerializeField]
  53. bool ObservedComponentsFoldoutOpen = true;
  54. #pragma warning restore 0414
  55. #endif
  56. #if UNITY_EDITOR
  57. /// called by Editor to reset the component
  58. private void Reset()
  59. {
  60. observableSearch = ObservableSearch.AutoFindAll;
  61. }
  62. #endif
  63. [FormerlySerializedAs("group")]
  64. public byte Group = 0;
  65. // NOTE: this is now an integer because unity won't serialize short (needed for instantiation). we SEND only a short though!
  66. // NOTE: prefabs have a prefixField of -1. this is replaced with any currentLevelPrefix that's used at runtime. instantiated GOs get their prefix set pre-instantiation (so those are not -1 anymore)
  67. public int Prefix
  68. {
  69. get
  70. {
  71. if (this.prefixField == -1 && PhotonNetwork.NetworkingClient != null)
  72. {
  73. this.prefixField = PhotonNetwork.currentLevelPrefix;
  74. }
  75. return this.prefixField;
  76. }
  77. set { this.prefixField = value; }
  78. }
  79. // this field is serialized by unity. that means it is copied when instantiating a persistent obj into the scene
  80. [FormerlySerializedAs("prefixBackup")]
  81. public int prefixField = -1;
  82. /// <summary>
  83. /// This is the InstantiationData that was passed when calling PhotonNetwork.Instantiate* (if that was used to spawn this prefab)
  84. /// </summary>
  85. public object[] InstantiationData
  86. {
  87. get { return this.instantiationDataField; }
  88. protected internal set { this.instantiationDataField = value; }
  89. }
  90. internal object[] instantiationDataField;
  91. /// <summary>
  92. /// For internal use only, don't use
  93. /// </summary>
  94. protected internal List<object> lastOnSerializeDataSent = null;
  95. protected internal List<object> syncValues;
  96. /// <summary>
  97. /// For internal use only, don't use
  98. /// </summary>
  99. protected internal object[] lastOnSerializeDataReceived = null;
  100. [FormerlySerializedAs("synchronization")]
  101. public ViewSynchronization Synchronization = ViewSynchronization.UnreliableOnChange;
  102. protected internal bool mixedModeIsReliable = false;
  103. /// <summary>Defines if ownership of this PhotonView is fixed, can be requested or simply taken.</summary>
  104. /// <remarks>
  105. /// Note that you can't edit this value at runtime.
  106. /// The options are described in enum OwnershipOption.
  107. /// The current owner has to implement IPunCallbacks.OnOwnershipRequest to react to the ownership request.
  108. /// </remarks>
  109. [FormerlySerializedAs("ownershipTransfer")]
  110. public OwnershipOption OwnershipTransfer = OwnershipOption.Fixed;
  111. public enum ObservableSearch { Manual, AutoFindActive, AutoFindAll }
  112. /// Default to manual so existing PVs in projects default to same as before. Reset() changes this to AutoAll for new implementations.
  113. public ObservableSearch observableSearch = ObservableSearch.Manual;
  114. public List<Component> ObservedComponents;
  115. internal MonoBehaviour[] RpcMonoBehaviours;
  116. [Obsolete("Renamed. Use IsRoomView instead")]
  117. public bool IsSceneView
  118. {
  119. get { return this.IsRoomView; }
  120. }
  121. /// <summary>True if the PhotonView was loaded with the scene (game object) or instantiated with InstantiateRoomObject.</summary>
  122. /// <remarks>
  123. /// Room objects are not owned by a particular player but belong to the scene. Thus they don't get destroyed when their
  124. /// creator leaves the game and the current Master Client can control them (whoever that is).
  125. /// The ownerId is 0 (player IDs are 1 and up).
  126. /// </remarks>
  127. public bool IsRoomView
  128. {
  129. get { return this.CreatorActorNr == 0; }
  130. }
  131. public bool IsOwnerActive
  132. {
  133. get { return this.Owner != null && !this.Owner.IsInactive; }
  134. }
  135. /// <summary>
  136. /// True if the PhotonView is "mine" and can be controlled by this client.
  137. /// </summary>
  138. /// <remarks>
  139. /// PUN has an ownership concept that defines who can control and destroy each PhotonView.
  140. /// True in case the controller matches the local Player.
  141. /// True if this is a scene photonview (null owner and ownerId == 0) on the Master client.
  142. /// </remarks>
  143. public bool IsMine { get; private set; }
  144. public bool AmController
  145. {
  146. get { return this.IsMine; }
  147. }
  148. public Player Controller { get; private set; }
  149. public int CreatorActorNr { get; private set; }
  150. public bool AmOwner { get; private set; }
  151. /// <summary>
  152. /// The owner of a PhotonView is the creator of an object by default Ownership can be transferred and the owner may not be in the room anymore. Objects in the scene don't have an owner.
  153. /// </summary>
  154. /// <remarks>
  155. /// The owner/controller of a PhotonView is also the client which sends position updates of the GameObject.
  156. ///
  157. /// Ownership can be transferred to another player with PhotonView.TransferOwnership or any player can request
  158. /// ownership by calling the PhotonView's RequestOwnership method.
  159. /// The current owner has to implement IPunCallbacks.OnOwnershipRequest to react to the ownership request.
  160. /// </remarks>
  161. public Player Owner { get; private set; }
  162. [NonSerialized]
  163. private int ownerActorNr;
  164. public int OwnerActorNr
  165. {
  166. get { return this.ownerActorNr; }
  167. set
  168. {
  169. if (value != 0 && this.ownerActorNr == value)
  170. {
  171. return;
  172. }
  173. Player prevOwner = this.Owner;
  174. this.Owner = PhotonNetwork.CurrentRoom == null ? null : PhotonNetwork.CurrentRoom.GetPlayer(value, true);
  175. this.ownerActorNr = this.Owner != null ? this.Owner.ActorNumber : value;
  176. this.AmOwner = PhotonNetwork.LocalPlayer != null && this.ownerActorNr == PhotonNetwork.LocalPlayer.ActorNumber;
  177. this.UpdateCallbackLists();
  178. if (!ReferenceEquals(this.OnOwnerChangeCallbacks, null))
  179. {
  180. for (int i = 0, cnt = this.OnOwnerChangeCallbacks.Count; i < cnt; ++i)
  181. {
  182. this.OnOwnerChangeCallbacks[i].OnOwnerChange(this.Owner, prevOwner);
  183. }
  184. }
  185. }
  186. }
  187. [NonSerialized]
  188. private int controllerActorNr;
  189. public int ControllerActorNr
  190. {
  191. get { return this.controllerActorNr; }
  192. set
  193. {
  194. Player prevController = this.Controller;
  195. this.Controller = PhotonNetwork.CurrentRoom == null ? null : PhotonNetwork.CurrentRoom.GetPlayer(value, true);
  196. if (this.Controller != null && this.Controller.IsInactive)
  197. {
  198. this.Controller = PhotonNetwork.MasterClient;
  199. }
  200. this.controllerActorNr = this.Controller != null ? this.Controller.ActorNumber : value;
  201. this.IsMine = PhotonNetwork.LocalPlayer != null && this.controllerActorNr == PhotonNetwork.LocalPlayer.ActorNumber;
  202. if (!ReferenceEquals(this.Controller, prevController))
  203. {
  204. this.UpdateCallbackLists();
  205. if (!ReferenceEquals(this.OnControllerChangeCallbacks, null))
  206. {
  207. for (int i = 0, cnt = this.OnControllerChangeCallbacks.Count; i < cnt; ++i)
  208. {
  209. this.OnControllerChangeCallbacks[i].OnControllerChange(this.Controller, prevController);
  210. }
  211. }
  212. }
  213. }
  214. }
  215. /// This field is the Scene ViewID (0 if not used). loaded with the scene, used in Awake().
  216. [SerializeField]
  217. [FormerlySerializedAs("viewIdField")]
  218. [HideInInspector]
  219. public int sceneViewId = 0; // TODO: in best case, this is not public
  220. /// This field is the "runtime" ViewID as backup for the property.
  221. [NonSerialized]
  222. private int viewIdField = 0;
  223. /// <summary>
  224. /// The ID of the PhotonView. Identifies it in a networked game (per room).
  225. /// </summary>
  226. /// <remarks>See: [Network Instantiation](@ref instantiateManual)</remarks>
  227. public int ViewID
  228. {
  229. get
  230. {
  231. return this.viewIdField;
  232. }
  233. set
  234. {
  235. // TODO: Check if the isPlaying check is needed when the PhotonViewHandler is updated
  236. if (value != 0 && this.viewIdField != 0)
  237. {
  238. Debug.LogWarning("Changing a ViewID while it's in use is not possible (except setting it to 0 (not being used). Current ViewID: " + this.viewIdField);
  239. return;
  240. }
  241. if (value == 0 && this.viewIdField != 0)
  242. {
  243. PhotonNetwork.LocalCleanPhotonView(this);
  244. }
  245. this.viewIdField = value;
  246. this.CreatorActorNr = value / PhotonNetwork.MAX_VIEW_IDS; // the creator can be derived from the viewId. this is also the initial owner and creator.
  247. this.OwnerActorNr = this.CreatorActorNr;
  248. this.ControllerActorNr = this.CreatorActorNr;
  249. this.RebuildControllerCache();
  250. // if the viewID is set to a new, legit value, the view should register in the list of active PVs.
  251. if (value != 0)
  252. {
  253. PhotonNetwork.RegisterPhotonView(this);
  254. }
  255. }
  256. }
  257. [FormerlySerializedAs("instantiationId")]
  258. public int InstantiationId; // if the view was instantiated with a GO, this GO has a instantiationID (first view's viewID)
  259. [SerializeField]
  260. [HideInInspector]
  261. public bool isRuntimeInstantiated;
  262. protected internal bool removedFromLocalViewList;
  263. /// <summary>Will FindObservables() and assign the sceneViewId, if that is != 0. This initializes the PhotonView if loaded with the scene. Called once by Unity, when this instance is created.</summary>
  264. protected internal void Awake()
  265. {
  266. if (this.ViewID != 0)
  267. {
  268. return;
  269. }
  270. if (this.sceneViewId != 0)
  271. {
  272. // PhotonNetwork.Instantiate will set a ViewID != 0 before the object awakes. So only objects loaded with the scene ever use the sceneViewId (even if the obj got pooled)
  273. this.ViewID = this.sceneViewId;
  274. }
  275. this.FindObservables();
  276. }
  277. /// called by PhotonNetwork.LocalCleanupAnythingInstantiated
  278. internal void ResetPhotonView(bool resetOwner)
  279. {
  280. //// If this was fired by this connection rejoining, reset the ownership cache to owner = creator.
  281. //// TODO: This reset may not be needed at all with the ownership being invalidated next.
  282. //if (resetOwner)
  283. // ResetOwnership();
  284. //this.ownershipCacheIsValid = OwnershipCacheState.Invalid;
  285. // Reset the delta check to force a complete update of owned objects, to ensure joining connections get full updates.
  286. this.lastOnSerializeDataSent = null;
  287. }
  288. /// called by OnJoinedRoom, OnMasterClientSwitched, OnPlayerEnteredRoom and OnEvent for OwnershipUpdate
  289. /// OnPlayerLeftRoom will set a new controller directly, if the controller or owner left
  290. internal void RebuildControllerCache(bool ownerHasChanged = false)
  291. {
  292. //var prevController = this.controller;
  293. // objects without controller and room objects (ownerId 0) check if controller update is needed
  294. if (this.controllerActorNr == 0 || this.OwnerActorNr == 0 || this.Owner == null || this.Owner.IsInactive)
  295. {
  296. var masterclient = PhotonNetwork.MasterClient;
  297. this.ControllerActorNr = masterclient == null ? -1 : masterclient.ActorNumber;
  298. }
  299. else
  300. {
  301. this.ControllerActorNr = this.OwnerActorNr;
  302. }
  303. }
  304. public void OnPreNetDestroy(PhotonView rootView)
  305. {
  306. UpdateCallbackLists();
  307. if (!ReferenceEquals(OnPreNetDestroyCallbacks, null))
  308. for (int i = 0, cnt = OnPreNetDestroyCallbacks.Count; i < cnt; ++i)
  309. {
  310. OnPreNetDestroyCallbacks[i].OnPreNetDestroy(rootView);
  311. }
  312. }
  313. protected internal void OnDestroy()
  314. {
  315. if (!this.removedFromLocalViewList)
  316. {
  317. bool wasInList = PhotonNetwork.LocalCleanPhotonView(this);
  318. if (wasInList && this.InstantiationId > 0 && !PhotonHandler.AppQuits && PhotonNetwork.LogLevel >= PunLogLevel.Informational)
  319. {
  320. Debug.Log("PUN-instantiated '" + this.gameObject.name + "' got destroyed by engine. This is OK when loading levels. Otherwise use: PhotonNetwork.Destroy().");
  321. }
  322. }
  323. }
  324. /// <summary>
  325. /// Depending on the PhotonView's OwnershipTransfer setting, any client can request to become owner of the PhotonView.
  326. /// </summary>
  327. /// <remarks>
  328. /// Requesting ownership can give you control over a PhotonView, if the OwnershipTransfer setting allows that.
  329. /// The current owner might have to implement IPunCallbacks.OnOwnershipRequest to react to the ownership request.
  330. ///
  331. /// The owner/controller of a PhotonView is also the client which sends position updates of the GameObject.
  332. /// </remarks>
  333. public void RequestOwnership()
  334. {
  335. if (OwnershipTransfer != OwnershipOption.Fixed)
  336. {
  337. PhotonNetwork.RequestOwnership(this.ViewID, this.ownerActorNr);
  338. }
  339. else
  340. {
  341. if (PhotonNetwork.LogLevel >= PunLogLevel.Informational)
  342. {
  343. Debug.LogWarning("Attempting to RequestOwnership of GameObject '" + name + "' viewId: " + ViewID +
  344. ", but PhotonView.OwnershipTransfer is set to Fixed.");
  345. }
  346. }
  347. }
  348. /// <summary>
  349. /// Transfers the ownership of this PhotonView (and GameObject) to another player.
  350. /// </summary>
  351. /// <remarks>
  352. /// The owner/controller of a PhotonView is also the client which sends position updates of the GameObject.
  353. /// </remarks>
  354. public void TransferOwnership(Player newOwner)
  355. {
  356. if (newOwner != null)
  357. TransferOwnership(newOwner.ActorNumber);
  358. else
  359. {
  360. if (PhotonNetwork.LogLevel >= PunLogLevel.Informational)
  361. {
  362. Debug.LogWarning("Attempting to TransferOwnership of GameObject '" + name + "' viewId: " + ViewID +
  363. ", but provided Player newOwner is null.");
  364. }
  365. }
  366. }
  367. /// <summary>
  368. /// Transfers the ownership of this PhotonView (and GameObject) to another player.
  369. /// </summary>
  370. /// <remarks>
  371. /// The owner/controller of a PhotonView is also the client which sends position updates of the GameObject.
  372. /// </remarks>
  373. public void TransferOwnership(int newOwnerId)
  374. {
  375. if (OwnershipTransfer == OwnershipOption.Takeover || (OwnershipTransfer == OwnershipOption.Request && this.AmController))
  376. {
  377. PhotonNetwork.TransferOwnership(this.ViewID, newOwnerId);
  378. }
  379. else
  380. {
  381. if (PhotonNetwork.LogLevel >= PunLogLevel.Informational)
  382. {
  383. if (OwnershipTransfer == OwnershipOption.Fixed)
  384. Debug.LogWarning("Attempting to TransferOwnership of GameObject '" + name + "' viewId: " + ViewID +
  385. " without the authority to do so. TransferOwnership is not allowed if PhotonView.OwnershipTransfer is set to Fixed.");
  386. else if (OwnershipTransfer == OwnershipOption.Request)
  387. Debug.LogWarning("Attempting to TransferOwnership of GameObject '" + name + "' viewId: " + ViewID +
  388. " without the authority to do so. PhotonView.OwnershipTransfer is set to Request, so only the controller of this object can TransferOwnership.");
  389. }
  390. }
  391. }
  392. /// <summary>
  393. /// Will find IPunObservable components on this GameObject and nested children and add them to the ObservedComponents list.
  394. /// </summary>
  395. /// <remarks>
  396. /// This is called via PhotonView.Awake(), which in turn is called immediately by the engine's AddComponent method.
  397. ///
  398. /// Changing the ObservedComponents of a PhotonView at runtime can be problematic, if other clients are not also
  399. /// updating their list.
  400. /// </remarks>
  401. /// <param name="force">If true, FindObservables will work as if observableSearch is AutoFindActive.</param>
  402. public void FindObservables(bool force = false)
  403. {
  404. if (!force && this.observableSearch == ObservableSearch.Manual)
  405. {
  406. return;
  407. }
  408. if (this.ObservedComponents == null)
  409. {
  410. this.ObservedComponents = new List<Component>();
  411. }
  412. else
  413. {
  414. this.ObservedComponents.Clear();
  415. }
  416. this.transform.GetNestedComponentsInChildren<Component, IPunObservable, PhotonView>(force || this.observableSearch == ObservableSearch.AutoFindAll, this.ObservedComponents);
  417. }
  418. public void SerializeView(PhotonStream stream, PhotonMessageInfo info)
  419. {
  420. if (this.ObservedComponents != null && this.ObservedComponents.Count > 0)
  421. {
  422. for (int i = 0; i < this.ObservedComponents.Count; ++i)
  423. {
  424. var component = this.ObservedComponents[i];
  425. if (component != null)
  426. SerializeComponent(this.ObservedComponents[i], stream, info);
  427. }
  428. }
  429. }
  430. public void DeserializeView(PhotonStream stream, PhotonMessageInfo info)
  431. {
  432. if (this.ObservedComponents != null && this.ObservedComponents.Count > 0)
  433. {
  434. for (int i = 0; i < this.ObservedComponents.Count; ++i)
  435. {
  436. var component = this.ObservedComponents[i];
  437. if (component != null)
  438. DeserializeComponent(component, stream, info);
  439. }
  440. }
  441. }
  442. protected internal void DeserializeComponent(Component component, PhotonStream stream, PhotonMessageInfo info)
  443. {
  444. IPunObservable observable = component as IPunObservable;
  445. if (observable != null)
  446. {
  447. observable.OnPhotonSerializeView(stream, info);
  448. }
  449. else
  450. {
  451. Debug.LogError("Observed scripts have to implement IPunObservable. " + component + " does not. It is Type: " + component.GetType(), component.gameObject);
  452. }
  453. }
  454. protected internal void SerializeComponent(Component component, PhotonStream stream, PhotonMessageInfo info)
  455. {
  456. IPunObservable observable = component as IPunObservable;
  457. if (observable != null)
  458. {
  459. observable.OnPhotonSerializeView(stream, info);
  460. }
  461. else
  462. {
  463. Debug.LogError("Observed scripts have to implement IPunObservable. " + component + " does not. It is Type: " + component.GetType(), component.gameObject);
  464. }
  465. }
  466. /// <summary>
  467. /// Can be used to refesh the list of MonoBehaviours on this GameObject while PhotonNetwork.UseRpcMonoBehaviourCache is true.
  468. /// </summary>
  469. /// <remarks>
  470. /// Set PhotonNetwork.UseRpcMonoBehaviourCache to true to enable the caching.
  471. /// Uses this.GetComponents<MonoBehaviour>() to get a list of MonoBehaviours to call RPCs on (potentially).
  472. ///
  473. /// While PhotonNetwork.UseRpcMonoBehaviourCache is false, this method has no effect,
  474. /// because the list is refreshed when a RPC gets called.
  475. /// </remarks>
  476. public void RefreshRpcMonoBehaviourCache()
  477. {
  478. this.RpcMonoBehaviours = this.GetComponents<MonoBehaviour>();
  479. }
  480. /// <summary>
  481. /// Call a RPC method of this GameObject on remote clients of this room (or on all, including this client).
  482. /// </summary>
  483. /// <remarks>
  484. /// [Remote Procedure Calls](@ref rpcManual) are an essential tool in making multiplayer games with PUN.
  485. /// It enables you to make every client in a room call a specific method.
  486. ///
  487. /// RPC calls can target "All" or the "Others".
  488. /// Usually, the target "All" gets executed locally immediately after sending the RPC.
  489. /// The "*ViaServer" options send the RPC to the server and execute it on this client when it's sent back.
  490. /// Of course, calls are affected by this client's lag and that of remote clients.
  491. ///
  492. /// Each call automatically is routed to the same PhotonView (and GameObject) that was used on the
  493. /// originating client.
  494. ///
  495. /// See: [Remote Procedure Calls](@ref rpcManual).
  496. /// </remarks>
  497. /// <param name="methodName">The name of a fitting method that was has the RPC attribute.</param>
  498. /// <param name="target">The group of targets and the way the RPC gets sent.</param>
  499. /// <param name="parameters">The parameters that the RPC method has (must fit this call!).</param>
  500. public void RPC(string methodName, RpcTarget target, params object[] parameters)
  501. {
  502. PhotonNetwork.RPC(this, methodName, target, false, parameters);
  503. }
  504. /// <summary>
  505. /// Call a RPC method of this GameObject on remote clients of this room (or on all, including this client).
  506. /// </summary>
  507. /// <remarks>
  508. /// [Remote Procedure Calls](@ref rpcManual) are an essential tool in making multiplayer games with PUN.
  509. /// It enables you to make every client in a room call a specific method.
  510. ///
  511. /// RPC calls can target "All" or the "Others".
  512. /// Usually, the target "All" gets executed locally immediately after sending the RPC.
  513. /// The "*ViaServer" options send the RPC to the server and execute it on this client when it's sent back.
  514. /// Of course, calls are affected by this client's lag and that of remote clients.
  515. ///
  516. /// Each call automatically is routed to the same PhotonView (and GameObject) that was used on the
  517. /// originating client.
  518. ///
  519. /// See: [Remote Procedure Calls](@ref rpcManual).
  520. /// </remarks>
  521. ///<param name="methodName">The name of a fitting method that was has the RPC attribute.</param>
  522. ///<param name="target">The group of targets and the way the RPC gets sent.</param>
  523. ///<param name="encrypt"> </param>
  524. ///<param name="parameters">The parameters that the RPC method has (must fit this call!).</param>
  525. public void RpcSecure(string methodName, RpcTarget target, bool encrypt, params object[] parameters)
  526. {
  527. PhotonNetwork.RPC(this, methodName, target, encrypt, parameters);
  528. }
  529. /// <summary>
  530. /// Call a RPC method of this GameObject on remote clients of this room (or on all, including this client).
  531. /// </summary>
  532. /// <remarks>
  533. /// [Remote Procedure Calls](@ref rpcManual) are an essential tool in making multiplayer games with PUN.
  534. /// It enables you to make every client in a room call a specific method.
  535. ///
  536. /// This method allows you to make an RPC calls on a specific player's client.
  537. /// Of course, calls are affected by this client's lag and that of remote clients.
  538. ///
  539. /// Each call automatically is routed to the same PhotonView (and GameObject) that was used on the
  540. /// originating client.
  541. ///
  542. /// See: [Remote Procedure Calls](@ref rpcManual).
  543. /// </remarks>
  544. /// <param name="methodName">The name of a fitting method that was has the RPC attribute.</param>
  545. /// <param name="targetPlayer">The group of targets and the way the RPC gets sent.</param>
  546. /// <param name="parameters">The parameters that the RPC method has (must fit this call!).</param>
  547. public void RPC(string methodName, Player targetPlayer, params object[] parameters)
  548. {
  549. PhotonNetwork.RPC(this, methodName, targetPlayer, false, parameters);
  550. }
  551. /// <summary>
  552. /// Call a RPC method of this GameObject on remote clients of this room (or on all, including this client).
  553. /// </summary>
  554. /// <remarks>
  555. /// [Remote Procedure Calls](@ref rpcManual) are an essential tool in making multiplayer games with PUN.
  556. /// It enables you to make every client in a room call a specific method.
  557. ///
  558. /// This method allows you to make an RPC calls on a specific player's client.
  559. /// Of course, calls are affected by this client's lag and that of remote clients.
  560. ///
  561. /// Each call automatically is routed to the same PhotonView (and GameObject) that was used on the
  562. /// originating client.
  563. ///
  564. /// See: [Remote Procedure Calls](@ref rpcManual).
  565. /// </remarks>
  566. ///<param name="methodName">The name of a fitting method that was has the RPC attribute.</param>
  567. ///<param name="targetPlayer">The group of targets and the way the RPC gets sent.</param>
  568. ///<param name="encrypt"> </param>
  569. ///<param name="parameters">The parameters that the RPC method has (must fit this call!).</param>
  570. public void RpcSecure(string methodName, Player targetPlayer, bool encrypt, params object[] parameters)
  571. {
  572. PhotonNetwork.RPC(this, methodName, targetPlayer, encrypt, parameters);
  573. }
  574. public static PhotonView Get(Component component)
  575. {
  576. return component.transform.GetParentComponent<PhotonView>();
  577. }
  578. public static PhotonView Get(GameObject gameObj)
  579. {
  580. return gameObj.transform.GetParentComponent<PhotonView>();
  581. }
  582. /// <summary>
  583. /// Finds the PhotonView Component with a viewID in the scene
  584. /// </summary>
  585. /// <param name="viewID"></param>
  586. /// <returns>The PhotonView with ViewID. Returns null if none found</returns>
  587. public static PhotonView Find(int viewID)
  588. {
  589. return PhotonNetwork.GetPhotonView(viewID);
  590. }
  591. #region Callback Interfaces
  592. private struct CallbackTargetChange
  593. {
  594. public IPhotonViewCallback obj;
  595. public Type type;
  596. public bool add;
  597. public CallbackTargetChange(IPhotonViewCallback obj, Type type, bool add)
  598. {
  599. this.obj = obj;
  600. this.type = type;
  601. this.add = add;
  602. }
  603. }
  604. private Queue<CallbackTargetChange> CallbackChangeQueue = new Queue<CallbackTargetChange>();
  605. private List<IOnPhotonViewPreNetDestroy> OnPreNetDestroyCallbacks;
  606. private List<IOnPhotonViewOwnerChange> OnOwnerChangeCallbacks;
  607. private List<IOnPhotonViewControllerChange> OnControllerChangeCallbacks;
  608. /// <summary>
  609. /// Add object to all applicable callback interfaces. Object must implement at least one IOnPhotonViewCallback derived interface.
  610. /// </summary>
  611. /// <param name="obj">An object that implements OnPhotonView callback interface(s).</param>
  612. public void AddCallbackTarget(IPhotonViewCallback obj)
  613. {
  614. CallbackChangeQueue.Enqueue(new CallbackTargetChange(obj, null, true));
  615. }
  616. /// <summary>
  617. /// Remove object from all applicable callback interfaces. Object must implement at least one IOnPhotonViewCallback derived interface.
  618. /// </summary>
  619. /// <param name="obj">An object that implements OnPhotonView callback interface(s).</param>
  620. public void RemoveCallbackTarget(IPhotonViewCallback obj)
  621. {
  622. CallbackChangeQueue.Enqueue(new CallbackTargetChange(obj, null, false));
  623. }
  624. /// <summary>
  625. /// Add object to this PhotonView's callback.
  626. /// T is the IOnPhotonViewCallback derived interface you want added to its associated callback list.
  627. /// Supplying IOnPhotonViewCallback (the interface base class) as T will add ALL implemented IOnPhotonViewCallback Interfaces found on the object.
  628. /// </summary>
  629. public void AddCallback<T>(IPhotonViewCallback obj) where T : class, IPhotonViewCallback
  630. {
  631. CallbackChangeQueue.Enqueue(new CallbackTargetChange(obj, typeof(T), true));
  632. }
  633. /// <summary>
  634. /// Remove object from this PhotonView's callback list for T.
  635. /// T is the IOnPhotonViewCallback derived interface you want removed from its associated callback list.
  636. /// Supplying IOnPhotonViewCallback (the interface base class) as T will remove ALL implemented IOnPhotonViewCallback Interfaces found on the object.
  637. /// </summary>
  638. public void RemoveCallback<T>(IPhotonViewCallback obj) where T : class, IPhotonViewCallback
  639. {
  640. CallbackChangeQueue.Enqueue(new CallbackTargetChange(obj, typeof(T), false));
  641. }
  642. /// <summary>
  643. /// Apply any queued add/remove of interfaces from the callback lists. Typically called before looping callback lists.
  644. /// </summary>
  645. private void UpdateCallbackLists()
  646. {
  647. while (CallbackChangeQueue.Count > 0)
  648. {
  649. var item = CallbackChangeQueue.Dequeue();
  650. var obj = item.obj;
  651. var type = item.type;
  652. var add = item.add;
  653. if (type == null)
  654. {
  655. TryRegisterCallback(obj, ref OnPreNetDestroyCallbacks, add);
  656. TryRegisterCallback(obj, ref OnOwnerChangeCallbacks, add);
  657. TryRegisterCallback(obj, ref OnControllerChangeCallbacks, add);
  658. }
  659. else if (type == typeof(IOnPhotonViewPreNetDestroy))
  660. RegisterCallback(obj as IOnPhotonViewPreNetDestroy, ref OnPreNetDestroyCallbacks, add);
  661. else if (type == typeof(IOnPhotonViewOwnerChange))
  662. RegisterCallback(obj as IOnPhotonViewOwnerChange, ref OnOwnerChangeCallbacks, add);
  663. else if (type == typeof(IOnPhotonViewControllerChange))
  664. RegisterCallback(obj as IOnPhotonViewControllerChange, ref OnControllerChangeCallbacks, add);
  665. }
  666. }
  667. private void TryRegisterCallback<T>(IPhotonViewCallback obj, ref List<T> list, bool add) where T : class, IPhotonViewCallback
  668. {
  669. T iobj = obj as T;
  670. if (iobj != null)
  671. {
  672. RegisterCallback(iobj, ref list, add);
  673. }
  674. }
  675. private void RegisterCallback<T>(T obj, ref List<T> list, bool add) where T : class, IPhotonViewCallback
  676. {
  677. if (ReferenceEquals(list, null))
  678. list = new List<T>();
  679. if (add)
  680. {
  681. if (!list.Contains(obj))
  682. list.Add(obj);
  683. }
  684. else
  685. {
  686. if (list.Contains(obj))
  687. list.Remove(obj);
  688. }
  689. }
  690. #endregion Callback Interfaces
  691. public override string ToString()
  692. {
  693. return string.Format("View {0}{3} on {1} {2}", this.ViewID, (this.gameObject != null) ? this.gameObject.name : "GO==null", (this.IsRoomView) ? "(scene)" : string.Empty, this.Prefix > 0 ? "lvl" + this.Prefix : "");
  694. }
  695. }
  696. }