提交工程
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
#if UNITY_EDITOR
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
public static class ConsoleProDebug
|
||||
{
|
||||
// Clear the console and the native console
|
||||
public static void Clear()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if(ConsoleClearMethod != null)
|
||||
{
|
||||
ConsoleClearMethod.Invoke(null, null);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Send a log to a specific filter regardless of contents
|
||||
// Ex: ConsoleProDebug.LogToFilter("Hi", "CustomFilter");
|
||||
public static void LogToFilter(string inLog, string inFilterName)
|
||||
{
|
||||
Debug.Log(inLog + "\nCPAPI:{\"cmd\":\"Filter\" \"name\":\"" + inFilterName + "\"}");
|
||||
}
|
||||
|
||||
// Send a log as a regular log but change its type in ConsolePro
|
||||
// Ex: ConsoleProDebug.LogAsType("Hi", "Error");
|
||||
public static void LogAsType(string inLog, string inTypeName)
|
||||
{
|
||||
Debug.Log(inLog + "\nCPAPI:{\"cmd\":\"LogType\" \"name\":\"" + inTypeName + "\"}");
|
||||
}
|
||||
|
||||
// Watch a variable. This will only produce one log entry regardless of how many times it is logged, allowing you to track variables without spam.
|
||||
// Ex:
|
||||
// void Update() {
|
||||
// ConsoleProDebug.Watch("Player X Position", transform.position.x);
|
||||
// }
|
||||
public static void Watch(string inName, string inValue)
|
||||
{
|
||||
Debug.Log(inName + " : " + inValue + "\nCPAPI:{\"cmd\":\"Watch\" \"name\":\"" + inName + "\"}");
|
||||
}
|
||||
|
||||
public static void Search(string inText)
|
||||
{
|
||||
Debug.Log("\nCPAPI:{\"cmd\":\"Search\" \"text\":\"" + inText + "\"}");
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Reflection calls to access Console Pro from runtime
|
||||
private static bool _checkedConsoleClearMethod = false;
|
||||
private static MethodInfo _consoleClearMethod = null;
|
||||
private static MethodInfo ConsoleClearMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_consoleClearMethod == null || !_checkedConsoleClearMethod)
|
||||
{
|
||||
_checkedConsoleClearMethod = true;
|
||||
if(ConsoleWindowType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_consoleClearMethod = ConsoleWindowType.GetMethod("ClearEntries", BindingFlags.Static | BindingFlags.Public);
|
||||
}
|
||||
|
||||
return _consoleClearMethod;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool _checkedConsoleWindowType = false;
|
||||
private static Type _consoleWindowType = null;
|
||||
private static Type ConsoleWindowType
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_consoleWindowType == null || !_checkedConsoleWindowType)
|
||||
{
|
||||
_checkedConsoleWindowType = true;
|
||||
Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
|
||||
for(int iAssembly = 0; iAssembly < assemblies.Length; iAssembly++)
|
||||
{
|
||||
Type[] types = assemblies[iAssembly].GetTypes();
|
||||
for(int iType = 0; iType < types.Length; iType++)
|
||||
{
|
||||
if(types[iType].Name == "ConsolePro3Window")
|
||||
{
|
||||
_consoleWindowType = types[iType];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _consoleWindowType;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30f42e8a12eb842acbe9a63057fb00e4
|
||||
timeCreated: 1469329295
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5de782a9528f04b41a8ba70afba32a61
|
||||
timeCreated: 1498113024
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be7aee03972b847e4a8b81ef8ce46a8e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2284c517ee274c19a6ba4c1a8c96fb6
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1d51a9b386eb4992af4cb193629d854
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,237 @@
|
||||
// Uncomment to use in Editor
|
||||
// #define USECONSOLEPROREMOTESERVERINEDITOR
|
||||
|
||||
#if (!UNITY_EDITOR && DEBUG) || (UNITY_EDITOR && USECONSOLEPROREMOTESERVERINEDITOR)
|
||||
#define USECONSOLEPROREMOTESERVER
|
||||
#endif
|
||||
|
||||
#if (UNITY_WP_8_1 || UNITY_WSA)
|
||||
#define UNSUPPORTEDCONSOLEPROREMOTESERVER
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR && !USECONSOLEPROREMOTESERVER
|
||||
#elif UNSUPPORTEDCONSOLEPROREMOTESERVER
|
||||
#elif !USECONSOLEPROREMOTESERVER
|
||||
#else
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
#if USECONSOLEPROREMOTESERVER
|
||||
using FlyingWormConsole3.LiteNetLib;
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
#endif
|
||||
|
||||
namespace FlyingWormConsole3
|
||||
{
|
||||
#if USECONSOLEPROREMOTESERVER
|
||||
public class ConsoleProRemoteServer : MonoBehaviour, INetEventListener
|
||||
#else
|
||||
public class ConsoleProRemoteServer : MonoBehaviour
|
||||
#endif
|
||||
{
|
||||
public bool useNATPunch = false;
|
||||
public int port = 51000;
|
||||
|
||||
#if UNITY_EDITOR && !USECONSOLEPROREMOTESERVER
|
||||
|
||||
#elif UNSUPPORTEDCONSOLEPROREMOTESERVER
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
Debug.Log("Console Pro Remote Server is not supported on this platform");
|
||||
}
|
||||
|
||||
#elif !USECONSOLEPROREMOTESERVER
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
Debug.Log("Console Pro Remote Server is disabled in release mode, please use a Development build or define DEBUG to use it");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
private NetManager _netServer;
|
||||
private NetPeer _ourPeer;
|
||||
private NetDataWriter _dataWriter;
|
||||
|
||||
[System.SerializableAttribute]
|
||||
public class QueuedLog
|
||||
{
|
||||
public string message;
|
||||
public string stackTrace;
|
||||
public LogType type;
|
||||
}
|
||||
|
||||
|
||||
[NonSerializedAttribute]
|
||||
public List<QueuedLog> logs = new List<QueuedLog>();
|
||||
|
||||
private static ConsoleProRemoteServer instance = null;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if(instance != null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
instance = this;
|
||||
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
Debug.Log("Starting Console Pro Server on port : " + port);
|
||||
|
||||
_dataWriter = new NetDataWriter();
|
||||
_netServer = new NetManager(this, 100, "ConsolePro");
|
||||
_netServer.Start(port);
|
||||
_netServer.DiscoveryEnabled = true;
|
||||
_netServer.UpdateTime = 15;
|
||||
_netServer.MergeEnabled = true;
|
||||
_netServer.NatPunchEnabled = useNATPunch;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if(_netServer != null)
|
||||
{
|
||||
_netServer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPeerConnected(NetPeer peer)
|
||||
{
|
||||
Debug.Log("Connected to " + peer.EndPoint);
|
||||
_ourPeer = peer;
|
||||
}
|
||||
|
||||
public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
|
||||
{
|
||||
Debug.Log("Disconnected from " + peer.EndPoint + ", info: " + disconnectInfo.Reason);
|
||||
if (peer == _ourPeer)
|
||||
{
|
||||
_ourPeer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNetworkReceive(NetPeer peer, NetDataReader reader)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnPeerDisconnected(NetPeer peer, DisconnectReason reason, int socketErrorCode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnNetworkError(NetEndPoint endPoint, int socketErrorCode)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnNetworkReceiveUnconnected(NetEndPoint remoteEndPoint, NetDataReader reader, UnconnectedMessageType messageType)
|
||||
{
|
||||
if (messageType == UnconnectedMessageType.DiscoveryRequest)
|
||||
{
|
||||
// Debug.Log("[SERVER] Received discovery request. Send discovery response");
|
||||
_netServer.SendDiscoveryResponse(new byte[] {1}, remoteEndPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNetworkLatencyUpdate(NetPeer peer, int latency)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
Application.RegisterLogCallback(LogCallback);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
Application.RegisterLogCallback(LogCallback);
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
Application.RegisterLogCallback(null);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
Application.logMessageReceived += LogCallback;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
Application.logMessageReceived -= LogCallback;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public void LogCallback(string logString, string stackTrace, LogType type)
|
||||
{
|
||||
if(!logString.StartsWith("CPIGNORE"))
|
||||
{
|
||||
QueueLog(logString, stackTrace, type);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueLog(string logString, string stackTrace, LogType type)
|
||||
{
|
||||
if(logs.Count > 200)
|
||||
{
|
||||
while(logs.Count > 200)
|
||||
{
|
||||
logs.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
logs.Add(new QueuedLog() { message = logString, stackTrace = stackTrace, type = type } );
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if(_netServer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_netServer.PollEvents();
|
||||
|
||||
if(_ourPeer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(logs.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string cMessage = "";
|
||||
|
||||
for(int i = 0; i < logs.Count; i++)
|
||||
{
|
||||
cMessage = "";
|
||||
|
||||
QueuedLog cLog = logs[i];
|
||||
cMessage = "::::" + cLog.type + "::::" + cLog.message + "\n" + cLog.stackTrace;
|
||||
_dataWriter.Reset();
|
||||
_dataWriter.Put(cMessage);
|
||||
_ourPeer.Send(_dataWriter, SendOptions.ReliableOrdered);
|
||||
}
|
||||
|
||||
logs.Clear();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6bcfaced529e418bb75980b297fda2a
|
||||
timeCreated: 1437614101
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c6bd635eeaa04c228b6d342c4758ad7
|
||||
folderAsset: yes
|
||||
timeCreated: 1494014730
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,120 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib.Utils
|
||||
{
|
||||
public static class FastBitConverter
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private struct ConverterHelperDouble
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ulong Along;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public double Adouble;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private struct ConverterHelperFloat
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public int Aint;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public float Afloat;
|
||||
}
|
||||
|
||||
private static void WriteLittleEndian(byte[] buffer, int offset, ulong data)
|
||||
{
|
||||
#if BIGENDIAN
|
||||
buffer[offset + 7] = (byte)(data);
|
||||
buffer[offset + 6] = (byte)(data >> 8);
|
||||
buffer[offset + 5] = (byte)(data >> 16);
|
||||
buffer[offset + 4] = (byte)(data >> 24);
|
||||
buffer[offset + 3] = (byte)(data >> 32);
|
||||
buffer[offset + 2] = (byte)(data >> 40);
|
||||
buffer[offset + 1] = (byte)(data >> 48);
|
||||
buffer[offset ] = (byte)(data >> 56);
|
||||
#else
|
||||
buffer[offset] = (byte)(data);
|
||||
buffer[offset + 1] = (byte)(data >> 8);
|
||||
buffer[offset + 2] = (byte)(data >> 16);
|
||||
buffer[offset + 3] = (byte)(data >> 24);
|
||||
buffer[offset + 4] = (byte)(data >> 32);
|
||||
buffer[offset + 5] = (byte)(data >> 40);
|
||||
buffer[offset + 6] = (byte)(data >> 48);
|
||||
buffer[offset + 7] = (byte)(data >> 56);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void WriteLittleEndian(byte[] buffer, int offset, int data)
|
||||
{
|
||||
#if BIGENDIAN
|
||||
buffer[offset + 3] = (byte)(data);
|
||||
buffer[offset + 2] = (byte)(data >> 8);
|
||||
buffer[offset + 1] = (byte)(data >> 16);
|
||||
buffer[offset ] = (byte)(data >> 24);
|
||||
#else
|
||||
buffer[offset] = (byte)(data);
|
||||
buffer[offset + 1] = (byte)(data >> 8);
|
||||
buffer[offset + 2] = (byte)(data >> 16);
|
||||
buffer[offset + 3] = (byte)(data >> 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void WriteLittleEndian(byte[] buffer, int offset, short data)
|
||||
{
|
||||
#if BIGENDIAN
|
||||
buffer[offset + 1] = (byte)(data);
|
||||
buffer[offset ] = (byte)(data >> 8);
|
||||
#else
|
||||
buffer[offset] = (byte)(data);
|
||||
buffer[offset + 1] = (byte)(data >> 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, double value)
|
||||
{
|
||||
ConverterHelperDouble ch = new ConverterHelperDouble { Adouble = value };
|
||||
WriteLittleEndian(bytes, startIndex, ch.Along);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, float value)
|
||||
{
|
||||
ConverterHelperFloat ch = new ConverterHelperFloat { Afloat = value };
|
||||
WriteLittleEndian(bytes, startIndex, ch.Aint);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, short value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, ushort value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, (short)value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, int value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, uint value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, (int)value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, long value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, (ulong)value);
|
||||
}
|
||||
|
||||
public static void GetBytes(byte[] bytes, int startIndex, ulong value)
|
||||
{
|
||||
WriteLittleEndian(bytes, startIndex, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55df0de9a58e74c9395dfe5ffdab9a5a
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,128 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public enum UnconnectedMessageType
|
||||
{
|
||||
Default,
|
||||
DiscoveryRequest,
|
||||
DiscoveryResponse
|
||||
}
|
||||
|
||||
public enum DisconnectReason
|
||||
{
|
||||
SocketReceiveError,
|
||||
ConnectionFailed,
|
||||
Timeout,
|
||||
SocketSendError,
|
||||
RemoteConnectionClose,
|
||||
DisconnectPeerCalled
|
||||
}
|
||||
|
||||
public struct DisconnectInfo
|
||||
{
|
||||
public DisconnectReason Reason;
|
||||
public int SocketErrorCode;
|
||||
public NetDataReader AdditionalData;
|
||||
}
|
||||
|
||||
public interface INetEventListener
|
||||
{
|
||||
/// <summary>
|
||||
/// New remote peer connected to host, or client connected to remote host
|
||||
/// </summary>
|
||||
/// <param name="peer">Connected peer object</param>
|
||||
void OnPeerConnected(NetPeer peer);
|
||||
|
||||
/// <summary>
|
||||
/// Peer disconnected
|
||||
/// </summary>
|
||||
/// <param name="peer">disconnected peer</param>
|
||||
/// <param name="disconnectInfo">additional info about reason, errorCode or data received with disconnect message</param>
|
||||
void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Network error (on send or receive)
|
||||
/// </summary>
|
||||
/// <param name="endPoint">From endPoint (can be null)</param>
|
||||
/// <param name="socketErrorCode">Socket error code</param>
|
||||
void OnNetworkError(NetEndPoint endPoint, int socketErrorCode);
|
||||
|
||||
/// <summary>
|
||||
/// Received some data
|
||||
/// </summary>
|
||||
/// <param name="peer">From peer</param>
|
||||
/// <param name="reader">DataReader containing all received data</param>
|
||||
void OnNetworkReceive(NetPeer peer, NetDataReader reader);
|
||||
|
||||
/// <summary>
|
||||
/// Received unconnected message
|
||||
/// </summary>
|
||||
/// <param name="remoteEndPoint">From address (IP and Port)</param>
|
||||
/// <param name="reader">Message data</param>
|
||||
/// <param name="messageType">Message type (simple, discovery request or responce)</param>
|
||||
void OnNetworkReceiveUnconnected(NetEndPoint remoteEndPoint, NetDataReader reader, UnconnectedMessageType messageType);
|
||||
|
||||
/// <summary>
|
||||
/// Latency information updated
|
||||
/// </summary>
|
||||
/// <param name="peer">Peer with updated latency</param>
|
||||
/// <param name="latency">latency value in milliseconds</param>
|
||||
void OnNetworkLatencyUpdate(NetPeer peer, int latency);
|
||||
}
|
||||
|
||||
public class EventBasedNetListener : INetEventListener
|
||||
{
|
||||
public delegate void OnPeerConnected(NetPeer peer);
|
||||
public delegate void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo);
|
||||
public delegate void OnNetworkError(NetEndPoint endPoint, int socketErrorCode);
|
||||
public delegate void OnNetworkReceive(NetPeer peer, NetDataReader reader);
|
||||
public delegate void OnNetworkReceiveUnconnected(NetEndPoint remoteEndPoint, NetDataReader reader, UnconnectedMessageType messageType);
|
||||
public delegate void OnNetworkLatencyUpdate(NetPeer peer, int latency);
|
||||
|
||||
public event OnPeerConnected PeerConnectedEvent;
|
||||
public event OnPeerDisconnected PeerDisconnectedEvent;
|
||||
public event OnNetworkError NetworkErrorEvent;
|
||||
public event OnNetworkReceive NetworkReceiveEvent;
|
||||
public event OnNetworkReceiveUnconnected NetworkReceiveUnconnectedEvent;
|
||||
public event OnNetworkLatencyUpdate NetworkLatencyUpdateEvent;
|
||||
|
||||
void INetEventListener.OnPeerConnected(NetPeer peer)
|
||||
{
|
||||
if (PeerConnectedEvent != null)
|
||||
PeerConnectedEvent(peer);
|
||||
}
|
||||
|
||||
void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
|
||||
{
|
||||
if (PeerDisconnectedEvent != null)
|
||||
PeerDisconnectedEvent(peer, disconnectInfo);
|
||||
}
|
||||
|
||||
void INetEventListener.OnNetworkError(NetEndPoint endPoint, int socketErrorCode)
|
||||
{
|
||||
if (NetworkErrorEvent != null)
|
||||
NetworkErrorEvent(endPoint, socketErrorCode);
|
||||
}
|
||||
|
||||
void INetEventListener.OnNetworkReceive(NetPeer peer, NetDataReader reader)
|
||||
{
|
||||
if (NetworkReceiveEvent != null)
|
||||
NetworkReceiveEvent(peer, reader);
|
||||
}
|
||||
|
||||
void INetEventListener.OnNetworkReceiveUnconnected(NetEndPoint remoteEndPoint, NetDataReader reader, UnconnectedMessageType messageType)
|
||||
{
|
||||
if (NetworkReceiveUnconnectedEvent != null)
|
||||
NetworkReceiveUnconnectedEvent(remoteEndPoint, reader, messageType);
|
||||
}
|
||||
|
||||
void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency)
|
||||
{
|
||||
if (NetworkLatencyUpdateEvent != null)
|
||||
NetworkLatencyUpdateEvent(peer, latency);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d391f9565d58e44a798d680ec5c11906
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,231 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
|
||||
//Some code parts taked from lidgren-network-gen3
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public interface INatPunchListener
|
||||
{
|
||||
void OnNatIntroductionRequest(NetEndPoint localEndPoint, NetEndPoint remoteEndPoint, string token);
|
||||
void OnNatIntroductionSuccess(NetEndPoint targetEndPoint, string token);
|
||||
}
|
||||
|
||||
public class EventBasedNatPunchListener : INatPunchListener
|
||||
{
|
||||
public delegate void OnNatIntroductionRequest(NetEndPoint localEndPoint, NetEndPoint remoteEndPoint, string token);
|
||||
public delegate void OnNatIntroductionSuccess(NetEndPoint targetEndPoint, string token);
|
||||
|
||||
public event OnNatIntroductionRequest NatIntroductionRequest;
|
||||
public event OnNatIntroductionSuccess NatIntroductionSuccess;
|
||||
|
||||
void INatPunchListener.OnNatIntroductionRequest(NetEndPoint localEndPoint, NetEndPoint remoteEndPoint, string token)
|
||||
{
|
||||
if(NatIntroductionRequest != null)
|
||||
NatIntroductionRequest(localEndPoint, remoteEndPoint, token);
|
||||
}
|
||||
|
||||
void INatPunchListener.OnNatIntroductionSuccess(NetEndPoint targetEndPoint, string token)
|
||||
{
|
||||
if (NatIntroductionSuccess != null)
|
||||
NatIntroductionSuccess(targetEndPoint, token);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NatPunchModule
|
||||
{
|
||||
struct RequestEventData
|
||||
{
|
||||
public NetEndPoint LocalEndPoint;
|
||||
public NetEndPoint RemoteEndPoint;
|
||||
public string Token;
|
||||
}
|
||||
|
||||
struct SuccessEventData
|
||||
{
|
||||
public NetEndPoint TargetEndPoint;
|
||||
public string Token;
|
||||
}
|
||||
|
||||
private readonly NetManager _netBase;
|
||||
private readonly Queue<RequestEventData> _requestEvents;
|
||||
private readonly Queue<SuccessEventData> _successEvents;
|
||||
private const byte HostByte = 1;
|
||||
private const byte ClientByte = 0;
|
||||
public const int MaxTokenLength = 256;
|
||||
|
||||
private INatPunchListener _natPunchListener;
|
||||
|
||||
internal NatPunchModule(NetManager netBase)
|
||||
{
|
||||
_netBase = netBase;
|
||||
_requestEvents = new Queue<RequestEventData>();
|
||||
_successEvents = new Queue<SuccessEventData>();
|
||||
}
|
||||
|
||||
public void Init(INatPunchListener listener)
|
||||
{
|
||||
_natPunchListener = listener;
|
||||
}
|
||||
|
||||
public void NatIntroduce(
|
||||
NetEndPoint hostInternal,
|
||||
NetEndPoint hostExternal,
|
||||
NetEndPoint clientInternal,
|
||||
NetEndPoint clientExternal,
|
||||
string additionalInfo)
|
||||
{
|
||||
NetDataWriter dw = new NetDataWriter();
|
||||
|
||||
//First packet (server)
|
||||
//send to client
|
||||
dw.Put(ClientByte);
|
||||
dw.Put(hostInternal);
|
||||
dw.Put(hostExternal);
|
||||
dw.Put(additionalInfo, MaxTokenLength);
|
||||
|
||||
var packet = _netBase.PacketPool.GetWithData(PacketProperty.NatIntroduction, dw);
|
||||
_netBase.SendRawAndRecycle(packet, clientExternal);
|
||||
|
||||
//Second packet (client)
|
||||
//send to server
|
||||
dw.Reset();
|
||||
dw.Put(HostByte);
|
||||
dw.Put(clientInternal);
|
||||
dw.Put(clientExternal);
|
||||
dw.Put(additionalInfo, MaxTokenLength);
|
||||
|
||||
packet = _netBase.PacketPool.GetWithData(PacketProperty.NatIntroduction, dw);
|
||||
_netBase.SendRawAndRecycle(packet, hostExternal);
|
||||
}
|
||||
|
||||
public void PollEvents()
|
||||
{
|
||||
if (_natPunchListener == null)
|
||||
return;
|
||||
lock (_successEvents)
|
||||
{
|
||||
while (_successEvents.Count > 0)
|
||||
{
|
||||
var evt = _successEvents.Dequeue();
|
||||
_natPunchListener.OnNatIntroductionSuccess(evt.TargetEndPoint, evt.Token);
|
||||
}
|
||||
}
|
||||
lock (_requestEvents)
|
||||
{
|
||||
while (_requestEvents.Count > 0)
|
||||
{
|
||||
var evt = _requestEvents.Dequeue();
|
||||
_natPunchListener.OnNatIntroductionRequest(evt.LocalEndPoint, evt.RemoteEndPoint, evt.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SendNatIntroduceRequest(NetEndPoint masterServerEndPoint, string additionalInfo)
|
||||
{
|
||||
if (!_netBase.IsRunning)
|
||||
return;
|
||||
|
||||
//prepare outgoing data
|
||||
NetDataWriter dw = new NetDataWriter();
|
||||
string networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv4);
|
||||
if (string.IsNullOrEmpty(networkIp))
|
||||
{
|
||||
networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv6);
|
||||
}
|
||||
int networkPort = _netBase.LocalEndPoint.Port;
|
||||
NetEndPoint localEndPoint = new NetEndPoint(networkIp, networkPort);
|
||||
dw.Put(localEndPoint);
|
||||
dw.Put(additionalInfo, MaxTokenLength);
|
||||
|
||||
//prepare packet
|
||||
var packet = _netBase.PacketPool.GetWithData(PacketProperty.NatIntroductionRequest, dw);
|
||||
_netBase.SendRawAndRecycle(packet, masterServerEndPoint);
|
||||
}
|
||||
|
||||
private void HandleNatPunch(NetEndPoint senderEndPoint, NetDataReader dr)
|
||||
{
|
||||
byte fromHostByte = dr.GetByte();
|
||||
if (fromHostByte != HostByte && fromHostByte != ClientByte)
|
||||
{
|
||||
//garbage
|
||||
return;
|
||||
}
|
||||
|
||||
//Read info
|
||||
string additionalInfo = dr.GetString(MaxTokenLength);
|
||||
NetUtils.DebugWrite(ConsoleColor.Green, "[NAT] punch received from {0} - additional info: {1}", senderEndPoint, additionalInfo);
|
||||
|
||||
//Release punch success to client; enabling him to Connect() to msg.Sender if token is ok
|
||||
lock (_successEvents)
|
||||
{
|
||||
_successEvents.Enqueue(new SuccessEventData { TargetEndPoint = senderEndPoint, Token = additionalInfo });
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleNatIntroduction(NetDataReader dr)
|
||||
{
|
||||
// read intro
|
||||
byte hostByte = dr.GetByte();
|
||||
NetEndPoint remoteInternal = dr.GetNetEndPoint();
|
||||
NetEndPoint remoteExternal = dr.GetNetEndPoint();
|
||||
string token = dr.GetString(MaxTokenLength);
|
||||
|
||||
NetUtils.DebugWrite(ConsoleColor.Cyan, "[NAT] introduction received; we are designated " + (hostByte == HostByte ? "host" : "client"));
|
||||
NetDataWriter writer = new NetDataWriter();
|
||||
|
||||
// send internal punch
|
||||
writer.Put(hostByte);
|
||||
writer.Put(token);
|
||||
var packet = _netBase.PacketPool.GetWithData(PacketProperty.NatPunchMessage, writer);
|
||||
_netBase.SendRawAndRecycle(packet, remoteInternal);
|
||||
NetUtils.DebugWrite(ConsoleColor.Cyan, "[NAT] internal punch sent to " + remoteInternal);
|
||||
|
||||
// send external punch
|
||||
writer.Reset();
|
||||
writer.Put(hostByte);
|
||||
writer.Put(token);
|
||||
packet = _netBase.PacketPool.GetWithData(PacketProperty.NatPunchMessage, writer);
|
||||
_netBase.SendRawAndRecycle(packet, remoteExternal);
|
||||
NetUtils.DebugWrite(ConsoleColor.Cyan, "[NAT] external punch sent to " + remoteExternal);
|
||||
}
|
||||
|
||||
private void HandleNatIntroductionRequest(NetEndPoint senderEndPoint, NetDataReader dr)
|
||||
{
|
||||
NetEndPoint localEp = dr.GetNetEndPoint();
|
||||
string token = dr.GetString(MaxTokenLength);
|
||||
lock (_requestEvents)
|
||||
{
|
||||
_requestEvents.Enqueue(new RequestEventData
|
||||
{
|
||||
LocalEndPoint = localEp,
|
||||
RemoteEndPoint = senderEndPoint,
|
||||
Token = token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal void ProcessMessage(NetEndPoint senderEndPoint, NetPacket packet)
|
||||
{
|
||||
var dr = new NetDataReader(packet.RawData, NetConstants.HeaderSize, packet.Size);
|
||||
switch (packet.Property)
|
||||
{
|
||||
case PacketProperty.NatIntroductionRequest:
|
||||
//We got request and must introduce
|
||||
HandleNatIntroductionRequest(senderEndPoint, dr);
|
||||
break;
|
||||
case PacketProperty.NatIntroduction:
|
||||
//We got introduce and must punch
|
||||
HandleNatIntroduction(dr);
|
||||
break;
|
||||
case PacketProperty.NatPunchMessage:
|
||||
//We got punch and can connect
|
||||
HandleNatPunch(senderEndPoint, dr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11f3508667cc14e3797a49d4695ffdd8
|
||||
timeCreated: 1497976517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public enum SendOptions
|
||||
{
|
||||
Unreliable,
|
||||
ReliableUnordered,
|
||||
Sequenced,
|
||||
ReliableOrdered
|
||||
}
|
||||
|
||||
public static class NetConstants
|
||||
{
|
||||
public const int HeaderSize = 1;
|
||||
public const int SequencedHeaderSize = 3;
|
||||
public const int FragmentHeaderSize = 6;
|
||||
public const int DefaultWindowSize = 64;
|
||||
public const ushort MaxSequence = 32768;
|
||||
public const ushort HalfMaxSequence = MaxSequence / 2;
|
||||
|
||||
//socket
|
||||
public const string MulticastGroupIPv4 = "224.0.0.1";
|
||||
public const string MulticastGroupIPv6 = "FF02:0:0:0:0:0:0:1";
|
||||
public const int SocketBufferSize = 1024*1024; //2mb
|
||||
public const int SocketTTL = 255;
|
||||
|
||||
//protocol
|
||||
public const int ProtocolId = 1;
|
||||
public const int MaxUdpHeaderSize = 68;
|
||||
public const int PacketSizeLimit = ushort.MaxValue - MaxUdpHeaderSize;
|
||||
public const int MinPacketSize = 576 - MaxUdpHeaderSize;
|
||||
public const int MinPacketDataSize = MinPacketSize - HeaderSize;
|
||||
public const int MinSequencedPacketDataSize = MinPacketSize - SequencedHeaderSize;
|
||||
|
||||
public static readonly int[] PossibleMtu =
|
||||
{
|
||||
576 - MaxUdpHeaderSize, //Internet Path MTU for X.25 (RFC 879)
|
||||
1492 - MaxUdpHeaderSize, //Ethernet with LLC and SNAP, PPPoE (RFC 1042)
|
||||
1500 - MaxUdpHeaderSize, //Ethernet II (RFC 1191)
|
||||
4352 - MaxUdpHeaderSize, //FDDI
|
||||
4464 - MaxUdpHeaderSize, //Token ring
|
||||
7981 - MaxUdpHeaderSize //WLAN
|
||||
};
|
||||
|
||||
public static int MaxPacketSize = PossibleMtu[PossibleMtu.Length - 1];
|
||||
|
||||
//peer specific
|
||||
public const int FlowUpdateTime = 1000;
|
||||
public const int FlowIncreaseThreshold = 4;
|
||||
public const int DefaultPingInterval = 1000;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02abb4740bff94f28bdd538839339932
|
||||
timeCreated: 1497976517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,444 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib.Utils
|
||||
{
|
||||
public class NetDataReader
|
||||
{
|
||||
protected byte[] _data;
|
||||
protected int _position;
|
||||
protected int _dataSize;
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get { return _data; }
|
||||
}
|
||||
|
||||
public int Position
|
||||
{
|
||||
get { return _position; }
|
||||
}
|
||||
|
||||
public bool EndOfData
|
||||
{
|
||||
get { return _position == _dataSize; }
|
||||
}
|
||||
|
||||
public int AvailableBytes
|
||||
{
|
||||
get { return _dataSize - _position; }
|
||||
}
|
||||
|
||||
public void SetSource(NetDataWriter dataWriter)
|
||||
{
|
||||
_data = dataWriter.Data;
|
||||
_position = 0;
|
||||
_dataSize = dataWriter.Length;
|
||||
}
|
||||
|
||||
public void SetSource(byte[] source)
|
||||
{
|
||||
_data = source;
|
||||
_position = 0;
|
||||
_dataSize = source.Length;
|
||||
}
|
||||
|
||||
public void SetSource(byte[] source, int offset)
|
||||
{
|
||||
_data = source;
|
||||
_position = offset;
|
||||
_dataSize = source.Length;
|
||||
}
|
||||
|
||||
public void SetSource(byte[] source, int offset, int dataSize)
|
||||
{
|
||||
_data = source;
|
||||
_position = offset;
|
||||
_dataSize = dataSize;
|
||||
}
|
||||
|
||||
public NetDataReader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public NetDataReader(byte[] source)
|
||||
{
|
||||
SetSource(source);
|
||||
}
|
||||
|
||||
public NetDataReader(byte[] source, int offset)
|
||||
{
|
||||
SetSource(source, offset);
|
||||
}
|
||||
|
||||
public NetDataReader(byte[] source, int offset, int maxSize)
|
||||
{
|
||||
SetSource(source, offset, maxSize);
|
||||
}
|
||||
|
||||
#region GetMethods
|
||||
public NetEndPoint GetNetEndPoint()
|
||||
{
|
||||
string host = GetString(1000);
|
||||
int port = GetInt();
|
||||
return new NetEndPoint(host, port);
|
||||
}
|
||||
|
||||
public byte GetByte()
|
||||
{
|
||||
byte res = _data[_position];
|
||||
_position += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
public sbyte GetSByte()
|
||||
{
|
||||
var b = (sbyte)_data[_position];
|
||||
_position++;
|
||||
return b;
|
||||
}
|
||||
|
||||
public bool[] GetBoolArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new bool[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetBool();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public ushort[] GetUShortArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new ushort[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetUShort();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public short[] GetShortArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new short[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetShort();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public long[] GetLongArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new long[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetLong();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public ulong[] GetULongArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new ulong[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetULong();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public int[] GetIntArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new int[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetInt();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public uint[] GetUIntArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new uint[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetUInt();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public float[] GetFloatArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new float[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetFloat();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public double[] GetDoubleArray()
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new double[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetDouble();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public string[] GetStringArray(int maxLength)
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
var arr = new string[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
arr[i] = GetString(maxLength);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public bool GetBool()
|
||||
{
|
||||
bool res = _data[_position] > 0;
|
||||
_position += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
public ushort GetUShort()
|
||||
{
|
||||
ushort result = BitConverter.ToUInt16(_data, _position);
|
||||
_position += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
public short GetShort()
|
||||
{
|
||||
short result = BitConverter.ToInt16(_data, _position);
|
||||
_position += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
public long GetLong()
|
||||
{
|
||||
long result = BitConverter.ToInt64(_data, _position);
|
||||
_position += 8;
|
||||
return result;
|
||||
}
|
||||
|
||||
public ulong GetULong()
|
||||
{
|
||||
ulong result = BitConverter.ToUInt64(_data, _position);
|
||||
_position += 8;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int GetInt()
|
||||
{
|
||||
int result = BitConverter.ToInt32(_data, _position);
|
||||
_position += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
public uint GetUInt()
|
||||
{
|
||||
uint result = BitConverter.ToUInt32(_data, _position);
|
||||
_position += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
public float GetFloat()
|
||||
{
|
||||
float result = BitConverter.ToSingle(_data, _position);
|
||||
_position += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
public double GetDouble()
|
||||
{
|
||||
double result = BitConverter.ToDouble(_data, _position);
|
||||
_position += 8;
|
||||
return result;
|
||||
}
|
||||
|
||||
public string GetString(int maxLength)
|
||||
{
|
||||
int bytesCount = GetInt();
|
||||
if (bytesCount <= 0 || bytesCount > maxLength*2)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
int charCount = Encoding.UTF8.GetCharCount(_data, _position, bytesCount);
|
||||
if (charCount > maxLength)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string result = Encoding.UTF8.GetString(_data, _position, bytesCount);
|
||||
_position += bytesCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
public string GetString()
|
||||
{
|
||||
int bytesCount = GetInt();
|
||||
if (bytesCount <= 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string result = Encoding.UTF8.GetString(_data, _position, bytesCount);
|
||||
_position += bytesCount;
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte[] GetRemainingBytes()
|
||||
{
|
||||
byte[] outgoingData = new byte[AvailableBytes];
|
||||
Buffer.BlockCopy(_data, _position, outgoingData, 0, AvailableBytes);
|
||||
_position = _data.Length;
|
||||
return outgoingData;
|
||||
}
|
||||
|
||||
public void GetRemainingBytes(byte[] destination)
|
||||
{
|
||||
Buffer.BlockCopy(_data, _position, destination, 0, AvailableBytes);
|
||||
_position = _data.Length;
|
||||
}
|
||||
|
||||
public void GetBytes(byte[] destination, int lenght)
|
||||
{
|
||||
Buffer.BlockCopy(_data, _position, destination, 0, lenght);
|
||||
_position += lenght;
|
||||
}
|
||||
|
||||
public byte[] GetBytesWithLength()
|
||||
{
|
||||
int length = GetInt();
|
||||
byte[] outgoingData = new byte[length];
|
||||
Buffer.BlockCopy(_data, _position, outgoingData, 0, length);
|
||||
_position += length;
|
||||
return outgoingData;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region PeekMethods
|
||||
|
||||
public byte PeekByte()
|
||||
{
|
||||
return _data[_position];
|
||||
}
|
||||
|
||||
public sbyte PeekSByte()
|
||||
{
|
||||
return (sbyte)_data[_position];
|
||||
}
|
||||
|
||||
public bool PeekBool()
|
||||
{
|
||||
return _data[_position] > 0;
|
||||
}
|
||||
|
||||
public ushort PeekUShort()
|
||||
{
|
||||
return BitConverter.ToUInt16(_data, _position);
|
||||
}
|
||||
|
||||
public short PeekShort()
|
||||
{
|
||||
return BitConverter.ToInt16(_data, _position);
|
||||
}
|
||||
|
||||
public long PeekLong()
|
||||
{
|
||||
return BitConverter.ToInt64(_data, _position);
|
||||
}
|
||||
|
||||
public ulong PeekULong()
|
||||
{
|
||||
return BitConverter.ToUInt64(_data, _position);
|
||||
}
|
||||
|
||||
public int PeekInt()
|
||||
{
|
||||
return BitConverter.ToInt32(_data, _position);
|
||||
}
|
||||
|
||||
public uint PeekUInt()
|
||||
{
|
||||
return BitConverter.ToUInt32(_data, _position);
|
||||
}
|
||||
|
||||
public float PeekFloat()
|
||||
{
|
||||
return BitConverter.ToSingle(_data, _position);
|
||||
}
|
||||
|
||||
public double PeekDouble()
|
||||
{
|
||||
return BitConverter.ToDouble(_data, _position);
|
||||
}
|
||||
|
||||
public string PeekString(int maxLength)
|
||||
{
|
||||
int bytesCount = BitConverter.ToInt32(_data, _position);
|
||||
if (bytesCount <= 0 || bytesCount > maxLength * 2)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
int charCount = Encoding.UTF8.GetCharCount(_data, _position + 4, bytesCount);
|
||||
if (charCount > maxLength)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string result = Encoding.UTF8.GetString(_data, _position + 4, bytesCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
public string PeekString()
|
||||
{
|
||||
int bytesCount = BitConverter.ToInt32(_data, _position);
|
||||
if (bytesCount <= 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string result = Encoding.UTF8.GetString(_data, _position + 4, bytesCount);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_position = 0;
|
||||
_dataSize = 0;
|
||||
_data = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37bb0204fc22b499690c4032caf14811
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,375 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib.Utils
|
||||
{
|
||||
public class NetDataWriter
|
||||
{
|
||||
protected byte[] _data;
|
||||
protected int _position;
|
||||
|
||||
private int _maxLength;
|
||||
private readonly bool _autoResize;
|
||||
|
||||
public NetDataWriter()
|
||||
{
|
||||
_maxLength = 64;
|
||||
_data = new byte[_maxLength];
|
||||
_autoResize = true;
|
||||
}
|
||||
|
||||
public NetDataWriter(bool autoResize)
|
||||
{
|
||||
_maxLength = 64;
|
||||
_data = new byte[_maxLength];
|
||||
_autoResize = autoResize;
|
||||
}
|
||||
|
||||
public NetDataWriter(bool autoResize, int initialSize)
|
||||
{
|
||||
_maxLength = initialSize;
|
||||
_data = new byte[_maxLength];
|
||||
_autoResize = autoResize;
|
||||
}
|
||||
|
||||
public void ResizeIfNeed(int newSize)
|
||||
{
|
||||
if (_maxLength < newSize)
|
||||
{
|
||||
while (_maxLength < newSize)
|
||||
{
|
||||
_maxLength *= 2;
|
||||
}
|
||||
Array.Resize(ref _data, _maxLength);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset(int size)
|
||||
{
|
||||
ResizeIfNeed(size);
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
public byte[] CopyData()
|
||||
{
|
||||
byte[] resultData = new byte[_position];
|
||||
Buffer.BlockCopy(_data, 0, resultData, 0, _position);
|
||||
return resultData;
|
||||
}
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get { return _data; }
|
||||
}
|
||||
|
||||
public int Length
|
||||
{
|
||||
get { return _position; }
|
||||
}
|
||||
|
||||
public void Put(float value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 4);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 4;
|
||||
}
|
||||
|
||||
public void Put(double value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 8);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 8;
|
||||
}
|
||||
|
||||
public void Put(long value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 8);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 8;
|
||||
}
|
||||
|
||||
public void Put(ulong value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 8);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 8;
|
||||
}
|
||||
|
||||
public void Put(int value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 4);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 4;
|
||||
}
|
||||
|
||||
public void Put(uint value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 4);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 4;
|
||||
}
|
||||
|
||||
public void Put(ushort value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 2);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 2;
|
||||
}
|
||||
|
||||
public void Put(short value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 2);
|
||||
FastBitConverter.GetBytes(_data, _position, value);
|
||||
_position += 2;
|
||||
}
|
||||
|
||||
public void Put(sbyte value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 1);
|
||||
_data[_position] = (byte)value;
|
||||
_position++;
|
||||
}
|
||||
|
||||
public void Put(byte value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 1);
|
||||
_data[_position] = value;
|
||||
_position++;
|
||||
}
|
||||
|
||||
public void Put(byte[] data, int offset, int length)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + length);
|
||||
Buffer.BlockCopy(data, offset, _data, _position, length);
|
||||
_position += length;
|
||||
}
|
||||
|
||||
public void Put(byte[] data)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + data.Length);
|
||||
Buffer.BlockCopy(data, 0, _data, _position, data.Length);
|
||||
_position += data.Length;
|
||||
}
|
||||
|
||||
public void PutBytesWithLength(byte[] data, int offset, int length)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + length);
|
||||
Put(length);
|
||||
Buffer.BlockCopy(data, offset, _data, _position, length);
|
||||
_position += length;
|
||||
}
|
||||
|
||||
public void PutBytesWithLength(byte[] data)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + data.Length);
|
||||
Put(data.Length);
|
||||
Buffer.BlockCopy(data, 0, _data, _position, data.Length);
|
||||
_position += data.Length;
|
||||
}
|
||||
|
||||
public void Put(bool value)
|
||||
{
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + 1);
|
||||
_data[_position] = (byte)(value ? 1 : 0);
|
||||
_position++;
|
||||
}
|
||||
|
||||
public void PutArray(float[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 4 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(double[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 8 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(long[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 8 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(ulong[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 8 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(int[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 4 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(uint[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 4 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(ushort[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 2 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(short[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len * 2 + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(bool[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + len + 2);
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(string[] value)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
Put(len);
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
Put(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void PutArray(string[] value, int maxLength)
|
||||
{
|
||||
ushort len = value == null ? (ushort)0 : (ushort)value.Length;
|
||||
Put(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Put(value[i], maxLength);
|
||||
}
|
||||
}
|
||||
|
||||
public void Put(NetEndPoint endPoint)
|
||||
{
|
||||
Put(endPoint.Host);
|
||||
Put(endPoint.Port);
|
||||
}
|
||||
|
||||
public void Put(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
Put(0);
|
||||
return;
|
||||
}
|
||||
|
||||
//put bytes count
|
||||
int bytesCount = Encoding.UTF8.GetByteCount(value);
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + bytesCount + 4);
|
||||
Put(bytesCount);
|
||||
|
||||
//put string
|
||||
Encoding.UTF8.GetBytes(value, 0, value.Length, _data, _position);
|
||||
_position += bytesCount;
|
||||
}
|
||||
|
||||
public void Put(string value, int maxLength)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
Put(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int length = value.Length > maxLength ? maxLength : value.Length;
|
||||
//calculate max count
|
||||
int bytesCount = Encoding.UTF8.GetByteCount(value);
|
||||
if (_autoResize)
|
||||
ResizeIfNeed(_position + bytesCount + 4);
|
||||
|
||||
//put bytes count
|
||||
Put(bytesCount);
|
||||
|
||||
//put string
|
||||
Encoding.UTF8.GetBytes(value, 0, length, _data, _position);
|
||||
|
||||
_position += bytesCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81ecf42c9cc394fc395942030e71bddd
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public interface INetLogger
|
||||
{
|
||||
void WriteNet(ConsoleColor color, string str, params object[] args);
|
||||
}
|
||||
|
||||
public static class NetDebug
|
||||
{
|
||||
public static INetLogger Logger = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78dc00ceb66ac4fdfa8c3957763522ba
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,221 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
#if !WINRT || UNITY_EDITOR
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public sealed class NetEndPoint
|
||||
{
|
||||
public string Host { get { return EndPoint.Address.ToString(); } }
|
||||
public int Port { get { return EndPoint.Port; } }
|
||||
|
||||
internal readonly IPEndPoint EndPoint;
|
||||
|
||||
internal NetEndPoint(IPEndPoint ipEndPoint)
|
||||
{
|
||||
EndPoint = ipEndPoint;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is NetEndPoint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return EndPoint.Equals(((NetEndPoint)obj).EndPoint);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return EndPoint.ToString();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return EndPoint.GetHashCode();
|
||||
}
|
||||
|
||||
public NetEndPoint(string hostStr, int port)
|
||||
{
|
||||
IPAddress ipAddress;
|
||||
if (!IPAddress.TryParse(hostStr, out ipAddress))
|
||||
{
|
||||
if (Socket.OSSupportsIPv6)
|
||||
{
|
||||
if (hostStr == "localhost")
|
||||
{
|
||||
ipAddress = IPAddress.IPv6Loopback;
|
||||
}
|
||||
else
|
||||
{
|
||||
ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetworkV6);
|
||||
}
|
||||
}
|
||||
if (ipAddress == null)
|
||||
{
|
||||
ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetwork);
|
||||
}
|
||||
}
|
||||
if (ipAddress == null)
|
||||
{
|
||||
throw new Exception("Invalid address: " + hostStr);
|
||||
}
|
||||
EndPoint = new IPEndPoint(ipAddress, port);
|
||||
}
|
||||
|
||||
private IPAddress ResolveAddress(string hostStr, AddressFamily addressFamily)
|
||||
{
|
||||
#if NETCORE
|
||||
var hostTask = Dns.GetHostEntryAsync(hostStr);
|
||||
hostTask.Wait();
|
||||
var host = hostTask.Result;
|
||||
#else
|
||||
var host = Dns.GetHostEntry(hostStr);
|
||||
#endif
|
||||
foreach (IPAddress ip in host.AddressList)
|
||||
{
|
||||
if (ip.AddressFamily == addressFamily)
|
||||
{
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal long GetId()
|
||||
{
|
||||
byte[] addr = EndPoint.Address.GetAddressBytes();
|
||||
long id = 0;
|
||||
|
||||
if (addr.Length == 4) //IPv4
|
||||
{
|
||||
id = addr[0];
|
||||
id |= (long)addr[1] << 8;
|
||||
id |= (long)addr[2] << 16;
|
||||
id |= (long)addr[3] << 24;
|
||||
id |= (long)EndPoint.Port << 32;
|
||||
}
|
||||
else if (addr.Length == 16) //IPv6
|
||||
{
|
||||
id = addr[0] ^ addr[8];
|
||||
id |= (long)(addr[1] ^ addr[9]) << 8;
|
||||
id |= (long)(addr[2] ^ addr[10]) << 16;
|
||||
|
||||
|
||||
id |= (long)(addr[3] ^ addr[11]) << 24;
|
||||
id |= (long)(addr[4] ^ addr[12]) << 32;
|
||||
id |= (long)(addr[5] ^ addr[13]) << 40;
|
||||
id |= (long)(addr[6] ^ addr[14]) << 48;
|
||||
id |= (long)(Port ^ addr[7] ^ addr[15]) << 56;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
using System;
|
||||
using Windows.Networking;
|
||||
using Windows.Networking.Sockets;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public sealed class NetEndPoint
|
||||
{
|
||||
public string Host { get { return HostName.DisplayName; } }
|
||||
public int Port { get; private set; }
|
||||
internal readonly HostName HostName;
|
||||
internal readonly string PortStr;
|
||||
|
||||
internal NetEndPoint(int port)
|
||||
{
|
||||
HostName = null;
|
||||
PortStr = port.ToString();
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is NetEndPoint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
NetEndPoint other = (NetEndPoint) obj;
|
||||
return HostName.IsEqual(other.HostName) && PortStr.Equals(other.PortStr);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HostName.CanonicalName.GetHashCode() ^ PortStr.GetHashCode();
|
||||
}
|
||||
|
||||
internal long GetId()
|
||||
{
|
||||
//Check locals
|
||||
if (HostName == null)
|
||||
{
|
||||
return ParseIpToId("0.0.0.0");
|
||||
}
|
||||
|
||||
if (HostName.DisplayName == "localhost")
|
||||
{
|
||||
return ParseIpToId("127.0.0.1");
|
||||
}
|
||||
|
||||
//Check remote
|
||||
string hostIp = string.Empty;
|
||||
var task = DatagramSocket.GetEndpointPairsAsync(HostName, "0").AsTask();
|
||||
task.Wait();
|
||||
|
||||
//IPv4
|
||||
foreach (var endpointPair in task.Result)
|
||||
{
|
||||
hostIp = endpointPair.RemoteHostName.CanonicalName;
|
||||
if (endpointPair.RemoteHostName.Type == HostNameType.Ipv4)
|
||||
{
|
||||
return ParseIpToId(hostIp);
|
||||
}
|
||||
}
|
||||
|
||||
//Else
|
||||
return hostIp.GetHashCode() ^ Port;
|
||||
}
|
||||
|
||||
private long ParseIpToId(string hostIp)
|
||||
{
|
||||
long id = 0;
|
||||
string[] ip = hostIp.Split('.');
|
||||
id |= long.Parse(ip[0]);
|
||||
id |= long.Parse(ip[1]) << 8;
|
||||
id |= long.Parse(ip[2]) << 16;
|
||||
id |= long.Parse(ip[3]) << 24;
|
||||
id |= (long)Port << 32;
|
||||
return id;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return HostName.CanonicalName + ":" + PortStr;
|
||||
}
|
||||
|
||||
public NetEndPoint(string hostName, int port)
|
||||
{
|
||||
var task = DatagramSocket.GetEndpointPairsAsync(new HostName(hostName), port.ToString()).AsTask();
|
||||
task.Wait();
|
||||
HostName = task.Result[0].RemoteHostName;
|
||||
Port = port;
|
||||
PortStr = port.ToString();
|
||||
}
|
||||
|
||||
internal NetEndPoint(HostName hostName, string port)
|
||||
{
|
||||
HostName = hostName;
|
||||
Port = int.Parse(port);
|
||||
PortStr = port;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33fb66c3ba5b8429fbbb0a2f5e7ceb57
|
||||
timeCreated: 1497976517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0197124da84847d8855d5f690b8c653
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,163 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal enum PacketProperty : byte
|
||||
{
|
||||
Unreliable, //0
|
||||
Reliable, //1
|
||||
Sequenced, //2
|
||||
ReliableOrdered, //3
|
||||
AckReliable, //4
|
||||
AckReliableOrdered, //5
|
||||
Ping, //6
|
||||
Pong, //7
|
||||
ConnectRequest, //8
|
||||
ConnectAccept, //9
|
||||
Disconnect, //10
|
||||
UnconnectedMessage, //11
|
||||
NatIntroductionRequest, //12
|
||||
NatIntroduction, //13
|
||||
NatPunchMessage, //14
|
||||
MtuCheck, //15
|
||||
MtuOk, //16
|
||||
DiscoveryRequest, //17
|
||||
DiscoveryResponse, //18
|
||||
Merged //19
|
||||
}
|
||||
|
||||
internal sealed class NetPacket
|
||||
{
|
||||
private const int LastProperty = 19;
|
||||
|
||||
//Header
|
||||
public PacketProperty Property
|
||||
{
|
||||
get { return (PacketProperty)(RawData[0] & 0x7F); }
|
||||
set { RawData[0] = (byte)((RawData[0] & 0x80) | ((byte)value & 0x7F)); }
|
||||
}
|
||||
|
||||
public ushort Sequence
|
||||
{
|
||||
get { return BitConverter.ToUInt16(RawData, 1); }
|
||||
set { FastBitConverter.GetBytes(RawData, 1, value); }
|
||||
}
|
||||
|
||||
public bool IsFragmented
|
||||
{
|
||||
get { return (RawData[0] & 0x80) != 0; }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
RawData[0] |= 0x80; //set first bit
|
||||
else
|
||||
RawData[0] &= 0x7F; //unset first bit
|
||||
}
|
||||
}
|
||||
|
||||
public ushort FragmentId
|
||||
{
|
||||
get { return BitConverter.ToUInt16(RawData, 3); }
|
||||
set { FastBitConverter.GetBytes(RawData, 3, value); }
|
||||
}
|
||||
|
||||
public ushort FragmentPart
|
||||
{
|
||||
get { return BitConverter.ToUInt16(RawData, 5); }
|
||||
set { FastBitConverter.GetBytes(RawData, 5, value); }
|
||||
}
|
||||
|
||||
public ushort FragmentsTotal
|
||||
{
|
||||
get { return BitConverter.ToUInt16(RawData, 7); }
|
||||
set { FastBitConverter.GetBytes(RawData, 7, value); }
|
||||
}
|
||||
|
||||
//Data
|
||||
public readonly byte[] RawData;
|
||||
public int Size;
|
||||
|
||||
public NetPacket(int size)
|
||||
{
|
||||
RawData = new byte[size];
|
||||
Size = 0;
|
||||
}
|
||||
|
||||
public static bool GetPacketProperty(byte[] data, out PacketProperty property)
|
||||
{
|
||||
byte properyByte = (byte)(data[0] & 0x7F);
|
||||
if (properyByte > LastProperty)
|
||||
{
|
||||
property = PacketProperty.Unreliable;
|
||||
return false;
|
||||
}
|
||||
property = (PacketProperty)properyByte;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int GetHeaderSize(PacketProperty property)
|
||||
{
|
||||
return IsSequenced(property)
|
||||
? NetConstants.SequencedHeaderSize
|
||||
: NetConstants.HeaderSize;
|
||||
}
|
||||
|
||||
public int GetHeaderSize()
|
||||
{
|
||||
return GetHeaderSize(Property);
|
||||
}
|
||||
|
||||
public byte[] GetPacketData()
|
||||
{
|
||||
int headerSize = GetHeaderSize(Property);
|
||||
int dataSize = Size - headerSize;
|
||||
byte[] data = new byte[dataSize];
|
||||
Buffer.BlockCopy(RawData, headerSize, data, 0, dataSize);
|
||||
return data;
|
||||
}
|
||||
|
||||
public bool IsClientData()
|
||||
{
|
||||
var property = Property;
|
||||
return property == PacketProperty.Reliable ||
|
||||
property == PacketProperty.ReliableOrdered ||
|
||||
property == PacketProperty.Unreliable ||
|
||||
property == PacketProperty.Sequenced;
|
||||
}
|
||||
|
||||
public static bool IsSequenced(PacketProperty property)
|
||||
{
|
||||
return property == PacketProperty.ReliableOrdered ||
|
||||
property == PacketProperty.Reliable ||
|
||||
property == PacketProperty.Sequenced ||
|
||||
property == PacketProperty.Ping ||
|
||||
property == PacketProperty.Pong ||
|
||||
property == PacketProperty.AckReliable ||
|
||||
property == PacketProperty.AckReliableOrdered;
|
||||
}
|
||||
|
||||
//Packet contstructor from byte array
|
||||
public bool FromBytes(byte[] data, int start, int packetSize)
|
||||
{
|
||||
//Reading property
|
||||
byte property = (byte)(data[start] & 0x7F);
|
||||
bool fragmented = (data[start] & 0x80) != 0;
|
||||
int headerSize = GetHeaderSize((PacketProperty) property);
|
||||
|
||||
if (property > LastProperty ||
|
||||
packetSize > NetConstants.PacketSizeLimit ||
|
||||
packetSize < headerSize ||
|
||||
(fragmented && packetSize < headerSize + NetConstants.FragmentHeaderSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Buffer.BlockCopy(data, start, RawData, 0, packetSize);
|
||||
Size = packetSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7aa9ed55f53fa48569ccd0963c50d8da
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,101 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal class NetPacketPool
|
||||
{
|
||||
private readonly Stack<NetPacket> _pool;
|
||||
|
||||
public NetPacketPool()
|
||||
{
|
||||
_pool = new Stack<NetPacket>();
|
||||
}
|
||||
|
||||
public NetPacket GetWithData(PacketProperty property, NetDataWriter writer)
|
||||
{
|
||||
var packet = Get(property, writer.Length);
|
||||
Buffer.BlockCopy(writer.Data, 0, packet.RawData, NetPacket.GetHeaderSize(property), writer.Length);
|
||||
return packet;
|
||||
}
|
||||
|
||||
public NetPacket GetWithData(PacketProperty property, byte[] data, int start, int length)
|
||||
{
|
||||
var packet = Get(property, length);
|
||||
Buffer.BlockCopy(data, start, packet.RawData, NetPacket.GetHeaderSize(property), length);
|
||||
return packet;
|
||||
}
|
||||
|
||||
//Get packet just for read
|
||||
public NetPacket GetAndRead(byte[] data, int start, int count)
|
||||
{
|
||||
NetPacket packet = null;
|
||||
lock (_pool)
|
||||
{
|
||||
if (_pool.Count > 0)
|
||||
{
|
||||
packet = _pool.Pop();
|
||||
}
|
||||
}
|
||||
if (packet == null)
|
||||
{
|
||||
//allocate new packet of max size or bigger
|
||||
packet = new NetPacket(NetConstants.MaxPacketSize);
|
||||
}
|
||||
if (!packet.FromBytes(data, start, count))
|
||||
{
|
||||
Recycle(packet);
|
||||
return null;
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
//Get packet with size
|
||||
public NetPacket Get(PacketProperty property, int size)
|
||||
{
|
||||
NetPacket packet = null;
|
||||
size += NetPacket.GetHeaderSize(property);
|
||||
if (size <= NetConstants.MaxPacketSize)
|
||||
{
|
||||
lock (_pool)
|
||||
{
|
||||
if (_pool.Count > 0)
|
||||
{
|
||||
packet = _pool.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (packet == null)
|
||||
{
|
||||
//allocate new packet of max size or bigger
|
||||
packet = new NetPacket(size > NetConstants.MaxPacketSize ? size : NetConstants.MaxPacketSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Clear(packet.RawData, 0, size);
|
||||
}
|
||||
packet.Property = property;
|
||||
packet.Size = size;
|
||||
return packet;
|
||||
}
|
||||
|
||||
public void Recycle(NetPacket packet)
|
||||
{
|
||||
if (packet.Size > NetConstants.MaxPacketSize)
|
||||
{
|
||||
//Dont pool big packets. Save memory
|
||||
return;
|
||||
}
|
||||
|
||||
//Clean fragmented flag
|
||||
packet.IsFragmented = false;
|
||||
lock (_pool)
|
||||
{
|
||||
_pool.Push(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3637381933a4745b996d4dd48cd71efe
|
||||
timeCreated: 1497976517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,857 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using FlyingWormConsole3.LiteNetLib.Utils;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public enum ConnectionState
|
||||
{
|
||||
InProgress,
|
||||
Connected,
|
||||
Disconnected
|
||||
}
|
||||
|
||||
public sealed class NetPeer
|
||||
{
|
||||
//Flow control
|
||||
private int _currentFlowMode;
|
||||
private int _sendedPacketsCount;
|
||||
private int _flowTimer;
|
||||
|
||||
//Ping and RTT
|
||||
private int _ping;
|
||||
private int _rtt;
|
||||
private int _avgRtt;
|
||||
private int _rttCount;
|
||||
private int _goodRttCount;
|
||||
private ushort _pingSequence;
|
||||
private ushort _remotePingSequence;
|
||||
private double _resendDelay = 27.0;
|
||||
|
||||
private int _pingSendTimer;
|
||||
private const int RttResetDelay = 1000;
|
||||
private int _rttResetTimer;
|
||||
|
||||
private DateTime _pingTimeStart;
|
||||
private int _timeSinceLastPacket;
|
||||
|
||||
//Common
|
||||
private readonly NetEndPoint _remoteEndPoint;
|
||||
private readonly NetManager _peerListener;
|
||||
private readonly NetPacketPool _packetPool;
|
||||
private readonly object _flushLock = new object();
|
||||
|
||||
//Channels
|
||||
private readonly ReliableChannel _reliableOrderedChannel;
|
||||
private readonly ReliableChannel _reliableUnorderedChannel;
|
||||
private readonly SequencedChannel _sequencedChannel;
|
||||
private readonly SimpleChannel _simpleChannel;
|
||||
|
||||
private int _windowSize = NetConstants.DefaultWindowSize;
|
||||
|
||||
//MTU
|
||||
private int _mtu = NetConstants.PossibleMtu[0];
|
||||
private int _mtuIdx;
|
||||
private bool _finishMtu;
|
||||
private int _mtuCheckTimer;
|
||||
private int _mtuCheckAttempts;
|
||||
private const int MtuCheckDelay = 1000;
|
||||
private const int MaxMtuCheckAttempts = 4;
|
||||
private readonly object _mtuMutex = new object();
|
||||
|
||||
//Fragment
|
||||
private class IncomingFragments
|
||||
{
|
||||
public NetPacket[] Fragments;
|
||||
public int ReceivedCount;
|
||||
public int TotalSize;
|
||||
}
|
||||
private ushort _fragmentId;
|
||||
private readonly Dictionary<ushort, IncomingFragments> _holdedFragments;
|
||||
|
||||
//Merging
|
||||
private readonly NetPacket _mergeData;
|
||||
private int _mergePos;
|
||||
private int _mergeCount;
|
||||
|
||||
//Connection
|
||||
private int _connectAttempts;
|
||||
private int _connectTimer;
|
||||
private long _connectId;
|
||||
private ConnectionState _connectionState;
|
||||
|
||||
public ConnectionState ConnectionState
|
||||
{
|
||||
get { return _connectionState; }
|
||||
}
|
||||
|
||||
public long ConnectId
|
||||
{
|
||||
get { return _connectId; }
|
||||
}
|
||||
|
||||
public NetEndPoint EndPoint
|
||||
{
|
||||
get { return _remoteEndPoint; }
|
||||
}
|
||||
|
||||
public int Ping
|
||||
{
|
||||
get { return _ping; }
|
||||
}
|
||||
|
||||
public int CurrentFlowMode
|
||||
{
|
||||
get { return _currentFlowMode; }
|
||||
}
|
||||
|
||||
public int Mtu
|
||||
{
|
||||
get { return _mtu; }
|
||||
}
|
||||
|
||||
public int TimeSinceLastPacket
|
||||
{
|
||||
get { return _timeSinceLastPacket; }
|
||||
}
|
||||
|
||||
public NetManager NetManager
|
||||
{
|
||||
get { return _peerListener; }
|
||||
}
|
||||
|
||||
public int PacketsCountInReliableQueue
|
||||
{
|
||||
get { return _reliableUnorderedChannel.PacketsInQueue; }
|
||||
}
|
||||
|
||||
public int PacketsCountInReliableOrderedQueue
|
||||
{
|
||||
get { return _reliableOrderedChannel.PacketsInQueue; }
|
||||
}
|
||||
|
||||
internal double ResendDelay
|
||||
{
|
||||
get { return _resendDelay; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Application defined object containing data about the connection
|
||||
/// </summary>
|
||||
public object Tag;
|
||||
|
||||
internal NetPeer(NetManager peerListener, NetEndPoint remoteEndPoint, long connectId)
|
||||
{
|
||||
_packetPool = peerListener.PacketPool;
|
||||
_peerListener = peerListener;
|
||||
_remoteEndPoint = remoteEndPoint;
|
||||
|
||||
_avgRtt = 0;
|
||||
_rtt = 0;
|
||||
_pingSendTimer = 0;
|
||||
|
||||
_reliableOrderedChannel = new ReliableChannel(this, true, _windowSize);
|
||||
_reliableUnorderedChannel = new ReliableChannel(this, false, _windowSize);
|
||||
_sequencedChannel = new SequencedChannel(this);
|
||||
_simpleChannel = new SimpleChannel(this);
|
||||
|
||||
_holdedFragments = new Dictionary<ushort, IncomingFragments>();
|
||||
|
||||
_mergeData = _packetPool.Get(PacketProperty.Merged, NetConstants.MaxPacketSize);
|
||||
|
||||
//if ID != 0 then we already connected
|
||||
_connectAttempts = 0;
|
||||
if (connectId == 0)
|
||||
{
|
||||
_connectId = DateTime.UtcNow.Ticks;
|
||||
SendConnectRequest();
|
||||
}
|
||||
else
|
||||
{
|
||||
_connectId = connectId;
|
||||
_connectionState = ConnectionState.Connected;
|
||||
SendConnectAccept();
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite(ConsoleColor.Cyan, "[CC] ConnectId: {0}", _connectId);
|
||||
}
|
||||
|
||||
private void SendConnectRequest()
|
||||
{
|
||||
//Get connect key bytes
|
||||
byte[] keyData = Encoding.UTF8.GetBytes(_peerListener.ConnectKey);
|
||||
|
||||
//Make initial packet
|
||||
var connectPacket = _packetPool.Get(PacketProperty.ConnectRequest, 12 + keyData.Length);
|
||||
|
||||
//Add data
|
||||
FastBitConverter.GetBytes(connectPacket.RawData, 1, NetConstants.ProtocolId);
|
||||
FastBitConverter.GetBytes(connectPacket.RawData, 5, _connectId);
|
||||
Buffer.BlockCopy(keyData, 0, connectPacket.RawData, 13, keyData.Length);
|
||||
|
||||
//Send raw
|
||||
_peerListener.SendRawAndRecycle(connectPacket, _remoteEndPoint);
|
||||
}
|
||||
|
||||
private void SendConnectAccept()
|
||||
{
|
||||
//Reset connection timer
|
||||
_timeSinceLastPacket = 0;
|
||||
|
||||
//Make initial packet
|
||||
var connectPacket = _packetPool.Get(PacketProperty.ConnectAccept, 8);
|
||||
|
||||
//Add data
|
||||
FastBitConverter.GetBytes(connectPacket.RawData, 1, _connectId);
|
||||
|
||||
//Send raw
|
||||
_peerListener.SendRawAndRecycle(connectPacket, _remoteEndPoint);
|
||||
}
|
||||
|
||||
internal bool ProcessConnectAccept(NetPacket packet)
|
||||
{
|
||||
if (_connectionState != ConnectionState.InProgress)
|
||||
return false;
|
||||
|
||||
//check connection id
|
||||
if (BitConverter.ToInt64(packet.RawData, 1) != _connectId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite(ConsoleColor.Cyan, "[NC] Received connection accept");
|
||||
_timeSinceLastPacket = 0;
|
||||
_connectionState = ConnectionState.Connected;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static PacketProperty SendOptionsToProperty(SendOptions options)
|
||||
{
|
||||
switch (options)
|
||||
{
|
||||
case SendOptions.ReliableUnordered:
|
||||
return PacketProperty.Reliable;
|
||||
case SendOptions.Sequenced:
|
||||
return PacketProperty.Sequenced;
|
||||
case SendOptions.ReliableOrdered:
|
||||
return PacketProperty.ReliableOrdered;
|
||||
default:
|
||||
return PacketProperty.Unreliable;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetMaxSinglePacketSize(SendOptions options)
|
||||
{
|
||||
return _mtu - NetPacket.GetHeaderSize(SendOptionsToProperty(options));
|
||||
}
|
||||
|
||||
public void Send(byte[] data, SendOptions options)
|
||||
{
|
||||
Send(data, 0, data.Length, options);
|
||||
}
|
||||
|
||||
public void Send(NetDataWriter dataWriter, SendOptions options)
|
||||
{
|
||||
Send(dataWriter.Data, 0, dataWriter.Length, options);
|
||||
}
|
||||
|
||||
public void Send(byte[] data, int start, int length, SendOptions options)
|
||||
{
|
||||
//Prepare
|
||||
PacketProperty property = SendOptionsToProperty(options);
|
||||
int headerSize = NetPacket.GetHeaderSize(property);
|
||||
|
||||
//Check fragmentation
|
||||
if (length + headerSize > _mtu)
|
||||
{
|
||||
if (options == SendOptions.Sequenced || options == SendOptions.Unreliable)
|
||||
{
|
||||
throw new Exception("Unreliable packet size > allowed (" + (_mtu - headerSize) + ")");
|
||||
}
|
||||
|
||||
int packetFullSize = _mtu - headerSize;
|
||||
int packetDataSize = packetFullSize - NetConstants.FragmentHeaderSize;
|
||||
|
||||
int fullPacketsCount = length / packetDataSize;
|
||||
int lastPacketSize = length % packetDataSize;
|
||||
int totalPackets = fullPacketsCount + (lastPacketSize == 0 ? 0 : 1);
|
||||
|
||||
NetUtils.DebugWrite("FragmentSend:\n" +
|
||||
" MTU: {0}\n" +
|
||||
" headerSize: {1}\n" +
|
||||
" packetFullSize: {2}\n" +
|
||||
" packetDataSize: {3}\n" +
|
||||
" fullPacketsCount: {4}\n" +
|
||||
" lastPacketSize: {5}\n" +
|
||||
" totalPackets: {6}",
|
||||
_mtu, headerSize, packetFullSize, packetDataSize, fullPacketsCount, lastPacketSize, totalPackets);
|
||||
|
||||
if (totalPackets > ushort.MaxValue)
|
||||
{
|
||||
throw new Exception("Too many fragments: " + totalPackets + " > " + ushort.MaxValue);
|
||||
}
|
||||
|
||||
int dataOffset = headerSize + NetConstants.FragmentHeaderSize;
|
||||
for (ushort i = 0; i < fullPacketsCount; i++)
|
||||
{
|
||||
NetPacket p = _packetPool.Get(property, packetFullSize);
|
||||
p.FragmentId = _fragmentId;
|
||||
p.FragmentPart = i;
|
||||
p.FragmentsTotal = (ushort)totalPackets;
|
||||
p.IsFragmented = true;
|
||||
Buffer.BlockCopy(data, i * packetDataSize, p.RawData, dataOffset, packetDataSize);
|
||||
SendPacket(p);
|
||||
}
|
||||
|
||||
if (lastPacketSize > 0)
|
||||
{
|
||||
NetPacket p = _packetPool.Get(property, lastPacketSize + NetConstants.FragmentHeaderSize);
|
||||
p.FragmentId = _fragmentId;
|
||||
p.FragmentPart = (ushort)fullPacketsCount; //last
|
||||
p.FragmentsTotal = (ushort)totalPackets;
|
||||
p.IsFragmented = true;
|
||||
Buffer.BlockCopy(data, fullPacketsCount * packetDataSize, p.RawData, dataOffset, lastPacketSize);
|
||||
SendPacket(p);
|
||||
}
|
||||
|
||||
_fragmentId++;
|
||||
return;
|
||||
}
|
||||
|
||||
//Else just send
|
||||
NetPacket packet = _packetPool.GetWithData(property, data, start, length);
|
||||
SendPacket(packet);
|
||||
}
|
||||
|
||||
private void CreateAndSend(PacketProperty property, ushort sequence)
|
||||
{
|
||||
NetPacket packet = _packetPool.Get(property, 0);
|
||||
packet.Sequence = sequence;
|
||||
SendPacket(packet);
|
||||
}
|
||||
|
||||
//from user thread, our thread, or recv?
|
||||
private void SendPacket(NetPacket packet)
|
||||
{
|
||||
NetUtils.DebugWrite("[RS]Packet: " + packet.Property);
|
||||
switch (packet.Property)
|
||||
{
|
||||
case PacketProperty.Reliable:
|
||||
_reliableUnorderedChannel.AddToQueue(packet);
|
||||
break;
|
||||
case PacketProperty.Sequenced:
|
||||
_sequencedChannel.AddToQueue(packet);
|
||||
break;
|
||||
case PacketProperty.ReliableOrdered:
|
||||
_reliableOrderedChannel.AddToQueue(packet);
|
||||
break;
|
||||
case PacketProperty.Unreliable:
|
||||
_simpleChannel.AddToQueue(packet);
|
||||
break;
|
||||
case PacketProperty.MtuCheck:
|
||||
//Must check result for MTU fix
|
||||
if (!_peerListener.SendRawAndRecycle(packet, _remoteEndPoint))
|
||||
{
|
||||
_finishMtu = true;
|
||||
}
|
||||
break;
|
||||
case PacketProperty.AckReliable:
|
||||
case PacketProperty.AckReliableOrdered:
|
||||
case PacketProperty.Ping:
|
||||
case PacketProperty.Pong:
|
||||
case PacketProperty.Disconnect:
|
||||
case PacketProperty.MtuOk:
|
||||
SendRawData(packet);
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown packet property: " + packet.Property);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRoundTripTime(int roundTripTime)
|
||||
{
|
||||
//Calc average round trip time
|
||||
_rtt += roundTripTime;
|
||||
_rttCount++;
|
||||
_avgRtt = _rtt/_rttCount;
|
||||
|
||||
//flowmode 0 = fastest
|
||||
//flowmode max = lowest
|
||||
|
||||
if (_avgRtt < _peerListener.GetStartRtt(_currentFlowMode - 1))
|
||||
{
|
||||
if (_currentFlowMode <= 0)
|
||||
{
|
||||
//Already maxed
|
||||
return;
|
||||
}
|
||||
|
||||
_goodRttCount++;
|
||||
if (_goodRttCount > NetConstants.FlowIncreaseThreshold)
|
||||
{
|
||||
_goodRttCount = 0;
|
||||
_currentFlowMode--;
|
||||
|
||||
NetUtils.DebugWrite("[PA]Increased flow speed, RTT: {0}, PPS: {1}", _avgRtt, _peerListener.GetPacketsPerSecond(_currentFlowMode));
|
||||
}
|
||||
}
|
||||
else if(_avgRtt > _peerListener.GetStartRtt(_currentFlowMode))
|
||||
{
|
||||
_goodRttCount = 0;
|
||||
if (_currentFlowMode < _peerListener.GetMaxFlowMode())
|
||||
{
|
||||
_currentFlowMode++;
|
||||
NetUtils.DebugWrite("[PA]Decreased flow speed, RTT: {0}, PPS: {1}", _avgRtt, _peerListener.GetPacketsPerSecond(_currentFlowMode));
|
||||
}
|
||||
}
|
||||
|
||||
//recalc resend delay
|
||||
double avgRtt = _avgRtt;
|
||||
if (avgRtt <= 0.0)
|
||||
avgRtt = 0.1;
|
||||
_resendDelay = 25 + (avgRtt * 2.1); // 25 ms + double rtt
|
||||
}
|
||||
|
||||
internal void AddIncomingPacket(NetPacket p)
|
||||
{
|
||||
if (p.IsFragmented)
|
||||
{
|
||||
NetUtils.DebugWrite("Fragment. Id: {0}, Part: {1}, Total: {2}", p.FragmentId, p.FragmentPart, p.FragmentsTotal);
|
||||
//Get needed array from dictionary
|
||||
ushort packetFragId = p.FragmentId;
|
||||
IncomingFragments incomingFragments;
|
||||
if (!_holdedFragments.TryGetValue(packetFragId, out incomingFragments))
|
||||
{
|
||||
incomingFragments = new IncomingFragments
|
||||
{
|
||||
Fragments = new NetPacket[p.FragmentsTotal]
|
||||
};
|
||||
_holdedFragments.Add(packetFragId, incomingFragments);
|
||||
}
|
||||
|
||||
//Cache
|
||||
var fragments = incomingFragments.Fragments;
|
||||
|
||||
//Error check
|
||||
if (p.FragmentPart >= fragments.Length || fragments[p.FragmentPart] != null)
|
||||
{
|
||||
_packetPool.Recycle(p);
|
||||
NetUtils.DebugWriteError("Invalid fragment packet");
|
||||
return;
|
||||
}
|
||||
//Fill array
|
||||
fragments[p.FragmentPart] = p;
|
||||
|
||||
//Increase received fragments count
|
||||
incomingFragments.ReceivedCount++;
|
||||
|
||||
//Increase total size
|
||||
int dataOffset = p.GetHeaderSize() + NetConstants.FragmentHeaderSize;
|
||||
incomingFragments.TotalSize += p.Size - dataOffset;
|
||||
|
||||
//Check for finish
|
||||
if (incomingFragments.ReceivedCount != fragments.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite("Received all fragments!");
|
||||
NetPacket resultingPacket = _packetPool.Get( p.Property, incomingFragments.TotalSize );
|
||||
|
||||
int resultingPacketOffset = resultingPacket.GetHeaderSize();
|
||||
int firstFragmentSize = fragments[0].Size - dataOffset;
|
||||
for (int i = 0; i < incomingFragments.ReceivedCount; i++)
|
||||
{
|
||||
//Create resulting big packet
|
||||
int fragmentSize = fragments[i].Size - dataOffset;
|
||||
Buffer.BlockCopy(
|
||||
fragments[i].RawData,
|
||||
dataOffset,
|
||||
resultingPacket.RawData,
|
||||
resultingPacketOffset + firstFragmentSize * i,
|
||||
fragmentSize);
|
||||
|
||||
//Free memory
|
||||
_packetPool.Recycle(fragments[i]);
|
||||
fragments[i] = null;
|
||||
}
|
||||
|
||||
//Send to process
|
||||
_peerListener.ReceiveFromPeer(resultingPacket, _remoteEndPoint);
|
||||
|
||||
//Clear memory
|
||||
_packetPool.Recycle(resultingPacket);
|
||||
_holdedFragments.Remove(packetFragId);
|
||||
}
|
||||
else //Just simple packet
|
||||
{
|
||||
_peerListener.ReceiveFromPeer(p, _remoteEndPoint);
|
||||
_packetPool.Recycle(p);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessMtuPacket(NetPacket packet)
|
||||
{
|
||||
if (packet.Size == 1 ||
|
||||
packet.RawData[1] >= NetConstants.PossibleMtu.Length)
|
||||
return;
|
||||
|
||||
//MTU auto increase
|
||||
if (packet.Property == PacketProperty.MtuCheck)
|
||||
{
|
||||
if (packet.Size != NetConstants.PossibleMtu[packet.RawData[1]])
|
||||
{
|
||||
return;
|
||||
}
|
||||
_mtuCheckAttempts = 0;
|
||||
NetUtils.DebugWrite("MTU check. Resend: " + packet.RawData[1]);
|
||||
var mtuOkPacket = _packetPool.Get(PacketProperty.MtuOk, 1);
|
||||
mtuOkPacket.RawData[1] = packet.RawData[1];
|
||||
SendPacket(mtuOkPacket);
|
||||
}
|
||||
else if(packet.RawData[1] > _mtuIdx) //MtuOk
|
||||
{
|
||||
lock (_mtuMutex)
|
||||
{
|
||||
_mtuIdx = packet.RawData[1];
|
||||
_mtu = NetConstants.PossibleMtu[_mtuIdx];
|
||||
}
|
||||
//if maxed - finish.
|
||||
if (_mtuIdx == NetConstants.PossibleMtu.Length - 1)
|
||||
{
|
||||
_finishMtu = true;
|
||||
}
|
||||
NetUtils.DebugWrite("MTU ok. Increase to: " + _mtu);
|
||||
}
|
||||
}
|
||||
|
||||
//Process incoming packet
|
||||
internal void ProcessPacket(NetPacket packet)
|
||||
{
|
||||
_timeSinceLastPacket = 0;
|
||||
|
||||
NetUtils.DebugWrite("[RR]PacketProperty: {0}", packet.Property);
|
||||
switch (packet.Property)
|
||||
{
|
||||
case PacketProperty.ConnectRequest:
|
||||
//response with connect
|
||||
long newId = BitConverter.ToInt64(packet.RawData, 1);
|
||||
if (newId > _connectId)
|
||||
{
|
||||
_connectId = newId;
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite("ConnectRequest LastId: {0}, NewId: {1}, EP: {2}", ConnectId, newId, _remoteEndPoint);
|
||||
SendConnectAccept();
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
|
||||
case PacketProperty.Merged:
|
||||
int pos = NetConstants.HeaderSize;
|
||||
while (pos < packet.Size)
|
||||
{
|
||||
ushort size = BitConverter.ToUInt16(packet.RawData, pos);
|
||||
pos += 2;
|
||||
NetPacket mergedPacket = _packetPool.GetAndRead(packet.RawData, pos, size);
|
||||
if (mergedPacket == null)
|
||||
{
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
}
|
||||
pos += size;
|
||||
ProcessPacket(mergedPacket);
|
||||
}
|
||||
break;
|
||||
//If we get ping, send pong
|
||||
case PacketProperty.Ping:
|
||||
if (NetUtils.RelativeSequenceNumber(packet.Sequence, _remotePingSequence) < 0)
|
||||
{
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
}
|
||||
NetUtils.DebugWrite("[PP]Ping receive, send pong");
|
||||
_remotePingSequence = packet.Sequence;
|
||||
_packetPool.Recycle(packet);
|
||||
|
||||
//send
|
||||
CreateAndSend(PacketProperty.Pong, _remotePingSequence);
|
||||
break;
|
||||
|
||||
//If we get pong, calculate ping time and rtt
|
||||
case PacketProperty.Pong:
|
||||
if (NetUtils.RelativeSequenceNumber(packet.Sequence, _pingSequence) < 0)
|
||||
{
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
}
|
||||
_pingSequence = packet.Sequence;
|
||||
int rtt = (int)(DateTime.UtcNow - _pingTimeStart).TotalMilliseconds;
|
||||
UpdateRoundTripTime(rtt);
|
||||
NetUtils.DebugWrite("[PP]Ping: {0}", rtt);
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
|
||||
//Process ack
|
||||
case PacketProperty.AckReliable:
|
||||
_reliableUnorderedChannel.ProcessAck(packet);
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
|
||||
case PacketProperty.AckReliableOrdered:
|
||||
_reliableOrderedChannel.ProcessAck(packet);
|
||||
_packetPool.Recycle(packet);
|
||||
break;
|
||||
|
||||
//Process in order packets
|
||||
case PacketProperty.Sequenced:
|
||||
_sequencedChannel.ProcessPacket(packet);
|
||||
break;
|
||||
|
||||
case PacketProperty.Reliable:
|
||||
_reliableUnorderedChannel.ProcessPacket(packet);
|
||||
break;
|
||||
|
||||
case PacketProperty.ReliableOrdered:
|
||||
_reliableOrderedChannel.ProcessPacket(packet);
|
||||
break;
|
||||
|
||||
//Simple packet without acks
|
||||
case PacketProperty.Unreliable:
|
||||
AddIncomingPacket(packet);
|
||||
return;
|
||||
|
||||
case PacketProperty.MtuCheck:
|
||||
case PacketProperty.MtuOk:
|
||||
ProcessMtuPacket(packet);
|
||||
break;
|
||||
|
||||
default:
|
||||
NetUtils.DebugWriteError("Error! Unexpected packet type: " + packet.Property);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CanMerge(PacketProperty property)
|
||||
{
|
||||
switch (property)
|
||||
{
|
||||
case PacketProperty.ConnectAccept:
|
||||
case PacketProperty.ConnectRequest:
|
||||
case PacketProperty.MtuOk:
|
||||
case PacketProperty.Pong:
|
||||
case PacketProperty.Disconnect:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal void SendRawData(NetPacket packet)
|
||||
{
|
||||
//2 - merge byte + minimal packet size + datalen(ushort)
|
||||
if (_peerListener.MergeEnabled &&
|
||||
CanMerge(packet.Property) &&
|
||||
_mergePos + packet.Size + NetConstants.HeaderSize*2 + 2 < _mtu)
|
||||
{
|
||||
FastBitConverter.GetBytes(_mergeData.RawData, _mergePos + NetConstants.HeaderSize, (ushort)packet.Size);
|
||||
Buffer.BlockCopy(packet.RawData, 0, _mergeData.RawData, _mergePos + NetConstants.HeaderSize + 2, packet.Size);
|
||||
_mergePos += packet.Size + 2;
|
||||
_mergeCount++;
|
||||
|
||||
//DebugWriteForce("Merged: " + _mergePos + "/" + (_mtu - 2) + ", count: " + _mergeCount);
|
||||
return;
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite(ConsoleColor.DarkYellow, "[P]SendingPacket: " + packet.Property);
|
||||
_peerListener.SendRaw(packet.RawData, 0, packet.Size, _remoteEndPoint);
|
||||
}
|
||||
|
||||
private void SendQueuedPackets(int currentMaxSend)
|
||||
{
|
||||
int currentSended = 0;
|
||||
while (currentSended < currentMaxSend)
|
||||
{
|
||||
//Get one of packets
|
||||
if (_reliableOrderedChannel.SendNextPacket() ||
|
||||
_reliableUnorderedChannel.SendNextPacket() ||
|
||||
_sequencedChannel.SendNextPacket() ||
|
||||
_simpleChannel.SendNextPacket())
|
||||
{
|
||||
currentSended++;
|
||||
}
|
||||
else
|
||||
{
|
||||
//no outgoing packets
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Increase counter
|
||||
_sendedPacketsCount += currentSended;
|
||||
|
||||
//If merging enabled
|
||||
if (_mergePos > 0)
|
||||
{
|
||||
if (_mergeCount > 1)
|
||||
{
|
||||
NetUtils.DebugWrite("Send merged: " + _mergePos + ", count: " + _mergeCount);
|
||||
_peerListener.SendRaw(_mergeData.RawData, 0, NetConstants.HeaderSize + _mergePos, _remoteEndPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Send without length information and merging
|
||||
_peerListener.SendRaw(_mergeData.RawData, NetConstants.HeaderSize + 2, _mergePos - 2, _remoteEndPoint);
|
||||
}
|
||||
_mergePos = 0;
|
||||
_mergeCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush all queued packets
|
||||
/// </summary>
|
||||
public void Flush()
|
||||
{
|
||||
lock (_flushLock)
|
||||
{
|
||||
SendQueuedPackets(int.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Update(int deltaTime)
|
||||
{
|
||||
if (_connectionState == ConnectionState.Disconnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeSinceLastPacket += deltaTime;
|
||||
if (_connectionState == ConnectionState.InProgress)
|
||||
{
|
||||
_connectTimer += deltaTime;
|
||||
if (_connectTimer > _peerListener.ReconnectDelay)
|
||||
{
|
||||
_connectTimer = 0;
|
||||
_connectAttempts++;
|
||||
if (_connectAttempts > _peerListener.MaxConnectAttempts)
|
||||
{
|
||||
_connectionState = ConnectionState.Disconnected;
|
||||
return;
|
||||
}
|
||||
|
||||
//else send connect again
|
||||
SendConnectRequest();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//Get current flow mode
|
||||
int maxSendPacketsCount = _peerListener.GetPacketsPerSecond(_currentFlowMode);
|
||||
int currentMaxSend;
|
||||
|
||||
if (maxSendPacketsCount > 0)
|
||||
{
|
||||
int availableSendPacketsCount = maxSendPacketsCount - _sendedPacketsCount;
|
||||
currentMaxSend = Math.Min(availableSendPacketsCount, (maxSendPacketsCount*deltaTime)/NetConstants.FlowUpdateTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentMaxSend = int.MaxValue;
|
||||
}
|
||||
|
||||
//DebugWrite("[UPDATE]Delta: {0}ms, MaxSend: {1}", deltaTime, currentMaxSend);
|
||||
|
||||
//Pending acks
|
||||
_reliableOrderedChannel.SendAcks();
|
||||
_reliableUnorderedChannel.SendAcks();
|
||||
|
||||
//ResetFlowTimer
|
||||
_flowTimer += deltaTime;
|
||||
if (_flowTimer >= NetConstants.FlowUpdateTime)
|
||||
{
|
||||
NetUtils.DebugWrite("[UPDATE]Reset flow timer, _sendedPackets - {0}", _sendedPacketsCount);
|
||||
_sendedPacketsCount = 0;
|
||||
_flowTimer = 0;
|
||||
}
|
||||
|
||||
//Send ping
|
||||
_pingSendTimer += deltaTime;
|
||||
if (_pingSendTimer >= _peerListener.PingInterval)
|
||||
{
|
||||
NetUtils.DebugWrite("[PP] Send ping...");
|
||||
|
||||
//reset timer
|
||||
_pingSendTimer = 0;
|
||||
|
||||
//send ping
|
||||
CreateAndSend(PacketProperty.Ping, _pingSequence);
|
||||
|
||||
//reset timer
|
||||
_pingTimeStart = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
//RTT - round trip time
|
||||
_rttResetTimer += deltaTime;
|
||||
if (_rttResetTimer >= RttResetDelay)
|
||||
{
|
||||
_rttResetTimer = 0;
|
||||
//Rtt update
|
||||
_rtt = _avgRtt;
|
||||
_ping = _avgRtt;
|
||||
_peerListener.ConnectionLatencyUpdated(this, _ping);
|
||||
_rttCount = 1;
|
||||
}
|
||||
|
||||
//MTU - Maximum transmission unit
|
||||
if (!_finishMtu)
|
||||
{
|
||||
_mtuCheckTimer += deltaTime;
|
||||
if (_mtuCheckTimer >= MtuCheckDelay)
|
||||
{
|
||||
_mtuCheckTimer = 0;
|
||||
_mtuCheckAttempts++;
|
||||
if (_mtuCheckAttempts >= MaxMtuCheckAttempts)
|
||||
{
|
||||
_finishMtu = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (_mtuMutex)
|
||||
{
|
||||
//Send increased packet
|
||||
if (_mtuIdx < NetConstants.PossibleMtu.Length - 1)
|
||||
{
|
||||
int newMtu = NetConstants.PossibleMtu[_mtuIdx + 1] - NetConstants.HeaderSize;
|
||||
var p = _packetPool.Get(PacketProperty.MtuCheck, newMtu);
|
||||
p.RawData[1] = (byte)(_mtuIdx + 1);
|
||||
SendPacket(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//MTU - end
|
||||
|
||||
//Pending send
|
||||
lock (_flushLock)
|
||||
{
|
||||
SendQueuedPackets(currentMaxSend);
|
||||
}
|
||||
}
|
||||
|
||||
//For channels
|
||||
internal void Recycle(NetPacket packet)
|
||||
{
|
||||
_packetPool.Recycle(packet);
|
||||
}
|
||||
|
||||
internal NetPacket GetPacketFromPool(PacketProperty property, int bytesCount)
|
||||
{
|
||||
return _packetPool.Get(property, bytesCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39ca2e83856ea4ba0a0100a00089e695
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class NetPeerCollection
|
||||
{
|
||||
private readonly Dictionary<NetEndPoint, NetPeer> _peersDict;
|
||||
private readonly NetPeer[] _peersArray;
|
||||
private int _count;
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _count; }
|
||||
}
|
||||
|
||||
public NetPeer this[int index]
|
||||
{
|
||||
get { return _peersArray[index]; }
|
||||
}
|
||||
|
||||
public NetPeerCollection(int maxPeers)
|
||||
{
|
||||
_peersArray = new NetPeer[maxPeers];
|
||||
_peersDict = new Dictionary<NetEndPoint, NetPeer>();
|
||||
}
|
||||
|
||||
public bool TryGetValue(NetEndPoint endPoint, out NetPeer peer)
|
||||
{
|
||||
return _peersDict.TryGetValue(endPoint, out peer);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(_peersArray, 0, _count);
|
||||
_peersDict.Clear();
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public void Add(NetEndPoint endPoint, NetPeer peer)
|
||||
{
|
||||
_peersArray[_count] = peer;
|
||||
_peersDict.Add(endPoint, peer);
|
||||
_count++;
|
||||
}
|
||||
|
||||
public bool ContainsAddress(NetEndPoint endPoint)
|
||||
{
|
||||
return _peersDict.ContainsKey(endPoint);
|
||||
}
|
||||
|
||||
public NetPeer[] ToArray()
|
||||
{
|
||||
NetPeer[] result = new NetPeer[_count];
|
||||
Array.Copy(_peersArray, 0, result, 0, _count);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void RemoveAt(int idx)
|
||||
{
|
||||
_peersDict.Remove(_peersArray[idx].EndPoint);
|
||||
_peersArray[idx] = _peersArray[_count - 1];
|
||||
_peersArray[_count - 1] = null;
|
||||
_count--;
|
||||
}
|
||||
|
||||
public void Remove(NetEndPoint endPoint)
|
||||
{
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
if (_peersArray[i].EndPoint.Equals(endPoint))
|
||||
{
|
||||
RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca9b271291848481a86c9b3cb7f07451
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,709 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
#if WINRT || NETCORE
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib.Utils
|
||||
{
|
||||
public interface INetSerializable
|
||||
{
|
||||
void Serialize(NetDataWriter writer);
|
||||
void Desereialize(NetDataReader reader);
|
||||
}
|
||||
|
||||
public abstract class NetSerializerHasher
|
||||
{
|
||||
public abstract ulong GetHash(string type);
|
||||
public abstract void WriteHash(ulong hash, NetDataWriter writer);
|
||||
public abstract ulong ReadHash(NetDataReader reader);
|
||||
}
|
||||
|
||||
public sealed class FNVHasher : NetSerializerHasher
|
||||
{
|
||||
private readonly Dictionary<string, ulong> _hashCache = new Dictionary<string, ulong>();
|
||||
private readonly char[] _hashBuffer = new char[1024];
|
||||
|
||||
public override ulong GetHash(string type)
|
||||
{
|
||||
ulong hash;
|
||||
if (_hashCache.TryGetValue(type, out hash))
|
||||
{
|
||||
return hash;
|
||||
}
|
||||
hash = 14695981039346656037UL; //offset
|
||||
int len = type.Length;
|
||||
type.CopyTo(0, _hashBuffer, 0, len);
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
hash = hash ^ _hashBuffer[i];
|
||||
hash *= 1099511628211UL; //prime
|
||||
}
|
||||
_hashCache.Add(type, hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public override ulong ReadHash(NetDataReader reader)
|
||||
{
|
||||
return reader.GetULong();
|
||||
}
|
||||
|
||||
public override void WriteHash(ulong hash, NetDataWriter writer)
|
||||
{
|
||||
writer.Put(hash);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NetSerializer
|
||||
{
|
||||
private sealed class CustomType
|
||||
{
|
||||
public readonly CustomTypeWrite WriteDelegate;
|
||||
public readonly CustomTypeRead ReadDelegate;
|
||||
|
||||
public CustomType(CustomTypeWrite writeDelegate, CustomTypeRead readDelegate)
|
||||
{
|
||||
WriteDelegate = writeDelegate;
|
||||
ReadDelegate = readDelegate;
|
||||
}
|
||||
}
|
||||
|
||||
private delegate void CustomTypeWrite(NetDataWriter writer, object customObj);
|
||||
private delegate object CustomTypeRead(NetDataReader reader);
|
||||
|
||||
private sealed class StructInfo
|
||||
{
|
||||
public readonly Action<NetDataWriter>[] WriteDelegate;
|
||||
public readonly Action<NetDataReader>[] ReadDelegate;
|
||||
public readonly Type[] FieldTypes;
|
||||
public object Reference;
|
||||
public Func<object> CreatorFunc;
|
||||
public Action<object, object> OnReceive;
|
||||
public readonly ulong Hash;
|
||||
public readonly int MembersCount;
|
||||
|
||||
public StructInfo(ulong hash, int membersCount)
|
||||
{
|
||||
Hash = hash;
|
||||
MembersCount = membersCount;
|
||||
WriteDelegate = new Action<NetDataWriter>[membersCount];
|
||||
ReadDelegate = new Action<NetDataReader>[membersCount];
|
||||
FieldTypes = new Type[membersCount];
|
||||
}
|
||||
|
||||
public void Write(NetDataWriter writer, object obj)
|
||||
{
|
||||
Reference = obj;
|
||||
for (int i = 0; i < MembersCount; i++)
|
||||
{
|
||||
WriteDelegate[i](writer);
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(NetDataReader reader)
|
||||
{
|
||||
for (int i = 0; i < MembersCount; i++)
|
||||
{
|
||||
ReadDelegate[i](reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<ulong, StructInfo> _cache;
|
||||
private readonly Dictionary<Type, CustomType> _registeredCustomTypes;
|
||||
|
||||
private static readonly HashSet<Type> BasicTypes = new HashSet<Type>
|
||||
{
|
||||
typeof(int),
|
||||
typeof(uint),
|
||||
typeof(byte),
|
||||
typeof(sbyte),
|
||||
typeof(short),
|
||||
typeof(ushort),
|
||||
typeof(long),
|
||||
typeof(ulong),
|
||||
typeof(string),
|
||||
typeof(float),
|
||||
typeof(double),
|
||||
typeof(bool)
|
||||
};
|
||||
|
||||
private readonly NetDataWriter _writer;
|
||||
private readonly NetSerializerHasher _hasher;
|
||||
private const int MaxStringLenght = 1024;
|
||||
|
||||
public NetSerializer() : this(new FNVHasher())
|
||||
{
|
||||
}
|
||||
|
||||
public NetSerializer(NetSerializerHasher hasher)
|
||||
{
|
||||
_hasher = hasher;
|
||||
_cache = new Dictionary<ulong, StructInfo>();
|
||||
_registeredCustomTypes = new Dictionary<Type, CustomType>();
|
||||
_writer = new NetDataWriter();
|
||||
}
|
||||
|
||||
private bool RegisterCustomTypeInternal<T>(Func<T> constructor) where T : INetSerializable
|
||||
{
|
||||
var t = typeof(T);
|
||||
if (_registeredCustomTypes.ContainsKey(t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var rwDelegates = new CustomType(
|
||||
(writer, obj) =>
|
||||
{
|
||||
((T)obj).Serialize(writer);
|
||||
},
|
||||
reader =>
|
||||
{
|
||||
var instance = constructor();
|
||||
instance.Desereialize(reader);
|
||||
return instance;
|
||||
});
|
||||
_registeredCustomTypes.Add(t, rwDelegates);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register custom property type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">INetSerializable structure</typeparam>
|
||||
/// <returns>True - if register successful, false - if type already registered</returns>
|
||||
public bool RegisterCustomType<T>() where T : struct, INetSerializable
|
||||
{
|
||||
return RegisterCustomTypeInternal(() => new T());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register custom property type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">INetSerializable class</typeparam>
|
||||
/// <returns>True - if register successful, false - if type already registered</returns>
|
||||
public bool RegisterCustomType<T>(Func<T> constructor) where T : class, INetSerializable
|
||||
{
|
||||
return RegisterCustomTypeInternal(constructor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register custom property type
|
||||
/// </summary>
|
||||
/// <param name="writeDelegate"></param>
|
||||
/// <param name="readDelegate"></param>
|
||||
/// <returns>True - if register successful, false - if type already registered</returns>
|
||||
public bool RegisterCustomType<T>(Action<NetDataWriter, T> writeDelegate, Func<NetDataReader, T> readDelegate)
|
||||
{
|
||||
var t = typeof(T);
|
||||
if (BasicTypes.Contains(t) || _registeredCustomTypes.ContainsKey(t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var rwDelegates = new CustomType(
|
||||
(writer, obj) => writeDelegate(writer, (T)obj),
|
||||
reader => readDelegate(reader));
|
||||
|
||||
_registeredCustomTypes.Add(t, rwDelegates);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Delegate CreateDelegate(Type type, MethodInfo info)
|
||||
{
|
||||
#if WINRT || NETCORE
|
||||
return info.CreateDelegate(type);
|
||||
#else
|
||||
return Delegate.CreateDelegate(type, info);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static Func<TClass, TProperty> ExtractGetDelegate<TClass, TProperty>(MethodInfo info)
|
||||
{
|
||||
return (Func<TClass, TProperty>)CreateDelegate(typeof(Func<TClass, TProperty>), info);
|
||||
}
|
||||
|
||||
private static Action<TClass, TProperty> ExtractSetDelegate<TClass, TProperty>(MethodInfo info)
|
||||
{
|
||||
return (Action<TClass, TProperty>)CreateDelegate(typeof(Action<TClass, TProperty>), info);
|
||||
}
|
||||
|
||||
private StructInfo RegisterInternal<T>() where T : class
|
||||
{
|
||||
Type t = typeof(T);
|
||||
ulong nameHash = _hasher.GetHash(t.Name);
|
||||
|
||||
StructInfo info;
|
||||
if (_cache.TryGetValue(nameHash, out info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
#if WINRT || NETCORE
|
||||
var props = t.GetRuntimeProperties().ToArray();
|
||||
int propsCount = props.Count();
|
||||
#else
|
||||
var props = t.GetProperties(
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.GetProperty |
|
||||
BindingFlags.SetProperty);
|
||||
int propsCount = props.Length;
|
||||
#endif
|
||||
if (props == null || propsCount < 0)
|
||||
{
|
||||
throw new ArgumentException("Type does not contain acceptable fields");
|
||||
}
|
||||
|
||||
info = new StructInfo(nameHash, propsCount);
|
||||
for (int i = 0; i < props.Length; i++)
|
||||
{
|
||||
var property = props[i];
|
||||
var propertyType = property.PropertyType;
|
||||
|
||||
//Set field type
|
||||
info.FieldTypes[i] = propertyType.IsArray ? propertyType.GetElementType() : propertyType;
|
||||
#if WINRT || NETCORE
|
||||
bool isEnum = propertyType.GetTypeInfo().IsEnum;
|
||||
var getMethod = property.GetMethod;
|
||||
var setMethod = property.SetMethod;
|
||||
#else
|
||||
bool isEnum = propertyType.IsEnum;
|
||||
var getMethod = property.GetGetMethod();
|
||||
var setMethod = property.GetSetMethod();
|
||||
#endif
|
||||
if (isEnum)
|
||||
{
|
||||
var underlyingType = Enum.GetUnderlyingType(propertyType);
|
||||
if (underlyingType == typeof(byte))
|
||||
{
|
||||
info.ReadDelegate[i] = reader =>
|
||||
{
|
||||
property.SetValue(info.Reference, Enum.ToObject(propertyType, reader.GetByte()), null);
|
||||
};
|
||||
info.WriteDelegate[i] = writer =>
|
||||
{
|
||||
writer.Put((byte)property.GetValue(info.Reference, null));
|
||||
};
|
||||
}
|
||||
else if (underlyingType == typeof(int))
|
||||
{
|
||||
info.ReadDelegate[i] = reader =>
|
||||
{
|
||||
property.SetValue(info.Reference, Enum.ToObject(propertyType, reader.GetInt()), null);
|
||||
};
|
||||
info.WriteDelegate[i] = writer =>
|
||||
{
|
||||
writer.Put((int)property.GetValue(info.Reference, null));
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Not supported enum underlying type: " + underlyingType.Name);
|
||||
}
|
||||
}
|
||||
else if (propertyType == typeof(string))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, string>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, string>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetString(MaxStringLenght));
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference), MaxStringLenght);
|
||||
}
|
||||
else if (propertyType == typeof(bool))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, bool>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, bool>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetBool());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(byte))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, byte>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, byte>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetByte());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(sbyte))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, sbyte>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, sbyte>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetSByte());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(short))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, short>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, short>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetShort());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(ushort))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, ushort>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, ushort>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUShort());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(int))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, int>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, int>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetInt());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(uint))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, uint>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, uint>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUInt());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(long))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, long>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, long>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetLong());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(ulong))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, ulong>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, ulong>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetULong());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(float))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, float>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, float>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetFloat());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(double))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, double>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, double>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetDouble());
|
||||
info.WriteDelegate[i] = writer => writer.Put(getDelegate((T)info.Reference));
|
||||
}
|
||||
// Array types
|
||||
else if (propertyType == typeof(string[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, string[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, string[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetStringArray(MaxStringLenght));
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference), MaxStringLenght);
|
||||
}
|
||||
else if (propertyType == typeof(byte[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, byte[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, byte[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetBytesWithLength());
|
||||
info.WriteDelegate[i] = writer => writer.PutBytesWithLength(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(short[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, short[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, short[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetShortArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(ushort[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, ushort[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, ushort[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUShortArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(int[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, int[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, int[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetIntArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(uint[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, uint[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, uint[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetUIntArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(long[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, long[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, long[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetLongArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(ulong[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, ulong[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, ulong[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetULongArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(float[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, float[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, float[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetFloatArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else if (propertyType == typeof(double[]))
|
||||
{
|
||||
var setDelegate = ExtractSetDelegate<T, double[]>(setMethod);
|
||||
var getDelegate = ExtractGetDelegate<T, double[]>(getMethod);
|
||||
info.ReadDelegate[i] = reader => setDelegate((T)info.Reference, reader.GetDoubleArray());
|
||||
info.WriteDelegate[i] = writer => writer.PutArray(getDelegate((T)info.Reference));
|
||||
}
|
||||
else
|
||||
{
|
||||
CustomType registeredCustomType;
|
||||
bool array = false;
|
||||
|
||||
if (propertyType.IsArray)
|
||||
{
|
||||
array = true;
|
||||
propertyType = propertyType.GetElementType();
|
||||
}
|
||||
|
||||
if (_registeredCustomTypes.TryGetValue(propertyType, out registeredCustomType))
|
||||
{
|
||||
if (array) //Array type serialize/deserialize
|
||||
{
|
||||
info.ReadDelegate[i] = reader =>
|
||||
{
|
||||
ushort arrLength = reader.GetUShort();
|
||||
Array arr = Array.CreateInstance(propertyType, arrLength);
|
||||
for (int k = 0; k < arrLength; k++)
|
||||
{
|
||||
arr.SetValue(registeredCustomType.ReadDelegate(reader), k);
|
||||
}
|
||||
|
||||
property.SetValue(info.Reference, arr, null);
|
||||
};
|
||||
|
||||
info.WriteDelegate[i] = writer =>
|
||||
{
|
||||
Array arr = (Array)property.GetValue(info.Reference, null);
|
||||
writer.Put((ushort)arr.Length);
|
||||
for (int k = 0; k < arr.Length; k++)
|
||||
{
|
||||
registeredCustomType.WriteDelegate(writer, arr.GetValue(k));
|
||||
}
|
||||
};
|
||||
}
|
||||
else //Simple
|
||||
{
|
||||
info.ReadDelegate[i] = reader =>
|
||||
{
|
||||
property.SetValue(info.Reference, registeredCustomType.ReadDelegate(reader), null);
|
||||
};
|
||||
|
||||
info.WriteDelegate[i] = writer =>
|
||||
{
|
||||
registeredCustomType.WriteDelegate(writer, property.GetValue(info.Reference, null));
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Unknown property type: " + propertyType.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
_cache.Add(nameHash, info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all available data from NetDataReader and calls OnReceive delegates
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packets data</param>
|
||||
public void ReadAllPackets(NetDataReader reader)
|
||||
{
|
||||
while (reader.AvailableBytes > 0)
|
||||
{
|
||||
ReadPacket(reader);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all available data from NetDataReader and calls OnReceive delegates
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packets data</param>
|
||||
/// <param name="userData">Argument that passed to OnReceivedEvent</param>
|
||||
public void ReadAllPackets<T>(NetDataReader reader, T userData)
|
||||
{
|
||||
while (reader.AvailableBytes > 0)
|
||||
{
|
||||
ReadPacket(reader, userData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads one packet from NetDataReader and calls OnReceive delegate
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packet</param>
|
||||
public void ReadPacket(NetDataReader reader)
|
||||
{
|
||||
ReadPacket(reader, null);
|
||||
}
|
||||
|
||||
private StructInfo ReadInfo(NetDataReader reader)
|
||||
{
|
||||
ulong hash = _hasher.ReadHash(reader);
|
||||
StructInfo info;
|
||||
if (!_cache.TryGetValue(hash, out info))
|
||||
{
|
||||
throw new Exception("Undefined packet received");
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads packet with known type
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packet</param>
|
||||
/// <returns>Returns packet if packet in reader is matched type</returns>
|
||||
public T ReadKnownPacket<T>(NetDataReader reader) where T : class, new()
|
||||
{
|
||||
var info = ReadInfo(reader);
|
||||
ulong typeHash = _hasher.GetHash(typeof(T).Name);
|
||||
if (typeHash != info.Hash)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
info.Reference = info.CreatorFunc != null ? info.CreatorFunc() : Activator.CreateInstance<T>();
|
||||
info.Read(reader);
|
||||
return (T)info.Reference;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads packet with known type (non alloc variant)
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packet</param>
|
||||
/// <param name="target">Deserialization target</param>
|
||||
/// <returns>Returns true if packet in reader is matched type</returns>
|
||||
public bool ReadKnownPacket<T>(NetDataReader reader, T target) where T : class, new()
|
||||
{
|
||||
var info = ReadInfo(reader);
|
||||
ulong typeHash = _hasher.GetHash(typeof(T).Name);
|
||||
if (typeHash != info.Hash)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
info.Reference = target;
|
||||
info.Read(reader);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads one packet from NetDataReader and calls OnReceive delegate
|
||||
/// </summary>
|
||||
/// <param name="reader">NetDataReader with packet</param>
|
||||
/// <param name="userData">Argument that passed to OnReceivedEvent</param>
|
||||
public void ReadPacket(NetDataReader reader, object userData)
|
||||
{
|
||||
var info = ReadInfo(reader);
|
||||
if (info.CreatorFunc != null)
|
||||
{
|
||||
info.Reference = info.CreatorFunc();
|
||||
}
|
||||
info.Read(reader);
|
||||
info.OnReceive(info.Reference, userData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register and subscribe to packet receive event
|
||||
/// </summary>
|
||||
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
|
||||
/// <param name="packetConstructor">Method that constructs packet intead of slow Activator.CreateInstance</param>
|
||||
public void Subscribe<T>(Action<T> onReceive, Func<T> packetConstructor) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
info.CreatorFunc = () => packetConstructor();
|
||||
info.OnReceive = (o, userData) => { onReceive((T)o); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register packet type for direct reading (ReadKnownPacket)
|
||||
/// </summary>
|
||||
/// <param name="packetConstructor">Method that constructs packet intead of slow Activator.CreateInstance</param>
|
||||
public void Register<T>(Func<T> packetConstructor = null) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
if (packetConstructor != null)
|
||||
{
|
||||
info.CreatorFunc = () => packetConstructor();
|
||||
}
|
||||
info.OnReceive = (o, userData) => { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register and subscribe to packet receive event (with userData)
|
||||
/// </summary>
|
||||
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
|
||||
/// <param name="packetConstructor">Method that constructs packet intead of slow Activator.CreateInstance</param>
|
||||
public void Subscribe<T, TUserData>(Action<T, TUserData> onReceive, Func<T> packetConstructor) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
info.CreatorFunc = () => packetConstructor();
|
||||
info.OnReceive = (o, userData) => { onReceive((T)o, (TUserData)userData); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register and subscribe to packet receive event
|
||||
/// This metod will overwrite last received packet class on receive (less garbage)
|
||||
/// </summary>
|
||||
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
|
||||
public void SubscribeReusable<T>(Action<T> onReceive) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
info.Reference = new T();
|
||||
info.OnReceive = (o, userData) => { onReceive((T)o); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register and subscribe to packet receive event
|
||||
/// This metod will overwrite last received packet class on receive (less garbage)
|
||||
/// </summary>
|
||||
/// <param name="onReceive">event that will be called when packet deserialized with ReadPacket method</param>
|
||||
public void SubscribeReusable<T, TUserData>(Action<T, TUserData> onReceive) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
info.Reference = new T();
|
||||
info.OnReceive = (o, userData) => { onReceive((T)o, (TUserData)userData); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize struct to NetDataWriter (fast)
|
||||
/// </summary>
|
||||
/// <param name="writer">Serialization target NetDataWriter</param>
|
||||
/// <param name="obj">Struct to serialize</param>
|
||||
public void Serialize<T>(NetDataWriter writer, T obj) where T : class, new()
|
||||
{
|
||||
var info = RegisterInternal<T>();
|
||||
_hasher.WriteHash(info.Hash, writer);
|
||||
info.Write(writer, obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize struct to byte array
|
||||
/// </summary>
|
||||
/// <param name="obj">Struct to serialize</param>
|
||||
/// <returns>byte array with serialized data</returns>
|
||||
public byte[] Serialize<T>(T obj) where T : class, new()
|
||||
{
|
||||
_writer.Reset();
|
||||
Serialize(_writer, obj);
|
||||
return _writer.CopyData();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e26db3a8188754e07b42db31f32e20f7
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,455 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
#if !WINRT || UNITY_EDITOR
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class NetSocket
|
||||
{
|
||||
private Socket _udpSocketv4;
|
||||
private Socket _udpSocketv6;
|
||||
private NetEndPoint _localEndPoint;
|
||||
private Thread _threadv4;
|
||||
private Thread _threadv6;
|
||||
private bool _running;
|
||||
private readonly NetManager.OnMessageReceived _onMessageReceived;
|
||||
|
||||
private static readonly IPAddress MulticastAddressV6 = IPAddress.Parse (NetConstants.MulticastGroupIPv6);
|
||||
private static readonly bool IPv6Support;
|
||||
private const int SocketReceivePollTime = 100000;
|
||||
private const int SocketSendPollTime = 5000;
|
||||
|
||||
public NetEndPoint LocalEndPoint
|
||||
{
|
||||
get { return _localEndPoint; }
|
||||
}
|
||||
|
||||
static NetSocket()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Unity3d .NET 2.0 throws exception.
|
||||
// IPv6Support = Socket.OSSupportsIPv6;
|
||||
IPv6Support = false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
IPv6Support = false;
|
||||
}
|
||||
}
|
||||
|
||||
public NetSocket(NetManager.OnMessageReceived onMessageReceived)
|
||||
{
|
||||
_onMessageReceived = onMessageReceived;
|
||||
}
|
||||
|
||||
private void ReceiveLogic(object state)
|
||||
{
|
||||
Socket socket = (Socket)state;
|
||||
EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0);
|
||||
NetEndPoint bufferNetEndPoint = new NetEndPoint((IPEndPoint)bufferEndPoint);
|
||||
byte[] receiveBuffer = new byte[NetConstants.PacketSizeLimit];
|
||||
|
||||
while (_running)
|
||||
{
|
||||
//wait for data
|
||||
if (!socket.Poll(SocketReceivePollTime, SelectMode.SelectRead))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int result;
|
||||
|
||||
//Reading data
|
||||
try
|
||||
{
|
||||
result = socket.ReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref bufferEndPoint);
|
||||
if (!bufferNetEndPoint.EndPoint.Equals(bufferEndPoint))
|
||||
{
|
||||
bufferNetEndPoint = new NetEndPoint((IPEndPoint)bufferEndPoint);
|
||||
}
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
if (ex.SocketErrorCode == SocketError.ConnectionReset ||
|
||||
ex.SocketErrorCode == SocketError.MessageSize)
|
||||
{
|
||||
//10040 - message too long
|
||||
//10054 - remote close (not error)
|
||||
//Just UDP
|
||||
NetUtils.DebugWrite(ConsoleColor.DarkRed, "[R] Ingored error: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString() );
|
||||
continue;
|
||||
}
|
||||
NetUtils.DebugWriteError("[R]Error code: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString());
|
||||
_onMessageReceived(null, 0, (int)ex.SocketErrorCode, bufferNetEndPoint);
|
||||
continue;
|
||||
}
|
||||
|
||||
//All ok!
|
||||
NetUtils.DebugWrite(ConsoleColor.Blue, "[R]Recieved data from {0}, result: {1}", bufferNetEndPoint.ToString(), result);
|
||||
_onMessageReceived(receiveBuffer, result, 0, bufferNetEndPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Bind(int port, bool reuseAddress)
|
||||
{
|
||||
_udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
_udpSocketv4.Blocking = false;
|
||||
_udpSocketv4.ReceiveBufferSize = NetConstants.SocketBufferSize;
|
||||
_udpSocketv4.SendBufferSize = NetConstants.SocketBufferSize;
|
||||
_udpSocketv4.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, NetConstants.SocketTTL);
|
||||
if(reuseAddress)
|
||||
_udpSocketv4.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
#if !NETCORE
|
||||
_udpSocketv4.DontFragment = true;
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
_udpSocketv4.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
NetUtils.DebugWriteError("Broadcast error: {0}", e.ToString());
|
||||
}
|
||||
|
||||
if (!BindSocket(_udpSocketv4, new IPEndPoint(IPAddress.Any, port)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_localEndPoint = new NetEndPoint((IPEndPoint)_udpSocketv4.LocalEndPoint);
|
||||
|
||||
_running = true;
|
||||
_threadv4 = new Thread(ReceiveLogic);
|
||||
_threadv4.Name = "SocketThreadv4(" + port + ")";
|
||||
_threadv4.IsBackground = true;
|
||||
_threadv4.Start(_udpSocketv4);
|
||||
|
||||
//Check IPv6 support
|
||||
if (!IPv6Support)
|
||||
return true;
|
||||
|
||||
//Use one port for two sockets
|
||||
port = _localEndPoint.Port;
|
||||
|
||||
_udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
|
||||
_udpSocketv6.Blocking = false;
|
||||
_udpSocketv6.ReceiveBufferSize = NetConstants.SocketBufferSize;
|
||||
_udpSocketv6.SendBufferSize = NetConstants.SocketBufferSize;
|
||||
if (reuseAddress)
|
||||
_udpSocketv6.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
|
||||
if (BindSocket(_udpSocketv6, new IPEndPoint(IPAddress.IPv6Any, port)))
|
||||
{
|
||||
_localEndPoint = new NetEndPoint((IPEndPoint)_udpSocketv6.LocalEndPoint);
|
||||
|
||||
try
|
||||
{
|
||||
_udpSocketv6.SetSocketOption(
|
||||
SocketOptionLevel.IPv6,
|
||||
SocketOptionName.AddMembership,
|
||||
new IPv6MulticastOption(MulticastAddressV6));
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
// Unity3d throws exception - ignored
|
||||
}
|
||||
|
||||
_threadv6 = new Thread(ReceiveLogic);
|
||||
_threadv6.Name = "SocketThreadv6(" + port + ")";
|
||||
_threadv6.IsBackground = true;
|
||||
_threadv6.Start(_udpSocketv6);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool BindSocket(Socket socket, IPEndPoint ep)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Bind(ep);
|
||||
NetUtils.DebugWrite(ConsoleColor.Blue, "[B]Succesfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
NetUtils.DebugWriteError("[B]Bind exception: {0}", ex.ToString());
|
||||
//ODO: very temporary hack for iOS (Unity3D)
|
||||
if (ex.SocketErrorCode == SocketError.AddressFamilyNotSupported)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SendBroadcast(byte[] data, int offset, int size, int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
int result = _udpSocketv4.SendTo(data, offset, size, SocketFlags.None, new IPEndPoint(IPAddress.Broadcast, port));
|
||||
if (result <= 0)
|
||||
return false;
|
||||
if (IPv6Support)
|
||||
{
|
||||
result = _udpSocketv6.SendTo(data, offset, size, SocketFlags.None, new IPEndPoint(MulticastAddressV6, port));
|
||||
if (result <= 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetUtils.DebugWriteError("[S][MCAST]" + ex);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int SendTo(byte[] data, int offset, int size, NetEndPoint remoteEndPoint, ref int errorCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
int result = 0;
|
||||
if (remoteEndPoint.EndPoint.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
if (!_udpSocketv4.Poll(SocketSendPollTime, SelectMode.SelectWrite))
|
||||
return -1;
|
||||
result = _udpSocketv4.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint.EndPoint);
|
||||
}
|
||||
else if(IPv6Support)
|
||||
{
|
||||
if (!_udpSocketv6.Poll(SocketSendPollTime, SelectMode.SelectWrite))
|
||||
return -1;
|
||||
result = _udpSocketv6.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint.EndPoint);
|
||||
}
|
||||
|
||||
NetUtils.DebugWrite(ConsoleColor.Blue, "[S]Send packet to {0}, result: {1}", remoteEndPoint.EndPoint, result);
|
||||
return result;
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
if (ex.SocketErrorCode != SocketError.MessageSize)
|
||||
{
|
||||
NetUtils.DebugWriteError("[S]" + ex);
|
||||
}
|
||||
|
||||
errorCode = (int)ex.SocketErrorCode;
|
||||
return -1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetUtils.DebugWriteError("[S]" + ex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseSocket(Socket s)
|
||||
{
|
||||
#if NETCORE
|
||||
s.Dispose();
|
||||
#else
|
||||
s.Close();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_running = false;
|
||||
|
||||
//Close IPv4
|
||||
if (Thread.CurrentThread != _threadv4)
|
||||
{
|
||||
_threadv4.Join();
|
||||
}
|
||||
_threadv4 = null;
|
||||
if (_udpSocketv4 != null)
|
||||
{
|
||||
CloseSocket(_udpSocketv4);
|
||||
_udpSocketv4 = null;
|
||||
}
|
||||
|
||||
//No ipv6
|
||||
if (_udpSocketv6 == null)
|
||||
return;
|
||||
|
||||
//Close IPv6
|
||||
if (Thread.CurrentThread != _threadv6)
|
||||
{
|
||||
_threadv6.Join();
|
||||
}
|
||||
_threadv6 = null;
|
||||
if (_udpSocketv6 != null)
|
||||
{
|
||||
CloseSocket(_udpSocketv6);
|
||||
_udpSocketv6 = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Networking;
|
||||
using Windows.Networking.Sockets;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class NetSocket
|
||||
{
|
||||
private DatagramSocket _datagramSocket;
|
||||
private readonly Dictionary<NetEndPoint, IOutputStream> _peers = new Dictionary<NetEndPoint, IOutputStream>();
|
||||
private readonly NetManager.OnMessageReceived _onMessageReceived;
|
||||
private readonly byte[] _byteBuffer = new byte[NetConstants.PacketSizeLimit];
|
||||
private readonly IBuffer _buffer;
|
||||
private NetEndPoint _bufferEndPoint;
|
||||
private NetEndPoint _localEndPoint;
|
||||
private static readonly HostName BroadcastAddress = new HostName("255.255.255.255");
|
||||
private static readonly HostName MulticastAddressV6 = new HostName(NetConstants.MulticastGroupIPv6);
|
||||
|
||||
public NetEndPoint LocalEndPoint
|
||||
{
|
||||
get { return _localEndPoint; }
|
||||
}
|
||||
|
||||
public NetSocket(NetManager.OnMessageReceived onMessageReceived)
|
||||
{
|
||||
_onMessageReceived = onMessageReceived;
|
||||
_buffer = _byteBuffer.AsBuffer();
|
||||
}
|
||||
|
||||
private void OnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
|
||||
{
|
||||
var result = args.GetDataStream().ReadAsync(_buffer, _buffer.Capacity, InputStreamOptions.None).AsTask().Result;
|
||||
int length = (int)result.Length;
|
||||
if (length <= 0)
|
||||
return;
|
||||
|
||||
if (_bufferEndPoint == null ||
|
||||
!_bufferEndPoint.HostName.IsEqual(args.RemoteAddress) ||
|
||||
!_bufferEndPoint.PortStr.Equals(args.RemotePort))
|
||||
{
|
||||
_bufferEndPoint = new NetEndPoint(args.RemoteAddress, args.RemotePort);
|
||||
}
|
||||
_onMessageReceived(_byteBuffer, length, 0, _bufferEndPoint);
|
||||
}
|
||||
|
||||
public bool Bind(int port, bool reuseAddress)
|
||||
{
|
||||
_datagramSocket = new DatagramSocket();
|
||||
_datagramSocket.Control.InboundBufferSizeInBytes = NetConstants.SocketBufferSize;
|
||||
_datagramSocket.Control.DontFragment = true;
|
||||
_datagramSocket.Control.OutboundUnicastHopLimit = NetConstants.SocketTTL;
|
||||
_datagramSocket.MessageReceived += OnMessageReceived;
|
||||
|
||||
try
|
||||
{
|
||||
_datagramSocket.BindServiceNameAsync(port.ToString()).AsTask().Wait();
|
||||
_datagramSocket.JoinMulticastGroup(MulticastAddressV6);
|
||||
_localEndPoint = new NetEndPoint(_datagramSocket.Information.LocalAddress, _datagramSocket.Information.LocalPort);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetUtils.DebugWriteError("[B]Bind exception: {0}", ex.ToString());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SendBroadcast(byte[] data, int offset, int size, int port)
|
||||
{
|
||||
var portString = port.ToString();
|
||||
try
|
||||
{
|
||||
var outputStream =
|
||||
_datagramSocket.GetOutputStreamAsync(BroadcastAddress, portString)
|
||||
.AsTask()
|
||||
.Result;
|
||||
var writer = outputStream.AsStreamForWrite();
|
||||
writer.Write(data, offset, size);
|
||||
writer.Flush();
|
||||
|
||||
outputStream =
|
||||
_datagramSocket.GetOutputStreamAsync(MulticastAddressV6, portString)
|
||||
.AsTask()
|
||||
.Result;
|
||||
writer = outputStream.AsStreamForWrite();
|
||||
writer.Write(data, offset, size);
|
||||
writer.Flush();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetUtils.DebugWriteError("[S][MCAST]" + ex);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int SendTo(byte[] data, int offset, int length, NetEndPoint remoteEndPoint, ref int errorCode)
|
||||
{
|
||||
Task<uint> task = null;
|
||||
try
|
||||
{
|
||||
IOutputStream writer;
|
||||
if (!_peers.TryGetValue(remoteEndPoint, out writer))
|
||||
{
|
||||
writer =
|
||||
_datagramSocket.GetOutputStreamAsync(remoteEndPoint.HostName, remoteEndPoint.PortStr)
|
||||
.AsTask()
|
||||
.Result;
|
||||
_peers.Add(remoteEndPoint, writer);
|
||||
}
|
||||
|
||||
task = writer.WriteAsync(data.AsBuffer(offset, length)).AsTask();
|
||||
return (int)task.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (task?.Exception?.InnerExceptions != null)
|
||||
{
|
||||
ex = task.Exception.InnerException;
|
||||
}
|
||||
var errorStatus = SocketError.GetStatus(ex.HResult);
|
||||
switch (errorStatus)
|
||||
{
|
||||
case SocketErrorStatus.MessageTooLong:
|
||||
errorCode = 10040;
|
||||
break;
|
||||
default:
|
||||
errorCode = (int)errorStatus;
|
||||
NetUtils.DebugWriteError("[S " + errorStatus + "(" + errorCode + ")]" + ex);
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemovePeer(NetEndPoint ep)
|
||||
{
|
||||
_peers.Remove(ep);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_datagramSocket.Dispose();
|
||||
_datagramSocket = null;
|
||||
ClearPeers();
|
||||
}
|
||||
|
||||
internal void ClearPeers()
|
||||
{
|
||||
_peers.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c517b909a8c704eae91d4eccf06bc8a1
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,97 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
#if WINRT && !UNITY_EDITOR
|
||||
#define USE_WINRT
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
#if USE_WINRT
|
||||
using Windows.Foundation;
|
||||
using Windows.System.Threading;
|
||||
using Windows.System.Threading.Core;
|
||||
#endif
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public sealed class NetThread
|
||||
{
|
||||
#if USE_WINRT
|
||||
private readonly ManualResetEvent _updateWaiter = new ManualResetEvent(false);
|
||||
private readonly ManualResetEvent _joinWaiter = new ManualResetEvent(false);
|
||||
#else
|
||||
private Thread _thread;
|
||||
#endif
|
||||
|
||||
private readonly Action _callback;
|
||||
|
||||
public int SleepTime;
|
||||
private bool _running;
|
||||
private readonly string _name;
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get { return _running; }
|
||||
}
|
||||
|
||||
public NetThread(string name, int sleepTime, Action callback)
|
||||
{
|
||||
_callback = callback;
|
||||
SleepTime = sleepTime;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_running)
|
||||
return;
|
||||
_running = true;
|
||||
#if USE_WINRT
|
||||
var thread = new PreallocatedWorkItem(ThreadLogic, WorkItemPriority.Normal, WorkItemOptions.TimeSliced);
|
||||
thread.RunAsync().AsTask();
|
||||
#else
|
||||
_thread = new Thread(ThreadLogic)
|
||||
{
|
||||
Name = _name,
|
||||
IsBackground = true
|
||||
};
|
||||
_thread.Start();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (!_running)
|
||||
return;
|
||||
_running = false;
|
||||
|
||||
#if USE_WINRT
|
||||
_joinWaiter.WaitOne();
|
||||
#else
|
||||
_thread.Join();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_WINRT
|
||||
private void ThreadLogic(IAsyncAction action)
|
||||
{
|
||||
while (_running)
|
||||
{
|
||||
_callback();
|
||||
_updateWaiter.WaitOne(SleepTime);
|
||||
}
|
||||
_joinWaiter.Set();
|
||||
}
|
||||
#else
|
||||
private void ThreadLogic()
|
||||
{
|
||||
while (_running)
|
||||
{
|
||||
_callback();
|
||||
Thread.Sleep(SleepTime);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1928476aac6242d29fab6849f102494
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,219 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
#define UNITY
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
#if WINRT && !UNITY_EDITOR
|
||||
using Windows.Networking;
|
||||
using Windows.Networking.Connectivity;
|
||||
#else
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.NetworkInformation;
|
||||
#endif
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
#if WINRT && !UNITY_EDITOR
|
||||
public enum ConsoleColor
|
||||
{
|
||||
Gray,
|
||||
Yellow,
|
||||
Cyan,
|
||||
DarkCyan,
|
||||
DarkGreen,
|
||||
Blue,
|
||||
DarkRed,
|
||||
Red,
|
||||
Green,
|
||||
DarkYellow
|
||||
}
|
||||
#endif
|
||||
|
||||
[Flags]
|
||||
public enum LocalAddrType
|
||||
{
|
||||
IPv4 = 1,
|
||||
IPv6 = 2,
|
||||
All = 3
|
||||
}
|
||||
|
||||
public static class NetUtils
|
||||
{
|
||||
internal static int RelativeSequenceNumber(int number, int expected)
|
||||
{
|
||||
return (number - expected + NetConstants.MaxSequence + NetConstants.HalfMaxSequence) % NetConstants.MaxSequence - NetConstants.HalfMaxSequence;
|
||||
}
|
||||
|
||||
internal static int GetDividedPacketsCount(int size, int mtu)
|
||||
{
|
||||
return (size / mtu) + (size % mtu == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
public static void PrintInterfaceInfos()
|
||||
{
|
||||
#if !WINRT || UNITY_EDITOR
|
||||
DebugWriteForce(ConsoleColor.Green, "IPv6Support: {0}", Socket.OSSupportsIPv6);
|
||||
try
|
||||
{
|
||||
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
if (ip.Address.AddressFamily == AddressFamily.InterNetwork ||
|
||||
ip.Address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
DebugWriteForce(
|
||||
ConsoleColor.Green,
|
||||
"Interface: {0}, Type: {1}, Ip: {2}, OpStatus: {3}",
|
||||
ni.Name,
|
||||
ni.NetworkInterfaceType.ToString(),
|
||||
ip.Address.ToString(),
|
||||
ni.OperationalStatus.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugWriteForce(ConsoleColor.Red, "Error while getting interface infos: {0}", e.ToString());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void GetLocalIpList(List<string> targetList, LocalAddrType addrType)
|
||||
{
|
||||
bool ipv4 = (addrType & LocalAddrType.IPv4) == LocalAddrType.IPv4;
|
||||
bool ipv6 = (addrType & LocalAddrType.IPv6) == LocalAddrType.IPv6;
|
||||
#if WINRT && !UNITY_EDITOR
|
||||
foreach (HostName localHostName in NetworkInformation.GetHostNames())
|
||||
{
|
||||
if (localHostName.IPInformation != null &&
|
||||
((ipv4 && localHostName.Type == HostNameType.Ipv4) ||
|
||||
(ipv6 && localHostName.Type == HostNameType.Ipv6)))
|
||||
{
|
||||
targetList.Add(localHostName.ToString());
|
||||
}
|
||||
}
|
||||
#else
|
||||
try
|
||||
{
|
||||
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
//Skip loopback
|
||||
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback)
|
||||
continue;
|
||||
|
||||
var ipProps = ni.GetIPProperties();
|
||||
|
||||
//Skip address without gateway
|
||||
if (ipProps.GatewayAddresses.Count == 0)
|
||||
continue;
|
||||
|
||||
foreach (UnicastIPAddressInformation ip in ipProps.UnicastAddresses)
|
||||
{
|
||||
var address = ip.Address;
|
||||
if ((ipv4 && address.AddressFamily == AddressFamily.InterNetwork) ||
|
||||
(ipv6 && address.AddressFamily == AddressFamily.InterNetworkV6))
|
||||
targetList.Add(address.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
//ignored
|
||||
}
|
||||
|
||||
//Fallback mode (unity android)
|
||||
if (targetList.Count == 0)
|
||||
{
|
||||
#if NETCORE
|
||||
var hostTask = Dns.GetHostEntryAsync(Dns.GetHostName());
|
||||
hostTask.Wait();
|
||||
var host = hostTask.Result;
|
||||
#else
|
||||
var host = Dns.GetHostEntry(Dns.GetHostName());
|
||||
#endif
|
||||
foreach (IPAddress ip in host.AddressList)
|
||||
{
|
||||
if((ipv4 && ip.AddressFamily == AddressFamily.InterNetwork) ||
|
||||
(ipv6 && ip.AddressFamily == AddressFamily.InterNetworkV6))
|
||||
targetList.Add(ip.ToString());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (targetList.Count == 0)
|
||||
{
|
||||
if(ipv4)
|
||||
targetList.Add("127.0.0.1");
|
||||
if(ipv6)
|
||||
targetList.Add("::1");
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<string> IpList = new List<string>();
|
||||
public static string GetLocalIp(LocalAddrType addrType)
|
||||
{
|
||||
lock (IpList)
|
||||
{
|
||||
IpList.Clear();
|
||||
GetLocalIpList(IpList, addrType);
|
||||
return IpList.Count == 0 ? string.Empty : IpList[0];
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly object DebugLogLock = new object();
|
||||
|
||||
private static void DebugWriteLogic(ConsoleColor color, string str, params object[] args)
|
||||
{
|
||||
lock (DebugLogLock)
|
||||
{
|
||||
|
||||
if (NetDebug.Logger == null)
|
||||
{
|
||||
#if UNITY
|
||||
#if !UNITY_4_0
|
||||
UnityEngine.Debug.LogFormat(str, args);
|
||||
#endif
|
||||
#elif WINRT
|
||||
Debug.WriteLine(str, args);
|
||||
#else
|
||||
Console.ForegroundColor = color;
|
||||
Console.WriteLine(str, args);
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
NetDebug.Logger.WriteNet(color, str, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES")]
|
||||
internal static void DebugWrite(string str, params object[] args)
|
||||
{
|
||||
DebugWriteLogic(ConsoleColor.DarkGreen, str, args);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES")]
|
||||
internal static void DebugWrite(ConsoleColor color, string str, params object[] args)
|
||||
{
|
||||
DebugWriteLogic(color, str, args);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")]
|
||||
internal static void DebugWriteForce(ConsoleColor color, string str, params object[] args)
|
||||
{
|
||||
DebugWriteLogic(color, str, args);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")]
|
||||
internal static void DebugWriteError(string str, params object[] args)
|
||||
{
|
||||
DebugWriteLogic(ConsoleColor.Red, str, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b27974b2b4714973a8f5a3ea4036677
|
||||
timeCreated: 1497976517
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
public class NtpSyncModule
|
||||
{
|
||||
public DateTime? SyncedTime { get; private set; }
|
||||
private readonly NetSocket _socket;
|
||||
private readonly NetEndPoint _ntpEndPoint;
|
||||
private readonly ManualResetEvent _waiter = new ManualResetEvent(false);
|
||||
|
||||
public NtpSyncModule(string ntpServer)
|
||||
{
|
||||
_ntpEndPoint = new NetEndPoint(ntpServer, 123);
|
||||
_socket = new NetSocket(OnMessageReceived);
|
||||
_socket.Bind(0, false);
|
||||
SyncedTime = null;
|
||||
}
|
||||
|
||||
private void OnMessageReceived(byte[] data, int length, int errorCode, NetEndPoint remoteEndPoint)
|
||||
{
|
||||
if (errorCode != 0)
|
||||
{
|
||||
_waiter.Set();
|
||||
return;
|
||||
}
|
||||
|
||||
ulong intPart = (ulong)data[40] << 24 | (ulong)data[41] << 16 | (ulong)data[42] << 8 | (ulong)data[43];
|
||||
ulong fractPart = (ulong)data[44] << 24 | (ulong)data[45] << 16 | (ulong)data[46] << 8 | (ulong)data[47];
|
||||
|
||||
var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
|
||||
SyncedTime = (new DateTime(1900, 1, 1)).AddMilliseconds((long)milliseconds);
|
||||
|
||||
_waiter.Set();
|
||||
}
|
||||
|
||||
public void GetNetworkTime()
|
||||
{
|
||||
if (SyncedTime != null)
|
||||
return;
|
||||
|
||||
var ntpData = new byte[48];
|
||||
//LeapIndicator = 0 (no warning)
|
||||
//VersionNum = 3
|
||||
//Mode = 3 (Client Mode)
|
||||
ntpData[0] = 0x1B;
|
||||
|
||||
//send
|
||||
int errorCode = 0;
|
||||
_socket.SendTo(ntpData, 0, ntpData.Length, _ntpEndPoint, ref errorCode);
|
||||
|
||||
if(errorCode == 0)
|
||||
_waiter.WaitOne(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91e481bf657774228854ba5923470b26
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,375 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class ReliableChannel
|
||||
{
|
||||
private class PendingPacket
|
||||
{
|
||||
public NetPacket Packet;
|
||||
public DateTime? TimeStamp;
|
||||
|
||||
public NetPacket GetAndClear()
|
||||
{
|
||||
var packet = Packet;
|
||||
Packet = null;
|
||||
TimeStamp = null;
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Queue<NetPacket> _outgoingPackets;
|
||||
private readonly bool[] _outgoingAcks; //for send acks
|
||||
private readonly PendingPacket[] _pendingPackets; //for unacked packets and duplicates
|
||||
private readonly NetPacket[] _receivedPackets; //for order
|
||||
private readonly bool[] _earlyReceived; //for unordered
|
||||
|
||||
private int _localSeqence;
|
||||
private int _remoteSequence;
|
||||
private int _localWindowStart;
|
||||
private int _remoteWindowStart;
|
||||
|
||||
private readonly NetPeer _peer;
|
||||
private bool _mustSendAcks;
|
||||
|
||||
private readonly bool _ordered;
|
||||
private readonly int _windowSize;
|
||||
private const int BitsInByte = 8;
|
||||
|
||||
private int _queueIndex;
|
||||
|
||||
public int PacketsInQueue
|
||||
{
|
||||
get { return _outgoingPackets.Count; }
|
||||
}
|
||||
|
||||
public ReliableChannel(NetPeer peer, bool ordered, int windowSize)
|
||||
{
|
||||
_windowSize = windowSize;
|
||||
_peer = peer;
|
||||
_ordered = ordered;
|
||||
|
||||
_outgoingPackets = new Queue<NetPacket>(_windowSize);
|
||||
|
||||
_outgoingAcks = new bool[_windowSize];
|
||||
_pendingPackets = new PendingPacket[_windowSize];
|
||||
for (int i = 0; i < _pendingPackets.Length; i++)
|
||||
{
|
||||
_pendingPackets[i] = new PendingPacket();
|
||||
}
|
||||
|
||||
if (_ordered)
|
||||
_receivedPackets = new NetPacket[_windowSize];
|
||||
else
|
||||
_earlyReceived = new bool[_windowSize];
|
||||
|
||||
_localWindowStart = 0;
|
||||
_localSeqence = 0;
|
||||
_remoteSequence = 0;
|
||||
_remoteWindowStart = 0;
|
||||
}
|
||||
|
||||
//ProcessAck in packet
|
||||
public void ProcessAck(NetPacket packet)
|
||||
{
|
||||
int validPacketSize = (_windowSize - 1) / BitsInByte + 1 + NetConstants.SequencedHeaderSize;
|
||||
if (packet.Size != validPacketSize)
|
||||
{
|
||||
NetUtils.DebugWrite("[PA]Invalid acks packet size");
|
||||
return;
|
||||
}
|
||||
|
||||
ushort ackWindowStart = packet.Sequence;
|
||||
if (ackWindowStart > NetConstants.MaxSequence)
|
||||
{
|
||||
NetUtils.DebugWrite("[PA]Bad window start");
|
||||
return;
|
||||
}
|
||||
|
||||
//check relevance
|
||||
if (NetUtils.RelativeSequenceNumber(ackWindowStart, _localWindowStart) <= -_windowSize)
|
||||
{
|
||||
NetUtils.DebugWrite("[PA]Old acks");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] acksData = packet.RawData;
|
||||
NetUtils.DebugWrite("[PA]AcksStart: {0}", ackWindowStart);
|
||||
int startByte = NetConstants.SequencedHeaderSize;
|
||||
|
||||
Monitor.Enter(_pendingPackets);
|
||||
for (int i = 0; i < _windowSize; i++)
|
||||
{
|
||||
int ackSequence = (ackWindowStart + i) % NetConstants.MaxSequence;
|
||||
if (NetUtils.RelativeSequenceNumber(ackSequence, _localWindowStart) < 0)
|
||||
{
|
||||
//NetUtils.DebugWrite(ConsoleColor.Cyan, "[PA] SKIP OLD: " + ackSequence);
|
||||
//Skip old ack
|
||||
continue;
|
||||
}
|
||||
|
||||
int currentByte = startByte + i / BitsInByte;
|
||||
int currentBit = i % BitsInByte;
|
||||
|
||||
if ((acksData[currentByte] & (1 << currentBit)) == 0)
|
||||
{
|
||||
//NetUtils.DebugWrite(ConsoleColor.Cyan, "[PA] SKIP FALSE: " + ackSequence);
|
||||
//Skip false ack
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ackSequence == _localWindowStart)
|
||||
{
|
||||
//Move window
|
||||
_localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence;
|
||||
}
|
||||
|
||||
NetPacket removed = _pendingPackets[ackSequence % _windowSize].GetAndClear();
|
||||
if (removed != null)
|
||||
{
|
||||
_peer.Recycle(removed);
|
||||
|
||||
NetUtils.DebugWrite("[PA]Removing reliableInOrder ack: {0} - true", ackSequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
NetUtils.DebugWrite("[PA]Removing reliableInOrder ack: {0} - false", ackSequence);
|
||||
}
|
||||
}
|
||||
Monitor.Exit(_pendingPackets);
|
||||
}
|
||||
|
||||
public void AddToQueue(NetPacket packet)
|
||||
{
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
_outgoingPackets.Enqueue(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessQueuedPackets()
|
||||
{
|
||||
//get packets from queue
|
||||
while (_outgoingPackets.Count > 0)
|
||||
{
|
||||
int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart);
|
||||
if (relate < _windowSize)
|
||||
{
|
||||
NetPacket packet;
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
packet = _outgoingPackets.Dequeue();
|
||||
}
|
||||
packet.Sequence = (ushort)_localSeqence;
|
||||
_pendingPackets[_localSeqence % _windowSize].Packet = packet;
|
||||
_localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence;
|
||||
}
|
||||
else //Queue filled
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool SendNextPacket()
|
||||
{
|
||||
//check sending acks
|
||||
DateTime currentTime = DateTime.UtcNow;
|
||||
|
||||
Monitor.Enter(_pendingPackets);
|
||||
ProcessQueuedPackets();
|
||||
|
||||
//send
|
||||
PendingPacket currentPacket;
|
||||
bool packetFound = false;
|
||||
int startQueueIndex = _queueIndex;
|
||||
do
|
||||
{
|
||||
currentPacket = _pendingPackets[_queueIndex];
|
||||
if (currentPacket.Packet != null)
|
||||
{
|
||||
//check send time
|
||||
if(currentPacket.TimeStamp.HasValue)
|
||||
{
|
||||
double packetHoldTime = (currentTime - currentPacket.TimeStamp.Value).TotalMilliseconds;
|
||||
if (packetHoldTime > _peer.ResendDelay)
|
||||
{
|
||||
NetUtils.DebugWrite("[RC]Resend: {0} > {1}", (int)packetHoldTime, _peer.ResendDelay);
|
||||
packetFound = true;
|
||||
}
|
||||
}
|
||||
else //Never sended
|
||||
{
|
||||
packetFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
_queueIndex = (_queueIndex + 1) % _windowSize;
|
||||
} while (!packetFound && _queueIndex != startQueueIndex);
|
||||
|
||||
if (packetFound)
|
||||
{
|
||||
currentPacket.TimeStamp = DateTime.UtcNow;
|
||||
_peer.SendRawData(currentPacket.Packet);
|
||||
NetUtils.DebugWrite("[RR]Sended");
|
||||
}
|
||||
Monitor.Exit(_pendingPackets);
|
||||
return packetFound;
|
||||
}
|
||||
|
||||
public void SendAcks()
|
||||
{
|
||||
if (!_mustSendAcks)
|
||||
return;
|
||||
_mustSendAcks = false;
|
||||
|
||||
NetUtils.DebugWrite("[RR]SendAcks");
|
||||
|
||||
//Init packet
|
||||
int bytesCount = (_windowSize - 1) / BitsInByte + 1;
|
||||
PacketProperty property = _ordered ? PacketProperty.AckReliableOrdered : PacketProperty.AckReliable;
|
||||
var acksPacket = _peer.GetPacketFromPool(property, bytesCount);
|
||||
|
||||
//For quick access
|
||||
byte[] data = acksPacket.RawData; //window start + acks size
|
||||
|
||||
//Put window start
|
||||
Monitor.Enter(_outgoingAcks);
|
||||
acksPacket.Sequence = (ushort)_remoteWindowStart;
|
||||
|
||||
//Put acks
|
||||
int startAckIndex = _remoteWindowStart % _windowSize;
|
||||
int currentAckIndex = startAckIndex;
|
||||
int currentBit = 0;
|
||||
int currentByte = NetConstants.SequencedHeaderSize;
|
||||
do
|
||||
{
|
||||
if (_outgoingAcks[currentAckIndex])
|
||||
{
|
||||
data[currentByte] |= (byte)(1 << currentBit);
|
||||
}
|
||||
|
||||
currentBit++;
|
||||
if (currentBit == BitsInByte)
|
||||
{
|
||||
currentByte++;
|
||||
currentBit = 0;
|
||||
}
|
||||
currentAckIndex = (currentAckIndex + 1) % _windowSize;
|
||||
} while (currentAckIndex != startAckIndex);
|
||||
Monitor.Exit(_outgoingAcks);
|
||||
|
||||
_peer.SendRawData(acksPacket);
|
||||
_peer.Recycle(acksPacket);
|
||||
}
|
||||
|
||||
//Process incoming packet
|
||||
public void ProcessPacket(NetPacket packet)
|
||||
{
|
||||
if (packet.Sequence >= NetConstants.MaxSequence)
|
||||
{
|
||||
NetUtils.DebugWrite("[RR]Bad sequence");
|
||||
return;
|
||||
}
|
||||
|
||||
int relate = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteWindowStart);
|
||||
int relateSeq = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence);
|
||||
|
||||
if (relateSeq > _windowSize)
|
||||
{
|
||||
NetUtils.DebugWrite("[RR]Bad sequence");
|
||||
return;
|
||||
}
|
||||
|
||||
//Drop bad packets
|
||||
if(relate < 0)
|
||||
{
|
||||
//Too old packet doesn't ack
|
||||
NetUtils.DebugWrite("[RR]ReliableInOrder too old");
|
||||
return;
|
||||
}
|
||||
if (relate >= _windowSize * 2)
|
||||
{
|
||||
//Some very new packet
|
||||
NetUtils.DebugWrite("[RR]ReliableInOrder too new");
|
||||
return;
|
||||
}
|
||||
|
||||
//If very new - move window
|
||||
Monitor.Enter(_outgoingAcks);
|
||||
if (relate >= _windowSize)
|
||||
{
|
||||
//New window position
|
||||
int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence;
|
||||
|
||||
//Clean old data
|
||||
while (_remoteWindowStart != newWindowStart)
|
||||
{
|
||||
_outgoingAcks[_remoteWindowStart % _windowSize] = false;
|
||||
_remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence;
|
||||
}
|
||||
}
|
||||
|
||||
//Final stage - process valid packet
|
||||
//trigger acks send
|
||||
_mustSendAcks = true;
|
||||
|
||||
if (_outgoingAcks[packet.Sequence % _windowSize])
|
||||
{
|
||||
NetUtils.DebugWrite("[RR]ReliableInOrder duplicate");
|
||||
Monitor.Exit(_outgoingAcks);
|
||||
return;
|
||||
}
|
||||
|
||||
//save ack
|
||||
_outgoingAcks[packet.Sequence % _windowSize] = true;
|
||||
Monitor.Exit(_outgoingAcks);
|
||||
|
||||
//detailed check
|
||||
if (packet.Sequence == _remoteSequence)
|
||||
{
|
||||
NetUtils.DebugWrite("[RR]ReliableInOrder packet succes");
|
||||
_peer.AddIncomingPacket(packet);
|
||||
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
|
||||
|
||||
if (_ordered)
|
||||
{
|
||||
NetPacket p;
|
||||
while ( (p = _receivedPackets[_remoteSequence % _windowSize]) != null)
|
||||
{
|
||||
//process holded packet
|
||||
_receivedPackets[_remoteSequence % _windowSize] = null;
|
||||
_peer.AddIncomingPacket(p);
|
||||
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (_earlyReceived[_remoteSequence % _windowSize])
|
||||
{
|
||||
//process early packet
|
||||
_earlyReceived[_remoteSequence % _windowSize] = false;
|
||||
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//holded packet
|
||||
if (_ordered)
|
||||
{
|
||||
_receivedPackets[packet.Sequence % _windowSize] = packet;
|
||||
}
|
||||
else
|
||||
{
|
||||
_earlyReceived[packet.Sequence % _windowSize] = true;
|
||||
_peer.AddIncomingPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 516bd901eb62f4bb391ef541f9effdec
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,4 @@
|
||||
for file in *.cs; do
|
||||
echo "#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA" | cat - $file > tempfile && mv tempfile $file
|
||||
echo '#endif' >> "$file"
|
||||
done
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9f1f24482ff84a25a1188dd49ec4d86
|
||||
timeCreated: 1498001160
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class SequencedChannel
|
||||
{
|
||||
private ushort _localSequence;
|
||||
private ushort _remoteSequence;
|
||||
private readonly Queue<NetPacket> _outgoingPackets;
|
||||
private readonly NetPeer _peer;
|
||||
|
||||
public SequencedChannel(NetPeer peer)
|
||||
{
|
||||
_outgoingPackets = new Queue<NetPacket>();
|
||||
_peer = peer;
|
||||
}
|
||||
|
||||
public void AddToQueue(NetPacket packet)
|
||||
{
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
_outgoingPackets.Enqueue(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SendNextPacket()
|
||||
{
|
||||
NetPacket packet;
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
if (_outgoingPackets.Count == 0)
|
||||
return false;
|
||||
packet = _outgoingPackets.Dequeue();
|
||||
}
|
||||
_localSequence++;
|
||||
packet.Sequence = _localSequence;
|
||||
_peer.SendRawData(packet);
|
||||
_peer.Recycle(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ProcessPacket(NetPacket packet)
|
||||
{
|
||||
if (NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence) > 0)
|
||||
{
|
||||
_remoteSequence = packet.Sequence;
|
||||
_peer.AddIncomingPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84bbbc6c6e45c4287917fc944650e4af
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlyingWormConsole3.LiteNetLib
|
||||
{
|
||||
internal sealed class SimpleChannel
|
||||
{
|
||||
private readonly Queue<NetPacket> _outgoingPackets;
|
||||
private readonly NetPeer _peer;
|
||||
|
||||
public SimpleChannel(NetPeer peer)
|
||||
{
|
||||
_outgoingPackets = new Queue<NetPacket>();
|
||||
_peer = peer;
|
||||
}
|
||||
|
||||
public void AddToQueue(NetPacket packet)
|
||||
{
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
_outgoingPackets.Enqueue(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SendNextPacket()
|
||||
{
|
||||
NetPacket packet;
|
||||
lock (_outgoingPackets)
|
||||
{
|
||||
if (_outgoingPackets.Count == 0)
|
||||
return false;
|
||||
packet = _outgoingPackets.Dequeue();
|
||||
}
|
||||
_peer.SendRawData(packet);
|
||||
_peer.Recycle(packet);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b4cdb673a0094d438a802844a8cc794
|
||||
timeCreated: 1497976518
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90979fd73276eab448e2785362667c6a
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user