added networking, win conditions, missions, guards, and music
This commit is contained in:
parent
c622cc2b97
commit
d08eb2bb7d
303 changed files with 147834 additions and 830 deletions
|
@ -1,7 +1,7 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: cb288d82f67d27a49bf14e1ab68d86e6
|
guid: 3ab83fe7cd8f9544f9a8de5a5cfe304c
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
timeCreated: 1505311868
|
timeCreated: 1505977769
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
userData:
|
userData:
|
2008
Project Undercover/Assets/Animation/HandShake.anim
Normal file
2008
Project Undercover/Assets/Animation/HandShake.anim
Normal file
File diff suppressed because it is too large
Load diff
9
Project Undercover/Assets/Animation/HandShake.anim.meta
Normal file
9
Project Undercover/Assets/Animation/HandShake.anim.meta
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0d1b4be09a8f56b49ae0c9839f5e5570
|
||||||
|
timeCreated: 1505977769
|
||||||
|
licenseType: Free
|
||||||
|
NativeFormatImporter:
|
||||||
|
mainObjectFileID: 7400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
9
Project Undercover/Assets/Audio.meta
Normal file
9
Project Undercover/Assets/Audio.meta
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5242c4426d5340746807b1877e5d95f4
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1505987639
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
9
Project Undercover/Assets/Audio/Music.meta
Normal file
9
Project Undercover/Assets/Audio/Music.meta
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 40d44d307b5f45a40a9dd15007d086c2
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1505987639
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
BIN
Project Undercover/Assets/Audio/Music/mainmusic.wav
Normal file
BIN
Project Undercover/Assets/Audio/Music/mainmusic.wav
Normal file
Binary file not shown.
23
Project Undercover/Assets/Audio/Music/mainmusic.wav.meta
Normal file
23
Project Undercover/Assets/Audio/Music/mainmusic.wav.meta
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4c08f73877bf0b2499ffa79dc92ef7db
|
||||||
|
timeCreated: 1505987640
|
||||||
|
licenseType: Free
|
||||||
|
AudioImporter:
|
||||||
|
serializedVersion: 6
|
||||||
|
defaultSettings:
|
||||||
|
loadType: 0
|
||||||
|
sampleRateSetting: 0
|
||||||
|
sampleRateOverride: 44100
|
||||||
|
compressionFormat: 1
|
||||||
|
quality: 1
|
||||||
|
conversionMode: 0
|
||||||
|
platformSettingOverrides: {}
|
||||||
|
forceToMono: 0
|
||||||
|
normalize: 1
|
||||||
|
preloadAudioData: 1
|
||||||
|
loadInBackground: 0
|
||||||
|
ambisonic: 0
|
||||||
|
3D: 1
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
76
Project Undercover/Assets/Materials/Light.mat
Normal file
76
Project Undercover/Assets/Materials/Light.mat
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!21 &2100000
|
||||||
|
Material:
|
||||||
|
serializedVersion: 6
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_PrefabParentObject: {fileID: 0}
|
||||||
|
m_PrefabInternal: {fileID: 0}
|
||||||
|
m_Name: Light
|
||||||
|
m_Shader: {fileID: 10755, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_ShaderKeywords:
|
||||||
|
m_LightmapFlags: 4
|
||||||
|
m_EnableInstancingVariants: 0
|
||||||
|
m_DoubleSidedGI: 0
|
||||||
|
m_CustomRenderQueue: -1
|
||||||
|
stringTagMap: {}
|
||||||
|
disabledShaderPasses: []
|
||||||
|
m_SavedProperties:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TexEnvs:
|
||||||
|
- _BumpMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailAlbedoMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailMask:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailNormalMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _EmissionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MainTex:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MetallicGlossMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _OcclusionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _ParallaxMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
m_Floats:
|
||||||
|
- _BumpScale: 1
|
||||||
|
- _Cutoff: 0.5
|
||||||
|
- _DetailNormalMapScale: 1
|
||||||
|
- _DstBlend: 0
|
||||||
|
- _GlossMapScale: 1
|
||||||
|
- _Glossiness: 0.5
|
||||||
|
- _GlossyReflections: 1
|
||||||
|
- _Metallic: 0
|
||||||
|
- _Mode: 0
|
||||||
|
- _OcclusionStrength: 1
|
||||||
|
- _Parallax: 0.02
|
||||||
|
- _SmoothnessTextureChannel: 0
|
||||||
|
- _SpecularHighlights: 1
|
||||||
|
- _SrcBlend: 1
|
||||||
|
- _UVSec: 0
|
||||||
|
- _ZWrite: 1
|
||||||
|
m_Colors:
|
||||||
|
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
9
Project Undercover/Assets/Materials/Light.mat.meta
Normal file
9
Project Undercover/Assets/Materials/Light.mat.meta
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 589cb9a3c1f004049a397907ef9631d5
|
||||||
|
timeCreated: 1505886045
|
||||||
|
licenseType: Free
|
||||||
|
NativeFormatImporter:
|
||||||
|
mainObjectFileID: 2100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
9
Project Undercover/Assets/NavMeshes.meta
Normal file
9
Project Undercover/Assets/NavMeshes.meta
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 808ef5ffd9b486c4290e3fd56aef73ec
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1505886940
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
BIN
Project Undercover/Assets/NavMeshes/NavMesh.asset
Normal file
BIN
Project Undercover/Assets/NavMeshes/NavMesh.asset
Normal file
Binary file not shown.
9
Project Undercover/Assets/NavMeshes/NavMesh.asset.meta
Normal file
9
Project Undercover/Assets/NavMeshes/NavMesh.asset.meta
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d7eb36ff892a3a7438b868633aac89d0
|
||||||
|
timeCreated: 1505764510
|
||||||
|
licenseType: Free
|
||||||
|
NativeFormatImporter:
|
||||||
|
mainObjectFileID: 23800000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
9
Project Undercover/Assets/Plugin.meta
Normal file
9
Project Undercover/Assets/Plugin.meta
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1fcf46d4b6bf9454ea18df08e17a920e
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1505887680
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9181f568e0445cd439c99917503ab6e5
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1505887640
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 079a99fc83b728040b6c06bd4562b9cf
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1505887640
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 66bfa0881bc062540b586572bd1489ea
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1505887640
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,235 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="AccountService.cs" company="Exit Games GmbH">
|
||||||
|
// Photon Cloud Account Service - Copyright (C) 2012 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// Provides methods to register a new user-account for the Photon Cloud and
|
||||||
|
// get the resulting appId.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
|
||||||
|
using System.Net.Security;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
|
||||||
|
public class AccountService
|
||||||
|
{
|
||||||
|
private const string ServiceUrl = "https://service.exitgames.com/AccountExt/AccountServiceExt.aspx";
|
||||||
|
|
||||||
|
private Action<AccountService> registrationCallback; // optional (when using async reg)
|
||||||
|
|
||||||
|
public string Message { get; private set; } // msg from server (in case of success, this is the appid)
|
||||||
|
|
||||||
|
protected internal Exception Exception { get; set; } // exceptions in account-server communication
|
||||||
|
|
||||||
|
public string AppId { get; private set; }
|
||||||
|
|
||||||
|
public string AppId2 { get; private set; }
|
||||||
|
|
||||||
|
public int ReturnCode { get; private set; } // 0 = OK. anything else is a error with Message
|
||||||
|
|
||||||
|
public enum Origin : byte { ServerWeb = 1, CloudWeb = 2, Pun = 3, Playmaker = 4 };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a instance of the Account Service to register Photon Cloud accounts.
|
||||||
|
/// </summary>
|
||||||
|
public AccountService()
|
||||||
|
{
|
||||||
|
WebRequest.DefaultWebProxy = null;
|
||||||
|
ServicePointManager.ServerCertificateValidationCallback = Validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Validator(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors policyErrors)
|
||||||
|
{
|
||||||
|
return true; // any certificate is ok in this case
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to create a Photon Cloud Account.
|
||||||
|
/// Check ReturnCode, Message and AppId to get the result of this attempt.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="email">Email of the account.</param>
|
||||||
|
/// <param name="origin">Marks which channel created the new account (if it's new).</param>
|
||||||
|
/// <param name="serviceType">Defines which type of Photon-service is being requested.</param>
|
||||||
|
public void RegisterByEmail(string email, Origin origin, string serviceType = null)
|
||||||
|
{
|
||||||
|
this.registrationCallback = null;
|
||||||
|
this.AppId = string.Empty;
|
||||||
|
this.AppId2 = string.Empty;
|
||||||
|
this.Message = string.Empty;
|
||||||
|
this.ReturnCode = -1;
|
||||||
|
|
||||||
|
string result;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
WebRequest req = HttpWebRequest.Create(this.RegistrationUri(email, (byte)origin, serviceType));
|
||||||
|
HttpWebResponse resp = req.GetResponse() as HttpWebResponse;
|
||||||
|
|
||||||
|
// now read result
|
||||||
|
StreamReader reader = new StreamReader(resp.GetResponseStream());
|
||||||
|
result = reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
this.Message = "Failed to connect to Cloud Account Service. Please register via account website.";
|
||||||
|
this.Exception = ex;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ParseResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to create a Photon Cloud Account asynchronously.
|
||||||
|
/// Once your callback is called, check ReturnCode, Message and AppId to get the result of this attempt.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="email">Email of the account.</param>
|
||||||
|
/// <param name="origin">Marks which channel created the new account (if it's new).</param>
|
||||||
|
/// <param name="serviceType">Defines which type of Photon-service is being requested.</param>
|
||||||
|
/// <param name="callback">Called when the result is available.</param>
|
||||||
|
public void RegisterByEmailAsync(string email, Origin origin, string serviceType, Action<AccountService> callback = null)
|
||||||
|
{
|
||||||
|
this.registrationCallback = callback;
|
||||||
|
this.AppId = string.Empty;
|
||||||
|
this.AppId2 = string.Empty;
|
||||||
|
this.Message = string.Empty;
|
||||||
|
this.ReturnCode = -1;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(this.RegistrationUri(email, (byte)origin, serviceType));
|
||||||
|
req.Timeout = 5000;
|
||||||
|
req.BeginGetResponse(this.OnRegisterByEmailCompleted, req);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
this.Message = "Failed to connect to Cloud Account Service. Please register via account website.";
|
||||||
|
this.Exception = ex;
|
||||||
|
if (this.registrationCallback != null)
|
||||||
|
{
|
||||||
|
this.registrationCallback(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal callback with result of async HttpWebRequest (in RegisterByEmailAsync).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ar"></param>
|
||||||
|
private void OnRegisterByEmailCompleted(IAsyncResult ar)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HttpWebRequest request = (HttpWebRequest)ar.AsyncState;
|
||||||
|
HttpWebResponse response = request.EndGetResponse(ar) as HttpWebResponse;
|
||||||
|
|
||||||
|
if (response != null && response.StatusCode == HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
// no error. use the result
|
||||||
|
StreamReader reader = new StreamReader(response.GetResponseStream());
|
||||||
|
string result = reader.ReadToEnd();
|
||||||
|
|
||||||
|
this.ParseResult(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// a response but some error on server. show message
|
||||||
|
this.Message = "Failed to connect to Cloud Account Service. Please register via account website.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// not even a response. show message
|
||||||
|
this.Message = "Failed to connect to Cloud Account Service. Please register via account website.";
|
||||||
|
this.Exception = ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.registrationCallback != null)
|
||||||
|
{
|
||||||
|
this.registrationCallback(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the service-call Uri, escaping the email for security reasons.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="email">Email of the account.</param>
|
||||||
|
/// <param name="origin">1 = server-web, 2 = cloud-web, 3 = PUN, 4 = playmaker</param>
|
||||||
|
/// <param name="serviceType">Defines which type of Photon-service is being requested. Options: "", "voice", "chat"</param>
|
||||||
|
/// <returns>Uri to call.</returns>
|
||||||
|
private Uri RegistrationUri(string email, byte origin, string serviceType)
|
||||||
|
{
|
||||||
|
if (serviceType == null)
|
||||||
|
{
|
||||||
|
serviceType = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
string emailEncoded = Uri.EscapeDataString(email);
|
||||||
|
string uriString = string.Format("{0}?email={1}&origin={2}&serviceType={3}", ServiceUrl, emailEncoded, origin, serviceType);
|
||||||
|
|
||||||
|
return new Uri(uriString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the Json response and applies it to local properties.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
private void ParseResult(string result)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(result))
|
||||||
|
{
|
||||||
|
this.Message = "Server's response was empty. Please register through account website during this service interruption.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, string> values = JsonConvert.DeserializeObject<Dictionary<string, string>>(result);
|
||||||
|
if (values == null)
|
||||||
|
{
|
||||||
|
this.Message = "Service temporarily unavailable. Please register through account website.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int returnCodeInt = -1;
|
||||||
|
string returnCodeString = string.Empty;
|
||||||
|
string message;
|
||||||
|
string messageDetailed;
|
||||||
|
|
||||||
|
values.TryGetValue("ReturnCode", out returnCodeString);
|
||||||
|
values.TryGetValue("Message", out message);
|
||||||
|
values.TryGetValue("MessageDetailed", out messageDetailed);
|
||||||
|
|
||||||
|
int.TryParse(returnCodeString, out returnCodeInt);
|
||||||
|
|
||||||
|
this.ReturnCode = returnCodeInt;
|
||||||
|
if (returnCodeInt == 0)
|
||||||
|
{
|
||||||
|
// returnCode == 0 means: all ok. message is new AppId
|
||||||
|
this.AppId = message;
|
||||||
|
if (PhotonEditorUtils.HasVoice)
|
||||||
|
{
|
||||||
|
this.AppId2 = messageDetailed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// any error gives returnCode != 0
|
||||||
|
this.AppId = string.Empty;
|
||||||
|
if (PhotonEditorUtils.HasVoice)
|
||||||
|
{
|
||||||
|
this.AppId2 = string.Empty;
|
||||||
|
}
|
||||||
|
this.Message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 166dfe22956ef0341b28e18d0499e363
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,232 @@
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[CanEditMultipleObjects]
|
||||||
|
[CustomEditor(typeof(CullArea))]
|
||||||
|
public class CullAreaEditor : Editor
|
||||||
|
{
|
||||||
|
private bool alignEditorCamera;
|
||||||
|
|
||||||
|
private CullArea cullArea;
|
||||||
|
|
||||||
|
private enum UP_AXIS_OPTIONS
|
||||||
|
{
|
||||||
|
SideScrollerMode = 0,
|
||||||
|
TopDownOr3DMode = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private UP_AXIS_OPTIONS upAxisOptions;
|
||||||
|
|
||||||
|
public void OnEnable()
|
||||||
|
{
|
||||||
|
cullArea = (CullArea) target;
|
||||||
|
|
||||||
|
// Destroying the newly created cull area if there is already one existing
|
||||||
|
if (FindObjectsOfType<CullArea>().Length > 1)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Destroying newly created cull area because there is already one existing in the scene.");
|
||||||
|
|
||||||
|
DestroyImmediate(cullArea);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevents the dropdown from resetting
|
||||||
|
if (cullArea != null)
|
||||||
|
{
|
||||||
|
upAxisOptions = cullArea.YIsUpAxis ? UP_AXIS_OPTIONS.SideScrollerMode : UP_AXIS_OPTIONS.TopDownOr3DMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical();
|
||||||
|
|
||||||
|
if (Application.isEditor && !Application.isPlaying)
|
||||||
|
{
|
||||||
|
OnInspectorGUIEditMode();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnInspectorGUIPlayMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the inspector GUI when edit mode is active.
|
||||||
|
/// </summary>
|
||||||
|
private void OnInspectorGUIEditMode()
|
||||||
|
{
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
|
||||||
|
#region DEFINE_UP_AXIS
|
||||||
|
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical();
|
||||||
|
EditorGUILayout.LabelField("Select game type", EditorStyles.boldLabel);
|
||||||
|
upAxisOptions = (UP_AXIS_OPTIONS) EditorGUILayout.EnumPopup("Game type", upAxisOptions);
|
||||||
|
cullArea.YIsUpAxis = (upAxisOptions == UP_AXIS_OPTIONS.SideScrollerMode);
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
#region SUBDIVISION
|
||||||
|
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical();
|
||||||
|
EditorGUILayout.LabelField("Set the number of subdivisions", EditorStyles.boldLabel);
|
||||||
|
cullArea.NumberOfSubdivisions = EditorGUILayout.IntSlider("Number of subdivisions", cullArea.NumberOfSubdivisions, 0, CullArea.MAX_NUMBER_OF_SUBDIVISIONS);
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
if (cullArea.NumberOfSubdivisions != 0)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < cullArea.Subdivisions.Length; ++index)
|
||||||
|
{
|
||||||
|
if ((index + 1) <= cullArea.NumberOfSubdivisions)
|
||||||
|
{
|
||||||
|
string countMessage = (index + 1) + ". Subdivision: row / column count";
|
||||||
|
|
||||||
|
EditorGUILayout.BeginVertical();
|
||||||
|
cullArea.Subdivisions[index] = EditorGUILayout.Vector2Field(countMessage, cullArea.Subdivisions[index]);
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cullArea.Subdivisions[index] = new UnityEngine.Vector2(1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
#region UPDATING_MAIN_CAMERA
|
||||||
|
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical();
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField("View and camera options", EditorStyles.boldLabel);
|
||||||
|
alignEditorCamera = EditorGUILayout.Toggle("Automatically align editor view with grid", alignEditorCamera);
|
||||||
|
|
||||||
|
if (Camera.main != null)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("Align main camera with grid"))
|
||||||
|
{
|
||||||
|
Undo.RecordObject(Camera.main.transform, "Align main camera with grid.");
|
||||||
|
|
||||||
|
float yCoord = cullArea.YIsUpAxis ? cullArea.Center.y : Mathf.Max(cullArea.Size.x, cullArea.Size.y);
|
||||||
|
float zCoord = cullArea.YIsUpAxis ? -Mathf.Max(cullArea.Size.x, cullArea.Size.y) : cullArea.Center.y;
|
||||||
|
|
||||||
|
Camera.main.transform.position = new Vector3(cullArea.Center.x, yCoord, zCoord);
|
||||||
|
Camera.main.transform.LookAt(cullArea.transform.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField("Current main camera position is " + Camera.main.transform.position.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
cullArea.RecreateCellHierarchy = true;
|
||||||
|
|
||||||
|
AlignEditorView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the inspector GUI when play mode is active.
|
||||||
|
/// </summary>
|
||||||
|
private void OnInspectorGUIPlayMode()
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("No changes allowed when game is running. Please exit play mode first.", EditorStyles.boldLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSceneGUI()
|
||||||
|
{
|
||||||
|
Handles.BeginGUI();
|
||||||
|
GUILayout.BeginArea(new Rect(Screen.width - 110, Screen.height - 90, 100, 60));
|
||||||
|
|
||||||
|
if (GUILayout.Button("Reset position"))
|
||||||
|
{
|
||||||
|
cullArea.transform.position = Vector3.zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Reset scaling"))
|
||||||
|
{
|
||||||
|
cullArea.transform.localScale = new Vector3(25.0f, 25.0f, 25.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.EndArea();
|
||||||
|
Handles.EndGUI();
|
||||||
|
|
||||||
|
// Checking for changes of the transform
|
||||||
|
if (cullArea.transform.hasChanged)
|
||||||
|
{
|
||||||
|
// Resetting position
|
||||||
|
float posX = cullArea.transform.position.x;
|
||||||
|
float posY = cullArea.YIsUpAxis ? cullArea.transform.position.y : 0.0f;
|
||||||
|
float posZ = !cullArea.YIsUpAxis ? cullArea.transform.position.z : 0.0f;
|
||||||
|
|
||||||
|
cullArea.transform.position = new Vector3(posX, posY, posZ);
|
||||||
|
|
||||||
|
// Resetting scaling
|
||||||
|
if (cullArea.Size.x < 1.0f || cullArea.Size.y < 1.0f)
|
||||||
|
{
|
||||||
|
float scaleX = (cullArea.transform.localScale.x < 1.0f) ? 1.0f : cullArea.transform.localScale.x;
|
||||||
|
float scaleY = (cullArea.transform.localScale.y < 1.0f) ? 1.0f : cullArea.transform.localScale.y;
|
||||||
|
float scaleZ = (cullArea.transform.localScale.z < 1.0f) ? 1.0f : cullArea.transform.localScale.z;
|
||||||
|
|
||||||
|
cullArea.transform.localScale = new Vector3(scaleX, scaleY, scaleZ);
|
||||||
|
|
||||||
|
Debug.LogWarning("Scaling on a single axis can not be lower than 1. Resetting...");
|
||||||
|
}
|
||||||
|
|
||||||
|
cullArea.RecreateCellHierarchy = true;
|
||||||
|
|
||||||
|
AlignEditorView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aligns the editor view with the created grid.
|
||||||
|
/// </summary>
|
||||||
|
private void AlignEditorView()
|
||||||
|
{
|
||||||
|
if (!alignEditorCamera)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This creates a temporary game object in order to align the editor view.
|
||||||
|
// The created game object is destroyed afterwards.
|
||||||
|
GameObject tmpGo = new GameObject();
|
||||||
|
|
||||||
|
float yCoord = cullArea.YIsUpAxis ? cullArea.Center.y : Mathf.Max(cullArea.Size.x, cullArea.Size.y);
|
||||||
|
float zCoord = cullArea.YIsUpAxis ? -Mathf.Max(cullArea.Size.x, cullArea.Size.y) : cullArea.Center.y;
|
||||||
|
|
||||||
|
tmpGo.transform.position = new Vector3(cullArea.Center.x, yCoord, zCoord);
|
||||||
|
tmpGo.transform.LookAt(cullArea.transform.position);
|
||||||
|
|
||||||
|
if (SceneView.lastActiveSceneView != null)
|
||||||
|
{
|
||||||
|
SceneView.lastActiveSceneView.AlignViewToObject(tmpGo.transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroyImmediate(tmpGo);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: abadaa451a7bff0489078ed9eec61133
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
Binary file not shown.
|
@ -0,0 +1,37 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0268f98d7c649564a818b0768fc68d4b
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
PluginImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
platformData:
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,68 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="PhotonConverter.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// Script to convert old RPC attributes into new RPC attributes.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if UNITY_5 && !UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2 || UNITY_5_4_OR_NEWER
|
||||||
|
#define UNITY_MIN_5_3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
public class PhotonConverter : Photon.MonoBehaviour
|
||||||
|
{
|
||||||
|
public static List<string> GetScriptsInFolder(string folder)
|
||||||
|
{
|
||||||
|
List<string> scripts = new List<string>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
scripts.AddRange(Directory.GetFiles(folder, "*.cs", SearchOption.AllDirectories));
|
||||||
|
scripts.AddRange(Directory.GetFiles(folder, "*.js", SearchOption.AllDirectories));
|
||||||
|
scripts.AddRange(Directory.GetFiles(folder, "*.boo", SearchOption.AllDirectories));
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Debug.Log("Getting script list from folder " + folder + " failed. Exception:\n" + ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return scripts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// default path: "Assets"
|
||||||
|
public static void ConvertRpcAttribute(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
path = "Assets";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> scripts = GetScriptsInFolder(path);
|
||||||
|
foreach (string file in scripts)
|
||||||
|
{
|
||||||
|
string text = File.ReadAllText(file);
|
||||||
|
string textCopy = text;
|
||||||
|
if (file.EndsWith("PhotonConverter.cs"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.Replace("[RPC]", "[PunRPC]");
|
||||||
|
text = text.Replace("@RPC", "@PunRPC");
|
||||||
|
|
||||||
|
if (!text.Equals(textCopy))
|
||||||
|
{
|
||||||
|
File.WriteAllText(file, text);
|
||||||
|
Debug.Log("Converted RPC to PunRPC in: " + file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 15757b26cd9b53247be86da9e8da19dd
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,759 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="PhotonEditor.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// MenuItems and in-Editor scripts for PhotonNetwork.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditorInternal;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
public class PunWizardText
|
||||||
|
{
|
||||||
|
public string WindowTitle = "PUN Wizard";
|
||||||
|
public string SetupWizardWarningTitle = "Warning";
|
||||||
|
public string SetupWizardWarningMessage = "You have not yet run the Photon setup wizard! Your game won't be able to connect. See Windows -> Photon Unity Networking.";
|
||||||
|
public string MainMenuButton = "Main Menu";
|
||||||
|
public string SetupWizardTitle = "PUN Setup";
|
||||||
|
public string SetupWizardInfo = "Thanks for importing Photon Unity Networking.\nThis window should set you up.\n\n<b>-</b> To use an existing Photon Cloud App, enter your AppId.\n<b>-</b> To register an account or access an existing one, enter the account's mail address.\n<b>-</b> To use Photon OnPremise, skip this step.";
|
||||||
|
public string EmailOrAppIdLabel = "AppId or Email";
|
||||||
|
public string AlreadyRegisteredInfo = "The email is registered so we can't fetch your AppId (without password).\n\nPlease login online to get your AppId and paste it above.";
|
||||||
|
public string SkipRegistrationInfo = "Skipping? No problem:\nEdit your server settings in the PhotonServerSettings file.";
|
||||||
|
public string RegisteredNewAccountInfo = "We created a (free) account and fetched you an AppId.\nWelcome. Your PUN project is setup.";
|
||||||
|
public string AppliedToSettingsInfo = "Your AppId is now applied to this project.";
|
||||||
|
public string SetupCompleteInfo = "<b>Done!</b>\nAll connection settings can be edited in the <b>PhotonServerSettings</b> now.\nHave a look.";
|
||||||
|
public string CloseWindowButton = "Close";
|
||||||
|
public string SkipButton = "Skip";
|
||||||
|
public string SetupButton = "Setup Project";
|
||||||
|
public string MobileExportNoteLabel = "Build for mobiles impossible. Get PUN+ or Unity Pro for mobile or use Unity 5.";
|
||||||
|
public string MobilePunPlusExportNoteLabel = "PUN+ available. Using native sockets for iOS/Android.";
|
||||||
|
public string CancelButton = "Cancel";
|
||||||
|
public string PUNWizardLabel = "PUN Wizard";
|
||||||
|
public string SettingsButton = "Settings";
|
||||||
|
public string SetupServerCloudLabel = "Setup wizard for setting up your own server or the cloud.";
|
||||||
|
public string WarningPhotonDisconnect = "";
|
||||||
|
public string StartButton = "Start";
|
||||||
|
public string LocateSettingsButton = "Locate PhotonServerSettings";
|
||||||
|
public string SettingsHighlightLabel = "Highlights the used photon settings file in the project.";
|
||||||
|
public string DocumentationLabel = "Documentation";
|
||||||
|
public string OpenPDFText = "Reference PDF";
|
||||||
|
public string OpenPDFTooltip = "Opens the local documentation pdf.";
|
||||||
|
public string OpenDevNetText = "DevNet / Manual";
|
||||||
|
public string OpenDevNetTooltip = "Online documentation for Photon.";
|
||||||
|
public string OpenCloudDashboardText = "Cloud Dashboard Login";
|
||||||
|
public string OpenCloudDashboardTooltip = "Review Cloud App information and statistics.";
|
||||||
|
public string OpenForumText = "Open Forum";
|
||||||
|
public string OpenForumTooltip = "Online support for Photon.";
|
||||||
|
public string OkButton = "Ok";
|
||||||
|
public string OwnHostCloudCompareLabel = "I am not quite sure how 'my own host' compares to 'cloud'.";
|
||||||
|
public string ComparisonPageButton = "Cloud versus OnPremise";
|
||||||
|
public string ConnectionTitle = "Connecting";
|
||||||
|
public string ConnectionInfo = "Connecting to the account service...";
|
||||||
|
public string ErrorTextTitle = "Error";
|
||||||
|
public string IncorrectRPCListTitle = "Warning: RPC-list becoming incompatible!";
|
||||||
|
public string IncorrectRPCListLabel = "Your project's RPC-list is full, so we can't add some RPCs just compiled.\n\nBy removing outdated RPCs, the list will be long enough but incompatible with older client builds!\n\nMake sure you change the game version where you use PhotonNetwork.ConnectUsingSettings().";
|
||||||
|
public string RemoveOutdatedRPCsLabel = "Remove outdated RPCs";
|
||||||
|
public string FullRPCListTitle = "Warning: RPC-list is full!";
|
||||||
|
public string FullRPCListLabel = "Your project's RPC-list is too long for PUN.\n\nYou can change PUN's source to use short-typed RPC index. Look for comments 'LIMITS RPC COUNT'\n\nAlternatively, remove some RPC methods (use more parameters per RPC maybe).\n\nAfter a RPC-list refresh, make sure you change the game version where you use PhotonNetwork.ConnectUsingSettings().";
|
||||||
|
public string SkipRPCListUpdateLabel = "Skip RPC-list update";
|
||||||
|
public string PUNNameReplaceTitle = "Warning: RPC-list Compatibility";
|
||||||
|
public string PUNNameReplaceLabel = "PUN replaces RPC names with numbers by using the RPC-list. All clients must use the same list for that.\n\nClearing it most likely makes your client incompatible with previous versions! Change your game version or make sure the RPC-list matches other clients.";
|
||||||
|
public string RPCListCleared = "Clear RPC-list";
|
||||||
|
public string ServerSettingsCleanedWarning = "Cleared the PhotonServerSettings.RpcList! This makes new builds incompatible with older ones. Better change game version in PhotonNetwork.ConnectUsingSettings().";
|
||||||
|
public string RpcFoundMessage = "Some code uses the obsolete RPC attribute. PUN now requires the PunRPC attribute to mark remote-callable methods.\nThe Editor can search and replace that code which will modify your source.";
|
||||||
|
public string RpcFoundDialogTitle = "RPC Attribute Outdated";
|
||||||
|
public string RpcReplaceButton = "Replace. I got a backup.";
|
||||||
|
public string RpcSkipReplace = "Not now.";
|
||||||
|
public string WizardMainWindowInfo = "This window should help you find important settings for PUN, as well as documentation.";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[InitializeOnLoad]
|
||||||
|
public class PhotonEditor : EditorWindow
|
||||||
|
{
|
||||||
|
protected static Type WindowType = typeof (PhotonEditor);
|
||||||
|
|
||||||
|
protected Vector2 scrollPos = Vector2.zero;
|
||||||
|
|
||||||
|
private readonly Vector2 preferredSize = new Vector2(350, 400);
|
||||||
|
|
||||||
|
private static Texture2D BackgroundImage;
|
||||||
|
|
||||||
|
public static PunWizardText CurrentLang = new PunWizardText();
|
||||||
|
|
||||||
|
|
||||||
|
protected static AccountService.Origin RegisterOrigin = AccountService.Origin.Pun;
|
||||||
|
|
||||||
|
protected static string DocumentationLocation = "Assets/Photon Unity Networking/PhotonNetwork-Documentation.pdf";
|
||||||
|
|
||||||
|
protected static string UrlFreeLicense = "https://www.photonengine.com/dashboard/OnPremise";
|
||||||
|
|
||||||
|
protected static string UrlDevNet = "http://doc.photonengine.com/en/pun/current";
|
||||||
|
|
||||||
|
protected static string UrlForum = "http://forum.exitgames.com";
|
||||||
|
|
||||||
|
protected static string UrlCompare = "http://doc.photonengine.com/en/realtime/current/getting-started/onpremise-or-saas";
|
||||||
|
|
||||||
|
protected static string UrlHowToSetup = "http://doc.photonengine.com/en/onpremise/current/getting-started/photon-server-in-5min";
|
||||||
|
|
||||||
|
protected static string UrlAppIDExplained = "http://doc.photonengine.com/en/realtime/current/getting-started/obtain-your-app-id";
|
||||||
|
|
||||||
|
protected static string UrlAccountPage = "https://www.photonengine.com/Account/SignIn?email="; // opened in browser
|
||||||
|
|
||||||
|
protected static string UrlCloudDashboard = "https://www.photonengine.com/dashboard?email=";
|
||||||
|
|
||||||
|
|
||||||
|
private enum PhotonSetupStates
|
||||||
|
{
|
||||||
|
MainUi,
|
||||||
|
|
||||||
|
RegisterForPhotonCloud,
|
||||||
|
|
||||||
|
EmailAlreadyRegistered,
|
||||||
|
|
||||||
|
GoEditPhotonServerSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isSetupWizard = false;
|
||||||
|
|
||||||
|
private PhotonSetupStates photonSetupState = PhotonSetupStates.RegisterForPhotonCloud;
|
||||||
|
|
||||||
|
|
||||||
|
private bool minimumInput = false;
|
||||||
|
private bool useMail = false;
|
||||||
|
private bool useAppId = false;
|
||||||
|
private bool useSkip = false;
|
||||||
|
private bool highlightedSettings = false;
|
||||||
|
private bool close = false;
|
||||||
|
private string mailOrAppId = string.Empty;
|
||||||
|
|
||||||
|
|
||||||
|
private static double lastWarning = 0;
|
||||||
|
private static bool postCompileActionsDone;
|
||||||
|
|
||||||
|
private static bool isPunPlus;
|
||||||
|
private static bool androidLibExists;
|
||||||
|
private static bool iphoneLibExists;
|
||||||
|
|
||||||
|
|
||||||
|
// setup once on load
|
||||||
|
static PhotonEditor()
|
||||||
|
{
|
||||||
|
EditorApplication.projectWindowChanged += EditorUpdate;
|
||||||
|
EditorApplication.hierarchyWindowChanged += EditorUpdate;
|
||||||
|
EditorApplication.playmodeStateChanged += PlaymodeStateChanged;
|
||||||
|
EditorApplication.update += OnUpdate;
|
||||||
|
|
||||||
|
// detect optional packages
|
||||||
|
PhotonEditor.CheckPunPlus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup per window
|
||||||
|
public PhotonEditor()
|
||||||
|
{
|
||||||
|
minSize = this.preferredSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Window/Photon Unity Networking/PUN Wizard &p", false, 0)]
|
||||||
|
protected static void MenuItemOpenWizard()
|
||||||
|
{
|
||||||
|
PhotonEditor win = GetWindow(WindowType, false, CurrentLang.WindowTitle, true) as PhotonEditor;
|
||||||
|
win.photonSetupState = PhotonSetupStates.MainUi;
|
||||||
|
win.isSetupWizard = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Window/Photon Unity Networking/Highlight Server Settings %#&p", false, 1)]
|
||||||
|
protected static void MenuItemHighlightSettings()
|
||||||
|
{
|
||||||
|
HighlightSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates an Editor window, showing the cloud-registration wizard for Photon (entry point to setup PUN).</summary>
|
||||||
|
protected static void ShowRegistrationWizard()
|
||||||
|
{
|
||||||
|
PhotonEditor win = GetWindow(WindowType, false, CurrentLang.WindowTitle, true) as PhotonEditor;
|
||||||
|
win.photonSetupState = PhotonSetupStates.RegisterForPhotonCloud;
|
||||||
|
win.isSetupWizard = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// called 100 times / sec
|
||||||
|
private static void OnUpdate()
|
||||||
|
{
|
||||||
|
// after a compile, check RPCs to create a cache-list
|
||||||
|
if (!postCompileActionsDone && !EditorApplication.isCompiling && !EditorApplication.isPlayingOrWillChangePlaymode && PhotonNetwork.PhotonServerSettings != null)
|
||||||
|
{
|
||||||
|
#if UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5 || UNITY_5_0 || UNITY_5_3_AND_NEWER
|
||||||
|
if (EditorApplication.isUpdating)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PhotonEditor.UpdateRpcList();
|
||||||
|
postCompileActionsDone = true; // on compile, this falls back to false (without actively doing anything)
|
||||||
|
|
||||||
|
#if UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5 || UNITY_5_0 || UNITY_5_3_AND_NEWER
|
||||||
|
PhotonEditor.ImportWin8Support();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// called in editor, opens wizard for initial setup, keeps scene PhotonViews up to date and closes connections when compiling (to avoid issues)
|
||||||
|
private static void EditorUpdate()
|
||||||
|
{
|
||||||
|
if (PhotonNetwork.PhotonServerSettings == null)
|
||||||
|
{
|
||||||
|
PhotonNetwork.CreateSettings();
|
||||||
|
}
|
||||||
|
if (PhotonNetwork.PhotonServerSettings == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverSetting is null when the file gets deleted. otherwise, the wizard should only run once and only if hosting option is not (yet) set
|
||||||
|
if (!PhotonNetwork.PhotonServerSettings.DisableAutoOpenWizard && PhotonNetwork.PhotonServerSettings.HostType == ServerSettings.HostingOption.NotSet)
|
||||||
|
{
|
||||||
|
ShowRegistrationWizard();
|
||||||
|
PhotonNetwork.PhotonServerSettings.DisableAutoOpenWizard = true;
|
||||||
|
PhotonEditor.SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for TCP crash. Plus this surpresses any other recompile errors.
|
||||||
|
if (EditorApplication.isCompiling)
|
||||||
|
{
|
||||||
|
if (PhotonNetwork.connected)
|
||||||
|
{
|
||||||
|
if (lastWarning > EditorApplication.timeSinceStartup - 3)
|
||||||
|
{
|
||||||
|
// Prevent error spam
|
||||||
|
Debug.LogWarning(CurrentLang.WarningPhotonDisconnect);
|
||||||
|
lastWarning = EditorApplication.timeSinceStartup;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotonNetwork.Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// called in editor on change of play-mode (used to show a message popup that connection settings are incomplete)
|
||||||
|
private static void PlaymodeStateChanged()
|
||||||
|
{
|
||||||
|
if (EditorApplication.isPlaying || !EditorApplication.isPlayingOrWillChangePlaymode)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PhotonNetwork.PhotonServerSettings.HostType == ServerSettings.HostingOption.NotSet)
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog(CurrentLang.SetupWizardWarningTitle, CurrentLang.SetupWizardWarningMessage, CurrentLang.OkButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region GUI and Wizard
|
||||||
|
|
||||||
|
// Window Update() callback. On-demand, when Window is open
|
||||||
|
protected void Update()
|
||||||
|
{
|
||||||
|
if (this.close)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnGUI()
|
||||||
|
{
|
||||||
|
if (BackgroundImage == null)
|
||||||
|
{
|
||||||
|
BackgroundImage = AssetDatabase.LoadAssetAtPath("Assets/Photon Unity Networking/Editor/PhotonNetwork/background.jpg", typeof(Texture2D)) as Texture2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotonSetupStates oldGuiState = this.photonSetupState; // used to fix an annoying Editor input field issue: wont refresh until focus is changed.
|
||||||
|
|
||||||
|
GUI.SetNextControlName("");
|
||||||
|
this.scrollPos = GUILayout.BeginScrollView(this.scrollPos);
|
||||||
|
|
||||||
|
|
||||||
|
if (this.photonSetupState == PhotonSetupStates.MainUi)
|
||||||
|
{
|
||||||
|
UiMainWizard();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UiSetupApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GUILayout.EndScrollView();
|
||||||
|
|
||||||
|
if (oldGuiState != this.photonSetupState)
|
||||||
|
{
|
||||||
|
GUI.FocusControl("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected virtual void UiSetupApp()
|
||||||
|
{
|
||||||
|
GUI.skin.label.wordWrap = true;
|
||||||
|
if (!this.isSetupWizard)
|
||||||
|
{
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
if (GUILayout.Button(CurrentLang.MainMenuButton, GUILayout.ExpandWidth(false)))
|
||||||
|
{
|
||||||
|
this.photonSetupState = PhotonSetupStates.MainUi;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// setup header
|
||||||
|
UiTitleBox(CurrentLang.SetupWizardTitle, BackgroundImage);
|
||||||
|
|
||||||
|
// setup info text
|
||||||
|
GUI.skin.label.richText = true;
|
||||||
|
GUILayout.Label(CurrentLang.SetupWizardInfo);
|
||||||
|
|
||||||
|
// input of appid or mail
|
||||||
|
EditorGUILayout.Separator();
|
||||||
|
GUILayout.Label(CurrentLang.EmailOrAppIdLabel);
|
||||||
|
this.mailOrAppId = EditorGUILayout.TextField(this.mailOrAppId).Trim(); // note: we trim all input
|
||||||
|
|
||||||
|
if (this.mailOrAppId.Contains("@"))
|
||||||
|
{
|
||||||
|
// this should be a mail address
|
||||||
|
this.minimumInput = (this.mailOrAppId.Length >= 5 && this.mailOrAppId.Contains("."));
|
||||||
|
this.useMail = this.minimumInput;
|
||||||
|
this.useAppId = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// this should be an appId
|
||||||
|
this.minimumInput = ServerSettings.IsAppId(this.mailOrAppId);
|
||||||
|
this.useMail = false;
|
||||||
|
this.useAppId = this.minimumInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
// button to skip setup
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
if (GUILayout.Button(CurrentLang.SkipButton, GUILayout.Width(100)))
|
||||||
|
{
|
||||||
|
this.photonSetupState = PhotonSetupStates.GoEditPhotonServerSettings;
|
||||||
|
this.useSkip = true;
|
||||||
|
this.useMail = false;
|
||||||
|
this.useAppId = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SETUP button
|
||||||
|
EditorGUI.BeginDisabledGroup(!this.minimumInput);
|
||||||
|
if (GUILayout.Button(CurrentLang.SetupButton, GUILayout.Width(100)))
|
||||||
|
{
|
||||||
|
this.useSkip = false;
|
||||||
|
GUIUtility.keyboardControl = 0;
|
||||||
|
if (this.useMail)
|
||||||
|
{
|
||||||
|
RegisterWithEmail(this.mailOrAppId); // sets state
|
||||||
|
}
|
||||||
|
if (this.useAppId)
|
||||||
|
{
|
||||||
|
this.photonSetupState = PhotonSetupStates.GoEditPhotonServerSettings;
|
||||||
|
Undo.RecordObject(PhotonNetwork.PhotonServerSettings, "Update PhotonServerSettings for PUN");
|
||||||
|
PhotonNetwork.PhotonServerSettings.UseCloud(this.mailOrAppId);
|
||||||
|
PhotonEditor.SaveSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
|
||||||
|
// existing account needs to fetch AppId online
|
||||||
|
if (this.photonSetupState == PhotonSetupStates.EmailAlreadyRegistered)
|
||||||
|
{
|
||||||
|
// button to open dashboard and get the AppId
|
||||||
|
GUILayout.Space(15);
|
||||||
|
GUILayout.Label(CurrentLang.AlreadyRegisteredInfo);
|
||||||
|
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
if (GUILayout.Button(new GUIContent(CurrentLang.OpenCloudDashboardText, CurrentLang.OpenCloudDashboardTooltip), GUILayout.Width(205)))
|
||||||
|
{
|
||||||
|
Application.OpenURL(UrlCloudDashboard + Uri.EscapeUriString(this.mailOrAppId));
|
||||||
|
this.mailOrAppId = "";
|
||||||
|
}
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (this.photonSetupState == PhotonSetupStates.GoEditPhotonServerSettings)
|
||||||
|
{
|
||||||
|
if (!this.highlightedSettings)
|
||||||
|
{
|
||||||
|
this.highlightedSettings = true;
|
||||||
|
HighlightSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.Space(15);
|
||||||
|
if (this.useSkip)
|
||||||
|
{
|
||||||
|
GUILayout.Label(CurrentLang.SkipRegistrationInfo);
|
||||||
|
}
|
||||||
|
else if (this.useMail)
|
||||||
|
{
|
||||||
|
GUILayout.Label(CurrentLang.RegisteredNewAccountInfo);
|
||||||
|
}
|
||||||
|
else if (this.useAppId)
|
||||||
|
{
|
||||||
|
GUILayout.Label(CurrentLang.AppliedToSettingsInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// setup-complete info
|
||||||
|
GUILayout.Space(15);
|
||||||
|
GUILayout.Label(CurrentLang.SetupCompleteInfo);
|
||||||
|
|
||||||
|
|
||||||
|
// close window (done)
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
if (GUILayout.Button(CurrentLang.CloseWindowButton, GUILayout.Width(205)))
|
||||||
|
{
|
||||||
|
this.close = true;
|
||||||
|
}
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
GUI.skin.label.richText = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UiTitleBox(string title, Texture2D bgIcon)
|
||||||
|
{
|
||||||
|
GUIStyle bgStyle = new GUIStyle(GUI.skin.GetStyle("Label"));
|
||||||
|
bgStyle.normal.background = bgIcon;
|
||||||
|
bgStyle.fontSize = 22;
|
||||||
|
bgStyle.fontStyle = FontStyle.Bold;
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
Rect scale = GUILayoutUtility.GetLastRect();
|
||||||
|
scale.height = 30;
|
||||||
|
|
||||||
|
GUI.Label(scale, title, bgStyle);
|
||||||
|
GUILayout.Space(scale.height+5);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UiMainWizard()
|
||||||
|
{
|
||||||
|
GUILayout.Space(15);
|
||||||
|
|
||||||
|
// title
|
||||||
|
UiTitleBox(CurrentLang.PUNWizardLabel, BackgroundImage);
|
||||||
|
|
||||||
|
// wizard info text
|
||||||
|
GUILayout.Label(CurrentLang.WizardMainWindowInfo);
|
||||||
|
GUILayout.Space(15);
|
||||||
|
|
||||||
|
|
||||||
|
// pun+ info
|
||||||
|
if (isPunPlus)
|
||||||
|
{
|
||||||
|
GUILayout.Label(CurrentLang.MobilePunPlusExportNoteLabel);
|
||||||
|
GUILayout.Space(15);
|
||||||
|
}
|
||||||
|
#if !(UNITY_5_0 || UNITY_5 || UNITY_5_3_AND_NEWER)
|
||||||
|
else if (!InternalEditorUtility.HasAdvancedLicenseOnBuildTarget(BuildTarget.Android) || !InternalEditorUtility.HasAdvancedLicenseOnBuildTarget(BuildTarget.iOS))
|
||||||
|
{
|
||||||
|
GUILayout.Label(CurrentLang.MobileExportNoteLabel);
|
||||||
|
GUILayout.Space(15);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// settings button
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
GUILayout.Label(CurrentLang.SettingsButton, EditorStyles.boldLabel, GUILayout.Width(100));
|
||||||
|
GUILayout.BeginVertical();
|
||||||
|
if (GUILayout.Button(new GUIContent(CurrentLang.LocateSettingsButton, CurrentLang.SettingsHighlightLabel)))
|
||||||
|
{
|
||||||
|
HighlightSettings();
|
||||||
|
}
|
||||||
|
if (GUILayout.Button(new GUIContent(CurrentLang.OpenCloudDashboardText, CurrentLang.OpenCloudDashboardTooltip)))
|
||||||
|
{
|
||||||
|
Application.OpenURL(UrlCloudDashboard + Uri.EscapeUriString(this.mailOrAppId));
|
||||||
|
}
|
||||||
|
if (GUILayout.Button(new GUIContent(CurrentLang.SetupButton, CurrentLang.SetupServerCloudLabel)))
|
||||||
|
{
|
||||||
|
this.photonSetupState = PhotonSetupStates.RegisterForPhotonCloud;
|
||||||
|
}
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
GUILayout.Space(15);
|
||||||
|
|
||||||
|
|
||||||
|
EditorGUILayout.Separator();
|
||||||
|
|
||||||
|
|
||||||
|
// documentation
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
GUILayout.Label(CurrentLang.DocumentationLabel, EditorStyles.boldLabel, GUILayout.Width(100));
|
||||||
|
GUILayout.BeginVertical();
|
||||||
|
if (GUILayout.Button(new GUIContent(CurrentLang.OpenPDFText, CurrentLang.OpenPDFTooltip)))
|
||||||
|
{
|
||||||
|
EditorUtility.OpenWithDefaultApp(DocumentationLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button(new GUIContent(CurrentLang.OpenDevNetText, CurrentLang.OpenDevNetTooltip)))
|
||||||
|
{
|
||||||
|
Application.OpenURL(UrlDevNet);
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI.skin.label.wordWrap = true;
|
||||||
|
GUILayout.Label(CurrentLang.OwnHostCloudCompareLabel);
|
||||||
|
if (GUILayout.Button(CurrentLang.ComparisonPageButton))
|
||||||
|
{
|
||||||
|
Application.OpenURL(UrlCompare);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (GUILayout.Button(new GUIContent(CurrentLang.OpenForumText, CurrentLang.OpenForumTooltip)))
|
||||||
|
{
|
||||||
|
Application.OpenURL(UrlForum);
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
protected virtual void RegisterWithEmail(string email)
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayProgressBar(CurrentLang.ConnectionTitle, CurrentLang.ConnectionInfo, 0.5f);
|
||||||
|
|
||||||
|
string accountServiceType = string.Empty;
|
||||||
|
if (PhotonEditorUtils.HasVoice)
|
||||||
|
{
|
||||||
|
accountServiceType = "voice";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AccountService client = new AccountService();
|
||||||
|
client.RegisterByEmail(email, RegisterOrigin, accountServiceType); // this is the synchronous variant using the static RegisterOrigin. "result" is in the client
|
||||||
|
|
||||||
|
EditorUtility.ClearProgressBar();
|
||||||
|
if (client.ReturnCode == 0)
|
||||||
|
{
|
||||||
|
this.mailOrAppId = client.AppId;
|
||||||
|
PhotonNetwork.PhotonServerSettings.UseCloud(this.mailOrAppId, 0);
|
||||||
|
if (PhotonEditorUtils.HasVoice)
|
||||||
|
{
|
||||||
|
PhotonNetwork.PhotonServerSettings.VoiceAppID = client.AppId2;
|
||||||
|
}
|
||||||
|
PhotonEditor.SaveSettings();
|
||||||
|
|
||||||
|
this.photonSetupState = PhotonSetupStates.GoEditPhotonServerSettings;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PhotonNetwork.PhotonServerSettings.HostType = ServerSettings.HostingOption.PhotonCloud;
|
||||||
|
PhotonEditor.SaveSettings();
|
||||||
|
|
||||||
|
Debug.LogWarning(client.Message + " ReturnCode: " + client.ReturnCode);
|
||||||
|
if (client.Message.Contains("registered"))
|
||||||
|
{
|
||||||
|
this.photonSetupState = PhotonSetupStates.EmailAlreadyRegistered;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog(CurrentLang.ErrorTextTitle, client.Message, CurrentLang.OkButton);
|
||||||
|
this.photonSetupState = PhotonSetupStates.RegisterForPhotonCloud;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected internal static bool CheckPunPlus()
|
||||||
|
{
|
||||||
|
androidLibExists = File.Exists("Assets/Plugins/Android/armeabi-v7a/libPhotonSocketPlugin.so") &&
|
||||||
|
File.Exists("Assets/Plugins/Android/x86/libPhotonSocketPlugin.so");
|
||||||
|
|
||||||
|
|
||||||
|
iphoneLibExists = File.Exists("Assets/Plugins/IOS/libPhotonSocketPlugin.a");
|
||||||
|
|
||||||
|
isPunPlus = androidLibExists || iphoneLibExists;
|
||||||
|
return isPunPlus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void ImportWin8Support()
|
||||||
|
{
|
||||||
|
if (EditorApplication.isCompiling || EditorApplication.isPlayingOrWillChangePlaymode)
|
||||||
|
{
|
||||||
|
return; // don't import while compiling
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5 || UNITY_5_0 || UNITY_5_3_AND_NEWER
|
||||||
|
const string win8Package = "Assets/Plugins/Photon3Unity3D-Win8.unitypackage";
|
||||||
|
|
||||||
|
bool win8LibsExist = File.Exists("Assets/Plugins/WP8/Photon3Unity3D.dll") && File.Exists("Assets/Plugins/Metro/Photon3Unity3D.dll");
|
||||||
|
if (!win8LibsExist && File.Exists(win8Package))
|
||||||
|
{
|
||||||
|
AssetDatabase.ImportPackage(win8Package, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Pings PhotonServerSettings and makes it selected (show in Inspector)
|
||||||
|
private static void HighlightSettings()
|
||||||
|
{
|
||||||
|
Selection.objects = new UnityEngine.Object[] { PhotonNetwork.PhotonServerSettings };
|
||||||
|
EditorGUIUtility.PingObject(PhotonNetwork.PhotonServerSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Marks settings object as dirty, so it gets saved.
|
||||||
|
// unity 5.3 changes the usecase for SetDirty(). but here we don't modify a scene object! so it's ok to use
|
||||||
|
private static void SaveSettings()
|
||||||
|
{
|
||||||
|
EditorUtility.SetDirty(PhotonNetwork.PhotonServerSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region RPC List Handling
|
||||||
|
|
||||||
|
public static void UpdateRpcList()
|
||||||
|
{
|
||||||
|
List<string> additionalRpcs = new List<string>();
|
||||||
|
HashSet<string> currentRpcs = new HashSet<string>();
|
||||||
|
|
||||||
|
var types = GetAllSubTypesInScripts(typeof(MonoBehaviour));
|
||||||
|
|
||||||
|
int countOldRpcs = 0;
|
||||||
|
foreach (var mono in types)
|
||||||
|
{
|
||||||
|
MethodInfo[] methods = mono.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
|
||||||
|
foreach (MethodInfo method in methods)
|
||||||
|
{
|
||||||
|
bool isOldRpc = false;
|
||||||
|
#pragma warning disable 618
|
||||||
|
// we let the Editor check for outdated RPC attributes in code. that should not cause a compile warning
|
||||||
|
if (method.IsDefined(typeof (RPC), false))
|
||||||
|
{
|
||||||
|
countOldRpcs++;
|
||||||
|
isOldRpc = true;
|
||||||
|
}
|
||||||
|
#pragma warning restore 618
|
||||||
|
|
||||||
|
if (isOldRpc || method.IsDefined(typeof(PunRPC), false))
|
||||||
|
{
|
||||||
|
currentRpcs.Add(method.Name);
|
||||||
|
|
||||||
|
if (!additionalRpcs.Contains(method.Name) && !PhotonNetwork.PhotonServerSettings.RpcList.Contains(method.Name))
|
||||||
|
{
|
||||||
|
additionalRpcs.Add(method.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalRpcs.Count > 0)
|
||||||
|
{
|
||||||
|
// LIMITS RPC COUNT
|
||||||
|
if (additionalRpcs.Count + PhotonNetwork.PhotonServerSettings.RpcList.Count >= byte.MaxValue)
|
||||||
|
{
|
||||||
|
if (currentRpcs.Count <= byte.MaxValue)
|
||||||
|
{
|
||||||
|
bool clearList = EditorUtility.DisplayDialog(CurrentLang.IncorrectRPCListTitle, CurrentLang.IncorrectRPCListLabel, CurrentLang.RemoveOutdatedRPCsLabel, CurrentLang.CancelButton);
|
||||||
|
if (clearList)
|
||||||
|
{
|
||||||
|
PhotonNetwork.PhotonServerSettings.RpcList.Clear();
|
||||||
|
PhotonNetwork.PhotonServerSettings.RpcList.AddRange(currentRpcs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog(CurrentLang.FullRPCListTitle, CurrentLang.FullRPCListLabel, CurrentLang.SkipRPCListUpdateLabel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
additionalRpcs.Sort();
|
||||||
|
Undo.RecordObject(PhotonNetwork.PhotonServerSettings, "Update PUN RPC-list");
|
||||||
|
PhotonNetwork.PhotonServerSettings.RpcList.AddRange(additionalRpcs);
|
||||||
|
PhotonEditor.SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countOldRpcs > 0)
|
||||||
|
{
|
||||||
|
bool convertRPCs = EditorUtility.DisplayDialog(CurrentLang.RpcFoundDialogTitle, CurrentLang.RpcFoundMessage, CurrentLang.RpcReplaceButton, CurrentLang.RpcSkipReplace);
|
||||||
|
if (convertRPCs)
|
||||||
|
{
|
||||||
|
PhotonConverter.ConvertRpcAttribute("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ClearRpcList()
|
||||||
|
{
|
||||||
|
bool clearList = EditorUtility.DisplayDialog(CurrentLang.PUNNameReplaceTitle, CurrentLang.PUNNameReplaceLabel, CurrentLang.RPCListCleared, CurrentLang.CancelButton);
|
||||||
|
if (clearList)
|
||||||
|
{
|
||||||
|
PhotonNetwork.PhotonServerSettings.RpcList.Clear();
|
||||||
|
Debug.LogWarning(CurrentLang.ServerSettingsCleanedWarning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static System.Type[] GetAllSubTypesInScripts(System.Type aBaseClass)
|
||||||
|
{
|
||||||
|
var result = new System.Collections.Generic.List<System.Type>();
|
||||||
|
System.Reflection.Assembly[] AS = System.AppDomain.CurrentDomain.GetAssemblies();
|
||||||
|
foreach (var A in AS)
|
||||||
|
{
|
||||||
|
// this skips all but the Unity-scripted assemblies for RPC-list creation. You could remove this to search all assemblies in project
|
||||||
|
if (!A.FullName.StartsWith("Assembly-"))
|
||||||
|
{
|
||||||
|
// Debug.Log("Skipping Assembly: " + A);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Debug.Log("Assembly: " + A.FullName);
|
||||||
|
System.Type[] types = A.GetTypes();
|
||||||
|
foreach (var T in types)
|
||||||
|
{
|
||||||
|
if (T.IsSubclassOf(aBaseClass))
|
||||||
|
{
|
||||||
|
result.Add(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dabbbed2a74eac44dac281f20d706ba8
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,70 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="PhotonEditorUtils.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// Unity Editor Utils
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
|
||||||
|
namespace ExitGames.Client.Photon
|
||||||
|
{
|
||||||
|
[InitializeOnLoad]
|
||||||
|
public class PhotonEditorUtils
|
||||||
|
{
|
||||||
|
/// <summary>True if the ChatClient of the Photon Chat API is available. If so, the editor may (e.g.) show additional options in settings.</summary>
|
||||||
|
public static bool HasChat;
|
||||||
|
/// <summary>True if the VoiceClient of the Photon Voice API is available. If so, the editor may (e.g.) show additional options in settings.</summary>
|
||||||
|
public static bool HasVoice;
|
||||||
|
/// <summary>True if the PhotonEditorUtils checked the available products / APIs. If so, the editor may (e.g.) show additional options in settings.</summary>
|
||||||
|
public static bool HasCheckedProducts;
|
||||||
|
|
||||||
|
static PhotonEditorUtils()
|
||||||
|
{
|
||||||
|
HasVoice = Type.GetType("ExitGames.Client.Photon.Voice.VoiceClient, Assembly-CSharp") != null || Type.GetType("ExitGames.Client.Photon.Voice.VoiceClient, Assembly-CSharp-firstpass") != null;
|
||||||
|
HasChat = Type.GetType("ExitGames.Client.Photon.Chat.ChatClient, Assembly-CSharp") != null || Type.GetType("ExitGames.Client.Photon.Chat.ChatClient, Assembly-CSharp-firstpass") != null;
|
||||||
|
PhotonEditorUtils.HasCheckedProducts = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void MountScriptingDefineSymbolToAllTargets(string defineSymbol)
|
||||||
|
{
|
||||||
|
foreach (BuildTargetGroup _group in Enum.GetValues(typeof(BuildTargetGroup)))
|
||||||
|
{
|
||||||
|
if (_group == BuildTargetGroup.Unknown) continue;
|
||||||
|
|
||||||
|
List<string> _defineSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(_group).Split(';').Select(d => d.Trim()).ToList();
|
||||||
|
|
||||||
|
if (!_defineSymbols.Contains(defineSymbol))
|
||||||
|
{
|
||||||
|
_defineSymbols.Add(defineSymbol);
|
||||||
|
PlayerSettings.SetScriptingDefineSymbolsForGroup(_group, string.Join(";", _defineSymbols.ToArray()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UnMountScriptingDefineSymbolToAllTargets(string defineSymbol)
|
||||||
|
{
|
||||||
|
foreach (BuildTargetGroup _group in Enum.GetValues(typeof(BuildTargetGroup)))
|
||||||
|
{
|
||||||
|
if (_group == BuildTargetGroup.Unknown) continue;
|
||||||
|
|
||||||
|
List<string> _defineSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(_group).Split(';').Select(d => d.Trim()).ToList();
|
||||||
|
|
||||||
|
if (_defineSymbols.Contains(defineSymbol))
|
||||||
|
{
|
||||||
|
_defineSymbols.Remove(defineSymbol);
|
||||||
|
PlayerSettings.SetScriptingDefineSymbolsForGroup(_group, string.Join(";", _defineSymbols.ToArray()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 42183086715e14a19a573546af09b321
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
|
@ -0,0 +1,281 @@
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
using Photon.Pun;
|
||||||
|
|
||||||
|
public class PhotonGUI
|
||||||
|
{
|
||||||
|
#region Styles
|
||||||
|
static GUIStyle m_DefaultTitleStyle;
|
||||||
|
public static GUIStyle DefaultTitleStyle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if( m_DefaultTitleStyle == null )
|
||||||
|
{
|
||||||
|
m_DefaultTitleStyle = new GUIStyle();
|
||||||
|
m_DefaultTitleStyle.border = new RectOffset( 2, 2, 2, 1 );
|
||||||
|
m_DefaultTitleStyle.margin = new RectOffset( 5, 5, 5, 0 );
|
||||||
|
m_DefaultTitleStyle.padding = new RectOffset( 5, 5, 0, 0 );
|
||||||
|
m_DefaultTitleStyle.alignment = TextAnchor.MiddleLeft;
|
||||||
|
m_DefaultTitleStyle.normal.background = ReorderableListResources.texTitleBackground;
|
||||||
|
m_DefaultTitleStyle.normal.textColor = EditorGUIUtility.isProSkin
|
||||||
|
? new Color( 0.8f, 0.8f, 0.8f )
|
||||||
|
: new Color( 0.2f, 0.2f, 0.2f );
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_DefaultTitleStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GUIStyle m_DefaultContainerStyle;
|
||||||
|
public static GUIStyle DefaultContainerStyle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if( m_DefaultContainerStyle == null )
|
||||||
|
{
|
||||||
|
m_DefaultContainerStyle = new GUIStyle();
|
||||||
|
m_DefaultContainerStyle.border = new RectOffset( 2, 2, 1, 2 );
|
||||||
|
m_DefaultContainerStyle.margin = new RectOffset( 5, 5, 5, 5 );
|
||||||
|
m_DefaultContainerStyle.padding = new RectOffset( 1, 1, 2, 2 );
|
||||||
|
m_DefaultContainerStyle.normal.background = ReorderableListResources.texContainerBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_DefaultContainerStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GUIStyle m_DefaultAddButtonStyle;
|
||||||
|
public static GUIStyle DefaultAddButtonStyle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if( m_DefaultAddButtonStyle == null )
|
||||||
|
{
|
||||||
|
m_DefaultAddButtonStyle = new GUIStyle();
|
||||||
|
m_DefaultAddButtonStyle.fixedWidth = 30;
|
||||||
|
m_DefaultAddButtonStyle.fixedHeight = 16;
|
||||||
|
m_DefaultAddButtonStyle.normal.background = ReorderableListResources.texAddButton;
|
||||||
|
m_DefaultAddButtonStyle.active.background = ReorderableListResources.texAddButtonActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_DefaultAddButtonStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GUIStyle m_DefaultRemoveButtonStyle;
|
||||||
|
public static GUIStyle DefaultRemoveButtonStyle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if( m_DefaultRemoveButtonStyle == null )
|
||||||
|
{
|
||||||
|
m_DefaultRemoveButtonStyle = new GUIStyle();
|
||||||
|
m_DefaultRemoveButtonStyle.fixedWidth = 30;
|
||||||
|
m_DefaultRemoveButtonStyle.fixedHeight = 20;
|
||||||
|
m_DefaultRemoveButtonStyle.active.background = ReorderableListResources.CreatePixelTexture( "Dark Pixel (List GUI)", new Color32( 18, 18, 18, 255 ) );
|
||||||
|
m_DefaultRemoveButtonStyle.imagePosition = ImagePosition.ImageOnly;
|
||||||
|
m_DefaultRemoveButtonStyle.alignment = TextAnchor.MiddleCenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_DefaultRemoveButtonStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GUIStyle m_DefaultContainerRowStyle;
|
||||||
|
public static GUIStyle DefaultContainerRowStyle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if( m_DefaultContainerRowStyle == null )
|
||||||
|
{
|
||||||
|
m_DefaultContainerRowStyle = new GUIStyle();
|
||||||
|
m_DefaultContainerRowStyle.border = new RectOffset( 2, 2, 2, 2 );
|
||||||
|
|
||||||
|
m_DefaultContainerRowStyle.margin = new RectOffset( 5, 5, 5, 5 );
|
||||||
|
m_DefaultContainerRowStyle.padding = new RectOffset( 1, 1, 2, 2 );
|
||||||
|
m_DefaultContainerRowStyle.normal.background = ReorderableListResources.texContainerBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_DefaultContainerRowStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GUIStyle m_FoldoutBold;
|
||||||
|
public static GUIStyle FoldoutBold
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if( m_FoldoutBold == null )
|
||||||
|
{
|
||||||
|
m_FoldoutBold = new GUIStyle( EditorStyles.foldout );
|
||||||
|
m_FoldoutBold.fontStyle = FontStyle.Bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_FoldoutBold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GUIStyle m_RichLabel;
|
||||||
|
public static GUIStyle RichLabel
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if( m_RichLabel == null )
|
||||||
|
{
|
||||||
|
m_RichLabel = new GUIStyle( GUI.skin.label );
|
||||||
|
m_RichLabel.richText = true;
|
||||||
|
m_RichLabel.wordWrap = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_RichLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
static Texture2D m_HelpIcon;
|
||||||
|
public static Texture2D HelpIcon
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if( m_HelpIcon == null )
|
||||||
|
{
|
||||||
|
m_HelpIcon = AssetDatabase.LoadAssetAtPath( "Assets/Photon Unity Networking/Editor/PhotonNetwork/help.png", typeof( Texture2D ) ) as Texture2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_HelpIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Interface
|
||||||
|
public static void ContainerHeader( string headline )
|
||||||
|
{
|
||||||
|
DoContainerHeader( headline, 27, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ContainerHeaderToggle( string headline, bool toggle )
|
||||||
|
{
|
||||||
|
return DoContainerHeaderToggle( headline, toggle );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ContainerHeaderFoldout( string headline, bool foldout )
|
||||||
|
{
|
||||||
|
return DoContainerHeaderFoldout( headline, foldout );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Rect ContainerBody( float height )
|
||||||
|
{
|
||||||
|
return DoContainerBody( height );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool AddButton()
|
||||||
|
{
|
||||||
|
Rect controlRect = EditorGUILayout.GetControlRect( false, DefaultAddButtonStyle.fixedHeight - 5 );
|
||||||
|
controlRect.yMin -= 5;
|
||||||
|
controlRect.yMax -= 5;
|
||||||
|
|
||||||
|
Rect addButtonRect = new Rect( controlRect.xMax - DefaultAddButtonStyle.fixedWidth,
|
||||||
|
controlRect.yMin,
|
||||||
|
DefaultAddButtonStyle.fixedWidth,
|
||||||
|
DefaultAddButtonStyle.fixedHeight );
|
||||||
|
|
||||||
|
return GUI.Button( addButtonRect, "", DefaultAddButtonStyle );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DrawSplitter( Rect position )
|
||||||
|
{
|
||||||
|
ReorderableListResources.DrawTexture( position, ReorderableListResources.texItemSplitter );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DrawGizmoOptions(
|
||||||
|
Rect position,
|
||||||
|
string label,
|
||||||
|
SerializedProperty gizmoEnabledProperty,
|
||||||
|
SerializedProperty gizmoColorProperty,
|
||||||
|
SerializedProperty gizmoTypeProperty,
|
||||||
|
SerializedProperty gizmoSizeProperty )
|
||||||
|
{
|
||||||
|
float height = EditorGUIUtility.singleLineHeight;
|
||||||
|
float flexibleWidth = Mathf.Max( 40, position.width - EditorGUIUtility.labelWidth - 20 - 75 - 5 - 40 - 5 );
|
||||||
|
|
||||||
|
Rect labelRect = new Rect( position.xMin, position.yMin, EditorGUIUtility.labelWidth, height );
|
||||||
|
GUI.Label( labelRect, label );
|
||||||
|
|
||||||
|
Rect enabledRect = new Rect( labelRect.xMax, labelRect.yMin, 20, height );
|
||||||
|
EditorGUI.PropertyField( enabledRect, gizmoEnabledProperty, GUIContent.none );
|
||||||
|
|
||||||
|
bool oldGUIEnabled = GUI.enabled;
|
||||||
|
GUI.enabled = gizmoEnabledProperty.boolValue;
|
||||||
|
|
||||||
|
Rect colorRect = new Rect( enabledRect.xMax + 5, labelRect.yMin, 70, height );
|
||||||
|
EditorGUI.PropertyField( colorRect, gizmoColorProperty, GUIContent.none );
|
||||||
|
|
||||||
|
Rect typeRect = new Rect( colorRect.xMax + 5, labelRect.yMin, flexibleWidth * 0.7f, height );
|
||||||
|
EditorGUI.PropertyField( typeRect, gizmoTypeProperty, GUIContent.none );
|
||||||
|
|
||||||
|
Rect sizeLabelRect = new Rect( typeRect.xMax + 10, labelRect.yMin, 30, height );
|
||||||
|
GUI.Label( sizeLabelRect, "Size" );
|
||||||
|
|
||||||
|
Rect sizeRect = new Rect( sizeLabelRect.xMax + 5, labelRect.yMin, flexibleWidth * 0.3f, height );
|
||||||
|
EditorGUI.PropertyField( sizeRect, gizmoSizeProperty, GUIContent.none );
|
||||||
|
|
||||||
|
GUI.enabled = oldGUIEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation
|
||||||
|
static Rect DoContainerBody( float height )
|
||||||
|
{
|
||||||
|
Rect controlRect = EditorGUILayout.GetControlRect( false, height );
|
||||||
|
controlRect.yMin -= 3;
|
||||||
|
controlRect.yMax -= 2;
|
||||||
|
|
||||||
|
int controlID = GUIUtility.GetControlID( FocusType.Passive, controlRect );
|
||||||
|
|
||||||
|
if( Event.current.type == EventType.Repaint )
|
||||||
|
{
|
||||||
|
PhotonGUI.DefaultContainerStyle.Draw( controlRect, GUIContent.none, controlID );
|
||||||
|
}
|
||||||
|
|
||||||
|
return controlRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DoContainerHeaderToggle( string headline, bool toggle )
|
||||||
|
{
|
||||||
|
Rect rect = DoContainerHeader( headline, 27, 15 );
|
||||||
|
Rect toggleRect = new Rect( rect.xMin + 5, rect.yMin + 5, EditorGUIUtility.labelWidth, rect.height );
|
||||||
|
|
||||||
|
return EditorGUI.Toggle( toggleRect, toggle );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool DoContainerHeaderFoldout( string headline, bool foldout )
|
||||||
|
{
|
||||||
|
Rect rect = DoContainerHeader( "", 27, 0f );
|
||||||
|
Rect foldoutRect = new Rect( rect.xMin + 15, rect.yMin + 5, rect.width, rect.height );
|
||||||
|
|
||||||
|
return EditorGUI.Foldout( foldoutRect, foldout, headline, FoldoutBold );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Rect DoContainerHeader( string headline, float height, float contentOffset )
|
||||||
|
{
|
||||||
|
GUILayout.Space( 5 );
|
||||||
|
Rect controlRect = EditorGUILayout.GetControlRect( false, height );
|
||||||
|
|
||||||
|
int controlID = GUIUtility.GetControlID( FocusType.Passive, controlRect );
|
||||||
|
|
||||||
|
if( Event.current.type == EventType.Repaint )
|
||||||
|
{
|
||||||
|
PhotonGUI.DefaultTitleStyle.Draw( controlRect, GUIContent.none, controlID );
|
||||||
|
|
||||||
|
Rect labelRect = new Rect( controlRect.xMin + 5 + contentOffset, controlRect.yMin + 5, controlRect.width, controlRect.height );
|
||||||
|
GUI.Label( labelRect, headline, EditorStyles.boldLabel );
|
||||||
|
}
|
||||||
|
|
||||||
|
return controlRect;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3d2cadb1ccf05074e8ce96b1393846cf
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
|
@ -0,0 +1,193 @@
|
||||||
|
#if UNITY_5 && !UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2 || UNITY_5_4_OR_NEWER
|
||||||
|
#define UNITY_MIN_5_3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Collections;
|
||||||
|
using Debug = UnityEngine.Debug;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
|
||||||
|
[InitializeOnLoad]
|
||||||
|
public class PhotonViewHandler : EditorWindow
|
||||||
|
{
|
||||||
|
private static bool CheckSceneForStuckHandlers = true;
|
||||||
|
|
||||||
|
static PhotonViewHandler()
|
||||||
|
{
|
||||||
|
// hierarchyWindowChanged is called on hierarchy changed and on save. It's even called when hierarchy-window is closed and if a prefab with instances is changed.
|
||||||
|
// this is not called when you edit a instance's value but: on save
|
||||||
|
EditorApplication.hierarchyWindowChanged += HierarchyChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this method corrects the IDs for photonviews in the scene and in prefabs
|
||||||
|
// make sure prefabs always use viewID 0
|
||||||
|
// make sure instances never use a owner
|
||||||
|
// this is a editor class that should only run if not playing
|
||||||
|
internal static void HierarchyChange()
|
||||||
|
{
|
||||||
|
if (Application.isPlaying)
|
||||||
|
{
|
||||||
|
//Debug.Log("HierarchyChange ignored, while running.");
|
||||||
|
CheckSceneForStuckHandlers = true; // done once AFTER play mode.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CheckSceneForStuckHandlers)
|
||||||
|
{
|
||||||
|
CheckSceneForStuckHandlers = false;
|
||||||
|
PhotonNetwork.InternalCleanPhotonMonoFromSceneIfStuck();
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<PhotonView> pvInstances = new HashSet<PhotonView>();
|
||||||
|
HashSet<int> usedInstanceViewNumbers = new HashSet<int>();
|
||||||
|
bool fixedSomeId = false;
|
||||||
|
|
||||||
|
//// the following code would be an option if we only checked scene objects (but we can check all PVs)
|
||||||
|
//PhotonView[] pvObjects = GameObject.FindSceneObjectsOfType(typeof(PhotonView)) as PhotonView[];
|
||||||
|
//Debug.Log("HierarchyChange. PV Count: " + pvObjects.Length);
|
||||||
|
|
||||||
|
string levelName = SceneManagerHelper.ActiveSceneName;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
levelName = SceneManagerHelper.EditorActiveSceneName;
|
||||||
|
#endif
|
||||||
|
int minViewIdInThisScene = PunSceneSettings.MinViewIdForScene(levelName);
|
||||||
|
//Debug.Log("Level '" + Application.loadedLevelName + "' has a minimum ViewId of: " + minViewIdInThisScene);
|
||||||
|
|
||||||
|
PhotonView[] pvObjects = Resources.FindObjectsOfTypeAll(typeof(PhotonView)) as PhotonView[];
|
||||||
|
|
||||||
|
foreach (PhotonView view in pvObjects)
|
||||||
|
{
|
||||||
|
// first pass: fix prefabs to viewID 0 if they got a view number assigned (cause they should not have one!)
|
||||||
|
if (EditorUtility.IsPersistent(view.gameObject))
|
||||||
|
{
|
||||||
|
if (view.viewID != 0 || view.prefixBackup != -1 || view.instantiationId != -1)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("PhotonView on persistent object being fixed (id and prefix must be 0). Was: " + view);
|
||||||
|
view.viewID = 0;
|
||||||
|
view.prefixBackup = -1;
|
||||||
|
view.instantiationId = -1;
|
||||||
|
EditorUtility.SetDirty(view); // even in Unity 5.3+ it's OK to SetDirty() for non-scene objects.
|
||||||
|
fixedSomeId = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// keep all scene-instanced PVs for later re-check
|
||||||
|
pvInstances.Add(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<GameObject, int> idPerObject = new Dictionary<GameObject, int>();
|
||||||
|
|
||||||
|
// second pass: check all used-in-scene viewIDs for duplicate viewIDs (only checking anything non-prefab)
|
||||||
|
// scene-PVs must have user == 0 (scene/room) and a subId != 0
|
||||||
|
foreach (PhotonView view in pvInstances)
|
||||||
|
{
|
||||||
|
if (view.ownerId > 0)
|
||||||
|
{
|
||||||
|
Debug.Log("Re-Setting Owner ID of: " + view);
|
||||||
|
}
|
||||||
|
view.ownerId = 0; // simply make sure no owner is set (cause room always uses 0)
|
||||||
|
view.prefix = -1; // TODO: prefix could be settable via inspector per scene?!
|
||||||
|
|
||||||
|
if (view.viewID != 0)
|
||||||
|
{
|
||||||
|
if (view.viewID < minViewIdInThisScene || usedInstanceViewNumbers.Contains(view.viewID))
|
||||||
|
{
|
||||||
|
view.viewID = 0; // avoid duplicates and negative values by assigning 0 as (temporary) number to be fixed in next pass
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
usedInstanceViewNumbers.Add(view.viewID); // builds a list of currently used viewIDs
|
||||||
|
|
||||||
|
int instId = 0;
|
||||||
|
if (idPerObject.TryGetValue(view.gameObject, out instId))
|
||||||
|
{
|
||||||
|
view.instantiationId = instId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
view.instantiationId = view.viewID;
|
||||||
|
idPerObject[view.gameObject] = view.instantiationId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// third pass: anything that's now 0 must get a new (not yet used) ID (starting at 0)
|
||||||
|
int lastUsedId = (minViewIdInThisScene > 0) ? minViewIdInThisScene - 1 : 0;
|
||||||
|
|
||||||
|
foreach (PhotonView view in pvInstances)
|
||||||
|
{
|
||||||
|
if (view.viewID == 0)
|
||||||
|
{
|
||||||
|
Undo.RecordObject(view, "Automatic viewID change for: "+view.gameObject.name);
|
||||||
|
|
||||||
|
// Debug.LogWarning("setting scene ID: " + view.gameObject.name + " ID: " + view.subId.ID + " scene ID: " + view.GetSceneID() + " IsPersistent: " + EditorUtility.IsPersistent(view.gameObject) + " IsSceneViewIDFree: " + IsSceneViewIDFree(view.subId.ID, view));
|
||||||
|
int nextViewId = PhotonViewHandler.GetID(lastUsedId, usedInstanceViewNumbers);
|
||||||
|
|
||||||
|
view.viewID = nextViewId;
|
||||||
|
|
||||||
|
int instId = 0;
|
||||||
|
if (idPerObject.TryGetValue(view.gameObject, out instId))
|
||||||
|
{
|
||||||
|
view.instantiationId = instId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
view.instantiationId = view.viewID;
|
||||||
|
idPerObject[view.gameObject] = nextViewId;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastUsedId = nextViewId;
|
||||||
|
fixedSomeId = true;
|
||||||
|
|
||||||
|
#if !UNITY_MIN_5_3
|
||||||
|
EditorUtility.SetDirty(view);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (fixedSomeId)
|
||||||
|
{
|
||||||
|
//Debug.LogWarning("Some subId was adjusted."); // this log is only interesting for Exit Games
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO fail if no ID was available anymore
|
||||||
|
// TODO look up lower numbers if offset hits max?!
|
||||||
|
public static int GetID(int idOffset, HashSet<int> usedInstanceViewNumbers)
|
||||||
|
{
|
||||||
|
while (idOffset < PhotonNetwork.MAX_VIEW_IDS)
|
||||||
|
{
|
||||||
|
idOffset++;
|
||||||
|
if (!usedInstanceViewNumbers.Contains(idOffset))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: check if this can be internal protected (as source in editor AND as dll)
|
||||||
|
public static void LoadAllScenesToFix()
|
||||||
|
{
|
||||||
|
string[] scenes = System.IO.Directory.GetFiles(".", "*.unity", SearchOption.AllDirectories);
|
||||||
|
|
||||||
|
foreach (string scene in scenes)
|
||||||
|
{
|
||||||
|
EditorSceneManager.OpenScene(scene);
|
||||||
|
PhotonViewHandler.HierarchyChange();//NOTE: most likely on load also triggers a hierarchy change
|
||||||
|
EditorSceneManager.SaveOpenScenes();
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("Corrected scene views where needed.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 57b538e62a0ca6248bfd354def935e57
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,276 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="PhotonViewInspector.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// Custom inspector for the PhotonView component.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if UNITY_5 && !UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2 || UNITY_5_4_OR_NEWER
|
||||||
|
#define UNITY_MIN_5_3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
using Photon.Pun;
|
||||||
|
|
||||||
|
|
||||||
|
[CustomEditor(typeof (PhotonView))]
|
||||||
|
public class PhotonViewInspector : Editor
|
||||||
|
{
|
||||||
|
private PhotonView m_Target;
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
this.m_Target = (PhotonView)target;
|
||||||
|
bool isProjectPrefab = EditorUtility.IsPersistent(this.m_Target.gameObject);
|
||||||
|
|
||||||
|
if (this.m_Target.ObservedComponents == null)
|
||||||
|
{
|
||||||
|
this.m_Target.ObservedComponents = new System.Collections.Generic.List<Component>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.m_Target.ObservedComponents.Count == 0)
|
||||||
|
{
|
||||||
|
this.m_Target.ObservedComponents.Add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
// Owner
|
||||||
|
if (isProjectPrefab)
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Owner:", "Set at runtime");
|
||||||
|
}
|
||||||
|
else if (!this.m_Target.isOwnerActive)
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Owner", "Scene");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PhotonPlayer owner = this.m_Target.owner;
|
||||||
|
string ownerInfo = (owner != null) ? owner.NickName : "<no PhotonPlayer found>";
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(ownerInfo))
|
||||||
|
{
|
||||||
|
ownerInfo = "<no playername set>";
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField("Owner", "[" + this.m_Target.ownerId + "] " + ownerInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ownership requests
|
||||||
|
EditorGUI.BeginDisabledGroup(Application.isPlaying);
|
||||||
|
OwnershipOption own = (OwnershipOption)EditorGUILayout.EnumPopup(this.m_Target.ownershipTransfer, GUILayout.Width(100));
|
||||||
|
if (own != this.m_Target.ownershipTransfer)
|
||||||
|
{
|
||||||
|
// jf: fixed 5 and up prefab not accepting changes if you quit Unity straight after change.
|
||||||
|
// not touching the define nor the rest of the code to avoid bringing more problem than solving.
|
||||||
|
EditorUtility.SetDirty(this.m_Target);
|
||||||
|
|
||||||
|
Undo.RecordObject(this.m_Target, "Change PhotonView Ownership Transfer");
|
||||||
|
this.m_Target.ownershipTransfer = own;
|
||||||
|
}
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
|
||||||
|
// View ID
|
||||||
|
if (isProjectPrefab)
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("View ID", "Set at runtime");
|
||||||
|
}
|
||||||
|
else if (EditorApplication.isPlaying)
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("View ID", this.m_Target.viewID.ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int idValue = EditorGUILayout.IntField("View ID [1.." + (PhotonNetwork.MAX_VIEW_IDS - 1) + "]", this.m_Target.viewID);
|
||||||
|
if (this.m_Target.viewID != idValue)
|
||||||
|
{
|
||||||
|
Undo.RecordObject(this.m_Target, "Change PhotonView viewID");
|
||||||
|
this.m_Target.viewID = idValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locally Controlled
|
||||||
|
if (EditorApplication.isPlaying)
|
||||||
|
{
|
||||||
|
string masterClientHint = PhotonNetwork.isMasterClient ? "(master)" : "";
|
||||||
|
EditorGUILayout.Toggle("Controlled locally: " + masterClientHint, this.m_Target.isMine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewSynchronization (reliability)
|
||||||
|
if (this.m_Target.synchronization == ViewSynchronization.Off)
|
||||||
|
{
|
||||||
|
GUI.color = Color.grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.PropertyField(serializedObject.FindProperty("synchronization"), new GUIContent("Observe option:"));
|
||||||
|
|
||||||
|
if (this.m_Target.synchronization != ViewSynchronization.Off && this.m_Target.ObservedComponents.FindAll(item => item != null).Count == 0)
|
||||||
|
{
|
||||||
|
GUILayout.BeginVertical(GUI.skin.box);
|
||||||
|
GUILayout.Label("Warning", EditorStyles.boldLabel);
|
||||||
|
GUILayout.Label("Setting the synchronization option only makes sense if you observe something.");
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawSpecificTypeSerializationOptions();
|
||||||
|
|
||||||
|
GUI.color = Color.white;
|
||||||
|
DrawObservedComponentsList();
|
||||||
|
|
||||||
|
// Cleanup: save and fix look
|
||||||
|
if (GUI.changed)
|
||||||
|
{
|
||||||
|
#if !UNITY_MIN_5_3
|
||||||
|
EditorUtility.SetDirty(this.m_Target);
|
||||||
|
#endif
|
||||||
|
PhotonViewHandler.HierarchyChange(); // TODO: check if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI.color = Color.white;
|
||||||
|
#if !UNITY_MIN_5_3
|
||||||
|
EditorGUIUtility.LookLikeControls();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSpecificTypeSerializationOptions()
|
||||||
|
{
|
||||||
|
if (this.m_Target.ObservedComponents.FindAll(item => item != null && item.GetType() == typeof (Transform)).Count > 0)
|
||||||
|
{
|
||||||
|
this.m_Target.onSerializeTransformOption = (OnSerializeTransform)EditorGUILayout.EnumPopup("Transform Serialization:", this.m_Target.onSerializeTransformOption);
|
||||||
|
}
|
||||||
|
else if (this.m_Target.ObservedComponents.FindAll(item => item != null && item.GetType() == typeof (Rigidbody)).Count > 0 ||
|
||||||
|
this.m_Target.ObservedComponents.FindAll(item => item != null && item.GetType() == typeof (Rigidbody2D)).Count > 0)
|
||||||
|
{
|
||||||
|
this.m_Target.onSerializeRigidBodyOption = (OnSerializeRigidBody)EditorGUILayout.EnumPopup("Rigidbody Serialization:", this.m_Target.onSerializeRigidBodyOption);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int GetObservedComponentsCount()
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < this.m_Target.ObservedComponents.Count; ++i)
|
||||||
|
{
|
||||||
|
if (this.m_Target.ObservedComponents[i] != null)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawObservedComponentsList()
|
||||||
|
{
|
||||||
|
GUILayout.Space(5);
|
||||||
|
SerializedProperty listProperty = serializedObject.FindProperty("ObservedComponents");
|
||||||
|
|
||||||
|
if (listProperty == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float containerElementHeight = 22;
|
||||||
|
float containerHeight = listProperty.arraySize*containerElementHeight;
|
||||||
|
|
||||||
|
bool isOpen = PhotonGUI.ContainerHeaderFoldout("Observed Components (" + GetObservedComponentsCount() + ")", serializedObject.FindProperty("ObservedComponentsFoldoutOpen").boolValue);
|
||||||
|
serializedObject.FindProperty("ObservedComponentsFoldoutOpen").boolValue = isOpen;
|
||||||
|
|
||||||
|
if (isOpen == false)
|
||||||
|
{
|
||||||
|
containerHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Texture2D statsIcon = AssetDatabase.LoadAssetAtPath( "Assets/Photon Unity Networking/Editor/PhotonNetwork/PhotonViewStats.png", typeof( Texture2D ) ) as Texture2D;
|
||||||
|
|
||||||
|
Rect containerRect = PhotonGUI.ContainerBody(containerHeight);
|
||||||
|
bool wasObservedComponentsEmpty = this.m_Target.ObservedComponents.FindAll(item => item != null).Count == 0;
|
||||||
|
if (isOpen == true)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < listProperty.arraySize; ++i)
|
||||||
|
{
|
||||||
|
Rect elementRect = new Rect(containerRect.xMin, containerRect.yMin + containerElementHeight*i, containerRect.width, containerElementHeight);
|
||||||
|
{
|
||||||
|
Rect texturePosition = new Rect(elementRect.xMin + 6, elementRect.yMin + elementRect.height/2f - 1, 9, 5);
|
||||||
|
ReorderableListResources.DrawTexture(texturePosition, ReorderableListResources.texGrabHandle);
|
||||||
|
|
||||||
|
Rect propertyPosition = new Rect(elementRect.xMin + 20, elementRect.yMin + 3, elementRect.width - 45, 16);
|
||||||
|
EditorGUI.PropertyField(propertyPosition, listProperty.GetArrayElementAtIndex(i), new GUIContent());
|
||||||
|
|
||||||
|
//Debug.Log( listProperty.GetArrayElementAtIndex( i ).objectReferenceValue.GetType() );
|
||||||
|
//Rect statsPosition = new Rect( propertyPosition.xMax + 7, propertyPosition.yMin, statsIcon.width, statsIcon.height );
|
||||||
|
//ReorderableListResources.DrawTexture( statsPosition, statsIcon );
|
||||||
|
|
||||||
|
Rect removeButtonRect = new Rect(elementRect.xMax - PhotonGUI.DefaultRemoveButtonStyle.fixedWidth,
|
||||||
|
elementRect.yMin + 2,
|
||||||
|
PhotonGUI.DefaultRemoveButtonStyle.fixedWidth,
|
||||||
|
PhotonGUI.DefaultRemoveButtonStyle.fixedHeight);
|
||||||
|
|
||||||
|
GUI.enabled = listProperty.arraySize > 1;
|
||||||
|
if (GUI.Button(removeButtonRect, new GUIContent(ReorderableListResources.texRemoveButton), PhotonGUI.DefaultRemoveButtonStyle))
|
||||||
|
{
|
||||||
|
listProperty.DeleteArrayElementAtIndex(i);
|
||||||
|
}
|
||||||
|
GUI.enabled = true;
|
||||||
|
|
||||||
|
if (i < listProperty.arraySize - 1)
|
||||||
|
{
|
||||||
|
texturePosition = new Rect(elementRect.xMin + 2, elementRect.yMax, elementRect.width - 4, 1);
|
||||||
|
PhotonGUI.DrawSplitter(texturePosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PhotonGUI.AddButton())
|
||||||
|
{
|
||||||
|
listProperty.InsertArrayElementAtIndex(Mathf.Max(0, listProperty.arraySize - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
bool isObservedComponentsEmpty = this.m_Target.ObservedComponents.FindAll(item => item != null).Count == 0;
|
||||||
|
|
||||||
|
if (wasObservedComponentsEmpty == true && isObservedComponentsEmpty == false && this.m_Target.synchronization == ViewSynchronization.Off)
|
||||||
|
{
|
||||||
|
Undo.RecordObject(this.m_Target, "Change PhotonView");
|
||||||
|
this.m_Target.synchronization = ViewSynchronization.UnreliableOnChange;
|
||||||
|
#if !UNITY_MIN_5_3
|
||||||
|
EditorUtility.SetDirty(this.m_Target);
|
||||||
|
#endif
|
||||||
|
serializedObject.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasObservedComponentsEmpty == false && isObservedComponentsEmpty == true)
|
||||||
|
{
|
||||||
|
Undo.RecordObject(this.m_Target, "Change PhotonView");
|
||||||
|
this.m_Target.synchronization = ViewSynchronization.Off;
|
||||||
|
#if !UNITY_MIN_5_3
|
||||||
|
EditorUtility.SetDirty(this.m_Target);
|
||||||
|
#endif
|
||||||
|
serializedObject.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GameObject GetPrefabParent(GameObject mp)
|
||||||
|
{
|
||||||
|
#if UNITY_2_6_1 || UNITY_2_6 || UNITY_3_0 || UNITY_3_0_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || UNITY_3_4
|
||||||
|
// Unity 3.4 and older use EditorUtility
|
||||||
|
return (EditorUtility.GetPrefabParent(mp) as GameObject);
|
||||||
|
#else
|
||||||
|
// Unity 3.5 uses PrefabUtility
|
||||||
|
return PrefabUtility.GetPrefabParent(mp) as GameObject;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e73a30c46df19194f873ea7a9ce12753
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,93 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class SceneSetting
|
||||||
|
{
|
||||||
|
public string sceneName;
|
||||||
|
public int minViewId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PunSceneSettings : ScriptableObject
|
||||||
|
{
|
||||||
|
[SerializeField] public List<SceneSetting> MinViewIdPerScene = new List<SceneSetting>();
|
||||||
|
|
||||||
|
private const string SceneSettingsFileName = "PunSceneSettingsFile.asset";
|
||||||
|
|
||||||
|
// we use the path to PunSceneSettings.cs as path to create a scene settings file
|
||||||
|
private static string punSceneSettingsCsPath;
|
||||||
|
public static string PunSceneSettingsCsPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(punSceneSettingsCsPath))
|
||||||
|
{
|
||||||
|
return punSceneSettingsCsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unity 4.3.4 does not yet have AssetDatabase.FindAssets(). Would be easier.
|
||||||
|
var result = Directory.GetFiles(Application.dataPath, "PunSceneSettings.cs", SearchOption.AllDirectories);
|
||||||
|
if (result.Length >= 1)
|
||||||
|
{
|
||||||
|
punSceneSettingsCsPath = Path.GetDirectoryName(result[0]);
|
||||||
|
punSceneSettingsCsPath = punSceneSettingsCsPath.Replace('\\', '/');
|
||||||
|
punSceneSettingsCsPath = punSceneSettingsCsPath.Replace(Application.dataPath, "Assets");
|
||||||
|
|
||||||
|
// AssetDatabase paths have to use '/' and are relative to the project's folder. Always.
|
||||||
|
punSceneSettingsCsPath = punSceneSettingsCsPath + "/" + SceneSettingsFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return punSceneSettingsCsPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static PunSceneSettings instanceField;
|
||||||
|
public static PunSceneSettings Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (instanceField != null)
|
||||||
|
{
|
||||||
|
return instanceField;
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceField = (PunSceneSettings)AssetDatabase.LoadAssetAtPath(PunSceneSettingsCsPath, typeof(PunSceneSettings));
|
||||||
|
if (instanceField == null)
|
||||||
|
{
|
||||||
|
instanceField = ScriptableObject.CreateInstance<PunSceneSettings>();
|
||||||
|
AssetDatabase.CreateAsset(instanceField, PunSceneSettingsCsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instanceField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static int MinViewIdForScene(string scene)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(scene))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PunSceneSettings pss = Instance;
|
||||||
|
if (pss == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("pss cant be null");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (SceneSetting setting in pss.MinViewIdPerScene)
|
||||||
|
{
|
||||||
|
if (setting.sceneName.Equals(scene))
|
||||||
|
{
|
||||||
|
return setting.minViewId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fc3284eace5a64d4bb516df7d7effdb9
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,14 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_PrefabParentObject: {fileID: 0}
|
||||||
|
m_PrefabInternal: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: fc3284eace5a64d4bb516df7d7effdb9, type: 3}
|
||||||
|
m_Name: PunSceneSettingsFile
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
MinViewIdPerScene: []
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d0aacb83307022d449e90a09d28222ae
|
|
@ -0,0 +1,237 @@
|
||||||
|
// Copyright (c) 2012-2013 Rotorz Limited. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Photon.Pun
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resources to assist with reorderable list control.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ReorderableListResources
|
||||||
|
{
|
||||||
|
|
||||||
|
static ReorderableListResources()
|
||||||
|
{
|
||||||
|
GenerateSpecialTextures();
|
||||||
|
LoadResourceAssets();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Texture Resources
|
||||||
|
|
||||||
|
private enum ResourceName
|
||||||
|
{
|
||||||
|
add_button = 0,
|
||||||
|
add_button_active,
|
||||||
|
container_background,
|
||||||
|
grab_handle,
|
||||||
|
remove_button,
|
||||||
|
remove_button_active,
|
||||||
|
title_background,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resource assets for light skin.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>Resource assets are PNG images which have been encoded using a base-64
|
||||||
|
/// string so that actual asset files are not necessary.</para>
|
||||||
|
/// </remarks>
|
||||||
|
private static string[] s_LightSkin = {
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAB4AAAAQCAYAAAABOs/SAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAAAW0lEQVRIS+3NywnAQAhF0anI4mzVCmzBBl7QEBgGE5JFhBAXd+OHM5gZZgYRKcktNxu+HRFF2e6qhtOjtQM7K/tZ+xY89wSbazg9eqOfw6oag4rcChjY8coAjA2l1RxFDY8IFAAAAABJRU5ErkJggg==",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAB4AAAAQCAYAAAABOs/SAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAGlJREFUeNpiFBER+f/jxw8GNjY2BnqAX79+MXBwcDAwMQwQGHoWnzp1CoxHjo8pBSykBi8+MTMzs2HmY2QfwXxKii9HExdZgNwgHuFB/efPH7pZCLOL8f///wyioqL/6enbL1++MAIEGABvGSLA+9GPZwAAAABJRU5ErkJggg==",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAECAYAAABGM/VAAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAAAMElEQVQYV2P4//8/Q1FR0X8YBvHBAp8+ffp/+fJlMA3igwUfPnwIFgDRYEFM7f8ZAG1EOYL9INrfAAAAAElFTkSuQmCC",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAkAAAAFCAYAAACXU8ZrAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAAAIElEQVQYV2P49OnTf0KYobCw8D8hzPD/P2FMLesK/wMAs5yJpK+6aN4AAAAASUVORK5CYII=",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAgAAAACCAIAAADq9gq6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABVJREFUeNpiVFZWZsAGmBhwAIAAAwAURgBt4C03ZwAAAABJRU5ErkJggg==",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAgAAAACCAIAAADq9gq6AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABVJREFUeNpivHPnDgM2wMSAAwAEGAB8VgKYlvqkBwAAAABJRU5ErkJggg==",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAECAYAAABGM/VAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEFJREFUeNpi/P//P0NxcfF/BgRgZP78+fN/VVVVhpCQEAZjY2OGs2fPNrCApBwdHRkePHgAVwoWnDVrFgMyAAgwAAt4E1dCq1obAAAAAElFTkSuQmCC"
|
||||||
|
};
|
||||||
|
/// <summary>
|
||||||
|
/// Resource assets for dark skin.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>Resource assets are PNG images which have been encoded using a base-64
|
||||||
|
/// string so that actual asset files are not necessary.</para>
|
||||||
|
/// </remarks>
|
||||||
|
private static string[] s_DarkSkin = {
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAB4AAAAQCAYAAAABOs/SAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIBJREFUeNpiVFZW/u/i4sLw4sULBnoACQkJhj179jAwMQwQGHoWl5aWgvHI8TGlgIXU4MUn1t3dPcx8HB8fD2cvXLgQQ0xHR4c2FmMzmBTLhl5QYwt2cn1MtsXkWjg4gvrt27fgWoMeAGQXCDD+//+fQUVF5T89fXvnzh1GgAADAFmSI1Ed3FqgAAAAAElFTkSuQmCC",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAB4AAAAQCAYAAAABOs/SAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHlJREFUeNpiFBER+f/jxw8GNjY2BnqAX79+MXBwcDAwMQwQGHoWv3nzBoxHjo8pBSykBi8+MWAOGWY+5uLigrO/ffuGIYbMppnF5Fg2tFM1yKfk+pbkoKZGEA+OVP3nzx+6WQizi/H///8MoqKi/+np2y9fvjACBBgAoTYjgvihfz0AAAAASUVORK5CYII=",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAECAYAAABGM/VAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD1JREFUeNpi/P//P4OKisp/Bii4c+cOIwtIwMXFheHFixcMEhISYAVMINm3b9+CBUA0CDCiazc0NGQECDAAdH0YelA27kgAAAAASUVORK5CYII=",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAkAAAAFCAYAAACXU8ZrAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACRJREFUeNpizM3N/c9AADAqKysTVMTi5eXFSFAREFPHOoAAAwBCfwcAO8g48QAAAABJRU5ErkJggg==",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAgAAAAECAYAAACzzX7wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi/P//PwM+wHL06FG8KpgYCABGZWVlvCYABBgA7/sHvGw+cz8AAAAASUVORK5CYII=",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAgAAAAECAYAAACzzX7wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi/P//PwM+wPKfgAomBgKAhYuLC68CgAADAAxjByOjCHIRAAAAAElFTkSuQmCC",
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAECAYAAABGM/VAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADtJREFUeNpi/P//P4OKisp/Bii4c+cOIwtIQE9Pj+HLly9gQRCfBcQACbx69QqmmAEseO/ePQZkABBgAD04FXsmmijSAAAAAElFTkSuQmCC"
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets light or dark texture "add_button.png".
|
||||||
|
/// </summary>
|
||||||
|
public static Texture2D texAddButton
|
||||||
|
{
|
||||||
|
get { return s_Cached[ (int)ResourceName.add_button ]; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets light or dark texture "add_button_active.png".
|
||||||
|
/// </summary>
|
||||||
|
public static Texture2D texAddButtonActive
|
||||||
|
{
|
||||||
|
get { return s_Cached[ (int)ResourceName.add_button_active ]; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets light or dark texture "container_background.png".
|
||||||
|
/// </summary>
|
||||||
|
public static Texture2D texContainerBackground
|
||||||
|
{
|
||||||
|
get { return s_Cached[ (int)ResourceName.container_background ]; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets light or dark texture "grab_handle.png".
|
||||||
|
/// </summary>
|
||||||
|
public static Texture2D texGrabHandle
|
||||||
|
{
|
||||||
|
get { return s_Cached[ (int)ResourceName.grab_handle ]; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets light or dark texture "remove_button.png".
|
||||||
|
/// </summary>
|
||||||
|
public static Texture2D texRemoveButton
|
||||||
|
{
|
||||||
|
get { return s_Cached[ (int)ResourceName.remove_button ]; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets light or dark texture "remove_button_active.png".
|
||||||
|
/// </summary>
|
||||||
|
public static Texture2D texRemoveButtonActive
|
||||||
|
{
|
||||||
|
get { return s_Cached[ (int)ResourceName.remove_button_active ]; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets light or dark texture "title_background.png".
|
||||||
|
/// </summary>
|
||||||
|
public static Texture2D texTitleBackground
|
||||||
|
{
|
||||||
|
get { return s_Cached[ (int)ResourceName.title_background ]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Generated Resources
|
||||||
|
|
||||||
|
public static Texture2D texItemSplitter { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate special textures.
|
||||||
|
/// </summary>
|
||||||
|
private static void GenerateSpecialTextures()
|
||||||
|
{
|
||||||
|
var splitterColor = EditorGUIUtility.isProSkin
|
||||||
|
? new Color( 1f, 1f, 1f, 0.14f )
|
||||||
|
: new Color( 0.59f, 0.59f, 0.59f, 0.55f )
|
||||||
|
;
|
||||||
|
texItemSplitter = CreatePixelTexture( "(Generated) Item Splitter", splitterColor );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create 1x1 pixel texture of specified color.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name for texture object.</param>
|
||||||
|
/// <param name="color">Pixel color.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// The new <c>Texture2D</c> instance.
|
||||||
|
/// </returns>
|
||||||
|
public static Texture2D CreatePixelTexture( string name, Color color )
|
||||||
|
{
|
||||||
|
var tex = new Texture2D( 1, 1, TextureFormat.ARGB32, false, true );
|
||||||
|
tex.name = name;
|
||||||
|
tex.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
tex.filterMode = FilterMode.Point;
|
||||||
|
tex.SetPixel( 0, 0, color );
|
||||||
|
tex.Apply();
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Load PNG from Base-64 Encoded String
|
||||||
|
|
||||||
|
private static Texture2D[] s_Cached;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read textures from base-64 encoded strings. Automatically selects assets based
|
||||||
|
/// upon whether the light or dark (pro) skin is active.
|
||||||
|
/// </summary>
|
||||||
|
private static void LoadResourceAssets()
|
||||||
|
{
|
||||||
|
var skin = EditorGUIUtility.isProSkin ? s_DarkSkin : s_LightSkin;
|
||||||
|
s_Cached = new Texture2D[ skin.Length ];
|
||||||
|
|
||||||
|
for( int i = 0; i < s_Cached.Length; ++i )
|
||||||
|
{
|
||||||
|
// Get image data (PNG) from base64 encoded strings.
|
||||||
|
byte[] imageData = Convert.FromBase64String( skin[ i ] );
|
||||||
|
|
||||||
|
// Gather image size from image data.
|
||||||
|
int texWidth, texHeight;
|
||||||
|
GetImageSize( imageData, out texWidth, out texHeight );
|
||||||
|
|
||||||
|
// Generate texture asset.
|
||||||
|
var tex = new Texture2D( texWidth, texHeight, TextureFormat.ARGB32, false, true );
|
||||||
|
tex.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
tex.name = "(Generated) ReorderableList:" + i;
|
||||||
|
tex.filterMode = FilterMode.Point;
|
||||||
|
tex.LoadImage( imageData );
|
||||||
|
|
||||||
|
s_Cached[ i ] = tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_LightSkin = null;
|
||||||
|
s_DarkSkin = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read width and height if PNG file in pixels.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imageData">PNG image data.</param>
|
||||||
|
/// <param name="width">Width of image in pixels.</param>
|
||||||
|
/// <param name="height">Height of image in pixels.</param>
|
||||||
|
private static void GetImageSize( byte[] imageData, out int width, out int height )
|
||||||
|
{
|
||||||
|
width = ReadInt( imageData, 3 + 15 );
|
||||||
|
height = ReadInt( imageData, 3 + 15 + 2 + 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ReadInt( byte[] imageData, int offset )
|
||||||
|
{
|
||||||
|
return ( imageData[ offset ] << 8 ) | imageData[ offset + 1 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GUI Helper
|
||||||
|
private static GUIStyle s_TempStyle = new GUIStyle();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draw texture using <see cref="GUIStyle"/> to workaround bug in Unity where
|
||||||
|
/// <see cref="GUI.DrawTexture"/> flickers when embedded inside a property drawer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="position">Position of which to draw texture in space of GUI.</param>
|
||||||
|
/// <param name="texture">Texture.</param>
|
||||||
|
public static void DrawTexture( Rect position, Texture2D texture )
|
||||||
|
{
|
||||||
|
if( Event.current.type != EventType.Repaint )
|
||||||
|
return;
|
||||||
|
|
||||||
|
s_TempStyle.normal.background = texture;
|
||||||
|
|
||||||
|
s_TempStyle.Draw( position, GUIContent.none, false, false, false, false );
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 60c609ded101b0a468fb5cf27b31cf27
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
|
@ -0,0 +1,350 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="ServerSettingsInspector.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2016 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// This is a custom editor for the ServerSettings scriptable object.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
[CustomEditor(typeof (ServerSettings))]
|
||||||
|
public class ServerSettingsInspector : Editor
|
||||||
|
{
|
||||||
|
private bool showMustHaveRegion;
|
||||||
|
private CloudRegionCode lastUsedRegion;
|
||||||
|
private ServerConnection lastServer;
|
||||||
|
|
||||||
|
|
||||||
|
[ExecuteInEditMode]
|
||||||
|
public void OnEnable()
|
||||||
|
{
|
||||||
|
this.lastUsedRegion = ServerSettings.BestRegionCodeInPreferences;
|
||||||
|
EditorApplication.update += this.OnUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void OnDisable()
|
||||||
|
{
|
||||||
|
EditorApplication.update -= this.OnUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void OnUpdate()
|
||||||
|
{
|
||||||
|
if (this.lastUsedRegion != ServerSettings.BestRegionCodeInPreferences)
|
||||||
|
{
|
||||||
|
this.lastUsedRegion = ServerSettings.BestRegionCodeInPreferences;
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
// this won't repaint when we disconnect but it's "good enough" to update when we connect and switch servers.
|
||||||
|
if (Application.isPlaying && this.lastServer != PhotonNetwork.Server)
|
||||||
|
{
|
||||||
|
this.lastServer = PhotonNetwork.Server;
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
ServerSettings settings = (ServerSettings) target;
|
||||||
|
Undo.RecordObject(settings, "Edit PhotonServerSettings");
|
||||||
|
settings.HostType = (ServerSettings.HostingOption) EditorGUILayout.EnumPopup("Hosting", settings.HostType);
|
||||||
|
EditorGUI.indentLevel = 1;
|
||||||
|
|
||||||
|
switch (settings.HostType)
|
||||||
|
{
|
||||||
|
case ServerSettings.HostingOption.BestRegion:
|
||||||
|
case ServerSettings.HostingOption.PhotonCloud:
|
||||||
|
// region selection
|
||||||
|
if (settings.HostType == ServerSettings.HostingOption.PhotonCloud)
|
||||||
|
{
|
||||||
|
settings.PreferredRegion = (CloudRegionCode)EditorGUILayout.EnumPopup("Region", settings.PreferredRegion);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string _regionFeedback = "Prefs:"+ServerSettings.BestRegionCodeInPreferences.ToString();
|
||||||
|
|
||||||
|
// the NameServer does not have a region itself. it's global (although it has regional instances)
|
||||||
|
if (PhotonNetwork.connected && PhotonNetwork.Server != ServerConnection.NameServer)
|
||||||
|
{
|
||||||
|
_regionFeedback = "Current:" + PhotonNetwork.CloudRegion + " " + _regionFeedback;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal ();
|
||||||
|
EditorGUILayout.PrefixLabel (" ");
|
||||||
|
Rect rect = GUILayoutUtility.GetRect(new GUIContent(_regionFeedback),"Label");
|
||||||
|
int indentLevel = EditorGUI.indentLevel;
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
EditorGUI.LabelField (rect, _regionFeedback);
|
||||||
|
EditorGUI.indentLevel = indentLevel;
|
||||||
|
|
||||||
|
rect.x += rect.width-39;
|
||||||
|
rect.width = 39;
|
||||||
|
|
||||||
|
rect.height -=2;
|
||||||
|
if (GUI.Button(rect,"Reset",EditorStyles.miniButton))
|
||||||
|
{
|
||||||
|
ServerSettings.ResetBestRegionCodeInPreferences();
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal ();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CloudRegionFlag valRegions = (CloudRegionFlag)EditorGUILayout.EnumMaskField("Enabled Regions", settings.EnabledRegions);
|
||||||
|
|
||||||
|
if (valRegions != settings.EnabledRegions)
|
||||||
|
{
|
||||||
|
settings.EnabledRegions = valRegions;
|
||||||
|
this.showMustHaveRegion = valRegions == 0;
|
||||||
|
}
|
||||||
|
if (this.showMustHaveRegion)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("You should enable at least two regions for 'Best Region' hosting.", MessageType.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// appid
|
||||||
|
string valAppId = EditorGUILayout.TextField("AppId", settings.AppID);
|
||||||
|
if (valAppId != settings.AppID)
|
||||||
|
{
|
||||||
|
settings.AppID = valAppId.Trim();
|
||||||
|
}
|
||||||
|
if (!ServerSettings.IsAppId(settings.AppID))
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("PUN needs an AppId (GUID).\nFind it online in the Dashboard.", MessageType.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
// protocol
|
||||||
|
ConnectionProtocol valProtocol = settings.Protocol;
|
||||||
|
valProtocol = (ConnectionProtocol) EditorGUILayout.EnumPopup("Protocol", valProtocol);
|
||||||
|
settings.Protocol = (ConnectionProtocol) valProtocol;
|
||||||
|
#if UNITY_WEBGL
|
||||||
|
EditorGUILayout.HelpBox("WebGL always use Secure WebSockets as protocol.\nThis setting gets ignored in current export.", MessageType.Warning);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ServerSettings.HostingOption.SelfHosted:
|
||||||
|
// address and port (depends on protocol below)
|
||||||
|
bool hidePort = false;
|
||||||
|
if (settings.Protocol == ConnectionProtocol.Udp && (settings.ServerPort == 4530 || settings.ServerPort == 0))
|
||||||
|
{
|
||||||
|
settings.ServerPort = 5055;
|
||||||
|
}
|
||||||
|
else if (settings.Protocol == ConnectionProtocol.Tcp && (settings.ServerPort == 5055 || settings.ServerPort == 0))
|
||||||
|
{
|
||||||
|
settings.ServerPort = 4530;
|
||||||
|
}
|
||||||
|
#if RHTTP
|
||||||
|
if (settings.Protocol == ConnectionProtocol.RHttp)
|
||||||
|
{
|
||||||
|
settings.ServerPort = 0;
|
||||||
|
hidePort = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
settings.ServerAddress = EditorGUILayout.TextField("Server Address", settings.ServerAddress);
|
||||||
|
settings.ServerAddress = settings.ServerAddress.Trim();
|
||||||
|
if (!hidePort)
|
||||||
|
{
|
||||||
|
settings.ServerPort = EditorGUILayout.IntField("Server Port", settings.ServerPort);
|
||||||
|
}
|
||||||
|
// protocol
|
||||||
|
valProtocol = settings.Protocol;
|
||||||
|
valProtocol = (ConnectionProtocol)EditorGUILayout.EnumPopup("Protocol", valProtocol);
|
||||||
|
settings.Protocol = (ConnectionProtocol)valProtocol;
|
||||||
|
#if UNITY_WEBGL
|
||||||
|
EditorGUILayout.HelpBox("WebGL always use Secure WebSockets as protocol.\nThis setting gets ignored in current export.", MessageType.Warning);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// appid
|
||||||
|
settings.AppID = EditorGUILayout.TextField("AppId", settings.AppID);
|
||||||
|
settings.AppID = settings.AppID.Trim();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ServerSettings.HostingOption.OfflineMode:
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
EditorGUILayout.HelpBox("In 'Offline Mode', the client does not communicate with a server.\nAll settings are hidden currently.", MessageType.Info);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ServerSettings.HostingOption.NotSet:
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
EditorGUILayout.HelpBox("Hosting is 'Not Set'.\nConnectUsingSettings() will not be able to connect.\nSelect another option or run the PUN Wizard.", MessageType.Info);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DrawDefaultInspector();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PhotonEditor.CheckPunPlus())
|
||||||
|
{
|
||||||
|
settings.Protocol = ConnectionProtocol.Udp;
|
||||||
|
EditorGUILayout.HelpBox("You seem to use PUN+.\nPUN+ only supports reliable UDP so the protocol is locked.", MessageType.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// CHAT SETTINGS
|
||||||
|
if (PhotonEditorUtils.HasChat)
|
||||||
|
{
|
||||||
|
GUILayout.Space(5);
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
EditorGUILayout.LabelField("Photon Chat Settings");
|
||||||
|
EditorGUI.indentLevel = 1;
|
||||||
|
string valChatAppid = EditorGUILayout.TextField("Chat AppId", settings.ChatAppID);
|
||||||
|
if (valChatAppid != settings.ChatAppID)
|
||||||
|
{
|
||||||
|
settings.ChatAppID = valChatAppid.Trim();
|
||||||
|
}
|
||||||
|
if (!ServerSettings.IsAppId(settings.ChatAppID))
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("Photon Chat needs an AppId (GUID).\nFind it online in the Dashboard.", MessageType.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// VOICE SETTINGS
|
||||||
|
if (PhotonEditorUtils.HasVoice)
|
||||||
|
{
|
||||||
|
GUILayout.Space(5);
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
EditorGUILayout.LabelField("Photon Voice Settings");
|
||||||
|
EditorGUI.indentLevel = 1;
|
||||||
|
switch (settings.HostType)
|
||||||
|
{
|
||||||
|
case ServerSettings.HostingOption.BestRegion:
|
||||||
|
case ServerSettings.HostingOption.PhotonCloud:
|
||||||
|
// voice appid
|
||||||
|
string valVoiceAppId = EditorGUILayout.TextField("Voice AppId", settings.VoiceAppID);
|
||||||
|
if (valVoiceAppId != settings.VoiceAppID)
|
||||||
|
{
|
||||||
|
settings.VoiceAppID = valVoiceAppId.Trim();
|
||||||
|
}
|
||||||
|
if (!ServerSettings.IsAppId(settings.VoiceAppID))
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("Photon Voice needs an AppId (GUID).\nFind it online in the Dashboard.", MessageType.Warning);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ServerSettings.HostingOption.SelfHosted:
|
||||||
|
if (settings.VoiceServerPort == 0)
|
||||||
|
{
|
||||||
|
settings.VoiceServerPort = 5055;
|
||||||
|
}
|
||||||
|
settings.VoiceServerPort = EditorGUILayout.IntField("Server Port UDP", settings.VoiceServerPort);
|
||||||
|
break;
|
||||||
|
case ServerSettings.HostingOption.OfflineMode:
|
||||||
|
case ServerSettings.HostingOption.NotSet:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// PUN Client Settings
|
||||||
|
GUILayout.Space(5);
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
EditorGUILayout.LabelField("Client Settings");
|
||||||
|
EditorGUI.indentLevel = 1;
|
||||||
|
//EditorGUILayout.LabelField("game version");
|
||||||
|
settings.JoinLobby = EditorGUILayout.Toggle("Auto-Join Lobby", settings.JoinLobby);
|
||||||
|
settings.EnableLobbyStatistics = EditorGUILayout.Toggle("Enable Lobby Stats", settings.EnableLobbyStatistics);
|
||||||
|
|
||||||
|
// Pun Logging Level
|
||||||
|
PhotonLogLevel _PunLogging = (PhotonLogLevel)EditorGUILayout.EnumPopup("Pun Logging", settings.PunLogging);
|
||||||
|
if (EditorApplication.isPlaying && PhotonNetwork.logLevel!=_PunLogging)
|
||||||
|
{
|
||||||
|
PhotonNetwork.logLevel = _PunLogging;
|
||||||
|
}
|
||||||
|
settings.PunLogging = _PunLogging;
|
||||||
|
|
||||||
|
// Network Logging Level
|
||||||
|
DebugLevel _DebugLevel = (DebugLevel)EditorGUILayout.EnumPopup("Network Logging", settings.NetworkLogging);
|
||||||
|
if (EditorApplication.isPlaying && settings.NetworkLogging!=_DebugLevel)
|
||||||
|
{
|
||||||
|
settings.NetworkLogging = _DebugLevel;
|
||||||
|
}
|
||||||
|
settings.NetworkLogging = _DebugLevel;
|
||||||
|
|
||||||
|
|
||||||
|
//EditorGUILayout.LabelField("automaticallySyncScene");
|
||||||
|
//EditorGUILayout.LabelField("autoCleanUpPlayerObjects");
|
||||||
|
//EditorGUILayout.LabelField("lobby stats");
|
||||||
|
//EditorGUILayout.LabelField("sendrate / serialize rate");
|
||||||
|
//EditorGUILayout.LabelField("quick resends");
|
||||||
|
//EditorGUILayout.LabelField("max resends");
|
||||||
|
//EditorGUILayout.LabelField("enable crc checking");
|
||||||
|
|
||||||
|
|
||||||
|
// Application settings
|
||||||
|
GUILayout.Space(5);
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
EditorGUILayout.LabelField("Build Settings");
|
||||||
|
EditorGUI.indentLevel = 1;
|
||||||
|
|
||||||
|
settings.RunInBackground = EditorGUILayout.Toggle("Run In Background", settings.RunInBackground);
|
||||||
|
|
||||||
|
|
||||||
|
// RPC-shortcut list
|
||||||
|
GUILayout.Space(5);
|
||||||
|
EditorGUI.indentLevel = 0;
|
||||||
|
SerializedObject sObj = new SerializedObject(target);
|
||||||
|
SerializedProperty sRpcs = sObj.FindProperty("RpcList");
|
||||||
|
EditorGUILayout.PropertyField(sRpcs, true);
|
||||||
|
sObj.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
GUILayout.Space(20);
|
||||||
|
if (GUILayout.Button("Refresh RPCs"))
|
||||||
|
{
|
||||||
|
PhotonEditor.UpdateRpcList();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("Clear RPCs"))
|
||||||
|
{
|
||||||
|
PhotonEditor.ClearRpcList();
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("Log HashCode"))
|
||||||
|
{
|
||||||
|
Debug.Log("RPC-List HashCode: " + RpcListHashCode() + ". Make sure clients that send each other RPCs have the same RPC-List.");
|
||||||
|
}
|
||||||
|
GUILayout.Space(20);
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
|
||||||
|
//SerializedProperty sp = serializedObject.FindProperty("RpcList");
|
||||||
|
//EditorGUILayout.PropertyField(sp, true);
|
||||||
|
|
||||||
|
if (GUI.changed)
|
||||||
|
{
|
||||||
|
EditorUtility.SetDirty(target); // even in Unity 5.3+ it's OK to SetDirty() for non-scene objects.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int RpcListHashCode()
|
||||||
|
{
|
||||||
|
// this is a hashcode generated to (more) easily compare this Editor's RPC List with some other
|
||||||
|
int hashCode = PhotonNetwork.PhotonServerSettings.RpcList.Count + 1;
|
||||||
|
foreach (string s in PhotonNetwork.PhotonServerSettings.RpcList)
|
||||||
|
{
|
||||||
|
int h1 = s.GetHashCode();
|
||||||
|
hashCode = ((h1 << 5) + h1) ^ hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 21239ba77ac4b534f958e8617ef13ede
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 00aec890a2a7844469d962ba13bc026b
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1505887640
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,349 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="PhotonAnimatorViewEditor.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2016 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// This is a custom editor for the AnimatorView component.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
#if UNITY_5 && !UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2 || UNITY_5_4_OR_NEWER
|
||||||
|
#define UNITY_MIN_5_3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#if UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7
|
||||||
|
using UnityEditorInternal;
|
||||||
|
#elif UNITY_5 || UNITY_5_0 || UNITY_2017
|
||||||
|
using UnityEditor.Animations;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[CustomEditor(typeof (PhotonAnimatorView))]
|
||||||
|
public class PhotonAnimatorViewEditor : Editor
|
||||||
|
{
|
||||||
|
private Animator m_Animator;
|
||||||
|
private PhotonAnimatorView m_Target;
|
||||||
|
|
||||||
|
#if UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5 || UNITY_5_0 || UNITY_2017
|
||||||
|
private AnimatorController m_Controller;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private const string TRIGGER_HELP_URL = "https://doc.photonengine.com/en-us/pun/current/manuals-and-demos/animatorviewtriggerhelp/";
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
//base.OnInspectorGUI();
|
||||||
|
|
||||||
|
if (this.m_Animator == null)
|
||||||
|
{
|
||||||
|
GUILayout.BeginVertical(GUI.skin.box);
|
||||||
|
GUILayout.Label("GameObject doesn't have an Animator component to synchronize");
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawWeightInspector();
|
||||||
|
|
||||||
|
if (GetLayerCount() == 0)
|
||||||
|
{
|
||||||
|
GUILayout.BeginVertical(GUI.skin.box);
|
||||||
|
GUILayout.Label("Animator doesn't have any layers setup to synchronize");
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawParameterInspector();
|
||||||
|
|
||||||
|
if (GetParameterCount() == 0)
|
||||||
|
{
|
||||||
|
GUILayout.BeginVertical(GUI.skin.box);
|
||||||
|
GUILayout.Label("Animator doesn't have any parameters setup to synchronize");
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
//GUILayout.Label( "m_SynchronizeLayers " + serializedObject.FindProperty( "m_SynchronizeLayers" ).arraySize );
|
||||||
|
//GUILayout.Label( "m_SynchronizeParameters " + serializedObject.FindProperty( "m_SynchronizeParameters" ).arraySize );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int GetLayerCount()
|
||||||
|
{
|
||||||
|
#if UNITY_5 || UNITY_5_0 || UNITY_2017
|
||||||
|
return (this.m_Controller == null) ? 0 : this.m_Controller.layers.Length;
|
||||||
|
#else
|
||||||
|
return (this.m_Controller == null) ? 0 : this.m_Controller.layerCount;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if UNITY_5 || UNITY_5_0 || UNITY_2017
|
||||||
|
private RuntimeAnimatorController GetEffectiveController(Animator animator)
|
||||||
|
{
|
||||||
|
RuntimeAnimatorController controller = animator.runtimeAnimatorController;
|
||||||
|
|
||||||
|
AnimatorOverrideController overrideController = controller as AnimatorOverrideController;
|
||||||
|
while (overrideController != null)
|
||||||
|
{
|
||||||
|
controller = overrideController.runtimeAnimatorController;
|
||||||
|
overrideController = controller as AnimatorOverrideController;
|
||||||
|
}
|
||||||
|
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
this.m_Target = (PhotonAnimatorView) target;
|
||||||
|
this.m_Animator = this.m_Target.GetComponent<Animator>();
|
||||||
|
|
||||||
|
#if UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7
|
||||||
|
this.m_Controller = AnimatorController.GetEffectiveAnimatorController(this.m_Animator);
|
||||||
|
#elif UNITY_5 || UNITY_5_0 || UNITY_2017
|
||||||
|
this.m_Controller = this.GetEffectiveController(this.m_Animator) as AnimatorController;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CheckIfStoredParametersExist();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawWeightInspector()
|
||||||
|
{
|
||||||
|
SerializedProperty foldoutProperty = serializedObject.FindProperty("ShowLayerWeightsInspector");
|
||||||
|
foldoutProperty.boolValue = PhotonGUI.ContainerHeaderFoldout("Synchronize Layer Weights", foldoutProperty.boolValue);
|
||||||
|
|
||||||
|
if (foldoutProperty.boolValue == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float lineHeight = 20;
|
||||||
|
Rect containerRect = PhotonGUI.ContainerBody(this.GetLayerCount()*lineHeight);
|
||||||
|
|
||||||
|
for (int i = 0; i < this.GetLayerCount(); ++i)
|
||||||
|
{
|
||||||
|
if (this.m_Target.DoesLayerSynchronizeTypeExist(i) == false)
|
||||||
|
{
|
||||||
|
this.m_Target.SetLayerSynchronized(i, PhotonAnimatorView.SynchronizeType.Disabled);
|
||||||
|
|
||||||
|
#if !UNITY_MIN_5_3
|
||||||
|
EditorUtility.SetDirty(this.m_Target);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotonAnimatorView.SynchronizeType syncType = this.m_Target.GetLayerSynchronizeType(i);
|
||||||
|
|
||||||
|
Rect elementRect = new Rect(containerRect.xMin, containerRect.yMin + i*lineHeight, containerRect.width, lineHeight);
|
||||||
|
|
||||||
|
Rect labelRect = new Rect(elementRect.xMin + 5, elementRect.yMin + 2, EditorGUIUtility.labelWidth - 5, elementRect.height);
|
||||||
|
GUI.Label(labelRect, "Layer " + i);
|
||||||
|
|
||||||
|
Rect popupRect = new Rect(elementRect.xMin + EditorGUIUtility.labelWidth, elementRect.yMin + 2, elementRect.width - EditorGUIUtility.labelWidth - 5, EditorGUIUtility.singleLineHeight);
|
||||||
|
syncType = (PhotonAnimatorView.SynchronizeType) EditorGUI.EnumPopup(popupRect, syncType);
|
||||||
|
|
||||||
|
if (i < this.GetLayerCount() - 1)
|
||||||
|
{
|
||||||
|
Rect splitterRect = new Rect(elementRect.xMin + 2, elementRect.yMax, elementRect.width - 4, 1);
|
||||||
|
PhotonGUI.DrawSplitter(splitterRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncType != this.m_Target.GetLayerSynchronizeType(i))
|
||||||
|
{
|
||||||
|
Undo.RecordObject(target, "Modify Synchronize Layer Weights");
|
||||||
|
this.m_Target.SetLayerSynchronized(i, syncType);
|
||||||
|
|
||||||
|
#if !UNITY_MIN_5_3
|
||||||
|
EditorUtility.SetDirty(this.m_Target);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetParameterCount()
|
||||||
|
{
|
||||||
|
#if UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7
|
||||||
|
return (this.m_Controller == null) ? 0 : this.m_Controller.parameterCount;
|
||||||
|
#elif UNITY_5 || UNITY_5_0 || UNITY_2017
|
||||||
|
return (this.m_Controller == null) ? 0 : this.m_Controller.parameters.Length;
|
||||||
|
#else
|
||||||
|
return (m_Animator == null) ? 0 : m_Animator.parameters.Length;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnimatorControllerParameter GetAnimatorControllerParameter(int i)
|
||||||
|
{
|
||||||
|
#if UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7
|
||||||
|
return this.m_Controller.GetParameter(i);
|
||||||
|
#elif UNITY_5 || UNITY_5_0 || UNITY_2017
|
||||||
|
return this.m_Controller.parameters[i];
|
||||||
|
#else
|
||||||
|
return m_Animator.parameters[i];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DoesParameterExist(string name)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < this.GetParameterCount(); ++i)
|
||||||
|
{
|
||||||
|
if (GetAnimatorControllerParameter(i).name == name)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckIfStoredParametersExist()
|
||||||
|
{
|
||||||
|
var syncedParams = this.m_Target.GetSynchronizedParameters();
|
||||||
|
List<string> paramsToRemove = new List<string>();
|
||||||
|
|
||||||
|
for (int i = 0; i < syncedParams.Count; ++i)
|
||||||
|
{
|
||||||
|
string parameterName = syncedParams[i].Name;
|
||||||
|
if (DoesParameterExist(parameterName) == false)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Parameter '" + this.m_Target.GetSynchronizedParameters()[i].Name + "' doesn't exist anymore. Removing it from the list of synchronized parameters");
|
||||||
|
paramsToRemove.Add(parameterName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paramsToRemove.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (string param in paramsToRemove)
|
||||||
|
{
|
||||||
|
this.m_Target.GetSynchronizedParameters().RemoveAll(item => item.Name == param);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !UNITY_MIN_5_3
|
||||||
|
EditorUtility.SetDirty(this.m_Target);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void DrawParameterInspector()
|
||||||
|
{
|
||||||
|
// flag to expose a note in Interface if one or more trigger(s) are synchronized
|
||||||
|
bool isUsingTriggers = false;
|
||||||
|
|
||||||
|
SerializedProperty foldoutProperty = serializedObject.FindProperty("ShowParameterInspector");
|
||||||
|
foldoutProperty.boolValue = PhotonGUI.ContainerHeaderFoldout("Synchronize Parameters", foldoutProperty.boolValue);
|
||||||
|
|
||||||
|
if (foldoutProperty.boolValue == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float lineHeight = 20;
|
||||||
|
Rect containerRect = PhotonGUI.ContainerBody(GetParameterCount()*lineHeight);
|
||||||
|
|
||||||
|
for (int i = 0; i < GetParameterCount(); i++)
|
||||||
|
{
|
||||||
|
AnimatorControllerParameter parameter = null;
|
||||||
|
parameter = GetAnimatorControllerParameter(i);
|
||||||
|
|
||||||
|
string defaultValue = "";
|
||||||
|
|
||||||
|
if (parameter.type == AnimatorControllerParameterType.Bool)
|
||||||
|
{
|
||||||
|
if (Application.isPlaying && m_Animator.gameObject.activeInHierarchy)
|
||||||
|
{
|
||||||
|
defaultValue += m_Animator.GetBool(parameter.name);
|
||||||
|
}else{
|
||||||
|
defaultValue += parameter.defaultBool.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parameter.type == AnimatorControllerParameterType.Float)
|
||||||
|
{
|
||||||
|
if (Application.isPlaying && m_Animator.gameObject.activeInHierarchy)
|
||||||
|
{
|
||||||
|
defaultValue += m_Animator.GetFloat(parameter.name).ToString("0.00");
|
||||||
|
}else{
|
||||||
|
defaultValue += parameter.defaultFloat.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parameter.type == AnimatorControllerParameterType.Int)
|
||||||
|
{
|
||||||
|
if (Application.isPlaying && m_Animator.gameObject.activeInHierarchy)
|
||||||
|
{
|
||||||
|
defaultValue += m_Animator.GetInteger(parameter.name);
|
||||||
|
}else{
|
||||||
|
defaultValue += parameter.defaultInt.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parameter.type == AnimatorControllerParameterType.Trigger)
|
||||||
|
{
|
||||||
|
if (Application.isPlaying && m_Animator.gameObject.activeInHierarchy)
|
||||||
|
{
|
||||||
|
defaultValue += m_Animator.GetBool(parameter.name);
|
||||||
|
}else{
|
||||||
|
defaultValue += parameter.defaultBool.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.m_Target.DoesParameterSynchronizeTypeExist(parameter.name) == false)
|
||||||
|
{
|
||||||
|
this.m_Target.SetParameterSynchronized(parameter.name, (PhotonAnimatorView.ParameterType) parameter.type, PhotonAnimatorView.SynchronizeType.Disabled);
|
||||||
|
|
||||||
|
#if !UNITY_MIN_5_3
|
||||||
|
EditorUtility.SetDirty(this.m_Target);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotonAnimatorView.SynchronizeType value = this.m_Target.GetParameterSynchronizeType(parameter.name);
|
||||||
|
|
||||||
|
// check if using trigger and actually synchronizing it
|
||||||
|
if (value!=PhotonAnimatorView.SynchronizeType.Disabled &¶meter.type == AnimatorControllerParameterType.Trigger)
|
||||||
|
{
|
||||||
|
isUsingTriggers = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect elementRect = new Rect(containerRect.xMin, containerRect.yMin + i*lineHeight, containerRect.width, lineHeight);
|
||||||
|
|
||||||
|
Rect labelRect = new Rect(elementRect.xMin + 5, elementRect.yMin + 2, EditorGUIUtility.labelWidth - 5, elementRect.height);
|
||||||
|
GUI.Label(labelRect, parameter.name + " (" + defaultValue + ")");
|
||||||
|
|
||||||
|
Rect popupRect = new Rect(elementRect.xMin + EditorGUIUtility.labelWidth, elementRect.yMin + 2, elementRect.width - EditorGUIUtility.labelWidth - 5, EditorGUIUtility.singleLineHeight);
|
||||||
|
value = (PhotonAnimatorView.SynchronizeType) EditorGUI.EnumPopup(popupRect, value);
|
||||||
|
|
||||||
|
if (i < GetParameterCount() - 1)
|
||||||
|
{
|
||||||
|
Rect splitterRect = new Rect(elementRect.xMin + 2, elementRect.yMax, elementRect.width - 4, 1);
|
||||||
|
PhotonGUI.DrawSplitter(splitterRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (value != this.m_Target.GetParameterSynchronizeType(parameter.name))
|
||||||
|
{
|
||||||
|
Undo.RecordObject(target, "Modify Synchronize Parameter " + parameter.name);
|
||||||
|
this.m_Target.SetParameterSynchronized(parameter.name, (PhotonAnimatorView.ParameterType) parameter.type, value);
|
||||||
|
|
||||||
|
#if !UNITY_MIN_5_3
|
||||||
|
EditorUtility.SetDirty(this.m_Target);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// display note when synchronized triggers are detected.
|
||||||
|
if (isUsingTriggers)
|
||||||
|
{
|
||||||
|
GUILayout.BeginHorizontal(GUI.skin.box);
|
||||||
|
GUILayout.Label("When using triggers, make sure this component is last in the stack");
|
||||||
|
if (GUILayout.Button(PhotonGUI.HelpIcon,GUIStyle.none,GUILayout.Width(16)) )
|
||||||
|
{
|
||||||
|
Application.OpenURL(TRIGGER_HELP_URL);
|
||||||
|
}
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a3f61bade114730459f7ad45f5f292c1
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
|
@ -0,0 +1,21 @@
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[CustomEditor(typeof (PhotonRigidbody2DView))]
|
||||||
|
public class PhotonRigidbody2DViewEditor : Editor
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
PhotonGUI.ContainerHeader("Options");
|
||||||
|
|
||||||
|
Rect containerRect = PhotonGUI.ContainerBody(EditorGUIUtility.singleLineHeight*2 + 10);
|
||||||
|
|
||||||
|
Rect propertyRect = new Rect(containerRect.xMin + 5, containerRect.yMin + 5, containerRect.width, EditorGUIUtility.singleLineHeight);
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_SynchronizeVelocity"), new GUIContent("Synchronize Velocity"));
|
||||||
|
|
||||||
|
propertyRect.y += EditorGUIUtility.singleLineHeight;
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_SynchronizeAngularVelocity"), new GUIContent("Synchronize Angular Velocity"));
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3a82e8e86b9eecb40ac3f6ebc949f6ef
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
|
@ -0,0 +1,21 @@
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[CustomEditor(typeof (PhotonRigidbodyView))]
|
||||||
|
public class PhotonRigidbodyViewEditor : Editor
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
PhotonGUI.ContainerHeader("Options");
|
||||||
|
|
||||||
|
Rect containerRect = PhotonGUI.ContainerBody(EditorGUIUtility.singleLineHeight*2 + 10);
|
||||||
|
|
||||||
|
Rect propertyRect = new Rect(containerRect.xMin + 5, containerRect.yMin + 5, containerRect.width, EditorGUIUtility.singleLineHeight);
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_SynchronizeVelocity"), new GUIContent("Synchronize Velocity"));
|
||||||
|
|
||||||
|
propertyRect.y += EditorGUIUtility.singleLineHeight;
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_SynchronizeAngularVelocity"), new GUIContent("Synchronize Angular Velocity"));
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4bcfebc9a2f1074488adedd1fe84e6c9
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
|
@ -0,0 +1,434 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="PhotonTransformViewEditor.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2016 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// This is a custom editor for the TransformView component.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
#if UNITY_5 && !UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2 || UNITY_5_4_OR_NEWER
|
||||||
|
#define UNITY_MIN_5_3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[CustomEditor(typeof (PhotonTransformView))]
|
||||||
|
public class PhotonTransformViewEditor : Editor
|
||||||
|
{
|
||||||
|
//private PhotonTransformView m_Target;
|
||||||
|
|
||||||
|
private SerializedProperty m_SynchronizePositionProperty;
|
||||||
|
private SerializedProperty m_SynchronizeRotationProperty;
|
||||||
|
private SerializedProperty m_SynchronizeScaleProperty;
|
||||||
|
|
||||||
|
private bool m_InterpolateHelpOpen;
|
||||||
|
private bool m_ExtrapolateHelpOpen;
|
||||||
|
private bool m_InterpolateRotationHelpOpen;
|
||||||
|
private bool m_InterpolateScaleHelpOpen;
|
||||||
|
|
||||||
|
private const int EDITOR_LINE_HEIGHT = 20;
|
||||||
|
|
||||||
|
private const string INTERPOLATE_TOOLTIP =
|
||||||
|
"Choose between synchronizing the value directly (by disabling interpolation) or smoothly move it towards the newest update.";
|
||||||
|
|
||||||
|
private const string INTERPOLATE_HELP =
|
||||||
|
"You can use interpolation to smoothly move your GameObject towards a new position that is received via the network. "
|
||||||
|
+ "This helps to reduce the stuttering movement that results because the network updates only arrive 10 times per second.\n"
|
||||||
|
+ "As a side effect, the GameObject is always lagging behind the actual position a little bit. This can be addressed with extrapolation.";
|
||||||
|
|
||||||
|
private const string EXTRAPOLATE_TOOLTIP = "Extrapolation is used to predict where the GameObject actually is";
|
||||||
|
|
||||||
|
private const string EXTRAPOLATE_HELP =
|
||||||
|
"Whenever you deal with network values, all values you receive will be a little bit out of date since that data needs "
|
||||||
|
+ "to reach you first. You can use extrapolation to try to predict where the player actually is, based on the movement data you have received.\n"
|
||||||
|
+
|
||||||
|
"This has to be tweaked carefully for each specific game in order to insure the optimal prediction. Sometimes it is very easy to extrapolate states, because "
|
||||||
|
+
|
||||||
|
"the GameObject behaves very predictable (for example for vehicles). Other times it can be very hard because the user input is translated directly to the game "
|
||||||
|
+ "and you cannot really predict what the user is going to do (for example in fighting games)";
|
||||||
|
|
||||||
|
private const string INTERPOLATE_HELP_URL = "http://doc.exitgames.com/en/pun/current/tutorials/rpg-movement";
|
||||||
|
private const string EXTRAPOLATE_HELP_URL = "http://doc.exitgames.com/en/pun/current/tutorials/rpg-movement";
|
||||||
|
|
||||||
|
public void OnEnable()
|
||||||
|
{
|
||||||
|
SetupSerializedProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
serializedObject.Update ();
|
||||||
|
|
||||||
|
//this.m_Target = (PhotonTransformView) target;
|
||||||
|
|
||||||
|
DrawIsPlayingWarning();
|
||||||
|
GUI.enabled = !Application.isPlaying;
|
||||||
|
|
||||||
|
DrawSynchronizePositionHeader();
|
||||||
|
DrawSynchronizePositionData();
|
||||||
|
|
||||||
|
GUI.enabled = !Application.isPlaying;
|
||||||
|
DrawSynchronizeRotationHeader();
|
||||||
|
DrawSynchronizeRotationData();
|
||||||
|
|
||||||
|
GUI.enabled = !Application.isPlaying;
|
||||||
|
DrawSynchronizeScaleHeader();
|
||||||
|
DrawSynchronizeScaleData();
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
GUI.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawIsPlayingWarning()
|
||||||
|
{
|
||||||
|
if (Application.isPlaying == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.BeginVertical(GUI.skin.box);
|
||||||
|
{
|
||||||
|
GUILayout.Label("Editing is disabled in play mode so the two objects don't go out of sync");
|
||||||
|
}
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupSerializedProperties()
|
||||||
|
{
|
||||||
|
this.m_SynchronizePositionProperty = serializedObject.FindProperty("m_PositionModel.SynchronizeEnabled");
|
||||||
|
this.m_SynchronizeRotationProperty = serializedObject.FindProperty("m_RotationModel.SynchronizeEnabled");
|
||||||
|
this.m_SynchronizeScaleProperty = serializedObject.FindProperty("m_ScaleModel.SynchronizeEnabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSynchronizePositionHeader()
|
||||||
|
{
|
||||||
|
DrawHeader("Synchronize Position", this.m_SynchronizePositionProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSynchronizePositionData()
|
||||||
|
{
|
||||||
|
if (this.m_SynchronizePositionProperty == null || this.m_SynchronizePositionProperty.boolValue == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedProperty interpolatePositionProperty = serializedObject.FindProperty("m_PositionModel.InterpolateOption");
|
||||||
|
PhotonTransformViewPositionModel.InterpolateOptions interpolateOption = (PhotonTransformViewPositionModel.InterpolateOptions)interpolatePositionProperty.enumValueIndex;
|
||||||
|
|
||||||
|
SerializedProperty extrapolatePositionProperty = serializedObject.FindProperty("m_PositionModel.ExtrapolateOption");
|
||||||
|
PhotonTransformViewPositionModel.ExtrapolateOptions extrapolateOption = (PhotonTransformViewPositionModel.ExtrapolateOptions)extrapolatePositionProperty.enumValueIndex;
|
||||||
|
|
||||||
|
float containerHeight = 155;
|
||||||
|
|
||||||
|
switch (interpolateOption)
|
||||||
|
{
|
||||||
|
case PhotonTransformViewPositionModel.InterpolateOptions.FixedSpeed:
|
||||||
|
case PhotonTransformViewPositionModel.InterpolateOptions.Lerp:
|
||||||
|
containerHeight += EDITOR_LINE_HEIGHT;
|
||||||
|
break;
|
||||||
|
/*case PhotonTransformViewPositionModel.InterpolateOptions.MoveTowardsComplex:
|
||||||
|
containerHeight += EDITOR_LINE_HEIGHT*3;
|
||||||
|
break;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extrapolateOption != PhotonTransformViewPositionModel.ExtrapolateOptions.Disabled)
|
||||||
|
{
|
||||||
|
containerHeight += EDITOR_LINE_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (extrapolateOption)
|
||||||
|
{
|
||||||
|
case PhotonTransformViewPositionModel.ExtrapolateOptions.FixedSpeed:
|
||||||
|
containerHeight += EDITOR_LINE_HEIGHT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.m_InterpolateHelpOpen == true)
|
||||||
|
{
|
||||||
|
containerHeight += GetInterpolateHelpBoxHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.m_ExtrapolateHelpOpen == true)
|
||||||
|
{
|
||||||
|
containerHeight += GetExtrapolateHelpBoxHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// removed Gizmo Options. -3 lines, -1 splitter
|
||||||
|
containerHeight -= EDITOR_LINE_HEIGHT * 2;
|
||||||
|
|
||||||
|
Rect rect = PhotonGUI.ContainerBody(containerHeight);
|
||||||
|
|
||||||
|
Rect propertyRect = new Rect(rect.xMin + 5, rect.yMin + 2, rect.width - 10, EditorGUIUtility.singleLineHeight);
|
||||||
|
|
||||||
|
DrawTeleport(ref propertyRect);
|
||||||
|
DrawSplitter(ref propertyRect);
|
||||||
|
|
||||||
|
DrawSynchronizePositionDataInterpolation(ref propertyRect, interpolatePositionProperty, interpolateOption);
|
||||||
|
DrawSplitter(ref propertyRect);
|
||||||
|
|
||||||
|
DrawSynchronizePositionDataExtrapolation(ref propertyRect, extrapolatePositionProperty, extrapolateOption);
|
||||||
|
DrawSplitter(ref propertyRect);
|
||||||
|
|
||||||
|
DrawSynchronizePositionDataGizmos(ref propertyRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetInterpolateHelpBoxHeight()
|
||||||
|
{
|
||||||
|
return PhotonGUI.RichLabel.CalcHeight(new GUIContent(INTERPOLATE_HELP), Screen.width - 54) + 35;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetExtrapolateHelpBoxHeight()
|
||||||
|
{
|
||||||
|
return PhotonGUI.RichLabel.CalcHeight(new GUIContent(EXTRAPOLATE_HELP), Screen.width - 54) + 35;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSplitter(ref Rect propertyRect)
|
||||||
|
{
|
||||||
|
Rect splitterRect = new Rect(propertyRect.xMin - 3, propertyRect.yMin, propertyRect.width + 6, 1);
|
||||||
|
PhotonGUI.DrawSplitter(splitterRect);
|
||||||
|
|
||||||
|
propertyRect.y += 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSynchronizePositionDataGizmos(ref Rect propertyRect)
|
||||||
|
{
|
||||||
|
GUI.enabled = true;
|
||||||
|
/*PhotonGUI.DrawGizmoOptions(propertyRect, "Synchronized Position Gizmo",
|
||||||
|
serializedObject.FindProperty("m_PositionModel.DrawNetworkGizmo"),
|
||||||
|
serializedObject.FindProperty("m_PositionModel.NetworkGizmoColor"),
|
||||||
|
serializedObject.FindProperty("m_PositionModel.NetworkGizmoType"),
|
||||||
|
serializedObject.FindProperty("m_PositionModel.NetworkGizmoSize"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
|
||||||
|
PhotonGUI.DrawGizmoOptions(propertyRect, "Extrapolated Position Gizmo",
|
||||||
|
serializedObject.FindProperty("m_PositionModel.DrawExtrapolatedGizmo"),
|
||||||
|
serializedObject.FindProperty("m_PositionModel.ExtrapolatedGizmoColor"),
|
||||||
|
serializedObject.FindProperty("m_PositionModel.ExtrapolatedGizmoType"),
|
||||||
|
serializedObject.FindProperty("m_PositionModel.ExtrapolatedGizmoSize"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;*/
|
||||||
|
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_PositionModel.DrawErrorGizmo"),
|
||||||
|
new GUIContent("Draw synchronized position error"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawHelpBox(ref Rect propertyRect, bool isOpen, float height, string helpText, string url)
|
||||||
|
{
|
||||||
|
if (isOpen == true)
|
||||||
|
{
|
||||||
|
Rect helpRect = new Rect(propertyRect.xMin, propertyRect.yMin, propertyRect.width, height - 5);
|
||||||
|
GUI.BeginGroup(helpRect, GUI.skin.box);
|
||||||
|
GUI.Label(new Rect(5, 5, propertyRect.width - 10, height - 30), helpText, PhotonGUI.RichLabel);
|
||||||
|
if (GUI.Button(new Rect(5, height - 30, propertyRect.width - 10, 20), "Read more in our documentation"))
|
||||||
|
{
|
||||||
|
Application.OpenURL(url);
|
||||||
|
}
|
||||||
|
GUI.EndGroup();
|
||||||
|
|
||||||
|
propertyRect.y += height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawPropertyWithHelpIcon(ref Rect propertyRect, ref bool isHelpOpen, SerializedProperty property, string tooltip)
|
||||||
|
{
|
||||||
|
Rect propertyFieldRect = new Rect(propertyRect.xMin, propertyRect.yMin, propertyRect.width - 20, propertyRect.height);
|
||||||
|
string propertyName = ObjectNames.NicifyVariableName(property.name);
|
||||||
|
EditorGUI.PropertyField(propertyFieldRect, property, new GUIContent(propertyName, tooltip));
|
||||||
|
|
||||||
|
Rect helpIconRect = new Rect(propertyFieldRect.xMax + 5, propertyFieldRect.yMin, 20, propertyFieldRect.height);
|
||||||
|
isHelpOpen = GUI.Toggle(helpIconRect, isHelpOpen, PhotonGUI.HelpIcon, GUIStyle.none);
|
||||||
|
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSynchronizePositionDataExtrapolation(ref Rect propertyRect, SerializedProperty extrapolatePositionProperty, PhotonTransformViewPositionModel.ExtrapolateOptions extrapolateOption)
|
||||||
|
{
|
||||||
|
DrawPropertyWithHelpIcon(ref propertyRect, ref this.m_ExtrapolateHelpOpen, extrapolatePositionProperty, EXTRAPOLATE_TOOLTIP);
|
||||||
|
DrawHelpBox(ref propertyRect, this.m_ExtrapolateHelpOpen, GetExtrapolateHelpBoxHeight(), EXTRAPOLATE_HELP, EXTRAPOLATE_HELP_URL);
|
||||||
|
|
||||||
|
if (extrapolateOption != PhotonTransformViewPositionModel.ExtrapolateOptions.Disabled)
|
||||||
|
{
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_PositionModel.ExtrapolateIncludingRoundTripTime"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (extrapolateOption)
|
||||||
|
{
|
||||||
|
case PhotonTransformViewPositionModel.ExtrapolateOptions.FixedSpeed:
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_PositionModel.ExtrapolateSpeed"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawTeleport(ref Rect propertyRect)
|
||||||
|
{
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_PositionModel.TeleportEnabled"),
|
||||||
|
new GUIContent("Enable teleport for great distances"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_PositionModel.TeleportIfDistanceGreaterThan"),
|
||||||
|
new GUIContent("Teleport if distance greater than"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSynchronizePositionDataInterpolation(ref Rect propertyRect, SerializedProperty interpolatePositionProperty,
|
||||||
|
PhotonTransformViewPositionModel.InterpolateOptions interpolateOption)
|
||||||
|
{
|
||||||
|
DrawPropertyWithHelpIcon(ref propertyRect, ref this.m_InterpolateHelpOpen, interpolatePositionProperty, INTERPOLATE_TOOLTIP);
|
||||||
|
DrawHelpBox(ref propertyRect, this.m_InterpolateHelpOpen, GetInterpolateHelpBoxHeight(), INTERPOLATE_HELP, INTERPOLATE_HELP_URL);
|
||||||
|
|
||||||
|
switch (interpolateOption)
|
||||||
|
{
|
||||||
|
case PhotonTransformViewPositionModel.InterpolateOptions.FixedSpeed:
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_PositionModel.InterpolateMoveTowardsSpeed"),
|
||||||
|
new GUIContent("MoveTowards Speed"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PhotonTransformViewPositionModel.InterpolateOptions.Lerp:
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_PositionModel.InterpolateLerpSpeed"), new GUIContent("Lerp Speed"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*case PhotonTransformViewPositionModel.InterpolateOptions.MoveTowardsComplex:
|
||||||
|
Rect curveRect = new Rect(propertyRect.xMin, propertyRect.yMin, propertyRect.width - 100, propertyRect.height);
|
||||||
|
EditorGUI.PropertyField(curveRect, serializedObject.FindProperty("m_PositionModel.InterpolateSpeedCurve"), new GUIContent("MoveTowards Speed Curve"));
|
||||||
|
|
||||||
|
Rect labelRect = new Rect(propertyRect.xMax - 95, propertyRect.yMin, 10, propertyRect.height);
|
||||||
|
GUI.Label(labelRect, "x");
|
||||||
|
|
||||||
|
Rect multiplierRect = new Rect(propertyRect.xMax - 80, propertyRect.yMin, 80, propertyRect.height);
|
||||||
|
EditorGUI.PropertyField(multiplierRect, serializedObject.FindProperty("m_PositionModel.InterpolateMoveTowardsSpeed"), GUIContent.none);
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_PositionModel.InterpolateMoveTowardsAcceleration"),
|
||||||
|
new GUIContent("Acceleration"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_PositionModel.InterpolateMoveTowardsDeceleration"),
|
||||||
|
new GUIContent("Deceleration"));
|
||||||
|
propertyRect.y += EDITOR_LINE_HEIGHT;
|
||||||
|
break;*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSynchronizeRotationHeader()
|
||||||
|
{
|
||||||
|
DrawHeader("Synchronize Rotation", this.m_SynchronizeRotationProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSynchronizeRotationData()
|
||||||
|
{
|
||||||
|
if (this.m_SynchronizeRotationProperty == null || this.m_SynchronizeRotationProperty.boolValue == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedProperty interpolateRotationProperty = serializedObject.FindProperty("m_RotationModel.InterpolateOption");
|
||||||
|
PhotonTransformViewRotationModel.InterpolateOptions interpolateOption =
|
||||||
|
(PhotonTransformViewRotationModel.InterpolateOptions) interpolateRotationProperty.enumValueIndex;
|
||||||
|
|
||||||
|
float containerHeight = 20;
|
||||||
|
|
||||||
|
switch (interpolateOption)
|
||||||
|
{
|
||||||
|
case PhotonTransformViewRotationModel.InterpolateOptions.RotateTowards:
|
||||||
|
case PhotonTransformViewRotationModel.InterpolateOptions.Lerp:
|
||||||
|
containerHeight += EDITOR_LINE_HEIGHT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.m_InterpolateRotationHelpOpen == true)
|
||||||
|
{
|
||||||
|
containerHeight += GetInterpolateHelpBoxHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect rect = PhotonGUI.ContainerBody(containerHeight);
|
||||||
|
Rect propertyRect = new Rect(rect.xMin + 5, rect.yMin + 2, rect.width - 10, EditorGUIUtility.singleLineHeight);
|
||||||
|
|
||||||
|
DrawPropertyWithHelpIcon(ref propertyRect, ref this.m_InterpolateRotationHelpOpen, interpolateRotationProperty, INTERPOLATE_TOOLTIP);
|
||||||
|
DrawHelpBox(ref propertyRect, this.m_InterpolateRotationHelpOpen, GetInterpolateHelpBoxHeight(), INTERPOLATE_HELP, INTERPOLATE_HELP_URL);
|
||||||
|
|
||||||
|
switch (interpolateOption)
|
||||||
|
{
|
||||||
|
case PhotonTransformViewRotationModel.InterpolateOptions.RotateTowards:
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_RotationModel.InterpolateRotateTowardsSpeed"),
|
||||||
|
new GUIContent("RotateTowards Speed"));
|
||||||
|
break;
|
||||||
|
case PhotonTransformViewRotationModel.InterpolateOptions.Lerp:
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_RotationModel.InterpolateLerpSpeed"), new GUIContent("Lerp Speed"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSynchronizeScaleHeader()
|
||||||
|
{
|
||||||
|
DrawHeader("Synchronize Scale", this.m_SynchronizeScaleProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSynchronizeScaleData()
|
||||||
|
{
|
||||||
|
if (this.m_SynchronizeScaleProperty == null || this.m_SynchronizeScaleProperty.boolValue == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedProperty interpolateScaleProperty = serializedObject.FindProperty("m_ScaleModel.InterpolateOption");
|
||||||
|
PhotonTransformViewScaleModel.InterpolateOptions interpolateOption = (PhotonTransformViewScaleModel.InterpolateOptions) interpolateScaleProperty.enumValueIndex;
|
||||||
|
|
||||||
|
float containerHeight = EDITOR_LINE_HEIGHT;
|
||||||
|
|
||||||
|
switch (interpolateOption)
|
||||||
|
{
|
||||||
|
case PhotonTransformViewScaleModel.InterpolateOptions.MoveTowards:
|
||||||
|
case PhotonTransformViewScaleModel.InterpolateOptions.Lerp:
|
||||||
|
containerHeight += EDITOR_LINE_HEIGHT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.m_InterpolateScaleHelpOpen == true)
|
||||||
|
{
|
||||||
|
containerHeight += GetInterpolateHelpBoxHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect rect = PhotonGUI.ContainerBody(containerHeight);
|
||||||
|
Rect propertyRect = new Rect(rect.xMin + 5, rect.yMin + 2, rect.width - 10, EditorGUIUtility.singleLineHeight);
|
||||||
|
|
||||||
|
DrawPropertyWithHelpIcon(ref propertyRect, ref this.m_InterpolateScaleHelpOpen, interpolateScaleProperty, INTERPOLATE_TOOLTIP);
|
||||||
|
DrawHelpBox(ref propertyRect, this.m_InterpolateScaleHelpOpen, GetInterpolateHelpBoxHeight(), INTERPOLATE_HELP, INTERPOLATE_HELP_URL);
|
||||||
|
|
||||||
|
switch (interpolateOption)
|
||||||
|
{
|
||||||
|
case PhotonTransformViewScaleModel.InterpolateOptions.MoveTowards:
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_ScaleModel.InterpolateMoveTowardsSpeed"),
|
||||||
|
new GUIContent("MoveTowards Speed"));
|
||||||
|
break;
|
||||||
|
case PhotonTransformViewScaleModel.InterpolateOptions.Lerp:
|
||||||
|
EditorGUI.PropertyField(propertyRect, serializedObject.FindProperty("m_ScaleModel.InterpolateLerpSpeed"), new GUIContent("Lerp Speed"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawHeader(string label, SerializedProperty property)
|
||||||
|
{
|
||||||
|
if (property == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool newValue = PhotonGUI.ContainerHeaderToggle(label, property.boolValue);
|
||||||
|
|
||||||
|
if (newValue != property.boolValue)
|
||||||
|
{
|
||||||
|
property.boolValue = newValue;
|
||||||
|
property.serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 22292ca8ffb574945bedfaf49266672e
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -0,0 +1,45 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1faa1cf0448470c4ebbb23b97759ab50
|
||||||
|
TextureImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 0
|
||||||
|
linearTexture: 1
|
||||||
|
correctGamma: 0
|
||||||
|
fadeOut: 0
|
||||||
|
borderMipMap: 0
|
||||||
|
mipMapFadeDistanceStart: 1
|
||||||
|
mipMapFadeDistanceEnd: 3
|
||||||
|
bumpmap:
|
||||||
|
convertToNormalMap: 0
|
||||||
|
externalNormalMap: 0
|
||||||
|
heightScale: .25
|
||||||
|
normalMapFilter: 0
|
||||||
|
isReadable: 0
|
||||||
|
grayScaleToAlpha: 0
|
||||||
|
generateCubemap: 0
|
||||||
|
seamlessCubemap: 0
|
||||||
|
textureFormat: -1
|
||||||
|
maxTextureSize: 256
|
||||||
|
textureSettings:
|
||||||
|
filterMode: 0
|
||||||
|
aniso: 1
|
||||||
|
mipBias: -1
|
||||||
|
wrapMode: 1
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 0
|
||||||
|
spriteExtrude: 1
|
||||||
|
spriteMeshType: 1
|
||||||
|
alignment: 0
|
||||||
|
spritePivot: {x: .5, y: .5}
|
||||||
|
spritePixelsToUnits: 100
|
||||||
|
alphaIsTransparency: 1
|
||||||
|
textureType: 2
|
||||||
|
buildTargetSettings: []
|
||||||
|
spriteSheet:
|
||||||
|
sprites: []
|
||||||
|
spritePackingTag:
|
||||||
|
userData:
|
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,45 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9f1212502533cb34188dd6ef094188cb
|
||||||
|
TextureImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 0
|
||||||
|
linearTexture: 1
|
||||||
|
correctGamma: 0
|
||||||
|
fadeOut: 0
|
||||||
|
borderMipMap: 0
|
||||||
|
mipMapFadeDistanceStart: 1
|
||||||
|
mipMapFadeDistanceEnd: 3
|
||||||
|
bumpmap:
|
||||||
|
convertToNormalMap: 0
|
||||||
|
externalNormalMap: 0
|
||||||
|
heightScale: .25
|
||||||
|
normalMapFilter: 0
|
||||||
|
isReadable: 0
|
||||||
|
grayScaleToAlpha: 0
|
||||||
|
generateCubemap: 0
|
||||||
|
seamlessCubemap: 0
|
||||||
|
textureFormat: -3
|
||||||
|
maxTextureSize: 1024
|
||||||
|
textureSettings:
|
||||||
|
filterMode: -1
|
||||||
|
aniso: 1
|
||||||
|
mipBias: -1
|
||||||
|
wrapMode: 1
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 0
|
||||||
|
spriteExtrude: 1
|
||||||
|
spriteMeshType: 1
|
||||||
|
alignment: 0
|
||||||
|
spritePivot: {x: .5, y: .5}
|
||||||
|
spritePixelsToUnits: 100
|
||||||
|
alphaIsTransparency: 1
|
||||||
|
textureType: 2
|
||||||
|
buildTargetSettings: []
|
||||||
|
spriteSheet:
|
||||||
|
sprites: []
|
||||||
|
spritePackingTag:
|
||||||
|
userData:
|
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,38 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8425b95fef16c3f4194e3a20e245cb67
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
TextureImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 0
|
||||||
|
linearTexture: 1
|
||||||
|
correctGamma: 0
|
||||||
|
fadeOut: 0
|
||||||
|
borderMipMap: 0
|
||||||
|
mipMapFadeDistanceStart: 1
|
||||||
|
mipMapFadeDistanceEnd: 3
|
||||||
|
bumpmap:
|
||||||
|
convertToNormalMap: 0
|
||||||
|
externalNormalMap: 0
|
||||||
|
heightScale: .25
|
||||||
|
normalMapFilter: 0
|
||||||
|
isReadable: 0
|
||||||
|
grayScaleToAlpha: 0
|
||||||
|
generateCubemap: 0
|
||||||
|
textureFormat: -1
|
||||||
|
maxTextureSize: 128
|
||||||
|
textureSettings:
|
||||||
|
filterMode: 0
|
||||||
|
aniso: 1
|
||||||
|
mipBias: -1
|
||||||
|
wrapMode: 1
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
textureType: 2
|
||||||
|
buildTargetSettings: []
|
Binary file not shown.
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d93cfd57d52f80543b09a3f2ce724f84
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f226ce9e7a33bc24c95f34dd9f583516
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a361dd6a4364f8b4bb4c06094157cf4e
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1505887640
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c7c13513be7d41d4b9836c4db2ccaa0c
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1505887640
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,177 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="CustomTypes.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
//
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#pragma warning disable 1587
|
||||||
|
/// \file
|
||||||
|
/// <summary>Sets up support for Unity-specific types. Can be a blueprint how to register your own Custom Types for sending.</summary>
|
||||||
|
#pragma warning restore 1587
|
||||||
|
|
||||||
|
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internally used class, containing de/serialization methods for various Unity-specific classes.
|
||||||
|
/// Adding those to the Photon serialization protocol allows you to send them in events, etc.
|
||||||
|
/// </summary>
|
||||||
|
internal static class CustomTypes
|
||||||
|
{
|
||||||
|
/// <summary>Register</summary>
|
||||||
|
internal static void Register()
|
||||||
|
{
|
||||||
|
PhotonPeer.RegisterType(typeof(Vector2), (byte)'W', SerializeVector2, DeserializeVector2);
|
||||||
|
PhotonPeer.RegisterType(typeof(Vector3), (byte)'V', SerializeVector3, DeserializeVector3);
|
||||||
|
PhotonPeer.RegisterType(typeof(Quaternion), (byte)'Q', SerializeQuaternion, DeserializeQuaternion);
|
||||||
|
PhotonPeer.RegisterType(typeof(PhotonPlayer), (byte)'P', SerializePhotonPlayer, DeserializePhotonPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Custom De/Serializer Methods
|
||||||
|
|
||||||
|
|
||||||
|
public static readonly byte[] memVector3 = new byte[3 * 4];
|
||||||
|
private static short SerializeVector3(StreamBuffer outStream, object customobject)
|
||||||
|
{
|
||||||
|
Vector3 vo = (Vector3)customobject;
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
lock (memVector3)
|
||||||
|
{
|
||||||
|
byte[] bytes = memVector3;
|
||||||
|
Protocol.Serialize(vo.x, bytes, ref index);
|
||||||
|
Protocol.Serialize(vo.y, bytes, ref index);
|
||||||
|
Protocol.Serialize(vo.z, bytes, ref index);
|
||||||
|
outStream.Write(bytes, 0, 3 * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 3 * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object DeserializeVector3(StreamBuffer inStream, short length)
|
||||||
|
{
|
||||||
|
Vector3 vo = new Vector3();
|
||||||
|
lock (memVector3)
|
||||||
|
{
|
||||||
|
inStream.Read(memVector3, 0, 3 * 4);
|
||||||
|
int index = 0;
|
||||||
|
Protocol.Deserialize(out vo.x, memVector3, ref index);
|
||||||
|
Protocol.Deserialize(out vo.y, memVector3, ref index);
|
||||||
|
Protocol.Deserialize(out vo.z, memVector3, ref index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static readonly byte[] memVector2 = new byte[2 * 4];
|
||||||
|
private static short SerializeVector2(StreamBuffer outStream, object customobject)
|
||||||
|
{
|
||||||
|
Vector2 vo = (Vector2)customobject;
|
||||||
|
lock (memVector2)
|
||||||
|
{
|
||||||
|
byte[] bytes = memVector2;
|
||||||
|
int index = 0;
|
||||||
|
Protocol.Serialize(vo.x, bytes, ref index);
|
||||||
|
Protocol.Serialize(vo.y, bytes, ref index);
|
||||||
|
outStream.Write(bytes, 0, 2 * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2 * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object DeserializeVector2(StreamBuffer inStream, short length)
|
||||||
|
{
|
||||||
|
Vector2 vo = new Vector2();
|
||||||
|
lock (memVector2)
|
||||||
|
{
|
||||||
|
inStream.Read(memVector2, 0, 2 * 4);
|
||||||
|
int index = 0;
|
||||||
|
Protocol.Deserialize(out vo.x, memVector2, ref index);
|
||||||
|
Protocol.Deserialize(out vo.y, memVector2, ref index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static readonly byte[] memQuarternion = new byte[4 * 4];
|
||||||
|
private static short SerializeQuaternion(StreamBuffer outStream, object customobject)
|
||||||
|
{
|
||||||
|
Quaternion o = (Quaternion)customobject;
|
||||||
|
|
||||||
|
lock (memQuarternion)
|
||||||
|
{
|
||||||
|
byte[] bytes = memQuarternion;
|
||||||
|
int index = 0;
|
||||||
|
Protocol.Serialize(o.w, bytes, ref index);
|
||||||
|
Protocol.Serialize(o.x, bytes, ref index);
|
||||||
|
Protocol.Serialize(o.y, bytes, ref index);
|
||||||
|
Protocol.Serialize(o.z, bytes, ref index);
|
||||||
|
outStream.Write(bytes, 0, 4 * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 4 * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object DeserializeQuaternion(StreamBuffer inStream, short length)
|
||||||
|
{
|
||||||
|
Quaternion o = new Quaternion();
|
||||||
|
|
||||||
|
lock (memQuarternion)
|
||||||
|
{
|
||||||
|
inStream.Read(memQuarternion, 0, 4 * 4);
|
||||||
|
int index = 0;
|
||||||
|
Protocol.Deserialize(out o.w, memQuarternion, ref index);
|
||||||
|
Protocol.Deserialize(out o.x, memQuarternion, ref index);
|
||||||
|
Protocol.Deserialize(out o.y, memQuarternion, ref index);
|
||||||
|
Protocol.Deserialize(out o.z, memQuarternion, ref index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly byte[] memPlayer = new byte[4];
|
||||||
|
private static short SerializePhotonPlayer(StreamBuffer outStream, object customobject)
|
||||||
|
{
|
||||||
|
int ID = ((PhotonPlayer)customobject).ID;
|
||||||
|
|
||||||
|
lock (memPlayer)
|
||||||
|
{
|
||||||
|
byte[] bytes = memPlayer;
|
||||||
|
int off = 0;
|
||||||
|
Protocol.Serialize(ID, bytes, ref off);
|
||||||
|
outStream.Write(bytes, 0, 4);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object DeserializePhotonPlayer(StreamBuffer inStream, short length)
|
||||||
|
{
|
||||||
|
int ID;
|
||||||
|
lock (memPlayer)
|
||||||
|
{
|
||||||
|
inStream.Read(memPlayer, 0, length);
|
||||||
|
int off = 0;
|
||||||
|
Protocol.Deserialize(out ID, memPlayer, ref off);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PhotonNetwork.networkingPeer.mActors.ContainsKey(ID))
|
||||||
|
{
|
||||||
|
return PhotonNetwork.networkingPeer.mActors[ID];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ab517bd36a2b2504b83979fcad45d4a2
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,591 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="Enums.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
//
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#pragma warning disable 1587
|
||||||
|
/// \file
|
||||||
|
/// <summary>Wraps up several of the commonly used enumerations. </summary>
|
||||||
|
#pragma warning restore 1587
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This enum defines the set of MonoMessages Photon Unity Networking is using as callbacks. Implemented by PunBehaviour.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Much like "Update()" in Unity, PUN will call methods in specific situations.
|
||||||
|
/// Often, these methods are triggered when network operations complete (example: when joining a room).
|
||||||
|
///
|
||||||
|
/// All those methods are defined and described in this enum and implemented by PunBehaviour
|
||||||
|
/// (which makes it easy to implement them as override).
|
||||||
|
///
|
||||||
|
/// Each entry is the name of such a method and the description tells you when it gets used by PUN.
|
||||||
|
///
|
||||||
|
/// Make sure to read the remarks per entry as some methods have optional parameters.
|
||||||
|
/// </remarks>
|
||||||
|
/// \ingroup publicApi
|
||||||
|
public enum PhotonNetworkingMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the initial connection got established but before you can use the server. OnJoinedLobby() or OnConnectedToMaster() are called when PUN is ready.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This callback is only useful to detect if the server can be reached at all (technically).
|
||||||
|
/// Most often, it's enough to implement OnFailedToConnectToPhoton() and OnDisconnectedFromPhoton().
|
||||||
|
///
|
||||||
|
/// <i>OnJoinedLobby() or OnConnectedToMaster() are called when PUN is ready.</i>
|
||||||
|
///
|
||||||
|
/// When this is called, the low level connection is established and PUN will send your AppId, the user, etc in the background.
|
||||||
|
/// This is not called for transitions from the masterserver to game servers.
|
||||||
|
///
|
||||||
|
/// Example: void OnConnectedToPhoton() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnConnectedToPhoton,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the local user/client left a room.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When leaving a room, PUN brings you back to the Master Server.
|
||||||
|
/// Before you can use lobbies and join or create rooms, OnJoinedLobby() or OnConnectedToMaster() will get called again.
|
||||||
|
///
|
||||||
|
/// Example: void OnLeftRoom() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnLeftRoom,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after switching to a new MasterClient when the current one leaves.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is not called when this client enters a room.
|
||||||
|
/// The former MasterClient is still in the player list when this method get called.
|
||||||
|
///
|
||||||
|
/// Example: void OnMasterClientSwitched(PhotonPlayer newMasterClient) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnMasterClientSwitched,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a CreateRoom() call failed. Optional parameters provide ErrorCode and message.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Most likely because the room name is already in use (some other client was faster than you).
|
||||||
|
/// PUN logs some info if the PhotonNetwork.logLevel is >= PhotonLogLevel.Informational.
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonCreateRoomFailed() { ... }
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonCreateRoomFailed(object[] codeAndMsg) { // codeAndMsg[0] is short ErrorCode. codeAndMsg[1] is string debug msg. }
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonCreateRoomFailed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a JoinRoom() call failed. Optional parameters provide ErrorCode and message.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Most likely error is that the room does not exist or the room is full (some other client was faster than you).
|
||||||
|
/// PUN logs some info if the PhotonNetwork.logLevel is >= PhotonLogLevel.Informational.
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonJoinRoomFailed() { ... }
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonJoinRoomFailed(object[] codeAndMsg) { // codeAndMsg[0] is short ErrorCode. codeAndMsg[1] is string debug msg. }
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonJoinRoomFailed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when this client created a room and entered it. OnJoinedRoom() will be called as well.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This callback is only called on the client which created a room (see PhotonNetwork.CreateRoom).
|
||||||
|
///
|
||||||
|
/// As any client might close (or drop connection) anytime, there is a chance that the
|
||||||
|
/// creator of a room does not execute OnCreatedRoom.
|
||||||
|
///
|
||||||
|
/// If you need specific room properties or a "start signal", it is safer to implement
|
||||||
|
/// OnMasterClientSwitched() and to make the new MasterClient check the room's state.
|
||||||
|
///
|
||||||
|
/// Example: void OnCreatedRoom() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnCreatedRoom,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called on entering a lobby on the Master Server. The actual room-list updates will call OnReceivedRoomListUpdate().
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Note: When PhotonNetwork.autoJoinLobby is false, OnConnectedToMaster() will be called and the room list won't become available.
|
||||||
|
///
|
||||||
|
/// While in the lobby, the roomlist is automatically updated in fixed intervals (which you can't modify).
|
||||||
|
/// The room list gets available when OnReceivedRoomListUpdate() gets called after OnJoinedLobby().
|
||||||
|
///
|
||||||
|
/// Example: void OnJoinedLobby() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnJoinedLobby,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after leaving a lobby.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When you leave a lobby, [CreateRoom](@ref PhotonNetwork.CreateRoom) and [JoinRandomRoom](@ref PhotonNetwork.JoinRandomRoom)
|
||||||
|
/// automatically refer to the default lobby.
|
||||||
|
///
|
||||||
|
/// Example: void OnLeftLobby() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnLeftLobby,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after disconnecting from the Photon server.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// In some cases, other callbacks are called before OnDisconnectedFromPhoton is called.
|
||||||
|
/// Examples: OnConnectionFail() and OnFailedToConnectToPhoton().
|
||||||
|
///
|
||||||
|
/// Example: void OnDisconnectedFromPhoton() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnDisconnectedFromPhoton,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when something causes the connection to fail (after it was established), followed by a call to OnDisconnectedFromPhoton().
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If the server could not be reached in the first place, OnFailedToConnectToPhoton is called instead.
|
||||||
|
/// The reason for the error is provided as StatusCode.
|
||||||
|
///
|
||||||
|
/// Example: void OnConnectionFail(DisconnectCause cause) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnConnectionFail,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called if a connect call to the Photon server failed before the connection was established, followed by a call to OnDisconnectedFromPhoton().
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// OnConnectionFail only gets called when a connection to a Photon server was established in the first place.
|
||||||
|
///
|
||||||
|
/// Example: void OnFailedToConnectToPhoton(DisconnectCause cause) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnFailedToConnectToPhoton,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called for any update of the room-listing while in a lobby (PhotonNetwork.insideLobby) on the Master Server.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// PUN provides the list of rooms by PhotonNetwork.GetRoomList().<br/>
|
||||||
|
/// Each item is a RoomInfo which might include custom properties (provided you defined those as lobby-listed when creating a room).
|
||||||
|
///
|
||||||
|
/// Not all types of lobbies provide a listing of rooms to the client. Some are silent and specialized for server-side matchmaking.
|
||||||
|
///
|
||||||
|
/// Example: void OnReceivedRoomListUpdate() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnReceivedRoomListUpdate,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when entering a room (by creating or joining it). Called on all clients (including the Master Client).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method is commonly used to instantiate player characters.
|
||||||
|
/// If a match has to be started "actively", you can instead call an [PunRPC](@ref PhotonView.RPC) triggered by a user's button-press or a timer.
|
||||||
|
///
|
||||||
|
/// When this is called, you can usually already access the existing players in the room via PhotonNetwork.playerList.
|
||||||
|
/// Also, all custom properties should be already available as Room.customProperties. Check Room.playerCount to find out if
|
||||||
|
/// enough players are in the room to start playing.
|
||||||
|
///
|
||||||
|
/// Example: void OnJoinedRoom() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnJoinedRoom,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a remote player entered the room. This PhotonPlayer is already added to the playerlist at this time.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If your game starts with a certain number of players, this callback can be useful to check the
|
||||||
|
/// Room.playerCount and find out if you can start.
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonPlayerConnected(PhotonPlayer newPlayer) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonPlayerConnected,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a remote player left the room. This PhotonPlayer is already removed from the playerlist at this time.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When your client calls PhotonNetwork.leaveRoom, PUN will call this method on the remaining clients.
|
||||||
|
/// When a remote client drops connection or gets closed, this callback gets executed. after a timeout
|
||||||
|
/// of several seconds.
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonPlayerDisconnected(PhotonPlayer otherPlayer) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonPlayerDisconnected,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after a JoinRandom() call failed. Optional parameters provide ErrorCode and message.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Most likely all rooms are full or no rooms are available.
|
||||||
|
/// When using multiple lobbies (via JoinLobby or TypedLobby), another lobby might have more/fitting rooms.
|
||||||
|
/// PUN logs some info if the PhotonNetwork.logLevel is >= PhotonLogLevel.Informational.
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonRandomJoinFailed() { ... }
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonRandomJoinFailed(object[] codeAndMsg) { // codeAndMsg[0] is short ErrorCode. codeAndMsg[1] is string debug msg. }
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonRandomJoinFailed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after the connection to the master is established and authenticated but only when PhotonNetwork.autoJoinLobby is false.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If you set PhotonNetwork.autoJoinLobby to true, OnJoinedLobby() will be called instead of this.
|
||||||
|
///
|
||||||
|
/// You can join rooms and create them even without being in a lobby. The default lobby is used in that case.
|
||||||
|
/// The list of available rooms won't become available unless you join a lobby via PhotonNetwork.joinLobby.
|
||||||
|
///
|
||||||
|
/// Example: void OnConnectedToMaster() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnConnectedToMaster,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implement to customize the data a PhotonView regularly synchronizes. Called every 'network-update' when observed by PhotonView.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method will be called in scripts that are assigned as Observed component of a PhotonView.
|
||||||
|
/// PhotonNetwork.sendRateOnSerialize affects how often this method is called.
|
||||||
|
/// PhotonNetwork.sendRate affects how often packages are sent by this client.
|
||||||
|
///
|
||||||
|
/// Implementing this method, you can customize which data a PhotonView regularly synchronizes.
|
||||||
|
/// Your code defines what is being sent (content) and how your data is used by receiving clients.
|
||||||
|
///
|
||||||
|
/// Unlike other callbacks, <i>OnPhotonSerializeView only gets called when it is assigned
|
||||||
|
/// to a PhotonView</i> as PhotonView.observed script.
|
||||||
|
///
|
||||||
|
/// To make use of this method, the PhotonStream is essential. It will be in "writing" mode" on the
|
||||||
|
/// client that controls a PhotonView (PhotonStream.isWriting == true) and in "reading mode" on the
|
||||||
|
/// remote clients that just receive that the controlling client sends.
|
||||||
|
///
|
||||||
|
/// If you skip writing any value into the stream, PUN will skip the update. Used carefully, this can
|
||||||
|
/// conserve bandwidth and messages (which have a limit per room/second).
|
||||||
|
///
|
||||||
|
/// Note that OnPhotonSerializeView is not called on remote clients when the sender does not send
|
||||||
|
/// any update. This can't be used as "x-times per second Update()".
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonSerializeView,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called on all scripts on a GameObject (and children) that have been Instantiated using PhotonNetwork.Instantiate.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// PhotonMessageInfo parameter provides info about who created the object and when (based off PhotonNetworking.time).
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonInstantiate(PhotonMessageInfo info) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonInstantiate,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Because the concurrent user limit was (temporarily) reached, this client is rejected by the server and disconnecting.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When this happens, the user might try again later. You can't create or join rooms in OnPhotonMaxCcuReached(), cause the client will be disconnecting.
|
||||||
|
/// You can raise the CCU limits with a new license (when you host yourself) or extended subscription (when using the Photon Cloud).
|
||||||
|
/// The Photon Cloud will mail you when the CCU limit was reached. This is also visible in the Dashboard (webpage).
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonMaxCccuReached() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonMaxCccuReached,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a room's custom properties changed. The propertiesThatChanged contains all that was set via Room.SetCustomProperties.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Since v1.25 this method has one parameter: Hashtable propertiesThatChanged.
|
||||||
|
/// Changing properties must be done by Room.SetCustomProperties, which causes this callback locally, too.
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonCustomRoomPropertiesChanged(Hashtable propertiesThatChanged) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonCustomRoomPropertiesChanged,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when custom player-properties are changed. Player and the changed properties are passed as object[].
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Since v1.25 this method has one parameter: object[] playerAndUpdatedProps, which contains two entries.<br/>
|
||||||
|
/// [0] is the affected PhotonPlayer.<br/>
|
||||||
|
/// [1] is the Hashtable of properties that changed.<br/>
|
||||||
|
///
|
||||||
|
/// We are using a object[] due to limitations of Unity's GameObject.SendMessage (which has only one optional parameter).
|
||||||
|
///
|
||||||
|
/// Changing properties must be done by PhotonPlayer.SetCustomProperties, which causes this callback locally, too.
|
||||||
|
///
|
||||||
|
/// Example:<pre>
|
||||||
|
/// void OnPhotonPlayerPropertiesChanged(object[] playerAndUpdatedProps) {
|
||||||
|
/// PhotonPlayer player = playerAndUpdatedProps[0] as PhotonPlayer;
|
||||||
|
/// Hashtable props = playerAndUpdatedProps[1] as Hashtable;
|
||||||
|
/// //...
|
||||||
|
/// }</pre>
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonPlayerPropertiesChanged,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the server sent the response to a FindFriends request and updated PhotonNetwork.Friends.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The friends list is available as PhotonNetwork.Friends, listing name, online state and
|
||||||
|
/// the room a user is in (if any).
|
||||||
|
///
|
||||||
|
/// Example: void OnUpdatedFriendList() { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnUpdatedFriendList,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the custom authentication failed. Followed by disconnect!
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Custom Authentication can fail due to user-input, bad tokens/secrets.
|
||||||
|
/// If authentication is successful, this method is not called. Implement OnJoinedLobby() or OnConnectedToMaster() (as usual).
|
||||||
|
///
|
||||||
|
/// During development of a game, it might also fail due to wrong configuration on the server side.
|
||||||
|
/// In those cases, logging the debugMessage is very important.
|
||||||
|
///
|
||||||
|
/// Unless you setup a custom authentication service for your app (in the [Dashboard](https://www.photonengine.com/dashboard)),
|
||||||
|
/// this won't be called!
|
||||||
|
///
|
||||||
|
/// Example: void OnCustomAuthenticationFailed(string debugMessage) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnCustomAuthenticationFailed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when your Custom Authentication service responds with additional data.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Custom Authentication services can include some custom data in their response.
|
||||||
|
/// When present, that data is made available in this callback as Dictionary.
|
||||||
|
/// While the keys of your data have to be strings, the values can be either string or a number (in Json).
|
||||||
|
/// You need to make extra sure, that the value type is the one you expect. Numbers become (currently) int64.
|
||||||
|
///
|
||||||
|
/// Example: void OnCustomAuthenticationResponse(Dictionary<string, object> data) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
/// <see cref="https://doc.photonengine.com/en/realtime/current/reference/custom-authentication"/>
|
||||||
|
OnCustomAuthenticationResponse,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called by PUN when the response to a WebRPC is available. See PhotonNetwork.WebRPC.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Important: The response.ReturnCode is 0 if Photon was able to reach your web-service.
|
||||||
|
/// The content of the response is what your web-service sent. You can create a WebResponse instance from it.
|
||||||
|
/// Example: WebRpcResponse webResponse = new WebRpcResponse(operationResponse);
|
||||||
|
///
|
||||||
|
/// Please note: Class OperationResponse is in a namespace which needs to be "used":
|
||||||
|
/// using ExitGames.Client.Photon; // includes OperationResponse (and other classes)
|
||||||
|
///
|
||||||
|
/// The OperationResponse.ReturnCode by Photon is:
|
||||||
|
/// 0 for "OK"
|
||||||
|
/// -3 for "Web-Service not configured" (see Dashboard / WebHooks)
|
||||||
|
/// -5 for "Web-Service does now have RPC path/name" (at least for Azure)
|
||||||
|
///
|
||||||
|
/// Example: void OnWebRpcResponse(OperationResponse response) { ... }
|
||||||
|
/// </remarks>
|
||||||
|
OnWebRpcResponse,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when another player requests ownership of a PhotonView from you (the current owner).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The parameter viewAndPlayer contains:
|
||||||
|
///
|
||||||
|
/// PhotonView view = viewAndPlayer[0] as PhotonView;
|
||||||
|
///
|
||||||
|
/// PhotonPlayer requestingPlayer = viewAndPlayer[1] as PhotonPlayer;
|
||||||
|
/// </remarks>
|
||||||
|
/// <example>void OnOwnershipRequest(object[] viewAndPlayer) {} //</example>
|
||||||
|
OnOwnershipRequest,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the Master Server sent an update for the Lobby Statistics, updating PhotonNetwork.LobbyStatistics.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This callback has two preconditions:
|
||||||
|
/// EnableLobbyStatistics must be set to true, before this client connects.
|
||||||
|
/// And the client has to be connected to the Master Server, which is providing the info about lobbies.
|
||||||
|
/// </remarks>
|
||||||
|
OnLobbyStatisticsUpdate,
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a remote Photon Player activity changed. This will be called ONLY is PlayerTtl is greater then 0.
|
||||||
|
///
|
||||||
|
/// Use PhotonPlayer.IsInactive to check the current activity state
|
||||||
|
///
|
||||||
|
/// Example: void OnPhotonPlayerActivityChanged(PhotonPlayer otherPlayer) {...}
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This callback has precondition:
|
||||||
|
/// PlayerTtl must be greater then 0
|
||||||
|
/// </remarks>
|
||||||
|
OnPhotonPlayerActivityChanged,
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a PhotonView Owner is transfered to a Player.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The parameter viewAndPlayers contains:
|
||||||
|
///
|
||||||
|
/// PhotonView view = viewAndPlayers[0] as PhotonView;
|
||||||
|
///
|
||||||
|
/// PhotonPlayer newOwner = viewAndPlayers[1] as PhotonPlayer;
|
||||||
|
///
|
||||||
|
/// PhotonPlayer oldOwner = viewAndPlayers[2] as PhotonPlayer;
|
||||||
|
/// </remarks>
|
||||||
|
/// <example>void OnOwnershipTransfered(object[] viewAndPlayers) {} //</example>
|
||||||
|
OnOwnershipTransfered,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Used to define the level of logging output created by the PUN classes. Either log errors, info (some more) or full.</summary>
|
||||||
|
/// \ingroup publicApi
|
||||||
|
public enum PhotonLogLevel
|
||||||
|
{
|
||||||
|
/// <summary>Show only errors. Minimal output. Note: Some might be "runtime errors" which you have to expect.</summary>
|
||||||
|
ErrorsOnly,
|
||||||
|
/// <summary>Logs some of the workflow, calls and results.</summary>
|
||||||
|
Informational,
|
||||||
|
/// <summary>Every available log call gets into the console/log. Only use for debugging.</summary>
|
||||||
|
Full
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Enum of "target" options for RPCs. These define which remote clients get your RPC call. </summary>
|
||||||
|
/// \ingroup publicApi
|
||||||
|
public enum PhotonTargets
|
||||||
|
{
|
||||||
|
/// <summary>Sends the RPC to everyone else and executes it immediately on this client. Player who join later will not execute this RPC.</summary>
|
||||||
|
All,
|
||||||
|
/// <summary>Sends the RPC to everyone else. This client does not execute the RPC. Player who join later will not execute this RPC.</summary>
|
||||||
|
Others,
|
||||||
|
/// <summary>Sends the RPC to MasterClient only. Careful: The MasterClient might disconnect before it executes the RPC and that might cause dropped RPCs.</summary>
|
||||||
|
MasterClient,
|
||||||
|
/// <summary>Sends the RPC to everyone else and executes it immediately on this client. New players get the RPC when they join as it's buffered (until this client leaves).</summary>
|
||||||
|
AllBuffered,
|
||||||
|
/// <summary>Sends the RPC to everyone. This client does not execute the RPC. New players get the RPC when they join as it's buffered (until this client leaves).</summary>
|
||||||
|
OthersBuffered,
|
||||||
|
/// <summary>Sends the RPC to everyone (including this client) through the server.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This client executes the RPC like any other when it received it from the server.
|
||||||
|
/// Benefit: The server's order of sending the RPCs is the same on all clients.
|
||||||
|
/// </remarks>
|
||||||
|
AllViaServer,
|
||||||
|
/// <summary>Sends the RPC to everyone (including this client) through the server and buffers it for players joining later.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This client executes the RPC like any other when it received it from the server.
|
||||||
|
/// Benefit: The server's order of sending the RPCs is the same on all clients.
|
||||||
|
/// </remarks>
|
||||||
|
AllBufferedViaServer
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Currently available <a href="http://doc.photonengine.com/en/pun/current/reference/regions">Photon Cloud regions</a> as enum.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is used in PhotonNetwork.ConnectToRegion.
|
||||||
|
/// </remarks>
|
||||||
|
public enum CloudRegionCode
|
||||||
|
{
|
||||||
|
/// <summary>European servers in Amsterdam.</summary>
|
||||||
|
eu = 0,
|
||||||
|
/// <summary>US servers (East Coast).</summary>
|
||||||
|
us = 1,
|
||||||
|
/// <summary>Asian servers in Singapore.</summary>
|
||||||
|
asia = 2,
|
||||||
|
/// <summary>Japanese servers in Tokyo.</summary>
|
||||||
|
jp = 3,
|
||||||
|
/// <summary>Australian servers in Melbourne.</summary>
|
||||||
|
au = 5,
|
||||||
|
///<summary>USA West, San José, usw</summary>
|
||||||
|
usw = 6,
|
||||||
|
///<summary>South America, Sao Paulo, sa</summary>
|
||||||
|
sa = 7,
|
||||||
|
///<summary>Canada East, Montreal, cae</summary>
|
||||||
|
cae = 8,
|
||||||
|
///<summary>South Korea, Seoul, kr</summary>
|
||||||
|
kr = 9,
|
||||||
|
///<summary>India, Chennai, in</summary>
|
||||||
|
@in = 10,
|
||||||
|
|
||||||
|
/// <summary>No region selected.</summary>
|
||||||
|
none = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Available regions as enum of flags. To be used as "enabled" flags for Best Region pinging.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Note that these enum values skip CloudRegionCode.none and their values are in strict order (power of 2).</remarks>
|
||||||
|
[Flags]
|
||||||
|
public enum CloudRegionFlag
|
||||||
|
{
|
||||||
|
eu = 1 << 0,
|
||||||
|
us = 1 << 1,
|
||||||
|
asia = 1 << 2,
|
||||||
|
jp = 1 << 3,
|
||||||
|
au = 1 << 4,
|
||||||
|
usw = 1 << 5,
|
||||||
|
sa = 1 << 6,
|
||||||
|
cae = 1 << 7,
|
||||||
|
kr = 1 << 8,
|
||||||
|
@in = 1 << 9,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// High level connection state of the client. Better use the more detailed <see cref="ClientState"/>.
|
||||||
|
/// </summary>
|
||||||
|
public enum ConnectionState
|
||||||
|
{
|
||||||
|
Disconnected,
|
||||||
|
Connecting,
|
||||||
|
Connected,
|
||||||
|
Disconnecting,
|
||||||
|
InitializingApplication
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines how the communication gets encrypted.
|
||||||
|
/// </summary>
|
||||||
|
public enum EncryptionMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is the default encryption mode: Messages get encrypted only on demand (when you send operations with the "encrypt" parameter set to true).
|
||||||
|
/// </summary>
|
||||||
|
PayloadEncryption,
|
||||||
|
/// <summary>
|
||||||
|
/// With this encryption mode for UDP, the connection gets setup and all further datagrams get encrypted almost entirely. On-demand message encryption (like in PayloadEncryption) is skipped.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This mode requires AuthOnce or AuthOnceWss as AuthMode!
|
||||||
|
/// </remarks>
|
||||||
|
DatagramEncryption = 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class EncryptionDataParameters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Key for encryption mode
|
||||||
|
/// </summary>
|
||||||
|
public const byte Mode = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// Key for first secret
|
||||||
|
/// </summary>
|
||||||
|
public const byte Secret1 = 1;
|
||||||
|
/// <summary>
|
||||||
|
/// Key for second secret
|
||||||
|
/// </summary>
|
||||||
|
public const byte Secret2 = 2;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b7962bbdaba2a4940b1341d755abd40d
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,246 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="Extensions.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// Provides some helpful methods and extensions for Hashtables, etc.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
using Hashtable = ExitGames.Client.Photon.Hashtable;
|
||||||
|
using SupportClassPun = ExitGames.Client.Photon.SupportClass;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This static class defines some useful extension methods for several existing classes (e.g. Vector3, float and others).
|
||||||
|
/// </summary>
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static Dictionary<MethodInfo, ParameterInfo[]> ParametersOfMethods = new Dictionary<MethodInfo, ParameterInfo[]>();
|
||||||
|
public static ParameterInfo[] GetCachedParemeters(this MethodInfo mo)
|
||||||
|
{
|
||||||
|
ParameterInfo[] result;
|
||||||
|
bool cached= ParametersOfMethods.TryGetValue(mo, out result);
|
||||||
|
|
||||||
|
if (!cached)
|
||||||
|
{
|
||||||
|
result = mo.GetParameters();
|
||||||
|
ParametersOfMethods[mo] = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PhotonView[] GetPhotonViewsInChildren(this UnityEngine.GameObject go)
|
||||||
|
{
|
||||||
|
return go.GetComponentsInChildren<PhotonView>(true) as PhotonView[];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PhotonView GetPhotonView(this UnityEngine.GameObject go)
|
||||||
|
{
|
||||||
|
return go.GetComponent<PhotonView>() as PhotonView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>compares the squared magnitude of target - second to given float value</summary>
|
||||||
|
public static bool AlmostEquals(this Vector3 target, Vector3 second, float sqrMagnitudePrecision)
|
||||||
|
{
|
||||||
|
return (target - second).sqrMagnitude < sqrMagnitudePrecision; // TODO: inline vector methods to optimize?
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>compares the squared magnitude of target - second to given float value</summary>
|
||||||
|
public static bool AlmostEquals(this Vector2 target, Vector2 second, float sqrMagnitudePrecision)
|
||||||
|
{
|
||||||
|
return (target - second).sqrMagnitude < sqrMagnitudePrecision; // TODO: inline vector methods to optimize?
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>compares the angle between target and second to given float value</summary>
|
||||||
|
public static bool AlmostEquals(this Quaternion target, Quaternion second, float maxAngle)
|
||||||
|
{
|
||||||
|
return Quaternion.Angle(target, second) < maxAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>compares two floats and returns true of their difference is less than floatDiff</summary>
|
||||||
|
public static bool AlmostEquals(this float target, float second, float floatDiff)
|
||||||
|
{
|
||||||
|
return Mathf.Abs(target - second) < floatDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Merges all keys from addHash into the target. Adds new keys and updates the values of existing keys in target.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The IDictionary to update.</param>
|
||||||
|
/// <param name="addHash">The IDictionary containing data to merge into target.</param>
|
||||||
|
public static void Merge(this IDictionary target, IDictionary addHash)
|
||||||
|
{
|
||||||
|
if (addHash == null || target.Equals(addHash))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (object key in addHash.Keys)
|
||||||
|
{
|
||||||
|
target[key] = addHash[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Merges keys of type string to target Hashtable.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Does not remove keys from target (so non-string keys CAN be in target if they were before).
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="target">The target IDicitionary passed in plus all string-typed keys from the addHash.</param>
|
||||||
|
/// <param name="addHash">A IDictionary that should be merged partly into target to update it.</param>
|
||||||
|
public static void MergeStringKeys(this IDictionary target, IDictionary addHash)
|
||||||
|
{
|
||||||
|
if (addHash == null || target.Equals(addHash))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (object key in addHash.Keys)
|
||||||
|
{
|
||||||
|
// only merge keys of type string
|
||||||
|
if (key is string)
|
||||||
|
{
|
||||||
|
target[key] = addHash[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Helper method for debugging of IDictionary content, inlcuding type-information. Using this is not performant.</summary>
|
||||||
|
/// <remarks>Should only be used for debugging as necessary.</remarks>
|
||||||
|
/// <param name="origin">Some Dictionary or Hashtable.</param>
|
||||||
|
/// <returns>String of the content of the IDictionary.</returns>
|
||||||
|
public static string ToStringFull(this IDictionary origin)
|
||||||
|
{
|
||||||
|
return SupportClassPun.DictionaryToString(origin, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Helper method for debugging of object[] content. Using this is not performant.</summary>
|
||||||
|
/// <remarks>Should only be used for debugging as necessary.</remarks>
|
||||||
|
/// <param name="data">Any object[].</param>
|
||||||
|
/// <returns>A comma-separated string containing each value's ToString().</returns>
|
||||||
|
public static string ToStringFull(this object[] data)
|
||||||
|
{
|
||||||
|
if (data == null) return "null";
|
||||||
|
|
||||||
|
string[] sb = new string[data.Length];
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
object o = data[i];
|
||||||
|
sb[i] = (o != null) ? o.ToString() : "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(", ", sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method copies all string-typed keys of the original into a new Hashtable.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Does not recurse (!) into hashes that might be values in the root-hash.
|
||||||
|
/// This does not modify the original.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="original">The original IDictonary to get string-typed keys from.</param>
|
||||||
|
/// <returns>New Hashtable containing only string-typed keys of the original.</returns>
|
||||||
|
public static Hashtable StripToStringKeys(this IDictionary original)
|
||||||
|
{
|
||||||
|
Hashtable target = new Hashtable();
|
||||||
|
if (original != null)
|
||||||
|
{
|
||||||
|
foreach (object key in original.Keys)
|
||||||
|
{
|
||||||
|
if (key is string)
|
||||||
|
{
|
||||||
|
target[key] = original[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This removes all key-value pairs that have a null-reference as value.
|
||||||
|
/// Photon properties are removed by setting their value to null.
|
||||||
|
/// Changes the original passed IDictionary!
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="original">The IDictionary to strip of keys with null-values.</param>
|
||||||
|
public static void StripKeysWithNullValues(this IDictionary original)
|
||||||
|
{
|
||||||
|
object[] keys = new object[original.Count];
|
||||||
|
//original.Keys.CopyTo(keys, 0); // todo: figure out which platform didn't support this
|
||||||
|
int i = 0;
|
||||||
|
foreach (object k in original.Keys)
|
||||||
|
{
|
||||||
|
keys[i++] = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < keys.Length; index++)
|
||||||
|
{
|
||||||
|
var key = keys[index];
|
||||||
|
if (original[key] == null)
|
||||||
|
{
|
||||||
|
original.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a particular integer value is in an int-array.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This might be useful to look up if a particular actorNumber is in the list of players of a room.</remarks>
|
||||||
|
/// <param name="target">The array of ints to check.</param>
|
||||||
|
/// <param name="nr">The number to lookup in target.</param>
|
||||||
|
/// <returns>True if nr was found in target.</returns>
|
||||||
|
public static bool Contains(this int[] target, int nr)
|
||||||
|
{
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < target.Length; index++)
|
||||||
|
{
|
||||||
|
if (target[index] == nr)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Small number of extension methods that make it easier for PUN to work cross-Unity-versions.</summary>
|
||||||
|
public static class GameObjectExtensions
|
||||||
|
{
|
||||||
|
/// <summary>Unity-version-independent replacement for active GO property.</summary>
|
||||||
|
/// <returns>Unity 3.5: active. Any newer Unity: activeInHierarchy.</returns>
|
||||||
|
public static bool GetActive(this GameObject target)
|
||||||
|
{
|
||||||
|
#if UNITY_3_5
|
||||||
|
return target.active;
|
||||||
|
#else
|
||||||
|
return target.activeInHierarchy;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_3_5
|
||||||
|
/// <summary>Unity-version-independent setter for active and SetActive().</summary>
|
||||||
|
public static void SetActive(this GameObject target, bool value)
|
||||||
|
{
|
||||||
|
target.active = value;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3c0464991e33a70498abdd85c150cc59
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,26 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="FriendInfo.cs" company="Exit Games GmbH">
|
||||||
|
// Loadbalancing Framework for Photon - Copyright (C) 2013 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// Collection of values related to a user / friend.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@photonengine.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to store info about a friend's online state and in which room he/she is.
|
||||||
|
/// </summary>
|
||||||
|
public class FriendInfo
|
||||||
|
{
|
||||||
|
public string Name { get; internal protected set; }
|
||||||
|
public bool IsOnline { get; internal protected set; }
|
||||||
|
public string Room { get; internal protected set; }
|
||||||
|
public bool IsInRoom { get { return IsOnline && !string.IsNullOrEmpty(this.Room); } }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("{0}\t is: {1}", this.Name, (!this.IsOnline) ? "offline" : this.IsInRoom ? "playing" : "on master");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 94ba1138c322ea04c8c37cfbcf87f468
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,36 @@
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ExitGames.Client.GUI
|
||||||
|
{
|
||||||
|
public enum GizmoType
|
||||||
|
{
|
||||||
|
WireSphere,
|
||||||
|
Sphere,
|
||||||
|
WireCube,
|
||||||
|
Cube,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GizmoTypeDrawer
|
||||||
|
{
|
||||||
|
public static void Draw( Vector3 center, GizmoType type, Color color, float size )
|
||||||
|
{
|
||||||
|
Gizmos.color = color;
|
||||||
|
|
||||||
|
switch( type )
|
||||||
|
{
|
||||||
|
case GizmoType.Cube:
|
||||||
|
Gizmos.DrawCube( center, Vector3.one * size );
|
||||||
|
break;
|
||||||
|
case GizmoType.Sphere:
|
||||||
|
Gizmos.DrawSphere( center, size * 0.5f );
|
||||||
|
break;
|
||||||
|
case GizmoType.WireCube:
|
||||||
|
Gizmos.DrawWireCube( center, Vector3.one * size );
|
||||||
|
break;
|
||||||
|
case GizmoType.WireSphere:
|
||||||
|
Gizmos.DrawWireSphere( center, size * 0.5f );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a744e8c91e32ce742b8f79e048a8714a
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 35c989013c977244186e524a4c90dcee
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6389c32085f1ef04f88e046b96ab6fc6
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f40f16a0227e5c14293e269c875c0f9b
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,330 @@
|
||||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||||
|
// <copyright file="PhotonHandler.cs" company="Exit Games GmbH">
|
||||||
|
// Part of: Photon Unity Networking
|
||||||
|
// </copyright>
|
||||||
|
// --------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if UNITY_5 && (!UNITY_5_0 && !UNITY_5_1 && !UNITY_5_2 && !UNITY_5_3) || UNITY_2017
|
||||||
|
#define UNITY_MIN_5_4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
using UnityEngine;
|
||||||
|
using Debug = UnityEngine.Debug;
|
||||||
|
using Hashtable = ExitGames.Client.Photon.Hashtable;
|
||||||
|
using SupportClassPun = ExitGames.Client.Photon.SupportClass;
|
||||||
|
|
||||||
|
#if UNITY_5_5_OR_NEWER
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal Monobehaviour that allows Photon to run an Update loop.
|
||||||
|
/// </summary>
|
||||||
|
internal class PhotonHandler : MonoBehaviour
|
||||||
|
{
|
||||||
|
public static PhotonHandler SP;
|
||||||
|
|
||||||
|
public int updateInterval; // time [ms] between consecutive SendOutgoingCommands calls
|
||||||
|
|
||||||
|
public int updateIntervalOnSerialize; // time [ms] between consecutive RunViewUpdate calls (sending syncs, etc)
|
||||||
|
|
||||||
|
private int nextSendTickCount = 0;
|
||||||
|
|
||||||
|
private int nextSendTickCountOnSerialize = 0;
|
||||||
|
|
||||||
|
private static bool sendThreadShouldRun;
|
||||||
|
|
||||||
|
private static Stopwatch timerToStopConnectionInBackground;
|
||||||
|
|
||||||
|
protected internal static bool AppQuits;
|
||||||
|
|
||||||
|
protected internal static Type PingImplementation = null;
|
||||||
|
|
||||||
|
protected void Awake()
|
||||||
|
{
|
||||||
|
if (SP != null && SP != this && SP.gameObject != null)
|
||||||
|
{
|
||||||
|
GameObject.DestroyImmediate(SP.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
SP = this;
|
||||||
|
DontDestroyOnLoad(this.gameObject);
|
||||||
|
|
||||||
|
this.updateInterval = 1000 / PhotonNetwork.sendRate;
|
||||||
|
this.updateIntervalOnSerialize = 1000 / PhotonNetwork.sendRateOnSerialize;
|
||||||
|
|
||||||
|
PhotonHandler.StartFallbackSendAckThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if UNITY_MIN_5_4
|
||||||
|
|
||||||
|
protected void Start()
|
||||||
|
{
|
||||||
|
UnityEngine.SceneManagement.SceneManager.sceneLoaded += (scene, loadingMode) =>
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.NewSceneLoaded();
|
||||||
|
PhotonNetwork.networkingPeer.SetLevelInPropsIfSynced(SceneManagerHelper.ActiveSceneName);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/// <summary>Called by Unity after a new level was loaded.</summary>
|
||||||
|
protected void OnLevelWasLoaded(int level)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.NewSceneLoaded();
|
||||||
|
PhotonNetwork.networkingPeer.SetLevelInPropsIfSynced(SceneManagerHelper.ActiveSceneName);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Called by Unity when the application is closed. Disconnects.</summary>
|
||||||
|
protected void OnApplicationQuit()
|
||||||
|
{
|
||||||
|
PhotonHandler.AppQuits = true;
|
||||||
|
PhotonHandler.StopFallbackSendAckThread();
|
||||||
|
PhotonNetwork.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called by Unity when the application gets paused (e.g. on Android when in background).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Sets a disconnect timer when PhotonNetwork.BackgroundTimeout > 0.1f. See PhotonNetwork.BackgroundTimeout.
|
||||||
|
///
|
||||||
|
/// Some versions of Unity will give false values for pause on Android (and possibly on other platforms).
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="pause">If the app pauses.</param>
|
||||||
|
protected void OnApplicationPause(bool pause)
|
||||||
|
{
|
||||||
|
if (PhotonNetwork.BackgroundTimeout > 0.1f)
|
||||||
|
{
|
||||||
|
if (timerToStopConnectionInBackground == null)
|
||||||
|
{
|
||||||
|
timerToStopConnectionInBackground = new Stopwatch();
|
||||||
|
}
|
||||||
|
timerToStopConnectionInBackground.Reset();
|
||||||
|
|
||||||
|
if (pause)
|
||||||
|
{
|
||||||
|
timerToStopConnectionInBackground.Start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timerToStopConnectionInBackground.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called by Unity when the play mode ends. Used to cleanup.</summary>
|
||||||
|
protected void OnDestroy()
|
||||||
|
{
|
||||||
|
//Debug.Log("OnDestroy on PhotonHandler.");
|
||||||
|
PhotonHandler.StopFallbackSendAckThread();
|
||||||
|
//PhotonNetwork.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Update()
|
||||||
|
{
|
||||||
|
if (PhotonNetwork.networkingPeer == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("NetworkPeer broke!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PhotonNetwork.connectionStateDetailed == ClientState.PeerCreated || PhotonNetwork.connectionStateDetailed == ClientState.Disconnected || PhotonNetwork.offlineMode)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the messageQueue might be paused. in that case a thread will send acknowledgements only. nothing else to do here.
|
||||||
|
if (!PhotonNetwork.isMessageQueueRunning)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doDispatch = true;
|
||||||
|
while (PhotonNetwork.isMessageQueueRunning && doDispatch)
|
||||||
|
{
|
||||||
|
// DispatchIncomingCommands() returns true of it found any command to dispatch (event, result or state change)
|
||||||
|
Profiler.BeginSample("DispatchIncomingCommands");
|
||||||
|
doDispatch = PhotonNetwork.networkingPeer.DispatchIncomingCommands();
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentMsSinceStart = (int)(Time.realtimeSinceStartup * 1000); // avoiding Environment.TickCount, which could be negative on long-running platforms
|
||||||
|
if (PhotonNetwork.isMessageQueueRunning && currentMsSinceStart > this.nextSendTickCountOnSerialize)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.RunViewUpdate();
|
||||||
|
this.nextSendTickCountOnSerialize = currentMsSinceStart + this.updateIntervalOnSerialize;
|
||||||
|
this.nextSendTickCount = 0; // immediately send when synchronization code was running
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMsSinceStart = (int)(Time.realtimeSinceStartup * 1000);
|
||||||
|
if (currentMsSinceStart > this.nextSendTickCount)
|
||||||
|
{
|
||||||
|
bool doSend = true;
|
||||||
|
while (PhotonNetwork.isMessageQueueRunning && doSend)
|
||||||
|
{
|
||||||
|
// Send all outgoing commands
|
||||||
|
Profiler.BeginSample("SendOutgoingCommands");
|
||||||
|
doSend = PhotonNetwork.networkingPeer.SendOutgoingCommands();
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nextSendTickCount = currentMsSinceStart + this.updateInterval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnJoinedRoom()
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.LoadLevelIfSynced();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnCreatedRoom()
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.SetLevelInPropsIfSynced(SceneManagerHelper.ActiveSceneName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StartFallbackSendAckThread()
|
||||||
|
{
|
||||||
|
#if !UNITY_WEBGL
|
||||||
|
if (sendThreadShouldRun)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendThreadShouldRun = true;
|
||||||
|
SupportClassPun.StartBackgroundCalls(FallbackSendAckThread); // thread will call this every 100ms until method returns false
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StopFallbackSendAckThread()
|
||||||
|
{
|
||||||
|
#if !UNITY_WEBGL
|
||||||
|
sendThreadShouldRun = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>A thread which runs independent from the Update() calls. Keeps connections online while loading or in background. See PhotonNetwork.BackgroundTimeout.</summary>
|
||||||
|
public static bool FallbackSendAckThread()
|
||||||
|
{
|
||||||
|
if (sendThreadShouldRun && !PhotonNetwork.offlineMode && PhotonNetwork.networkingPeer != null)
|
||||||
|
{
|
||||||
|
// check if the client should disconnect after some seconds in background
|
||||||
|
if (timerToStopConnectionInBackground != null && PhotonNetwork.BackgroundTimeout > 0.1f)
|
||||||
|
{
|
||||||
|
if (timerToStopConnectionInBackground.ElapsedMilliseconds > PhotonNetwork.BackgroundTimeout * 1000)
|
||||||
|
{
|
||||||
|
if (PhotonNetwork.connected)
|
||||||
|
{
|
||||||
|
PhotonNetwork.Disconnect();
|
||||||
|
}
|
||||||
|
timerToStopConnectionInBackground.Stop();
|
||||||
|
timerToStopConnectionInBackground.Reset();
|
||||||
|
return sendThreadShouldRun;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PhotonNetwork.isMessageQueueRunning || PhotonNetwork.networkingPeer.ConnectionTime - PhotonNetwork.networkingPeer.LastSendOutgoingTime > 200)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.SendAcksOnly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendThreadShouldRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Photon Cloud Ping Evaluation
|
||||||
|
|
||||||
|
|
||||||
|
private const string PlayerPrefsKey = "PUNCloudBestRegion";
|
||||||
|
|
||||||
|
internal static CloudRegionCode BestRegionCodeInPreferences
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string prefsRegionCode = PlayerPrefs.GetString(PlayerPrefsKey, "");
|
||||||
|
if (!string.IsNullOrEmpty(prefsRegionCode))
|
||||||
|
{
|
||||||
|
CloudRegionCode loadedRegion = Region.Parse(prefsRegionCode);
|
||||||
|
return loadedRegion;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CloudRegionCode.none;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == CloudRegionCode.none)
|
||||||
|
{
|
||||||
|
PlayerPrefs.DeleteKey(PlayerPrefsKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PlayerPrefs.SetString(PlayerPrefsKey, value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal protected static void PingAvailableRegionsAndConnectToBest()
|
||||||
|
{
|
||||||
|
SP.StartCoroutine(SP.PingAvailableRegionsCoroutine(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal IEnumerator PingAvailableRegionsCoroutine(bool connectToBest)
|
||||||
|
{
|
||||||
|
while (PhotonNetwork.networkingPeer.AvailableRegions == null)
|
||||||
|
{
|
||||||
|
if (PhotonNetwork.connectionStateDetailed != ClientState.ConnectingToNameServer && PhotonNetwork.connectionStateDetailed != ClientState.ConnectedToNameServer)
|
||||||
|
{
|
||||||
|
Debug.LogError("Call ConnectToNameServer to ping available regions.");
|
||||||
|
yield break; // break if we don't connect to the nameserver at all
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("Waiting for AvailableRegions. State: " + PhotonNetwork.connectionStateDetailed + " Server: " + PhotonNetwork.Server + " PhotonNetwork.networkingPeer.AvailableRegions " + (PhotonNetwork.networkingPeer.AvailableRegions != null));
|
||||||
|
yield return new WaitForSeconds(0.25f); // wait until pinging finished (offline mode won't ping)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PhotonNetwork.networkingPeer.AvailableRegions == null || PhotonNetwork.networkingPeer.AvailableRegions.Count == 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("No regions available. Are you sure your appid is valid and setup?");
|
||||||
|
yield break; // break if we don't get regions at all
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotonPingManager pingManager = new PhotonPingManager();
|
||||||
|
foreach (Region region in PhotonNetwork.networkingPeer.AvailableRegions)
|
||||||
|
{
|
||||||
|
SP.StartCoroutine(pingManager.PingSocket(region));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!pingManager.Done)
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(0.1f); // wait until pinging finished (offline mode won't ping)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Region best = pingManager.BestRegion;
|
||||||
|
PhotonHandler.BestRegionCodeInPreferences = best.Code;
|
||||||
|
|
||||||
|
Debug.Log("Found best region: '" + best.Code + "' ping: " + best.Ping + ". Calling ConnectToRegionMaster() is: " + connectToBest);
|
||||||
|
|
||||||
|
if (connectToBest)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.ConnectToRegionMaster(best.Code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 177bddf229f8d8445a70c0652f03b7df
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,97 @@
|
||||||
|
#pragma warning disable 1587
|
||||||
|
/// \file
|
||||||
|
/// <summary>Part of the [Optional GUI](@ref optionalGui).</summary>
|
||||||
|
#pragma warning restore 1587
|
||||||
|
|
||||||
|
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This MonoBehaviour is a basic GUI for the Photon client's network-simulation feature.
|
||||||
|
/// It can modify lag (fixed delay), jitter (random lag) and packet loss.
|
||||||
|
/// </summary>
|
||||||
|
/// \ingroup optionalGui
|
||||||
|
public class PhotonLagSimulationGui : MonoBehaviour
|
||||||
|
{
|
||||||
|
/// <summary>Positioning rect for window.</summary>
|
||||||
|
public Rect WindowRect = new Rect(0, 100, 120, 100);
|
||||||
|
|
||||||
|
/// <summary>Unity GUI Window ID (must be unique or will cause issues).</summary>
|
||||||
|
public int WindowId = 101;
|
||||||
|
|
||||||
|
/// <summary>Shows or hides GUI (does not affect settings).</summary>
|
||||||
|
public bool Visible = true;
|
||||||
|
|
||||||
|
/// <summary>The peer currently in use (to set the network simulation).</summary>
|
||||||
|
public PhotonPeer Peer { get; set; }
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
this.Peer = PhotonNetwork.networkingPeer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnGUI()
|
||||||
|
{
|
||||||
|
if (!this.Visible)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.Peer == null)
|
||||||
|
{
|
||||||
|
this.WindowRect = GUILayout.Window(this.WindowId, this.WindowRect, this.NetSimHasNoPeerWindow, "Netw. Sim.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.WindowRect = GUILayout.Window(this.WindowId, this.WindowRect, this.NetSimWindow, "Netw. Sim.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NetSimHasNoPeerWindow(int windowId)
|
||||||
|
{
|
||||||
|
GUILayout.Label("No peer to communicate with. ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NetSimWindow(int windowId)
|
||||||
|
{
|
||||||
|
GUILayout.Label(string.Format("Rtt:{0,4} +/-{1,3}", this.Peer.RoundTripTime, this.Peer.RoundTripTimeVariance));
|
||||||
|
|
||||||
|
bool simEnabled = this.Peer.IsSimulationEnabled;
|
||||||
|
bool newSimEnabled = GUILayout.Toggle(simEnabled, "Simulate");
|
||||||
|
if (newSimEnabled != simEnabled)
|
||||||
|
{
|
||||||
|
this.Peer.IsSimulationEnabled = newSimEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
float inOutLag = this.Peer.NetworkSimulationSettings.IncomingLag;
|
||||||
|
GUILayout.Label("Lag " + inOutLag);
|
||||||
|
inOutLag = GUILayout.HorizontalSlider(inOutLag, 0, 500);
|
||||||
|
|
||||||
|
this.Peer.NetworkSimulationSettings.IncomingLag = (int)inOutLag;
|
||||||
|
this.Peer.NetworkSimulationSettings.OutgoingLag = (int)inOutLag;
|
||||||
|
|
||||||
|
float inOutJitter = this.Peer.NetworkSimulationSettings.IncomingJitter;
|
||||||
|
GUILayout.Label("Jit " + inOutJitter);
|
||||||
|
inOutJitter = GUILayout.HorizontalSlider(inOutJitter, 0, 100);
|
||||||
|
|
||||||
|
this.Peer.NetworkSimulationSettings.IncomingJitter = (int)inOutJitter;
|
||||||
|
this.Peer.NetworkSimulationSettings.OutgoingJitter = (int)inOutJitter;
|
||||||
|
|
||||||
|
float loss = this.Peer.NetworkSimulationSettings.IncomingLossPercentage;
|
||||||
|
GUILayout.Label("Loss " + loss);
|
||||||
|
loss = GUILayout.HorizontalSlider(loss, 0, 10);
|
||||||
|
|
||||||
|
this.Peer.NetworkSimulationSettings.IncomingLossPercentage = (int)loss;
|
||||||
|
this.Peer.NetworkSimulationSettings.OutgoingLossPercentage = (int)loss;
|
||||||
|
|
||||||
|
// if anything was clicked, the height of this window is likely changed. reduce it to be layouted again next frame
|
||||||
|
if (GUI.changed)
|
||||||
|
{
|
||||||
|
this.WindowRect.height = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI.DragWindow();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5867a53c8db0e6745818285bb6b6e1b9
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 88e11b3353de7e94d84b1ec5adbdd15e
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,428 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="PhotonPlayer.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// Represents a player, identified by actorID (a.k.a. ActorNumber).
|
||||||
|
// Caches properties of a player.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
using UnityEngine;
|
||||||
|
using Hashtable = ExitGames.Client.Photon.Hashtable;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Summarizes a "player" within a room, identified (in that room) by actorID.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Each player has an actorId (or ID), valid for that room. It's -1 until it's assigned by server.
|
||||||
|
/// Each client can set it's player's custom properties with SetCustomProperties, even before being in a room.
|
||||||
|
/// They are synced when joining a room.
|
||||||
|
/// </remarks>
|
||||||
|
/// \ingroup publicApi
|
||||||
|
public class PhotonPlayer : IComparable<PhotonPlayer>, IComparable<int>, IEquatable<PhotonPlayer>, IEquatable<int>
|
||||||
|
{
|
||||||
|
/// <summary>This player's actorID</summary>
|
||||||
|
public int ID
|
||||||
|
{
|
||||||
|
get { return this.actorID; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Identifier of this player in current room.</summary>
|
||||||
|
private int actorID = -1;
|
||||||
|
|
||||||
|
private string nameField = "";
|
||||||
|
|
||||||
|
/// <summary>Nickname of this player.</summary>
|
||||||
|
/// <remarks>Set the PhotonNetwork.playerName to make the name synchronized in a room.</remarks>
|
||||||
|
public string NickName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.nameField;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!IsLocal)
|
||||||
|
{
|
||||||
|
Debug.LogError("Error: Cannot change the name of a remote player!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(value) || value.Equals(this.nameField))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nameField = value;
|
||||||
|
PhotonNetwork.playerName = value; // this will sync the local player's name in a room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>UserId of the player, available when the room got created with RoomOptions.PublishUserId = true.</summary>
|
||||||
|
/// <remarks>Useful for PhotonNetwork.FindFriends and blocking slots in a room for expected players (e.g. in PhotonNetwork.CreateRoom).</remarks>
|
||||||
|
public string UserId { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Only one player is controlled by each client. Others are not local.</summary>
|
||||||
|
public readonly bool IsLocal = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if this player is the Master Client of the current room.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// See also: PhotonNetwork.masterClient.
|
||||||
|
/// </remarks>
|
||||||
|
public bool IsMasterClient
|
||||||
|
{
|
||||||
|
get { return (PhotonNetwork.networkingPeer.mMasterClientId == this.ID); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Players might be inactive in a room when PlayerTTL for a room is > 0. If true, the player is not getting events from this room (now) but can return later.</summary>
|
||||||
|
public bool IsInactive { get; set; } // needed for rejoins
|
||||||
|
|
||||||
|
/// <summary>Read-only cache for custom properties of player. Set via PhotonPlayer.SetCustomProperties.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Don't modify the content of this Hashtable. Use SetCustomProperties and the
|
||||||
|
/// properties of this class to modify values. When you use those, the client will
|
||||||
|
/// sync values with the server.
|
||||||
|
/// </remarks>
|
||||||
|
/// <see cref="SetCustomProperties"/>
|
||||||
|
public Hashtable CustomProperties { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Creates a Hashtable with all properties (custom and "well known" ones).</summary>
|
||||||
|
/// <remarks>If used more often, this should be cached.</remarks>
|
||||||
|
public Hashtable AllProperties
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Hashtable allProps = new Hashtable();
|
||||||
|
allProps.Merge(this.CustomProperties);
|
||||||
|
allProps[ActorProperties.PlayerName] = this.NickName;
|
||||||
|
return allProps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Can be used to store a reference that's useful to know "by player".</summary>
|
||||||
|
/// <remarks>Example: Set a player's character as Tag by assigning the GameObject on Instantiate.</remarks>
|
||||||
|
public object TagObject;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a PhotonPlayer instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isLocal">If this is the local peer's player (or a remote one).</param>
|
||||||
|
/// <param name="actorID">ID or ActorNumber of this player in the current room (a shortcut to identify each player in room)</param>
|
||||||
|
/// <param name="name">Name of the player (a "well known property").</param>
|
||||||
|
public PhotonPlayer(bool isLocal, int actorID, string name)
|
||||||
|
{
|
||||||
|
this.CustomProperties = new Hashtable();
|
||||||
|
this.IsLocal = isLocal;
|
||||||
|
this.actorID = actorID;
|
||||||
|
this.nameField = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internally used to create players from event Join
|
||||||
|
/// </summary>
|
||||||
|
protected internal PhotonPlayer(bool isLocal, int actorID, Hashtable properties)
|
||||||
|
{
|
||||||
|
this.CustomProperties = new Hashtable();
|
||||||
|
this.IsLocal = isLocal;
|
||||||
|
this.actorID = actorID;
|
||||||
|
|
||||||
|
this.InternalCacheProperties(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes PhotonPlayer comparable
|
||||||
|
/// </summary>
|
||||||
|
public override bool Equals(object p)
|
||||||
|
{
|
||||||
|
PhotonPlayer pp = p as PhotonPlayer;
|
||||||
|
return (pp != null && this.GetHashCode() == pp.GetHashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return this.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used internally, to update this client's playerID when assigned.
|
||||||
|
/// </summary>
|
||||||
|
internal void InternalChangeLocalID(int newID)
|
||||||
|
{
|
||||||
|
if (!this.IsLocal)
|
||||||
|
{
|
||||||
|
Debug.LogError("ERROR You should never change PhotonPlayer IDs!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.actorID = newID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Caches custom properties for this player.
|
||||||
|
/// </summary>
|
||||||
|
internal void InternalCacheProperties(Hashtable properties)
|
||||||
|
{
|
||||||
|
if (properties == null || properties.Count == 0 || this.CustomProperties.Equals(properties))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (properties.ContainsKey(ActorProperties.PlayerName))
|
||||||
|
{
|
||||||
|
this.nameField = (string)properties[ActorProperties.PlayerName];
|
||||||
|
}
|
||||||
|
if (properties.ContainsKey(ActorProperties.UserId))
|
||||||
|
{
|
||||||
|
this.UserId = (string)properties[ActorProperties.UserId];
|
||||||
|
}
|
||||||
|
if (properties.ContainsKey(ActorProperties.IsInactive))
|
||||||
|
{
|
||||||
|
this.IsInactive = (bool)properties[ActorProperties.IsInactive]; //TURNBASED new well-known propery for players
|
||||||
|
}
|
||||||
|
|
||||||
|
this.CustomProperties.MergeStringKeys(properties);
|
||||||
|
this.CustomProperties.StripKeysWithNullValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the this player's Custom Properties with new/updated key-values.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Custom Properties are a key-value set (Hashtable) which is available to all players in a room.
|
||||||
|
/// They can relate to the room or individual players and are useful when only the current value
|
||||||
|
/// of something is of interest. For example: The map of a room.
|
||||||
|
/// All keys must be strings.
|
||||||
|
///
|
||||||
|
/// The Room and the PhotonPlayer class both have SetCustomProperties methods.
|
||||||
|
/// Also, both classes offer access to current key-values by: customProperties.
|
||||||
|
///
|
||||||
|
/// Always use SetCustomProperties to change values.
|
||||||
|
/// To reduce network traffic, set only values that actually changed.
|
||||||
|
/// New properties are added, existing values are updated.
|
||||||
|
/// Other values will not be changed, so only provide values that changed or are new.
|
||||||
|
///
|
||||||
|
/// To delete a named (custom) property of this room, use null as value.
|
||||||
|
///
|
||||||
|
/// Locally, SetCustomProperties will update it's cache without delay.
|
||||||
|
/// Other clients are updated through Photon (the server) with a fitting operation.
|
||||||
|
///
|
||||||
|
/// <b>Check and Swap</b>
|
||||||
|
///
|
||||||
|
/// SetCustomProperties have the option to do a server-side Check-And-Swap (CAS):
|
||||||
|
/// Values only get updated if the expected values are correct.
|
||||||
|
/// The expectedValues can be different key/values than the propertiesToSet. So you can
|
||||||
|
/// check some key and set another key's value (if the check succeeds).
|
||||||
|
///
|
||||||
|
/// If the client's knowledge of properties is wrong or outdated, it can't set values with CAS.
|
||||||
|
/// This can be useful to keep players from concurrently setting values. For example: If all players
|
||||||
|
/// try to pickup some card or item, only one should get it. With CAS, only the first SetProperties
|
||||||
|
/// gets executed server-side and any other (sent at the same time) fails.
|
||||||
|
///
|
||||||
|
/// The server will broadcast successfully changed values and the local "cache" of customProperties
|
||||||
|
/// only gets updated after a roundtrip (if anything changed).
|
||||||
|
///
|
||||||
|
/// You can do a "webForward": Photon will send the changed properties to a WebHook defined
|
||||||
|
/// for your application.
|
||||||
|
///
|
||||||
|
/// <b>OfflineMode</b>
|
||||||
|
///
|
||||||
|
/// While PhotonNetwork.offlineMode is true, the expectedValues and webForward parameters are ignored.
|
||||||
|
/// In OfflineMode, the local customProperties values are immediately updated (without the roundtrip).
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="propertiesToSet">The new properties to be set. </param>
|
||||||
|
/// <param name="expectedValues">At least one property key/value set to check server-side. Key and value must be correct. Ignored in OfflineMode.</param>
|
||||||
|
/// <param name="webForward">Set to true, to forward the set properties to a WebHook, defined for this app (in Dashboard). Ignored in OfflineMode.</param>
|
||||||
|
public void SetCustomProperties(Hashtable propertiesToSet, Hashtable expectedValues = null, bool webForward = false)
|
||||||
|
{
|
||||||
|
if (propertiesToSet == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hashtable customProps = propertiesToSet.StripToStringKeys() as Hashtable;
|
||||||
|
Hashtable customPropsToCheck = expectedValues.StripToStringKeys() as Hashtable;
|
||||||
|
|
||||||
|
|
||||||
|
// no expected values -> set and callback
|
||||||
|
bool noCas = customPropsToCheck == null || customPropsToCheck.Count == 0;
|
||||||
|
bool inOnlineRoom = this.actorID > 0 && !PhotonNetwork.offlineMode;
|
||||||
|
|
||||||
|
if (noCas)
|
||||||
|
{
|
||||||
|
this.CustomProperties.Merge(customProps);
|
||||||
|
this.CustomProperties.StripKeysWithNullValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inOnlineRoom)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.OpSetPropertiesOfActor(this.actorID, customProps, customPropsToCheck, webForward);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inOnlineRoom || noCas)
|
||||||
|
{
|
||||||
|
this.InternalCacheProperties(customProps);
|
||||||
|
NetworkingPeer.SendMonoMessage(PhotonNetworkingMessage.OnPhotonPlayerPropertiesChanged, this, customProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to get a specific player by id.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ID">ActorID</param>
|
||||||
|
/// <returns>The player with matching actorID or null, if the actorID is not in use.</returns>
|
||||||
|
public static PhotonPlayer Find(int ID)
|
||||||
|
{
|
||||||
|
if (PhotonNetwork.networkingPeer != null)
|
||||||
|
{
|
||||||
|
return PhotonNetwork.networkingPeer.GetPlayerWithId(ID);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PhotonPlayer Get(int id)
|
||||||
|
{
|
||||||
|
return PhotonPlayer.Find(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PhotonPlayer GetNext()
|
||||||
|
{
|
||||||
|
return GetNextFor(this.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PhotonPlayer GetNextFor(PhotonPlayer currentPlayer)
|
||||||
|
{
|
||||||
|
if (currentPlayer == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return GetNextFor(currentPlayer.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PhotonPlayer GetNextFor(int currentPlayerId)
|
||||||
|
{
|
||||||
|
if (PhotonNetwork.networkingPeer == null || PhotonNetwork.networkingPeer.mActors == null || PhotonNetwork.networkingPeer.mActors.Count < 2)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<int, PhotonPlayer> players = PhotonNetwork.networkingPeer.mActors;
|
||||||
|
int nextHigherId = int.MaxValue; // we look for the next higher ID
|
||||||
|
int lowestId = currentPlayerId; // if we are the player with the highest ID, there is no higher and we return to the lowest player's id
|
||||||
|
|
||||||
|
foreach (int playerid in players.Keys)
|
||||||
|
{
|
||||||
|
if (playerid < lowestId)
|
||||||
|
{
|
||||||
|
lowestId = playerid; // less than any other ID (which must be at least less than this player's id).
|
||||||
|
}
|
||||||
|
else if (playerid > currentPlayerId && playerid < nextHigherId)
|
||||||
|
{
|
||||||
|
nextHigherId = playerid; // more than our ID and less than those found so far.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//UnityEngine.Debug.LogWarning("Debug. " + currentPlayerId + " lower: " + lowestId + " higher: " + nextHigherId + " ");
|
||||||
|
//UnityEngine.Debug.LogWarning(this.RoomReference.GetPlayer(currentPlayerId));
|
||||||
|
//UnityEngine.Debug.LogWarning(this.RoomReference.GetPlayer(lowestId));
|
||||||
|
//if (nextHigherId != int.MaxValue) UnityEngine.Debug.LogWarning(this.RoomReference.GetPlayer(nextHigherId));
|
||||||
|
return (nextHigherId != int.MaxValue) ? players[nextHigherId] : players[lowestId];
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IComparable implementation
|
||||||
|
|
||||||
|
public int CompareTo (PhotonPlayer other)
|
||||||
|
{
|
||||||
|
if ( other == null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.GetHashCode().CompareTo(other.GetHashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo (int other)
|
||||||
|
{
|
||||||
|
return this.GetHashCode().CompareTo(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IEquatable implementation
|
||||||
|
|
||||||
|
public bool Equals (PhotonPlayer other)
|
||||||
|
{
|
||||||
|
if ( other == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.GetHashCode().Equals(other.GetHashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals (int other)
|
||||||
|
{
|
||||||
|
return this.GetHashCode().Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Brief summary string of the PhotonPlayer. Includes name or player.ID and if it's the Master Client.
|
||||||
|
/// </summary>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(this.NickName))
|
||||||
|
{
|
||||||
|
return string.Format("#{0:00}{1}{2}", this.ID, this.IsInactive ? " (inactive)" : " ", this.IsMasterClient ? "(master)":"");
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Format("'{0}'{1}{2}", this.NickName, this.IsInactive ? " (inactive)" : " ", this.IsMasterClient ? "(master)" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String summary of the PhotonPlayer: player.ID, name and all custom properties of this user.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Use with care and not every frame!
|
||||||
|
/// Converts the customProperties to a String on every single call.
|
||||||
|
/// </remarks>
|
||||||
|
public string ToStringFull()
|
||||||
|
{
|
||||||
|
return string.Format("#{0:00} '{1}'{2} {3}", this.ID, this.NickName, this.IsInactive ? " (inactive)" : "", this.CustomProperties.ToStringFull());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Obsoleted variable names
|
||||||
|
|
||||||
|
[Obsolete("Please use NickName (updated case for naming).")]
|
||||||
|
public string name { get { return this.NickName; } set { this.NickName = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use UserId (updated case for naming).")]
|
||||||
|
public string userId { get { return this.UserId; } internal set { this.UserId = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use IsLocal (updated case for naming).")]
|
||||||
|
public bool isLocal { get { return this.IsLocal; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use IsMasterClient (updated case for naming).")]
|
||||||
|
public bool isMasterClient { get { return this.IsMasterClient; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use IsInactive (updated case for naming).")]
|
||||||
|
public bool isInactive { get { return this.IsInactive; } set { this.IsInactive = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use CustomProperties (updated case for naming).")]
|
||||||
|
public Hashtable customProperties { get { return this.CustomProperties; } internal set { this.CustomProperties = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use AllProperties (updated case for naming).")]
|
||||||
|
public Hashtable allProperties { get { return this.AllProperties; } }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e3e4b5bebc687044b9c6c2803c36be3d
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,159 @@
|
||||||
|
#pragma warning disable 1587
|
||||||
|
/// \file
|
||||||
|
/// <summary>Part of the [Optional GUI](@ref optionalGui).</summary>
|
||||||
|
#pragma warning restore 1587
|
||||||
|
|
||||||
|
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Basic GUI to show traffic and health statistics of the connection to Photon,
|
||||||
|
/// toggled by shift+tab.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The shown health values can help identify problems with connection losses or performance.
|
||||||
|
/// Example:
|
||||||
|
/// If the time delta between two consecutive SendOutgoingCommands calls is a second or more,
|
||||||
|
/// chances rise for a disconnect being caused by this (because acknowledgements to the server
|
||||||
|
/// need to be sent in due time).
|
||||||
|
/// </remarks>
|
||||||
|
/// \ingroup optionalGui
|
||||||
|
public class PhotonStatsGui : MonoBehaviour
|
||||||
|
{
|
||||||
|
/// <summary>Shows or hides GUI (does not affect if stats are collected).</summary>
|
||||||
|
public bool statsWindowOn = true;
|
||||||
|
|
||||||
|
/// <summary>Option to turn collecting stats on or off (used in Update()).</summary>
|
||||||
|
public bool statsOn = true;
|
||||||
|
|
||||||
|
/// <summary>Shows additional "health" values of connection.</summary>
|
||||||
|
public bool healthStatsVisible;
|
||||||
|
|
||||||
|
/// <summary>Shows additional "lower level" traffic stats.</summary>
|
||||||
|
public bool trafficStatsOn;
|
||||||
|
|
||||||
|
/// <summary>Show buttons to control stats and reset them.</summary>
|
||||||
|
public bool buttonsOn;
|
||||||
|
|
||||||
|
/// <summary>Positioning rect for window.</summary>
|
||||||
|
public Rect statsRect = new Rect(0, 100, 200, 50);
|
||||||
|
|
||||||
|
/// <summary>Unity GUI Window ID (must be unique or will cause issues).</summary>
|
||||||
|
public int WindowId = 100;
|
||||||
|
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
if (this.statsRect.x <= 0)
|
||||||
|
{
|
||||||
|
this.statsRect.x = Screen.width - this.statsRect.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Checks for shift+tab input combination (to toggle statsOn).</summary>
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(KeyCode.Tab) && Input.GetKey(KeyCode.LeftShift))
|
||||||
|
{
|
||||||
|
this.statsWindowOn = !this.statsWindowOn;
|
||||||
|
this.statsOn = true; // enable stats when showing the window
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnGUI()
|
||||||
|
{
|
||||||
|
if (PhotonNetwork.networkingPeer.TrafficStatsEnabled != statsOn)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.TrafficStatsEnabled = this.statsOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.statsWindowOn)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.statsRect = GUILayout.Window(this.WindowId, this.statsRect, this.TrafficStatsWindow, "Messages (shift+tab)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TrafficStatsWindow(int windowID)
|
||||||
|
{
|
||||||
|
bool statsToLog = false;
|
||||||
|
TrafficStatsGameLevel gls = PhotonNetwork.networkingPeer.TrafficStatsGameLevel;
|
||||||
|
long elapsedMs = PhotonNetwork.networkingPeer.TrafficStatsElapsedMs / 1000;
|
||||||
|
if (elapsedMs == 0)
|
||||||
|
{
|
||||||
|
elapsedMs = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
this.buttonsOn = GUILayout.Toggle(this.buttonsOn, "buttons");
|
||||||
|
this.healthStatsVisible = GUILayout.Toggle(this.healthStatsVisible, "health");
|
||||||
|
this.trafficStatsOn = GUILayout.Toggle(this.trafficStatsOn, "traffic");
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
string total = string.Format("Out {0,4} | In {1,4} | Sum {2,4}", gls.TotalOutgoingMessageCount, gls.TotalIncomingMessageCount, gls.TotalMessageCount);
|
||||||
|
string elapsedTime = string.Format("{0}sec average:", elapsedMs);
|
||||||
|
string average = string.Format("Out {0,4} | In {1,4} | Sum {2,4}", gls.TotalOutgoingMessageCount / elapsedMs, gls.TotalIncomingMessageCount / elapsedMs, gls.TotalMessageCount / elapsedMs);
|
||||||
|
GUILayout.Label(total);
|
||||||
|
GUILayout.Label(elapsedTime);
|
||||||
|
GUILayout.Label(average);
|
||||||
|
|
||||||
|
if (this.buttonsOn)
|
||||||
|
{
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
this.statsOn = GUILayout.Toggle(this.statsOn, "stats on");
|
||||||
|
if (GUILayout.Button("Reset"))
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.TrafficStatsReset();
|
||||||
|
PhotonNetwork.networkingPeer.TrafficStatsEnabled = true;
|
||||||
|
}
|
||||||
|
statsToLog = GUILayout.Button("To Log");
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
string trafficStatsIn = string.Empty;
|
||||||
|
string trafficStatsOut = string.Empty;
|
||||||
|
if (this.trafficStatsOn)
|
||||||
|
{
|
||||||
|
GUILayout.Box("Traffic Stats");
|
||||||
|
trafficStatsIn = "Incoming: \n" + PhotonNetwork.networkingPeer.TrafficStatsIncoming.ToString();
|
||||||
|
trafficStatsOut = "Outgoing: \n" + PhotonNetwork.networkingPeer.TrafficStatsOutgoing.ToString();
|
||||||
|
GUILayout.Label(trafficStatsIn);
|
||||||
|
GUILayout.Label(trafficStatsOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
string healthStats = string.Empty;
|
||||||
|
if (this.healthStatsVisible)
|
||||||
|
{
|
||||||
|
GUILayout.Box("Health Stats");
|
||||||
|
healthStats = string.Format(
|
||||||
|
"ping: {6}[+/-{7}]ms resent:{8} \n\nmax ms between\nsend: {0,4} \ndispatch: {1,4} \n\nlongest dispatch for: \nev({3}):{2,3}ms \nop({5}):{4,3}ms",
|
||||||
|
gls.LongestDeltaBetweenSending,
|
||||||
|
gls.LongestDeltaBetweenDispatching,
|
||||||
|
gls.LongestEventCallback,
|
||||||
|
gls.LongestEventCallbackCode,
|
||||||
|
gls.LongestOpResponseCallback,
|
||||||
|
gls.LongestOpResponseCallbackOpCode,
|
||||||
|
PhotonNetwork.networkingPeer.RoundTripTime,
|
||||||
|
PhotonNetwork.networkingPeer.RoundTripTimeVariance,
|
||||||
|
PhotonNetwork.networkingPeer.ResentReliableCommands);
|
||||||
|
GUILayout.Label(healthStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statsToLog)
|
||||||
|
{
|
||||||
|
string complete = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}", total, elapsedTime, average, trafficStatsIn, trafficStatsOut, healthStats);
|
||||||
|
Debug.Log(complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if anything was clicked, the height of this window is likely changed. reduce it to be layouted again next frame
|
||||||
|
if (GUI.changed)
|
||||||
|
{
|
||||||
|
this.statsRect.height = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI.DragWindow();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d06466c03d263624786afa88b52928b6
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,187 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The PhotonStreamQueue helps you poll object states at higher frequencies then what
|
||||||
|
/// PhotonNetwork.sendRate dictates and then sends all those states at once when
|
||||||
|
/// Serialize() is called.
|
||||||
|
/// On the receiving end you can call Deserialize() and then the stream will roll out
|
||||||
|
/// the received object states in the same order and timeStep they were recorded in.
|
||||||
|
/// </summary>
|
||||||
|
public class PhotonStreamQueue
|
||||||
|
{
|
||||||
|
#region Members
|
||||||
|
|
||||||
|
private int m_SampleRate;
|
||||||
|
private int m_SampleCount;
|
||||||
|
private int m_ObjectsPerSample = -1;
|
||||||
|
|
||||||
|
private float m_LastSampleTime = -Mathf.Infinity;
|
||||||
|
private int m_LastFrameCount = -1;
|
||||||
|
private int m_NextObjectIndex = -1;
|
||||||
|
|
||||||
|
private List<object> m_Objects = new List<object>();
|
||||||
|
|
||||||
|
private bool m_IsWriting;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PhotonStreamQueue"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sampleRate">How many times per second should the object states be sampled</param>
|
||||||
|
public PhotonStreamQueue(int sampleRate)
|
||||||
|
{
|
||||||
|
this.m_SampleRate = sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BeginWritePackage()
|
||||||
|
{
|
||||||
|
//If not enough time has passed since the last sample, we don't want to write anything
|
||||||
|
if (Time.realtimeSinceStartup < this.m_LastSampleTime + 1f/this.m_SampleRate)
|
||||||
|
{
|
||||||
|
this.m_IsWriting = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.m_SampleCount == 1)
|
||||||
|
{
|
||||||
|
this.m_ObjectsPerSample = this.m_Objects.Count;
|
||||||
|
//Debug.Log( "Setting m_ObjectsPerSample to " + m_ObjectsPerSample );
|
||||||
|
}
|
||||||
|
else if (this.m_SampleCount > 1)
|
||||||
|
{
|
||||||
|
if (this.m_Objects.Count/this.m_SampleCount != this.m_ObjectsPerSample)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("The number of objects sent via a PhotonStreamQueue has to be the same each frame");
|
||||||
|
Debug.LogWarning("Objects in List: " + this.m_Objects.Count + " / Sample Count: " + this.m_SampleCount + " = " + (this.m_Objects.Count/this.m_SampleCount) + " != " + this.m_ObjectsPerSample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.m_IsWriting = true;
|
||||||
|
this.m_SampleCount++;
|
||||||
|
this.m_LastSampleTime = Time.realtimeSinceStartup;
|
||||||
|
|
||||||
|
/*if( m_SampleCount > 1 )
|
||||||
|
{
|
||||||
|
Debug.Log( "Check: " + m_Objects.Count + " / " + m_SampleCount + " = " + ( m_Objects.Count / m_SampleCount ) + " = " + m_ObjectsPerSample );
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the PhotonStreamQueue. You need to do this whenever the amount of objects you are observing changes
|
||||||
|
/// </summary>
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
this.m_SampleCount = 0;
|
||||||
|
this.m_ObjectsPerSample = -1;
|
||||||
|
|
||||||
|
this.m_LastSampleTime = -Mathf.Infinity;
|
||||||
|
this.m_LastFrameCount = -1;
|
||||||
|
|
||||||
|
this.m_Objects.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the next object to the queue. This works just like PhotonStream.SendNext
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object you want to add to the queue</param>
|
||||||
|
public void SendNext(object obj)
|
||||||
|
{
|
||||||
|
if (Time.frameCount != this.m_LastFrameCount)
|
||||||
|
{
|
||||||
|
BeginWritePackage();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.m_LastFrameCount = Time.frameCount;
|
||||||
|
|
||||||
|
if (this.m_IsWriting == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.m_Objects.Add(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the queue has stored any objects
|
||||||
|
/// </summary>
|
||||||
|
public bool HasQueuedObjects()
|
||||||
|
{
|
||||||
|
return this.m_NextObjectIndex != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Receives the next object from the queue. This works just like PhotonStream.ReceiveNext
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public object ReceiveNext()
|
||||||
|
{
|
||||||
|
if (this.m_NextObjectIndex == -1)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.m_NextObjectIndex >= this.m_Objects.Count)
|
||||||
|
{
|
||||||
|
this.m_NextObjectIndex -= this.m_ObjectsPerSample;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.m_Objects[this.m_NextObjectIndex++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serializes the specified stream. Call this in your OnPhotonSerializeView method to send the whole recorded stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The PhotonStream you receive as a parameter in OnPhotonSerializeView</param>
|
||||||
|
public void Serialize(PhotonStream stream)
|
||||||
|
{
|
||||||
|
// TODO: find a better solution for this:
|
||||||
|
// the "if" is a workaround for packages which have only 1 sample/frame. in that case, SendNext didn't set the obj per sample.
|
||||||
|
if (m_Objects.Count > 0 && this.m_ObjectsPerSample < 0)
|
||||||
|
{
|
||||||
|
this.m_ObjectsPerSample = m_Objects.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.SendNext(this.m_SampleCount);
|
||||||
|
stream.SendNext(this.m_ObjectsPerSample);
|
||||||
|
|
||||||
|
for (int i = 0; i < this.m_Objects.Count; ++i)
|
||||||
|
{
|
||||||
|
stream.SendNext(this.m_Objects[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Debug.Log( "Serialize " + m_SampleCount + " samples with " + m_ObjectsPerSample + " objects per sample. object count: " + m_Objects.Count + " / " + ( m_SampleCount * m_ObjectsPerSample ) );
|
||||||
|
|
||||||
|
this.m_Objects.Clear();
|
||||||
|
this.m_SampleCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserializes the specified stream. Call this in your OnPhotonSerializeView method to receive the whole recorded stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The PhotonStream you receive as a parameter in OnPhotonSerializeView</param>
|
||||||
|
public void Deserialize(PhotonStream stream)
|
||||||
|
{
|
||||||
|
this.m_Objects.Clear();
|
||||||
|
|
||||||
|
this.m_SampleCount = (int)stream.ReceiveNext();
|
||||||
|
this.m_ObjectsPerSample = (int)stream.ReceiveNext();
|
||||||
|
|
||||||
|
for (int i = 0; i < this.m_SampleCount*this.m_ObjectsPerSample; ++i)
|
||||||
|
{
|
||||||
|
this.m_Objects.Add(stream.ReceiveNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.m_Objects.Count > 0)
|
||||||
|
{
|
||||||
|
this.m_NextObjectIndex = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.m_NextObjectIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Debug.Log( "Deserialized " + m_SampleCount + " samples with " + m_ObjectsPerSample + " objects per sample. object count: " + m_Objects.Count + " / " + ( m_SampleCount * m_ObjectsPerSample ) );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 006991e32d9020c4d896f161318a2bc0
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
|
@ -0,0 +1,692 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="PhotonView.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
//
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
public enum ViewSynchronization { Off, ReliableDeltaCompressed, Unreliable, UnreliableOnChange }
|
||||||
|
public enum OnSerializeTransform { OnlyPosition, OnlyRotation, OnlyScale, PositionAndRotation, All }
|
||||||
|
public enum OnSerializeRigidBody { OnlyVelocity, OnlyAngularVelocity, All }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Options to define how Ownership Transfer is handled per PhotonView.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This setting affects how RequestOwnership and TransferOwnership work at runtime.
|
||||||
|
/// </remarks>
|
||||||
|
public enum OwnershipOption
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ownership is fixed. Instantiated objects stick with their creator, scene objects always belong to the Master Client.
|
||||||
|
/// </summary>
|
||||||
|
Fixed,
|
||||||
|
/// <summary>
|
||||||
|
/// Ownership can be taken away from the current owner who can't object.
|
||||||
|
/// </summary>
|
||||||
|
Takeover,
|
||||||
|
/// <summary>
|
||||||
|
/// Ownership can be requested with PhotonView.RequestOwnership but the current owner has to agree to give up ownership.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The current owner has to implement IPunCallbacks.OnOwnershipRequest to react to the ownership request.</remarks>
|
||||||
|
Request
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PUN's NetworkView replacement class for networking. Use it like a NetworkView.
|
||||||
|
/// </summary>
|
||||||
|
/// \ingroup publicApi
|
||||||
|
[AddComponentMenu("Photon Networking/Photon View &v")]
|
||||||
|
public class PhotonView : Photon.MonoBehaviour
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[ContextMenu("Open PUN Wizard")]
|
||||||
|
void OpenPunWizard()
|
||||||
|
{
|
||||||
|
EditorApplication.ExecuteMenuItem("Window/Photon Unity Networking");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public int ownerId;
|
||||||
|
|
||||||
|
public byte group = 0;
|
||||||
|
|
||||||
|
protected internal bool mixedModeIsReliable = false;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flag to check if ownership of this photonView was set during the lifecycle. Used for checking when joining late if event with mismatched owner and sender needs addressing.
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if owner ship was transfered; otherwise, <c>false</c>.</value>
|
||||||
|
public bool OwnerShipWasTransfered;
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE: this is now an integer because unity won't serialize short (needed for instantiation). we SEND only a short though!
|
||||||
|
// NOTE: prefabs have a prefixBackup of -1. this is replaced with any currentLevelPrefix that's used at runtime. instantiated GOs get their prefix set pre-instantiation (so those are not -1 anymore)
|
||||||
|
public int prefix
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.prefixBackup == -1 && PhotonNetwork.networkingPeer != null)
|
||||||
|
{
|
||||||
|
this.prefixBackup = PhotonNetwork.networkingPeer.currentLevelPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.prefixBackup;
|
||||||
|
}
|
||||||
|
set { this.prefixBackup = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// this field is serialized by unity. that means it is copied when instantiating a persistent obj into the scene
|
||||||
|
public int prefixBackup = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is the instantiationData that was passed when calling PhotonNetwork.Instantiate* (if that was used to spawn this prefab)
|
||||||
|
/// </summary>
|
||||||
|
public object[] instantiationData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!this.didAwake)
|
||||||
|
{
|
||||||
|
// even though viewID and instantiationID are setup before the GO goes live, this data can't be set. as workaround: fetch it if needed
|
||||||
|
this.instantiationDataField = PhotonNetwork.networkingPeer.FetchInstantiationData(this.instantiationId);
|
||||||
|
}
|
||||||
|
return this.instantiationDataField;
|
||||||
|
}
|
||||||
|
set { this.instantiationDataField = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object[] instantiationDataField;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For internal use only, don't use
|
||||||
|
/// </summary>
|
||||||
|
protected internal object[] lastOnSerializeDataSent = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For internal use only, don't use
|
||||||
|
/// </summary>
|
||||||
|
protected internal object[] lastOnSerializeDataReceived = null;
|
||||||
|
|
||||||
|
public ViewSynchronization synchronization;
|
||||||
|
|
||||||
|
public OnSerializeTransform onSerializeTransformOption = OnSerializeTransform.PositionAndRotation;
|
||||||
|
|
||||||
|
public OnSerializeRigidBody onSerializeRigidBodyOption = OnSerializeRigidBody.All;
|
||||||
|
|
||||||
|
/// <summary>Defines if ownership of this PhotonView is fixed, can be requested or simply taken.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Note that you can't edit this value at runtime.
|
||||||
|
/// The options are described in enum OwnershipOption.
|
||||||
|
/// The current owner has to implement IPunCallbacks.OnOwnershipRequest to react to the ownership request.
|
||||||
|
/// </remarks>
|
||||||
|
public OwnershipOption ownershipTransfer = OwnershipOption.Fixed;
|
||||||
|
|
||||||
|
public List<Component> ObservedComponents;
|
||||||
|
Dictionary<Component, MethodInfo> m_OnSerializeMethodInfos = new Dictionary<Component, MethodInfo>(3);
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
// Suppressing compiler warning "this variable is never used". Only used in the CustomEditor, only in Editor
|
||||||
|
#pragma warning disable 0414
|
||||||
|
[SerializeField]
|
||||||
|
bool ObservedComponentsFoldoutOpen = true;
|
||||||
|
#pragma warning restore 0414
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
private int viewIdField = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the PhotonView. Identifies it in a networked game (per room).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>See: [Network Instantiation](@ref instantiateManual)</remarks>
|
||||||
|
public int viewID
|
||||||
|
{
|
||||||
|
get { return this.viewIdField; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// if ID was 0 for an awakened PhotonView, the view should add itself into the networkingPeer.photonViewList after setup
|
||||||
|
bool viewMustRegister = this.didAwake && this.viewIdField == 0;
|
||||||
|
|
||||||
|
// TODO: decide if a viewID can be changed once it wasn't 0. most likely that is not a good idea
|
||||||
|
// check if this view is in networkingPeer.photonViewList and UPDATE said list (so we don't keep the old viewID with a reference to this object)
|
||||||
|
// PhotonNetwork.networkingPeer.RemovePhotonView(this, true);
|
||||||
|
|
||||||
|
this.ownerId = value / PhotonNetwork.MAX_VIEW_IDS;
|
||||||
|
|
||||||
|
this.viewIdField = value;
|
||||||
|
|
||||||
|
if (viewMustRegister)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.RegisterPhotonView(this);
|
||||||
|
}
|
||||||
|
//Debug.Log("Set viewID: " + value + " -> owner: " + this.ownerId + " subId: " + this.subId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int instantiationId; // if the view was instantiated with a GO, this GO has a instantiationID (first view's viewID)
|
||||||
|
|
||||||
|
/// <summary>True if the PhotonView was loaded with the scene (game object) or instantiated with InstantiateSceneObject.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Scene objects are not owned by a particular player but belong to the scene. Thus they don't get destroyed when their
|
||||||
|
/// creator leaves the game and the current Master Client can control them (whoever that is).
|
||||||
|
/// The ownerId is 0 (player IDs are 1 and up).
|
||||||
|
/// </remarks>
|
||||||
|
public bool isSceneView
|
||||||
|
{
|
||||||
|
get { return this.CreatorActorNr == 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The owner of a PhotonView is the player who created the GameObject with that view. Objects in the scene don't have an owner.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The owner/controller of a PhotonView is also the client which sends position updates of the GameObject.
|
||||||
|
///
|
||||||
|
/// Ownership can be transferred to another player with PhotonView.TransferOwnership or any player can request
|
||||||
|
/// ownership by calling the PhotonView's RequestOwnership method.
|
||||||
|
/// The current owner has to implement IPunCallbacks.OnOwnershipRequest to react to the ownership request.
|
||||||
|
/// </remarks>
|
||||||
|
public PhotonPlayer owner
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return PhotonPlayer.Find(this.ownerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int OwnerActorNr
|
||||||
|
{
|
||||||
|
get { return this.ownerId; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool isOwnerActive
|
||||||
|
{
|
||||||
|
get { return this.ownerId != 0 && PhotonNetwork.networkingPeer.mActors.ContainsKey(this.ownerId); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CreatorActorNr
|
||||||
|
{
|
||||||
|
get { return this.viewIdField / PhotonNetwork.MAX_VIEW_IDS; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if the PhotonView is "mine" and can be controlled by this client.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// PUN has an ownership concept that defines who can control and destroy each PhotonView.
|
||||||
|
/// True in case the owner matches the local PhotonPlayer.
|
||||||
|
/// True if this is a scene photonview on the Master client.
|
||||||
|
/// </remarks>
|
||||||
|
public bool isMine
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (this.ownerId == PhotonNetwork.player.ID) || (!this.isOwnerActive && PhotonNetwork.isMasterClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current master ID so that we can compare when we receive OnMasterClientSwitched() callback
|
||||||
|
/// It's public so that we can check it during ownerId assignments in networkPeer script
|
||||||
|
/// TODO: Maybe we can have the networkPeer always aware of the previous MasterClient?
|
||||||
|
/// </summary>
|
||||||
|
public int currentMasterID = -1;
|
||||||
|
protected internal bool didAwake;
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
protected internal bool isRuntimeInstantiated;
|
||||||
|
|
||||||
|
protected internal bool removedFromLocalViewList;
|
||||||
|
|
||||||
|
internal MonoBehaviour[] RpcMonoBehaviours;
|
||||||
|
private MethodInfo OnSerializeMethodInfo;
|
||||||
|
|
||||||
|
private bool failedToFindOnSerialize;
|
||||||
|
|
||||||
|
/// <summary>Called by Unity on start of the application and does a setup the PhotonView.</summary>
|
||||||
|
protected internal void Awake()
|
||||||
|
{
|
||||||
|
if (this.viewID != 0)
|
||||||
|
{
|
||||||
|
// registration might be too late when some script (on this GO) searches this view BUT GetPhotonView() can search ALL in that case
|
||||||
|
PhotonNetwork.networkingPeer.RegisterPhotonView(this);
|
||||||
|
this.instantiationDataField = PhotonNetwork.networkingPeer.FetchInstantiationData(this.instantiationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.didAwake = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Depending on the PhotonView's ownershipTransfer setting, any client can request to become owner of the PhotonView.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Requesting ownership can give you control over a PhotonView, if the ownershipTransfer setting allows that.
|
||||||
|
/// The current owner might have to implement IPunCallbacks.OnOwnershipRequest to react to the ownership request.
|
||||||
|
///
|
||||||
|
/// The owner/controller of a PhotonView is also the client which sends position updates of the GameObject.
|
||||||
|
/// </remarks>
|
||||||
|
public void RequestOwnership()
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.RequestOwnership(this.viewID, this.ownerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transfers the ownership of this PhotonView (and GameObject) to another player.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The owner/controller of a PhotonView is also the client which sends position updates of the GameObject.
|
||||||
|
/// </remarks>
|
||||||
|
public void TransferOwnership(PhotonPlayer newOwner)
|
||||||
|
{
|
||||||
|
this.TransferOwnership(newOwner.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transfers the ownership of this PhotonView (and GameObject) to another player.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The owner/controller of a PhotonView is also the client which sends position updates of the GameObject.
|
||||||
|
/// </remarks>
|
||||||
|
public void TransferOwnership(int newOwnerId)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.TransferOwnership(this.viewID, newOwnerId);
|
||||||
|
this.ownerId = newOwnerId; // immediately switch ownership locally, to avoid more updates sent from this client.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///Check ownerId assignment for sceneObjects to keep being owned by the MasterClient.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newMasterClient">New master client.</param>
|
||||||
|
public void OnMasterClientSwitched(PhotonPlayer newMasterClient)
|
||||||
|
{
|
||||||
|
if (this.CreatorActorNr == 0 && !this.OwnerShipWasTransfered && (this.currentMasterID== -1 || this.ownerId==this.currentMasterID))
|
||||||
|
{
|
||||||
|
this.ownerId = newMasterClient.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentMasterID = newMasterClient.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected internal void OnDestroy()
|
||||||
|
{
|
||||||
|
if (!this.removedFromLocalViewList)
|
||||||
|
{
|
||||||
|
bool wasInList = PhotonNetwork.networkingPeer.LocalCleanPhotonView(this);
|
||||||
|
bool loading = false;
|
||||||
|
|
||||||
|
#if (!UNITY_5 || UNITY_5_0 || UNITY_5_1) && !UNITY_5_3_OR_NEWER
|
||||||
|
loading = Application.isLoadingLevel;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (wasInList && !loading && this.instantiationId > 0 && !PhotonHandler.AppQuits && PhotonNetwork.logLevel >= PhotonLogLevel.Informational)
|
||||||
|
{
|
||||||
|
Debug.Log("PUN-instantiated '" + this.gameObject.name + "' got destroyed by engine. This is OK when loading levels. Otherwise use: PhotonNetwork.Destroy().");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SerializeView(PhotonStream stream, PhotonMessageInfo info)
|
||||||
|
{
|
||||||
|
if (this.ObservedComponents != null && this.ObservedComponents.Count > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < this.ObservedComponents.Count; ++i)
|
||||||
|
{
|
||||||
|
SerializeComponent(this.ObservedComponents[i], stream, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeserializeView(PhotonStream stream, PhotonMessageInfo info)
|
||||||
|
{
|
||||||
|
if (this.ObservedComponents != null && this.ObservedComponents.Count > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < this.ObservedComponents.Count; ++i)
|
||||||
|
{
|
||||||
|
DeserializeComponent(this.ObservedComponents[i], stream, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal void DeserializeComponent(Component component, PhotonStream stream, PhotonMessageInfo info)
|
||||||
|
{
|
||||||
|
if (component == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use incoming data according to observed type
|
||||||
|
if (component is MonoBehaviour)
|
||||||
|
{
|
||||||
|
ExecuteComponentOnSerialize(component, stream, info);
|
||||||
|
}
|
||||||
|
else if (component is Transform)
|
||||||
|
{
|
||||||
|
Transform trans = (Transform) component;
|
||||||
|
|
||||||
|
switch (this.onSerializeTransformOption)
|
||||||
|
{
|
||||||
|
case OnSerializeTransform.All:
|
||||||
|
trans.localPosition = (Vector3) stream.ReceiveNext();
|
||||||
|
trans.localRotation = (Quaternion) stream.ReceiveNext();
|
||||||
|
trans.localScale = (Vector3) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
case OnSerializeTransform.OnlyPosition:
|
||||||
|
trans.localPosition = (Vector3) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
case OnSerializeTransform.OnlyRotation:
|
||||||
|
trans.localRotation = (Quaternion) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
case OnSerializeTransform.OnlyScale:
|
||||||
|
trans.localScale = (Vector3) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
case OnSerializeTransform.PositionAndRotation:
|
||||||
|
trans.localPosition = (Vector3) stream.ReceiveNext();
|
||||||
|
trans.localRotation = (Quaternion) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (component is Rigidbody)
|
||||||
|
{
|
||||||
|
Rigidbody rigidB = (Rigidbody) component;
|
||||||
|
|
||||||
|
switch (this.onSerializeRigidBodyOption)
|
||||||
|
{
|
||||||
|
case OnSerializeRigidBody.All:
|
||||||
|
rigidB.velocity = (Vector3) stream.ReceiveNext();
|
||||||
|
rigidB.angularVelocity = (Vector3) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
case OnSerializeRigidBody.OnlyAngularVelocity:
|
||||||
|
rigidB.angularVelocity = (Vector3) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
case OnSerializeRigidBody.OnlyVelocity:
|
||||||
|
rigidB.velocity = (Vector3) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (component is Rigidbody2D)
|
||||||
|
{
|
||||||
|
Rigidbody2D rigidB = (Rigidbody2D) component;
|
||||||
|
|
||||||
|
switch (this.onSerializeRigidBodyOption)
|
||||||
|
{
|
||||||
|
case OnSerializeRigidBody.All:
|
||||||
|
rigidB.velocity = (Vector2) stream.ReceiveNext();
|
||||||
|
rigidB.angularVelocity = (float) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
case OnSerializeRigidBody.OnlyAngularVelocity:
|
||||||
|
rigidB.angularVelocity = (float) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
case OnSerializeRigidBody.OnlyVelocity:
|
||||||
|
rigidB.velocity = (Vector2) stream.ReceiveNext();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError("Type of observed is unknown when receiving.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal void SerializeComponent(Component component, PhotonStream stream, PhotonMessageInfo info)
|
||||||
|
{
|
||||||
|
if (component == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component is MonoBehaviour)
|
||||||
|
{
|
||||||
|
ExecuteComponentOnSerialize(component, stream, info);
|
||||||
|
}
|
||||||
|
else if (component is Transform)
|
||||||
|
{
|
||||||
|
Transform trans = (Transform) component;
|
||||||
|
|
||||||
|
switch (this.onSerializeTransformOption)
|
||||||
|
{
|
||||||
|
case OnSerializeTransform.All:
|
||||||
|
stream.SendNext(trans.localPosition);
|
||||||
|
stream.SendNext(trans.localRotation);
|
||||||
|
stream.SendNext(trans.localScale);
|
||||||
|
break;
|
||||||
|
case OnSerializeTransform.OnlyPosition:
|
||||||
|
stream.SendNext(trans.localPosition);
|
||||||
|
break;
|
||||||
|
case OnSerializeTransform.OnlyRotation:
|
||||||
|
stream.SendNext(trans.localRotation);
|
||||||
|
break;
|
||||||
|
case OnSerializeTransform.OnlyScale:
|
||||||
|
stream.SendNext(trans.localScale);
|
||||||
|
break;
|
||||||
|
case OnSerializeTransform.PositionAndRotation:
|
||||||
|
stream.SendNext(trans.localPosition);
|
||||||
|
stream.SendNext(trans.localRotation);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (component is Rigidbody)
|
||||||
|
{
|
||||||
|
Rigidbody rigidB = (Rigidbody) component;
|
||||||
|
|
||||||
|
switch (this.onSerializeRigidBodyOption)
|
||||||
|
{
|
||||||
|
case OnSerializeRigidBody.All:
|
||||||
|
stream.SendNext(rigidB.velocity);
|
||||||
|
stream.SendNext(rigidB.angularVelocity);
|
||||||
|
break;
|
||||||
|
case OnSerializeRigidBody.OnlyAngularVelocity:
|
||||||
|
stream.SendNext(rigidB.angularVelocity);
|
||||||
|
break;
|
||||||
|
case OnSerializeRigidBody.OnlyVelocity:
|
||||||
|
stream.SendNext(rigidB.velocity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (component is Rigidbody2D)
|
||||||
|
{
|
||||||
|
Rigidbody2D rigidB = (Rigidbody2D) component;
|
||||||
|
|
||||||
|
switch (this.onSerializeRigidBodyOption)
|
||||||
|
{
|
||||||
|
case OnSerializeRigidBody.All:
|
||||||
|
stream.SendNext(rigidB.velocity);
|
||||||
|
stream.SendNext(rigidB.angularVelocity);
|
||||||
|
break;
|
||||||
|
case OnSerializeRigidBody.OnlyAngularVelocity:
|
||||||
|
stream.SendNext(rigidB.angularVelocity);
|
||||||
|
break;
|
||||||
|
case OnSerializeRigidBody.OnlyVelocity:
|
||||||
|
stream.SendNext(rigidB.velocity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError("Observed type is not serializable: " + component.GetType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal void ExecuteComponentOnSerialize(Component component, PhotonStream stream, PhotonMessageInfo info)
|
||||||
|
{
|
||||||
|
IPunObservable observable = component as IPunObservable;
|
||||||
|
if (observable != null)
|
||||||
|
{
|
||||||
|
observable.OnPhotonSerializeView(stream, info);
|
||||||
|
}
|
||||||
|
else if (component != null)
|
||||||
|
{
|
||||||
|
MethodInfo method = null;
|
||||||
|
bool found = this.m_OnSerializeMethodInfos.TryGetValue(component, out method);
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
bool foundMethod = NetworkingPeer.GetMethod(component as MonoBehaviour, PhotonNetworkingMessage.OnPhotonSerializeView.ToString(), out method);
|
||||||
|
|
||||||
|
if (foundMethod == false)
|
||||||
|
{
|
||||||
|
Debug.LogError("The observed monobehaviour (" + component.name + ") of this PhotonView does not implement OnPhotonSerializeView()!");
|
||||||
|
method = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.m_OnSerializeMethodInfos.Add(component, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
method.Invoke(component, new object[] {stream, info});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can be used to refesh the list of MonoBehaviours on this GameObject while PhotonNetwork.UseRpcMonoBehaviourCache is true.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Set PhotonNetwork.UseRpcMonoBehaviourCache to true to enable the caching.
|
||||||
|
/// Uses this.GetComponents<MonoBehaviour>() to get a list of MonoBehaviours to call RPCs on (potentially).
|
||||||
|
///
|
||||||
|
/// While PhotonNetwork.UseRpcMonoBehaviourCache is false, this method has no effect,
|
||||||
|
/// because the list is refreshed when a RPC gets called.
|
||||||
|
/// </remarks>
|
||||||
|
public void RefreshRpcMonoBehaviourCache()
|
||||||
|
{
|
||||||
|
this.RpcMonoBehaviours = this.GetComponents<MonoBehaviour>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Call a RPC method of this GameObject on remote clients of this room (or on all, inclunding this client).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// [Remote Procedure Calls](@ref rpcManual) are an essential tool in making multiplayer games with PUN.
|
||||||
|
/// It enables you to make every client in a room call a specific method.
|
||||||
|
///
|
||||||
|
/// RPC calls can target "All" or the "Others".
|
||||||
|
/// Usually, the target "All" gets executed locally immediately after sending the RPC.
|
||||||
|
/// The "*ViaServer" options send the RPC to the server and execute it on this client when it's sent back.
|
||||||
|
/// Of course, calls are affected by this client's lag and that of remote clients.
|
||||||
|
///
|
||||||
|
/// Each call automatically is routed to the same PhotonView (and GameObject) that was used on the
|
||||||
|
/// originating client.
|
||||||
|
///
|
||||||
|
/// See: [Remote Procedure Calls](@ref rpcManual).
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="methodName">The name of a fitting method that was has the RPC attribute.</param>
|
||||||
|
/// <param name="target">The group of targets and the way the RPC gets sent.</param>
|
||||||
|
/// <param name="parameters">The parameters that the RPC method has (must fit this call!).</param>
|
||||||
|
public void RPC(string methodName, PhotonTargets target, params object[] parameters)
|
||||||
|
{
|
||||||
|
PhotonNetwork.RPC(this, methodName, target, false, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Call a RPC method of this GameObject on remote clients of this room (or on all, inclunding this client).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// [Remote Procedure Calls](@ref rpcManual) are an essential tool in making multiplayer games with PUN.
|
||||||
|
/// It enables you to make every client in a room call a specific method.
|
||||||
|
///
|
||||||
|
/// RPC calls can target "All" or the "Others".
|
||||||
|
/// Usually, the target "All" gets executed locally immediately after sending the RPC.
|
||||||
|
/// The "*ViaServer" options send the RPC to the server and execute it on this client when it's sent back.
|
||||||
|
/// Of course, calls are affected by this client's lag and that of remote clients.
|
||||||
|
///
|
||||||
|
/// Each call automatically is routed to the same PhotonView (and GameObject) that was used on the
|
||||||
|
/// originating client.
|
||||||
|
///
|
||||||
|
/// See: [Remote Procedure Calls](@ref rpcManual).
|
||||||
|
/// </remarks>
|
||||||
|
///<param name="methodName">The name of a fitting method that was has the RPC attribute.</param>
|
||||||
|
///<param name="target">The group of targets and the way the RPC gets sent.</param>
|
||||||
|
///<param name="encrypt"> </param>
|
||||||
|
///<param name="parameters">The parameters that the RPC method has (must fit this call!).</param>
|
||||||
|
public void RpcSecure(string methodName, PhotonTargets target, bool encrypt, params object[] parameters)
|
||||||
|
{
|
||||||
|
PhotonNetwork.RPC(this, methodName, target, encrypt, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Call a RPC method of this GameObject on remote clients of this room (or on all, inclunding this client).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// [Remote Procedure Calls](@ref rpcManual) are an essential tool in making multiplayer games with PUN.
|
||||||
|
/// It enables you to make every client in a room call a specific method.
|
||||||
|
///
|
||||||
|
/// This method allows you to make an RPC calls on a specific player's client.
|
||||||
|
/// Of course, calls are affected by this client's lag and that of remote clients.
|
||||||
|
///
|
||||||
|
/// Each call automatically is routed to the same PhotonView (and GameObject) that was used on the
|
||||||
|
/// originating client.
|
||||||
|
///
|
||||||
|
/// See: [Remote Procedure Calls](@ref rpcManual).
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="methodName">The name of a fitting method that was has the RPC attribute.</param>
|
||||||
|
/// <param name="targetPlayer">The group of targets and the way the RPC gets sent.</param>
|
||||||
|
/// <param name="parameters">The parameters that the RPC method has (must fit this call!).</param>
|
||||||
|
public void RPC(string methodName, PhotonPlayer targetPlayer, params object[] parameters)
|
||||||
|
{
|
||||||
|
PhotonNetwork.RPC(this, methodName, targetPlayer, false, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Call a RPC method of this GameObject on remote clients of this room (or on all, inclunding this client).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// [Remote Procedure Calls](@ref rpcManual) are an essential tool in making multiplayer games with PUN.
|
||||||
|
/// It enables you to make every client in a room call a specific method.
|
||||||
|
///
|
||||||
|
/// This method allows you to make an RPC calls on a specific player's client.
|
||||||
|
/// Of course, calls are affected by this client's lag and that of remote clients.
|
||||||
|
///
|
||||||
|
/// Each call automatically is routed to the same PhotonView (and GameObject) that was used on the
|
||||||
|
/// originating client.
|
||||||
|
///
|
||||||
|
/// See: [Remote Procedure Calls](@ref rpcManual).
|
||||||
|
/// </remarks>
|
||||||
|
///<param name="methodName">The name of a fitting method that was has the RPC attribute.</param>
|
||||||
|
///<param name="targetPlayer">The group of targets and the way the RPC gets sent.</param>
|
||||||
|
///<param name="encrypt"> </param>
|
||||||
|
///<param name="parameters">The parameters that the RPC method has (must fit this call!).</param>
|
||||||
|
public void RpcSecure(string methodName, PhotonPlayer targetPlayer, bool encrypt, params object[] parameters)
|
||||||
|
{
|
||||||
|
PhotonNetwork.RPC(this, methodName, targetPlayer, encrypt, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PhotonView Get(Component component)
|
||||||
|
{
|
||||||
|
return component.GetComponent<PhotonView>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PhotonView Get(GameObject gameObj)
|
||||||
|
{
|
||||||
|
return gameObj.GetComponent<PhotonView>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PhotonView Find(int viewID)
|
||||||
|
{
|
||||||
|
return PhotonNetwork.networkingPeer.GetPhotonView(viewID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("View ({3}){0} on {1} {2}", this.viewID, (this.gameObject != null) ? this.gameObject.name : "GO==null", (this.isSceneView) ? "(scene)" : string.Empty, this.prefix);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aa584fbee541324448dd18d8409c7a41
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,352 @@
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
using UnityEngine;
|
||||||
|
using Debug = UnityEngine.Debug;
|
||||||
|
using SupportClassPun = ExitGames.Client.Photon.SupportClass;
|
||||||
|
|
||||||
|
|
||||||
|
#if UNITY_EDITOR || (!UNITY_ANDROID && !UNITY_IPHONE && !UNITY_PS3 && !UNITY_WINRT)
|
||||||
|
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Uses C# Socket class from System.Net.Sockets (as Unity usually does).</summary>
|
||||||
|
/// <remarks>Incompatible with Windows 8 Store/Phone API.</remarks>
|
||||||
|
public class PingMonoEditor : PhotonPing
|
||||||
|
{
|
||||||
|
private Socket sock;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a "Photon Ping" to a server.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ip">Address in IPv4 or IPv6 format. An address containing a '.' will be interpretet as IPv4.</param>
|
||||||
|
/// <returns>True if the Photon Ping could be sent.</returns>
|
||||||
|
public override bool StartPing(string ip)
|
||||||
|
{
|
||||||
|
base.Init();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (ip.Contains("."))
|
||||||
|
{
|
||||||
|
this.sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.sock = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
|
||||||
|
}
|
||||||
|
|
||||||
|
sock.ReceiveTimeout = 5000;
|
||||||
|
sock.Connect(ip, 5055);
|
||||||
|
|
||||||
|
PingBytes[PingBytes.Length - 1] = PingId;
|
||||||
|
sock.Send(PingBytes);
|
||||||
|
PingBytes[PingBytes.Length - 1] = (byte)(PingId - 1);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
sock = null;
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Done()
|
||||||
|
{
|
||||||
|
if (this.GotResult || sock == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sock.Available <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read = sock.Receive(PingBytes, SocketFlags.None);
|
||||||
|
//Debug.Log("Got: " + SupportClassPun.ByteArrayToString(PingBytes));
|
||||||
|
bool replyMatch = PingBytes[PingBytes.Length - 1] == PingId && read == PingLength;
|
||||||
|
if (!replyMatch) Debug.Log("ReplyMatch is false! ");
|
||||||
|
|
||||||
|
|
||||||
|
this.Successful = read == PingBytes.Length && PingBytes[PingBytes.Length - 1] == PingId;
|
||||||
|
this.GotResult = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sock.Close();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
sock = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if UNITY_WEBGL
|
||||||
|
|
||||||
|
public class PingHttp : PhotonPing
|
||||||
|
{
|
||||||
|
private WWW webRequest;
|
||||||
|
|
||||||
|
public override bool StartPing(string address)
|
||||||
|
{
|
||||||
|
address = "https://" + address + "/photon/m/?ping&r=" + UnityEngine.Random.Range(0, 10000);
|
||||||
|
Debug.Log("StartPing: " + address);
|
||||||
|
this.webRequest = new WWW(address);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Done()
|
||||||
|
{
|
||||||
|
if (this.webRequest.isDone)
|
||||||
|
{
|
||||||
|
Successful = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
this.webRequest.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
public class PhotonPingManager
|
||||||
|
{
|
||||||
|
public bool UseNative;
|
||||||
|
public static int Attempts = 5;
|
||||||
|
public static bool IgnoreInitialAttempt = true;
|
||||||
|
public static int MaxMilliseconsPerPing = 800; // enter a value you're sure some server can beat (have a lower rtt)
|
||||||
|
|
||||||
|
private const string wssProtocolString = "wss://";
|
||||||
|
|
||||||
|
public Region BestRegion
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Region result = null;
|
||||||
|
int bestRtt = Int32.MaxValue;
|
||||||
|
foreach (Region region in PhotonNetwork.networkingPeer.AvailableRegions)
|
||||||
|
{
|
||||||
|
Debug.Log("BestRegion checks region: " + region);
|
||||||
|
if (region.Ping != 0 && region.Ping < bestRtt)
|
||||||
|
{
|
||||||
|
bestRtt = region.Ping;
|
||||||
|
result = region;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Region)result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Done
|
||||||
|
{
|
||||||
|
get { return this.PingsRunning == 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private int PingsRunning;
|
||||||
|
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// Affected by frame-rate of app, as this Coroutine checks the socket for a result once per frame.
|
||||||
|
/// </remarks>
|
||||||
|
public IEnumerator PingSocket(Region region)
|
||||||
|
{
|
||||||
|
region.Ping = Attempts*MaxMilliseconsPerPing;
|
||||||
|
|
||||||
|
this.PingsRunning++; // TODO: Add try-catch to make sure the PingsRunning are reduced at the end and that the lib does not crash the app
|
||||||
|
PhotonPing ping;
|
||||||
|
if (PhotonHandler.PingImplementation == typeof(PingNativeDynamic))
|
||||||
|
{
|
||||||
|
Debug.Log("Using constructor for new PingNativeDynamic()"); // it seems on Android, the Activator can't find the default Constructor
|
||||||
|
ping = new PingNativeDynamic();
|
||||||
|
}
|
||||||
|
else if(PhotonHandler.PingImplementation == typeof(PingNativeStatic))
|
||||||
|
{
|
||||||
|
Debug.Log("Using constructor for new PingNativeStatic()"); // it seems on Switch, the Activator can't find the default Constructor
|
||||||
|
ping = new PingNativeStatic();
|
||||||
|
}
|
||||||
|
else if (PhotonHandler.PingImplementation == typeof(PingMono))
|
||||||
|
{
|
||||||
|
ping = new PingMono(); // using this type explicitly saves it from IL2CPP bytecode stripping
|
||||||
|
}
|
||||||
|
#if UNITY_WEBGL
|
||||||
|
else if (PhotonHandler.PingImplementation == typeof(PingHttp))
|
||||||
|
{
|
||||||
|
ping = new PingHttp();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ping = (PhotonPing)Activator.CreateInstance(PhotonHandler.PingImplementation);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Debug.Log(region);
|
||||||
|
|
||||||
|
float rttSum = 0.0f;
|
||||||
|
int replyCount = 0;
|
||||||
|
|
||||||
|
// all addresses for Photon region servers will contain a :port ending. this needs to be removed first.
|
||||||
|
// PhotonPing.StartPing() requires a plain (IP) address without port or protocol-prefix (on all but Windows 8.1 and WebGL platforms).
|
||||||
|
|
||||||
|
string regionAddress = region.HostAndPort;
|
||||||
|
int indexOfColon = regionAddress.LastIndexOf(':');
|
||||||
|
if (indexOfColon > 1)
|
||||||
|
{
|
||||||
|
regionAddress = regionAddress.Substring(0, indexOfColon);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we also need to remove the protocol or Dns.GetHostAddresses(hostName) will throw an exception
|
||||||
|
// This is for xBox One for example.
|
||||||
|
int indexOfProtocol = regionAddress.IndexOf(PhotonPingManager.wssProtocolString);
|
||||||
|
if (indexOfProtocol > -1)
|
||||||
|
{
|
||||||
|
regionAddress = regionAddress.Substring(indexOfProtocol+PhotonPingManager.wssProtocolString.Length);
|
||||||
|
}
|
||||||
|
regionAddress = ResolveHost(regionAddress);
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < Attempts; i++)
|
||||||
|
{
|
||||||
|
bool overtime = false;
|
||||||
|
Stopwatch sw = new Stopwatch();
|
||||||
|
sw.Start();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ping.StartPing(regionAddress);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Log("catched: " + e);
|
||||||
|
this.PingsRunning--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
while (!ping.Done())
|
||||||
|
{
|
||||||
|
if (sw.ElapsedMilliseconds >= MaxMilliseconsPerPing)
|
||||||
|
{
|
||||||
|
overtime = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
yield return 0; // keep this loop tight, to avoid adding local lag to rtt.
|
||||||
|
}
|
||||||
|
int rtt = (int)sw.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
|
||||||
|
if (IgnoreInitialAttempt && i == 0)
|
||||||
|
{
|
||||||
|
// do nothing.
|
||||||
|
}
|
||||||
|
else if (ping.Successful && !overtime)
|
||||||
|
{
|
||||||
|
rttSum += rtt;
|
||||||
|
replyCount++;
|
||||||
|
region.Ping = (int)((rttSum) / replyCount);
|
||||||
|
//Debug.Log("region " + region.Code + " RTT " + region.Ping + " success: " + ping.Successful + " over: " + overtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return new WaitForSeconds(0.1f);
|
||||||
|
}
|
||||||
|
ping.Dispose();
|
||||||
|
|
||||||
|
this.PingsRunning--;
|
||||||
|
|
||||||
|
//Debug.Log("this.PingsRunning: " + this.PingsRunning + " this debug: " + ping.DebugString);
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (UNITY_WINRT && !UNITY_EDITOR) || UNITY_WEBGL
|
||||||
|
|
||||||
|
public static string ResolveHost(string hostName)
|
||||||
|
{
|
||||||
|
#if UNITY_WEBGL
|
||||||
|
if (hostName.StartsWith("wss://"))
|
||||||
|
{
|
||||||
|
hostName = hostName.Substring(6);
|
||||||
|
}
|
||||||
|
if (hostName.StartsWith("ws://"))
|
||||||
|
{
|
||||||
|
hostName = hostName.Substring(5);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return hostName;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to resolve a hostname into an IP string or returns empty string if that fails.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// To be compatible with most platforms, the address family is checked like this:</br>
|
||||||
|
/// if (ipAddress.AddressFamily.ToString().Contains("6")) // ipv6...
|
||||||
|
/// </reamrks>
|
||||||
|
/// <param name="hostName">Hostname to resolve.</param>
|
||||||
|
/// <returns>IP string or empty string if resolution fails</returns>
|
||||||
|
public static string ResolveHost(string hostName)
|
||||||
|
{
|
||||||
|
string ipv4Address = string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IPAddress[] address = Dns.GetHostAddresses(hostName);
|
||||||
|
//foreach (IPAddress adr in address)
|
||||||
|
//{
|
||||||
|
// Debug.Log(hostName + " -> Adress: " + adr + " family: " + adr.AddressFamily.ToString());
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (address.Length == 1)
|
||||||
|
{
|
||||||
|
return address[0].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got more addresses, try to pick a IPv4 one
|
||||||
|
for (int index = 0; index < address.Length; index++)
|
||||||
|
{
|
||||||
|
IPAddress ipAddress = address[index];
|
||||||
|
if (ipAddress != null)
|
||||||
|
{
|
||||||
|
// checking ipAddress.ToString() means we don't have to import System.Net.Sockets, which is not available on some platforms (Metro)
|
||||||
|
if (ipAddress.ToString().Contains(":"))
|
||||||
|
{
|
||||||
|
return ipAddress.ToString();
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(ipv4Address))
|
||||||
|
{
|
||||||
|
ipv4Address = address.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception e)
|
||||||
|
{
|
||||||
|
Debug.Log("Exception caught! " + e.Source + " Message: " + e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv4Address;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 207807222df026f40ac3688a3a051e38
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
#pragma warning disable 1587
|
||||||
|
/// \file
|
||||||
|
/// <summary>Reimplements a RPC Attribute, as it's no longer in all versions of the UnityEngine assembly.</summary>
|
||||||
|
#pragma warning restore 1587
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
/// <summary>Replacement for RPC attribute with different name. Used to flag methods as remote-callable.</summary>
|
||||||
|
public class PunRPC : Attribute
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c40d255b1edd61842ae10e3346a2251e
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,366 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="Room.cs" company="Exit Games GmbH">
|
||||||
|
// PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// Represents a room/game on the server and caches the properties of that.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class resembles a room that PUN joins (or joined).
|
||||||
|
/// The properties are settable as opposed to those of a RoomInfo and you can close or hide "your" room.
|
||||||
|
/// </summary>
|
||||||
|
/// \ingroup publicApi
|
||||||
|
public class Room : RoomInfo
|
||||||
|
{
|
||||||
|
/// <summary>The name of a room. Unique identifier (per Loadbalancing group) for a room/match.</summary>
|
||||||
|
public new string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.nameField;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
this.nameField = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines if the room can be joined.
|
||||||
|
/// This does not affect listing in a lobby but joining the room will fail if not open.
|
||||||
|
/// If not open, the room is excluded from random matchmaking.
|
||||||
|
/// Due to racing conditions, found matches might become closed before they are joined.
|
||||||
|
/// Simply re-connect to master and find another.
|
||||||
|
/// Use property "visible" to not list the room.
|
||||||
|
/// </summary>
|
||||||
|
public new bool IsOpen
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.openField;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!this.Equals(PhotonNetwork.room))
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogWarning("Can't set open when not in that room.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != this.openField && !PhotonNetwork.offlineMode)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.OpSetPropertiesOfRoom(new Hashtable() { { GamePropertyKey.IsOpen, value } }, expectedProperties: null, webForward: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.openField = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines if the room is listed in its lobby.
|
||||||
|
/// Rooms can be created invisible, or changed to invisible.
|
||||||
|
/// To change if a room can be joined, use property: open.
|
||||||
|
/// </summary>
|
||||||
|
public new bool IsVisible
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.visibleField;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!this.Equals(PhotonNetwork.room))
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogWarning("Can't set visible when not in that room.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != this.visibleField && !PhotonNetwork.offlineMode)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.OpSetPropertiesOfRoom(new Hashtable() { { GamePropertyKey.IsVisible, value } }, expectedProperties: null, webForward: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.visibleField = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of custom properties that should be forwarded to the lobby and listed there.
|
||||||
|
/// </summary>
|
||||||
|
public string[] PropertiesListedInLobby { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets if this room uses autoCleanUp to remove all (buffered) RPCs and instantiated GameObjects when a player leaves.
|
||||||
|
/// </summary>
|
||||||
|
public bool AutoCleanUp
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.autoCleanUpField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a limit of players to this room. This property is shown in lobby, too.
|
||||||
|
/// If the room is full (players count == maxplayers), joining this room will fail.
|
||||||
|
/// </summary>
|
||||||
|
public new int MaxPlayers
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (int)this.maxPlayersField;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!this.Equals(PhotonNetwork.room))
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogWarning("Can't set MaxPlayers when not in that room.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value > 255)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogWarning("Can't set Room.MaxPlayers to: " + value + ". Using max value: 255.");
|
||||||
|
value = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != this.maxPlayersField && !PhotonNetwork.offlineMode)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.OpSetPropertiesOfRoom(new Hashtable() { { GamePropertyKey.MaxPlayers, (byte)value } }, expectedProperties: null, webForward: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.maxPlayersField = (byte)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Count of players in this room.</summary>
|
||||||
|
public new int PlayerCount
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (PhotonNetwork.playerList != null)
|
||||||
|
{
|
||||||
|
return PhotonNetwork.playerList.Length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of users who are expected to join this room. In matchmaking, Photon blocks a slot for each of these UserIDs out of the MaxPlayers.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The corresponding feature in Photon is called "Slot Reservation" and can be found in the doc pages.
|
||||||
|
/// Define expected players in the PhotonNetwork methods: CreateRoom, JoinRoom and JoinOrCreateRoom.
|
||||||
|
/// </remarks>
|
||||||
|
public string[] ExpectedUsers
|
||||||
|
{
|
||||||
|
get { return this.expectedUsersField; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The ID (actorNumber) of the current Master Client of this room.</summary>
|
||||||
|
/// <remarks>See also: PhotonNetwork.masterClient.</remarks>
|
||||||
|
protected internal int MasterClientId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.masterClientIdField;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.masterClientIdField = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal Room(string roomName, RoomOptions options) : base(roomName, null)
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
options = new RoomOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.visibleField = options.IsVisible;
|
||||||
|
this.openField = options.IsOpen;
|
||||||
|
this.maxPlayersField = (byte)options.MaxPlayers;
|
||||||
|
this.autoCleanUpField = false; // defaults to false, unless set to true when room gets created.
|
||||||
|
|
||||||
|
this.InternalCacheProperties(options.CustomRoomProperties);
|
||||||
|
this.PropertiesListedInLobby = options.CustomRoomPropertiesForLobby;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the current room's Custom Properties with new/updated key-values.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Custom Properties are a key-value set (Hashtable) which is available to all players in a room.
|
||||||
|
/// They can relate to the room or individual players and are useful when only the current value
|
||||||
|
/// of something is of interest. For example: The map of a room.
|
||||||
|
/// All keys must be strings.
|
||||||
|
///
|
||||||
|
/// The Room and the PhotonPlayer class both have SetCustomProperties methods.
|
||||||
|
/// Also, both classes offer access to current key-values by: customProperties.
|
||||||
|
///
|
||||||
|
/// Always use SetCustomProperties to change values.
|
||||||
|
/// To reduce network traffic, set only values that actually changed.
|
||||||
|
/// New properties are added, existing values are updated.
|
||||||
|
/// Other values will not be changed, so only provide values that changed or are new.
|
||||||
|
///
|
||||||
|
/// To delete a named (custom) property of this room, use null as value.
|
||||||
|
///
|
||||||
|
/// Locally, SetCustomProperties will update it's cache without delay.
|
||||||
|
/// Other clients are updated through Photon (the server) with a fitting operation.
|
||||||
|
///
|
||||||
|
/// <b>Check and Swap</b>
|
||||||
|
///
|
||||||
|
/// SetCustomProperties have the option to do a server-side Check-And-Swap (CAS):
|
||||||
|
/// Values only get updated if the expected values are correct.
|
||||||
|
/// The expectedValues can be different key/values than the propertiesToSet. So you can
|
||||||
|
/// check some key and set another key's value (if the check succeeds).
|
||||||
|
///
|
||||||
|
/// If the client's knowledge of properties is wrong or outdated, it can't set values with CAS.
|
||||||
|
/// This can be useful to keep players from concurrently setting values. For example: If all players
|
||||||
|
/// try to pickup some card or item, only one should get it. With CAS, only the first SetProperties
|
||||||
|
/// gets executed server-side and any other (sent at the same time) fails.
|
||||||
|
///
|
||||||
|
/// The server will broadcast successfully changed values and the local "cache" of customProperties
|
||||||
|
/// only gets updated after a roundtrip (if anything changed).
|
||||||
|
///
|
||||||
|
/// You can do a "webForward": Photon will send the changed properties to a WebHook defined
|
||||||
|
/// for your application.
|
||||||
|
///
|
||||||
|
/// <b>OfflineMode</b>
|
||||||
|
///
|
||||||
|
/// While PhotonNetwork.offlineMode is true, the expectedValues and webForward parameters are ignored.
|
||||||
|
/// In OfflineMode, the local customProperties values are immediately updated (without the roundtrip).
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="propertiesToSet">The new properties to be set. </param>
|
||||||
|
/// <param name="expectedValues">At least one property key/value set to check server-side. Key and value must be correct. Ignored in OfflineMode.</param>
|
||||||
|
/// <param name="webForward">Set to true, to forward the set properties to a WebHook, defined for this app (in Dashboard). Ignored in OfflineMode.</param>
|
||||||
|
public void SetCustomProperties(Hashtable propertiesToSet, Hashtable expectedValues = null, bool webForward = false)
|
||||||
|
{
|
||||||
|
if (propertiesToSet == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hashtable customProps = propertiesToSet.StripToStringKeys() as Hashtable;
|
||||||
|
Hashtable customPropsToCheck = expectedValues.StripToStringKeys() as Hashtable;
|
||||||
|
|
||||||
|
|
||||||
|
// no expected values -> set and callback
|
||||||
|
bool noCas = customPropsToCheck == null || customPropsToCheck.Count == 0;
|
||||||
|
|
||||||
|
if (PhotonNetwork.offlineMode || noCas)
|
||||||
|
{
|
||||||
|
this.CustomProperties.Merge(customProps); // the customProps are already stripped to string-keys-only (custom-props keys)
|
||||||
|
this.CustomProperties.StripKeysWithNullValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PhotonNetwork.offlineMode)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.OpSetPropertiesOfRoom(customProps, customPropsToCheck, webForward); // as the customProps are stripped already, this equals OpSetCustomPropertiesOfRoom()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PhotonNetwork.offlineMode || noCas)
|
||||||
|
{
|
||||||
|
NetworkingPeer.SendMonoMessage(PhotonNetworkingMessage.OnPhotonCustomRoomPropertiesChanged, customProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables you to define the properties available in the lobby if not all properties are needed to pick a room.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It makes sense to limit the amount of properties sent to users in the lobby as this improves speed and stability.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="propsListedInLobby">An array of custom room property names to forward to the lobby.</param>
|
||||||
|
public void SetPropertiesListedInLobby(string[] propsListedInLobby)
|
||||||
|
{
|
||||||
|
Hashtable customProps = new Hashtable();
|
||||||
|
customProps[GamePropertyKey.PropsListedInLobby] = propsListedInLobby;
|
||||||
|
PhotonNetwork.networkingPeer.OpSetPropertiesOfRoom(customProps, expectedProperties: null, webForward: false);
|
||||||
|
|
||||||
|
this.PropertiesListedInLobby = propsListedInLobby;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to remove all current expected users from the server's Slot Reservation list.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Note that this operation can conflict with new/other users joining. They might be
|
||||||
|
/// adding users to the list of expected users before or after this client called ClearExpectedUsers.
|
||||||
|
///
|
||||||
|
/// This room's expectedUsers value will update, when the server sends a successful update.
|
||||||
|
///
|
||||||
|
/// Internals: This methods wraps up setting the ExpectedUsers property of a room.
|
||||||
|
/// </remarks>
|
||||||
|
public void ClearExpectedUsers()
|
||||||
|
{
|
||||||
|
Hashtable props = new Hashtable();
|
||||||
|
props[GamePropertyKey.ExpectedUsers] = new string[0];
|
||||||
|
Hashtable expected = new Hashtable();
|
||||||
|
expected[GamePropertyKey.ExpectedUsers] = this.ExpectedUsers;
|
||||||
|
PhotonNetwork.networkingPeer.OpSetPropertiesOfRoom(props, expected, webForward: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Returns a summary of this Room instance as string.</summary>
|
||||||
|
/// <returns>Summary of this Room instance.</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("Room: '{0}' {1},{2} {4}/{3} players.", this.nameField, this.visibleField ? "visible" : "hidden", this.openField ? "open" : "closed", this.maxPlayersField, this.PlayerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns a summary of this Room instance as longer string, including Custom Properties.</summary>
|
||||||
|
/// <returns>Summary of this Room instance.</returns>
|
||||||
|
public new string ToStringFull()
|
||||||
|
{
|
||||||
|
return string.Format("Room: '{0}' {1},{2} {4}/{3} players.\ncustomProps: {5}", this.nameField, this.visibleField ? "visible" : "hidden", this.openField ? "open" : "closed", this.maxPlayersField, this.PlayerCount, this.CustomProperties.ToStringFull());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Obsoleted variable names
|
||||||
|
|
||||||
|
[Obsolete("Please use Name (updated case for naming).")]
|
||||||
|
public new string name { get { return this.Name; } internal set { this.Name = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use IsOpen (updated case for naming).")]
|
||||||
|
public new bool open { get { return this.IsOpen; } set { this.IsOpen = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use IsVisible (updated case for naming).")]
|
||||||
|
public new bool visible { get { return this.IsVisible; } set { this.IsVisible = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use PropertiesListedInLobby (updated case for naming).")]
|
||||||
|
public string[] propertiesListedInLobby { get { return this.PropertiesListedInLobby; } private set { this.PropertiesListedInLobby = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use AutoCleanUp (updated case for naming).")]
|
||||||
|
public bool autoCleanUp { get { return this.AutoCleanUp; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use MaxPlayers (updated case for naming).")]
|
||||||
|
public new int maxPlayers { get { return this.MaxPlayers; } set { this.MaxPlayers = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use PlayerCount (updated case for naming).")]
|
||||||
|
public new int playerCount { get { return this.PlayerCount; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use ExpectedUsers (updated case for naming).")]
|
||||||
|
public string[] expectedUsers { get { return this.ExpectedUsers; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use MasterClientId (updated case for naming).")]
|
||||||
|
protected internal int masterClientId { get { return this.MasterClientId; } set { this.MasterClientId = value; } }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 17568a7a5552c09428dd48e73548b8b8
|
||||||
|
labels:
|
||||||
|
- ExitGames
|
||||||
|
- PUN
|
||||||
|
- Photon
|
||||||
|
- Networking
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
|
@ -0,0 +1,282 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// <copyright file="RoomInfo.cs" company="Exit Games GmbH">
|
||||||
|
// Loadbalancing Framework for Photon - Copyright (C) 2011 Exit Games GmbH
|
||||||
|
// </copyright>
|
||||||
|
// <summary>
|
||||||
|
// This class resembles info about available rooms, as sent by the Master
|
||||||
|
// server's lobby. Consider all values as readonly.
|
||||||
|
// </summary>
|
||||||
|
// <author>developer@exitgames.com</author>
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using ExitGames.Client.Photon;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A simplified room with just the info required to list and join, used for the room listing in the lobby.
|
||||||
|
/// The properties are not settable (open, MaxPlayers, etc).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This class resembles info about available rooms, as sent by the Master server's lobby.
|
||||||
|
/// Consider all values as readonly. None are synced (only updated by events by server).
|
||||||
|
/// </remarks>
|
||||||
|
/// \ingroup publicApi
|
||||||
|
public class RoomInfo
|
||||||
|
{
|
||||||
|
/// <summary>Used internally in lobby, to mark rooms that are no longer listed.</summary>
|
||||||
|
public bool removedFromList { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>Backing field for property.</summary>
|
||||||
|
private Hashtable customPropertiesField = new Hashtable();
|
||||||
|
|
||||||
|
/// <summary>Backing field for property.</summary>
|
||||||
|
protected byte maxPlayersField = 0;
|
||||||
|
|
||||||
|
/// <summary>Backing field for property.</summary>
|
||||||
|
protected string[] expectedUsersField;
|
||||||
|
|
||||||
|
/// <summary>Backing field for property.</summary>
|
||||||
|
protected bool openField = true;
|
||||||
|
|
||||||
|
/// <summary>Backing field for property.</summary>
|
||||||
|
protected bool visibleField = true;
|
||||||
|
|
||||||
|
/// <summary>Backing field for property. False unless the GameProperty is set to true (else it's not sent).</summary>
|
||||||
|
protected bool autoCleanUpField = PhotonNetwork.autoCleanUpPlayerObjects;
|
||||||
|
|
||||||
|
/// <summary>Backing field for property.</summary>
|
||||||
|
protected string nameField;
|
||||||
|
|
||||||
|
/// <summary>Backing field for master client id (actorNumber). defined by server in room props and ev leave.</summary>
|
||||||
|
protected internal int masterClientIdField;
|
||||||
|
|
||||||
|
protected internal bool serverSideMasterClient { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Read-only "cache" of custom properties of a room. Set via Room.SetCustomProperties (not available for RoomInfo class!).</summary>
|
||||||
|
/// <remarks>All keys are string-typed and the values depend on the game/application.</remarks>
|
||||||
|
/// <see cref="Room.SetCustomProperties"/>
|
||||||
|
public Hashtable CustomProperties
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.customPropertiesField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The name of a room. Unique identifier (per Loadbalancing group) for a room/match.</summary>
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.nameField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Only used internally in lobby, to display number of players in room (while you're not in).
|
||||||
|
/// </summary>
|
||||||
|
public int PlayerCount { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// State if the local client is already in the game or still going to join it on gameserver (in lobby always false).
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLocalClientInside { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a limit of players to this room. This property is shown in lobby, too.
|
||||||
|
/// If the room is full (players count == maxplayers), joining this room will fail.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// As part of RoomInfo this can't be set.
|
||||||
|
/// As part of a Room (which the player joined), the setter will update the server and all clients.
|
||||||
|
/// </remarks>
|
||||||
|
public byte MaxPlayers
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.maxPlayersField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines if the room can be joined.
|
||||||
|
/// This does not affect listing in a lobby but joining the room will fail if not open.
|
||||||
|
/// If not open, the room is excluded from random matchmaking.
|
||||||
|
/// Due to racing conditions, found matches might become closed before they are joined.
|
||||||
|
/// Simply re-connect to master and find another.
|
||||||
|
/// Use property "IsVisible" to not list the room.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// As part of RoomInfo this can't be set.
|
||||||
|
/// As part of a Room (which the player joined), the setter will update the server and all clients.
|
||||||
|
/// </remarks>
|
||||||
|
public bool IsOpen
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.openField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines if the room is listed in its lobby.
|
||||||
|
/// Rooms can be created invisible, or changed to invisible.
|
||||||
|
/// To change if a room can be joined, use property: open.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// As part of RoomInfo this can't be set.
|
||||||
|
/// As part of a Room (which the player joined), the setter will update the server and all clients.
|
||||||
|
/// </remarks>
|
||||||
|
public bool IsVisible
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.visibleField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a RoomInfo to be used in room listings in lobby.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="roomName"></param>
|
||||||
|
/// <param name="properties"></param>
|
||||||
|
protected internal RoomInfo(string roomName, Hashtable properties)
|
||||||
|
{
|
||||||
|
this.InternalCacheProperties(properties);
|
||||||
|
|
||||||
|
this.nameField = roomName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes RoomInfo comparable (by name).
|
||||||
|
/// </summary>
|
||||||
|
public override bool Equals(object other)
|
||||||
|
{
|
||||||
|
RoomInfo otherRoomInfo = other as RoomInfo;
|
||||||
|
return (otherRoomInfo != null && this.Name.Equals(otherRoomInfo.nameField));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Accompanies Equals, using the name's HashCode as return.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return this.nameField.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Simple printingin method.</summary>
|
||||||
|
/// <returns>Summary of this RoomInfo instance.</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("Room: '{0}' {1},{2} {4}/{3} players.", this.nameField, this.visibleField ? "visible" : "hidden", this.openField ? "open" : "closed", this.maxPlayersField, this.PlayerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Simple printingin method.</summary>
|
||||||
|
/// <returns>Summary of this RoomInfo instance.</returns>
|
||||||
|
public string ToStringFull()
|
||||||
|
{
|
||||||
|
return string.Format("Room: '{0}' {1},{2} {4}/{3} players.\ncustomProps: {5}", this.nameField, this.visibleField ? "visible" : "hidden", this.openField ? "open" : "closed", this.maxPlayersField, this.PlayerCount, this.customPropertiesField.ToStringFull());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Copies "well known" properties to fields (IsVisible, etc) and caches the custom properties (string-keys only) in a local hashtable.</summary>
|
||||||
|
/// <param name="propertiesToCache">New or updated properties to store in this RoomInfo.</param>
|
||||||
|
protected internal void InternalCacheProperties(Hashtable propertiesToCache)
|
||||||
|
{
|
||||||
|
if (propertiesToCache == null || propertiesToCache.Count == 0 || this.customPropertiesField.Equals(propertiesToCache))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check of this game was removed from the list. in that case, we don't
|
||||||
|
// need to read any further properties
|
||||||
|
// list updates will remove this game from the game listing
|
||||||
|
if (propertiesToCache.ContainsKey(GamePropertyKey.Removed))
|
||||||
|
{
|
||||||
|
this.removedFromList = (Boolean)propertiesToCache[GamePropertyKey.Removed];
|
||||||
|
if (this.removedFromList)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch the "well known" properties of the room, if available
|
||||||
|
if (propertiesToCache.ContainsKey(GamePropertyKey.MaxPlayers))
|
||||||
|
{
|
||||||
|
this.maxPlayersField = (byte)propertiesToCache[GamePropertyKey.MaxPlayers];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertiesToCache.ContainsKey(GamePropertyKey.IsOpen))
|
||||||
|
{
|
||||||
|
this.openField = (bool)propertiesToCache[GamePropertyKey.IsOpen];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertiesToCache.ContainsKey(GamePropertyKey.IsVisible))
|
||||||
|
{
|
||||||
|
this.visibleField = (bool)propertiesToCache[GamePropertyKey.IsVisible];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertiesToCache.ContainsKey(GamePropertyKey.PlayerCount))
|
||||||
|
{
|
||||||
|
this.PlayerCount = (int)((byte)propertiesToCache[GamePropertyKey.PlayerCount]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertiesToCache.ContainsKey(GamePropertyKey.CleanupCacheOnLeave))
|
||||||
|
{
|
||||||
|
this.autoCleanUpField = (bool)propertiesToCache[GamePropertyKey.CleanupCacheOnLeave];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertiesToCache.ContainsKey(GamePropertyKey.MasterClientId))
|
||||||
|
{
|
||||||
|
this.serverSideMasterClient = true;
|
||||||
|
bool isUpdate = this.masterClientIdField != 0;
|
||||||
|
this.masterClientIdField = (int) propertiesToCache[GamePropertyKey.MasterClientId];
|
||||||
|
if (isUpdate)
|
||||||
|
{
|
||||||
|
PhotonNetwork.networkingPeer.UpdateMasterClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (propertiesToCache.ContainsKey(GamePropertyKey.PropsListedInLobby))
|
||||||
|
//{
|
||||||
|
// // could be cached but isn't useful
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (propertiesToCache.ContainsKey((byte)GamePropertyKey.ExpectedUsers))
|
||||||
|
{
|
||||||
|
this.expectedUsersField = (string[])propertiesToCache[GamePropertyKey.ExpectedUsers];
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge the custom properties (from your application) to the cache (only string-typed keys will be kept)
|
||||||
|
this.customPropertiesField.MergeStringKeys(propertiesToCache);
|
||||||
|
this.customPropertiesField.StripKeysWithNullValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region Obsoleted variable names
|
||||||
|
|
||||||
|
[Obsolete("Please use CustomProperties (updated case for naming).")]
|
||||||
|
public Hashtable customProperties { get { return this.CustomProperties; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use Name (updated case for naming).")]
|
||||||
|
public string name { get { return this.Name; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use PlayerCount (updated case for naming).")]
|
||||||
|
public int playerCount { get { return this.PlayerCount; } set { this.PlayerCount = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use IsLocalClientInside (updated case for naming).")]
|
||||||
|
public bool isLocalClientInside { get { return this.IsLocalClientInside; } set { this.IsLocalClientInside = value; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use MaxPlayers (updated case for naming).")]
|
||||||
|
public byte maxPlayers { get { return this.MaxPlayers; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use IsOpen (updated case for naming).")]
|
||||||
|
public bool open { get { return this.IsOpen; } }
|
||||||
|
|
||||||
|
[Obsolete("Please use IsVisible (updated case for naming).")]
|
||||||
|
public bool visible { get { return this.IsVisible; } }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue