init
This commit is contained in:
500
Assets/Photon/PhotonRealtime/Code/PhotonPing.cs
Normal file
500
Assets/Photon/PhotonRealtime/Code/PhotonPing.cs
Normal file
@@ -0,0 +1,500 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// <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 UnityWebRequest
|
||||
using UnityEngine.Networking;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Abstract implementation of PhotonPing, ase for pinging servers to find the "Best Region".
|
||||
/// </summary>
|
||||
public abstract class PhotonPing : IDisposable
|
||||
{
|
||||
/// <summary>Caches the last exception/error message, if any.</summary>
|
||||
public string DebugString = "";
|
||||
|
||||
/// <summary>True of the ping was successful.</summary>
|
||||
public bool Successful;
|
||||
|
||||
/// <summary>True if there was any result.</summary>
|
||||
protected internal bool GotResult;
|
||||
|
||||
/// <summary>Length of a ping.</summary>
|
||||
protected internal int PingLength = 13;
|
||||
|
||||
/// <summary>Bytes to send in a (Photon UDP) ping.</summary>
|
||||
protected internal byte[] PingBytes = new byte[] { 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x00 };
|
||||
|
||||
/// <summary>Randomized number to identify a ping.</summary>
|
||||
protected internal byte PingId;
|
||||
|
||||
private static readonly System.Random RandomIdProvider = new System.Random();
|
||||
|
||||
/// <summary>Begins sending a ping.</summary>
|
||||
public virtual bool StartPing(string ip)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>Check if done.</summary>
|
||||
public virtual bool Done()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>Dispose of this ping.</summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>Initialize this ping (GotResult, Successful, PingId).</summary>
|
||||
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;
|
||||
int port = (RegionHandler.PortToPingOverride != 0) ? RegionHandler.PortToPingOverride : 5055;
|
||||
this.sock.Connect(ip, port);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
System.Diagnostics.Debug.WriteLine(e.ToString());
|
||||
|
||||
// bubble up
|
||||
throw;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Check if done.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>Dispose of this ping.</summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
if (this.sock == null) { return; }
|
||||
|
||||
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)
|
||||
{
|
||||
lock (this.syncer)
|
||||
{
|
||||
this.Init();
|
||||
|
||||
int port = (RegionHandler.PortToPingOverride != 0) ? RegionHandler.PortToPingOverride : 5055;
|
||||
EndpointPair endPoint = new EndpointPair(null, string.Empty, new HostName(host), port.ToString());
|
||||
this.sock = new DatagramSocket();
|
||||
this.sock.MessageReceived += this.OnMessageReceived;
|
||||
|
||||
IAsyncAction result = this.sock.ConnectAsync(endPoint);
|
||||
result.Completed = this.OnConnected;
|
||||
this.DebugString += " End StartPing";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Check if done.</summary>
|
||||
public override bool Done()
|
||||
{
|
||||
lock (this.syncer)
|
||||
{
|
||||
return this.GotResult || this.sock == null; // this just indicates the ping is no longer waiting. this.Successful value defines if the roundtrip completed
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Dispose of this ping.</summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
lock (this.syncer)
|
||||
{
|
||||
this.sock = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConnected(IAsyncAction asyncinfo, AsyncStatus asyncstatus)
|
||||
{
|
||||
lock (this.syncer)
|
||||
{
|
||||
if (asyncinfo.AsTask().IsCompleted && !asyncinfo.AsTask().IsFaulted && this.sock != null && this.sock.Information.RemoteAddress != null)
|
||||
{
|
||||
this.PingBytes[this.PingBytes.Length - 1] = this.PingId;
|
||||
|
||||
DataWriter writer;
|
||||
writer = new DataWriter(this.sock.OutputStream);
|
||||
writer.WriteBytes(this.PingBytes);
|
||||
DataWriterStoreOperation res = writer.StoreAsync();
|
||||
res.AsTask().Wait(100);
|
||||
|
||||
this.PingBytes[this.PingBytes.Length - 1] = (byte)(this.PingId + 1); // this buffer is re-used for the result/receive. invalidate the result now.
|
||||
|
||||
writer.DetachStream();
|
||||
writer.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.sock = null; // will cause Done() to return true but this.Successful defines if the roundtrip completed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
|
||||
{
|
||||
lock (this.syncer)
|
||||
{
|
||||
DataReader reader = null;
|
||||
try
|
||||
{
|
||||
reader = args.GetDataReader();
|
||||
uint receivedByteCount = reader.UnconsumedBufferLength;
|
||||
if (receivedByteCount > 0)
|
||||
{
|
||||
byte[] resultBytes = new byte[receivedByteCount];
|
||||
reader.ReadBytes(resultBytes);
|
||||
|
||||
//TODO: check result bytes!
|
||||
|
||||
|
||||
this.Successful = receivedByteCount == this.PingLength && resultBytes[resultBytes.Length - 1] == this.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 UnityWebRequest webRequest;
|
||||
|
||||
public override bool StartPing(string address)
|
||||
{
|
||||
base.Init();
|
||||
|
||||
address = "https://" + address + "/photon/m/?ping&r=" + UnityEngine.Random.Range(0, 10000);
|
||||
this.webRequest = UnityWebRequest.Get(address);
|
||||
this.webRequest.SendWebRequest();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Done()
|
||||
{
|
||||
if (this.webRequest.isDone)
|
||||
{
|
||||
Successful = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
this.webRequest.Dispose();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user