PhotonNetwork.cs 149 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313
  1. // ----------------------------------------------------------------------------
  2. // <copyright file="PhotonNetwork.cs" company="Exit Games GmbH">
  3. // PhotonNetwork Framework for Unity - Copyright (C) 2018 Exit Games GmbH
  4. // </copyright>
  5. // <summary>
  6. // PhotonNetwork is the central class of the PUN package.
  7. // </summary>
  8. // <author>developer@exitgames.com</author>
  9. // ----------------------------------------------------------------------------
  10. namespace Photon.Pun
  11. {
  12. using System.Diagnostics;
  13. using UnityEngine;
  14. using System;
  15. using System.Linq;
  16. using System.Collections.Generic;
  17. using ExitGames.Client.Photon;
  18. using UnityEngine.SceneManagement;
  19. using Photon.Realtime;
  20. using Debug = UnityEngine.Debug;
  21. using Hashtable = ExitGames.Client.Photon.Hashtable;
  22. #if UNITY_EDITOR
  23. using UnityEditor;
  24. using System.IO;
  25. #endif
  26. public struct InstantiateParameters
  27. {
  28. public int[] viewIDs;
  29. public byte objLevelPrefix;
  30. public object[] data;
  31. public byte @group;
  32. public Quaternion rotation;
  33. public Vector3 position;
  34. public string prefabName;
  35. public Player creator;
  36. public int timestamp;
  37. public InstantiateParameters(string prefabName, Vector3 position, Quaternion rotation, byte @group, object[] data, byte objLevelPrefix, int[] viewIDs, Player creator, int timestamp)
  38. {
  39. this.prefabName = prefabName;
  40. this.position = position;
  41. this.rotation = rotation;
  42. this.@group = @group;
  43. this.data = data;
  44. this.objLevelPrefix = objLevelPrefix;
  45. this.viewIDs = viewIDs;
  46. this.creator = creator;
  47. this.timestamp = timestamp;
  48. }
  49. }
  50. /// <summary>
  51. /// The main class to use the PhotonNetwork plugin.
  52. /// This class is static.
  53. /// </summary>
  54. /// \ingroup publicApi
  55. public static partial class PhotonNetwork
  56. {
  57. /// <summary>Version number of PUN. Used in the AppVersion, which separates your playerbase in matchmaking.</summary>
  58. public const string PunVersion = "2.43";
  59. /// <summary>Version number of your game. Setting this updates the AppVersion, which separates your playerbase in matchmaking.</summary>
  60. /// <remarks>
  61. /// In PUN, the GameVersion is only one component of the LoadBalancingClient.AppVersion.
  62. /// Setting the GameVersion will also set the LoadBalancingClient.AppVersion to: value+'_'+ PhotonNetwork.PunVersion.
  63. ///
  64. /// The AppVersion is used to split your playerbase as needed.
  65. /// One AppId may have various AppVersions and each is a separate set of users for matchmaking.
  66. ///
  67. /// The AppVersion gets sent in the "Authenticate" step.
  68. /// This means you can set the GameVersion right after calling ConnectUsingSettings (e.g.) and the new value will be used on the server.
  69. /// Once the client is connected, authentication is done and the value won't be sent to the server anymore.
  70. /// </remarks>
  71. public static string GameVersion
  72. {
  73. get { return gameVersion; }
  74. set
  75. {
  76. gameVersion = value;
  77. NetworkingClient.AppVersion = string.Format("{0}_{1}", value, PhotonNetwork.PunVersion);
  78. }
  79. }
  80. private static string gameVersion;
  81. /// <summary>Sent to Photon Server to specify the "Virtual AppId".</summary>
  82. /// <remarks>Sent with the operation Authenticate. When using PUN, you should set the GameVersion or use ConnectUsingSettings().</remarks>
  83. public static string AppVersion
  84. {
  85. get { return NetworkingClient.AppVersion; }
  86. }
  87. /// <summary>The LoadBalancingClient is part of Photon Realtime and wraps up multiple servers and states for PUN.</summary>
  88. public static LoadBalancingClient NetworkingClient;
  89. /// <summary>
  90. /// The maximum number of assigned PhotonViews <i>per player</i> (or scene). See the [General Documentation](@ref general) topic "Limitations" on how to raise this limitation.
  91. /// </summary>
  92. public static readonly int MAX_VIEW_IDS = 1000; // VIEW & PLAYER LIMIT CAN BE EASILY CHANGED, SEE DOCS
  93. /// <summary>Name of the PhotonServerSettings file (used to load and by PhotonEditor to save new files).</summary>
  94. public const string ServerSettingsFileName = "PhotonServerSettings";
  95. private static ServerSettings photonServerSettings;
  96. /// <summary>Serialized server settings, written by the Setup Wizard for use in ConnectUsingSettings.</summary>
  97. public static ServerSettings PhotonServerSettings
  98. {
  99. get
  100. {
  101. if (photonServerSettings == null)
  102. {
  103. LoadOrCreateSettings();
  104. }
  105. return photonServerSettings;
  106. }
  107. private set { photonServerSettings = value; }
  108. }
  109. /// <summary>Currently used server address (no matter if master or game server).</summary>
  110. public static string ServerAddress { get { return (NetworkingClient != null) ? NetworkingClient.CurrentServerAddress : "<not connected>"; } }
  111. /// <summary>Currently used Cloud Region (if any). As long as the client is not on a Master Server or Game Server, the region is not yet defined.</summary>
  112. public static string CloudRegion { get { return (NetworkingClient != null && IsConnected && Server!=ServerConnection.NameServer) ? NetworkingClient.CloudRegion : null; } }
  113. /// <summary>The cluster name provided by the Name Server.</summary>
  114. /// <remarks>
  115. /// The value is provided by the OpResponse for OpAuthenticate/OpAuthenticateOnce. See ConnectToRegion.
  116. ///
  117. /// Null until set.
  118. ///
  119. /// Note that the Name Server may assign another cluster, if the requested one is not configured or available.
  120. /// </remarks>
  121. public static string CurrentCluster { get { return (NetworkingClient != null ) ? NetworkingClient.CurrentCluster : null; } }
  122. /// <summary>Key to save the "Best Region Summary" in the Player Preferences.</summary>
  123. private const string PlayerPrefsKey = "PUNCloudBestRegion";
  124. /// <summary>Used to store and access the "Best Region Summary" in the Player Preferences.</summary>
  125. /// <remarks>
  126. /// Set this value to null before you connect, to discard the previously selected Best Region for the client.
  127. /// </remarks>
  128. public static string BestRegionSummaryInPreferences
  129. {
  130. get
  131. {
  132. return PlayerPrefs.GetString(PlayerPrefsKey, null);
  133. }
  134. internal set
  135. {
  136. if (String.IsNullOrEmpty(value))
  137. {
  138. PlayerPrefs.DeleteKey(PlayerPrefsKey);
  139. }
  140. else
  141. {
  142. PlayerPrefs.SetString(PlayerPrefsKey, value.ToString());
  143. }
  144. }
  145. }
  146. /// <summary>
  147. /// False until you connected to Photon initially. True immediately after Connect-call, in offline mode, while connected to any server and even while switching servers.
  148. /// </summary>
  149. /// <remarks>
  150. /// It is recommended to use the IConnectionCallbacks to establish a connection workflow.
  151. /// Also have a look at IsConnectedAndReady, which provides more info on when you can call operations at all.
  152. /// </remarks>
  153. public static bool IsConnected
  154. {
  155. get
  156. {
  157. if (OfflineMode)
  158. {
  159. return true;
  160. }
  161. if (NetworkingClient == null)
  162. {
  163. return false;
  164. }
  165. return NetworkingClient.IsConnected;
  166. }
  167. }
  168. /// <summary>
  169. /// A refined version of connected which is true only if your connection to the server is ready to accept operations like join, leave, etc.
  170. /// </summary>
  171. public static bool IsConnectedAndReady
  172. {
  173. get
  174. {
  175. if (OfflineMode)
  176. {
  177. return true;
  178. }
  179. if (NetworkingClient == null)
  180. {
  181. return false;
  182. }
  183. return NetworkingClient.IsConnectedAndReady;
  184. }
  185. }
  186. /// <summary>
  187. /// Directly provides the network-level client state, unless in OfflineMode.
  188. /// </summary>
  189. /// <remarks>
  190. /// In context of PUN, you should usually use IsConnected or IsConnectedAndReady.
  191. ///
  192. /// This is the lower level connection state. Keep in mind that PUN uses more than one server,
  193. /// so the client may become Disconnected, even though it's just switching servers.
  194. ///
  195. /// While OfflineMode is true, this is ClientState.Joined (after create/join) or ConnectedToMasterServer in all other cases.
  196. /// </remarks>
  197. public static ClientState NetworkClientState
  198. {
  199. get
  200. {
  201. if (OfflineMode)
  202. {
  203. return (offlineModeRoom != null) ? ClientState.Joined : ClientState.ConnectedToMasterServer;
  204. }
  205. if (NetworkingClient == null)
  206. {
  207. return ClientState.Disconnected;
  208. }
  209. return NetworkingClient.State;
  210. }
  211. }
  212. /// <summary>Tracks, which Connect method was called last. </summary>
  213. /// <remarks>
  214. /// ConnectToMaster sets this to ConnectToMaster.
  215. /// ConnectToRegion sets this to ConnectToRegion.
  216. /// ConnectToBestCloudServer sets this to ConnectToBest.
  217. /// PhotonNetwork.ConnectUsingSettings will call either ConnectToMaster, ConnectToRegion or ConnectToBest, depending on the settings.
  218. /// </remarks>
  219. public static ConnectMethod ConnectMethod = ConnectMethod.NotCalled;
  220. /// <summary>The server (type) this client is currently connected or connecting to.</summary>
  221. /// <remarks>Photon uses 3 different roles of servers: Name Server, Master Server and Game Server.</remarks>
  222. public static ServerConnection Server
  223. {
  224. get
  225. {
  226. if (OfflineMode)
  227. {
  228. return CurrentRoom == null ? ServerConnection.MasterServer : ServerConnection.GameServer;
  229. }
  230. return (PhotonNetwork.NetworkingClient != null) ? PhotonNetwork.NetworkingClient.Server : ServerConnection.NameServer;
  231. }
  232. }
  233. /// <summary>
  234. /// A user's authentication values used during connect.
  235. /// </summary>
  236. /// <remarks>
  237. /// Set these before calling Connect if you want custom authentication.
  238. /// These values set the userId, if and how that userId gets verified (server-side), etc..
  239. ///
  240. /// If authentication fails for any values, PUN will call your implementation of OnCustomAuthenticationFailed(string debugMessage).
  241. /// See <see cref="Photon.Realtime.IConnectionCallbacks.OnCustomAuthenticationFailed"/>.
  242. /// </remarks>
  243. public static AuthenticationValues AuthValues
  244. {
  245. get { return (NetworkingClient != null) ? NetworkingClient.AuthValues : null; }
  246. set { if (NetworkingClient != null) NetworkingClient.AuthValues = value; }
  247. }
  248. /// <summary>
  249. /// The lobby that will be used when PUN joins a lobby or creates a game.
  250. /// This is defined when joining a lobby or creating rooms
  251. /// </summary>
  252. /// <remarks>
  253. /// The default lobby uses an empty string as name.
  254. /// So when you connect or leave a room, PUN automatically gets you into a lobby again.
  255. ///
  256. /// Check PhotonNetwork.InLobby if the client is in a lobby.
  257. /// (@ref masterServerAndLobby)
  258. /// </remarks>
  259. public static TypedLobby CurrentLobby
  260. {
  261. get { return NetworkingClient.CurrentLobby; }
  262. }
  263. /// <summary>
  264. /// Get the room we're currently in (also when in OfflineMode). Null if we aren't in any room.
  265. /// </summary>
  266. /// <remarks>
  267. /// LoadBalancing Client is not aware of the Photon Offline Mode, so never use PhotonNetwork.NetworkingClient.CurrentRoom will be null if you are using OffLine Mode, while PhotonNetwork.CurrentRoom will be set when offlineMode is true
  268. /// </remarks>
  269. public static Room CurrentRoom
  270. {
  271. get
  272. {
  273. if (offlineMode)
  274. {
  275. return offlineModeRoom;
  276. }
  277. return NetworkingClient == null ? null : NetworkingClient.CurrentRoom;
  278. }
  279. }
  280. /// <summary>
  281. /// Controls how verbose PUN is.
  282. /// </summary>
  283. public static PunLogLevel LogLevel = PunLogLevel.ErrorsOnly;
  284. /// <summary>
  285. /// This client's Player instance is always available, unless the app shuts down.
  286. /// </summary>
  287. /// <remarks>
  288. /// Useful (e.g.) to set the Custom Player Properties or the NickName for this client anytime.
  289. /// When the client joins a room, the Custom Properties and other values are synced.
  290. /// </remarks>
  291. public static Player LocalPlayer
  292. {
  293. get
  294. {
  295. if (NetworkingClient == null)
  296. {
  297. return null; // suppress ExitApplication errors
  298. }
  299. return NetworkingClient.LocalPlayer;
  300. }
  301. }
  302. /// <summary>
  303. /// Set to synchronize the player's nickname with everyone in the room(s) you enter. This sets PhotonNetwork.player.NickName.
  304. /// </summary>
  305. /// <remarks>
  306. /// The NickName is just a nickname and does not have to be unique or backed up with some account.<br/>
  307. /// Set the value any time (e.g. before you connect) and it will be available to everyone you play with.<br/>
  308. /// Access the names of players by: Player.NickName. <br/>
  309. /// PhotonNetwork.PlayerListOthers is a list of other players - each contains the NickName the remote player set.
  310. /// </remarks>
  311. public static string NickName
  312. {
  313. get
  314. {
  315. return NetworkingClient.NickName;
  316. }
  317. set
  318. {
  319. NetworkingClient.NickName = value;
  320. }
  321. }
  322. /// <summary>
  323. /// A sorted copy of the players-list of the current room. This is using Linq, so better cache this value. Update when players join / leave.
  324. /// </summary>
  325. public static Player[] PlayerList
  326. {
  327. get
  328. {
  329. Room room = CurrentRoom;
  330. if (room != null)
  331. {
  332. // TODO: implement more effectively. maybe cache?!
  333. return room.Players.Values.OrderBy((x) => x.ActorNumber).ToArray();
  334. }
  335. return new Player[0];
  336. }
  337. }
  338. /// <summary>
  339. /// A sorted copy of the players-list of the current room, excluding this client. This is using Linq, so better cache this value. Update when players join / leave.
  340. /// </summary>
  341. public static Player[] PlayerListOthers
  342. {
  343. get
  344. {
  345. Room room = CurrentRoom;
  346. if (room != null)
  347. {
  348. // TODO: implement more effectively. maybe cache?!
  349. return room.Players.Values.OrderBy((x) => x.ActorNumber).Where(x => !x.IsLocal).ToArray();
  350. }
  351. return new Player[0];
  352. }
  353. }
  354. /// <summary>
  355. /// Used to enable reaction to CloseConnection events. Default: false.
  356. /// </summary>
  357. /// <remarks>
  358. /// Using CloseConnection is a security risk, as exploiters can send the event as Master Client.
  359. ///
  360. /// In best case, a game would implement this "disconnect others" independently from PUN in game-code
  361. /// with some security checks.
  362. /// </remarks>
  363. public static bool EnableCloseConnection = false;
  364. /// <summary>
  365. /// The minimum difference that a Vector2 or Vector3(e.g. a transforms rotation) needs to change before we send it via a PhotonView's OnSerialize/ObservingComponent.
  366. /// </summary>
  367. /// <remarks>
  368. /// Note that this is the sqrMagnitude. E.g. to send only after a 0.01 change on the Y-axix, we use 0.01f*0.01f=0.0001f. As a remedy against float inaccuracy we use 0.000099f instead of 0.0001f.
  369. /// </remarks>
  370. public static float PrecisionForVectorSynchronization = 0.000099f;
  371. /// <summary>
  372. /// The minimum angle that a rotation needs to change before we send it via a PhotonView's OnSerialize/ObservingComponent.
  373. /// </summary>
  374. public static float PrecisionForQuaternionSynchronization = 1.0f;
  375. /// <summary>
  376. /// The minimum difference between floats before we send it via a PhotonView's OnSerialize/ObservingComponent.
  377. /// </summary>
  378. public static float PrecisionForFloatSynchronization = 0.01f;
  379. /// <summary>
  380. /// Offline mode can be set to re-use your multiplayer code in singleplayer game modes.
  381. /// When this is on PhotonNetwork will not create any connections and there is near to
  382. /// no overhead. Mostly usefull for reusing RPC's and PhotonNetwork.Instantiate
  383. /// </summary>
  384. public static bool OfflineMode
  385. {
  386. get
  387. {
  388. return offlineMode;
  389. }
  390. set
  391. {
  392. if (value == offlineMode)
  393. {
  394. return;
  395. }
  396. if (value && IsConnected)
  397. {
  398. Debug.LogError("Can't start OFFLINE mode while connected!");
  399. return;
  400. }
  401. if (NetworkingClient.IsConnected)
  402. {
  403. NetworkingClient.Disconnect(); // Cleanup (also calls OnLeftRoom to reset stuff)
  404. }
  405. offlineMode = value;
  406. if (offlineMode)
  407. {
  408. NetworkingClient.ChangeLocalID(-1);
  409. //SendMonoMessage(PhotonNetworkingMessage.OnConnectedToMaster);
  410. NetworkingClient.ConnectionCallbackTargets.OnConnectedToMaster();
  411. }
  412. else
  413. {
  414. bool wasInOfflineRoom = offlineModeRoom != null;
  415. if (wasInOfflineRoom)
  416. {
  417. LeftRoomCleanup();
  418. }
  419. offlineModeRoom = null;
  420. PhotonNetwork.NetworkingClient.CurrentRoom = null;
  421. NetworkingClient.ChangeLocalID(-1);
  422. if (wasInOfflineRoom)
  423. {
  424. NetworkingClient.MatchMakingCallbackTargets.OnLeftRoom();
  425. }
  426. }
  427. }
  428. }
  429. private static bool offlineMode = false;
  430. private static Room offlineModeRoom = null;
  431. /// <summary>Defines if all clients in a room should automatically load the same level as the Master Client.</summary>
  432. /// <remarks>
  433. /// When enabled, clients load the same scene that is active on the Master Client.
  434. /// When a client joins a room, the scene gets loaded even before the callback OnJoinedRoom gets called.
  435. ///
  436. /// To synchronize the loaded level, the Master Client should use PhotonNetwork.LoadLevel, which
  437. /// notifies the other clients before starting to load the scene.
  438. /// If the Master Client loads a level directly via Unity's API, PUN will notify the other players after
  439. /// the scene loading completed (using SceneManager.sceneLoaded).
  440. ///
  441. /// Internally, a Custom Room Property is set for the loaded scene. On change, clients use LoadLevel
  442. /// if they are not in the same scene.
  443. ///
  444. /// Note that this works only for a single active scene and that reloading the scene is not supported.
  445. /// The Master Client will actually reload a scene but other clients won't.
  446. /// To get everyone to reload, the game can send an RPC or event to trigger the loading.
  447. /// </remarks>
  448. public static bool AutomaticallySyncScene
  449. {
  450. get
  451. {
  452. return automaticallySyncScene;
  453. }
  454. set
  455. {
  456. automaticallySyncScene = value;
  457. if (automaticallySyncScene && CurrentRoom != null)
  458. {
  459. LoadLevelIfSynced();
  460. }
  461. }
  462. }
  463. private static bool automaticallySyncScene = false;
  464. /// <summary>
  465. /// If enabled, the client will get a list of available lobbies from the Master Server.
  466. /// </summary>
  467. /// <remarks>
  468. /// Set this value before the client connects to the Master Server. While connected to the Master
  469. /// Server, a change has no effect.
  470. ///
  471. /// Implement OptionalInfoCallbacks.OnLobbyStatisticsUpdate, to get the list of used lobbies.
  472. ///
  473. /// The lobby statistics can be useful if your title dynamically uses lobbies, depending (e.g.)
  474. /// on current player activity or such.
  475. /// In this case, getting a list of available lobbies, their room-count and player-count can
  476. /// be useful info.
  477. ///
  478. /// ConnectUsingSettings sets this to the PhotonServerSettings value.
  479. /// </remarks>
  480. public static bool EnableLobbyStatistics
  481. {
  482. get
  483. {
  484. return NetworkingClient.EnableLobbyStatistics;
  485. }
  486. }
  487. /// <summary>True while this client is in a lobby.</summary>
  488. /// <remarks>
  489. /// Implement IPunCallbacks.OnRoomListUpdate() for a notification when the list of rooms
  490. /// becomes available or updated.
  491. ///
  492. /// You are automatically leaving any lobby when you join a room!
  493. /// Lobbies only exist on the Master Server (whereas rooms are handled by Game Servers).
  494. /// </remarks>
  495. public static bool InLobby
  496. {
  497. get
  498. {
  499. return NetworkingClient.InLobby;
  500. }
  501. }
  502. /// <summary>
  503. /// Defines how many times per second the PhotonHandler should send data, if any is queued. Default: 30.
  504. /// </summary>
  505. /// <remarks>
  506. /// This value defines how often PUN will call the low level PhotonPeer to put queued outgoing messages
  507. /// into a datagram to be sent. This is implemented in the PhotonHandler component, which integrates PUN
  508. /// into the Unity game loop.
  509. /// The PhotonHandler.MaxDatagrams value defines how many datagrams can be sent in one iteration.
  510. ///
  511. /// This value does not affect how often updates are written by PhotonViews. That is controlled by the
  512. /// SerializationRate. To avoid send-delays for PhotonView updates, PUN will also send data at the end
  513. /// of frames that wrote data in OnPhotonSerializeView, so sending may actually be more frequent than
  514. /// the SendRate.
  515. ///
  516. /// Messages queued due to RPCs and RaiseEvent, will be sent with at least SendRate frequency. They
  517. /// are included, when OnPhotonSerialize wrote updates and triggers early sending.
  518. ///
  519. /// Setting this value does not adjust the SerializationRate anymore (as of PUN 2.24).
  520. ///
  521. /// Sending less often will aggregate messages in datagrams, which avoids overhead on the network.
  522. /// It is also important to not push too many datagrams per frame. Three to five seem to be the sweet spot.
  523. ///
  524. /// Keep your target platform in mind: mobile networks are usually slower.
  525. /// WiFi is slower with more variance and bursts of loss.
  526. ///
  527. /// A low framerate (as in Update calls) will affect sending of messages.
  528. /// </remarks>
  529. public static int SendRate
  530. {
  531. get
  532. {
  533. return 1000 / sendFrequency;
  534. }
  535. set
  536. {
  537. sendFrequency = 1000 / value;
  538. if (PhotonHandler.Instance != null)
  539. {
  540. PhotonHandler.Instance.UpdateInterval = sendFrequency;
  541. }
  542. }
  543. }
  544. private static int sendFrequency = 33; // in milliseconds.
  545. /// <summary>
  546. /// Defines how many times per second OnPhotonSerialize should be called on PhotonViews for controlled objects.
  547. /// </summary>
  548. /// <remarks>
  549. /// This value defines how often PUN will call OnPhotonSerialize on controlled network objects.
  550. /// This is implemented in the PhotonHandler component, which integrates PUN into the Unity game loop.
  551. ///
  552. /// The updates written in OnPhotonSerialize will be queued temporarily and sent in the next LateUpdate,
  553. /// so a high SerializationRate also causes more sends. The idea is to keep the delay short during
  554. /// which written updates are queued.
  555. ///
  556. /// Calling RPCs will not trigger a send.
  557. ///
  558. /// A low framerate will affect how frequent updates are written and how "on time" they are.
  559. ///
  560. /// A lower rate takes up less performance but the receiving side needs to interpolate longer times
  561. /// between updates.
  562. /// </remarks>
  563. public static int SerializationRate
  564. {
  565. get
  566. {
  567. return 1000 / serializationFrequency;
  568. }
  569. set
  570. {
  571. serializationFrequency = 1000 / value;
  572. if (PhotonHandler.Instance != null)
  573. {
  574. PhotonHandler.Instance.UpdateIntervalOnSerialize = serializationFrequency;
  575. }
  576. }
  577. }
  578. private static int serializationFrequency = 100; // in milliseconds. I.e. 100 = 100ms which makes 10 times/second
  579. /// <summary>
  580. /// Can be used to pause dispatching of incoming events (RPCs, Instantiates and anything else incoming).
  581. /// </summary>
  582. /// <remarks>
  583. /// While IsMessageQueueRunning == false, the OnPhotonSerializeView calls are not done and nothing is sent by
  584. /// a client. Also, incoming messages will be queued until you re-activate the message queue.
  585. ///
  586. /// This can be useful if you first want to load a level, then go on receiving data of PhotonViews and RPCs.
  587. /// The client will go on receiving and sending acknowledgements for incoming packages and your RPCs/Events.
  588. /// This adds "lag" and can cause issues when the pause is longer, as all incoming messages are just queued.
  589. /// </remarks>
  590. public static bool IsMessageQueueRunning
  591. {
  592. get
  593. {
  594. return isMessageQueueRunning;
  595. }
  596. set
  597. {
  598. isMessageQueueRunning = value;
  599. }
  600. }
  601. /// <summary>Backup for property IsMessageQueueRunning.</summary>
  602. private static bool isMessageQueueRunning = true;
  603. /// <summary>
  604. /// Photon network time, synched with the server.
  605. /// </summary>
  606. /// <remarks>
  607. /// v1.55<br/>
  608. /// This time value depends on the server's Environment.TickCount. It is different per server
  609. /// but inside a Room, all clients should have the same value (Rooms are on one server only).<br/>
  610. /// This is not a DateTime!<br/>
  611. ///
  612. /// Use this value with care: <br/>
  613. /// It can start with any positive value.<br/>
  614. /// It will "wrap around" from 4294967.295 to 0!
  615. /// </remarks>
  616. public static double Time
  617. {
  618. get
  619. {
  620. if (UnityEngine.Time.frameCount == frame)
  621. {
  622. return frametime;
  623. }
  624. uint u = (uint)ServerTimestamp;
  625. double t = u;
  626. frametime = t / 1000.0d;
  627. frame = UnityEngine.Time.frameCount;
  628. return frametime;
  629. }
  630. }
  631. private static double frametime;
  632. private static int frame;
  633. /// <summary>
  634. /// The current server's millisecond timestamp.
  635. /// </summary>
  636. /// <remarks>
  637. /// This can be useful to sync actions and events on all clients in one room.
  638. /// The timestamp is based on the server's Environment.TickCount.
  639. ///
  640. /// It will overflow from a positive to a negative value every so often, so
  641. /// be careful to use only time-differences to check the Time delta when things
  642. /// happen.
  643. ///
  644. /// This is the basis for PhotonNetwork.Time.
  645. /// </remarks>
  646. public static int ServerTimestamp
  647. {
  648. get
  649. {
  650. if (OfflineMode)
  651. {
  652. if (StartupStopwatch != null && StartupStopwatch.IsRunning)
  653. {
  654. return (int)StartupStopwatch.ElapsedMilliseconds;
  655. }
  656. return Environment.TickCount;
  657. }
  658. return NetworkingClient.LoadBalancingPeer.ServerTimeInMilliSeconds; // TODO: implement ServerTimeInMilliSeconds in LBC
  659. }
  660. }
  661. /// <summary>Used for Photon/PUN timing, as Time.time can't be called from Threads.</summary>
  662. private static Stopwatch StartupStopwatch;
  663. /// <summary>
  664. /// Defines how many seconds PUN keeps the connection after Unity's OnApplicationPause(true) call. Default: 60 seconds.
  665. /// </summary>
  666. /// <remarks>
  667. /// It's best practice to disconnect inactive apps/connections after a while but to also allow users to take calls, etc..
  668. /// We think a reasonable background timeout is 60 seconds.
  669. ///
  670. /// To handle the timeout, implement: OnDisconnected(), as usual.
  671. /// Your application will "notice" the background disconnect when it becomes active again (running the Update() loop).
  672. ///
  673. /// If you need to separate this case from others, you need to track if the app was in the background
  674. /// (there is no special callback by PUN).
  675. ///
  676. ///
  677. /// Info:
  678. /// PUN is running a "fallback thread" to send ACKs to the server, even when Unity is not calling Update() regularly.
  679. /// This helps keeping the connection while loading scenes and assets and when the app is in the background.
  680. ///
  681. /// Note:
  682. /// Some platforms (e.g. iOS) don't allow to keep a connection while the app is in background.
  683. /// In those cases, this value does not change anything, the app immediately loses connection in background.
  684. ///
  685. /// Unity's OnApplicationPause() callback is broken in some exports (Android) of some Unity versions.
  686. /// Make sure OnApplicationPause() gets the callbacks you expect on the platform you target!
  687. /// Check PhotonHandler.OnApplicationPause(bool pause) to see the implementation.
  688. /// </remarks>
  689. public static float KeepAliveInBackground
  690. {
  691. set
  692. {
  693. if (PhotonHandler.Instance != null)
  694. {
  695. PhotonHandler.Instance.KeepAliveInBackground = (int)Mathf.Round(value * 1000.0f);
  696. }
  697. }
  698. get { return PhotonHandler.Instance != null ? Mathf.Round(PhotonHandler.Instance.KeepAliveInBackground / 1000.0f) : 60.0f; }
  699. }
  700. /// <summary>Affects if the PhotonHandler dispatches incoming messages in LateUpdate or FixedUpdate (default).</summary>
  701. /// <remarks>
  702. /// By default the PhotonHandler component dispatches incoming messages in FixedUpdate.
  703. ///
  704. /// When the Time.timeScale is low, FixedUpdate is called less frequently up to a point where updates may get paused.
  705. /// PUN can automatically dispatch messages in LateUpdate for low timeScale values (when Time.timeScale is lower than this value).
  706. ///
  707. /// PUN will use either FixedUpdate or LateUpdate but not both (as of v2.23).
  708. ///
  709. /// When you use this value, be aware that Instantiates and RPCs execute with a changed timing within a frame.
  710. /// If Instantiate is called from FixedUpdate, the physics engine seems to run for instantiated objects before the engine calls Start() on them.
  711. ///
  712. /// By default, this value is -1f, so there is no fallback to LateUpdate.
  713. /// </remarks>
  714. public static float MinimalTimeScaleToDispatchInFixedUpdate = -1f;
  715. /// <summary>
  716. /// Are we the master client?
  717. /// </summary>
  718. public static bool IsMasterClient
  719. {
  720. get
  721. {
  722. if (OfflineMode)
  723. {
  724. return true;
  725. }
  726. return NetworkingClient.CurrentRoom != null && NetworkingClient.CurrentRoom.MasterClientId == LocalPlayer.ActorNumber; // TODO: implement MasterClient shortcut in LBC?
  727. }
  728. }
  729. /// <summary>
  730. /// The Master Client of the current room or null (outside of rooms).
  731. /// </summary>
  732. /// <remarks>
  733. /// Can be used as "authoritative" client/player to make descisions, run AI or other.
  734. ///
  735. /// If the current Master Client leaves the room (leave/disconnect), the server will quickly assign someone else.
  736. /// If the current Master Client times out (closed app, lost connection, etc), messages sent to this client are
  737. /// effectively lost for the others! A timeout can take 10 seconds in which no Master Client is active.
  738. ///
  739. /// Implement the method IPunCallbacks.OnMasterClientSwitched to be called when the Master Client switched.
  740. ///
  741. /// Use PhotonNetwork.SetMasterClient, to switch manually to some other player / client.
  742. ///
  743. /// With OfflineMode == true, this always returns the PhotonNetwork.player.
  744. /// </remarks>
  745. public static Player MasterClient
  746. {
  747. get
  748. {
  749. if (OfflineMode)
  750. {
  751. return PhotonNetwork.LocalPlayer;
  752. }
  753. if (NetworkingClient == null || NetworkingClient.CurrentRoom == null)
  754. {
  755. return null;
  756. }
  757. return NetworkingClient.CurrentRoom.GetPlayer(NetworkingClient.CurrentRoom.MasterClientId);
  758. }
  759. }
  760. /// <summary>Is true while being in a room (NetworkClientState == ClientState.Joined).</summary>
  761. /// <remarks>
  762. /// Aside from polling this value, game logic should implement IMatchmakingCallbacks in some class
  763. /// and react when that gets called.<br/>
  764. ///
  765. /// Many actions can only be executed in a room, like Instantiate or Leave, etc.<br/>
  766. /// A client can join a room in offline mode. In that case, don't use LoadBalancingClient.InRoom, which
  767. /// does not cover offline mode.
  768. /// </remarks>
  769. public static bool InRoom
  770. {
  771. get
  772. {
  773. // in offline mode, you can be in a room too and NetworkClientState then returns Joined like on online mode!
  774. return NetworkClientState == ClientState.Joined;
  775. }
  776. }
  777. /// <summary>
  778. /// The count of players currently looking for a room (available on MasterServer in 5sec intervals).
  779. /// </summary>
  780. public static int CountOfPlayersOnMaster
  781. {
  782. get
  783. {
  784. return NetworkingClient.PlayersOnMasterCount;
  785. }
  786. }
  787. /// <summary>
  788. /// Count of users currently playing your app in some room (sent every 5sec by Master Server).
  789. /// Use PhotonNetwork.PlayerList.Length or PhotonNetwork.CurrentRoom.PlayerCount to get the count of players in the room you're in!
  790. /// </summary>
  791. public static int CountOfPlayersInRooms
  792. {
  793. get
  794. {
  795. return NetworkingClient.PlayersInRoomsCount;
  796. }
  797. }
  798. /// <summary>
  799. /// The count of players currently using this application (available on MasterServer in 5sec intervals).
  800. /// </summary>
  801. public static int CountOfPlayers
  802. {
  803. get
  804. {
  805. return NetworkingClient.PlayersInRoomsCount + NetworkingClient.PlayersOnMasterCount;
  806. }
  807. }
  808. /// <summary>
  809. /// The count of rooms currently in use (available on MasterServer in 5sec intervals).
  810. /// </summary>
  811. public static int CountOfRooms
  812. {
  813. get
  814. {
  815. return NetworkingClient.RoomsCount;
  816. }
  817. }
  818. /// <summary>
  819. /// Enables or disables the collection of statistics about this client's traffic.
  820. /// </summary>
  821. /// <remarks>
  822. /// If you encounter issues with clients, the traffic stats are a good starting point to find solutions.
  823. /// Only with enabled stats, you can use GetVitalStats
  824. /// </remarks>
  825. public static bool NetworkStatisticsEnabled
  826. {
  827. get
  828. {
  829. return NetworkingClient.LoadBalancingPeer.TrafficStatsEnabled;
  830. }
  831. set
  832. {
  833. NetworkingClient.LoadBalancingPeer.TrafficStatsEnabled = value;
  834. }
  835. }
  836. /// <summary>
  837. /// Count of commands that got repeated (due to local repeat-timing before an ACK was received).
  838. /// </summary>
  839. /// <remarks>
  840. /// If this value increases a lot, there is a good chance that a timeout disconnect will happen due to bad conditions.
  841. /// </remarks>
  842. public static int ResentReliableCommands
  843. {
  844. get { return NetworkingClient.LoadBalancingPeer.ResentReliableCommands; }
  845. }
  846. /// <summary>Crc checks can be useful to detect and avoid issues with broken datagrams. Can be enabled while not connected.</summary>
  847. public static bool CrcCheckEnabled
  848. {
  849. get { return NetworkingClient.LoadBalancingPeer.CrcEnabled; }
  850. set
  851. {
  852. if (!IsConnected)
  853. {
  854. NetworkingClient.LoadBalancingPeer.CrcEnabled = value;
  855. }
  856. else
  857. {
  858. Debug.Log("Can't change CrcCheckEnabled while being connected. CrcCheckEnabled stays " + NetworkingClient.LoadBalancingPeer.CrcEnabled);
  859. }
  860. }
  861. }
  862. /// <summary>If CrcCheckEnabled, this counts the incoming packages that don't have a valid CRC checksum and got rejected.</summary>
  863. public static int PacketLossByCrcCheck
  864. {
  865. get { return NetworkingClient.LoadBalancingPeer.PacketLossByCrc; }
  866. }
  867. /// <summary>Defines the number of times a reliable message can be resent before not getting an ACK for it will trigger a disconnect. Default: 5.</summary>
  868. /// <remarks>Less resends mean quicker disconnects, while more can lead to much more lag without helping. Min: 3. Max: 10.</remarks>
  869. public static int MaxResendsBeforeDisconnect
  870. {
  871. get { return NetworkingClient.LoadBalancingPeer.SentCountAllowance; }
  872. set
  873. {
  874. if (value < 3) value = 3;
  875. if (value > 10) value = 10;
  876. NetworkingClient.LoadBalancingPeer.SentCountAllowance = value;
  877. }
  878. }
  879. /// <summary>In case of network loss, reliable messages can be repeated quickly up to 3 times.</summary>
  880. /// <remarks>
  881. /// When reliable messages get lost more than once, subsequent repeats are delayed a bit
  882. /// to allow the network to recover.<br/>
  883. /// With this option, the repeats 2 and 3 can be sped up. This can help avoid timeouts but
  884. /// also it increases the speed in which gaps are closed.<br/>
  885. /// When you set this, increase PhotonNetwork.MaxResendsBeforeDisconnect to 6 or 7.
  886. /// </remarks>
  887. public static int QuickResends
  888. {
  889. get { return NetworkingClient.LoadBalancingPeer.QuickResendAttempts; }
  890. set
  891. {
  892. if (value < 0) value = 0;
  893. if (value > 3) value = 3;
  894. NetworkingClient.LoadBalancingPeer.QuickResendAttempts = (byte)value;
  895. }
  896. }
  897. /// <summary>Replaced by ServerPortOverrides.</summary>
  898. [Obsolete("Set port overrides in ServerPortOverrides. Not used anymore!")]
  899. public static bool UseAlternativeUdpPorts { get; set; }
  900. /// <summary>Defines overrides for server ports. Used per server-type if > 0. Important: If you change the transport protocol, adjust the overrides, too.</summary>
  901. /// <see cref="LoadBalancingClient.ServerPortOverrides"/>
  902. public static PhotonPortDefinition ServerPortOverrides
  903. {
  904. get { return (NetworkingClient == null) ? new PhotonPortDefinition() : NetworkingClient.ServerPortOverrides; }
  905. set { if (NetworkingClient != null) NetworkingClient.ServerPortOverrides = value; }
  906. }
  907. private static int lastUsedViewSubId = 0; // each player only needs to remember it's own (!) last used subId to speed up assignment
  908. private static int lastUsedViewSubIdStatic = 0; // per room, the master is able to instantiate GOs. the subId for this must be unique too
  909. /// <summary>
  910. /// Static constructor used for basic setup.
  911. /// </summary>
  912. static PhotonNetwork()
  913. {
  914. #if !UNITY_EDITOR
  915. StaticReset(); // in builds, we just reset/init the client once
  916. #else
  917. #if UNITY_2019_4_OR_NEWER
  918. if (NetworkingClient == null)
  919. {
  920. NetworkingClient = new LoadBalancingClient();
  921. }
  922. #else
  923. StaticReset(); // in OLDER unity editor versions there is no RuntimeInitializeOnLoadMethod, so call reset
  924. #endif
  925. #endif
  926. }
  927. #if UNITY_EDITOR && UNITY_2019_4_OR_NEWER
  928. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
  929. #endif
  930. private static void StaticReset()
  931. {
  932. #if UNITY_EDITOR
  933. if (!EditorApplication.isPlayingOrWillChangePlaymode)
  934. {
  935. return;
  936. }
  937. #endif
  938. // This clear is for when Domain Reloading is disabled. Typically will already be empty.
  939. monoRPCMethodsCache.Clear();
  940. // set up the NetworkingClient, protocol, etc
  941. OfflineMode = false;
  942. ConnectionProtocol protocol = PhotonNetwork.PhotonServerSettings.AppSettings.Protocol;
  943. NetworkingClient = new LoadBalancingClient(protocol);
  944. NetworkingClient.LoadBalancingPeer.QuickResendAttempts = 2;
  945. NetworkingClient.LoadBalancingPeer.SentCountAllowance = 9;
  946. NetworkingClient.EventReceived -= OnEvent;
  947. NetworkingClient.EventReceived += OnEvent;
  948. NetworkingClient.OpResponseReceived -= OnOperation;
  949. NetworkingClient.OpResponseReceived += OnOperation;
  950. NetworkingClient.StateChanged -= OnClientStateChanged;
  951. NetworkingClient.StateChanged += OnClientStateChanged;
  952. StartupStopwatch = new Stopwatch();
  953. StartupStopwatch.Start();
  954. // using a singleton PhotonHandler to control the new client (which is also a singleton for PUN)
  955. PhotonHandler.Instance.Client = NetworkingClient;
  956. Application.runInBackground = PhotonServerSettings.RunInBackground;
  957. PrefabPool = new DefaultPool();
  958. // RPC shortcut lookup creation (from list of RPCs, which is updated by Editor scripts)
  959. rpcShortcuts = new Dictionary<string, int>(PhotonNetwork.PhotonServerSettings.RpcList.Count);
  960. for (int index = 0; index < PhotonNetwork.PhotonServerSettings.RpcList.Count; index++)
  961. {
  962. var name = PhotonNetwork.PhotonServerSettings.RpcList[index];
  963. rpcShortcuts[name] = index;
  964. }
  965. // PUN custom types (typical for Unity)
  966. CustomTypes.Register();
  967. }
  968. /// <summary>Connect to Photon as configured in the PhotonServerSettings file.</summary>
  969. /// <remarks>
  970. /// Implement IConnectionCallbacks, to make your game logic aware of state changes.
  971. /// Especially, IConnectionCallbacks.ConnectedToMasterServer is useful to react when
  972. /// the client can do matchmaking.
  973. ///
  974. /// This method will disable OfflineMode (which won't destroy any instantiated GOs) and it
  975. /// will set IsMessageQueueRunning to true.
  976. ///
  977. /// Your Photon configuration is created by the PUN Wizard and contains the AppId,
  978. /// region for Photon Cloud games, the server address among other things.
  979. ///
  980. /// To ignore the settings file, set the relevant values and connect by calling
  981. /// ConnectToMaster, ConnectToRegion.
  982. ///
  983. /// To connect to the Photon Cloud, a valid AppId must be in the settings file
  984. /// (shown in the <a href="https://dashboard.photonengine.com">Photon Cloud Dashboard</a>).
  985. ///
  986. /// Connecting to the Photon Cloud might fail due to:
  987. /// - Invalid AppId
  988. /// - Network issues
  989. /// - Invalid region
  990. /// - Subscription CCU limit reached
  991. /// - etc.
  992. ///
  993. /// In general check out the <see cref="DisconnectCause"/> from the <see cref="IConnectionCallbacks.OnDisconnected"/> callback.
  994. /// </remarks>
  995. public static bool ConnectUsingSettings()
  996. {
  997. if (PhotonServerSettings == null)
  998. {
  999. Debug.LogError("Can't connect: Loading settings failed. ServerSettings asset must be in any 'Resources' folder as: " + ServerSettingsFileName);
  1000. return false;
  1001. }
  1002. return ConnectUsingSettings(PhotonServerSettings.AppSettings, PhotonServerSettings.StartInOfflineMode);
  1003. }
  1004. public static bool ConnectUsingSettings(AppSettings appSettings, bool startInOfflineMode = false) // parameter name hides static class member
  1005. {
  1006. if (NetworkingClient.LoadBalancingPeer.PeerState != PeerStateValue.Disconnected)
  1007. {
  1008. Debug.LogWarning("ConnectUsingSettings() failed. Can only connect while in state 'Disconnected'. Current state: " + NetworkingClient.LoadBalancingPeer.PeerState);
  1009. return false;
  1010. }
  1011. if (PhotonHandler.AppQuits)
  1012. {
  1013. Debug.LogWarning("Can't connect: Application is closing. Unity called OnApplicationQuit().");
  1014. return false;
  1015. }
  1016. if (PhotonServerSettings == null)
  1017. {
  1018. Debug.LogError("Can't connect: Loading settings failed. ServerSettings asset must be in any 'Resources' folder as: " + ServerSettingsFileName);
  1019. return false;
  1020. }
  1021. SetupLogging();
  1022. NetworkingClient.LoadBalancingPeer.TransportProtocol = appSettings.Protocol;
  1023. NetworkingClient.ExpectedProtocol = null;
  1024. NetworkingClient.EnableProtocolFallback = appSettings.EnableProtocolFallback;
  1025. NetworkingClient.AuthMode = appSettings.AuthMode;
  1026. IsMessageQueueRunning = true;
  1027. NetworkingClient.AppId = appSettings.AppIdRealtime;
  1028. GameVersion = appSettings.AppVersion;
  1029. if (startInOfflineMode)
  1030. {
  1031. OfflineMode = true;
  1032. return true;
  1033. }
  1034. if (OfflineMode)
  1035. {
  1036. OfflineMode = false; // Cleanup offline mode
  1037. // someone can set OfflineMode in code and then call ConnectUsingSettings() with non-offline settings. Warning for that case:
  1038. Debug.LogWarning("ConnectUsingSettings() disabled the offline mode. No longer offline.");
  1039. }
  1040. NetworkingClient.EnableLobbyStatistics = appSettings.EnableLobbyStatistics;
  1041. NetworkingClient.ProxyServerAddress = appSettings.ProxyServer;
  1042. if (appSettings.IsMasterServerAddress)
  1043. {
  1044. if (AuthValues == null)
  1045. {
  1046. AuthValues = new AuthenticationValues(Guid.NewGuid().ToString());
  1047. }
  1048. else if (string.IsNullOrEmpty(AuthValues.UserId))
  1049. {
  1050. AuthValues.UserId = Guid.NewGuid().ToString();
  1051. }
  1052. return ConnectToMaster(appSettings.Server, appSettings.Port, appSettings.AppIdRealtime);
  1053. }
  1054. NetworkingClient.NameServerPortInAppSettings = appSettings.Port;
  1055. if (!appSettings.IsDefaultNameServer)
  1056. {
  1057. NetworkingClient.NameServerHost = appSettings.Server;
  1058. }
  1059. if (appSettings.IsBestRegion)
  1060. {
  1061. return ConnectToBestCloudServer();
  1062. }
  1063. return ConnectToRegion(appSettings.FixedRegion);
  1064. }
  1065. /// <summary>Connect to a Photon Master Server by address, port, appID.</summary>
  1066. /// <remarks>
  1067. /// To connect to the Photon Cloud, a valid AppId must be in the settings file (shown in the Photon Cloud Dashboard).
  1068. /// https://dashboard.photonengine.com
  1069. ///
  1070. /// Connecting to the Photon Cloud might fail due to:
  1071. /// - Invalid AppId
  1072. /// - Network issues
  1073. /// - Invalid region
  1074. /// - Subscription CCU limit reached
  1075. /// - etc.
  1076. ///
  1077. /// In general check out the <see cref="DisconnectCause"/> from the <see cref="IConnectionCallbacks.OnDisconnected"/> callback.
  1078. /// </remarks>
  1079. /// <param name="masterServerAddress">The server's address (either your own or Photon Cloud address).</param>
  1080. /// <param name="port">The server's port to connect to.</param>
  1081. /// <param name="appID">Your application ID (Photon Cloud provides you with a GUID for your game).</param>
  1082. public static bool ConnectToMaster(string masterServerAddress, int port, string appID)
  1083. {
  1084. // TODO: refactor NetworkingClient.LoadBalancingPeer.PeerState to not use the peer but LBC.connected or so
  1085. if (NetworkingClient.LoadBalancingPeer.PeerState != PeerStateValue.Disconnected)
  1086. {
  1087. Debug.LogWarning("ConnectToMaster() failed. Can only connect while in state 'Disconnected'. Current state: " + NetworkingClient.LoadBalancingPeer.PeerState);
  1088. return false;
  1089. }
  1090. if (PhotonHandler.AppQuits)
  1091. {
  1092. Debug.LogWarning("Can't connect: Application is closing. Unity called OnApplicationQuit().");
  1093. return false;
  1094. }
  1095. if (OfflineMode)
  1096. {
  1097. OfflineMode = false; // Cleanup offline mode
  1098. Debug.LogWarning("ConnectToMaster() disabled the offline mode. No longer offline.");
  1099. }
  1100. if (!IsMessageQueueRunning)
  1101. {
  1102. IsMessageQueueRunning = true;
  1103. Debug.LogWarning("ConnectToMaster() enabled IsMessageQueueRunning. Needs to be able to dispatch incoming messages.");
  1104. }
  1105. SetupLogging();
  1106. ConnectMethod = ConnectMethod.ConnectToMaster;
  1107. NetworkingClient.IsUsingNameServer = false;
  1108. NetworkingClient.MasterServerAddress = (port == 0) ? masterServerAddress : masterServerAddress + ":" + port;
  1109. NetworkingClient.AppId = appID;
  1110. return NetworkingClient.ConnectToMasterServer();
  1111. }
  1112. /// <summary>
  1113. /// Connect to the Photon Cloud region with the lowest ping (on platforms that support Unity's Ping).
  1114. /// </summary>
  1115. /// <remarks>
  1116. /// Will save the result of pinging all cloud servers in PlayerPrefs. Calling this the first time can take +-2 seconds.
  1117. /// The ping result can be overridden via PhotonNetwork.OverrideBestCloudServer(..)
  1118. /// This call can take up to 2 seconds if it is the first time you are using this, all cloud servers will be pinged to check for the best region.
  1119. ///
  1120. /// The PUN Setup Wizard stores your appID in a settings file and applies a server address/port.
  1121. /// To connect to the Photon Cloud, a valid AppId must be in the settings file (shown in the Photon Cloud Dashboard).
  1122. /// https://dashboard.photonengine.com
  1123. ///
  1124. /// Connecting to the Photon Cloud might fail due to:
  1125. /// - Invalid AppId
  1126. /// - Network issues
  1127. /// - Invalid region
  1128. /// - Subscription CCU limit reached
  1129. /// - etc.
  1130. ///
  1131. /// In general check out the <see cref="DisconnectCause"/> from the <see cref="IConnectionCallbacks.OnDisconnected"/> callback.
  1132. /// </remarks>
  1133. /// <returns>If this client is going to connect to cloud server based on ping. Even if true, this does not guarantee a connection but the attempt is being made.</returns>
  1134. public static bool ConnectToBestCloudServer()
  1135. {
  1136. if (NetworkingClient.LoadBalancingPeer.PeerState != PeerStateValue.Disconnected)
  1137. {
  1138. Debug.LogWarning("ConnectToBestCloudServer() failed. Can only connect while in state 'Disconnected'. Current state: " + NetworkingClient.LoadBalancingPeer.PeerState);
  1139. return false;
  1140. }
  1141. if (PhotonHandler.AppQuits)
  1142. {
  1143. Debug.LogWarning("Can't connect: Application is closing. Unity called OnApplicationQuit().");
  1144. return false;
  1145. }
  1146. SetupLogging();
  1147. ConnectMethod = ConnectMethod.ConnectToBest;
  1148. // Connecting to "Best Region" begins with connecting to the Name Server.
  1149. bool couldConnect = PhotonNetwork.NetworkingClient.ConnectToNameServer();
  1150. return couldConnect;
  1151. }
  1152. /// <summary>
  1153. /// Connects to the Photon Cloud region of choice.
  1154. /// </summary>
  1155. /// <remarks>
  1156. /// It's typically enough to define the region code ("eu", "us", etc).
  1157. /// Connecting to a specific cluster may be necessary, when regions get sharded and you support friends / invites.
  1158. ///
  1159. /// In all other cases, you should not define a cluster as this allows the Name Server to distribute
  1160. /// clients as needed. A random, load balanced cluster will be selected.
  1161. ///
  1162. /// The Name Server has the final say to assign a cluster as available.
  1163. /// If the requested cluster is not available another will be assigned.
  1164. ///
  1165. /// Once connected, check the value of CurrentCluster.
  1166. /// </remarks>
  1167. public static bool ConnectToRegion(string region)
  1168. {
  1169. if (NetworkingClient.LoadBalancingPeer.PeerState != PeerStateValue.Disconnected && NetworkingClient.Server != ServerConnection.NameServer)
  1170. {
  1171. Debug.LogWarning("ConnectToRegion() failed. Can only connect while in state 'Disconnected'. Current state: " + NetworkingClient.LoadBalancingPeer.PeerState);
  1172. return false;
  1173. }
  1174. if (PhotonHandler.AppQuits)
  1175. {
  1176. Debug.LogWarning("Can't connect: Application is closing. Unity called OnApplicationQuit().");
  1177. return false;
  1178. }
  1179. SetupLogging();
  1180. ConnectMethod = ConnectMethod.ConnectToRegion;
  1181. if (!string.IsNullOrEmpty(region))
  1182. {
  1183. return NetworkingClient.ConnectToRegionMaster(region);
  1184. }
  1185. return false;
  1186. }
  1187. /// <summary>
  1188. /// Makes this client disconnect from the photon server, a process that leaves any room and calls OnDisconnected on completion.
  1189. /// </summary>
  1190. /// <remarks>
  1191. /// When you disconnect, the client will send a "disconnecting" message to the server. This speeds up leave/disconnect
  1192. /// messages for players in the same room as you (otherwise the server would timeout this client's connection).
  1193. /// When used in OfflineMode, the state-change and event-call OnDisconnected are immediate.
  1194. /// Offline mode is set to false as well.
  1195. /// Once disconnected, the client can connect again. Use ConnectUsingSettings.
  1196. /// </remarks>
  1197. public static void Disconnect()
  1198. {
  1199. if (OfflineMode)
  1200. {
  1201. OfflineMode = false;
  1202. offlineModeRoom = null;
  1203. NetworkingClient.State = ClientState.Disconnecting;
  1204. NetworkingClient.OnStatusChanged(StatusCode.Disconnect);
  1205. return;
  1206. }
  1207. if (NetworkingClient == null)
  1208. {
  1209. return; // Suppress error when quitting playmode in the editor
  1210. }
  1211. NetworkingClient.Disconnect();
  1212. }
  1213. /// <summary>Can be used to reconnect to the master server after a disconnect.</summary>
  1214. /// <remarks>
  1215. /// After losing connection, you can use this to connect a client to the region Master Server again.
  1216. /// Cache the room name you're in and use RejoinRoom(roomname) to return to a game.
  1217. /// Common use case: Press the Lock Button on a iOS device and you get disconnected immediately.
  1218. /// </remarks>
  1219. public static bool Reconnect()
  1220. {
  1221. if (string.IsNullOrEmpty(NetworkingClient.MasterServerAddress))
  1222. {
  1223. Debug.LogWarning("Reconnect() failed. It seems the client wasn't connected before?! Current state: " + NetworkingClient.LoadBalancingPeer.PeerState);
  1224. return false;
  1225. }
  1226. if (NetworkingClient.LoadBalancingPeer.PeerState != PeerStateValue.Disconnected)
  1227. {
  1228. Debug.LogWarning("Reconnect() failed. Can only connect while in state 'Disconnected'. Current state: " + NetworkingClient.LoadBalancingPeer.PeerState);
  1229. return false;
  1230. }
  1231. if (OfflineMode)
  1232. {
  1233. OfflineMode = false; // Cleanup offline mode
  1234. Debug.LogWarning("Reconnect() disabled the offline mode. No longer offline.");
  1235. }
  1236. if (!IsMessageQueueRunning)
  1237. {
  1238. IsMessageQueueRunning = true;
  1239. Debug.LogWarning("Reconnect() enabled IsMessageQueueRunning. Needs to be able to dispatch incoming messages.");
  1240. }
  1241. NetworkingClient.IsUsingNameServer = false;
  1242. return NetworkingClient.ReconnectToMaster();
  1243. }
  1244. /// <summary>
  1245. /// Resets the traffic stats and re-enables them.
  1246. /// </summary>
  1247. public static void NetworkStatisticsReset()
  1248. {
  1249. NetworkingClient.LoadBalancingPeer.TrafficStatsReset();
  1250. }
  1251. /// <summary>
  1252. /// Only available when NetworkStatisticsEnabled was used to gather some stats.
  1253. /// </summary>
  1254. /// <returns>A string with vital networking statistics.</returns>
  1255. public static string NetworkStatisticsToString()
  1256. {
  1257. if (NetworkingClient == null || OfflineMode)
  1258. {
  1259. return "Offline or in OfflineMode. No VitalStats available.";
  1260. }
  1261. return NetworkingClient.LoadBalancingPeer.VitalStatsToString(false);
  1262. }
  1263. /// <summary>
  1264. /// Helper function which is called inside this class to erify if certain functions can be used (e.g. RPC when not connected)
  1265. /// </summary>
  1266. /// <returns></returns>
  1267. private static bool VerifyCanUseNetwork()
  1268. {
  1269. if (IsConnected)
  1270. {
  1271. return true;
  1272. }
  1273. Debug.LogError("Cannot send messages when not connected. Either connect to Photon OR use offline mode!");
  1274. return false;
  1275. }
  1276. /// <summary>
  1277. /// The current roundtrip time to the photon server.
  1278. /// </summary>
  1279. /// <returns>Roundtrip time (to server and back).</returns>
  1280. public static int GetPing()
  1281. {
  1282. return NetworkingClient.LoadBalancingPeer.RoundTripTime;
  1283. }
  1284. /// <summary>Refreshes the server timestamp (async operation, takes a roundtrip).</summary>
  1285. /// <remarks>Can be useful if a bad connection made the timestamp unusable or imprecise.</remarks>
  1286. public static void FetchServerTimestamp()
  1287. {
  1288. if (NetworkingClient != null)
  1289. {
  1290. NetworkingClient.LoadBalancingPeer.FetchServerTimestamp();
  1291. }
  1292. }
  1293. /// <summary>
  1294. /// Can be used to immediately send the RPCs and Instantiates just called, so they are on their way to the other players.
  1295. /// </summary>
  1296. /// <remarks>
  1297. /// This could be useful if you do a RPC to load a level and then load it yourself.
  1298. /// While loading, no RPCs are sent to others, so this would delay the "load" RPC.
  1299. /// You can send the RPC to "others", use this method, disable the message queue
  1300. /// (by IsMessageQueueRunning) and then load.
  1301. /// </remarks>
  1302. public static void SendAllOutgoingCommands()
  1303. {
  1304. if (!VerifyCanUseNetwork())
  1305. {
  1306. return;
  1307. }
  1308. while (NetworkingClient.LoadBalancingPeer.SendOutgoingCommands())
  1309. {
  1310. }
  1311. }
  1312. /// <summary>Request a client to disconnect/kick, which happens if EnableCloseConnection is set to true. Only the master client can do this.</summary>
  1313. /// <remarks>Only the target player gets this event. That player will disconnect if EnableCloseConnection = true.</remarks>
  1314. /// <param name="kickPlayer">The Player to kick.</param>
  1315. public static bool CloseConnection(Player kickPlayer)
  1316. {
  1317. if (!VerifyCanUseNetwork())
  1318. {
  1319. return false;
  1320. }
  1321. if (!PhotonNetwork.EnableCloseConnection)
  1322. {
  1323. Debug.LogError("CloseConnection is disabled. No need to call it.");
  1324. return false;
  1325. }
  1326. if (!LocalPlayer.IsMasterClient)
  1327. {
  1328. Debug.LogError("CloseConnection: Only the masterclient can kick another player.");
  1329. return false;
  1330. }
  1331. if (kickPlayer == null)
  1332. {
  1333. Debug.LogError("CloseConnection: No such player connected!");
  1334. return false;
  1335. }
  1336. RaiseEventOptions options = new RaiseEventOptions() { TargetActors = new int[] { kickPlayer.ActorNumber } };
  1337. return NetworkingClient.OpRaiseEvent(PunEvent.CloseConnection, null, options, SendOptions.SendReliable);
  1338. }
  1339. /// <summary>
  1340. /// Asks the server to assign another player as Master Client of your current room.
  1341. /// </summary>
  1342. /// <remarks>
  1343. /// RPCs and RaiseEvent have the option to send messages only to the Master Client of a room.
  1344. /// SetMasterClient affects which client gets those messages.
  1345. ///
  1346. /// This method calls an operation on the server to set a new Master Client, which takes a roundtrip.
  1347. /// In case of success, this client and the others get the new Master Client from the server.
  1348. ///
  1349. /// SetMasterClient tells the server which current Master Client should be replaced with the new one.
  1350. /// It will fail, if anything switches the Master Client moments earlier. There is no callback for this
  1351. /// error. All clients should get the new Master Client assigned by the server anyways.
  1352. ///
  1353. /// See also: PhotonNetwork.MasterClient
  1354. ///
  1355. /// On v3 servers:
  1356. /// The ReceiverGroup.MasterClient (usable in RPCs) is not affected by this (still points to lowest player.ID in room).
  1357. /// Avoid using this enum value (and send to a specific player instead).
  1358. ///
  1359. /// If the current Master Client leaves, PUN will detect a new one by "lowest player ID". Implement OnMasterClientSwitched
  1360. /// to get a callback in this case. The PUN-selected Master Client might assign a new one.
  1361. ///
  1362. /// Make sure you don't create an endless loop of Master-assigning! When selecting a custom Master Client, all clients
  1363. /// should point to the same player, no matter who actually assigns this player.
  1364. ///
  1365. /// Locally the Master Client is immediately switched, while remote clients get an event. This means the game
  1366. /// is tempoarily without Master Client like when a current Master Client leaves.
  1367. ///
  1368. /// When switching the Master Client manually, keep in mind that this user might leave and not do it's work, just like
  1369. /// any Master Client.
  1370. ///
  1371. /// </remarks>
  1372. /// <param name="masterClientPlayer">The player to become the next Master Client.</param>
  1373. /// <returns>False when this operation couldn't be done. Must be in a room (not in OfflineMode).</returns>
  1374. public static bool SetMasterClient(Player masterClientPlayer)
  1375. {
  1376. if (!InRoom || !VerifyCanUseNetwork() || OfflineMode)
  1377. {
  1378. if (LogLevel == PunLogLevel.Informational) Debug.Log("Can not SetMasterClient(). Not in room or in OfflineMode.");
  1379. return false;
  1380. }
  1381. return CurrentRoom.SetMasterClient(masterClientPlayer);
  1382. }
  1383. /// <summary>
  1384. /// Joins a random room that matches the filter. Will callback: OnJoinedRoom or OnJoinRandomFailed.
  1385. /// </summary>
  1386. /// <remarks>
  1387. /// Used for random matchmaking. You can join any room or one with specific properties defined in opJoinRandomRoomParams.
  1388. ///
  1389. /// This operation fails if no rooms are fitting or available (all full, closed, in another lobby or not visible).
  1390. /// It may also fail when actually joining the room which was found. Rooms may close, become full or empty anytime.
  1391. ///
  1392. /// This method can only be called while the client is connected to a Master Server so you should
  1393. /// implement the callback OnConnectedToMaster.
  1394. /// Check the return value to make sure the operation will be called on the server.
  1395. /// Note: There will be no callbacks if this method returned false.
  1396. ///
  1397. /// More about PUN matchmaking:
  1398. /// https://doc.photonengine.com/en-us/pun/v2/lobby-and-matchmaking/matchmaking-and-lobby
  1399. /// </remarks>
  1400. public static bool JoinRandomRoom()
  1401. {
  1402. return JoinRandomRoom(null, 0, MatchmakingMode.FillRoom, null, null);
  1403. }
  1404. /// <summary>
  1405. /// Joins a random room that matches the filter. Will callback: OnJoinedRoom or OnJoinRandomFailed.
  1406. /// </summary>
  1407. /// <remarks>
  1408. /// Used for random matchmaking. You can join any room or one with specific properties defined in opJoinRandomRoomParams.
  1409. ///
  1410. /// This operation fails if no rooms are fitting or available (all full, closed, in another lobby or not visible).
  1411. /// It may also fail when actually joining the room which was found. Rooms may close, become full or empty anytime.
  1412. ///
  1413. /// This method can only be called while the client is connected to a Master Server so you should
  1414. /// implement the callback OnConnectedToMaster.
  1415. /// Check the return value to make sure the operation will be called on the server.
  1416. /// Note: There will be no callbacks if this method returned false.
  1417. ///
  1418. /// More about PUN matchmaking:
  1419. /// https://doc.photonengine.com/en-us/pun/v2/lobby-and-matchmaking/matchmaking-and-lobby
  1420. /// </remarks>
  1421. /// <param name="expectedCustomRoomProperties">Filters for rooms that match these custom properties (string keys and values). To ignore, pass null.</param>
  1422. /// <param name="expectedMaxPlayers">Filters for a particular maxplayer setting. Use 0 to accept any maxPlayer value.</param>
  1423. /// <returns>If the operation got queued and will be sent.</returns>
  1424. public static bool JoinRandomRoom(Hashtable expectedCustomRoomProperties, byte expectedMaxPlayers)
  1425. {
  1426. return JoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers, MatchmakingMode.FillRoom, null, null);
  1427. }
  1428. /// <summary>
  1429. /// Joins a random room that matches the filter. Will callback: OnJoinedRoom or OnJoinRandomFailed.
  1430. /// </summary>
  1431. /// <remarks>
  1432. /// Used for random matchmaking. You can join any room or one with specific properties defined in opJoinRandomRoomParams.
  1433. ///
  1434. /// This operation fails if no rooms are fitting or available (all full, closed, in another lobby or not visible).
  1435. /// It may also fail when actually joining the room which was found. Rooms may close, become full or empty anytime.
  1436. ///
  1437. /// This method can only be called while the client is connected to a Master Server so you should
  1438. /// implement the callback OnConnectedToMaster.
  1439. /// Check the return value to make sure the operation will be called on the server.
  1440. /// Note: There will be no callbacks if this method returned false.
  1441. ///
  1442. /// More about PUN matchmaking:
  1443. /// https://doc.photonengine.com/en-us/pun/v2/lobby-and-matchmaking/matchmaking-and-lobby
  1444. /// </remarks>
  1445. /// <param name="expectedCustomRoomProperties">Filters for rooms that match these custom properties (string keys and values). To ignore, pass null.</param>
  1446. /// <param name="expectedMaxPlayers">Filters for a particular maxplayer setting. Use 0 to accept any maxPlayer value.</param>
  1447. /// <param name="matchingType">Selects one of the available matchmaking algorithms. See MatchmakingMode enum for options.</param>
  1448. /// <param name="typedLobby">The lobby in which you want to lookup a room. Pass null, to use the default lobby. This does not join that lobby and neither sets the lobby property.</param>
  1449. /// <param name="sqlLobbyFilter">A filter-string for SQL-typed lobbies.</param>
  1450. /// <param name="expectedUsers">Optional list of users (by UserId) who are expected to join this game and who you want to block a slot for.</param>
  1451. /// <returns>If the operation got queued and will be sent.</returns>
  1452. public static bool JoinRandomRoom(Hashtable expectedCustomRoomProperties, byte expectedMaxPlayers, MatchmakingMode matchingType, TypedLobby typedLobby, string sqlLobbyFilter, string[] expectedUsers = null)
  1453. {
  1454. if (OfflineMode)
  1455. {
  1456. if (offlineModeRoom != null)
  1457. {
  1458. Debug.LogError("JoinRandomRoom failed. In offline mode you still have to leave a room to enter another.");
  1459. return false;
  1460. }
  1461. EnterOfflineRoom("offline room", null, true);
  1462. return true;
  1463. }
  1464. if (NetworkingClient.Server != ServerConnection.MasterServer || !IsConnectedAndReady)
  1465. {
  1466. Debug.LogError("JoinRandomRoom failed. Client is on "+ NetworkingClient.Server+ " (must be Master Server for matchmaking)" + (IsConnectedAndReady ? " and ready" : " but not ready for operations (State: "+ NetworkingClient.State + ")") + ". Wait for callback: OnJoinedLobby or OnConnectedToMaster.");
  1467. return false;
  1468. }
  1469. typedLobby = typedLobby ?? ((NetworkingClient.InLobby) ? NetworkingClient.CurrentLobby : null); // use given lobby, or active lobby (if any active) or none
  1470. OpJoinRandomRoomParams opParams = new OpJoinRandomRoomParams();
  1471. opParams.ExpectedCustomRoomProperties = expectedCustomRoomProperties;
  1472. opParams.ExpectedMaxPlayers = expectedMaxPlayers;
  1473. opParams.MatchingType = matchingType;
  1474. opParams.TypedLobby = typedLobby;
  1475. opParams.SqlLobbyFilter = sqlLobbyFilter;
  1476. opParams.ExpectedUsers = expectedUsers;
  1477. return NetworkingClient.OpJoinRandomRoom(opParams);
  1478. }
  1479. /// <summary>
  1480. /// Attempts to join a room that matches the specified filter and creates a room if none found.
  1481. /// </summary>
  1482. /// <remarks>
  1483. /// This operation is a combination of filter-based random matchmaking with the option to create a new room,
  1484. /// if no fitting room exists.
  1485. /// The benefit of that is that the room creation is done by the same operation and the room can be found
  1486. /// by the very next client, looking for similar rooms.
  1487. ///
  1488. /// There are separate parameters for joining and creating a room.
  1489. ///
  1490. /// This method can only be called while connected to a Master Server.
  1491. /// This client's State is set to ClientState.Joining immediately.
  1492. ///
  1493. /// Either IMatchmakingCallbacks.OnJoinedRoom or IMatchmakingCallbacks.OnCreatedRoom gets called.
  1494. ///
  1495. /// Should the creation on the Master Server, IMatchmakingCallbacks.OnJoinRandomFailed gets called.
  1496. /// Should the "join" on the Game Server fail, IMatchmakingCallbacks.OnJoinRoomFailed gets called.
  1497. ///
  1498. ///
  1499. /// Check the return value to make sure the operation will be called on the server.
  1500. /// Note: There will be no callbacks if this method returned false.
  1501. /// </remarks>
  1502. /// <returns>If the operation will be sent (requires connection to Master Server).</returns>
  1503. public static bool JoinRandomOrCreateRoom(Hashtable expectedCustomRoomProperties = null, byte expectedMaxPlayers = 0, MatchmakingMode matchingType = MatchmakingMode.FillRoom, TypedLobby typedLobby = null, string sqlLobbyFilter = null, string roomName = null, RoomOptions roomOptions = null, string[] expectedUsers = null)
  1504. {
  1505. if (OfflineMode)
  1506. {
  1507. if (offlineModeRoom != null)
  1508. {
  1509. Debug.LogError("JoinRandomOrCreateRoom failed. In offline mode you still have to leave a room to enter another.");
  1510. return false;
  1511. }
  1512. EnterOfflineRoom("offline room", null, true);
  1513. return true;
  1514. }
  1515. if (NetworkingClient.Server != ServerConnection.MasterServer || !IsConnectedAndReady)
  1516. {
  1517. Debug.LogError("JoinRandomOrCreateRoom failed. Client is on "+ NetworkingClient.Server+ " (must be Master Server for matchmaking)" + (IsConnectedAndReady ? " and ready" : " but not ready for operations (State: "+ NetworkingClient.State + ")") + ". Wait for callback: OnJoinedLobby or OnConnectedToMaster.");
  1518. return false;
  1519. }
  1520. typedLobby = typedLobby ?? ((NetworkingClient.InLobby) ? NetworkingClient.CurrentLobby : null); // use given lobby, or active lobby (if any active) or none
  1521. OpJoinRandomRoomParams opParams = new OpJoinRandomRoomParams();
  1522. opParams.ExpectedCustomRoomProperties = expectedCustomRoomProperties;
  1523. opParams.ExpectedMaxPlayers = expectedMaxPlayers;
  1524. opParams.MatchingType = matchingType;
  1525. opParams.TypedLobby = typedLobby;
  1526. opParams.SqlLobbyFilter = sqlLobbyFilter;
  1527. opParams.ExpectedUsers = expectedUsers;
  1528. EnterRoomParams enterRoomParams = new EnterRoomParams();
  1529. enterRoomParams.RoomName = roomName;
  1530. enterRoomParams.RoomOptions = roomOptions;
  1531. enterRoomParams.Lobby = typedLobby;
  1532. enterRoomParams.ExpectedUsers = expectedUsers;
  1533. return NetworkingClient.OpJoinRandomOrCreateRoom(opParams, enterRoomParams);
  1534. }
  1535. /// <summary>
  1536. /// Creates a new room. Will callback: OnCreatedRoom and OnJoinedRoom or OnCreateRoomFailed.
  1537. /// </summary>
  1538. /// <remarks>
  1539. /// When successful, this calls the callbacks OnCreatedRoom and OnJoinedRoom (the latter, cause you join as first player).
  1540. /// In all error cases, OnCreateRoomFailed gets called.
  1541. ///
  1542. /// Creating a room will fail if the room name is already in use or when the RoomOptions clashing
  1543. /// with one another. Check the EnterRoomParams reference for the various room creation options.
  1544. ///
  1545. /// If you don't want to create a unique room-name, pass null or "" as name and the server will assign a roomName (a GUID as string).
  1546. ///
  1547. /// This method can only be called while the client is connected to a Master Server so you should
  1548. /// implement the callback OnConnectedToMaster.
  1549. /// Check the return value to make sure the operation will be called on the server.
  1550. /// Note: There will be no callbacks if this method returned false.
  1551. ///
  1552. /// More about PUN matchmaking:
  1553. /// https://doc.photonengine.com/en-us/pun/v2/lobby-and-matchmaking/matchmaking-and-lobby
  1554. /// </remarks>
  1555. /// <param name="roomName">Unique name of the room to create. Pass null or "" to make the server generate a name.</param>
  1556. /// <param name="roomOptions">Common options for the room like MaxPlayers, initial custom room properties and similar. See RoomOptions type..</param>
  1557. /// <param name="typedLobby">If null, the room is automatically created in the currently used lobby (which is "default" when you didn't join one explicitly).</param>
  1558. /// <param name="expectedUsers">Optional list of users (by UserId) who are expected to join this game and who you want to block a slot for.</param>
  1559. /// <returns>If the operation got queued and will be sent.</returns>
  1560. public static bool CreateRoom(string roomName, RoomOptions roomOptions = null, TypedLobby typedLobby = null, string[] expectedUsers = null)
  1561. {
  1562. if (OfflineMode)
  1563. {
  1564. if (offlineModeRoom != null)
  1565. {
  1566. Debug.LogError("CreateRoom failed. In offline mode you still have to leave a room to enter another.");
  1567. return false;
  1568. }
  1569. EnterOfflineRoom(roomName, roomOptions, true);
  1570. return true;
  1571. }
  1572. if (NetworkingClient.Server != ServerConnection.MasterServer || !IsConnectedAndReady)
  1573. {
  1574. Debug.LogError("CreateRoom failed. Client is on " + NetworkingClient.Server + " (must be Master Server for matchmaking)" + (IsConnectedAndReady ? " and ready" : "but not ready for operations (State: " + NetworkingClient.State + ")") + ". Wait for callback: OnJoinedLobby or OnConnectedToMaster.");
  1575. return false;
  1576. }
  1577. typedLobby = typedLobby ?? ((NetworkingClient.InLobby) ? NetworkingClient.CurrentLobby : null); // use given lobby, or active lobby (if any active) or none
  1578. EnterRoomParams opParams = new EnterRoomParams();
  1579. opParams.RoomName = roomName;
  1580. opParams.RoomOptions = roomOptions;
  1581. opParams.Lobby = typedLobby;
  1582. opParams.ExpectedUsers = expectedUsers;
  1583. return NetworkingClient.OpCreateRoom(opParams);
  1584. }
  1585. /// <summary>
  1586. /// Joins a specific room by name and creates it on demand. Will callback: OnJoinedRoom or OnJoinRoomFailed.
  1587. /// </summary>
  1588. /// <remarks>
  1589. /// Useful when players make up a room name to meet in:
  1590. /// All involved clients call the same method and whoever is first, also creates the room.
  1591. ///
  1592. /// When successful, the client will enter the specified room.
  1593. /// The client which creates the room, will callback both OnCreatedRoom and OnJoinedRoom.
  1594. /// Clients that join an existing room will only callback OnJoinedRoom.
  1595. /// In all error cases, OnJoinRoomFailed gets called.
  1596. ///
  1597. /// Joining a room will fail, if the room is full, closed or when the user
  1598. /// already is present in the room (checked by userId).
  1599. ///
  1600. /// To return to a room, use OpRejoinRoom.
  1601. ///
  1602. /// This method can only be called while the client is connected to a Master Server so you should
  1603. /// implement the callback OnConnectedToMaster.
  1604. /// Check the return value to make sure the operation will be called on the server.
  1605. /// Note: There will be no callbacks if this method returned false.
  1606. ///
  1607. ///
  1608. /// If you set room properties in roomOptions, they get ignored when the room is existing already.
  1609. /// This avoids changing the room properties by late joining players.
  1610. ///
  1611. /// You can define an array of expectedUsers, to block player slots in the room for these users.
  1612. /// The corresponding feature in Photon is called "Slot Reservation" and can be found in the doc pages.
  1613. ///
  1614. ///
  1615. /// More about PUN matchmaking:
  1616. /// https://doc.photonengine.com/en-us/pun/v2/lobby-and-matchmaking/matchmaking-and-lobby
  1617. /// </remarks>
  1618. /// <param name="roomName">Name of the room to join. Must be non null.</param>
  1619. /// <param name="roomOptions">Options for the room, in case it does not exist yet. Else these values are ignored.</param>
  1620. /// <param name="typedLobby">Lobby you want a new room to be listed in. Ignored if the room was existing and got joined.</param>
  1621. /// <param name="expectedUsers">Optional list of users (by UserId) who are expected to join this game and who you want to block a slot for.</param>
  1622. /// <returns>If the operation got queued and will be sent.</returns>
  1623. public static bool JoinOrCreateRoom(string roomName, RoomOptions roomOptions, TypedLobby typedLobby, string[] expectedUsers = null)
  1624. {
  1625. if (OfflineMode)
  1626. {
  1627. if (offlineModeRoom != null)
  1628. {
  1629. Debug.LogError("JoinOrCreateRoom failed. In offline mode you still have to leave a room to enter another.");
  1630. return false;
  1631. }
  1632. EnterOfflineRoom(roomName, roomOptions, true); // in offline mode, JoinOrCreateRoom assumes you create the room
  1633. return true;
  1634. }
  1635. if (NetworkingClient.Server != ServerConnection.MasterServer || !IsConnectedAndReady)
  1636. {
  1637. Debug.LogError("JoinOrCreateRoom failed. Client is on " + NetworkingClient.Server + " (must be Master Server for matchmaking)" + (IsConnectedAndReady ? " and ready" : "but not ready for operations (State: " + NetworkingClient.State + ")") + ". Wait for callback: OnJoinedLobby or OnConnectedToMaster.");
  1638. return false;
  1639. }
  1640. if (string.IsNullOrEmpty(roomName))
  1641. {
  1642. Debug.LogError("JoinOrCreateRoom failed. A roomname is required. If you don't know one, how will you join?");
  1643. return false;
  1644. }
  1645. typedLobby = typedLobby ?? ((NetworkingClient.InLobby) ? NetworkingClient.CurrentLobby : null); // use given lobby, or active lobby (if any active) or none
  1646. EnterRoomParams opParams = new EnterRoomParams();
  1647. opParams.RoomName = roomName;
  1648. opParams.RoomOptions = roomOptions;
  1649. opParams.Lobby = typedLobby;
  1650. opParams.PlayerProperties = LocalPlayer.CustomProperties;
  1651. opParams.ExpectedUsers = expectedUsers;
  1652. return NetworkingClient.OpJoinOrCreateRoom(opParams);
  1653. }
  1654. /// <summary>
  1655. /// Joins a room by name. Will callback: OnJoinedRoom or OnJoinRoomFailed.
  1656. /// </summary>
  1657. /// <remarks>
  1658. /// Useful when using lobbies or when players follow friends or invite each other.
  1659. ///
  1660. /// When successful, the client will enter the specified room and callback via OnJoinedRoom.
  1661. /// In all error cases, OnJoinRoomFailed gets called.
  1662. ///
  1663. /// Joining a room will fail if the room is full, closed, not existing or when the user
  1664. /// already is present in the room (checked by userId).
  1665. ///
  1666. /// To return to a room, use OpRejoinRoom.
  1667. /// When players invite each other and it's unclear who's first to respond, use OpJoinOrCreateRoom instead.
  1668. ///
  1669. /// This method can only be called while the client is connected to a Master Server so you should
  1670. /// implement the callback OnConnectedToMaster.
  1671. /// Check the return value to make sure the operation will be called on the server.
  1672. /// Note: There will be no callbacks if this method returned false.
  1673. ///
  1674. ///
  1675. /// More about PUN matchmaking:
  1676. /// https://doc.photonengine.com/en-us/pun/v2/lobby-and-matchmaking/matchmaking-and-lobby
  1677. /// </remarks>
  1678. /// <see cref="OnJoinRoomFailed"/>
  1679. /// <see cref="OnJoinedRoom"/>
  1680. /// <param name="roomName">Unique name of the room to join.</param>
  1681. /// <param name="expectedUsers">Optional list of users (by UserId) who are expected to join this game and who you want to block a slot for.</param>
  1682. /// <returns>If the operation got queued and will be sent.</returns>
  1683. public static bool JoinRoom(string roomName, string[] expectedUsers = null)
  1684. {
  1685. if (OfflineMode)
  1686. {
  1687. if (offlineModeRoom != null)
  1688. {
  1689. Debug.LogError("JoinRoom failed. In offline mode you still have to leave a room to enter another.");
  1690. return false;
  1691. }
  1692. EnterOfflineRoom(roomName, null, true);
  1693. return true;
  1694. }
  1695. if (NetworkingClient.Server != ServerConnection.MasterServer || !IsConnectedAndReady)
  1696. {
  1697. Debug.LogError("JoinRoom failed. Client is on " + NetworkingClient.Server + " (must be Master Server for matchmaking)" + (IsConnectedAndReady ? " and ready" : "but not ready for operations (State: " + NetworkingClient.State + ")") + ". Wait for callback: OnJoinedLobby or OnConnectedToMaster.");
  1698. return false;
  1699. }
  1700. if (string.IsNullOrEmpty(roomName))
  1701. {
  1702. Debug.LogError("JoinRoom failed. A roomname is required. If you don't know one, how will you join?");
  1703. return false;
  1704. }
  1705. EnterRoomParams opParams = new EnterRoomParams();
  1706. opParams.RoomName = roomName;
  1707. opParams.ExpectedUsers = expectedUsers;
  1708. return NetworkingClient.OpJoinRoom(opParams);
  1709. }
  1710. /// <summary>
  1711. /// Rejoins a room by roomName (using the userID internally to return). Will callback: OnJoinedRoom or OnJoinRoomFailed.
  1712. /// </summary>
  1713. /// <remarks>
  1714. /// After losing connection, you might be able to return to a room and continue playing,
  1715. /// if the client is reconnecting fast enough. Use Reconnect() and this method.
  1716. /// Cache the room name you're in and use RejoinRoom(roomname) to return to a game.
  1717. ///
  1718. /// Note: To be able to Rejoin any room, you need to use UserIDs!
  1719. /// You also need to set RoomOptions.PlayerTtl.
  1720. ///
  1721. /// <b>Important: Instantiate() and use of RPCs is not yet supported.</b>
  1722. /// The ownership rules of PhotonViews prevent a seamless return to a game, if you use PhotonViews.
  1723. /// Use Custom Properties and RaiseEvent with event caching instead.
  1724. ///
  1725. /// Common use case: Press the Lock Button on a iOS device and you get disconnected immediately.
  1726. ///
  1727. /// Rejoining room will not send any player properties. Instead client will receive up-to-date ones from server.
  1728. /// If you want to set new player properties, do it once rejoined.
  1729. /// </remarks>
  1730. public static bool RejoinRoom(string roomName)
  1731. {
  1732. if (OfflineMode)
  1733. {
  1734. Debug.LogError("RejoinRoom failed due to offline mode.");
  1735. return false;
  1736. }
  1737. if (NetworkingClient.Server != ServerConnection.MasterServer || !IsConnectedAndReady)
  1738. {
  1739. Debug.LogError("RejoinRoom failed. Client is on " + NetworkingClient.Server + " (must be Master Server for matchmaking)" + (IsConnectedAndReady ? " and ready" : "but not ready for operations (State: " + NetworkingClient.State + ")") + ". Wait for callback: OnJoinedLobby or OnConnectedToMaster.");
  1740. return false;
  1741. }
  1742. if (string.IsNullOrEmpty(roomName))
  1743. {
  1744. Debug.LogError("RejoinRoom failed. A roomname is required. If you don't know one, how will you join?");
  1745. return false;
  1746. }
  1747. return NetworkingClient.OpRejoinRoom(roomName);
  1748. }
  1749. /// <summary>When the client lost connection during gameplay, this method attempts to reconnect and rejoin the room.</summary>
  1750. /// <remarks>
  1751. /// This method re-connects directly to the game server which was hosting the room PUN was in before.
  1752. /// If the room was shut down in the meantime, PUN will call OnJoinRoomFailed and return this client to the Master Server.
  1753. ///
  1754. /// Check the return value, if this client will attempt a reconnect and rejoin (if the conditions are met).
  1755. /// If ReconnectAndRejoin returns false, you can still attempt a Reconnect and Rejoin.
  1756. ///
  1757. /// Similar to PhotonNetwork.RejoinRoom, this requires you to use unique IDs per player (the UserID).
  1758. ///
  1759. /// Rejoining room will not send any player properties. Instead client will receive up-to-date ones from server.
  1760. /// If you want to set new player properties, do it once rejoined.
  1761. /// </remarks>
  1762. /// <returns>False, if there is no known room or game server to return to. Then, this client does not attempt the ReconnectAndRejoin.</returns>
  1763. public static bool ReconnectAndRejoin()
  1764. {
  1765. if (NetworkingClient.LoadBalancingPeer.PeerState != PeerStateValue.Disconnected)
  1766. {
  1767. Debug.LogWarning("ReconnectAndRejoin() failed. Can only connect while in state 'Disconnected'. Current state: " + NetworkingClient.LoadBalancingPeer.PeerState);
  1768. return false;
  1769. }
  1770. if (OfflineMode)
  1771. {
  1772. OfflineMode = false; // Cleanup offline mode
  1773. Debug.LogWarning("ReconnectAndRejoin() disabled the offline mode. No longer offline.");
  1774. }
  1775. if (!IsMessageQueueRunning)
  1776. {
  1777. IsMessageQueueRunning = true;
  1778. Debug.LogWarning("ReconnectAndRejoin() enabled IsMessageQueueRunning. Needs to be able to dispatch incoming messages.");
  1779. }
  1780. return NetworkingClient.ReconnectAndRejoin();
  1781. }
  1782. /// <summary>Leave the current room and return to the Master Server where you can join or create rooms (see remarks).</summary>
  1783. /// <remarks>
  1784. /// This will clean up all (network) GameObjects with a PhotonView, unless you changed autoCleanUp to false.
  1785. /// Returns to the Master Server.
  1786. ///
  1787. /// In OfflineMode, the local "fake" room gets cleaned up and OnLeftRoom gets called immediately.
  1788. ///
  1789. /// In a room with playerTTL &lt; 0, LeaveRoom just turns a client inactive. The player stays in the room's player list
  1790. /// and can return later on. Setting becomeInactive to false deliberately, means to "abandon" the room, despite the
  1791. /// playerTTL allowing you to come back.
  1792. ///
  1793. /// In a room with playerTTL == 0, become inactive has no effect (clients are removed from the room right away).
  1794. /// </remarks>
  1795. /// <param name="becomeInactive">If this client becomes inactive in a room with playerTTL &lt; 0. Defaults to true.</param>
  1796. public static bool LeaveRoom(bool becomeInactive = true)
  1797. {
  1798. if (OfflineMode)
  1799. {
  1800. offlineModeRoom = null;
  1801. NetworkingClient.MatchMakingCallbackTargets.OnLeftRoom();
  1802. NetworkingClient.ConnectionCallbackTargets.OnConnectedToMaster();
  1803. }
  1804. else
  1805. {
  1806. if (CurrentRoom == null)
  1807. {
  1808. Debug.LogWarning("PhotonNetwork.CurrentRoom is null. You don't have to call LeaveRoom() when you're not in one. State: " + PhotonNetwork.NetworkClientState);
  1809. }
  1810. else
  1811. {
  1812. becomeInactive = becomeInactive && CurrentRoom.PlayerTtl != 0; // in a room with playerTTL == 0, the operation "leave" will never turn a client inactive
  1813. }
  1814. return NetworkingClient.OpLeaveRoom(becomeInactive);
  1815. }
  1816. return true;
  1817. }
  1818. /// <summary>
  1819. /// Internally used helper-method to setup an offline room, the numbers for actor and master-client and to do the callbacks.
  1820. /// </summary>
  1821. private static void EnterOfflineRoom(string roomName, RoomOptions roomOptions, bool createdRoom)
  1822. {
  1823. offlineModeRoom = new Room(roomName, roomOptions, true);
  1824. NetworkingClient.ChangeLocalID(1);
  1825. offlineModeRoom.masterClientId = 1;
  1826. offlineModeRoom.AddPlayer(PhotonNetwork.LocalPlayer);
  1827. offlineModeRoom.LoadBalancingClient = PhotonNetwork.NetworkingClient;
  1828. PhotonNetwork.NetworkingClient.CurrentRoom = offlineModeRoom;
  1829. if (createdRoom)
  1830. {
  1831. NetworkingClient.MatchMakingCallbackTargets.OnCreatedRoom();
  1832. }
  1833. NetworkingClient.MatchMakingCallbackTargets.OnJoinedRoom();
  1834. }
  1835. /// <summary>On MasterServer this joins the default lobby which list rooms currently in use.</summary>
  1836. /// <remarks>
  1837. /// The room list is sent and refreshed by the server using <see cref="ILobbyCallbacks.OnRoomListUpdate"/>.
  1838. ///
  1839. /// Per room you should check if it's full or not before joining. Photon also lists rooms that are
  1840. /// full, unless you close and hide them (room.open = false and room.visible = false).
  1841. ///
  1842. /// In best case, you make your clients join random games, as described here:
  1843. /// https://doc.photonengine.com/en-us/pun/v2/lobby-and-matchmaking/matchmaking-and-lobby
  1844. ///
  1845. ///
  1846. /// You can show your current players and room count without joining a lobby (but you must
  1847. /// be on the master server). Use: CountOfPlayers, CountOfPlayersOnMaster, CountOfPlayersInRooms and
  1848. /// CountOfRooms.
  1849. ///
  1850. /// You can use more than one lobby to keep the room lists shorter. See JoinLobby(TypedLobby lobby).
  1851. /// When creating new rooms, they will be "attached" to the currently used lobby or the default lobby.
  1852. ///
  1853. /// You can use JoinRandomRoom without being in a lobby!
  1854. /// </remarks>
  1855. public static bool JoinLobby()
  1856. {
  1857. return JoinLobby(null);
  1858. }
  1859. /// <summary>On a Master Server you can join a lobby to get lists of available rooms.</summary>
  1860. /// <remarks>
  1861. /// The room list is sent and refreshed by the server using <see cref="ILobbyCallbacks.OnRoomListUpdate"/>.
  1862. ///
  1863. /// Any client can "make up" any lobby on the fly. Splitting rooms into multiple lobbies will
  1864. /// keep each list shorter. However, having too many lists might ruin the matchmaking experience.
  1865. ///
  1866. /// In best case, you create a limited number of lobbies. For example, create a lobby per
  1867. /// game-mode: "koth" for king of the hill and "ffa" for free for all, etc.
  1868. ///
  1869. /// There is no listing of lobbies at the moment.
  1870. ///
  1871. /// Sql-typed lobbies offer a different filtering model for random matchmaking. This might be more
  1872. /// suited for skillbased-games. However, you will also need to follow the conventions for naming
  1873. /// filterable properties in sql-lobbies! Both is explained in the matchmaking doc linked below.
  1874. ///
  1875. /// In best case, you make your clients join random games, as described here:
  1876. /// https://doc.photonengine.com/en-us/realtime/current/reference/matchmaking-and-lobby
  1877. ///
  1878. ///
  1879. /// Per room you should check if it's full or not before joining. Photon does list rooms that are
  1880. /// full, unless you close and hide them (room.open = false and room.visible = false).
  1881. ///
  1882. /// You can show your games current players and room count without joining a lobby (but you must
  1883. /// be on the master server). Use: CountOfPlayers, CountOfPlayersOnMaster, CountOfPlayersInRooms and
  1884. /// CountOfRooms.
  1885. ///
  1886. /// When creating new rooms, they will be "attached" to the currently used lobby or the default lobby.
  1887. ///
  1888. /// You can use JoinRandomRoom without being in a lobby!
  1889. /// </remarks>
  1890. /// <param name="typedLobby">A typed lobby to join (must have name and type).</param>
  1891. public static bool JoinLobby(TypedLobby typedLobby)
  1892. {
  1893. if (PhotonNetwork.IsConnected && PhotonNetwork.Server == ServerConnection.MasterServer)
  1894. {
  1895. return NetworkingClient.OpJoinLobby(typedLobby);
  1896. }
  1897. return false;
  1898. }
  1899. /// <summary>Leave a lobby to stop getting updates about available rooms.</summary>
  1900. /// <remarks>
  1901. /// This does not reset PhotonNetwork.lobby! This allows you to join this particular lobby later
  1902. /// easily.
  1903. ///
  1904. /// The values CountOfPlayers, CountOfPlayersOnMaster, CountOfPlayersInRooms and CountOfRooms
  1905. /// are received even without being in a lobby.
  1906. ///
  1907. /// You can use JoinRandomRoom without being in a lobby.
  1908. /// </remarks>
  1909. public static bool LeaveLobby()
  1910. {
  1911. if (PhotonNetwork.IsConnected && PhotonNetwork.Server == ServerConnection.MasterServer)
  1912. {
  1913. return NetworkingClient.OpLeaveLobby();
  1914. }
  1915. return false;
  1916. }
  1917. /// <summary>
  1918. /// Requests the rooms and online status for a list of friends and saves the result in PhotonNetwork.Friends.
  1919. /// </summary>
  1920. /// <remarks>
  1921. /// Works only on Master Server to find the rooms played by a selected list of users.
  1922. ///
  1923. /// The result will be stored in PhotonNetwork.Friends when available.
  1924. /// That list is initialized on first use of OpFindFriends (before that, it is null).
  1925. /// To refresh the list, call FindFriends again (in 5 seconds or 10 or 20).
  1926. ///
  1927. /// Users identify themselves by setting a unique userId in the PhotonNetwork.AuthValues.
  1928. /// See remarks of AuthenticationValues for info about how this is set and used.
  1929. ///
  1930. /// The list of friends must be fetched from some other source (not provided by Photon).
  1931. ///
  1932. ///
  1933. /// Internal:
  1934. /// The server response includes 2 arrays of info (each index matching a friend from the request):
  1935. /// ParameterCode.FindFriendsResponseOnlineList = bool[] of online states
  1936. /// ParameterCode.FindFriendsResponseRoomIdList = string[] of room names (empty string if not in a room)
  1937. /// </remarks>
  1938. /// <param name="friendsToFind">Array of friend (make sure to use unique NickName or AuthValues).</param>
  1939. /// <returns>If the operation could be sent (requires connection, only one request is allowed at any time). Always false in offline mode.</returns>
  1940. public static bool FindFriends(string[] friendsToFind)
  1941. {
  1942. if (NetworkingClient == null || offlineMode)
  1943. {
  1944. return false;
  1945. }
  1946. return NetworkingClient.OpFindFriends(friendsToFind);
  1947. }
  1948. /// <summary>Fetches a custom list of games from the server, matching a (non-empty) SQL-like filter. Triggers OnRoomListUpdate callback.</summary>
  1949. /// <remarks>
  1950. /// Operation is only available for lobbies of type SqlLobby and the filter can not be empty.
  1951. /// It will check those conditions and fail locally, returning false.
  1952. /// This is an async request.
  1953. ///
  1954. /// Note: You don't have to join a lobby to query it. Rooms need to be "attached" to a lobby, which can be done
  1955. /// via the typedLobby parameter in CreateRoom, JoinOrCreateRoom, etc..
  1956. ///
  1957. /// When done, OnRoomListUpdate gets called.
  1958. /// </remarks>
  1959. /// <see cref="https://doc.photonengine.com/en-us/pun/v2/lobby-and-matchmaking/matchmaking-and-lobby/#sql_lobby_type"/>
  1960. /// <param name="typedLobby">The lobby to query. Has to be of type SqlLobby.</param>
  1961. /// <param name="sqlLobbyFilter">The sql query statement.</param>
  1962. /// <returns>If the operation could be sent (has to be connected).</returns>
  1963. public static bool GetCustomRoomList(TypedLobby typedLobby, string sqlLobbyFilter)
  1964. {
  1965. return NetworkingClient.OpGetGameList(typedLobby, sqlLobbyFilter);
  1966. }
  1967. /// <summary>
  1968. /// Sets this (local) player's properties and synchronizes them to the other players (don't modify them directly).
  1969. /// </summary>
  1970. /// <remarks>
  1971. /// While in a room, your properties are synced with the other players.
  1972. /// CreateRoom, JoinRoom and JoinRandomRoom will all apply your player's custom properties when you enter the room.
  1973. /// The whole Hashtable will get sent. Minimize the traffic by setting only updated key/values.
  1974. ///
  1975. /// If the Hashtable is null, the custom properties will be cleared.
  1976. /// Custom properties are never cleared automatically, so they carry over to the next room, if you don't change them.
  1977. ///
  1978. /// Don't set properties by modifying PhotonNetwork.player.customProperties!
  1979. /// </remarks>
  1980. /// <param name="customProperties">Only string-typed keys will be used from this hashtable. If null, custom properties are all deleted.</param>
  1981. /// <returns>
  1982. /// False if customProperties is empty or have zero string keys.
  1983. /// True in offline mode.
  1984. /// True if not in a room and this is the local player
  1985. /// (use this to cache properties to be sent when joining a room).
  1986. /// Otherwise, returns if this operation could be sent to the server.
  1987. /// </returns>
  1988. public static bool SetPlayerCustomProperties(Hashtable customProperties)
  1989. {
  1990. if (customProperties == null)
  1991. {
  1992. customProperties = new Hashtable();
  1993. foreach (object k in LocalPlayer.CustomProperties.Keys)
  1994. {
  1995. customProperties[(string)k] = null;
  1996. }
  1997. }
  1998. return LocalPlayer.SetCustomProperties(customProperties);
  1999. }
  2000. /// <summary>
  2001. /// Locally removes Custom Properties of "this" player. Important: This does not synchronize the change! Useful when you switch rooms.
  2002. /// </summary>
  2003. /// <remarks>
  2004. /// Use this method with care. It can create inconsistencies of state between players!
  2005. /// This only changes the player.customProperties locally. This can be useful to clear your
  2006. /// Custom Properties between games (let's say they store which turn you made, kills, etc).
  2007. ///
  2008. /// SetPlayerCustomProperties() syncs and can be used to set values to null while in a room.
  2009. /// That can be considered "removed" while in a room.
  2010. ///
  2011. /// If customPropertiesToDelete is null or has 0 entries, all Custom Properties are deleted (replaced with a new Hashtable).
  2012. /// If you specify keys to remove, those will be removed from the Hashtable but other keys are unaffected.
  2013. /// </remarks>
  2014. /// <param name="customPropertiesToDelete">List of Custom Property keys to remove. See remarks.</param>
  2015. public static void RemovePlayerCustomProperties(string[] customPropertiesToDelete)
  2016. {
  2017. // TODO: decide if this option makes sense
  2018. if (customPropertiesToDelete == null || customPropertiesToDelete.Length == 0 || LocalPlayer.CustomProperties == null)
  2019. {
  2020. LocalPlayer.CustomProperties = new Hashtable();
  2021. return;
  2022. }
  2023. // if a specific list of props should be deleted, we do that here
  2024. for (int i = 0; i < customPropertiesToDelete.Length; i++)
  2025. {
  2026. string key = customPropertiesToDelete[i];
  2027. if (LocalPlayer.CustomProperties.ContainsKey(key))
  2028. {
  2029. LocalPlayer.CustomProperties.Remove(key);
  2030. }
  2031. }
  2032. }
  2033. /// <summary>
  2034. /// Sends fully customizable events in a room. Events consist of at least an EventCode (0..199) and can have content.
  2035. /// </summary>
  2036. /// <remarks>
  2037. /// To receive events, implement IOnEventCallback in any class and register it via PhotonNetwork.AddCallbackTarget.
  2038. /// See <see cref="IOnEventCallback.OnEvent"/>.
  2039. ///
  2040. /// The eventContent is optional. If set, eventContent must be a "serializable type", something that
  2041. /// the client can turn into a byte[] basically. Most basic types and arrays of them are supported, including
  2042. /// Unity's Vector2, Vector3, Quaternion. Transforms are not supported.
  2043. ///
  2044. /// You can turn a class into a "serializable type" by following the example in CustomTypes.cs.
  2045. ///
  2046. /// The RaiseEventOptions have some (less intuitive) combination rules:
  2047. /// If you set targetActors (an array of Player.ID values), the receivers parameter gets ignored.
  2048. /// When using event caching, the targetActors, receivers and interestGroup can't be used. Buffered events go to all.
  2049. /// When using cachingOption removeFromRoomCache, the eventCode and content are actually not sent but used as filter.
  2050. /// </remarks>
  2051. /// <param name="eventCode">A byte identifying the type of event. You might want to use a code per action or to signal which content can be expected. Allowed: 0..199.</param>
  2052. /// <param name="eventContent">Some serializable object like string, byte, integer, float (etc) and arrays of those. Hashtables with byte keys are good to send variable content.</param>
  2053. /// <param name="raiseEventOptions">Allows more complex usage of events. If null, RaiseEventOptions.Default will be used (which is fine).</param>
  2054. /// <param name="sendOptions">Send options for reliable, encryption etc..</param>
  2055. /// <returns>False if event could not be sent.</returns>
  2056. public static bool RaiseEvent(byte eventCode, object eventContent, RaiseEventOptions raiseEventOptions, SendOptions sendOptions)
  2057. {
  2058. if (offlineMode)
  2059. {
  2060. if (raiseEventOptions.Receivers == ReceiverGroup.Others)
  2061. {
  2062. return true;
  2063. }
  2064. EventData evData = new EventData { Code = eventCode }; // creates the equivalent of a received event
  2065. evData.Parameters[ParameterCode.Data] = eventContent;
  2066. evData.Parameters[ParameterCode.ActorNr] = 1;
  2067. NetworkingClient.OnEvent(evData);
  2068. return true;
  2069. }
  2070. if (!InRoom || eventCode >= 200)
  2071. {
  2072. Debug.LogWarning("RaiseEvent(" + eventCode + ") failed. Your event is not being sent! Check if your are in a Room and the eventCode must be less than 200 (0..199).");
  2073. return false;
  2074. }
  2075. return NetworkingClient.OpRaiseEvent(eventCode, eventContent, raiseEventOptions, sendOptions);
  2076. }
  2077. /// <summary>Sends PUN-specific events to the server, unless in offlineMode.</summary>
  2078. /// <param name="eventCode">A byte identifying the type of event.</param>
  2079. /// <param name="eventContent">Serializable object or container.</param>
  2080. /// <param name="raiseEventOptions">Allows more complex usage of events. If null, RaiseEventOptions.</param>
  2081. /// <param name="sendOptions">Send options for reliable, encryption etc..</param>
  2082. /// <returns>False if event could not be sent</returns>
  2083. private static bool RaiseEventInternal(byte eventCode, object eventContent, RaiseEventOptions raiseEventOptions, SendOptions sendOptions)
  2084. {
  2085. if (offlineMode)
  2086. {
  2087. return false;
  2088. }
  2089. if (!InRoom)
  2090. {
  2091. Debug.LogWarning("RaiseEvent(" + eventCode + ") failed. Your event is not being sent! Check if your are in a Room");
  2092. return false;
  2093. }
  2094. return NetworkingClient.OpRaiseEvent(eventCode, eventContent, raiseEventOptions, sendOptions);
  2095. }
  2096. /// <summary>
  2097. /// Allocates a viewID for the current/local player.
  2098. /// </summary>
  2099. /// <returns>True if a viewId was assigned. False if the PhotonView already had a non-zero viewID.</returns>
  2100. public static bool AllocateViewID(PhotonView view)
  2101. {
  2102. if (view.ViewID != 0)
  2103. {
  2104. Debug.LogError("AllocateViewID() can't be used for PhotonViews that already have a viewID. This view is: " + view.ToString());
  2105. return false;
  2106. }
  2107. int manualId = AllocateViewID(LocalPlayer.ActorNumber);
  2108. view.ViewID = manualId;
  2109. return true;
  2110. }
  2111. [Obsolete("Renamed. Use AllocateRoomViewID instead")]
  2112. public static bool AllocateSceneViewID(PhotonView view)
  2113. {
  2114. return AllocateRoomViewID(view);
  2115. }
  2116. /// <summary>
  2117. /// Enables the Master Client to allocate a viewID for room objects.
  2118. /// </summary>
  2119. /// <returns>True if a viewId was assigned. False if the PhotonView already had a non-zero viewID or if this client is not the Master Client.</returns>
  2120. public static bool AllocateRoomViewID(PhotonView view)
  2121. {
  2122. if (!PhotonNetwork.IsMasterClient)
  2123. {
  2124. Debug.LogError("Only the Master Client can AllocateRoomViewID(). Check PhotonNetwork.IsMasterClient!");
  2125. return false;
  2126. }
  2127. if (view.ViewID != 0)
  2128. {
  2129. Debug.LogError("AllocateRoomViewID() can't be used for PhotonViews that already have a viewID. This view is: " + view.ToString());
  2130. return false;
  2131. }
  2132. int manualId = AllocateViewID(0);
  2133. view.ViewID = manualId;
  2134. return true;
  2135. }
  2136. /// <summary>Allocates a viewID for the current/local player or the room.</summary>
  2137. /// <param name="roomObject">Use true, to allocate a room viewID and false to allocate a viewID for the local player.</param>
  2138. /// <returns>Returns a viewID (combined owner and sequential number) that can be assigned as PhotonView.ViewID.</returns>
  2139. public static int AllocateViewID(bool roomObject)
  2140. {
  2141. if (roomObject && !LocalPlayer.IsMasterClient)
  2142. {
  2143. Debug.LogError("Only a Master Client can AllocateViewID() for room objects. This client/player is not a Master Client. Returning an invalid viewID: -1.");
  2144. return 0;
  2145. }
  2146. int ownerActorNumber = roomObject ? 0 : LocalPlayer.ActorNumber;
  2147. return AllocateViewID(ownerActorNumber);
  2148. }
  2149. /// <summary>Allocates a viewID for the current/local player or the room.</summary>
  2150. /// <param name="ownerId">ActorNumber to allocate a viewID for.</param>
  2151. /// <returns>Returns a viewID (combined owner and sequential number) that can be assigned as PhotonView.ViewID.</returns>
  2152. public static int AllocateViewID(int ownerId)
  2153. {
  2154. if (ownerId == 0)
  2155. {
  2156. // we look up a fresh subId for the owner "room" (mind the "sub" in subId)
  2157. int newSubId = lastUsedViewSubIdStatic;
  2158. int newViewId;
  2159. int ownerIdOffset = ownerId * MAX_VIEW_IDS;
  2160. for (int i = 1; i < MAX_VIEW_IDS; i++)
  2161. {
  2162. newSubId = (newSubId + 1) % MAX_VIEW_IDS;
  2163. if (newSubId == 0)
  2164. {
  2165. continue; // avoid using subID 0
  2166. }
  2167. newViewId = newSubId + ownerIdOffset;
  2168. if (!photonViewList.ContainsKey(newViewId))
  2169. {
  2170. lastUsedViewSubIdStatic = newSubId;
  2171. return newViewId;
  2172. }
  2173. }
  2174. // this is the error case: we didn't find any (!) free subId for this user
  2175. throw new Exception(string.Format("AllocateViewID() failed. The room (user {0}) is out of 'room' viewIDs. It seems all available are in use.", ownerId));
  2176. }
  2177. else
  2178. {
  2179. // we look up a fresh SUBid for the owner
  2180. int newSubId = lastUsedViewSubId;
  2181. int newViewId;
  2182. int ownerIdOffset = ownerId * MAX_VIEW_IDS;
  2183. for (int i = 1; i <= MAX_VIEW_IDS; i++)
  2184. {
  2185. newSubId = (newSubId + 1) % MAX_VIEW_IDS;
  2186. if (newSubId == 0)
  2187. {
  2188. continue; // avoid using subID 0
  2189. }
  2190. newViewId = newSubId + ownerIdOffset;
  2191. if (!photonViewList.ContainsKey(newViewId))
  2192. {
  2193. lastUsedViewSubId = newSubId;
  2194. return newViewId;
  2195. }
  2196. }
  2197. throw new Exception(string.Format("AllocateViewID() failed. User {0} is out of viewIDs. It seems all available are in use.", ownerId));
  2198. }
  2199. }
  2200. public static GameObject Instantiate(string prefabName, Vector3 position, Quaternion rotation, byte group = 0, object[] data = null)
  2201. {
  2202. if (CurrentRoom == null)
  2203. {
  2204. Debug.LogError("Can not Instantiate before the client joined/created a room. State: "+PhotonNetwork.NetworkClientState);
  2205. return null;
  2206. }
  2207. Pun.InstantiateParameters netParams = new InstantiateParameters(prefabName, position, rotation, group, data, currentLevelPrefix, null, LocalPlayer, ServerTimestamp);
  2208. return NetworkInstantiate(netParams, false);
  2209. }
  2210. [Obsolete("Renamed. Use InstantiateRoomObject instead")]
  2211. public static GameObject InstantiateSceneObject(string prefabName, Vector3 position, Quaternion rotation, byte group = 0, object[] data = null)
  2212. {
  2213. return InstantiateRoomObject(prefabName, position, rotation, group, data);
  2214. }
  2215. public static GameObject InstantiateRoomObject(string prefabName, Vector3 position, Quaternion rotation, byte group = 0, object[] data = null)
  2216. {
  2217. if (CurrentRoom == null)
  2218. {
  2219. Debug.LogError("Can not Instantiate before the client joined/created a room.");
  2220. return null;
  2221. }
  2222. if (LocalPlayer.IsMasterClient)
  2223. {
  2224. Pun.InstantiateParameters netParams = new InstantiateParameters(prefabName, position, rotation, group, data, currentLevelPrefix, null, LocalPlayer, ServerTimestamp);
  2225. return NetworkInstantiate(netParams, true);
  2226. }
  2227. return null;
  2228. }
  2229. private static GameObject NetworkInstantiate(Hashtable networkEvent, Player creator)
  2230. {
  2231. // some values always present:
  2232. string prefabName = (string)networkEvent[keyByteZero];
  2233. int serverTime = (int)networkEvent[keyByteSix];
  2234. int instantiationId = (int)networkEvent[keyByteSeven];
  2235. Vector3 position;
  2236. if (networkEvent.ContainsKey(keyByteOne))
  2237. {
  2238. position = (Vector3)networkEvent[keyByteOne];
  2239. }
  2240. else
  2241. {
  2242. position = Vector3.zero;
  2243. }
  2244. Quaternion rotation = Quaternion.identity;
  2245. if (networkEvent.ContainsKey(keyByteTwo))
  2246. {
  2247. rotation = (Quaternion)networkEvent[keyByteTwo];
  2248. }
  2249. byte group = 0;
  2250. if (networkEvent.ContainsKey(keyByteThree))
  2251. {
  2252. group = (byte)networkEvent[keyByteThree];
  2253. }
  2254. byte objLevelPrefix = 0;
  2255. if (networkEvent.ContainsKey(keyByteEight))
  2256. {
  2257. objLevelPrefix = (byte)networkEvent[keyByteEight];
  2258. }
  2259. int[] viewsIDs;
  2260. if (networkEvent.ContainsKey(keyByteFour))
  2261. {
  2262. viewsIDs = (int[])networkEvent[keyByteFour];
  2263. }
  2264. else
  2265. {
  2266. viewsIDs = new int[1] { instantiationId };
  2267. }
  2268. object[] incomingInstantiationData;
  2269. if (networkEvent.ContainsKey(keyByteFive))
  2270. {
  2271. incomingInstantiationData = (object[])networkEvent[keyByteFive];
  2272. }
  2273. else
  2274. {
  2275. incomingInstantiationData = null;
  2276. }
  2277. // SetReceiving filtering
  2278. if (group != 0 && !allowedReceivingGroups.Contains(group))
  2279. {
  2280. return null; // Ignore group
  2281. }
  2282. Pun.InstantiateParameters netParams = new InstantiateParameters(prefabName, position, rotation, group, incomingInstantiationData, objLevelPrefix, viewsIDs, creator, serverTime);
  2283. return NetworkInstantiate(netParams, false, true);
  2284. }
  2285. private static readonly HashSet<string> PrefabsWithoutMagicCallback = new HashSet<string>();
  2286. private static GameObject NetworkInstantiate(Pun.InstantiateParameters parameters, bool roomObject = false, bool instantiateEvent = false)
  2287. {
  2288. //Instantiate(name, pos, rot)
  2289. //pv[] GetPhotonViewsInChildren()
  2290. //if (event==null) init send-params
  2291. //Setup of PVs and callback
  2292. //if (event == null) SendInstantiate(name, pos, rot, etc...)
  2293. GameObject go = null;
  2294. PhotonView[] photonViews;
  2295. go = prefabPool.Instantiate(parameters.prefabName, parameters.position, parameters.rotation);
  2296. if (go == null)
  2297. {
  2298. Debug.LogError("Failed to network-Instantiate: " + parameters.prefabName);
  2299. return null;
  2300. }
  2301. if (go.activeSelf)
  2302. {
  2303. Debug.LogWarning("PrefabPool.Instantiate() should return an inactive GameObject. " + prefabPool.GetType().Name + " returned an active object. PrefabId: " + parameters.prefabName);
  2304. }
  2305. photonViews = go.GetPhotonViewsInChildren();
  2306. if (photonViews.Length == 0)
  2307. {
  2308. Debug.LogError("PhotonNetwork.Instantiate() can only instantiate objects with a PhotonView component. This prefab does not have one: " + parameters.prefabName);
  2309. return null;
  2310. }
  2311. bool localInstantiate = !instantiateEvent && LocalPlayer.Equals(parameters.creator);
  2312. if (localInstantiate)
  2313. {
  2314. // init viewIDs array, so it can be filled (below), before it gets sent
  2315. parameters.viewIDs = new int[photonViews.Length];
  2316. }
  2317. for (int i = 0; i < photonViews.Length; i++)
  2318. {
  2319. if (localInstantiate)
  2320. {
  2321. // when this client instantiates a GO, it has to allocate viewIDs accordingly.
  2322. // ROOM objects are created as actorNumber 0 (no matter which number this player has).
  2323. parameters.viewIDs[i] = (roomObject) ? AllocateViewID(0) : AllocateViewID(parameters.creator.ActorNumber);
  2324. }
  2325. var view = photonViews[i];
  2326. view.ViewID = 0;
  2327. view.sceneViewId = 0;
  2328. view.isRuntimeInstantiated = true;
  2329. view.lastOnSerializeDataSent = null;
  2330. view.lastOnSerializeDataReceived = null;
  2331. view.Prefix = parameters.objLevelPrefix;
  2332. view.InstantiationId = parameters.viewIDs[0];
  2333. view.InstantiationData = parameters.data;
  2334. view.ViewID = parameters.viewIDs[i]; // with didAwake true and viewID == 0, this will also register the view
  2335. view.Group = parameters.group;
  2336. }
  2337. if (localInstantiate)
  2338. {
  2339. // send instantiate network event
  2340. SendInstantiate(parameters, roomObject);
  2341. }
  2342. go.SetActive(true);
  2343. // if IPunInstantiateMagicCallback is implemented on any script of the instantiated GO, let's call it directly:
  2344. if (!PrefabsWithoutMagicCallback.Contains(parameters.prefabName))
  2345. {
  2346. var list = go.GetComponents<IPunInstantiateMagicCallback>();
  2347. if (list.Length > 0)
  2348. {
  2349. PhotonMessageInfo pmi = new PhotonMessageInfo(parameters.creator, parameters.timestamp, photonViews[0]);
  2350. foreach (IPunInstantiateMagicCallback callbackComponent in list)
  2351. {
  2352. callbackComponent.OnPhotonInstantiate(pmi);
  2353. }
  2354. }
  2355. else
  2356. {
  2357. PrefabsWithoutMagicCallback.Add(parameters.prefabName);
  2358. }
  2359. }
  2360. return go;
  2361. }
  2362. private static readonly Hashtable SendInstantiateEvHashtable = new Hashtable(); // SendInstantiate reuses this to reduce GC
  2363. private static readonly RaiseEventOptions SendInstantiateRaiseEventOptions = new RaiseEventOptions(); // SendInstantiate reuses this to reduce GC
  2364. internal static bool SendInstantiate(Pun.InstantiateParameters parameters, bool roomObject = false)
  2365. {
  2366. // first viewID is now also the gameobject's instantiateId
  2367. int instantiateId = parameters.viewIDs[0]; // LIMITS PHOTONVIEWS&PLAYERS
  2368. SendInstantiateEvHashtable.Clear(); // SendInstantiate reuses this Hashtable to reduce GC
  2369. SendInstantiateEvHashtable[keyByteZero] = parameters.prefabName;
  2370. if (parameters.position != Vector3.zero)
  2371. {
  2372. SendInstantiateEvHashtable[keyByteOne] = parameters.position;
  2373. }
  2374. if (parameters.rotation != Quaternion.identity)
  2375. {
  2376. SendInstantiateEvHashtable[keyByteTwo] = parameters.rotation;
  2377. }
  2378. if (parameters.group != 0)
  2379. {
  2380. SendInstantiateEvHashtable[keyByteThree] = parameters.group;
  2381. }
  2382. // send the list of viewIDs only if there are more than one. else the instantiateId is the viewID
  2383. if (parameters.viewIDs.Length > 1)
  2384. {
  2385. SendInstantiateEvHashtable[keyByteFour] = parameters.viewIDs; // LIMITS PHOTONVIEWS&PLAYERS
  2386. }
  2387. if (parameters.data != null)
  2388. {
  2389. SendInstantiateEvHashtable[keyByteFive] = parameters.data;
  2390. }
  2391. if (currentLevelPrefix > 0)
  2392. {
  2393. SendInstantiateEvHashtable[keyByteEight] = currentLevelPrefix; // photonview's / object's level prefix
  2394. }
  2395. SendInstantiateEvHashtable[keyByteSix] = PhotonNetwork.ServerTimestamp;
  2396. SendInstantiateEvHashtable[keyByteSeven] = instantiateId;
  2397. SendInstantiateRaiseEventOptions.CachingOption = (roomObject) ? EventCaching.AddToRoomCacheGlobal : EventCaching.AddToRoomCache;
  2398. return PhotonNetwork.RaiseEventInternal(PunEvent.Instantiation, SendInstantiateEvHashtable, SendInstantiateRaiseEventOptions, SendOptions.SendReliable);
  2399. }
  2400. /// <summary>
  2401. /// Network-Destroy the GameObject associated with the PhotonView, unless the PhotonView is static or not under this client's control.
  2402. /// </summary>
  2403. /// <remarks>
  2404. /// Destroying a networked GameObject while in a Room includes:
  2405. /// - Removal of the Instantiate call from the server's room buffer.
  2406. /// - Removing RPCs buffered for PhotonViews that got created indirectly with the PhotonNetwork.Instantiate call.
  2407. /// - Sending a message to other clients to remove the GameObject also (affected by network lag).
  2408. ///
  2409. /// Usually, when you leave a room, the GOs get destroyed automatically.
  2410. /// If you have to destroy a GO while not in a room, the Destroy is only done locally.
  2411. ///
  2412. /// Destroying networked objects works only if they got created with PhotonNetwork.Instantiate().
  2413. /// Objects loaded with a scene are ignored, no matter if they have PhotonView components.
  2414. ///
  2415. /// The GameObject must be under this client's control:
  2416. /// - Instantiated and owned by this client.
  2417. /// - Instantiated objects of players who left the room are controlled by the Master Client.
  2418. /// - Room-owned game objects are controlled by the Master Client.
  2419. /// - GameObject can be destroyed while client is not in a room.
  2420. /// </remarks>
  2421. /// <returns>Nothing. Check error debug log for any issues.</returns>
  2422. public static void Destroy(PhotonView targetView)
  2423. {
  2424. if (targetView != null)
  2425. {
  2426. RemoveInstantiatedGO(targetView.gameObject, !InRoom);
  2427. }
  2428. else
  2429. {
  2430. Debug.LogError("Destroy(targetPhotonView) failed, cause targetPhotonView is null.");
  2431. }
  2432. }
  2433. /// <summary>
  2434. /// Network-Destroy the GameObject, unless it is static or not under this client's control.
  2435. /// </summary>
  2436. /// <remarks>
  2437. /// Destroying a networked GameObject includes:
  2438. /// - Removal of the Instantiate call from the server's room buffer.
  2439. /// - Removing RPCs buffered for PhotonViews that got created indirectly with the PhotonNetwork.Instantiate call.
  2440. /// - Sending a message to other clients to remove the GameObject also (affected by network lag).
  2441. ///
  2442. /// Usually, when you leave a room, the GOs get destroyed automatically.
  2443. /// If you have to destroy a GO while not in a room, the Destroy is only done locally.
  2444. ///
  2445. /// Destroying networked objects works only if they got created with PhotonNetwork.Instantiate().
  2446. /// Objects loaded with a scene are ignored, no matter if they have PhotonView components.
  2447. ///
  2448. /// The GameObject must be under this client's control:
  2449. /// - Instantiated and owned by this client.
  2450. /// - Instantiated objects of players who left the room are controlled by the Master Client.
  2451. /// - Room-owned game objects are controlled by the Master Client.
  2452. /// - GameObject can be destroyed while client is not in a room.
  2453. /// </remarks>
  2454. /// <returns>Nothing. Check error debug log for any issues.</returns>
  2455. public static void Destroy(GameObject targetGo)
  2456. {
  2457. RemoveInstantiatedGO(targetGo, !InRoom);
  2458. }
  2459. /// <summary>
  2460. /// Network-Destroy all GameObjects, PhotonViews and their RPCs of targetPlayer. Can only be called on local player (for "self") or Master Client (for anyone).
  2461. /// </summary>
  2462. /// <remarks>
  2463. /// Destroying a networked GameObject includes:
  2464. /// - Removal of the Instantiate call from the server's room buffer.
  2465. /// - Removing RPCs buffered for PhotonViews that got created indirectly with the PhotonNetwork.Instantiate call.
  2466. /// - Sending a message to other clients to remove the GameObject also (affected by network lag).
  2467. ///
  2468. /// Destroying networked objects works only if they got created with PhotonNetwork.Instantiate().
  2469. /// Objects loaded with a scene are ignored, no matter if they have PhotonView components.
  2470. /// </remarks>
  2471. /// <returns>Nothing. Check error debug log for any issues.</returns>
  2472. public static void DestroyPlayerObjects(Player targetPlayer)
  2473. {
  2474. if (targetPlayer == null)
  2475. {
  2476. Debug.LogError("DestroyPlayerObjects() failed, cause parameter 'targetPlayer' was null.");
  2477. }
  2478. DestroyPlayerObjects(targetPlayer.ActorNumber);
  2479. }
  2480. /// <summary>
  2481. /// Network-Destroy all GameObjects, PhotonViews and their RPCs of this player (by ID). Can only be called on local player (for "self") or Master Client (for anyone).
  2482. /// </summary>
  2483. /// <remarks>
  2484. /// Destroying a networked GameObject includes:
  2485. /// - Removal of the Instantiate call from the server's room buffer.
  2486. /// - Removing RPCs buffered for PhotonViews that got created indirectly with the PhotonNetwork.Instantiate call.
  2487. /// - Sending a message to other clients to remove the GameObject also (affected by network lag).
  2488. ///
  2489. /// Destroying networked objects works only if they got created with PhotonNetwork.Instantiate().
  2490. /// Objects loaded with a scene are ignored, no matter if they have PhotonView components.
  2491. /// </remarks>
  2492. /// <returns>Nothing. Check error debug log for any issues.</returns>
  2493. public static void DestroyPlayerObjects(int targetPlayerId)
  2494. {
  2495. if (!VerifyCanUseNetwork())
  2496. {
  2497. return;
  2498. }
  2499. if (LocalPlayer.IsMasterClient || targetPlayerId == LocalPlayer.ActorNumber)
  2500. {
  2501. DestroyPlayerObjects(targetPlayerId, false);
  2502. }
  2503. else
  2504. {
  2505. Debug.LogError("DestroyPlayerObjects() failed, cause players can only destroy their own GameObjects. A Master Client can destroy anyone's. This is master: " + PhotonNetwork.IsMasterClient);
  2506. }
  2507. }
  2508. /// <summary>
  2509. /// Network-Destroy all GameObjects, PhotonViews and their RPCs in the room. Removes anything buffered from the server. Can only be called by Master Client (for anyone).
  2510. /// </summary>
  2511. /// <remarks>
  2512. /// Can only be called by Master Client (for anyone).
  2513. /// Unlike the Destroy methods, this will remove anything from the server's room buffer. If your game
  2514. /// buffers anything beyond Instantiate and RPC calls, that will be cleaned as well from server.
  2515. ///
  2516. /// Destroying all includes:
  2517. /// - Remove anything from the server's room buffer (Instantiate, RPCs, anything buffered).
  2518. /// - Sending a message to other clients to destroy everything locally, too (affected by network lag).
  2519. ///
  2520. /// Destroying networked objects works only if they got created with PhotonNetwork.Instantiate().
  2521. /// Objects loaded with a scene are ignored, no matter if they have PhotonView components.
  2522. /// </remarks>
  2523. /// <returns>Nothing. Check error debug log for any issues.</returns>
  2524. public static void DestroyAll()
  2525. {
  2526. if (IsMasterClient)
  2527. {
  2528. DestroyAll(false);
  2529. }
  2530. else
  2531. {
  2532. Debug.LogError("Couldn't call DestroyAll() as only the master client is allowed to call this.");
  2533. }
  2534. }
  2535. /// <summary>
  2536. /// Remove all buffered RPCs from server that were sent by targetPlayer. Can only be called on local player (for "self") or Master Client (for anyone).
  2537. /// </summary>
  2538. /// <remarks>
  2539. /// This method requires either:
  2540. /// - This is the targetPlayer's client.
  2541. /// - This client is the Master Client (can remove any Player's RPCs).
  2542. ///
  2543. /// If the targetPlayer calls RPCs at the same time that this is called,
  2544. /// network lag will determine if those get buffered or cleared like the rest.
  2545. /// </remarks>
  2546. /// <param name="targetPlayer">This player's buffered RPCs get removed from server buffer.</param>
  2547. public static void RemoveRPCs(Player targetPlayer)
  2548. {
  2549. if (!VerifyCanUseNetwork())
  2550. {
  2551. return;
  2552. }
  2553. if (!targetPlayer.IsLocal && !IsMasterClient)
  2554. {
  2555. Debug.LogError("Error; Only the MasterClient can call RemoveRPCs for other players.");
  2556. return;
  2557. }
  2558. OpCleanActorRpcBuffer(targetPlayer.ActorNumber);
  2559. }
  2560. /// <summary>
  2561. /// Remove all buffered RPCs from server that were sent via targetPhotonView. The Master Client and the owner of the targetPhotonView may call this.
  2562. /// </summary>
  2563. /// <remarks>
  2564. /// This method requires either:
  2565. /// - The targetPhotonView is owned by this client (Instantiated by it).
  2566. /// - This client is the Master Client (can remove any PhotonView's RPCs).
  2567. /// </remarks>
  2568. /// <param name="targetPhotonView">RPCs buffered for this PhotonView get removed from server buffer.</param>
  2569. public static void RemoveRPCs(PhotonView targetPhotonView)
  2570. {
  2571. if (!VerifyCanUseNetwork())
  2572. {
  2573. return;
  2574. }
  2575. CleanRpcBufferIfMine(targetPhotonView);
  2576. }
  2577. /// <summary>
  2578. /// Internal to send an RPC on given PhotonView. Do not call this directly but use: PhotonView.RPC!
  2579. /// </summary>
  2580. internal static void RPC(PhotonView view, string methodName, RpcTarget target, bool encrypt, params object[] parameters)
  2581. {
  2582. if (string.IsNullOrEmpty(methodName))
  2583. {
  2584. Debug.LogError("RPC method name cannot be null or empty.");
  2585. return;
  2586. }
  2587. if (!VerifyCanUseNetwork())
  2588. {
  2589. return;
  2590. }
  2591. if (CurrentRoom == null)
  2592. {
  2593. Debug.LogWarning("RPCs can only be sent in rooms. Call of \"" + methodName + "\" gets executed locally only, if at all.");
  2594. return;
  2595. }
  2596. if (NetworkingClient != null)
  2597. {
  2598. RPC(view, methodName, target, null, encrypt, parameters);
  2599. }
  2600. else
  2601. {
  2602. Debug.LogWarning("Could not execute RPC " + methodName + ". Possible scene loading in progress?");
  2603. }
  2604. }
  2605. /// <summary>
  2606. /// Internal to send an RPC on given PhotonView. Do not call this directly but use: PhotonView.RPC!
  2607. /// </summary>
  2608. internal static void RPC(PhotonView view, string methodName, Player targetPlayer, bool encrypt, params object[] parameters)
  2609. {
  2610. if (!VerifyCanUseNetwork())
  2611. {
  2612. return;
  2613. }
  2614. if (CurrentRoom == null)
  2615. {
  2616. Debug.LogWarning("RPCs can only be sent in rooms. Call of \"" + methodName + "\" gets executed locally only, if at all.");
  2617. return;
  2618. }
  2619. if (LocalPlayer == null)
  2620. {
  2621. Debug.LogError("RPC can't be sent to target Player being null! Did not send \"" + methodName + "\" call.");
  2622. }
  2623. if (NetworkingClient != null)
  2624. {
  2625. RPC(view, methodName, RpcTarget.Others, targetPlayer, encrypt, parameters);
  2626. }
  2627. else
  2628. {
  2629. Debug.LogWarning("Could not execute RPC " + methodName + ". Possible scene loading in progress?");
  2630. }
  2631. }
  2632. /// <summary>Finds the GameObjects with Components of a specific type (using FindObjectsOfType).</summary>
  2633. /// <param name="type">Type must be a Component</param>
  2634. /// <returns>HashSet with GameObjects that have a specific type of Component.</returns>
  2635. public static HashSet<GameObject> FindGameObjectsWithComponent(Type type)
  2636. {
  2637. HashSet<GameObject> objectsWithComponent = new HashSet<GameObject>();
  2638. Component[] targetComponents = (Component[]) GameObject.FindObjectsOfType(type);
  2639. for (int index = 0; index < targetComponents.Length; index++)
  2640. {
  2641. if (targetComponents[index] != null)
  2642. {
  2643. objectsWithComponent.Add(targetComponents[index].gameObject);
  2644. }
  2645. }
  2646. return objectsWithComponent;
  2647. }
  2648. /// <summary>Enable/disable receiving events from a given Interest Group.</summary>
  2649. /// <remarks>
  2650. /// A client can tell the server which Interest Groups it's interested in.
  2651. /// The server will only forward events for those Interest Groups to that client (saving bandwidth and performance).
  2652. ///
  2653. /// See: https://doc.photonengine.com/en-us/pun/v2/gameplay/interestgroups
  2654. ///
  2655. /// See: https://doc.photonengine.com/en-us/pun/v2/demos-and-tutorials/package-demos/culling-demo
  2656. /// </remarks>
  2657. /// <param name="group">The interest group to affect.</param>
  2658. /// <param name="enabled">Sets if receiving from group to enabled (or not).</param>
  2659. public static void SetInterestGroups(byte group, bool enabled)
  2660. {
  2661. if (!VerifyCanUseNetwork())
  2662. {
  2663. return;
  2664. }
  2665. if (enabled)
  2666. {
  2667. byte[] groups = new byte[1] { (byte)group };
  2668. SetInterestGroups(null, groups);
  2669. }
  2670. else
  2671. {
  2672. byte[] groups = new byte[1] { (byte)group };
  2673. SetInterestGroups(groups, null);
  2674. }
  2675. }
  2676. /// <summary>This method wraps loading a level asynchronously and pausing network messages during the process.</summary>
  2677. /// <remarks>
  2678. /// While loading levels in a networked game, it makes sense to not dispatch messages received by other players.
  2679. /// LoadLevel takes care of that by setting PhotonNetwork.IsMessageQueueRunning = false until the scene loaded.
  2680. ///
  2681. /// To sync the loaded level in a room, set PhotonNetwork.AutomaticallySyncScene to true.
  2682. /// The Master Client of a room will then sync the loaded level with every other player in the room.
  2683. /// Note that this works only for a single active scene and that reloading the scene is not supported.
  2684. /// The Master Client will actually reload a scene but other clients won't.
  2685. ///
  2686. /// You should make sure you don't fire RPCs before you load another scene (which doesn't contain
  2687. /// the same GameObjects and PhotonViews).
  2688. ///
  2689. /// LoadLevel uses SceneManager.LoadSceneAsync().
  2690. ///
  2691. /// Check the progress of the LevelLoading using PhotonNetwork.LevelLoadingProgress.
  2692. ///
  2693. /// Calling LoadLevel before the previous scene finished loading is not recommended.
  2694. /// If AutomaticallySyncScene is enabled, PUN cancels the previous load (and prevent that from
  2695. /// becoming the active scene). If AutomaticallySyncScene is off, the previous scene loading can finish.
  2696. /// In both cases, a new scene is loaded locally.
  2697. /// </remarks>
  2698. /// <param name='levelNumber'>
  2699. /// Build-index number of the level to load. When using level numbers, make sure they are identical on all clients.
  2700. /// </param>
  2701. public static void LoadLevel(int levelNumber)
  2702. {
  2703. if (PhotonHandler.AppQuits)
  2704. {
  2705. return;
  2706. }
  2707. if (PhotonNetwork.AutomaticallySyncScene)
  2708. {
  2709. SetLevelInPropsIfSynced(levelNumber);
  2710. }
  2711. PhotonNetwork.IsMessageQueueRunning = false;
  2712. loadingLevelAndPausedNetwork = true;
  2713. _AsyncLevelLoadingOperation = SceneManager.LoadSceneAsync(levelNumber,LoadSceneMode.Single);
  2714. }
  2715. /// <summary>This method wraps loading a level asynchronously and pausing network messages during the process.</summary>
  2716. /// <remarks>
  2717. /// While loading levels in a networked game, it makes sense to not dispatch messages received by other players.
  2718. /// LoadLevel takes care of that by setting PhotonNetwork.IsMessageQueueRunning = false until the scene loaded.
  2719. ///
  2720. /// To sync the loaded level in a room, set PhotonNetwork.AutomaticallySyncScene to true.
  2721. /// The Master Client of a room will then sync the loaded level with every other player in the room.
  2722. /// Note that this works only for a single active scene and that reloading the scene is not supported.
  2723. /// The Master Client will actually reload a scene but other clients won't.
  2724. ///
  2725. /// You should make sure you don't fire RPCs before you load another scene (which doesn't contain
  2726. /// the same GameObjects and PhotonViews).
  2727. ///
  2728. /// LoadLevel uses SceneManager.LoadSceneAsync().
  2729. ///
  2730. /// Check the progress of the LevelLoading using PhotonNetwork.LevelLoadingProgress.
  2731. ///
  2732. /// Calling LoadLevel before the previous scene finished loading is not recommended.
  2733. /// If AutomaticallySyncScene is enabled, PUN cancels the previous load (and prevent that from
  2734. /// becoming the active scene). If AutomaticallySyncScene is off, the previous scene loading can finish.
  2735. /// In both cases, a new scene is loaded locally.
  2736. /// </remarks>
  2737. /// <param name='levelName'>
  2738. /// Name of the level to load. Make sure it's available to all clients in the same room.
  2739. /// </param>
  2740. public static void LoadLevel(string levelName)
  2741. {
  2742. if (PhotonHandler.AppQuits)
  2743. {
  2744. return;
  2745. }
  2746. if (PhotonNetwork.AutomaticallySyncScene)
  2747. {
  2748. SetLevelInPropsIfSynced(levelName);
  2749. }
  2750. PhotonNetwork.IsMessageQueueRunning = false;
  2751. loadingLevelAndPausedNetwork = true;
  2752. _AsyncLevelLoadingOperation = SceneManager.LoadSceneAsync(levelName, LoadSceneMode.Single);
  2753. }
  2754. /// <summary>
  2755. /// This operation makes Photon call your custom web-service by name (path) with the given parameters.
  2756. /// </summary>
  2757. /// <remarks>
  2758. /// This is a server-side feature which must be setup in the Photon Cloud Dashboard prior to use.
  2759. /// <see cref="https://doc.photonengine.com/en-us/pun/v2/gameplay/web-extensions/webrpc"/>
  2760. /// The Parameters will be converted into JSon format, so make sure your parameters are compatible.
  2761. ///
  2762. /// See <see cref="Photon.Realtime.IWebRpcCallback.OnWebRpcResponse"/> on how to get a response.
  2763. ///
  2764. /// It's important to understand that the OperationResponse only tells if the WebRPC could be called.
  2765. /// The content of the response contains any values your web-service sent and the error/success code.
  2766. /// In case the web-service failed, an error code and a debug message are usually inside the
  2767. /// OperationResponse.
  2768. ///
  2769. /// The class WebRpcResponse is a helper-class that extracts the most valuable content from the WebRPC
  2770. /// response.
  2771. /// </remarks>
  2772. /// <example>
  2773. /// Example callback implementation:<pre>
  2774. ///
  2775. /// public void OnWebRpcResponse(OperationResponse response)
  2776. /// {
  2777. /// WebRpcResponse webResponse = new WebRpcResponse(operationResponse);
  2778. /// if (webResponse.ReturnCode != 0) { //...
  2779. /// }
  2780. ///
  2781. /// switch (webResponse.Name) { //...
  2782. /// }
  2783. /// // and so on
  2784. /// }</pre>
  2785. /// </example>
  2786. public static bool WebRpc(string name, object parameters, bool sendAuthCookie = false)
  2787. {
  2788. return NetworkingClient.OpWebRpc(name, parameters, sendAuthCookie);
  2789. }
  2790. /// <summary>
  2791. /// Applies default log settings if they are not set up programmatically.
  2792. /// </summary>
  2793. private static void SetupLogging()
  2794. {
  2795. // only apply Settings if LogLevel is default ( see ServerSettings.cs), else it means it's been set programmatically
  2796. if (PhotonNetwork.LogLevel == PunLogLevel.ErrorsOnly)
  2797. {
  2798. PhotonNetwork.LogLevel = PhotonServerSettings.PunLogging;
  2799. }
  2800. // only apply Settings if LogLevel is default ( see ServerSettings.cs), else it means it's been set programmatically
  2801. if (PhotonNetwork.NetworkingClient.LoadBalancingPeer.DebugOut == DebugLevel.ERROR)
  2802. {
  2803. PhotonNetwork.NetworkingClient.LoadBalancingPeer.DebugOut = PhotonServerSettings.AppSettings.NetworkLogging;
  2804. }
  2805. }
  2806. public static void LoadOrCreateSettings(bool reload = false)
  2807. {
  2808. if (reload)
  2809. {
  2810. photonServerSettings = null; // PhotonEditor will use this to load and save the settings delayed
  2811. }
  2812. else if (photonServerSettings != null)
  2813. {
  2814. Debug.LogWarning("photonServerSettings is not null. Will not LoadOrCreateSettings().");
  2815. return;
  2816. }
  2817. // try to load the resource / asset (ServerSettings a.k.a. PhotonServerSettings)
  2818. photonServerSettings = (ServerSettings)Resources.Load(PhotonNetwork.ServerSettingsFileName, typeof(ServerSettings));
  2819. if (photonServerSettings != null)
  2820. {
  2821. return;
  2822. }
  2823. // create the ScriptableObject if it could not be loaded
  2824. if (photonServerSettings == null)
  2825. {
  2826. photonServerSettings = (ServerSettings)ScriptableObject.CreateInstance("ServerSettings");
  2827. if (photonServerSettings == null)
  2828. {
  2829. Debug.LogError("Failed to create ServerSettings. PUN is unable to run this way. If you deleted it from the project, reload the Editor.");
  2830. return;
  2831. }
  2832. }
  2833. #if UNITY_EDITOR
  2834. // in the editor, store the settings file as it could not be loaded
  2835. // unless Unity still imports assets
  2836. if (UnityEditor.EditorApplication.isUpdating)
  2837. {
  2838. EditorApplication.delayCall += delegate { LoadOrCreateSettings(true); };
  2839. return;
  2840. }
  2841. string punResourcesDirectory = PhotonNetwork.FindPunAssetFolder() + "Resources/";
  2842. string serverSettingsAssetPath = punResourcesDirectory + PhotonNetwork.ServerSettingsFileName + ".asset";
  2843. string serverSettingsDirectory = Path.GetDirectoryName(serverSettingsAssetPath);
  2844. if (!Directory.Exists(serverSettingsDirectory))
  2845. {
  2846. Directory.CreateDirectory(serverSettingsDirectory);
  2847. AssetDatabase.ImportAsset(serverSettingsDirectory);
  2848. }
  2849. if (!File.Exists(serverSettingsAssetPath))
  2850. {
  2851. AssetDatabase.CreateAsset(photonServerSettings, serverSettingsAssetPath);
  2852. }
  2853. AssetDatabase.SaveAssets();
  2854. // if the project does not have PhotonServerSettings yet, enable "Development Build" to use the Dev Region.
  2855. EditorUserBuildSettings.development = true;
  2856. #endif
  2857. }
  2858. #if UNITY_EDITOR
  2859. /// <summary>
  2860. /// Finds the asset path base on its name or search query: https://docs.unity3d.com/ScriptReference/AssetDatabase.FindAssets.html
  2861. /// </summary>
  2862. /// <returns>The asset path.</returns>
  2863. /// <param name="asset">Asset.</param>
  2864. public static string FindAssetPath(string asset)
  2865. {
  2866. string[] guids = AssetDatabase.FindAssets (asset, null);
  2867. if (guids.Length != 1)
  2868. {
  2869. return string.Empty;
  2870. } else
  2871. {
  2872. return AssetDatabase.GUIDToAssetPath (guids [0]);
  2873. }
  2874. }
  2875. /// <summary>
  2876. /// Finds the pun asset folder. Something like Assets/Photon Unity Networking/Resources/
  2877. /// </summary>
  2878. /// <returns>The pun asset folder.</returns>
  2879. public static string FindPunAssetFolder()
  2880. {
  2881. string _thisPath = FindAssetPath("PunClasses");
  2882. string _PunFolderPath = string.Empty;
  2883. //Debug.Log("FindPunAssetFolder "+_thisPath);
  2884. string[] subdirectoryEntries = _thisPath.Split ('/');
  2885. foreach (string dir in subdirectoryEntries)
  2886. {
  2887. if (!string.IsNullOrEmpty (dir))
  2888. {
  2889. _PunFolderPath += dir +"/";
  2890. if (string.Equals (dir, "PhotonUnityNetworking"))
  2891. {
  2892. // Debug.Log("_PunFolderPath "+_PunFolderPath);
  2893. return _PunFolderPath;
  2894. }
  2895. }
  2896. }
  2897. //Debug.Log("_PunFolderPath fallback to default Assets/Photon Unity Networking/");
  2898. return "Assets/Photon/PhotonUnityNetworking/";
  2899. }
  2900. /// <summary>
  2901. /// Internally used by Editor scripts, called on Hierarchy change (includes scene save) to remove surplus hidden PhotonHandlers.
  2902. /// </summary>
  2903. /// <remarks>This is done in this class, because the Editor assembly can't access PhotonHandler.</remarks>
  2904. public static void InternalCleanPhotonMonoFromSceneIfStuck()
  2905. {
  2906. PhotonHandler[] photonHandlers = GameObject.FindObjectsOfType(typeof(PhotonHandler)) as PhotonHandler[];
  2907. if (photonHandlers != null && photonHandlers.Length > 0)
  2908. {
  2909. Debug.Log("Cleaning up hidden PhotonHandler instances in scene. Please save the scene to fix the problem.");
  2910. foreach (PhotonHandler photonHandler in photonHandlers)
  2911. {
  2912. // Debug.Log("Removing Handler: " + photonHandler + " photonHandler.gameObject: " + photonHandler.gameObject);
  2913. if (photonHandler.gameObject != null && photonHandler.gameObject.name == "PhotonMono")
  2914. {
  2915. photonHandler.gameObject.hideFlags = 0;
  2916. GameObject.DestroyImmediate(photonHandler.gameObject);
  2917. }
  2918. Component.DestroyImmediate(photonHandler);
  2919. }
  2920. }
  2921. }
  2922. #endif
  2923. }
  2924. }