Luke Stampfli
4 年前
当前提交
3e08e7ff
共有 116 个文件被更改,包括 14752 次插入 和 0 次删除
-
8Packages/packages-lock.json
-
5Packages/com.unity.multiplayer.transport.photon-realtime/CHANGELOG.md
-
7Packages/com.unity.multiplayer.transport.photon-realtime/CHANGELOG.md.meta
-
9Packages/com.unity.multiplayer.transport.photon-realtime/README.md
-
7Packages/com.unity.multiplayer.transport.photon-realtime/README.md.meta
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime.meta
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon.meta
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat.meta
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code.meta
-
18Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChannelCreationOptions.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChannelCreationOptions.cs.meta
-
14Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChannelWellKnownProperties.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChannelWellKnownProperties.cs.meta
-
191Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatChannel.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatChannel.cs.meta
-
1001Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatClient.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatClient.cs.meta
-
36Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatDisconnectCause.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatDisconnectCause.cs.meta
-
33Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatEventCode.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatEventCode.cs.meta
-
36Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatOperationCode.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatOperationCode.cs.meta
-
68Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatParameterCode.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatParameterCode.cs.meta
-
412Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatPeer.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatPeer.cs.meta
-
37Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatState.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatState.cs.meta
-
35Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatUserStatus.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatUserStatus.cs.meta
-
110Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/IChatClientListener.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/IChatClientListener.cs.meta
-
81Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/changes-chat.txt
-
7Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/changes-chat.txt.meta
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs.meta
-
5Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Metro.meta
-
1001Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Metro/Photon3Unity3D.dll
-
69Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Metro/Photon3Unity3D.dll.meta
-
1Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Metro/Photon3Unity3D.pri
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Metro/Photon3Unity3D.pri.meta
-
1001Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Photon3Unity3D.dll
-
106Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Photon3Unity3D.dll.meta
-
1001Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Photon3Unity3D.xml
-
9Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Photon3Unity3D.xml.meta
-
5Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket.meta
-
283Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/SocketWebTcp.cs
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/SocketWebTcp.cs.meta
-
161Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/WebSocket.cs
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/WebSocket.cs.meta
-
117Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/WebSocket.jslib
-
21Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/WebSocket.jslib.meta
-
3Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/websocket-sharp.README
-
4Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/websocket-sharp.README.meta
-
1001Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/websocket-sharp.dll
-
69Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/websocket-sharp.dll.meta
-
1001Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/changes-library.txt
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/changes-library.txt.meta
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/netstandard2.0.meta
-
47Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.deps.json
-
7Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.deps.json.meta
-
1001Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.dll
-
100Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.dll.meta
-
1001Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.xml
-
7Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.xml.meta
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime.meta
-
8Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code.meta
-
153Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/AppSettings.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/AppSettings.cs.meta
-
173Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/ConnectionHandler.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/ConnectionHandler.cs.meta
-
215Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/Extensions.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/Extensions.cs.meta
-
48Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/FriendInfo.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/FriendInfo.cs.meta
-
1001Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/LoadBalancingClient.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/LoadBalancingClient.cs.meta
-
1001Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/LoadBalancingPeer.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/LoadBalancingPeer.cs.meta
-
466Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/PhotonPing.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/PhotonPing.cs.meta
-
1Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/PhotonPingClasses.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/PhotonPingClasses.cs.meta
-
452Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/Player.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/Player.cs.meta
-
90Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/Region.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/Region.cs.meta
-
667Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/RegionHandler.cs
-
11Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/RegionHandler.cs.meta
|
|||
Changelog |
|||
All notable changes to this package will be documented in this file. The format is based on Keep a Changelog |
|||
|
|||
[0.0.1-preview.1] - 2020-10-05 |
|||
This is the first release of Enet MLAPI Package |
|
|||
fileFormatVersion: 2 |
|||
guid: 9d6ccdb72b9b37548a841fcc1c4dee76 |
|||
TextScriptImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Photon Realtime transport for MLAPI |
|||
|
|||
|
|||
--- |
|||
We do NOT have a license for the photon source so keep this internal. |
|||
|
|||
Original `license.txt` in the realtime sdk: |
|||
See the License Agreement at |
|||
https://www.photonengine.com/terms |
|
|||
fileFormatVersion: 2 |
|||
guid: ea5e2ac1b219ffb428dc8f61477267d2 |
|||
TextScriptImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: caaee5b70b02eae44b04ad8149d1ba63 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: ef193029478e7d84692c98b909eaaa00 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 02fbda138c6cf9143aacd0267c5db552 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 29466ff33d534e74baaef92e3459e59f |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChannelCreationOptions is a parameter used when subscribing to a public channel for the first time.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2018 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
public class ChannelCreationOptions |
|||
{ |
|||
/// <summary>Default values of channel creation options.</summary>
|
|||
public static ChannelCreationOptions Default = new ChannelCreationOptions(); |
|||
/// <summary>Whether or not the channel to be created will allow client to keep a list of users.</summary>
|
|||
public bool PublishSubscribers { get; set; } |
|||
/// <summary>Limit of the number of users subscribed to the channel to be created.</summary>
|
|||
public int MaxSubscribers { get; set; } |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 8f478e74d7c37554ea2edebe1300c2f9 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChannelWellKnownProperties contains the list of well-known channel properties.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2018 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
public class ChannelWellKnownProperties |
|||
{ |
|||
public const byte MaxSubscribers = 255; |
|||
public const byte PublishSubscribers = 254; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 9ff211198d3295e4ca2a4fa56babeb3a |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChatClient is the main class of this api.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2014 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
#if UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER
|
|||
#define SUPPORTED_UNITY
|
|||
#endif
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
#if SUPPORTED_UNITY || NETFX_CORE
|
|||
using Hashtable = ExitGames.Client.Photon.Hashtable; |
|||
using SupportClass = ExitGames.Client.Photon.SupportClass; |
|||
#endif
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// A channel of communication in Photon Chat, updated by ChatClient and provided as READ ONLY.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Contains messages and senders to use (read!) and display by your GUI.
|
|||
/// Access these by:
|
|||
/// ChatClient.PublicChannels
|
|||
/// ChatClient.PrivateChannels
|
|||
/// </remarks>
|
|||
public class ChatChannel |
|||
{ |
|||
/// <summary>Name of the channel (used to subscribe and unsubscribe).</summary>
|
|||
public readonly string Name; |
|||
|
|||
/// <summary>Senders of messages in chronological order. Senders and Messages refer to each other by index. Senders[x] is the sender of Messages[x].</summary>
|
|||
public readonly List<string> Senders = new List<string>(); |
|||
|
|||
/// <summary>Messages in chronological order. Senders and Messages refer to each other by index. Senders[x] is the sender of Messages[x].</summary>
|
|||
public readonly List<object> Messages = new List<object>(); |
|||
|
|||
/// <summary>If greater than 0, this channel will limit the number of messages, that it caches locally.</summary>
|
|||
public int MessageLimit; |
|||
|
|||
/// <summary>Unique channel ID.</summary>
|
|||
public int ChannelID; |
|||
|
|||
/// <summary>Is this a private 1:1 channel?</summary>
|
|||
public bool IsPrivate { get; protected internal set; } |
|||
|
|||
/// <summary>Count of messages this client still buffers/knows for this channel.</summary>
|
|||
public int MessageCount { get { return this.Messages.Count; } } |
|||
|
|||
/// <summary>
|
|||
/// ID of the last message received.
|
|||
/// </summary>
|
|||
public int LastMsgId { get; protected set; } |
|||
|
|||
private Dictionary<object, object> properties; |
|||
|
|||
/// <summary>Whether or not this channel keeps track of the list of its subscribers.</summary>
|
|||
public bool PublishSubscribers { get; protected set; } |
|||
|
|||
/// <summary>Maximum number of channel subscribers. 0 means infinite.</summary>
|
|||
public int MaxSubscribers { get; protected set; } |
|||
|
|||
/// <summary>Subscribed users.</summary>
|
|||
public readonly HashSet<string> Subscribers = new HashSet<string>(); |
|||
|
|||
/// <summary>Used internally to create new channels. This does NOT create a channel on the server! Use ChatClient.Subscribe.</summary>
|
|||
public ChatChannel(string name) |
|||
{ |
|||
this.Name = name; |
|||
} |
|||
|
|||
public bool TryGetProperty(string key, out object value) |
|||
{ |
|||
if (properties == null) |
|||
{ |
|||
value = default(object); |
|||
return false; |
|||
} |
|||
|
|||
return properties.TryGetValue(key, out value); |
|||
} |
|||
|
|||
/// <summary>Used internally to add messages to this channel.</summary>
|
|||
public void Add(string sender, object message, int msgId) |
|||
{ |
|||
this.Senders.Add(sender); |
|||
this.Messages.Add(message); |
|||
this.LastMsgId = msgId; |
|||
this.TruncateMessages(); |
|||
} |
|||
|
|||
/// <summary>Used internally to add messages to this channel.</summary>
|
|||
public void Add(string[] senders, object[] messages, int lastMsgId) |
|||
{ |
|||
this.Senders.AddRange(senders); |
|||
this.Messages.AddRange(messages); |
|||
this.LastMsgId = lastMsgId; |
|||
this.TruncateMessages(); |
|||
} |
|||
|
|||
/// <summary>Reduces the number of locally cached messages in this channel to the MessageLimit (if set).</summary>
|
|||
public void TruncateMessages() |
|||
{ |
|||
if (this.MessageLimit <= 0 || this.Messages.Count <= this.MessageLimit) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
int excessCount = this.Messages.Count - this.MessageLimit; |
|||
this.Senders.RemoveRange(0, excessCount); |
|||
this.Messages.RemoveRange(0, excessCount); |
|||
} |
|||
|
|||
/// <summary>Clear the local cache of messages currently stored. This frees memory but doesn't affect the server.</summary>
|
|||
public void ClearMessages() |
|||
{ |
|||
this.Senders.Clear(); |
|||
this.Messages.Clear(); |
|||
} |
|||
|
|||
/// <summary>Provides a string-representation of all messages in this channel.</summary>
|
|||
/// <returns>All known messages in format "Sender: Message", line by line.</returns>
|
|||
public string ToStringMessages() |
|||
{ |
|||
StringBuilder txt = new StringBuilder(); |
|||
for (int i = 0; i < this.Messages.Count; i++) |
|||
{ |
|||
txt.AppendLine(string.Format("{0}: {1}", this.Senders[i], this.Messages[i])); |
|||
} |
|||
return txt.ToString(); |
|||
} |
|||
|
|||
internal void ReadProperties(Dictionary<object, object> newProperties) |
|||
{ |
|||
if (newProperties != null && newProperties.Count > 0) |
|||
{ |
|||
if (this.properties == null) |
|||
{ |
|||
this.properties = new Dictionary<object, object>(newProperties.Count); |
|||
} |
|||
foreach (var k in newProperties.Keys) |
|||
{ |
|||
if (newProperties[k] == null) |
|||
{ |
|||
if (this.properties.ContainsKey(k)) |
|||
{ |
|||
this.properties.Remove(k); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
this.properties[k] = newProperties[k]; |
|||
} |
|||
} |
|||
object temp; |
|||
if (this.properties.TryGetValue(ChannelWellKnownProperties.PublishSubscribers, out temp)) |
|||
{ |
|||
this.PublishSubscribers = (bool)temp; |
|||
} |
|||
if (this.properties.TryGetValue(ChannelWellKnownProperties.MaxSubscribers, out temp)) |
|||
{ |
|||
this.MaxSubscribers = (int)temp; |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal void AddSubscribers(string[] users) |
|||
{ |
|||
if (users == null) |
|||
{ |
|||
return; |
|||
} |
|||
for (int i = 0; i < users.Length; i++) |
|||
{ |
|||
this.Subscribers.Add(users[i]); |
|||
} |
|||
} |
|||
|
|||
internal void ClearProperties() |
|||
{ |
|||
if (this.properties != null && this.properties.Count > 0) |
|||
{ |
|||
this.properties.Clear(); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d84ef73471cfacf4daee3a333fb0039a |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonChat/Code/ChatClient.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 3dc58b7807acc4d488636fa1d4019f2b |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChatClient is the main class of this api.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2014 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
/// <summary>Enumeration of causes for Disconnects (used in <see cref="ChatClient.DisconnectedCause"/>).</summary>
|
|||
/// <remarks>Read the individual descriptions to find out what to do about this type of disconnect.</remarks>
|
|||
public enum ChatDisconnectCause |
|||
{ |
|||
/// <summary>No error was tracked.</summary>
|
|||
None, |
|||
/// <summary>OnStatusChanged: The CCUs count of your Photon Server License is exhausted (temporarily).</summary>
|
|||
DisconnectByServerUserLimit, |
|||
/// <summary>OnStatusChanged: The server is not available or the address is wrong. Make sure the port is provided and the server is up.</summary>
|
|||
ExceptionOnConnect, |
|||
/// <summary>OnStatusChanged: The server disconnected this client. Most likely the server's send buffer is full (receiving too much from other clients).</summary>
|
|||
DisconnectByServer, |
|||
/// <summary>OnStatusChanged: This client detected that the server's responses are not received in due time. Maybe you send / receive too much?</summary>
|
|||
TimeoutDisconnect, |
|||
/// <summary>OnStatusChanged: Some internal exception caused the socket code to fail. Contact Exit Games.</summary>
|
|||
Exception, |
|||
/// <summary>OnOperationResponse: Authenticate in the Photon Cloud with invalid AppId. Update your subscription or contact Exit Games.</summary>
|
|||
InvalidAuthentication, |
|||
/// <summary>OnOperationResponse: Authenticate (temporarily) failed when using a Photon Cloud subscription without CCU Burst. Update your subscription.</summary>
|
|||
MaxCcuReached, |
|||
/// <summary>OnOperationResponse: Authenticate when the app's Photon Cloud subscription is locked to some (other) region(s). Update your subscription or change region.</summary>
|
|||
InvalidRegion, |
|||
/// <summary>OnOperationResponse: Operation that's (currently) not available for this client (not authorized usually). Only tracked for op Authenticate.</summary>
|
|||
OperationNotAllowedInCurrentState, |
|||
/// <summary>OnOperationResponse: Authenticate in the Photon Cloud with invalid client values or custom authentication setup in Cloud Dashboard.</summary>
|
|||
CustomAuthenticationFailed, |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: cb82ca173d68bbf45bdc93c4c827d4f2 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChatClient is the main class of this api.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2014 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
/// <summary>
|
|||
/// Wraps up internally used constants in Photon Chat events. You don't have to use them directly usually.
|
|||
/// </summary>
|
|||
public class ChatEventCode |
|||
{ |
|||
/// <summary>(0) Event code for messages published in public channels.</summary>
|
|||
public const byte ChatMessages = 0; |
|||
/// <summary>(1) Not Used. </summary>
|
|||
public const byte Users = 1;// List of users or List of changes for List of users
|
|||
/// <summary>(2) Event code for messages published in private channels</summary>
|
|||
public const byte PrivateMessage = 2; |
|||
/// <summary>(3) Not Used. </summary>
|
|||
public const byte FriendsList = 3; |
|||
/// <summary>(4) Event code for status updates. </summary>
|
|||
public const byte StatusUpdate = 4; |
|||
/// <summary>(5) Event code for subscription acks. </summary>
|
|||
public const byte Subscribe = 5; |
|||
/// <summary>(6) Event code for unsubscribe acks. </summary>
|
|||
public const byte Unsubscribe = 6; |
|||
/// <summary>(8) Event code for new user subscription to a channel where <see cref="ChatChannel.PublishSubscribers"/> is enabled. </summary>
|
|||
public const byte UserSubscribed = 8; |
|||
/// <summary>(9) Event code for when user unsubscribes from a channel where <see cref="ChatChannel.PublishSubscribers"/> is enabled. </summary>
|
|||
public const byte UserUnsubscribed = 9; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b1fd08cfa8721bf4c8ec428885ae1c39 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChatClient is the main class of this api.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2014 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
/// <summary>
|
|||
/// Wraps up codes for operations used internally in Photon Chat. You don't have to use them directly usually.
|
|||
/// </summary>
|
|||
public class ChatOperationCode |
|||
{ |
|||
/// <summary>(230) Operation Authenticate.</summary>
|
|||
public const byte Authenticate = 230; |
|||
|
|||
/// <summary>(0) Operation to subscribe to chat channels.</summary>
|
|||
public const byte Subscribe = 0; |
|||
/// <summary>(1) Operation to unsubscribe from chat channels.</summary>
|
|||
public const byte Unsubscribe = 1; |
|||
/// <summary>(2) Operation to publish a message in a chat channel.</summary>
|
|||
public const byte Publish = 2; |
|||
/// <summary>(3) Operation to send a private message to some other user.</summary>
|
|||
public const byte SendPrivate = 3; |
|||
|
|||
/// <summary>(4) Not used yet.</summary>
|
|||
public const byte ChannelHistory = 4; |
|||
|
|||
/// <summary>(5) Set your (client's) status.</summary>
|
|||
public const byte UpdateStatus = 5; |
|||
/// <summary>(6) Add friends the list of friends that should update you of their status.</summary>
|
|||
public const byte AddFriends = 6; |
|||
/// <summary>(7) Remove friends from list of friends that should update you of their status.</summary>
|
|||
public const byte RemoveFriends = 7; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d2e1a2163f978664f8e98cfc0e178f45 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChatClient is the main class of this api.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2014 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
/// <summary>
|
|||
/// Wraps up codes for parameters (in operations and events) used internally in Photon Chat. You don't have to use them directly usually.
|
|||
/// </summary>
|
|||
public class ChatParameterCode |
|||
{ |
|||
/// <summary>(0) Array of chat channels.</summary>
|
|||
public const byte Channels = 0; |
|||
/// <summary>(1) Name of a single chat channel.</summary>
|
|||
public const byte Channel = 1; |
|||
/// <summary>(2) Array of chat messages.</summary>
|
|||
public const byte Messages = 2; |
|||
/// <summary>(3) A single chat message.</summary>
|
|||
public const byte Message = 3; |
|||
/// <summary>(4) Array of names of the users who sent the array of chat messages.</summary>
|
|||
public const byte Senders = 4; |
|||
/// <summary>(5) Name of a the user who sent a chat message.</summary>
|
|||
public const byte Sender = 5; |
|||
/// <summary>(6) Not used.</summary>
|
|||
public const byte ChannelUserCount = 6; |
|||
/// <summary>(225) Name of user to send a (private) message to.</summary><remarks>The code is used in LoadBalancing and copied over here.</remarks>
|
|||
public const byte UserId = 225; |
|||
/// <summary>(8) Id of a message.</summary>
|
|||
public const byte MsgId = 8; |
|||
/// <summary>(9) Not used.</summary>
|
|||
public const byte MsgIds = 9; |
|||
/// <summary>(221) Secret token to identify an authorized user.</summary><remarks>The code is used in LoadBalancing and copied over here.</remarks>
|
|||
public const byte Secret = 221; |
|||
/// <summary>(15) Subscribe operation result parameter. A bool[] with result per channel.</summary>
|
|||
public const byte SubscribeResults = 15; |
|||
|
|||
/// <summary>(10) Status</summary>
|
|||
public const byte Status = 10; |
|||
/// <summary>(11) Friends</summary>
|
|||
public const byte Friends = 11; |
|||
/// <summary>(12) SkipMessage is used in SetOnlineStatus and if true, the message is not being broadcast.</summary>
|
|||
public const byte SkipMessage = 12; |
|||
|
|||
/// <summary>(14) Number of message to fetch from history. 0: no history. 1 and higher: number of messages in history. -1: all history.</summary>
|
|||
public const byte HistoryLength = 14; |
|||
|
|||
/// <summary>(21) WebFlags object for changing behaviour of webhooks from client.</summary>
|
|||
public const byte WebFlags = 21; |
|||
|
|||
/// <summary>(22) Properties of channel or user.</summary>
|
|||
/// <remarks>
|
|||
/// In event <see cref="ChatEventCode.Subscribe"/> it's always channel properties.
|
|||
/// </remarks>
|
|||
public const byte Properties = 22; |
|||
/// <summary>(23) Array of UserIds of users already subscribed to a channel.</summary>
|
|||
/// <remarks>Used in Subscribe event when PublishSubscribers is enabled.
|
|||
/// Does not include local user who just subscribed.
|
|||
/// Maximum length is (<see cref="ChatChannel.MaxSubscribers"/> - 1).</remarks>
|
|||
public const byte ChannelSubscribers = 23; |
|||
|
|||
/// <summary>
|
|||
/// Generated unique reusable room id
|
|||
/// </summary>
|
|||
public const byte UniqueRoomId = 29; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 9ba813811b3fa234ab7bd4c8b8759faf |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChatClient is the main class of this api.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2014 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
#if UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER
|
|||
#define SUPPORTED_UNITY
|
|||
#endif
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Collections.Generic; |
|||
using ExitGames.Client.Photon; |
|||
|
|||
#if SUPPORTED_UNITY || NETFX_CORE
|
|||
using Hashtable = ExitGames.Client.Photon.Hashtable; |
|||
using SupportClass = ExitGames.Client.Photon.SupportClass; |
|||
#endif
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Provides basic operations of the Photon Chat server. This internal class is used by public ChatClient.
|
|||
/// </summary>
|
|||
public class ChatPeer : PhotonPeer |
|||
{ |
|||
/// <summary>Name Server Host Name for Photon Cloud. Without port and without any prefix.</summary>
|
|||
public const string NameServerHost = "ns.exitgames.com"; |
|||
|
|||
/// <summary>Name Server for HTTP connections to the Photon Cloud. Includes prefix and port.</summary>
|
|||
public const string NameServerHttp = "http://ns.exitgamescloud.com:80/photon/n"; |
|||
|
|||
/// <summary>Name Server port per protocol (the UDP port is different than TCP, etc).</summary>
|
|||
private static readonly Dictionary<ConnectionProtocol, int> ProtocolToNameServerPort = new Dictionary<ConnectionProtocol, int>() { { ConnectionProtocol.Udp, 5058 }, { ConnectionProtocol.Tcp, 4533 }, { ConnectionProtocol.WebSocket, 9093 }, { ConnectionProtocol.WebSocketSecure, 19093 } }; //, { ConnectionProtocol.RHttp, 6063 } };
|
|||
|
|||
/// <summary>Name Server Address for Photon Cloud (based on current protocol). You can use the default values and usually won't have to set this value.</summary>
|
|||
public string NameServerAddress { get { return this.GetNameServerAddress(); } } |
|||
|
|||
virtual internal bool IsProtocolSecure { get { return this.UsedProtocol == ConnectionProtocol.WebSocketSecure; } } |
|||
|
|||
/// <summary> Chat Peer constructor. </summary>
|
|||
/// <param name="listener">Chat listener implementation.</param>
|
|||
/// <param name="protocol">Protocol to be used by the peer.</param>
|
|||
public ChatPeer(IPhotonPeerListener listener, ConnectionProtocol protocol) : base(listener, protocol) |
|||
{ |
|||
this.ConfigUnitySockets(); |
|||
} |
|||
|
|||
|
|||
|
|||
// Sets up the socket implementations to use, depending on platform
|
|||
[Conditional("SUPPORTED_UNITY")] |
|||
private void ConfigUnitySockets() |
|||
{ |
|||
Type websocketType = null; |
|||
#if UNITY_XBOXONE && !UNITY_EDITOR
|
|||
websocketType = Type.GetType("ExitGames.Client.Photon.SocketWebTcpNativeDynamic, PhotonWebSocket", false); |
|||
if (websocketType == null) |
|||
{ |
|||
websocketType = Type.GetType("ExitGames.Client.Photon.SocketWebTcpNativeDynamic, Assembly-CSharp-firstpass", false); |
|||
} |
|||
if (websocketType == null) |
|||
{ |
|||
websocketType = Type.GetType("ExitGames.Client.Photon.SocketWebTcpNativeDynamic, Assembly-CSharp", false); |
|||
} |
|||
if (websocketType == null) |
|||
{ |
|||
UnityEngine.Debug.LogError("UNITY_XBOXONE is defined but peer could not find SocketWebTcpNativeDynamic. Check your project files to make sure the native WSS implementation is available. Won't connect."); |
|||
} |
|||
#else
|
|||
// to support WebGL export in Unity, we find and assign the SocketWebTcp class (if it's in the project).
|
|||
// alternatively class SocketWebTcp might be in the Photon3Unity3D.dll
|
|||
websocketType = Type.GetType("ExitGames.Client.Photon.SocketWebTcp, PhotonWebSocket", false); |
|||
if (websocketType == null) |
|||
{ |
|||
websocketType = Type.GetType("ExitGames.Client.Photon.SocketWebTcp, Assembly-CSharp-firstpass", false); |
|||
} |
|||
if (websocketType == null) |
|||
{ |
|||
websocketType = Type.GetType("ExitGames.Client.Photon.SocketWebTcp, Assembly-CSharp", false); |
|||
} |
|||
#endif
|
|||
|
|||
if (websocketType != null) |
|||
{ |
|||
this.SocketImplementationConfig[ConnectionProtocol.WebSocket] = websocketType; |
|||
this.SocketImplementationConfig[ConnectionProtocol.WebSocketSecure] = websocketType; |
|||
} |
|||
|
|||
#if NET_4_6 && (UNITY_EDITOR || !ENABLE_IL2CPP)
|
|||
this.SocketImplementationConfig[ConnectionProtocol.Udp] = typeof(SocketUdpAsync); |
|||
this.SocketImplementationConfig[ConnectionProtocol.Tcp] = typeof(SocketTcpAsync); |
|||
#endif
|
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the NameServer Address (with prefix and port), based on the set protocol (this.UsedProtocol).
|
|||
/// </summary>
|
|||
/// <returns>NameServer Address (with prefix and port).</returns>
|
|||
private string GetNameServerAddress() |
|||
{ |
|||
var protocolPort = 0; |
|||
ProtocolToNameServerPort.TryGetValue(this.TransportProtocol, out protocolPort); |
|||
|
|||
switch (this.TransportProtocol) |
|||
{ |
|||
case ConnectionProtocol.Udp: |
|||
case ConnectionProtocol.Tcp: |
|||
return string.Format("{0}:{1}", NameServerHost, protocolPort); |
|||
#if RHTTP
|
|||
case ConnectionProtocol.RHttp: |
|||
return NameServerHttp; |
|||
#endif
|
|||
case ConnectionProtocol.WebSocket: |
|||
return string.Format("ws://{0}:{1}", NameServerHost, protocolPort); |
|||
case ConnectionProtocol.WebSocketSecure: |
|||
return string.Format("wss://{0}:{1}", NameServerHost, protocolPort); |
|||
default: |
|||
throw new ArgumentOutOfRangeException(); |
|||
} |
|||
} |
|||
|
|||
/// <summary> Connects to NameServer. </summary>
|
|||
/// <returns>If the connection attempt could be sent.</returns>
|
|||
public bool Connect() |
|||
{ |
|||
if (this.DebugOut >= DebugLevel.INFO) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.INFO, "Connecting to nameserver " + this.NameServerAddress); |
|||
} |
|||
|
|||
return this.Connect(this.NameServerAddress, "NameServer"); |
|||
} |
|||
|
|||
/// <summary> Authenticates on NameServer. </summary>
|
|||
/// <returns>If the authentication operation request could be sent.</returns>
|
|||
public bool AuthenticateOnNameServer(string appId, string appVersion, string region, AuthenticationValues authValues) |
|||
{ |
|||
if (this.DebugOut >= DebugLevel.INFO) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.INFO, "OpAuthenticate()"); |
|||
} |
|||
|
|||
var opParameters = new Dictionary<byte, object>(); |
|||
|
|||
opParameters[ParameterCode.AppVersion] = appVersion; |
|||
opParameters[ParameterCode.ApplicationId] = appId; |
|||
opParameters[ParameterCode.Region] = region; |
|||
|
|||
if (authValues != null) |
|||
{ |
|||
if (!string.IsNullOrEmpty(authValues.UserId)) |
|||
{ |
|||
opParameters[ParameterCode.UserId] = authValues.UserId; |
|||
} |
|||
|
|||
if (authValues != null && authValues.AuthType != CustomAuthenticationType.None) |
|||
{ |
|||
opParameters[ParameterCode.ClientAuthenticationType] = (byte) authValues.AuthType; |
|||
if (!string.IsNullOrEmpty(authValues.Token)) |
|||
{ |
|||
opParameters[ParameterCode.Secret] = authValues.Token; |
|||
} |
|||
else |
|||
{ |
|||
if (!string.IsNullOrEmpty(authValues.AuthGetParameters)) |
|||
{ |
|||
opParameters[ParameterCode.ClientAuthenticationParams] = authValues.AuthGetParameters; |
|||
} |
|||
if (authValues.AuthPostData != null) |
|||
{ |
|||
opParameters[ParameterCode.ClientAuthenticationData] = authValues.AuthPostData; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return this.SendOperation(ChatOperationCode.Authenticate, opParameters, new SendOptions() { Reliability = true, Encrypt = this.IsEncryptionAvailable }); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Options for optional "Custom Authentication" services used with Photon. Used by OpAuthenticate after connecting to Photon.
|
|||
/// </summary>
|
|||
public enum CustomAuthenticationType : byte |
|||
{ |
|||
/// <summary>Use a custom authentification service. Currently the only implemented option.</summary>
|
|||
Custom = 0, |
|||
|
|||
/// <summary>Authenticates users by their Steam Account. Set auth values accordingly!</summary>
|
|||
Steam = 1, |
|||
|
|||
/// <summary>Authenticates users by their Facebook Account. Set auth values accordingly!</summary>
|
|||
Facebook = 2, |
|||
|
|||
/// <summary>Authenticates users by their Oculus Account and token.</summary>
|
|||
Oculus = 3, |
|||
|
|||
/// <summary>Authenticates users by their PSN Account and token.</summary>
|
|||
PlayStation = 4, |
|||
|
|||
/// <summary>Authenticates users by their Xbox Account and XSTS token.</summary>
|
|||
Xbox = 5, |
|||
|
|||
/// <summary>Authenticates users by their HTC VIVEPORT Account and user token.</summary>
|
|||
Viveport = 10, |
|||
|
|||
/// <summary>Disables custom authentification. Same as not providing any AuthenticationValues for connect (more precisely for: OpAuthenticate).</summary>
|
|||
None = byte.MaxValue |
|||
} |
|||
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Container for user authentication in Photon. Set AuthValues before you connect - all else is handled.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// On Photon, user authentication is optional but can be useful in many cases.
|
|||
/// If you want to FindFriends, a unique ID per user is very practical.
|
|||
///
|
|||
/// There are basically three options for user authentification: None at all, the client sets some UserId
|
|||
/// or you can use some account web-service to authenticate a user (and set the UserId server-side).
|
|||
///
|
|||
/// Custom Authentication lets you verify end-users by some kind of login or token. It sends those
|
|||
/// values to Photon which will verify them before granting access or disconnecting the client.
|
|||
///
|
|||
/// The Photon Cloud Dashboard will let you enable this feature and set important server values for it.
|
|||
/// https://dashboard.photonengine.com
|
|||
/// </remarks>
|
|||
public class AuthenticationValues |
|||
{ |
|||
/// <summary>See AuthType.</summary>
|
|||
private CustomAuthenticationType authType = CustomAuthenticationType.None; |
|||
|
|||
/// <summary>The type of custom authentication provider that should be used. Currently only "Custom" or "None" (turns this off).</summary>
|
|||
public CustomAuthenticationType AuthType |
|||
{ |
|||
get { return authType; } |
|||
set { authType = value; } |
|||
} |
|||
|
|||
/// <summary>This string must contain any (http get) parameters expected by the used authentication service. By default, username and token.</summary>
|
|||
/// <remarks>
|
|||
/// Maps to operation parameter 216.
|
|||
/// Standard http get parameters are used here and passed on to the service that's defined in the server (Photon Cloud Dashboard).</remarks>
|
|||
public string AuthGetParameters { get; set; } |
|||
|
|||
/// <summary>Data to be passed-on to the auth service via POST. Default: null (not sent). Either string or byte[] (see setters).</summary>
|
|||
/// <remarks>Maps to operation parameter 214.</remarks>
|
|||
public object AuthPostData { get; private set; } |
|||
|
|||
/// <summary>After initial authentication, Photon provides a token for this client / user, which is subsequently used as (cached) validation.</summary>
|
|||
public string Token { get; set; } |
|||
|
|||
/// <summary>The UserId should be a unique identifier per user. This is for finding friends, etc..</summary>
|
|||
public string UserId { get; set; } |
|||
|
|||
|
|||
/// <summary>Creates empty auth values without any info.</summary>
|
|||
public AuthenticationValues() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>Creates minimal info about the user. If this is authenticated or not, depends on the set AuthType.</summary>
|
|||
/// <param name="userId">Some UserId to set in Photon.</param>
|
|||
public AuthenticationValues(string userId) |
|||
{ |
|||
this.UserId = userId; |
|||
} |
|||
|
|||
/// <summary>Sets the data to be passed-on to the auth service via POST.</summary>
|
|||
/// <param name="stringData">String data to be used in the body of the POST request. Null or empty string will set AuthPostData to null.</param>
|
|||
public virtual void SetAuthPostData(string stringData) |
|||
{ |
|||
this.AuthPostData = (string.IsNullOrEmpty(stringData)) ? null : stringData; |
|||
} |
|||
|
|||
/// <summary>Sets the data to be passed-on to the auth service via POST.</summary>
|
|||
/// <param name="byteData">Binary token / auth-data to pass on.</param>
|
|||
public virtual void SetAuthPostData(byte[] byteData) |
|||
{ |
|||
this.AuthPostData = byteData; |
|||
} |
|||
|
|||
/// <summary>Adds a key-value pair to the get-parameters used for Custom Auth (AuthGetParameters).</summary>
|
|||
/// <remarks>This method does uri-encoding for you.</remarks>
|
|||
/// <param name="key">Key for the value to set.</param>
|
|||
/// <param name="value">Some value relevant for Custom Authentication.</param>
|
|||
public virtual void AddAuthParameter(string key, string value) |
|||
{ |
|||
string ampersand = string.IsNullOrEmpty(this.AuthGetParameters) ? string.Empty : "&"; |
|||
this.AuthGetParameters = string.Format("{0}{1}{2}={3}", this.AuthGetParameters, ampersand, System.Uri.EscapeDataString(key), System.Uri.EscapeDataString(value)); |
|||
} |
|||
/// <summary>
|
|||
/// Transform this object into string.
|
|||
/// </summary>
|
|||
/// <returns>string representation of this object.</returns>
|
|||
public override string ToString() |
|||
{ |
|||
return string.Format("AuthenticationValues UserId: {0}, GetParameters: {1} Token available: {2}", UserId, this.AuthGetParameters, Token != null); |
|||
} |
|||
} |
|||
|
|||
/// <summary>Class for constants. Codes for parameters of Operations and Events.</summary>
|
|||
public class ParameterCode |
|||
{ |
|||
/// <summary>(224) Your application's ID: a name on your own Photon or a GUID on the Photon Cloud</summary>
|
|||
public const byte ApplicationId = 224; |
|||
/// <summary>(221) Internally used to establish encryption</summary>
|
|||
public const byte Secret = 221; |
|||
/// <summary>(220) Version of your application</summary>
|
|||
public const byte AppVersion = 220; |
|||
/// <summary>(217) This key's (byte) value defines the target custom authentication type/service the client connects with. Used in OpAuthenticate</summary>
|
|||
public const byte ClientAuthenticationType = 217; |
|||
/// <summary>(216) This key's (string) value provides parameters sent to the custom authentication type/service the client connects with. Used in OpAuthenticate</summary>
|
|||
public const byte ClientAuthenticationParams = 216; |
|||
/// <summary>(214) This key's (string or byte[]) value provides parameters sent to the custom authentication service setup in Photon Dashboard. Used in OpAuthenticate</summary>
|
|||
public const byte ClientAuthenticationData = 214; |
|||
/// <summary>(210) Used for region values in OpAuth and OpGetRegions.</summary>
|
|||
public const byte Region = 210; |
|||
/// <summary>(230) Address of a (game) server to use.</summary>
|
|||
public const byte Address = 230; |
|||
/// <summary>(225) User's ID</summary>
|
|||
public const byte UserId = 225; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// ErrorCode defines the default codes associated with Photon client/server communication.
|
|||
/// </summary>
|
|||
public class ErrorCode |
|||
{ |
|||
/// <summary>(0) is always "OK", anything else an error or specific situation.</summary>
|
|||
public const int Ok = 0; |
|||
|
|||
// server - Photon low(er) level: <= 0
|
|||
|
|||
/// <summary>
|
|||
/// (-3) Operation can't be executed yet (e.g. OpJoin can't be called before being authenticated, RaiseEvent cant be used before getting into a room).
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Before you call any operations on the Cloud servers, the automated client workflow must complete its authorization.
|
|||
/// In PUN, wait until State is: JoinedLobby or ConnectedToMaster
|
|||
/// </remarks>
|
|||
public const int OperationNotAllowedInCurrentState = -3; |
|||
|
|||
/// <summary>(-2) The operation you called is not implemented on the server (application) you connect to. Make sure you run the fitting applications.</summary>
|
|||
public const int InvalidOperationCode = -2; |
|||
|
|||
/// <summary>(-1) Something went wrong in the server. Try to reproduce and contact Exit Games.</summary>
|
|||
public const int InternalServerError = -1; |
|||
|
|||
// server - PhotonNetwork: 0x7FFF and down
|
|||
// logic-level error codes start with short.max
|
|||
|
|||
/// <summary>(32767) Authentication failed. Possible cause: AppId is unknown to Photon (in cloud service).</summary>
|
|||
public const int InvalidAuthentication = 0x7FFF; |
|||
|
|||
/// <summary>(32766) GameId (name) already in use (can't create another). Change name.</summary>
|
|||
public const int GameIdAlreadyExists = 0x7FFF - 1; |
|||
|
|||
/// <summary>(32765) Game is full. This rarely happens when some player joined the room before your join completed.</summary>
|
|||
public const int GameFull = 0x7FFF - 2; |
|||
|
|||
/// <summary>(32764) Game is closed and can't be joined. Join another game.</summary>
|
|||
public const int GameClosed = 0x7FFF - 3; |
|||
|
|||
/// <summary>(32762) Not in use currently.</summary>
|
|||
public const int ServerFull = 0x7FFF - 5; |
|||
|
|||
/// <summary>(32761) Not in use currently.</summary>
|
|||
public const int UserBlocked = 0x7FFF - 6; |
|||
|
|||
/// <summary>(32760) Random matchmaking only succeeds if a room exists that is neither closed nor full. Repeat in a few seconds or create a new room.</summary>
|
|||
public const int NoRandomMatchFound = 0x7FFF - 7; |
|||
|
|||
/// <summary>(32758) Join can fail if the room (name) is not existing (anymore). This can happen when players leave while you join.</summary>
|
|||
public const int GameDoesNotExist = 0x7FFF - 9; |
|||
|
|||
/// <summary>(32757) Authorization on the Photon Cloud failed because the concurrent users (CCU) limit of the app's subscription is reached.</summary>
|
|||
/// <remarks>
|
|||
/// Unless you have a plan with "CCU Burst", clients might fail the authentication step during connect.
|
|||
/// Affected client are unable to call operations. Please note that players who end a game and return
|
|||
/// to the master server will disconnect and re-connect, which means that they just played and are rejected
|
|||
/// in the next minute / re-connect.
|
|||
/// This is a temporary measure. Once the CCU is below the limit, players will be able to connect an play again.
|
|||
///
|
|||
/// OpAuthorize is part of connection workflow but only on the Photon Cloud, this error can happen.
|
|||
/// Self-hosted Photon servers with a CCU limited license won't let a client connect at all.
|
|||
/// </remarks>
|
|||
public const int MaxCcuReached = 0x7FFF - 10; |
|||
|
|||
/// <summary>(32756) Authorization on the Photon Cloud failed because the app's subscription does not allow to use a particular region's server.</summary>
|
|||
/// <remarks>
|
|||
/// Some subscription plans for the Photon Cloud are region-bound. Servers of other regions can't be used then.
|
|||
/// Check your master server address and compare it with your Photon Cloud Dashboard's info.
|
|||
/// https://cloud.photonengine.com/dashboard
|
|||
///
|
|||
/// OpAuthorize is part of connection workflow but only on the Photon Cloud, this error can happen.
|
|||
/// Self-hosted Photon servers with a CCU limited license won't let a client connect at all.
|
|||
/// </remarks>
|
|||
public const int InvalidRegion = 0x7FFF - 11; |
|||
|
|||
/// <summary>
|
|||
/// (32755) Custom Authentication of the user failed due to setup reasons (see Cloud Dashboard) or the provided user data (like username or token). Check error message for details.
|
|||
/// </summary>
|
|||
public const int CustomAuthenticationFailed = 0x7FFF - 12; |
|||
} |
|||
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: dfc4187da281ad0449182289897fafab |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChatClient is the main class of this api.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2014 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
/// <summary>Possible states for a Chat Client.</summary>
|
|||
public enum ChatState |
|||
{ |
|||
/// <summary>Peer is created but not used yet.</summary>
|
|||
Uninitialized, |
|||
/// <summary>Connecting to name server.</summary>
|
|||
ConnectingToNameServer, |
|||
/// <summary>Connected to name server.</summary>
|
|||
ConnectedToNameServer, |
|||
/// <summary>Authenticating on current server.</summary>
|
|||
Authenticating, |
|||
/// <summary>Finished authentication on current server.</summary>
|
|||
Authenticated, |
|||
/// <summary>Disconnecting from name server. This is usually a transition from name server to frontend server.</summary>
|
|||
DisconnectingFromNameServer, |
|||
/// <summary>Connecting to frontend server.</summary>
|
|||
ConnectingToFrontEnd, |
|||
/// <summary>Connected to frontend server.</summary>
|
|||
ConnectedToFrontEnd, |
|||
/// <summary>Disconnecting from frontend server.</summary>
|
|||
DisconnectingFromFrontEnd, |
|||
/// <summary>Currently not used.</summary>
|
|||
QueuedComingFromFrontEnd, |
|||
/// <summary>The client disconnects (from any server).</summary>
|
|||
Disconnecting, |
|||
/// <summary>The client is no longer connected (to any server).</summary>
|
|||
Disconnected, |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: ab9e4dd4ddc26d442b893662400bd937 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChatClient is the main class of this api.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2014 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
/// <summary>Contains commonly used status values for SetOnlineStatus. You can define your own.</summary>
|
|||
/// <remarks>
|
|||
/// While "online" (value 2 and up), the status message will be sent to anyone who has you on his friend list.
|
|||
///
|
|||
/// Define custom online status values as you like with these rules:
|
|||
/// 0: Means "offline". It will be used when you are not connected. In this status, there is no status message.
|
|||
/// 1: Means "invisible" and is sent to friends as "offline". They see status 0, no message but you can chat.
|
|||
/// 2: And any higher value will be treated as "online". Status can be set.
|
|||
/// </remarks>
|
|||
public static class ChatUserStatus |
|||
{ |
|||
/// <summary>(0) Offline.</summary>
|
|||
public const int Offline = 0; |
|||
/// <summary>(1) Be invisible to everyone. Sends no message.</summary>
|
|||
public const int Invisible = 1; |
|||
/// <summary>(2) Online and available.</summary>
|
|||
public const int Online = 2; |
|||
/// <summary>(3) Online but not available.</summary>
|
|||
public const int Away = 3; |
|||
/// <summary>(4) Do not disturb.</summary>
|
|||
public const int DND = 4; |
|||
/// <summary>(5) Looking For Game/Group. Could be used when you want to be invited or do matchmaking.</summary>
|
|||
public const int LFG = 5; |
|||
/// <summary>(6) Could be used when in a room, playing.</summary>
|
|||
public const int Playing = 6; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 9260d436aa141e94cb520a09cd33bb86 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
// <summary>The Photon Chat Api enables clients to connect to a chat server and communicate with other clients.</summary>
|
|||
// <remarks>ChatClient is the main class of this api.</remarks>
|
|||
// <copyright company="Exit Games GmbH">Photon Chat Api - Copyright (C) 2014 Exit Games GmbH</copyright>
|
|||
// ----------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
|
|||
namespace Photon.Chat |
|||
{ |
|||
using ExitGames.Client.Photon; |
|||
|
|||
/// <summary>
|
|||
/// Callback interface for Chat client side. Contains callback methods to notify your app about updates.
|
|||
/// Must be provided to new ChatClient in constructor
|
|||
/// </summary>
|
|||
public interface IChatClientListener |
|||
{ |
|||
/// <summary>
|
|||
/// All debug output of the library will be reported through this method. Print it or put it in a
|
|||
/// buffer to use it on-screen.
|
|||
/// </summary>
|
|||
/// <param name="level">DebugLevel (severity) of the message.</param>
|
|||
/// <param name="message">Debug text. Print to System.Console or screen.</param>
|
|||
void DebugReturn(DebugLevel level, string message); |
|||
|
|||
/// <summary>
|
|||
/// Disconnection happened.
|
|||
/// </summary>
|
|||
void OnDisconnected(); |
|||
|
|||
/// <summary>
|
|||
/// Client is connected now.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Clients have to be connected before they can send their state, subscribe to channels and send any messages.
|
|||
/// </remarks>
|
|||
void OnConnected(); |
|||
|
|||
/// <summary>The ChatClient's state changed. Usually, OnConnected and OnDisconnected are the callbacks to react to.</summary>
|
|||
/// <param name="state">The new state.</param>
|
|||
void OnChatStateChange(ChatState state); |
|||
|
|||
/// <summary>
|
|||
/// Notifies app that client got new messages from server
|
|||
/// Number of senders is equal to number of messages in 'messages'. Sender with number '0' corresponds to message with
|
|||
/// number '0', sender with number '1' corresponds to message with number '1' and so on
|
|||
/// </summary>
|
|||
/// <param name="channelName">channel from where messages came</param>
|
|||
/// <param name="senders">list of users who sent messages</param>
|
|||
/// <param name="messages">list of messages it self</param>
|
|||
void OnGetMessages(string channelName, string[] senders, object[] messages); |
|||
|
|||
/// <summary>
|
|||
/// Notifies client about private message
|
|||
/// </summary>
|
|||
/// <param name="sender">user who sent this message</param>
|
|||
/// <param name="message">message it self</param>
|
|||
/// <param name="channelName">channelName for private messages (messages you sent yourself get added to a channel per target username)</param>
|
|||
void OnPrivateMessage(string sender, object message, string channelName); |
|||
|
|||
/// <summary>
|
|||
/// Result of Subscribe operation. Returns subscription result for every requested channel name.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// If multiple channels sent in Subscribe operation, OnSubscribed may be called several times, each call with part of sent array or with single channel in "channels" parameter.
|
|||
/// Calls order and order of channels in "channels" parameter may differ from order of channels in "channels" parameter of Subscribe operation.
|
|||
/// </remarks>
|
|||
/// <param name="channels">Array of channel names.</param>
|
|||
/// <param name="results">Per channel result if subscribed.</param>
|
|||
void OnSubscribed(string[] channels, bool[] results); |
|||
|
|||
/// <summary>
|
|||
/// Result of Unsubscribe operation. Returns for channel name if the channel is now unsubscribed.
|
|||
/// </summary>
|
|||
/// If multiple channels sent in Unsubscribe operation, OnUnsubscribed may be called several times, each call with part of sent array or with single channel in "channels" parameter.
|
|||
/// Calls order and order of channels in "channels" parameter may differ from order of channels in "channels" parameter of Unsubscribe operation.
|
|||
/// <param name="channels">Array of channel names that are no longer subscribed.</param>
|
|||
void OnUnsubscribed(string[] channels); |
|||
|
|||
/// <summary>
|
|||
/// New status of another user (you get updates for users set in your friends list).
|
|||
/// </summary>
|
|||
/// <param name="user">Name of the user.</param>
|
|||
/// <param name="status">New status of that user.</param>
|
|||
/// <param name="gotMessage">True if the status contains a message you should cache locally. False: This status update does not include a message (keep any you have).</param>
|
|||
/// <param name="message">Message that user set.</param>
|
|||
void OnStatusUpdate(string user, int status, bool gotMessage, object message); |
|||
|
|||
/// <summary>
|
|||
/// A user has subscribed to a public chat channel
|
|||
/// </summary>
|
|||
/// <param name="channel">Name of the chat channel</param>
|
|||
/// <param name="user">UserId of the user who subscribed</param>
|
|||
void OnUserSubscribed(string channel, string user); |
|||
|
|||
/// <summary>
|
|||
/// A user has unsubscribed from a public chat channel
|
|||
/// </summary>
|
|||
/// <param name="channel">Name of the chat channel</param>
|
|||
/// <param name="user">UserId of the user who unsubscribed</param>
|
|||
void OnUserUnsubscribed(string channel, string user); |
|||
|
|||
/// <summary>
|
|||
/// Received a broadcast message
|
|||
/// </summary>
|
|||
/// <param name="channel">Name of the chat channel</param>
|
|||
/// <param name="message">Message data</param>
|
|||
void OnReceiveBroadcastMessage(string channel, byte[] message); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: e1cca82d8945e624e8a26de10025586b |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
|
|||
Photon Chat C# Client - Changelog |
|||
Exit Games GmbH - www.photonengine.com - forum.photonengine.com |
|||
|
|||
|
|||
|
|||
Version 4.1.4.2 (8. May 2020 - rev5519) |
|||
Added: Broadcast receive and read channel using UniqueRoomID UniqueRoomID read from SubscribeChannel response |
|||
|
|||
Version 4.1.2.14 (6. May 2019 - rev5097) |
|||
Changed: Chat API changes are now listed in a separate changes file. |
|||
|
|||
Version 4.1.2.13 (3. May 2019 - rev5086) |
|||
Fixed: properly add local client's UserId to public channels' Subscribers list when applicable. |
|||
|
|||
Version 4.1.2.11 (15. April 2019 - rev5043) |
|||
Added: Feature: PublishSubscribers. Per channel, you can now define if the server broadcasts users joining and leaving. |
|||
Fixed: proper way to handle Subscribe event when channel properties are returned. |
|||
Added: Viveport Auth Provider enum value. |
|||
Added: Switch-related workaround. Setting a Thread.Name was causing a crash in some exports on console. This affects Unity to Nintendo Switch exports. |
|||
Added: ChannelCreationOptions class to be used in the new Subscribe overload method. |
|||
Changed: Chat to use the same 1.8 serialization as Realtime/PUN. This also now matches the SocketWebTcp.SerializationProtocol default. |
|||
|
|||
Version 4.1.2.1 (31. July 2018 - rev4787) |
|||
Changed: Namespace from "ExitGames.Client.Photon.Chat" to "Photon.Chat". |
|||
Added: ConnectAndSetStatus method. |
|||
|
|||
Version 4.1.1.17 (11. October 2017 - rev4465) |
|||
Fixed: Unity "6" compile define is now UNITY_2017. |
|||
|
|||
Version 4.1.1.15 (17. July 2017 - rev4232) |
|||
Added: ChatClient.TransportProtocol as shortcut to the use PhotonPeer's TransportProtocol value. This enables setting the protocol easily while not connected. |
|||
Added: ChatClient.SocketImplementationConfig as shortcut to modify PhotonPeer's SocketImplementationConfig. This enables you to setup which IPhotonSocket implementation to use for which network protocol. |
|||
Changed: ChatPeer.ConfigUnitySockets() to try to find our websocket implementations in the assembly, making the SocketWebTcpCoroutine and SocketWebTcpThread classes optional. |
|||
Removed: Class "SocketWebTcp" is no longer found by ConfigUnitySockets(). |
|||
|
|||
Version 4.1.1.2 (13. September 2016 - rev3652) |
|||
Changed: ChatPeer now has ConfigUnitySockets(), which defines the SocketImplementationConfig. It's only used in Unity (using UNITY define). |
|||
Changed: ChatClient is not setting socket implementations anymore. |
|||
Added: Hashtable definition to use Photon's own implementation for Windows Store builds (NETFX_CORE). This must be used but it means you to use the same Hashtable definition in all builds (no matter if 8.1 or 10). |
|||
Added: Support for WebGL export in Unity. |
|||
|
|||
Version 4.0.5.0 (3. December 2015 - rev3144) |
|||
Added: A MessageLimit field for ChatClient and ChatChannel to limit the number of messages the client keeps locally. It might be useful to limit memory usage in long running chats. Set ChatClient.MessageLimit to apply the limit to any channel subscribed afterwards or apply a limit individually. |
|||
|
|||
Version 4.0.0.11 (28. October 2015 - rev3093) |
|||
Added: More sanity checks on operations (empty userId or secret, max friends). |
|||
Added: Special debug logging when the server returns an error for "Operation Unknown". In this case, it's highly likely that you don't use a Chat AppId. |
|||
Added: More helpful error logging. |
|||
|
|||
Version 4.0.0.10 (14. July 2015 - rev2988) |
|||
Added: A Unity 4.6 demo with uGUI. It's missing a few features but should give you a good start to making your own. |
|||
Added: Unity/WebGL support (merged from PUN). |
|||
Added: Breaking! IChatClientListener.DebugReturn(). Photon lib and chat client log via this method (no logging to console by default). |
|||
Changed: ChatClient.CustomAuthenticationValues is now .AuthValues. You can use those values to identify a user, even if you don't setup an external, custom authentication service. |
|||
Changed: ChatClient.UserId no longer directly stores the id but puts it into AuthValues. This means, the UserId could also be set via setting AuthValues. |
|||
Changed: The API of AuthenticationValues. There is now the UserId and AddAuthParameter() replaces the less general SetAuthParameters() (which only set specific key/values). |
|||
Note: All users should have a UserId. You can set chatClient.UserId before you connect, or you can set the AuthenticationValues in Connect(..., authValues) to set a UserId. |
|||
Added: ChatChannel.ToStringMessages(), which gets all messages in a single string, line by line. The format is "Sender:Message". |
|||
Added: ChatClient.TryGetChannel() to find a channel only by name, no matter if public or private. |
|||
|
|||
Version 4.0.0.7 (12. January 2015 - rev2763) |
|||
Internal: Changed code for UserID from 7 to 225. The latter is used in LoadBalancing, too, so we want to re-use the code here. |
|||
|
|||
Version 4.0.0.1 (17. June 2014 - rev2663) |
|||
Changed: How the server responds to Subscribe and Unsubscribe. Events will now contain success/failure of those. This allows us to send the answer after calling a WebHook if needed and we can even send it to multiple clients (which authenticated with the same userID). |
|||
Changed: Handling of subscription responsed. This is done to allow web services to subscribe a client remotely and to be able to prevent joining some channel that a user should not join (the channel of some guild or another team, e.g.). |
|||
Changed: Debug loggging. In Unity we can't use Debug.Assert, etc. So we have to log more cleanly. This works in Editor and several platforms (but not all). |
|||
Changed: Folder for Chat API. It now begins with "Photon" which provides some context no matter where you copy the files. Easier to find in Unity projects. |
|||
Changed: Operation FriendList and method SendFriendList renamed to AddFriends |
|||
Added: Operation RemoveFriends and corresponding method in ChatClient.cs |
|||
Added: Console Demo has new command 'fr' to remove friends |
|||
|
|||
Version 4.0.0.0 (23. May 2014 - rev2614) |
|||
Added: SendPrivateMessage() overload that has option to encrypt private messages. Public messages don't need encryption. |
|||
Removed: lastId and LastMessageIndex from channels. Those were impractical and should be removed from the API. |
|||
Changed: UserStatus class to ChatUserStatus. |
|||
Changed: Most classes are defined in their own file now. |
|||
Removed: Folders "Shared" and their subfolders. This gives a much better overview. |
|||
Added: Handling for event SubscribeResponse. This is not actually a response but gives you info about channels that got subscribed (e.g. when you re-login quickly or when a user is logged in in multiple clients). |
|||
Added: HandleSubscriptionResults() method to handle the event and proper responses. |
|
|||
fileFormatVersion: 2 |
|||
guid: 4b1b7c7ca29327d48b0172f7cbfd7491 |
|||
TextScriptImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 1bfec6f6ef56ba64097eca90791237af |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 887ac71c799552346b6cf7654fb699cb |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
userData: |
1001
Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Metro/Photon3Unity3D.dll
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 3b5a0a7dce46a13459199d174ad3db3f |
|||
PluginImporter: |
|||
serializedVersion: 1 |
|||
iconMap: {} |
|||
executionOrder: {} |
|||
isPreloaded: 0 |
|||
platformData: |
|||
Android: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
Any: |
|||
enabled: 0 |
|||
settings: {} |
|||
Editor: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
DefaultValueInitialized: true |
|||
OS: AnyOS |
|||
Linux: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: x86 |
|||
Linux64: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: x86_64 |
|||
OSXIntel: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
OSXIntel64: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
WP8: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
DontProcess: False |
|||
PlaceholderPath: Assets/Photon/PhotonLibs/Photon3Unity3D.dll |
|||
SDK: AnySDK |
|||
ScriptingBackend: DotNet |
|||
Win: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
Win64: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
WindowsStoreApps: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
DontProcess: False |
|||
PlaceholderPath: Assets/Photon/PhotonLibs/Photon3Unity3D.dll |
|||
SDK: AnySDK |
|||
ScriptingBackend: DotNet |
|||
iOS: |
|||
enabled: 0 |
|||
settings: |
|||
CompileFlags: |
|||
FrameworkDependencies: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
mrm_pri2 � � �� [mrm_decn_info] X [mrm_pridescex] X H [mrm_hschemaex] � � [mrm_res_map2_] x � [mrm_decn_info] X ����X [mrm_pridescex] H �� ����H [mrm_hschemaex] � [def_hnames] s��� m s - a p p x : / / P h o t o n 3 U n i t y 3 D / P h o t o n 3 U n i t y 3 D 0 ����� [mrm_res_map2_] � ���ހ ���ި mrm_pri2 |
|
|||
fileFormatVersion: 2 |
|||
guid: 3439a9e4030efca45b6cc06240106c02 |
|||
timeCreated: 1460035811 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Photon3Unity3D.dll
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: aadb37a20a33632429047acaef43658a |
|||
labels: |
|||
- ExitGames |
|||
- PUN |
|||
- Photon |
|||
- Networking |
|||
PluginImporter: |
|||
serializedVersion: 1 |
|||
iconMap: {} |
|||
executionOrder: {} |
|||
isPreloaded: 0 |
|||
platformData: |
|||
Android: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
Any: |
|||
enabled: 0 |
|||
settings: {} |
|||
Editor: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
DefaultValueInitialized: true |
|||
OS: AnyOS |
|||
Linux: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: x86 |
|||
Linux64: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: x86_64 |
|||
LinuxUniversal: |
|||
enabled: 1 |
|||
settings: {} |
|||
OSXIntel: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
OSXIntel64: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
OSXUniversal: |
|||
enabled: 1 |
|||
settings: {} |
|||
WP8: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
DontProcess: False |
|||
PlaceholderPath: |
|||
SDK: AnySDK |
|||
ScriptingBackend: Il2Cpp |
|||
Web: |
|||
enabled: 1 |
|||
settings: {} |
|||
WebGL: |
|||
enabled: 1 |
|||
settings: {} |
|||
WebStreamed: |
|||
enabled: 1 |
|||
settings: {} |
|||
Win: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
Win64: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
WindowsStoreApps: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
DontProcess: False |
|||
PlaceholderPath: |
|||
SDK: AnySDK |
|||
ScriptingBackend: Il2Cpp |
|||
iOS: |
|||
enabled: 1 |
|||
settings: |
|||
CompileFlags: |
|||
FrameworkDependencies: |
|||
tvOS: |
|||
enabled: 1 |
|||
settings: |
|||
CompileFlags: |
|||
FrameworkDependencies: |
|||
PS4: |
|||
enabled: 1 |
|||
settings: {} |
|||
Switch: |
|||
enabled: 1 |
|||
settings: {} |
|||
XboxOne: |
|||
enabled: 1 |
|||
settings: {} |
|||
Lumin: |
|||
enabled: 1 |
|||
settings: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/Photon3Unity3D.xml
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 8d4f08d435c4b6343969d8af249460ff |
|||
labels: |
|||
- ExitGames |
|||
- PUN |
|||
- Photon |
|||
- Networking |
|||
TextScriptImporter: |
|||
userData: |
|
|||
fileFormatVersion: 2 |
|||
guid: 2f243ce0406bd1c40a9ff5fc2d78d905 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
userData: |
|
|||
#if UNITY_WEBGL || WEBSOCKET || (UNITY_XBOXONE && UNITY_EDITOR)
|
|||
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="SocketWebTcp.cs" company="Exit Games GmbH">
|
|||
// Copyright (c) Exit Games GmbH. All rights reserved.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Internal class to encapsulate the network i/o functionality for the realtime library.
|
|||
// </summary>
|
|||
// <author>developer@exitgames.com</author>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
|
|||
namespace ExitGames.Client.Photon |
|||
{ |
|||
using System; |
|||
using System.Collections; |
|||
using UnityEngine; |
|||
using SupportClassPun = ExitGames.Client.Photon.SupportClass; |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Yield Instruction to Wait for real seconds. Very important to keep connection working if Time.TimeScale is altered, we still want accurate network events
|
|||
/// </summary>
|
|||
public sealed class WaitForRealSeconds : CustomYieldInstruction |
|||
{ |
|||
private readonly float _endTime; |
|||
|
|||
public override bool keepWaiting |
|||
{ |
|||
get { return this._endTime > Time.realtimeSinceStartup; } |
|||
} |
|||
|
|||
public WaitForRealSeconds(float seconds) |
|||
{ |
|||
this._endTime = Time.realtimeSinceStartup + seconds; |
|||
} |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Internal class to encapsulate the network i/o functionality for the realtime libary.
|
|||
/// </summary>
|
|||
public class SocketWebTcp : IPhotonSocket, IDisposable |
|||
{ |
|||
private WebSocket sock; |
|||
|
|||
private readonly object syncer = new object(); |
|||
|
|||
public SocketWebTcp(PeerBase npeer) : base(npeer) |
|||
{ |
|||
this.ServerAddress = npeer.ServerAddress; |
|||
if (this.ReportDebugOfLevel(DebugLevel.INFO)) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.INFO, "new SocketWebTcp() for Unity. Server: " + this.ServerAddress); |
|||
} |
|||
|
|||
//this.Protocol = ConnectionProtocol.WebSocket;
|
|||
this.PollReceive = false; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
this.State = PhotonSocketState.Disconnecting; |
|||
|
|||
if (this.sock != null) |
|||
{ |
|||
try |
|||
{ |
|||
if (this.sock.Connected) this.sock.Close(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
this.EnqueueDebugReturn(DebugLevel.INFO, "Exception in Dispose(): " + ex); |
|||
} |
|||
} |
|||
|
|||
this.sock = null; |
|||
this.State = PhotonSocketState.Disconnected; |
|||
} |
|||
|
|||
GameObject websocketConnectionObject; |
|||
public override bool Connect() |
|||
{ |
|||
//bool baseOk = base.Connect();
|
|||
//if (!baseOk)
|
|||
//{
|
|||
// return false;
|
|||
//}
|
|||
|
|||
|
|||
this.State = PhotonSocketState.Connecting; |
|||
|
|||
if (this.websocketConnectionObject != null) |
|||
{ |
|||
UnityEngine.Object.Destroy(this.websocketConnectionObject); |
|||
} |
|||
|
|||
this.websocketConnectionObject = new GameObject("websocketConnectionObject"); |
|||
MonoBehaviour mb = this.websocketConnectionObject.AddComponent<MonoBehaviourExt>(); |
|||
this.websocketConnectionObject.hideFlags = HideFlags.HideInHierarchy; |
|||
UnityEngine.Object.DontDestroyOnLoad(this.websocketConnectionObject); |
|||
this.sock = new WebSocket(new Uri(this.ConnectAddress), this.SerializationProtocol); |
|||
this.sock.Connect(); |
|||
|
|||
mb.StartCoroutine(this.ReceiveLoop()); |
|||
return true; |
|||
} |
|||
|
|||
|
|||
public override bool Disconnect() |
|||
{ |
|||
if (this.ReportDebugOfLevel(DebugLevel.INFO)) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.INFO, "SocketWebTcp.Disconnect()"); |
|||
} |
|||
|
|||
this.State = PhotonSocketState.Disconnecting; |
|||
|
|||
lock (this.syncer) |
|||
{ |
|||
if (this.sock != null) |
|||
{ |
|||
try |
|||
{ |
|||
this.sock.Close(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.ERROR, "Exception in Disconnect(): " + ex); |
|||
} |
|||
this.sock = null; |
|||
} |
|||
} |
|||
|
|||
if (this.websocketConnectionObject != null) |
|||
{ |
|||
UnityEngine.Object.Destroy(this.websocketConnectionObject); |
|||
} |
|||
|
|||
this.State = PhotonSocketState.Disconnected; |
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// used by TPeer*
|
|||
/// </summary>
|
|||
public override PhotonSocketError Send(byte[] data, int length) |
|||
{ |
|||
if (this.State != PhotonSocketState.Connected) |
|||
{ |
|||
return PhotonSocketError.Skipped; |
|||
} |
|||
|
|||
try |
|||
{ |
|||
if (data.Length > length) |
|||
{ |
|||
byte[] trimmedData = new byte[length]; |
|||
Buffer.BlockCopy(data, 0, trimmedData, 0, length); |
|||
data = trimmedData; |
|||
} |
|||
|
|||
if (this.ReportDebugOfLevel(DebugLevel.ALL)) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.ALL, "Sending: " + SupportClassPun.ByteArrayToString(data)); |
|||
} |
|||
|
|||
if (this.sock != null) |
|||
{ |
|||
this.sock.Send(data); |
|||
} |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.ERROR, "Cannot send to: " + this.ServerAddress + ". " + e.Message); |
|||
|
|||
this.HandleException(StatusCode.Exception); |
|||
return PhotonSocketError.Exception; |
|||
} |
|||
|
|||
return PhotonSocketError.Success; |
|||
} |
|||
|
|||
public override PhotonSocketError Receive(out byte[] data) |
|||
{ |
|||
data = null; |
|||
return PhotonSocketError.NoData; |
|||
} |
|||
|
|||
|
|||
internal const int ALL_HEADER_BYTES = 9; |
|||
internal const int TCP_HEADER_BYTES = 7; |
|||
internal const int MSG_HEADER_BYTES = 2; |
|||
|
|||
public IEnumerator ReceiveLoop() |
|||
{ |
|||
//this.Listener.DebugReturn(DebugLevel.INFO, "ReceiveLoop()");
|
|||
if (this.sock != null) |
|||
{ |
|||
while (this.sock != null && !this.sock.Connected && this.sock.Error == null) |
|||
{ |
|||
yield return new WaitForRealSeconds(0.1f); |
|||
} |
|||
|
|||
|
|||
if (this.sock != null) |
|||
{ |
|||
if (this.sock.Error != null) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.ERROR, "Exiting receive thread. Server: " + this.ServerAddress + ":" + this.ServerPort + " Error: " + this.sock.Error); |
|||
this.HandleException(StatusCode.ExceptionOnConnect); |
|||
} |
|||
else |
|||
{ |
|||
// connected
|
|||
if (this.ReportDebugOfLevel(DebugLevel.ALL)) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.ALL, "Receiving by websocket. this.State: " + this.State); |
|||
} |
|||
|
|||
this.State = PhotonSocketState.Connected; |
|||
this.peerBase.OnConnect(); |
|||
|
|||
while (this.State == PhotonSocketState.Connected) |
|||
{ |
|||
if (this.sock != null) |
|||
{ |
|||
if (this.sock.Error != null) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.ERROR, "Exiting receive thread (inside loop). Server: " + this.ServerAddress + ":" + this.ServerPort + " Error: " + this.sock.Error); |
|||
this.HandleException(StatusCode.ExceptionOnReceive); |
|||
break; |
|||
} |
|||
else |
|||
{ |
|||
byte[] inBuff = this.sock.Recv(); |
|||
if (inBuff == null || inBuff.Length == 0) |
|||
{ |
|||
// nothing received. wait a bit, try again
|
|||
yield return new WaitForRealSeconds(0.02f); |
|||
continue; |
|||
} |
|||
|
|||
if (this.ReportDebugOfLevel(DebugLevel.ALL)) |
|||
{ |
|||
this.Listener.DebugReturn(DebugLevel.ALL, "TCP << " + inBuff.Length + " = " + SupportClassPun.ByteArrayToString(inBuff)); |
|||
} |
|||
|
|||
if (inBuff.Length > 0) |
|||
{ |
|||
try |
|||
{ |
|||
this.HandleReceivedDatagram(inBuff, inBuff.Length, false); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
if (this.State != PhotonSocketState.Disconnecting && this.State != PhotonSocketState.Disconnected) |
|||
{ |
|||
if (this.ReportDebugOfLevel(DebugLevel.ERROR)) |
|||
{ |
|||
this.EnqueueDebugReturn(DebugLevel.ERROR, "Receive issue. State: " + this.State + ". Server: '" + this.ServerAddress + "' Exception: " + e); |
|||
} |
|||
|
|||
this.HandleException(StatusCode.ExceptionOnReceive); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
this.Disconnect(); |
|||
} |
|||
|
|||
private class MonoBehaviourExt : MonoBehaviour { } |
|||
} |
|||
} |
|||
|
|||
#endif
|
|
|||
fileFormatVersion: 2 |
|||
guid: ac953d6a57a9ea94e96ec689598995d5 |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|
|||
#if UNITY_WEBGL || UNITY_XBOXONE || WEBSOCKET
|
|||
|
|||
using System; |
|||
using System.Text; |
|||
|
|||
#if UNITY_WEBGL && !UNITY_EDITOR
|
|||
using System.Runtime.InteropServices; |
|||
#else
|
|||
using System.Collections.Generic; |
|||
using System.Security.Authentication; |
|||
#endif
|
|||
|
|||
|
|||
public class WebSocket |
|||
{ |
|||
private Uri mUrl; |
|||
/// <summary>Photon uses this to agree on a serialization protocol. Either: GpBinaryV16 or GpBinaryV18. Based on enum SerializationProtocol.</summary>
|
|||
private string protocols = "GpBinaryV16"; |
|||
|
|||
public WebSocket(Uri url, string serialization = null) |
|||
{ |
|||
this.mUrl = url; |
|||
if (serialization != null) |
|||
{ |
|||
this.protocols = serialization; |
|||
} |
|||
|
|||
string protocol = mUrl.Scheme; |
|||
if (!protocol.Equals("ws") && !protocol.Equals("wss")) |
|||
throw new ArgumentException("Unsupported protocol: " + protocol); |
|||
} |
|||
|
|||
public void SendString(string str) |
|||
{ |
|||
Send(Encoding.UTF8.GetBytes (str)); |
|||
} |
|||
|
|||
public string RecvString() |
|||
{ |
|||
byte[] retval = Recv(); |
|||
if (retval == null) |
|||
return null; |
|||
return Encoding.UTF8.GetString (retval); |
|||
} |
|||
|
|||
#if UNITY_WEBGL && !UNITY_EDITOR
|
|||
[DllImport("__Internal")] |
|||
private static extern int SocketCreate (string url, string protocols); |
|||
|
|||
[DllImport("__Internal")] |
|||
private static extern int SocketState (int socketInstance); |
|||
|
|||
[DllImport("__Internal")] |
|||
private static extern void SocketSend (int socketInstance, byte[] ptr, int length); |
|||
|
|||
[DllImport("__Internal")] |
|||
private static extern void SocketRecv (int socketInstance, byte[] ptr, int length); |
|||
|
|||
[DllImport("__Internal")] |
|||
private static extern int SocketRecvLength (int socketInstance); |
|||
|
|||
[DllImport("__Internal")] |
|||
private static extern void SocketClose (int socketInstance); |
|||
|
|||
[DllImport("__Internal")] |
|||
private static extern int SocketError (int socketInstance, byte[] ptr, int length); |
|||
|
|||
int m_NativeRef = 0; |
|||
|
|||
public void Send(byte[] buffer) |
|||
{ |
|||
SocketSend (m_NativeRef, buffer, buffer.Length); |
|||
} |
|||
|
|||
public byte[] Recv() |
|||
{ |
|||
int length = SocketRecvLength (m_NativeRef); |
|||
if (length == 0) |
|||
return null; |
|||
byte[] buffer = new byte[length]; |
|||
SocketRecv (m_NativeRef, buffer, length); |
|||
return buffer; |
|||
} |
|||
|
|||
public void Connect() |
|||
{ |
|||
m_NativeRef = SocketCreate (mUrl.ToString(), this.protocols); |
|||
|
|||
//while (SocketState(m_NativeRef) == 0)
|
|||
// yield return 0;
|
|||
} |
|||
|
|||
public void Close() |
|||
{ |
|||
SocketClose(m_NativeRef); |
|||
} |
|||
|
|||
public bool Connected |
|||
{ |
|||
get { return SocketState(m_NativeRef) != 0; } |
|||
} |
|||
|
|||
public string Error |
|||
{ |
|||
get { |
|||
const int bufsize = 1024; |
|||
byte[] buffer = new byte[bufsize]; |
|||
int result = SocketError (m_NativeRef, buffer, bufsize); |
|||
|
|||
if (result == 0) |
|||
return null; |
|||
|
|||
return Encoding.UTF8.GetString (buffer); |
|||
} |
|||
} |
|||
#else
|
|||
WebSocketSharp.WebSocket m_Socket; |
|||
Queue<byte[]> m_Messages = new Queue<byte[]>(); |
|||
bool m_IsConnected = false; |
|||
string m_Error = null; |
|||
|
|||
public void Connect() |
|||
{ |
|||
m_Socket = new WebSocketSharp.WebSocket(mUrl.ToString(), new string[] { this.protocols }); |
|||
m_Socket.SslConfiguration.EnabledSslProtocols = m_Socket.SslConfiguration.EnabledSslProtocols | (SslProtocols)(3072| 768); |
|||
m_Socket.OnMessage += (sender, e) => m_Messages.Enqueue(e.RawData); |
|||
m_Socket.OnOpen += (sender, e) => m_IsConnected = true; |
|||
m_Socket.OnError += (sender, e) => m_Error = e.Message + (e.Exception == null ? "" : " / " + e.Exception); |
|||
m_Socket.ConnectAsync(); |
|||
} |
|||
|
|||
public bool Connected { get { return m_IsConnected; } }// added by TS
|
|||
|
|||
|
|||
public void Send(byte[] buffer) |
|||
{ |
|||
m_Socket.Send(buffer); |
|||
} |
|||
|
|||
public byte[] Recv() |
|||
{ |
|||
if (m_Messages.Count == 0) |
|||
return null; |
|||
return m_Messages.Dequeue(); |
|||
} |
|||
|
|||
public void Close() |
|||
{ |
|||
m_Socket.Close(); |
|||
} |
|||
|
|||
public string Error |
|||
{ |
|||
get |
|||
{ |
|||
return m_Error; |
|||
} |
|||
} |
|||
#endif
|
|||
} |
|||
#endif
|
|
|||
fileFormatVersion: 2 |
|||
guid: b1bad04f7805f764dba77f0d4518e0f0 |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|
|||
var LibraryWebSockets = { |
|||
$webSocketInstances: [], |
|||
|
|||
SocketCreate: function(url, protocols) |
|||
{ |
|||
var str = Pointer_stringify(url); |
|||
var prot = Pointer_stringify(protocols); |
|||
var socket = { |
|||
socket: new WebSocket(str, [prot]), |
|||
buffer: new Uint8Array(0), |
|||
error: null, |
|||
messages: [] |
|||
} |
|||
socket.socket.binaryType = 'arraybuffer'; |
|||
socket.socket.onmessage = function (e) { |
|||
// if (e.data instanceof Blob) |
|||
// { |
|||
// var reader = new FileReader(); |
|||
// reader.addEventListener("loadend", function() { |
|||
// var array = new Uint8Array(reader.result); |
|||
// socket.messages.push(array); |
|||
// }); |
|||
// reader.readAsArrayBuffer(e.data); |
|||
// } |
|||
if (e.data instanceof ArrayBuffer) |
|||
{ |
|||
var array = new Uint8Array(e.data); |
|||
socket.messages.push(array); |
|||
} |
|||
}; |
|||
socket.socket.onclose = function (e) { |
|||
if (e.code != 1000) |
|||
{ |
|||
if (e.reason != null && e.reason.length > 0) |
|||
socket.error = e.reason; |
|||
else |
|||
{ |
|||
switch (e.code) |
|||
{ |
|||
case 1001: |
|||
socket.error = "Endpoint going away."; |
|||
break; |
|||
case 1002: |
|||
socket.error = "Protocol error."; |
|||
break; |
|||
case 1003: |
|||
socket.error = "Unsupported message."; |
|||
break; |
|||
case 1005: |
|||
socket.error = "No status."; |
|||
break; |
|||
case 1006: |
|||
socket.error = "Abnormal disconnection."; |
|||
break; |
|||
case 1009: |
|||
socket.error = "Data frame too large."; |
|||
break; |
|||
default: |
|||
socket.error = "Error "+e.code; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
var instance = webSocketInstances.push(socket) - 1; |
|||
return instance; |
|||
}, |
|||
|
|||
SocketState: function (socketInstance) |
|||
{ |
|||
var socket = webSocketInstances[socketInstance]; |
|||
return socket.socket.readyState; |
|||
}, |
|||
|
|||
SocketError: function (socketInstance, ptr, bufsize) |
|||
{ |
|||
var socket = webSocketInstances[socketInstance]; |
|||
if (socket.error == null) |
|||
return 0; |
|||
var str = socket.error.slice(0, Math.max(0, bufsize - 1)); |
|||
writeStringToMemory(str, ptr, false); |
|||
return 1; |
|||
}, |
|||
|
|||
SocketSend: function (socketInstance, ptr, length) |
|||
{ |
|||
var socket = webSocketInstances[socketInstance]; |
|||
socket.socket.send (HEAPU8.buffer.slice(ptr, ptr+length)); |
|||
}, |
|||
|
|||
SocketRecvLength: function(socketInstance) |
|||
{ |
|||
var socket = webSocketInstances[socketInstance]; |
|||
if (socket.messages.length == 0) |
|||
return 0; |
|||
return socket.messages[0].length; |
|||
}, |
|||
|
|||
SocketRecv: function (socketInstance, ptr, length) |
|||
{ |
|||
var socket = webSocketInstances[socketInstance]; |
|||
if (socket.messages.length == 0) |
|||
return 0; |
|||
if (socket.messages[0].length > length) |
|||
return 0; |
|||
HEAPU8.set(socket.messages[0], ptr); |
|||
socket.messages = socket.messages.slice(1); |
|||
}, |
|||
|
|||
SocketClose: function (socketInstance) |
|||
{ |
|||
var socket = webSocketInstances[socketInstance]; |
|||
socket.socket.close(); |
|||
} |
|||
}; |
|||
|
|||
autoAddDeps(LibraryWebSockets, '$webSocketInstances'); |
|||
mergeInto(LibraryManager.library, LibraryWebSockets); |
|
|||
fileFormatVersion: 2 |
|||
guid: 04bb5f307f2e48b4fbaa6da865fd4091 |
|||
PluginImporter: |
|||
serializedVersion: 1 |
|||
iconMap: {} |
|||
executionOrder: {} |
|||
isPreloaded: 0 |
|||
platformData: |
|||
Any: |
|||
enabled: 0 |
|||
settings: {} |
|||
Editor: |
|||
enabled: 0 |
|||
settings: |
|||
DefaultValueInitialized: true |
|||
WebGL: |
|||
enabled: 1 |
|||
settings: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
websocket-sharp.dll built from https://github.com/sta/websocket-sharp.git, commit 869dfb09778de51081b0ae64bd2c3217cffe0699 on Aug 24, 2016. |
|||
|
|||
websocket-sharp is provided under The MIT License as mentioned here: https://github.com/sta/websocket-sharp#license |
|
|||
fileFormatVersion: 2 |
|||
guid: 3e262c2b04eaa8440987b50a91e86674 |
|||
DefaultImporter: |
|||
userData: |
1001
Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/WebSocket/websocket-sharp.dll
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 748eb70bc0d7515498ef73fed155520a |
|||
PluginImporter: |
|||
serializedVersion: 1 |
|||
iconMap: {} |
|||
executionOrder: {} |
|||
isPreloaded: 0 |
|||
platformData: |
|||
Android: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
Any: |
|||
enabled: 0 |
|||
settings: {} |
|||
Editor: |
|||
enabled: 1 |
|||
settings: |
|||
CPU: AnyCPU |
|||
DefaultValueInitialized: true |
|||
OS: AnyOS |
|||
Linux: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: x86 |
|||
Linux64: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: x86_64 |
|||
OSXIntel: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
OSXIntel64: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
WP8: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
DontProcess: False |
|||
PlaceholderPath: |
|||
Win: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
Win64: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
WindowsStoreApps: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
DontProcess: False |
|||
PlaceholderPath: |
|||
SDK: AnySDK |
|||
XboxOne: |
|||
enabled: 1 |
|||
settings: {} |
|||
iOS: |
|||
enabled: 0 |
|||
settings: |
|||
CompileFlags: |
|||
FrameworkDependencies: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/changes-library.txt
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: d8040d310df77714a90a561261bfb2cb |
|||
timeCreated: 1557919981 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: beb9d2a25c882b54ab2fd9adaec2b53c |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
{ |
|||
"runtimeTarget": { |
|||
"name": ".NETStandard,Version=v2.0/", |
|||
"signature": "" |
|||
}, |
|||
"compilationOptions": {}, |
|||
"targets": { |
|||
".NETStandard,Version=v2.0": {}, |
|||
".NETStandard,Version=v2.0/": { |
|||
"Photon3Unity3D/4.1.4.4": { |
|||
"dependencies": { |
|||
"NETStandard.Library": "2.0.3" |
|||
}, |
|||
"runtime": { |
|||
"Photon3Unity3D.dll": {} |
|||
} |
|||
}, |
|||
"Microsoft.NETCore.Platforms/1.1.0": {}, |
|||
"NETStandard.Library/2.0.3": { |
|||
"dependencies": { |
|||
"Microsoft.NETCore.Platforms": "1.1.0" |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"libraries": { |
|||
"Photon3Unity3D/4.1.4.4": { |
|||
"type": "project", |
|||
"serviceable": false, |
|||
"sha512": "" |
|||
}, |
|||
"Microsoft.NETCore.Platforms/1.1.0": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", |
|||
"path": "microsoft.netcore.platforms/1.1.0", |
|||
"hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512" |
|||
}, |
|||
"NETStandard.Library/2.0.3": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", |
|||
"path": "netstandard.library/2.0.3", |
|||
"hashPath": "netstandard.library.2.0.3.nupkg.sha512" |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a966b98b651e18748abf2829786b5899 |
|||
TextScriptImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.dll
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 6a558ae793155af4b9b9ab945fc64a0f |
|||
PluginImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
iconMap: {} |
|||
executionOrder: {} |
|||
isPreloaded: 0 |
|||
isOverridable: 0 |
|||
platformData: |
|||
- first: |
|||
'': Any |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
Exclude Android: 1 |
|||
Exclude Editor: 1 |
|||
Exclude Linux: 1 |
|||
Exclude Linux64: 1 |
|||
Exclude LinuxUniversal: 1 |
|||
Exclude OSXUniversal: 1 |
|||
Exclude WebGL: 1 |
|||
Exclude Win: 1 |
|||
Exclude Win64: 1 |
|||
- first: |
|||
Android: Android |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: ARMv7 |
|||
- first: |
|||
Any: |
|||
second: |
|||
enabled: 0 |
|||
settings: {} |
|||
- first: |
|||
Editor: Editor |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
DefaultValueInitialized: true |
|||
OS: AnyOS |
|||
- first: |
|||
Facebook: Win |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
- first: |
|||
Facebook: Win64 |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
- first: |
|||
Standalone: Linux |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: x86 |
|||
- first: |
|||
Standalone: Linux64 |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: x86_64 |
|||
- first: |
|||
Standalone: LinuxUniversal |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: None |
|||
- first: |
|||
Standalone: OSXUniversal |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
- first: |
|||
Standalone: Win |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
- first: |
|||
Standalone: Win64 |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
- first: |
|||
Windows Store Apps: WindowsStoreApps |
|||
second: |
|||
enabled: 0 |
|||
settings: |
|||
CPU: AnyCPU |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.xml
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 7015e500cd5b71244af56448dfb59804 |
|||
TextScriptImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 414f6601a5d7a674895efad90f9d1f49 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 064a4c0213b08584d9aa4fe170bb3b66 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// -----------------------------------------------------------------------
|
|||
// <copyright file="AppSettings.cs" company="Exit Games GmbH">
|
|||
// Loadbalancing Framework for Photon - Copyright (C) 2018 Exit Games GmbH
|
|||
// </copyright>
|
|||
// <summary>Settings for Photon application(s) and the server to connect to.</summary>
|
|||
// <author>developer@photonengine.com</author>
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
#if UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER
|
|||
#define SUPPORTED_UNITY
|
|||
#endif
|
|||
|
|||
|
|||
namespace Photon.Realtime |
|||
{ |
|||
using System; |
|||
using ExitGames.Client.Photon; |
|||
|
|||
#if SUPPORTED_UNITY || NETFX_CORE
|
|||
using Hashtable = ExitGames.Client.Photon.Hashtable; |
|||
using SupportClass = ExitGames.Client.Photon.SupportClass; |
|||
#endif
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Settings for Photon application(s) and the server to connect to.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This is Serializable for Unity, so it can be included in ScriptableObject instances.
|
|||
/// </remarks>
|
|||
#if !NETFX_CORE || SUPPORTED_UNITY
|
|||
[Serializable] |
|||
#endif
|
|||
public class AppSettings |
|||
{ |
|||
/// <summary>AppId for Realtime or PUN.</summary>
|
|||
public string AppIdRealtime; |
|||
/// <summary>AppId for the Chat Api.</summary>
|
|||
public string AppIdChat; |
|||
/// <summary>AppId for use in the Voice Api.</summary>
|
|||
public string AppIdVoice; |
|||
|
|||
/// <summary>The AppVersion can be used to identify builds and will split the AppId distinct "Virtual AppIds" (important for matchmaking).</summary>
|
|||
public string AppVersion; |
|||
|
|||
|
|||
/// <summary>If false, the app will attempt to connect to a Master Server (which is obsolete but sometimes still necessary).</summary>
|
|||
/// <remarks>if true, Server points to a NameServer (or is null, using the default), else it points to a MasterServer.</remarks>
|
|||
public bool UseNameServer = true; |
|||
|
|||
/// <summary>Can be set to any of the Photon Cloud's region names to directly connect to that region.</summary>
|
|||
/// <remarks>if this IsNullOrEmpty() AND UseNameServer == true, use BestRegion. else, use a server</remarks>
|
|||
public string FixedRegion; |
|||
|
|||
/// <summary>Set to a previous BestRegionSummary value before connecting.</summary>
|
|||
/// <remarks>
|
|||
/// This is a value used when the client connects to the "Best Region".</br>
|
|||
/// If this is null or empty, all regions gets pinged. Providing a previous summary on connect,
|
|||
/// speeds up best region selection and makes the previously selected region "sticky".</br>
|
|||
///
|
|||
/// Unity clients should store the BestRegionSummary in the PlayerPrefs.
|
|||
/// You can store the new result by implementing <see cref="IConnectionCallbacks.OnConnectedToMaster"/>.
|
|||
/// If <see cref="LoadBalancingClient.SummaryToCache"/> is not null, store this string.
|
|||
/// To avoid storing the value multiple times, you could set SummaryToCache to null.
|
|||
/// </remarks>
|
|||
#if SUPPORTED_UNITY
|
|||
[NonSerialized] |
|||
#endif
|
|||
public string BestRegionSummaryFromStorage; |
|||
|
|||
/// <summary>The address (hostname or IP) of the server to connect to.</summary>
|
|||
public string Server; |
|||
/// <summary>If not null, this sets the port of the first Photon server to connect to (that will "forward" the client as needed).</summary>
|
|||
public int Port; |
|||
/// <summary>The address (hostname or IP and port) of the proxy server.</summary>
|
|||
public string ProxyServer; |
|||
/// <summary>The network level protocol to use.</summary>
|
|||
public ConnectionProtocol Protocol = ConnectionProtocol.Udp; |
|||
|
|||
/// <summary>Enables a fallback to another protocol in case a connect to the Name Server fails.</summary>
|
|||
/// <remarks>See: LoadBalancingClient.EnableProtocolFallback.</remarks>
|
|||
public bool EnableProtocolFallback = true; |
|||
|
|||
/// <summary>Defines how authentication is done. On each system, once or once via a WSS connection (safe).</summary>
|
|||
public AuthModeOption AuthMode = AuthModeOption.Auth; |
|||
|
|||
/// <summary>If true, the client will request the list of currently available lobbies.</summary>
|
|||
public bool EnableLobbyStatistics; |
|||
/// <summary>Log level for the network lib.</summary>
|
|||
public DebugLevel NetworkLogging = DebugLevel.ERROR; |
|||
|
|||
/// <summary>If true, the Server field contains a Master Server address (if any address at all).</summary>
|
|||
public bool IsMasterServerAddress { get { return !this.UseNameServer; } } |
|||
/// <summary>If true, the client should fetch the region list from the Name Server and find the one with best ping.</summary>
|
|||
/// <remarks>See "Best Region" in the online docs.</remarks>
|
|||
public bool IsBestRegion { get { return this.UseNameServer && string.IsNullOrEmpty(this.FixedRegion); } } |
|||
/// <summary>If true, the default nameserver address for the Photon Cloud should be used.</summary>
|
|||
public bool IsDefaultNameServer { get { return this.UseNameServer && string.IsNullOrEmpty(this.Server); } } |
|||
/// <summary>If true, the default ports for a protocol will be used.</summary>
|
|||
public bool IsDefaultPort { get { return this.Port <= 0; } } |
|||
|
|||
/// <summary>ToString but with more details.</summary>
|
|||
public string ToStringFull() |
|||
{ |
|||
return string.Format( |
|||
"appId {0}{1}{2}{3}" + |
|||
"use ns: {4}, reg: {5}, {9}, " + |
|||
"{6}{7}{8}" + |
|||
"auth: {10}", |
|||
String.IsNullOrEmpty(this.AppIdRealtime) ? string.Empty : "rt: " + this.HideAppId(this.AppIdRealtime) + ", ", |
|||
String.IsNullOrEmpty(this.AppIdChat) ? string.Empty : "chat: " + this.HideAppId(this.AppIdChat) + ", ", |
|||
String.IsNullOrEmpty(this.AppIdVoice) ? string.Empty : "voice: " + this.HideAppId(this.AppIdVoice) + ", ", |
|||
String.IsNullOrEmpty(this.AppVersion) ? string.Empty : "appV: " + this.AppVersion + ", ", |
|||
this.UseNameServer, |
|||
this.IsBestRegion ? "/best/" : this.FixedRegion, |
|||
//this.BestRegionSummaryFromStorage,
|
|||
String.IsNullOrEmpty(this.Server) ? string.Empty : "server: " + this.Server + ", ", |
|||
this.IsDefaultPort ? string.Empty : "port: " + this.Port + ", ", |
|||
String.IsNullOrEmpty(ProxyServer) ? string.Empty : "proxy: " + this.ProxyServer + ", ", |
|||
this.Protocol, |
|||
this.AuthMode |
|||
//this.EnableLobbyStatistics,
|
|||
//this.NetworkLogging,
|
|||
); |
|||
} |
|||
|
|||
private string HideAppId(string appId) |
|||
{ |
|||
return string.IsNullOrEmpty(appId) || appId.Length < 8 |
|||
? appId |
|||
: string.Concat(appId.Substring(0, 8), "***"); |
|||
} |
|||
|
|||
public AppSettings CopyTo(AppSettings d) |
|||
{ |
|||
d.AppIdRealtime = this.AppIdRealtime; |
|||
d.AppIdChat = this.AppIdChat; |
|||
d.AppIdVoice = this.AppIdVoice; |
|||
d.AppVersion = this.AppVersion; |
|||
d.UseNameServer = this.UseNameServer; |
|||
d.FixedRegion = this.FixedRegion; |
|||
d.BestRegionSummaryFromStorage = this.BestRegionSummaryFromStorage; |
|||
d.Server = this.Server; |
|||
d.Port = this.Port; |
|||
d.ProxyServer = this.ProxyServer; |
|||
d.Protocol = this.Protocol; |
|||
d.AuthMode = this.AuthMode; |
|||
d.EnableLobbyStatistics = this.EnableLobbyStatistics; |
|||
d.NetworkLogging = this.NetworkLogging; |
|||
return d; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a53d5fb81b78605468c7b0ff013c849d |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------
|
|||
// <copyright file="ConnectionHandler.cs" company="Exit Games GmbH">
|
|||
// Loadbalancing Framework for Photon - Copyright (C) 2018 Exit Games GmbH
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// If the game logic does not call Service() for whatever reason, this keeps the connection.
|
|||
// </summary>
|
|||
// <author>developer@photonengine.com</author>
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
|
|||
#if UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER
|
|||
#define SUPPORTED_UNITY
|
|||
#endif
|
|||
|
|||
|
|||
namespace Photon.Realtime |
|||
{ |
|||
using System; |
|||
using SupportClass = ExitGames.Client.Photon.SupportClass; |
|||
|
|||
#if SUPPORTED_UNITY
|
|||
using UnityEngine; |
|||
#endif
|
|||
|
|||
|
|||
#if SUPPORTED_UNITY
|
|||
public class ConnectionHandler : MonoBehaviour |
|||
#else
|
|||
public class ConnectionHandler |
|||
#endif
|
|||
{ |
|||
/// <summary>
|
|||
/// Photon client to log information and statistics from.
|
|||
/// </summary>
|
|||
public LoadBalancingClient Client { get; set; } |
|||
|
|||
private byte fallbackThreadId = 255; |
|||
|
|||
private bool didSendAcks; |
|||
private int startedAckingTimestamp; |
|||
private int deltaSinceStartedToAck; |
|||
|
|||
/// <summary>Defines for how long the Fallback Thread should keep the connection, before it may time out as usual.</summary>
|
|||
/// <remarks>We want to the Client to keep it's connection when an app is in the background (and doesn't call Update / Service Clients should not keep their connection indefinitely in the background, so after some milliseconds, the Fallback Thread should stop keeping it up.</remarks>
|
|||
public int KeepAliveInBackground = 60000; |
|||
|
|||
/// <summary>Counts how often the Fallback Thread called SendAcksOnly, which is purely of interest to monitor if the game logic called SendOutgoingCommands as intended.</summary>
|
|||
public int CountSendAcksOnly { get; private set; } |
|||
|
|||
public bool FallbackThreadRunning |
|||
{ |
|||
get { return this.fallbackThreadId < 255; } |
|||
} |
|||
|
|||
|
|||
#if SUPPORTED_UNITY
|
|||
|
|||
/// <summary>Keeps the ConnectionHandler, even if a new scene gets loaded.</summary>
|
|||
public bool ApplyDontDestroyOnLoad = true; |
|||
|
|||
/// <summary>Indicates that the app is closing. Set in OnApplicationQuit().</summary>
|
|||
[NonSerialized] |
|||
public static bool AppQuits; |
|||
|
|||
/// <summary>Called by Unity when the application gets closed. The UnityEngine will also call OnDisable, which disconnects.</summary>
|
|||
protected void OnApplicationQuit() |
|||
{ |
|||
AppQuits = true; |
|||
} |
|||
|
|||
|
|||
/// <summary></summary>
|
|||
protected virtual void Awake() |
|||
{ |
|||
if (this.ApplyDontDestroyOnLoad) |
|||
{ |
|||
DontDestroyOnLoad(this.gameObject); |
|||
} |
|||
} |
|||
|
|||
/// <summary>Called by Unity when the application gets closed. Disconnects if OnApplicationQuit() was called before.</summary>
|
|||
protected virtual void OnDisable() |
|||
{ |
|||
this.StopFallbackSendAckThread(); |
|||
|
|||
if (AppQuits) |
|||
{ |
|||
if (this.Client != null && this.Client.IsConnected) |
|||
{ |
|||
this.Client.Disconnect(); |
|||
this.Client.LoadBalancingPeer.StopThread(); |
|||
} |
|||
|
|||
SupportClass.StopAllBackgroundCalls(); |
|||
} |
|||
} |
|||
|
|||
#endif
|
|||
|
|||
|
|||
public void StartFallbackSendAckThread() |
|||
{ |
|||
#if !UNITY_WEBGL
|
|||
if (this.FallbackThreadRunning) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
#if UNITY_SWITCH
|
|||
this.fallbackThreadId = SupportClass.StartBackgroundCalls(this.RealtimeFallbackThread, 50); // as workaround, we don't name the Thread.
|
|||
#else
|
|||
this.fallbackThreadId = SupportClass.StartBackgroundCalls(this.RealtimeFallbackThread, 50, "RealtimeFallbackThread"); |
|||
#endif
|
|||
#endif
|
|||
} |
|||
|
|||
public void StopFallbackSendAckThread() |
|||
{ |
|||
#if !UNITY_WEBGL
|
|||
if (!this.FallbackThreadRunning) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
SupportClass.StopBackgroundCalls(this.fallbackThreadId); |
|||
this.fallbackThreadId = 255; |
|||
#endif
|
|||
} |
|||
|
|||
|
|||
/// <summary>A thread which runs independent from the Update() calls. Keeps connections online while loading or in background. See <see cref="KeepAliveInBackground"/>.</summary>
|
|||
public bool RealtimeFallbackThread() |
|||
{ |
|||
if (this.Client != null) |
|||
{ |
|||
if (!this.Client.IsConnected) |
|||
{ |
|||
this.didSendAcks = false; |
|||
return true; |
|||
} |
|||
|
|||
if (this.Client.LoadBalancingPeer.ConnectionTime - this.Client.LoadBalancingPeer.LastSendOutgoingTime > 100) |
|||
{ |
|||
if (this.didSendAcks) |
|||
{ |
|||
// check if the client should disconnect after some seconds in background
|
|||
this.deltaSinceStartedToAck = Environment.TickCount - this.startedAckingTimestamp; |
|||
if (this.deltaSinceStartedToAck > this.KeepAliveInBackground) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
this.startedAckingTimestamp = Environment.TickCount; |
|||
} |
|||
|
|||
|
|||
this.didSendAcks = true; |
|||
this.CountSendAcksOnly++; |
|||
this.Client.LoadBalancingPeer.SendAcksOnly(); |
|||
} |
|||
else |
|||
{ |
|||
this.didSendAcks = false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: ca21514fadd666444803e4f1e29331c8 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------
|
|||
// <copyright file="Extensions.cs" company="Exit Games GmbH">
|
|||
// Photon Extensions - Copyright (C) 2018 Exit Games GmbH
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Provides some helpful methods and extensions for Hashtables, etc.
|
|||
// </summary>
|
|||
// <author>developer@photonengine.com</author>
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
#if UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER
|
|||
#define SUPPORTED_UNITY
|
|||
#endif
|
|||
|
|||
|
|||
namespace Photon.Realtime |
|||
{ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using ExitGames.Client.Photon; |
|||
|
|||
#if SUPPORTED_UNITY
|
|||
using UnityEngine; |
|||
using Debug = UnityEngine.Debug; |
|||
#endif
|
|||
#if SUPPORTED_UNITY || NETFX_CORE
|
|||
using Hashtable = ExitGames.Client.Photon.Hashtable; |
|||
using SupportClass = ExitGames.Client.Photon.SupportClass; |
|||
#endif
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// This static class defines some useful extension methods for several existing classes (e.g. Vector3, float and others).
|
|||
/// </summary>
|
|||
public static class Extensions |
|||
{ |
|||
/// <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 SupportClass.DictionaryToString(origin, false); |
|||
} |
|||
|
|||
/// <summary>Helper method for debugging of List<T> content. Using this is not performant.</summary>
|
|||
/// <remarks>Should only be used for debugging as necessary.</remarks>
|
|||
/// <param name="data">Any List<T> where T implements .ToString().</param>
|
|||
/// <returns>A comma-separated string containing each value's ToString().</returns>
|
|||
public static string ToStringFull<T>(this List<T> data) |
|||
{ |
|||
if (data == null) return "null"; |
|||
|
|||
string[] sb = new string[data.Count]; |
|||
for (int i = 0; i < data.Count; i++) |
|||
{ |
|||
object o = data[i]; |
|||
sb[i] = (o != null) ? o.ToString() : "null"; |
|||
} |
|||
|
|||
return string.Join(", ", sb); |
|||
} |
|||
|
|||
/// <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>Used by StripKeysWithNullValues.</summary>
|
|||
/// <remarks>
|
|||
/// By making keysWithNullValue a static variable to clear before using, allocations only happen during the warm-up phase
|
|||
/// as the list needs to grow. Once it hit the high water mark for keys you need to remove.
|
|||
/// </remarks>
|
|||
private static readonly List<object> keysWithNullValue = new List<object>(); |
|||
|
|||
/// <summary>Removes all keys with null values.</summary>
|
|||
/// <remarks>
|
|||
/// Photon properties are removed by setting their value to null. Changes the original IDictionary!
|
|||
/// Uses lock(keysWithNullValue), which should be no problem in expected use cases.
|
|||
/// </remarks>
|
|||
/// <param name="original">The IDictionary to strip of keys with null value.</param>
|
|||
public static void StripKeysWithNullValues(this IDictionary original) |
|||
{ |
|||
lock (keysWithNullValue) |
|||
{ |
|||
keysWithNullValue.Clear(); |
|||
|
|||
foreach (DictionaryEntry entry in original) |
|||
{ |
|||
if (entry.Value == null) |
|||
{ |
|||
keysWithNullValue.Add(entry.Key); |
|||
} |
|||
} |
|||
|
|||
for (int i = 0; i < keysWithNullValue.Count; i++) |
|||
{ |
|||
var key = keysWithNullValue[i]; |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
|
|
|||
fileFormatVersion: 2 |
|||
guid: 7c44a529f6327f74192c77c930615b6e |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------
|
|||
// <copyright file="FriendInfo.cs" company="Exit Games GmbH">
|
|||
// Loadbalancing Framework for Photon - Copyright (C) 2018 Exit Games GmbH
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Collection of values related to a user / friend.
|
|||
// </summary>
|
|||
// <author>developer@photonengine.com</author>
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
#if UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER
|
|||
#define SUPPORTED_UNITY
|
|||
#endif
|
|||
|
|||
|
|||
namespace Photon.Realtime |
|||
{ |
|||
using ExitGames.Client.Photon; |
|||
|
|||
#if SUPPORTED_UNITY || NETFX_CORE
|
|||
using Hashtable = ExitGames.Client.Photon.Hashtable; |
|||
using SupportClass = ExitGames.Client.Photon.SupportClass; |
|||
#endif
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Used to store info about a friend's online state and in which room he/she is.
|
|||
/// </summary>
|
|||
public class FriendInfo |
|||
{ |
|||
[System.Obsolete("Use UserId.")] |
|||
public string Name { get { return this.UserId; } } |
|||
public string UserId { get; internal protected set; } |
|||
|
|||
public bool IsOnline { get; internal protected set; } |
|||
public string Room { get; internal protected set; } |
|||
|
|||
public bool IsInRoom |
|||
{ |
|||
get { return this.IsOnline && !string.IsNullOrEmpty(this.Room); } |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return string.Format("{0}\t is: {1}", this.UserId, (!this.IsOnline) ? "offline" : this.IsInRoom ? "playing" : "on master"); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: db008a1bee8a5754ba96b896a503e212 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/LoadBalancingClient.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 9fd0e102b1ad3f042afc3c02624b81d1 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Packages/com.unity.multiplayer.transport.photon-realtime/Runtime/Photon/PhotonRealtime/Code/LoadBalancingPeer.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 1f4cdbcd242e4f74299f4a780e4c3ff3 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------
|
|||
// <copyright file="PhotonPing.cs" company="Exit Games GmbH">
|
|||
// PhotonNetwork Framework for Unity - Copyright (C) 2018 Exit Games GmbH
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// This file includes various PhotonPing implementations for different APIs,
|
|||
// platforms and protocols.
|
|||
// The RegionPinger class is the instance which selects the Ping implementation
|
|||
// to use.
|
|||
// </summary>
|
|||
// <author>developer@exitgames.com</author>
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
|
|||
namespace Photon.Realtime |
|||
{ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Threading; |
|||
|
|||
#if NETFX_CORE
|
|||
using System.Diagnostics; |
|||
using Windows.Foundation; |
|||
using Windows.Networking; |
|||
using Windows.Networking.Sockets; |
|||
using Windows.Storage.Streams; |
|||
#endif
|
|||
|
|||
#if !NO_SOCKET && !NETFX_CORE
|
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Net.Sockets; |
|||
#endif
|
|||
|
|||
#if UNITY_WEBGL
|
|||
// import WWW class
|
|||
using UnityEngine; |
|||
#endif
|
|||
|
|||
/// <summary>
|
|||
/// Abstract implementation of PhotonPing, ase for pinging servers to find the "Best Region".
|
|||
/// </summary>
|
|||
public abstract class PhotonPing : IDisposable |
|||
{ |
|||
public string DebugString = ""; |
|||
public bool Successful; |
|||
|
|||
protected internal bool GotResult; |
|||
|
|||
protected internal int PingLength = 13; |
|||
|
|||
protected internal byte[] PingBytes = new byte[] { 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x00 }; |
|||
|
|||
protected internal byte PingId; |
|||
|
|||
private static readonly System.Random RandomIdProvider = new System.Random(); |
|||
|
|||
public virtual bool StartPing(string ip) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public virtual bool Done() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public virtual void Dispose() |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
protected internal void Init() |
|||
{ |
|||
this.GotResult = false; |
|||
this.Successful = false; |
|||
this.PingId = (byte)(RandomIdProvider.Next(255)); |
|||
} |
|||
} |
|||
|
|||
|
|||
#if !NETFX_CORE && !NO_SOCKET
|
|||
/// <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 PingMono : 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 interpreted as IPv4.</param>
|
|||
/// <returns>True if the Photon Ping could be sent.</returns>
|
|||
public override bool StartPing(string ip) |
|||
{ |
|||
this.Init(); |
|||
|
|||
try |
|||
{ |
|||
if (this.sock == null) |
|||
{ |
|||
if (ip.Contains(".")) |
|||
{ |
|||
this.sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); |
|||
} |
|||
else |
|||
{ |
|||
this.sock = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); |
|||
} |
|||
|
|||
this.sock.ReceiveTimeout = 5000; |
|||
this.sock.Connect(ip, 5055); |
|||
} |
|||
|
|||
|
|||
this.PingBytes[this.PingBytes.Length - 1] = this.PingId; |
|||
this.sock.Send(this.PingBytes); |
|||
this.PingBytes[this.PingBytes.Length - 1] = (byte)(this.PingId+1); // this buffer is re-used for the result/receive. invalidate the result now.
|
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
this.sock = null; |
|||
Console.WriteLine(e); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public override bool Done() |
|||
{ |
|||
if (this.GotResult || this.sock == null) |
|||
{ |
|||
return true; // this just indicates the ping is no longer waiting. this.Successful value defines if the roundtrip completed
|
|||
} |
|||
|
|||
int read = 0; |
|||
try |
|||
{ |
|||
if (!this.sock.Poll(0, SelectMode.SelectRead)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
read = this.sock.Receive(this.PingBytes, SocketFlags.None); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
if (this.sock != null) |
|||
{ |
|||
this.sock.Close(); |
|||
this.sock = null; |
|||
} |
|||
this.DebugString += " Exception of socket! " + ex.GetType() + " "; |
|||
return true; // this just indicates the ping is no longer waiting. this.Successful value defines if the roundtrip completed
|
|||
} |
|||
|
|||
bool replyMatch = this.PingBytes[this.PingBytes.Length - 1] == this.PingId && read == this.PingLength; |
|||
if (!replyMatch) |
|||
{ |
|||
this.DebugString += " ReplyMatch is false! "; |
|||
} |
|||
|
|||
|
|||
this.Successful = replyMatch; |
|||
this.GotResult = true; |
|||
return true; |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
try |
|||
{ |
|||
this.sock.Close(); |
|||
} |
|||
catch |
|||
{ |
|||
} |
|||
|
|||
this.sock = null; |
|||
} |
|||
|
|||
} |
|||
#endif
|
|||
|
|||
|
|||
#if NETFX_CORE
|
|||
/// <summary>Windows store API implementation of PhotonPing, based on DatagramSocket for UDP.</summary>
|
|||
public class PingWindowsStore : PhotonPing |
|||
{ |
|||
private DatagramSocket sock; |
|||
private readonly object syncer = new object(); |
|||
|
|||
public override bool StartPing(string host) |
|||
{ |
|||
base.Init(); |
|||
|
|||
EndpointPair endPoint = new EndpointPair(null, string.Empty, new HostName(host), "5055"); |
|||
this.sock = new DatagramSocket(); |
|||
this.sock.MessageReceived += OnMessageReceived; |
|||
|
|||
var result = this.sock.ConnectAsync(endPoint); |
|||
result.Completed = this.OnConnected; |
|||
this.DebugString += " End StartPing"; |
|||
return true; |
|||
} |
|||
|
|||
public override bool Done() |
|||
{ |
|||
return this.GotResult; |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
this.sock = null; |
|||
} |
|||
|
|||
private void OnConnected(IAsyncAction asyncinfo, AsyncStatus asyncstatus) |
|||
{ |
|||
if (asyncinfo.AsTask().IsCompleted) |
|||
{ |
|||
PingBytes[PingBytes.Length - 1] = PingId; |
|||
|
|||
DataWriter writer; |
|||
writer = new DataWriter(sock.OutputStream); |
|||
writer.WriteBytes(PingBytes); |
|||
var res = writer.StoreAsync(); |
|||
res.AsTask().Wait(100); |
|||
|
|||
writer.DetachStream(); |
|||
writer.Dispose(); |
|||
|
|||
PingBytes[PingBytes.Length - 1] = (byte)(PingId - 1); |
|||
} |
|||
else |
|||
{ |
|||
// TODO: handle error
|
|||
} |
|||
} |
|||
|
|||
private void OnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args) |
|||
{ |
|||
lock (syncer) |
|||
{ |
|||
DataReader reader = null; |
|||
try |
|||
{ |
|||
reader = args.GetDataReader(); |
|||
uint receivedByteCount = reader.UnconsumedBufferLength; |
|||
if (receivedByteCount > 0) |
|||
{ |
|||
var resultBytes = new byte[receivedByteCount]; |
|||
reader.ReadBytes(resultBytes); |
|||
|
|||
//TODO: check result bytes!
|
|||
|
|||
|
|||
this.Successful = receivedByteCount == PingLength && resultBytes[resultBytes.Length - 1] == PingId; |
|||
this.GotResult = true; |
|||
|
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
// TODO: handle error
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
|
|||
|
|||
#if NATIVE_SOCKETS
|
|||
/// <summary>Abstract base class to provide proper resource management for the below native ping implementations</summary>
|
|||
public abstract class PingNative : PhotonPing |
|||
{ |
|||
// Native socket states - according to EnetConnect.h state definitions
|
|||
protected enum NativeSocketState : byte |
|||
{ |
|||
Disconnected = 0, |
|||
Connecting = 1, |
|||
Connected = 2, |
|||
ConnectionError = 3, |
|||
SendError = 4, |
|||
ReceiveError = 5, |
|||
Disconnecting = 6 |
|||
} |
|||
|
|||
protected IntPtr pConnectionHandler = IntPtr.Zero; |
|||
|
|||
~PingNative() |
|||
{ |
|||
Dispose(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>Uses dynamic linked native Photon socket library via DllImport("PhotonSocketPlugin") attribute (as done by Unity Android and Unity PS3).</summary>
|
|||
public class PingNativeDynamic : PingNative |
|||
{ |
|||
public override bool StartPing(string ip) |
|||
{ |
|||
lock (SocketUdpNativeDynamic.syncer) |
|||
{ |
|||
base.Init(); |
|||
|
|||
if(pConnectionHandler == IntPtr.Zero) |
|||
{ |
|||
pConnectionHandler = SocketUdpNativeDynamic.egconnect(ip); |
|||
SocketUdpNativeDynamic.egservice(pConnectionHandler); |
|||
byte state = SocketUdpNativeDynamic.eggetState(pConnectionHandler); |
|||
while (state == (byte) NativeSocketState.Connecting) |
|||
{ |
|||
SocketUdpNativeDynamic.egservice(pConnectionHandler); |
|||
state = SocketUdpNativeDynamic.eggetState(pConnectionHandler); |
|||
} |
|||
} |
|||
|
|||
PingBytes[PingBytes.Length - 1] = PingId; |
|||
SocketUdpNativeDynamic.egsend(pConnectionHandler, PingBytes, PingBytes.Length); |
|||
SocketUdpNativeDynamic.egservice(pConnectionHandler); |
|||
|
|||
PingBytes[PingBytes.Length - 1] = (byte) (PingId - 1); |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
public override bool Done() |
|||
{ |
|||
lock (SocketUdpNativeDynamic.syncer) |
|||
{ |
|||
if (this.GotResult || pConnectionHandler == IntPtr.Zero) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
int available = SocketUdpNativeDynamic.egservice(pConnectionHandler); |
|||
if (available < PingLength) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
int pingBytesLength = PingBytes.Length; |
|||
int bytesInRemainginDatagrams = SocketUdpNativeDynamic.egread(pConnectionHandler, PingBytes, ref pingBytesLength); |
|||
this.Successful = (PingBytes != null && PingBytes[PingBytes.Length - 1] == PingId); |
|||
//Debug.Log("Successful: " + this.Successful + " bytesInRemainginDatagrams: " + bytesInRemainginDatagrams + " PingId: " + PingId);
|
|||
|
|||
this.GotResult = true; |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
lock (SocketUdpNativeDynamic.syncer) |
|||
{ |
|||
if (this.pConnectionHandler != IntPtr.Zero) |
|||
SocketUdpNativeDynamic.egdisconnect(this.pConnectionHandler); |
|||
this.pConnectionHandler = IntPtr.Zero; |
|||
} |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
} |
|||
|
|||
#if NATIVE_SOCKETS && NATIVE_SOCKETS_STATIC
|
|||
/// <summary>Uses static linked native Photon socket library via DllImport("__Internal") attribute (as done by Unity iOS and Unity Switch).</summary>
|
|||
public class PingNativeStatic : PingNative |
|||
{ |
|||
public override bool StartPing(string ip) |
|||
{ |
|||
base.Init(); |
|||
|
|||
lock (SocketUdpNativeStatic.syncer) |
|||
{ |
|||
if(pConnectionHandler == IntPtr.Zero) |
|||
{ |
|||
pConnectionHandler = SocketUdpNativeStatic.egconnect(ip); |
|||
SocketUdpNativeStatic.egservice(pConnectionHandler); |
|||
byte state = SocketUdpNativeStatic.eggetState(pConnectionHandler); |
|||
while (state == (byte) NativeSocketState.Connecting) |
|||
{ |
|||
SocketUdpNativeStatic.egservice(pConnectionHandler); |
|||
state = SocketUdpNativeStatic.eggetState(pConnectionHandler); |
|||
Thread.Sleep(0); // suspending execution for a moment is critical on Switch for the OS to update the socket
|
|||
} |
|||
} |
|||
|
|||
PingBytes[PingBytes.Length - 1] = PingId; |
|||
SocketUdpNativeStatic.egsend(pConnectionHandler, PingBytes, PingBytes.Length); |
|||
SocketUdpNativeStatic.egservice(pConnectionHandler); |
|||
|
|||
PingBytes[PingBytes.Length - 1] = (byte) (PingId - 1); |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
public override bool Done() |
|||
{ |
|||
lock (SocketUdpNativeStatic.syncer) |
|||
{ |
|||
if (this.GotResult || pConnectionHandler == IntPtr.Zero) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
int available = SocketUdpNativeStatic.egservice(pConnectionHandler); |
|||
if (available < PingLength) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
int pingBytesLength = PingBytes.Length; |
|||
int bytesInRemainginDatagrams = SocketUdpNativeStatic.egread(pConnectionHandler, PingBytes, ref pingBytesLength); |
|||
this.Successful = (PingBytes != null && PingBytes[PingBytes.Length - 1] == PingId); |
|||
//Debug.Log("Successful: " + this.Successful + " bytesInRemainginDatagrams: " + bytesInRemainginDatagrams + " PingId: " + PingId);
|
|||
|
|||
this.GotResult = true; |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
lock (SocketUdpNativeStatic.syncer) |
|||
{ |
|||
if (pConnectionHandler != IntPtr.Zero) |
|||
SocketUdpNativeStatic.egdisconnect(pConnectionHandler); |
|||
pConnectionHandler = IntPtr.Zero; |
|||
} |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
} |
|||
#endif
|
|||
#endif
|
|||
|
|||
|
|||
#if UNITY_WEBGL
|
|||
public class PingHttp : PhotonPing |
|||
{ |
|||
private WWW webRequest; |
|||
|
|||
public override bool StartPing(string address) |
|||
{ |
|||
base.Init(); |
|||
|
|||
address = "https://" + address + "/photon/m/?ping&r=" + UnityEngine.Random.Range(0, 10000); |
|||
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
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 67137a1b19aa760409002b5b2c63809c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// this file is no longer used. it can be deleted safely.
|
|
|||
fileFormatVersion: 2 |
|||
guid: 26bab4a7373ca7d40a5ee8e5e2b0da16 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------
|
|||
// <copyright file="Player.cs" company="Exit Games GmbH">
|
|||
// Loadbalancing Framework for Photon - Copyright (C) 2018 Exit Games GmbH
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Per client in a room, a Player is created. This client's Player is also
|
|||
// known as PhotonClient.LocalPlayer and the only one you might change
|
|||
// properties for.
|
|||
// </summary>
|
|||
// <author>developer@photonengine.com</author>
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
#if UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER
|
|||
#define SUPPORTED_UNITY
|
|||
#endif
|
|||
|
|||
|
|||
namespace Photon.Realtime |
|||
{ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using ExitGames.Client.Photon; |
|||
|
|||
#if SUPPORTED_UNITY
|
|||
using UnityEngine; |
|||
#endif
|
|||
#if SUPPORTED_UNITY || NETFX_CORE
|
|||
using Hashtable = ExitGames.Client.Photon.Hashtable; |
|||
using SupportClass = ExitGames.Client.Photon.SupportClass; |
|||
#endif
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Summarizes a "player" within a room, identified (in that room) by ID (or "actorNumber").
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Each player has a actorNumber, valid for that room. It's -1 until assigned by server (and client logic).
|
|||
/// </remarks>
|
|||
public class Player |
|||
{ |
|||
/// <summary>
|
|||
/// Used internally to identify the masterclient of a room.
|
|||
/// </summary>
|
|||
protected internal Room RoomReference { get; set; } |
|||
|
|||
|
|||
/// <summary>Backing field for property.</summary>
|
|||
private int actorNumber = -1; |
|||
|
|||
/// <summary>Identifier of this player in current room. Also known as: actorNumber or actorNumber. It's -1 outside of rooms.</summary>
|
|||
/// <remarks>The ID is assigned per room and only valid in that context. It will change even on leave and re-join. IDs are never re-used per room.</remarks>
|
|||
public int ActorNumber |
|||
{ |
|||
get { return this.actorNumber; } |
|||
} |
|||
|
|||
|
|||
/// <summary>Only one player is controlled by each client. Others are not local.</summary>
|
|||
public readonly bool IsLocal; |
|||
|
|||
|
|||
public bool HasRejoined |
|||
{ |
|||
get; internal set; |
|||
} |
|||
|
|||
|
|||
/// <summary>Background field for nickName.</summary>
|
|||
private string nickName = string.Empty; |
|||
|
|||
/// <summary>Non-unique nickname of this player. Synced automatically in a room.</summary>
|
|||
/// <remarks>
|
|||
/// A player might change his own playername in a room (it's only a property).
|
|||
/// Setting this value updates the server and other players (using an operation).
|
|||
/// </remarks>
|
|||
public string NickName |
|||
{ |
|||
get |
|||
{ |
|||
return this.nickName; |
|||
} |
|||
set |
|||
{ |
|||
if (!string.IsNullOrEmpty(this.nickName) && this.nickName.Equals(value)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
this.nickName = value; |
|||
|
|||
// update a room, if we changed our nickName locally
|
|||
if (this.IsLocal) |
|||
{ |
|||
this.SetPlayerNameProperty(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>UserId of the player, available when the room got created with RoomOptions.PublishUserId = true.</summary>
|
|||
/// <remarks>Useful for <see cref="LoadBalancingClient.OpFindFriends"/> and blocking slots in a room for expected players (e.g. in <see cref="LoadBalancingClient.OpCreateRoom"/>).</remarks>
|
|||
public string UserId { get; internal set; } |
|||
|
|||
/// <summary>
|
|||
/// True if this player is the Master Client of the current room.
|
|||
/// </summary>
|
|||
public bool IsMasterClient |
|||
{ |
|||
get |
|||
{ |
|||
if (this.RoomReference == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return this.ActorNumber == this.RoomReference.MasterClientId; |
|||
} |
|||
} |
|||
|
|||
/// <summary>If this player is active in the room (and getting events which are currently being sent).</summary>
|
|||
/// <remarks>
|
|||
/// Inactive players keep their spot in a room but otherwise behave as if offline (no matter what their actual connection status is).
|
|||
/// The room needs a PlayerTTL != 0. If a player is inactive for longer than PlayerTTL, the server will remove this player from the room.
|
|||
/// For a client "rejoining" a room, is the same as joining it: It gets properties, cached events and then the live events.
|
|||
/// </remarks>
|
|||
public bool IsInactive { get; protected internal set; } |
|||
|
|||
/// <summary>Read-only cache for custom properties of player. Set via Player.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; set; } |
|||
|
|||
/// <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 player instance.
|
|||
/// To extend and replace this Player, override LoadBalancingPeer.CreatePlayer().
|
|||
/// </summary>
|
|||
/// <param name="nickName">NickName of the player (a "well known property").</param>
|
|||
/// <param name="actorNumber">ID or ActorNumber of this player in the current room (a shortcut to identify each player in room)</param>
|
|||
/// <param name="isLocal">If this is the local peer's player (or a remote one).</param>
|
|||
protected internal Player(string nickName, int actorNumber, bool isLocal) : this(nickName, actorNumber, isLocal, null) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates a player instance.
|
|||
/// To extend and replace this Player, override LoadBalancingPeer.CreatePlayer().
|
|||
/// </summary>
|
|||
/// <param name="nickName">NickName of the player (a "well known property").</param>
|
|||
/// <param name="actorNumber">ID or ActorNumber of this player in the current room (a shortcut to identify each player in room)</param>
|
|||
/// <param name="isLocal">If this is the local peer's player (or a remote one).</param>
|
|||
/// <param name="playerProperties">A Hashtable of custom properties to be synced. Must use String-typed keys and serializable datatypes as values.</param>
|
|||
protected internal Player(string nickName, int actorNumber, bool isLocal, Hashtable playerProperties) |
|||
{ |
|||
this.IsLocal = isLocal; |
|||
this.actorNumber = actorNumber; |
|||
this.NickName = nickName; |
|||
|
|||
this.CustomProperties = new Hashtable(); |
|||
this.InternalCacheProperties(playerProperties); |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Get a Player by ActorNumber (Player.ID).
|
|||
/// </summary>
|
|||
/// <param name="id">ActorNumber of the a player in this room.</param>
|
|||
/// <returns>Player or null.</returns>
|
|||
public Player Get(int id) |
|||
{ |
|||
if (this.RoomReference == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return this.RoomReference.GetPlayer(id); |
|||
} |
|||
|
|||
/// <summary>Gets this Player's next Player, as sorted by ActorNumber (Player.ID). Wraps around.</summary>
|
|||
/// <returns>Player or null.</returns>
|
|||
public Player GetNext() |
|||
{ |
|||
return GetNextFor(this.ActorNumber); |
|||
} |
|||
|
|||
/// <summary>Gets a Player's next Player, as sorted by ActorNumber (Player.ID). Wraps around.</summary>
|
|||
/// <remarks>Useful when you pass something to the next player. For example: passing the turn to the next player.</remarks>
|
|||
/// <param name="currentPlayer">The Player for which the next is being needed.</param>
|
|||
/// <returns>Player or null.</returns>
|
|||
public Player GetNextFor(Player currentPlayer) |
|||
{ |
|||
if (currentPlayer == null) |
|||
{ |
|||
return null; |
|||
} |
|||
return GetNextFor(currentPlayer.ActorNumber); |
|||
} |
|||
|
|||
/// <summary>Gets a Player's next Player, as sorted by ActorNumber (Player.ID). Wraps around.</summary>
|
|||
/// <remarks>Useful when you pass something to the next player. For example: passing the turn to the next player.</remarks>
|
|||
/// <param name="currentPlayerId">The ActorNumber (Player.ID) for which the next is being needed.</param>
|
|||
/// <returns>Player or null.</returns>
|
|||
public Player GetNextFor(int currentPlayerId) |
|||
{ |
|||
if (this.RoomReference == null || this.RoomReference.Players == null || this.RoomReference.Players.Count < 2) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
Dictionary<int, Player> players = this.RoomReference.Players; |
|||
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]; |
|||
} |
|||
|
|||
|
|||
/// <summary>Caches properties for new Players or when updates of remote players are received. Use SetCustomProperties() for a synced update.</summary>
|
|||
/// <remarks>
|
|||
/// This only updates the CustomProperties and doesn't send them to the server.
|
|||
/// Mostly used when creating new remote players, where the server sends their properties.
|
|||
/// </remarks>
|
|||
protected internal virtual void InternalCacheProperties(Hashtable properties) |
|||
{ |
|||
if (properties == null || properties.Count == 0 || this.CustomProperties.Equals(properties)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (properties.ContainsKey(ActorProperties.PlayerName)) |
|||
{ |
|||
string nameInServersProperties = (string)properties[ActorProperties.PlayerName]; |
|||
if (nameInServersProperties != null) |
|||
{ |
|||
if (this.IsLocal) |
|||
{ |
|||
// the local playername is different than in the properties coming from the server
|
|||
// so the local nickName was changed and the server is outdated -> update server
|
|||
// update property instead of using the outdated nickName coming from server
|
|||
if (!nameInServersProperties.Equals(this.nickName)) |
|||
{ |
|||
this.SetPlayerNameProperty(); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
this.NickName = nameInServersProperties; |
|||
} |
|||
} |
|||
} |
|||
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>
|
|||
/// Brief summary string of the Player: ActorNumber and NickName
|
|||
/// </summary>
|
|||
public override string ToString() |
|||
{ |
|||
return string.Format("#{0:00} '{1}'",this.ActorNumber, this.NickName); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// String summary of the Player: 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.ActorNumber, this.NickName, this.IsInactive ? " (inactive)" : "", this.CustomProperties.ToStringFull()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// If players are equal (by GetHasCode, which returns this.ID).
|
|||
/// </summary>
|
|||
public override bool Equals(object p) |
|||
{ |
|||
Player pp = p as Player; |
|||
return (pp != null && this.GetHashCode() == pp.GetHashCode()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Accompanies Equals, using the ID (actorNumber) as HashCode to return.
|
|||
/// </summary>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return this.ActorNumber; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Used internally, to update this client's playerID when assigned (doesn't change after assignment).
|
|||
/// </summary>
|
|||
protected internal void ChangeLocalID(int newID) |
|||
{ |
|||
if (!this.IsLocal) |
|||
{ |
|||
//Debug.LogError("ERROR You should never change Player IDs!");
|
|||
return; |
|||
} |
|||
|
|||
this.actorNumber = newID; |
|||
} |
|||
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Updates and synchronizes this Player's Custom Properties. Optionally, expectedProperties can be provided as condition.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Custom Properties are a set of string keys and arbitrary values which is synchronized
|
|||
/// for the players in a Room. They are available when the client enters the room, as
|
|||
/// they are in the response of OpJoin and OpCreate.
|
|||
///
|
|||
/// Custom Properties either relate to the (current) Room or a Player (in that Room).
|
|||
///
|
|||
/// Both classes locally cache the current key/values and make them available as
|
|||
/// property: CustomProperties. This is provided only to read them.
|
|||
/// You must use the method SetCustomProperties to set/modify them.
|
|||
///
|
|||
/// Any client can set any Custom Properties anytime (when in a room).
|
|||
/// It's up to the game logic to organize how they are best used.
|
|||
///
|
|||
/// You should call SetCustomProperties only with key/values that are new or changed. This reduces
|
|||
/// traffic and performance.
|
|||
///
|
|||
/// Unless you define some expectedProperties, setting key/values is always permitted.
|
|||
/// In this case, the property-setting client will not receive the new values from the server but
|
|||
/// instead update its local cache in SetCustomProperties.
|
|||
///
|
|||
/// If you define expectedProperties, the server will skip updates if the server property-cache
|
|||
/// does not contain all expectedProperties with the same values.
|
|||
/// In this case, the property-setting client will get an update from the server and update it's
|
|||
/// cached key/values at about the same time as everyone else.
|
|||
///
|
|||
/// The benefit of using expectedProperties can be only one client successfully sets a key from
|
|||
/// one known value to another.
|
|||
/// As example: Store who owns an item in a Custom Property "ownedBy". It's 0 initally.
|
|||
/// When multiple players reach the item, they all attempt to change "ownedBy" from 0 to their
|
|||
/// actorNumber. If you use expectedProperties {"ownedBy", 0} as condition, the first player to
|
|||
/// take the item will have it (and the others fail to set the ownership).
|
|||
///
|
|||
/// Properties get saved with the game state for Turnbased games (which use IsPersistent = true).
|
|||
/// </remarks>
|
|||
/// <param name="propertiesToSet">Hashtable of Custom Properties to be set. </param>
|
|||
/// <param name="expectedValues">If non-null, these are the property-values the server will check as condition for this update.</param>
|
|||
/// <param name="webFlags">Defines if this SetCustomProperties-operation gets forwarded to your WebHooks. Client must be in room.</param>
|
|||
/// <returns>
|
|||
/// False if propertiesToSet is null or empty or have zero string keys.
|
|||
/// True in offline mode even if expectedProperties or webFlags are used.
|
|||
/// If not in a room, returns true if local player and expectedValues and webFlags are null.
|
|||
/// (Use this to cache properties to be sent when joining a room).
|
|||
/// Otherwise, returns if this operation could be sent to the server.
|
|||
/// </returns>
|
|||
public bool SetCustomProperties(Hashtable propertiesToSet, Hashtable expectedValues = null, WebFlags webFlags = null) |
|||
{ |
|||
if (propertiesToSet == null || propertiesToSet.Count == 0) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
Hashtable customProps = propertiesToSet.StripToStringKeys() as Hashtable; |
|||
|
|||
if (this.RoomReference != null) |
|||
{ |
|||
if (this.RoomReference.IsOffline) |
|||
{ |
|||
if (customProps.Count == 0) |
|||
{ |
|||
return false; |
|||
} |
|||
this.CustomProperties.Merge(customProps); |
|||
this.CustomProperties.StripKeysWithNullValues(); |
|||
// invoking callbacks
|
|||
this.RoomReference.LoadBalancingClient.InRoomCallbackTargets.OnPlayerPropertiesUpdate(this, customProps); |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
Hashtable customPropsToCheck = expectedValues.StripToStringKeys() as Hashtable; |
|||
|
|||
// send (sync) these new values if in online room
|
|||
return this.RoomReference.LoadBalancingClient.OpSetPropertiesOfActor(this.actorNumber, customProps, customPropsToCheck, webFlags); |
|||
} |
|||
} |
|||
if (this.IsLocal) |
|||
{ |
|||
if (customProps.Count == 0) |
|||
{ |
|||
return false; |
|||
} |
|||
if (expectedValues == null && webFlags == null) |
|||
{ |
|||
this.CustomProperties.Merge(customProps); |
|||
this.CustomProperties.StripKeysWithNullValues(); |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
/// <summary>Uses OpSetPropertiesOfActor to sync this player's NickName (server is being updated with this.NickName).</summary>
|
|||
private bool SetPlayerNameProperty() |
|||
{ |
|||
if (this.RoomReference != null && !this.RoomReference.IsOffline) |
|||
{ |
|||
Hashtable properties = new Hashtable(); |
|||
properties[ActorProperties.PlayerName] = this.nickName; |
|||
return this.RoomReference.LoadBalancingClient.OpSetPropertiesOfActor(this.ActorNumber, properties); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 773eccd0bf28b5246a65b38198bc645c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------
|
|||
// <copyright file="Region.cs" company="Exit Games GmbH">
|
|||
// Loadbalancing Framework for Photon - Copyright (C) 2018 Exit Games GmbH
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// Represents regions in the Photon Cloud.
|
|||
// </summary>
|
|||
// <author>developer@photonengine.com</author>
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
#if UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER
|
|||
#define SUPPORTED_UNITY
|
|||
#endif
|
|||
|
|||
|
|||
namespace Photon.Realtime |
|||
{ |
|||
using ExitGames.Client.Photon; |
|||
|
|||
#if SUPPORTED_UNITY || NETFX_CORE
|
|||
using Hashtable = ExitGames.Client.Photon.Hashtable; |
|||
using SupportClass = ExitGames.Client.Photon.SupportClass; |
|||
#endif
|
|||
|
|||
|
|||
public class Region |
|||
{ |
|||
public string Code { get; private set; } |
|||
|
|||
/// <summary>Unlike the CloudRegionCode, this may contain cluster information.</summary>
|
|||
public string Cluster { get; private set; } |
|||
|
|||
public string HostAndPort { get; protected internal set; } |
|||
|
|||
public int Ping { get; set; } |
|||
|
|||
public bool WasPinged { get { return this.Ping != int.MaxValue; } } |
|||
|
|||
public Region(string code, string address) |
|||
{ |
|||
this.SetCodeAndCluster(code); |
|||
this.HostAndPort = address; |
|||
this.Ping = int.MaxValue; |
|||
} |
|||
|
|||
public Region(string code, int ping) |
|||
{ |
|||
this.SetCodeAndCluster(code); |
|||
this.Ping = ping; |
|||
} |
|||
|
|||
private void SetCodeAndCluster(string codeAsString) |
|||
{ |
|||
if (codeAsString == null) |
|||
{ |
|||
this.Code = ""; |
|||
this.Cluster = ""; |
|||
return; |
|||
} |
|||
|
|||
codeAsString = codeAsString.ToLower(); |
|||
int slash = codeAsString.IndexOf('/'); |
|||
this.Code = slash <= 0 ? codeAsString : codeAsString.Substring(0, slash); |
|||
this.Cluster = slash <= 0 ? "" : codeAsString.Substring(slash+1, codeAsString.Length-slash-1); |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return this.ToString(false); |
|||
} |
|||
|
|||
public string ToString(bool compact = false) |
|||
{ |
|||
string regionCluster = this.Code; |
|||
if (!string.IsNullOrEmpty(this.Cluster)) |
|||
{ |
|||
regionCluster += "/" + this.Cluster; |
|||
} |
|||
|
|||
if (compact) |
|||
{ |
|||
return string.Format("{0}:{1}", regionCluster, this.Ping); |
|||
} |
|||
else |
|||
{ |
|||
return string.Format("{0}[{2}]: {1}ms ", regionCluster, this.Ping, this.HostAndPort); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 47fa07f9beebdfe4884f9f0eeaac7b16 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// ----------------------------------------------------------------------------
|
|||
// <copyright file="RegionHandler.cs" company="Exit Games GmbH">
|
|||
// Loadbalancing Framework for Photon - Copyright (C) 2018 Exit Games GmbH
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// The RegionHandler class provides methods to ping a list of regions,
|
|||
// to find the one with best ping.
|
|||
// </summary>
|
|||
// <author>developer@photonengine.com</author>
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
|
|||
#if UNITY_4_7 || UNITY_5 || UNITY_5_3_OR_NEWER
|
|||
#define SUPPORTED_UNITY
|
|||
#endif
|
|||
|
|||
#if UNITY_WEBGL
|
|||
#define PING_VIA_COROUTINE
|
|||
#endif
|
|||
|
|||
namespace Photon.Realtime |
|||
{ |
|||
using System; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Net; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using ExitGames.Client.Photon; |
|||
|
|||
#if SUPPORTED_UNITY
|
|||
using UnityEngine; |
|||
using Debug = UnityEngine.Debug; |
|||
#endif
|
|||
#if SUPPORTED_UNITY || NETFX_CORE
|
|||
using Hashtable = ExitGames.Client.Photon.Hashtable; |
|||
using SupportClass = ExitGames.Client.Photon.SupportClass; |
|||
#endif
|
|||
|
|||
/// <summary>
|
|||
/// Provides methods to work with Photon's regions (Photon Cloud) and can be use to find the one with best ping.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// When a client uses a Name Server to fetch the list of available regions, the LoadBalancingClient will create a RegionHandler
|
|||
/// and provide it via the OnRegionListReceived callback.
|
|||
///
|
|||
/// Your logic can decide to either connect to one of those regional servers, or it may use PingMinimumOfRegions to test
|
|||
/// which region provides the best ping.
|
|||
///
|
|||
/// It makes sense to make clients "sticky" to a region when one gets selected.
|
|||
/// This can be achieved by storing the SummaryToCache value, once pinging was done.
|
|||
/// When the client connects again, the previous SummaryToCache helps limiting the number of regions to ping.
|
|||
/// In best case, only the previously selected region gets re-pinged and if the current ping is not much worse, this one region is used again.
|
|||
/// </remarks>
|
|||
public class RegionHandler |
|||
{ |
|||
/// <summary>The implementation of PhotonPing to use for region pinging (Best Region detection).</summary>
|
|||
/// <remarks>Defaults to null, which means the Type is set automatically.</remarks>
|
|||
public static Type PingImplementation; |
|||
|
|||
/// <summary>A list of region names for the Photon Cloud. Set by the result of OpGetRegions().</summary>
|
|||
/// <remarks>
|
|||
/// Implement ILoadBalancingCallbacks and register for the callbacks to get OnRegionListReceived(RegionHandler regionHandler).
|
|||
/// You can also put a "case OperationCode.GetRegions:" into your OnOperationResponse method to notice when the result is available.
|
|||
/// </remarks>
|
|||
public List<Region> EnabledRegions { get; protected internal set; } |
|||
|
|||
private string availableRegionCodes; |
|||
|
|||
private Region bestRegionCache; |
|||
|
|||
/// <summary>
|
|||
/// When PingMinimumOfRegions was called and completed, the BestRegion is identified by best ping.
|
|||
/// </summary>
|
|||
public Region BestRegion |
|||
{ |
|||
get |
|||
{ |
|||
if (this.EnabledRegions == null) |
|||
{ |
|||
return null; |
|||
} |
|||
if (this.bestRegionCache != null) |
|||
{ |
|||
return this.bestRegionCache; |
|||
} |
|||
|
|||
this.EnabledRegions.Sort((a, b) => a.Ping.CompareTo(b.Ping) ); |
|||
|
|||
this.bestRegionCache = this.EnabledRegions[0]; |
|||
return this.bestRegionCache; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This value summarizes the results of pinging currently available regions (after PingMinimumOfRegions finished).
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This value should be stored in the client by the game logic.
|
|||
/// When connecting again, use it as previous summary to speed up pinging regions and to make the best region sticky for the client.
|
|||
/// </remarks>
|
|||
public string SummaryToCache |
|||
{ |
|||
get |
|||
{ |
|||
if (this.BestRegion != null) { |
|||
return this.BestRegion.Code + ";" + this.BestRegion.Ping + ";" + this.availableRegionCodes; |
|||
} |
|||
|
|||
return this.availableRegionCodes; |
|||
} |
|||
} |
|||
|
|||
public string GetResults() |
|||
{ |
|||
StringBuilder sb = new StringBuilder(); |
|||
sb.AppendFormat("Region Pinging Result: {0}\n", this.BestRegion.ToString()); |
|||
if (this.pingerList != null) |
|||
{ |
|||
foreach (RegionPinger region in this.pingerList) |
|||
{ |
|||
sb.AppendFormat(region.GetResults() + "\n"); |
|||
} |
|||
} |
|||
|
|||
sb.AppendFormat("Previous summary: {0}", this.previousSummaryProvided); |
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public void SetRegions(OperationResponse opGetRegions) |
|||
{ |
|||
if (opGetRegions.OperationCode != OperationCode.GetRegions) |
|||
{ |
|||
return; |
|||
} |
|||
if (opGetRegions.ReturnCode != ErrorCode.Ok) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
string[] regions = opGetRegions[ParameterCode.Region] as string[]; |
|||
string[] servers = opGetRegions[ParameterCode.Address] as string[]; |
|||
if (regions == null || servers == null || regions.Length != servers.Length) |
|||
{ |
|||
//TODO: log error
|
|||
//Debug.LogError("The region arrays from Name Server are not ok. Must be non-null and same length. " + (regions == null) + " " + (servers == null) + "\n" + opGetRegions.ToStringFull());
|
|||
return; |
|||
} |
|||
|
|||
this.bestRegionCache = null; |
|||
this.EnabledRegions = new List<Region>(regions.Length); |
|||
|
|||
for (int i = 0; i < regions.Length; i++) |
|||
{ |
|||
Region tmp = new Region(regions[i], servers[i]); |
|||
if (string.IsNullOrEmpty(tmp.Code)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
this.EnabledRegions.Add(tmp); |
|||
} |
|||
|
|||
Array.Sort(regions); |
|||
this.availableRegionCodes = string.Join(",", regions); |
|||
} |
|||
|
|||
private List<RegionPinger> pingerList; |
|||
private Action<RegionHandler> onCompleteCall; |
|||
private int previousPing; |
|||
public bool IsPinging { get; private set; } |
|||
private string previousSummaryProvided; |
|||
|
|||
|
|||
public bool PingMinimumOfRegions(Action<RegionHandler> onCompleteCallback, string previousSummary) |
|||
{ |
|||
if (this.EnabledRegions == null || this.EnabledRegions.Count == 0) |
|||
{ |
|||
//TODO: log error
|
|||
//Debug.LogError("No regions available. Maybe all got filtered out or the AppId is not correctly configured.");
|
|||
return false; |
|||
} |
|||
|
|||
if (this.IsPinging) |
|||
{ |
|||
//TODO: log warning
|
|||
//Debug.LogWarning("PingMinimumOfRegions() skipped, because this RegionHandler is already pinging some regions.");
|
|||
return false; |
|||
} |
|||
|
|||
this.IsPinging = true; |
|||
this.onCompleteCall = onCompleteCallback; |
|||
this.previousSummaryProvided = previousSummary; |
|||
|
|||
if (string.IsNullOrEmpty(previousSummary)) |
|||
{ |
|||
return this.PingEnabledRegions(); |
|||
} |
|||
|
|||
string[] values = previousSummary.Split(';'); |
|||
if (values.Length < 3) |
|||
{ |
|||
return this.PingEnabledRegions(); |
|||
} |
|||
|
|||
int prevBestRegionPing; |
|||
bool secondValueIsInt = Int32.TryParse(values[1], out prevBestRegionPing); |
|||
if (!secondValueIsInt) |
|||
{ |
|||
return this.PingEnabledRegions(); |
|||
} |
|||
|
|||
string prevBestRegionCode = values[0]; |
|||
string prevAvailableRegionCodes = values[2]; |
|||
|
|||
|
|||
if (string.IsNullOrEmpty(prevBestRegionCode)) |
|||
{ |
|||
return this.PingEnabledRegions(); |
|||
} |
|||
if (string.IsNullOrEmpty(prevAvailableRegionCodes)) |
|||
{ |
|||
return this.PingEnabledRegions(); |
|||
} |
|||
if (!this.availableRegionCodes.Equals(prevAvailableRegionCodes) || !this.availableRegionCodes.Contains(prevBestRegionCode)) |
|||
{ |
|||
return this.PingEnabledRegions(); |
|||
} |
|||
if (prevBestRegionPing >= RegionPinger.PingWhenFailed) |
|||
{ |
|||
return this.PingEnabledRegions(); |
|||
} |
|||
|
|||
// let's check only the preferred region to detect if it's still "good enough"
|
|||
this.previousPing = prevBestRegionPing; |
|||
|
|||
Region preferred = this.EnabledRegions.Find(r => r.Code.Equals(prevBestRegionCode)); |
|||
RegionPinger singlePinger = new RegionPinger(preferred, this.OnPreferredRegionPinged); |
|||
singlePinger.Start(); |
|||
return true; |
|||
} |
|||
|
|||
private void OnPreferredRegionPinged(Region preferredRegion) |
|||
{ |
|||
if (preferredRegion.Ping > this.previousPing * 1.50f) |
|||
{ |
|||
this.PingEnabledRegions(); |
|||
} |
|||
else |
|||
{ |
|||
this.IsPinging = false; |
|||
this.onCompleteCall(this); |
|||
#if PING_VIA_COROUTINE
|
|||
MonoBehaviourEmpty.SelfDestroy(); |
|||
#endif
|
|||
} |
|||
} |
|||
|
|||
|
|||
private bool PingEnabledRegions() |
|||
{ |
|||
if (this.EnabledRegions == null || this.EnabledRegions.Count == 0) |
|||
{ |
|||
//TODO: log
|
|||
//Debug.LogError("No regions available. Maybe all got filtered out or the AppId is not correctly configured.");
|
|||
return false; |
|||
} |
|||
|
|||
this.pingerList = new List<RegionPinger>(); |
|||
foreach (Region region in this.EnabledRegions) |
|||
{ |
|||
RegionPinger rp = new RegionPinger(region, this.OnRegionDone); |
|||
this.pingerList.Add(rp); |
|||
rp.Start(); // TODO: check return value
|
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private void OnRegionDone(Region region) |
|||
{ |
|||
this.bestRegionCache = null; |
|||
foreach (RegionPinger pinger in this.pingerList) |
|||
{ |
|||
if (!pinger.Done) |
|||
{ |
|||
return; |
|||
} |
|||
} |
|||
|
|||
this.IsPinging = false; |
|||
this.onCompleteCall(this); |
|||
#if PING_VIA_COROUTINE
|
|||
MonoBehaviourEmpty.SelfDestroy(); |
|||
#endif
|
|||
} |
|||
} |
|||
|
|||
public class RegionPinger |
|||
{ |
|||
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)
|
|||
public static int PingWhenFailed = Attempts * MaxMilliseconsPerPing; |
|||
|
|||
private Region region; |
|||
private string regionAddress; |
|||
public int CurrentAttempt = 0; |
|||
|
|||
public bool Done { get; private set; } |
|||
private Action<Region> onDoneCall; |
|||
|
|||
private PhotonPing ping; |
|||
|
|||
private List<int> rttResults; |
|||
|
|||
public RegionPinger(Region region, Action<Region> onDoneCallback) |
|||
{ |
|||
this.region = region; |
|||
this.region.Ping = PingWhenFailed; |
|||
this.Done = false; |
|||
this.onDoneCall = onDoneCallback; |
|||
} |
|||
|
|||
/// <summary>Selects the best fitting ping implementation or uses the one set in RegionHandler.PingImplementation.</summary>
|
|||
/// <returns>PhotonPing instance to use.</returns>
|
|||
private PhotonPing GetPingImplementation() |
|||
{ |
|||
PhotonPing ping = null; |
|||
|
|||
// using each type explicitly in the conditional code, makes sure Unity doesn't strip the class / constructor.
|
|||
|
|||
#if !UNITY_EDITOR && NETFX_CORE
|
|||
if (RegionHandler.PingImplementation == null || RegionHandler.PingImplementation == typeof(PingWindowsStore)) |
|||
{ |
|||
ping = new PingWindowsStore(); |
|||
} |
|||
#elif NATIVE_SOCKETS || NO_SOCKET
|
|||
if (RegionHandler.PingImplementation == null || RegionHandler.PingImplementation == typeof(PingNativeDynamic)) |
|||
{ |
|||
ping = new PingNativeDynamic(); |
|||
} |
|||
#elif UNITY_WEBGL
|
|||
if (RegionHandler.PingImplementation == null || RegionHandler.PingImplementation == typeof(PingHttp)) |
|||
{ |
|||
ping = new PingHttp(); |
|||
} |
|||
#else
|
|||
if (RegionHandler.PingImplementation == null || RegionHandler.PingImplementation == typeof(PingMono)) |
|||
{ |
|||
ping = new PingMono(); |
|||
} |
|||
#endif
|
|||
|
|||
if (ping == null) |
|||
{ |
|||
if (RegionHandler.PingImplementation != null) |
|||
{ |
|||
ping = (PhotonPing)Activator.CreateInstance(RegionHandler.PingImplementation); |
|||
} |
|||
} |
|||
|
|||
return ping; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Starts the ping routine for the assigned region.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Pinging runs in a ThreadPool worker item or (if needed) in a Thread.
|
|||
/// WebGL runs pinging on the Main Thread as coroutine.
|
|||
/// </remarks>
|
|||
/// <returns>Always true.</returns>
|
|||
public bool Start() |
|||
{ |
|||
// 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 address = this.region.HostAndPort; |
|||
int indexOfColon = address.LastIndexOf(':'); |
|||
if (indexOfColon > 1) |
|||
{ |
|||
address = address.Substring(0, indexOfColon); |
|||
} |
|||
this.regionAddress = ResolveHost(address); |
|||
|
|||
|
|||
this.ping = this.GetPingImplementation(); |
|||
|
|||
|
|||
this.Done = false; |
|||
this.CurrentAttempt = 0; |
|||
this.rttResults = new List<int>(Attempts); |
|||
|
|||
|
|||
#if PING_VIA_COROUTINE
|
|||
MonoBehaviourEmpty.Instance.StartCoroutine(this.RegionPingCoroutine()); |
|||
#else
|
|||
bool queued = false; |
|||
#if !NETFX_CORE
|
|||
try |
|||
{ |
|||
queued = ThreadPool.QueueUserWorkItem(this.RegionPingPooled); |
|||
} |
|||
catch |
|||
{ |
|||
queued = false; |
|||
} |
|||
#endif
|
|||
if (!queued) |
|||
{ |
|||
SupportClass.StartBackgroundCalls(this.RegionPingThreaded, 0, "RegionPing_" + this.region.Code + "_" + this.region.Cluster); |
|||
} |
|||
#endif
|
|||
|
|||
|
|||
return true; |
|||
} |
|||
|
|||
// wraps RegionPingThreaded() to get the signature compatible with ThreadPool.QueueUserWorkItem
|
|||
protected internal void RegionPingPooled(object context) |
|||
{ |
|||
this.RegionPingThreaded(); |
|||
} |
|||
|
|||
protected internal bool RegionPingThreaded() |
|||
{ |
|||
this.region.Ping = PingWhenFailed; |
|||
|
|||
float rttSum = 0.0f; |
|||
int replyCount = 0; |
|||
|
|||
|
|||
Stopwatch sw = new Stopwatch(); |
|||
for (this.CurrentAttempt = 0; this.CurrentAttempt < Attempts; this.CurrentAttempt++) |
|||
{ |
|||
bool overtime = false; |
|||
sw.Reset(); |
|||
sw.Start(); |
|||
|
|||
try |
|||
{ |
|||
this.ping.StartPing(this.regionAddress); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
System.Diagnostics.Debug.WriteLine("RegionPinger.RegionPingThreaded() catched an exception for ping.StartPing(). Exception: " + e + " Source: " + e.Source + " Message: " + e.Message); |
|||
break; |
|||
} |
|||
|
|||
|
|||
while (!this.ping.Done()) |
|||
{ |
|||
if (sw.ElapsedMilliseconds >= MaxMilliseconsPerPing) |
|||
{ |
|||
overtime = true; |
|||
break; |
|||
} |
|||
#if !NETFX_CORE
|
|||
System.Threading.Thread.Sleep(0); |
|||
#endif
|
|||
} |
|||
|
|||
|
|||
sw.Stop(); |
|||
int rtt = (int)sw.ElapsedMilliseconds; |
|||
this.rttResults.Add(rtt); |
|||
|
|||
if (IgnoreInitialAttempt && this.CurrentAttempt == 0) |
|||
{ |
|||
// do nothing.
|
|||
} |
|||
else if (this.ping.Successful && !overtime) |
|||
{ |
|||
rttSum += rtt; |
|||
replyCount++; |
|||
this.region.Ping = (int)((rttSum) / replyCount); |
|||
} |
|||
|
|||
#if !NETFX_CORE
|
|||
System.Threading.Thread.Sleep(10); |
|||
#endif
|
|||
} |
|||
|
|||
//Debug.Log("Done: "+ this.region.Code);
|
|||
this.Done = true; |
|||
this.ping.Dispose(); |
|||
|
|||
this.onDoneCall(this.region); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
#if SUPPORTED_UNITY
|
|||
/// <remarks>
|
|||
/// Affected by frame-rate of app, as this Coroutine checks the socket for a result once per frame.
|
|||
/// </remarks>
|
|||
protected internal IEnumerator RegionPingCoroutine() |
|||
{ |
|||
this.region.Ping = PingWhenFailed; |
|||
|
|||
float rttSum = 0.0f; |
|||
int replyCount = 0; |
|||
|
|||
|
|||
Stopwatch sw = new Stopwatch(); |
|||
for (this.CurrentAttempt = 0; this.CurrentAttempt < Attempts; this.CurrentAttempt++) |
|||
{ |
|||
bool overtime = false; |
|||
sw.Reset(); |
|||
sw.Start(); |
|||
|
|||
try |
|||
{ |
|||
this.ping.StartPing(this.regionAddress); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Debug.Log("catched: " + e); |
|||
break; |
|||
} |
|||
|
|||
|
|||
while (!this.ping.Done()) |
|||
{ |
|||
if (sw.ElapsedMilliseconds >= MaxMilliseconsPerPing) |
|||
{ |
|||
overtime = true; |
|||
break; |
|||
} |
|||
yield return 0; // keep this loop tight, to avoid adding local lag to rtt.
|
|||
} |
|||
|
|||
|
|||
sw.Stop(); |
|||
int rtt = (int)sw.ElapsedMilliseconds; |
|||
this.rttResults.Add(rtt); |
|||
|
|||
|
|||
if (IgnoreInitialAttempt && this.CurrentAttempt == 0) |
|||
{ |
|||
// do nothing.
|
|||
} |
|||
else if (this.ping.Successful && !overtime) |
|||
{ |
|||
rttSum += rtt; |
|||
replyCount++; |
|||
this.region.Ping = (int)((rttSum) / replyCount); |
|||
} |
|||
|
|||
yield return new WaitForSeconds(0.1f); |
|||
} |
|||
|
|||
|
|||
//Debug.Log("Done: "+ this.region.Code);
|
|||
this.Done = true; |
|||
this.ping.Dispose(); |
|||
this.onDoneCall(this.region); |
|||
yield return null; |
|||
} |
|||
#endif
|
|||
|
|||
|
|||
public string GetResults() |
|||
{ |
|||
return string.Format("{0}: {1} ({2})", this.region.Code, this.region.Ping, this.rttResults.ToStringFull()); |
|||
} |
|||
|
|||
/// <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...
|
|||
/// </remarks>
|
|||
/// <param name="hostName">Hostname to resolve.</param>
|
|||
/// <returns>IP string or empty string if resolution fails</returns>
|
|||
public static string ResolveHost(string hostName) |
|||
{ |
|||
|
|||
if (hostName.StartsWith("wss://")) |
|||
{ |
|||
hostName = hostName.Substring(6); |
|||
} |
|||
if (hostName.StartsWith("ws://")) |
|||
{ |
|||
hostName = hostName.Substring(5); |
|||
} |
|||
|
|||
string ipv4Address = string.Empty; |
|||
|
|||
try |
|||
{ |
|||
#if UNITY_WSA || NETFX_CORE || UNITY_WEBGL
|
|||
return hostName; |
|||
#else
|
|||
|
|||
IPAddress[] address = Dns.GetHostAddresses(hostName); |
|||
if (address.Length == 1) |
|||
{ |
|||
return address[0].ToString(); |
|||
} |
|||
|
|||
// if we got more addresses, try to pick a IPv6 one
|
|||
// checking ipAddress.ToString() means we don't have to import System.Net.Sockets, which is not available on some platforms (Metro)
|
|||
for (int index = 0; index < address.Length; index++) |
|||
{ |
|||
IPAddress ipAddress = address[index]; |
|||
if (ipAddress != null) |
|||
{ |
|||
if (ipAddress.ToString().Contains(":")) |
|||
{ |
|||
return ipAddress.ToString(); |
|||
} |
|||
if (string.IsNullOrEmpty(ipv4Address)) |
|||
{ |
|||
ipv4Address = address.ToString(); |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
} |
|||
catch (System.Exception e) |
|||
{ |
|||
System.Diagnostics.Debug.WriteLine("RegionPinger.ResolveHost() catched an exception for Dns.GetHostAddresses(). Exception: " + e + " Source: " + e.Source + " Message: " + e.Message); |
|||
} |
|||
|
|||
return ipv4Address; |
|||
} |
|||
} |
|||
|
|||
#if PING_VIA_COROUTINE
|
|||
internal class MonoBehaviourEmpty : MonoBehaviour |
|||
{ |
|||
private static bool instanceSet; // to avoid instance null check which may be incorrect
|
|||
private static MonoBehaviourEmpty instance; |
|||
|
|||
public static MonoBehaviourEmpty Instance |
|||
{ |
|||
get |
|||
{ |
|||
if (instanceSet) |
|||
{ |
|||
return instance; |
|||
} |
|||
GameObject go = new GameObject(); |
|||
DontDestroyOnLoad(go); |
|||
go.name = "RegionPinger"; |
|||
instance = go.AddComponent<MonoBehaviourEmpty>(); |
|||
instanceSet = true; |
|||
return instance; |
|||
} |
|||
} |
|||
|
|||
public static void SelfDestroy() |
|||
{ |
|||
if (instanceSet) |
|||
{ |
|||
instanceSet = false; |
|||
Destroy(instance.gameObject); |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: e44fa9826b5332748a512d41bdcfbc48 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
部分文件因为文件数量过多而无法显示
撰写
预览
正在加载...
取消
保存
Reference in new issue