|
@@ -0,0 +1,1039 @@
|
|
|
|
|
+#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA_8_1
|
|
|
|
|
+#if DEBUG
|
|
|
|
|
+#define STATS_ENABLED
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+using System;
|
|
|
|
|
+using System.Collections.Generic;
|
|
|
|
|
+using System.Text;
|
|
|
|
|
+using System.Threading;
|
|
|
|
|
+using FlyingWormConsole3.LiteNetLib.Utils;
|
|
|
|
|
+
|
|
|
|
|
+namespace FlyingWormConsole3.LiteNetLib
|
|
|
|
|
+{
|
|
|
|
|
+ public sealed class NetManager
|
|
|
|
|
+ {
|
|
|
|
|
+ internal delegate void OnMessageReceived(byte[] data, int length, int errorCode, NetEndPoint remoteEndPoint);
|
|
|
|
|
+
|
|
|
|
|
+ private struct FlowMode
|
|
|
|
|
+ {
|
|
|
|
|
+ public int PacketsPerSecond;
|
|
|
|
|
+ public int StartRtt;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private enum NetEventType
|
|
|
|
|
+ {
|
|
|
|
|
+ Connect,
|
|
|
|
|
+ Disconnect,
|
|
|
|
|
+ Receive,
|
|
|
|
|
+ ReceiveUnconnected,
|
|
|
|
|
+ Error,
|
|
|
|
|
+ ConnectionLatencyUpdated,
|
|
|
|
|
+ DiscoveryRequest,
|
|
|
|
|
+ DiscoveryResponse
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private sealed class NetEvent
|
|
|
|
|
+ {
|
|
|
|
|
+ public NetPeer Peer;
|
|
|
|
|
+ public readonly NetDataReader DataReader = new NetDataReader();
|
|
|
|
|
+ public NetEventType Type;
|
|
|
|
|
+ public NetEndPoint RemoteEndPoint;
|
|
|
|
|
+ public int AdditionalData;
|
|
|
|
|
+ public DisconnectReason DisconnectReason;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+#if DEBUG
|
|
|
|
|
+ private struct IncomingData
|
|
|
|
|
+ {
|
|
|
|
|
+ public byte[] Data;
|
|
|
|
|
+ public NetEndPoint EndPoint;
|
|
|
|
|
+ public DateTime TimeWhenGet;
|
|
|
|
|
+ }
|
|
|
|
|
+ private readonly List<IncomingData> _pingSimulationList = new List<IncomingData>();
|
|
|
|
|
+ private readonly Random _randomGenerator = new Random();
|
|
|
|
|
+ private const int MinLatencyTreshold = 5;
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ private readonly NetSocket _socket;
|
|
|
|
|
+ private readonly List<FlowMode> _flowModes;
|
|
|
|
|
+
|
|
|
|
|
+ private readonly NetThread _logicThread;
|
|
|
|
|
+
|
|
|
|
|
+ private readonly Queue<NetEvent> _netEventsQueue;
|
|
|
|
|
+ private readonly Stack<NetEvent> _netEventsPool;
|
|
|
|
|
+ private readonly INetEventListener _netEventListener;
|
|
|
|
|
+
|
|
|
|
|
+ private readonly NetPeerCollection _peers;
|
|
|
|
|
+ private readonly int _maxConnections;
|
|
|
|
|
+ private readonly string _connectKey;
|
|
|
|
|
+
|
|
|
|
|
+ private readonly NetPacketPool _netPacketPool;
|
|
|
|
|
+
|
|
|
|
|
+ //config section
|
|
|
|
|
+ public bool UnconnectedMessagesEnabled = false;
|
|
|
|
|
+ public bool NatPunchEnabled = false;
|
|
|
|
|
+ public int UpdateTime { get { return _logicThread.SleepTime; } set { _logicThread.SleepTime = value; } }
|
|
|
|
|
+ public int PingInterval = NetConstants.DefaultPingInterval;
|
|
|
|
|
+ public long DisconnectTimeout = 5000;
|
|
|
|
|
+ public bool SimulatePacketLoss = false;
|
|
|
|
|
+ public bool SimulateLatency = false;
|
|
|
|
|
+ public int SimulationPacketLossChance = 10;
|
|
|
|
|
+ public int SimulationMinLatency = 30;
|
|
|
|
|
+ public int SimulationMaxLatency = 100;
|
|
|
|
|
+ public bool UnsyncedEvents = false;
|
|
|
|
|
+ public bool DiscoveryEnabled = false;
|
|
|
|
|
+ public bool MergeEnabled = false;
|
|
|
|
|
+ public int ReconnectDelay = 500;
|
|
|
|
|
+ public int MaxConnectAttempts = 10;
|
|
|
|
|
+ public bool ReuseAddress = false;
|
|
|
|
|
+
|
|
|
|
|
+ private const int DefaultUpdateTime = 15;
|
|
|
|
|
+
|
|
|
|
|
+ //stats
|
|
|
|
|
+ public ulong PacketsSent { get; private set; }
|
|
|
|
|
+ public ulong PacketsReceived { get; private set; }
|
|
|
|
|
+ public ulong BytesSent { get; private set; }
|
|
|
|
|
+ public ulong BytesReceived { get; private set; }
|
|
|
|
|
+
|
|
|
|
|
+ //modules
|
|
|
|
|
+ public readonly NatPunchModule NatPunchModule;
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Returns true if socket listening and update thread is running
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public bool IsRunning
|
|
|
|
|
+ {
|
|
|
|
|
+ get { return _logicThread.IsRunning; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Local EndPoint (host and port)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public NetEndPoint LocalEndPoint
|
|
|
|
|
+ {
|
|
|
|
|
+ get { return _socket.LocalEndPoint; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Connected peers count
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public int PeersCount
|
|
|
|
|
+ {
|
|
|
|
|
+ get { return _peers.Count; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public string ConnectKey
|
|
|
|
|
+ {
|
|
|
|
|
+ get { return _connectKey; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //Flow
|
|
|
|
|
+ public void AddFlowMode(int startRtt, int packetsPerSecond)
|
|
|
|
|
+ {
|
|
|
|
|
+ var fm = new FlowMode {PacketsPerSecond = packetsPerSecond, StartRtt = startRtt};
|
|
|
|
|
+
|
|
|
|
|
+ if (_flowModes.Count > 0 && startRtt < _flowModes[0].StartRtt)
|
|
|
|
|
+ {
|
|
|
|
|
+ _flowModes.Insert(0, fm);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ _flowModes.Add(fm);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ internal int GetPacketsPerSecond(int flowMode)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (flowMode < 0 || _flowModes.Count == 0)
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ return _flowModes[flowMode].PacketsPerSecond;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ internal int GetMaxFlowMode()
|
|
|
|
|
+ {
|
|
|
|
|
+ return _flowModes.Count - 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ internal int GetStartRtt(int flowMode)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (flowMode < 0 || _flowModes.Count == 0)
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ return _flowModes[flowMode].StartRtt;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ internal NetPacketPool PacketPool
|
|
|
|
|
+ {
|
|
|
|
|
+ get { return _netPacketPool; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// NetManager constructor with maxConnections = 1 (usable for client)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="listener">Network events listener</param>
|
|
|
|
|
+ /// <param name="connectKey">Application key (must be same with remote host for establish connection)</param>
|
|
|
|
|
+ public NetManager(INetEventListener listener, string connectKey) : this(listener, 1, connectKey)
|
|
|
|
|
+ {
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// NetManager constructor
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="listener">Network events listener</param>
|
|
|
|
|
+ /// <param name="maxConnections">Maximum connections (incoming and outcoming)</param>
|
|
|
|
|
+ /// <param name="connectKey">Application key (must be same with remote host for establish connection)</param>
|
|
|
|
|
+ public NetManager(INetEventListener listener, int maxConnections, string connectKey)
|
|
|
|
|
+ {
|
|
|
|
|
+ _logicThread = new NetThread("LogicThread", DefaultUpdateTime, UpdateLogic);
|
|
|
|
|
+ _socket = new NetSocket(ReceiveLogic);
|
|
|
|
|
+ _netEventListener = listener;
|
|
|
|
|
+ _flowModes = new List<FlowMode>();
|
|
|
|
|
+ _netEventsQueue = new Queue<NetEvent>();
|
|
|
|
|
+ _netEventsPool = new Stack<NetEvent>();
|
|
|
|
|
+ _netPacketPool = new NetPacketPool();
|
|
|
|
|
+ NatPunchModule = new NatPunchModule(this);
|
|
|
|
|
+
|
|
|
|
|
+ _connectKey = connectKey;
|
|
|
|
|
+ _peers = new NetPeerCollection(maxConnections);
|
|
|
|
|
+ _maxConnections = maxConnections;
|
|
|
|
|
+ _connectKey = connectKey;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ internal void ConnectionLatencyUpdated(NetPeer fromPeer, int latency)
|
|
|
|
|
+ {
|
|
|
|
|
+ var evt = CreateEvent(NetEventType.ConnectionLatencyUpdated);
|
|
|
|
|
+ evt.Peer = fromPeer;
|
|
|
|
|
+ evt.AdditionalData = latency;
|
|
|
|
|
+ EnqueueEvent(evt);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ internal bool SendRawAndRecycle(NetPacket packet, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ var result = SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint);
|
|
|
|
|
+ _netPacketPool.Recycle(packet);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ internal bool SendRaw(byte[] message, int start, int length, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!IsRunning)
|
|
|
|
|
+ return false;
|
|
|
|
|
+
|
|
|
|
|
+ int errorCode = 0;
|
|
|
|
|
+ bool result = _socket.SendTo(message, start, length, remoteEndPoint, ref errorCode) > 0;
|
|
|
|
|
+
|
|
|
|
|
+ //10040 message to long... need to check
|
|
|
|
|
+ //10065 no route to host
|
|
|
|
|
+ if (errorCode != 0 && errorCode != 10040 && errorCode != 10065)
|
|
|
|
|
+ {
|
|
|
|
|
+ //Send error
|
|
|
|
|
+ NetPeer fromPeer;
|
|
|
|
|
+ if (_peers.TryGetValue(remoteEndPoint, out fromPeer))
|
|
|
|
|
+ {
|
|
|
|
|
+ DisconnectPeer(fromPeer, DisconnectReason.SocketSendError, errorCode, false, null, 0, 0);
|
|
|
|
|
+ }
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.Error);
|
|
|
|
|
+ netEvent.RemoteEndPoint = remoteEndPoint;
|
|
|
|
|
+ netEvent.AdditionalData = errorCode;
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (errorCode == 10040)
|
|
|
|
|
+ {
|
|
|
|
|
+ NetUtils.DebugWrite(ConsoleColor.Red, "[SRD] 10040, datalen: {0}", length);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+#if STATS_ENABLED
|
|
|
|
|
+ PacketsSent++;
|
|
|
|
|
+ BytesSent += (uint)length;
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void DisconnectPeer(
|
|
|
|
|
+ NetPeer peer,
|
|
|
|
|
+ DisconnectReason reason,
|
|
|
|
|
+ int socketErrorCode,
|
|
|
|
|
+ bool sendDisconnectPacket,
|
|
|
|
|
+ byte[] data,
|
|
|
|
|
+ int start,
|
|
|
|
|
+ int count)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (sendDisconnectPacket)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (count + 8 >= peer.Mtu)
|
|
|
|
|
+ {
|
|
|
|
|
+ //Drop additional data
|
|
|
|
|
+ data = null;
|
|
|
|
|
+ count = 0;
|
|
|
|
|
+ NetUtils.DebugWriteError("[NM] Disconnect additional data size more than MTU - 8!");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var disconnectPacket = _netPacketPool.Get(PacketProperty.Disconnect, 8 + count);
|
|
|
|
|
+ FastBitConverter.GetBytes(disconnectPacket.RawData, 1, peer.ConnectId);
|
|
|
|
|
+ if (data != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ Buffer.BlockCopy(data, start, disconnectPacket.RawData, 9, count);
|
|
|
|
|
+ }
|
|
|
|
|
+ SendRawAndRecycle(disconnectPacket, peer.EndPoint);
|
|
|
|
|
+ }
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.Disconnect);
|
|
|
|
|
+ netEvent.Peer = peer;
|
|
|
|
|
+ netEvent.AdditionalData = socketErrorCode;
|
|
|
|
|
+ netEvent.DisconnectReason = reason;
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+ RemovePeer(peer.EndPoint);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void ClearPeers()
|
|
|
|
|
+ {
|
|
|
|
|
+ lock (_peers)
|
|
|
|
|
+ {
|
|
|
|
|
+#if WINRT && !UNITY_EDITOR
|
|
|
|
|
+ _socket.ClearPeers();
|
|
|
|
|
+#endif
|
|
|
|
|
+ _peers.Clear();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void RemovePeer(NetEndPoint endPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ _peers.Remove(endPoint);
|
|
|
|
|
+#if WINRT && !UNITY_EDITOR
|
|
|
|
|
+ _socket.RemovePeer(endPoint);
|
|
|
|
|
+#endif
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void RemovePeerAt(int idx)
|
|
|
|
|
+ {
|
|
|
|
|
+#if WINRT && !UNITY_EDITOR
|
|
|
|
|
+ var endPoint = _peers[idx].EndPoint;
|
|
|
|
|
+ _socket.RemovePeer(endPoint);
|
|
|
|
|
+#endif
|
|
|
|
|
+ _peers.RemoveAt(idx);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private NetEvent CreateEvent(NetEventType type)
|
|
|
|
|
+ {
|
|
|
|
|
+ NetEvent evt = null;
|
|
|
|
|
+
|
|
|
|
|
+ lock (_netEventsPool)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (_netEventsPool.Count > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ evt = _netEventsPool.Pop();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if(evt == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ evt = new NetEvent();
|
|
|
|
|
+ }
|
|
|
|
|
+ evt.Type = type;
|
|
|
|
|
+ return evt;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void EnqueueEvent(NetEvent evt)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (UnsyncedEvents)
|
|
|
|
|
+ {
|
|
|
|
|
+ ProcessEvent(evt);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ lock (_netEventsQueue)
|
|
|
|
|
+ {
|
|
|
|
|
+ _netEventsQueue.Enqueue(evt);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void ProcessEvent(NetEvent evt)
|
|
|
|
|
+ {
|
|
|
|
|
+ switch (evt.Type)
|
|
|
|
|
+ {
|
|
|
|
|
+ case NetEventType.Connect:
|
|
|
|
|
+ _netEventListener.OnPeerConnected(evt.Peer);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEventType.Disconnect:
|
|
|
|
|
+ var info = new DisconnectInfo
|
|
|
|
|
+ {
|
|
|
|
|
+ Reason = evt.DisconnectReason,
|
|
|
|
|
+ AdditionalData = evt.DataReader,
|
|
|
|
|
+ SocketErrorCode = evt.AdditionalData
|
|
|
|
|
+ };
|
|
|
|
|
+ _netEventListener.OnPeerDisconnected(evt.Peer, info);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEventType.Receive:
|
|
|
|
|
+ _netEventListener.OnNetworkReceive(evt.Peer, evt.DataReader);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEventType.ReceiveUnconnected:
|
|
|
|
|
+ _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.Default);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEventType.DiscoveryRequest:
|
|
|
|
|
+ _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.DiscoveryRequest);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEventType.DiscoveryResponse:
|
|
|
|
|
+ _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.DiscoveryResponse);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEventType.Error:
|
|
|
|
|
+ _netEventListener.OnNetworkError(evt.RemoteEndPoint, evt.AdditionalData);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NetEventType.ConnectionLatencyUpdated:
|
|
|
|
|
+ _netEventListener.OnNetworkLatencyUpdate(evt.Peer, evt.AdditionalData);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //Recycle
|
|
|
|
|
+ evt.DataReader.Clear();
|
|
|
|
|
+ evt.Peer = null;
|
|
|
|
|
+ evt.AdditionalData = 0;
|
|
|
|
|
+ evt.RemoteEndPoint = null;
|
|
|
|
|
+
|
|
|
|
|
+ lock (_netEventsPool)
|
|
|
|
|
+ {
|
|
|
|
|
+ _netEventsPool.Push(evt);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //Update function
|
|
|
|
|
+ private void UpdateLogic()
|
|
|
|
|
+ {
|
|
|
|
|
+#if DEBUG
|
|
|
|
|
+ if (SimulateLatency)
|
|
|
|
|
+ {
|
|
|
|
|
+ var time = DateTime.UtcNow;
|
|
|
|
|
+ lock (_pingSimulationList)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (int i = 0; i < _pingSimulationList.Count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ var incomingData = _pingSimulationList[i];
|
|
|
|
|
+ if (incomingData.TimeWhenGet <= time)
|
|
|
|
|
+ {
|
|
|
|
|
+ DataReceived(incomingData.Data, incomingData.Data.Length, incomingData.EndPoint);
|
|
|
|
|
+ _pingSimulationList.RemoveAt(i);
|
|
|
|
|
+ i--;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ //Process acks
|
|
|
|
|
+ lock (_peers)
|
|
|
|
|
+ {
|
|
|
|
|
+ int delta = _logicThread.SleepTime;
|
|
|
|
|
+ for(int i = 0; i < _peers.Count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ var netPeer = _peers[i];
|
|
|
|
|
+ if (netPeer.ConnectionState == ConnectionState.Connected && netPeer.TimeSinceLastPacket > DisconnectTimeout)
|
|
|
|
|
+ {
|
|
|
|
|
+ NetUtils.DebugWrite("[NM] Disconnect by timeout: {0} > {1}", netPeer.TimeSinceLastPacket, DisconnectTimeout);
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.Disconnect);
|
|
|
|
|
+ netEvent.Peer = netPeer;
|
|
|
|
|
+ netEvent.DisconnectReason = DisconnectReason.Timeout;
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+
|
|
|
|
|
+ RemovePeerAt(i);
|
|
|
|
|
+ i--;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if(netPeer.ConnectionState == ConnectionState.Disconnected)
|
|
|
|
|
+ {
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.Disconnect);
|
|
|
|
|
+ netEvent.Peer = netPeer;
|
|
|
|
|
+ netEvent.DisconnectReason = DisconnectReason.ConnectionFailed;
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+
|
|
|
|
|
+ RemovePeerAt(i);
|
|
|
|
|
+ i--;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ netPeer.Update(delta);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void ReceiveLogic(byte[] data, int length, int errorCode, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ //Receive some info
|
|
|
|
|
+ if (errorCode == 0)
|
|
|
|
|
+ {
|
|
|
|
|
+#if DEBUG
|
|
|
|
|
+ bool receivePacket = true;
|
|
|
|
|
+
|
|
|
|
|
+ if (SimulatePacketLoss && _randomGenerator.Next(100/SimulationPacketLossChance) == 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ receivePacket = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (SimulateLatency)
|
|
|
|
|
+ {
|
|
|
|
|
+ int latency = _randomGenerator.Next(SimulationMinLatency, SimulationMaxLatency);
|
|
|
|
|
+ if (latency > MinLatencyTreshold)
|
|
|
|
|
+ {
|
|
|
|
|
+ byte[] holdedData = new byte[length];
|
|
|
|
|
+ Buffer.BlockCopy(data, 0, holdedData, 0, length);
|
|
|
|
|
+
|
|
|
|
|
+ lock (_pingSimulationList)
|
|
|
|
|
+ {
|
|
|
|
|
+ _pingSimulationList.Add(new IncomingData
|
|
|
|
|
+ {
|
|
|
|
|
+ Data = holdedData,
|
|
|
|
|
+ EndPoint = remoteEndPoint,
|
|
|
|
|
+ TimeWhenGet = DateTime.UtcNow.AddMilliseconds(latency)
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ receivePacket = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (receivePacket) //DataReceived
|
|
|
|
|
+#endif
|
|
|
|
|
+ //ProcessEvents
|
|
|
|
|
+ DataReceived(data, length, remoteEndPoint);
|
|
|
|
|
+ }
|
|
|
|
|
+ else //Error on receive
|
|
|
|
|
+ {
|
|
|
|
|
+ ClearPeers();
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.Error);
|
|
|
|
|
+ netEvent.AdditionalData = errorCode;
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void DataReceived(byte[] reusableBuffer, int count, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+#if STATS_ENABLED
|
|
|
|
|
+ PacketsReceived++;
|
|
|
|
|
+ BytesReceived += (uint) count;
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ //Try read packet
|
|
|
|
|
+ NetPacket packet = _netPacketPool.GetAndRead(reusableBuffer, 0, count);
|
|
|
|
|
+ if (packet == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ NetUtils.DebugWriteError("[NM] DataReceived: bad!");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //Check unconnected
|
|
|
|
|
+ switch (packet.Property)
|
|
|
|
|
+ {
|
|
|
|
|
+ case PacketProperty.DiscoveryRequest:
|
|
|
|
|
+ if(DiscoveryEnabled)
|
|
|
|
|
+ {
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.DiscoveryRequest);
|
|
|
|
|
+ netEvent.RemoteEndPoint = remoteEndPoint;
|
|
|
|
|
+ netEvent.DataReader.SetSource(packet.RawData, NetConstants.HeaderSize);
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ case PacketProperty.DiscoveryResponse:
|
|
|
|
|
+ {
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.DiscoveryResponse);
|
|
|
|
|
+ netEvent.RemoteEndPoint = remoteEndPoint;
|
|
|
|
|
+ netEvent.DataReader.SetSource(packet.RawData, NetConstants.HeaderSize);
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ case PacketProperty.UnconnectedMessage:
|
|
|
|
|
+ if (UnconnectedMessagesEnabled)
|
|
|
|
|
+ {
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.ReceiveUnconnected);
|
|
|
|
|
+ netEvent.RemoteEndPoint = remoteEndPoint;
|
|
|
|
|
+ netEvent.DataReader.SetSource(packet.RawData, NetConstants.HeaderSize);
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ case PacketProperty.NatIntroduction:
|
|
|
|
|
+ case PacketProperty.NatIntroductionRequest:
|
|
|
|
|
+ case PacketProperty.NatPunchMessage:
|
|
|
|
|
+ {
|
|
|
|
|
+ if (NatPunchEnabled)
|
|
|
|
|
+ NatPunchModule.ProcessMessage(remoteEndPoint, packet);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //Check normal packets
|
|
|
|
|
+ NetPeer netPeer;
|
|
|
|
|
+
|
|
|
|
|
+ //Check peers
|
|
|
|
|
+ Monitor.Enter(_peers);
|
|
|
|
|
+ int peersCount = _peers.Count;
|
|
|
|
|
+
|
|
|
|
|
+ if (_peers.TryGetValue(remoteEndPoint, out netPeer))
|
|
|
|
|
+ {
|
|
|
|
|
+ Monitor.Exit(_peers);
|
|
|
|
|
+ //Send
|
|
|
|
|
+ if (packet.Property == PacketProperty.Disconnect)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (BitConverter.ToInt64(packet.RawData, 1) != netPeer.ConnectId)
|
|
|
|
|
+ {
|
|
|
|
|
+ //Old or incorrect disconnect
|
|
|
|
|
+ _netPacketPool.Recycle(packet);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.Disconnect);
|
|
|
|
|
+ netEvent.Peer = netPeer;
|
|
|
|
|
+ netEvent.DataReader.SetSource(packet.RawData, 5, packet.Size - 5);
|
|
|
|
|
+ netEvent.DisconnectReason = DisconnectReason.RemoteConnectionClose;
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+
|
|
|
|
|
+ _peers.Remove(netPeer.EndPoint);
|
|
|
|
|
+ //do not recycle because no sense)
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (packet.Property == PacketProperty.ConnectAccept)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (netPeer.ProcessConnectAccept(packet))
|
|
|
|
|
+ {
|
|
|
|
|
+ var connectEvent = CreateEvent(NetEventType.Connect);
|
|
|
|
|
+ connectEvent.Peer = netPeer;
|
|
|
|
|
+ EnqueueEvent(connectEvent);
|
|
|
|
|
+ }
|
|
|
|
|
+ _netPacketPool.Recycle(packet);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ netPeer.ProcessPacket(packet);
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ if (peersCount < _maxConnections && packet.Property == PacketProperty.ConnectRequest)
|
|
|
|
|
+ {
|
|
|
|
|
+ int protoId = BitConverter.ToInt32(packet.RawData, 1);
|
|
|
|
|
+ if (protoId != NetConstants.ProtocolId)
|
|
|
|
|
+ {
|
|
|
|
|
+ NetUtils.DebugWrite(ConsoleColor.Cyan,
|
|
|
|
|
+ "[NM] Peer connect reject. Invalid protocol ID: " + protoId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ string peerKey = Encoding.UTF8.GetString(packet.RawData, 13, packet.Size - 13);
|
|
|
|
|
+ if (peerKey != _connectKey)
|
|
|
|
|
+ {
|
|
|
|
|
+ NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Peer connect reject. Invalid key: " + peerKey);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //Getting new id for peer
|
|
|
|
|
+ long connectionId = BitConverter.ToInt64(packet.RawData, 5);
|
|
|
|
|
+ //response with id
|
|
|
|
|
+ netPeer = new NetPeer(this, remoteEndPoint, connectionId);
|
|
|
|
|
+ NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Received peer connect request Id: {0}, EP: {1}",
|
|
|
|
|
+ netPeer.ConnectId, remoteEndPoint);
|
|
|
|
|
+
|
|
|
|
|
+ //clean incoming packet
|
|
|
|
|
+ _netPacketPool.Recycle(packet);
|
|
|
|
|
+
|
|
|
|
|
+ _peers.Add(remoteEndPoint, netPeer);
|
|
|
|
|
+
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.Connect);
|
|
|
|
|
+ netEvent.Peer = netPeer;
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ finally
|
|
|
|
|
+ {
|
|
|
|
|
+ Monitor.Exit(_peers);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ internal void ReceiveFromPeer(NetPacket packet, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ NetPeer fromPeer;
|
|
|
|
|
+ if (_peers.TryGetValue(remoteEndPoint, out fromPeer))
|
|
|
|
|
+ {
|
|
|
|
|
+ NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Received message");
|
|
|
|
|
+ var netEvent = CreateEvent(NetEventType.Receive);
|
|
|
|
|
+ netEvent.Peer = fromPeer;
|
|
|
|
|
+ netEvent.RemoteEndPoint = fromPeer.EndPoint;
|
|
|
|
|
+ netEvent.DataReader.SetSource(packet.GetPacketData());
|
|
|
|
|
+ EnqueueEvent(netEvent);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Send data to all connected peers
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="writer">DataWriter with data</param>
|
|
|
|
|
+ /// <param name="options">Send options (reliable, unreliable, etc.)</param>
|
|
|
|
|
+ public void SendToAll(NetDataWriter writer, SendOptions options)
|
|
|
|
|
+ {
|
|
|
|
|
+ SendToAll(writer.Data, 0, writer.Length, options);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Send data to all connected peers
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="data">Data</param>
|
|
|
|
|
+ /// <param name="options">Send options (reliable, unreliable, etc.)</param>
|
|
|
|
|
+ public void SendToAll(byte[] data, SendOptions options)
|
|
|
|
|
+ {
|
|
|
|
|
+ SendToAll(data, 0, data.Length, options);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Send data to all connected peers
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="data">Data</param>
|
|
|
|
|
+ /// <param name="start">Start of data</param>
|
|
|
|
|
+ /// <param name="length">Length of data</param>
|
|
|
|
|
+ /// <param name="options">Send options (reliable, unreliable, etc.)</param>
|
|
|
|
|
+ public void SendToAll(byte[] data, int start, int length, SendOptions options)
|
|
|
|
|
+ {
|
|
|
|
|
+ lock (_peers)
|
|
|
|
|
+ {
|
|
|
|
|
+ for(int i = 0; i < _peers.Count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ _peers[i].Send(data, start, length, options);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Send data to all connected peers
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="writer">DataWriter with data</param>
|
|
|
|
|
+ /// <param name="options">Send options (reliable, unreliable, etc.)</param>
|
|
|
|
|
+ /// <param name="excludePeer">Excluded peer</param>
|
|
|
|
|
+ public void SendToAll(NetDataWriter writer, SendOptions options, NetPeer excludePeer)
|
|
|
|
|
+ {
|
|
|
|
|
+ SendToAll(writer.Data, 0, writer.Length, options, excludePeer);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Send data to all connected peers
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="data">Data</param>
|
|
|
|
|
+ /// <param name="options">Send options (reliable, unreliable, etc.)</param>
|
|
|
|
|
+ /// <param name="excludePeer">Excluded peer</param>
|
|
|
|
|
+ public void SendToAll(byte[] data, SendOptions options, NetPeer excludePeer)
|
|
|
|
|
+ {
|
|
|
|
|
+ SendToAll(data, 0, data.Length, options, excludePeer);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Send data to all connected peers
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="data">Data</param>
|
|
|
|
|
+ /// <param name="start">Start of data</param>
|
|
|
|
|
+ /// <param name="length">Length of data</param>
|
|
|
|
|
+ /// <param name="options">Send options (reliable, unreliable, etc.)</param>
|
|
|
|
|
+ /// <param name="excludePeer">Excluded peer</param>
|
|
|
|
|
+ public void SendToAll(byte[] data, int start, int length, SendOptions options, NetPeer excludePeer)
|
|
|
|
|
+ {
|
|
|
|
|
+ lock (_peers)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (int i = 0; i < _peers.Count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ var netPeer = _peers[i];
|
|
|
|
|
+ if (netPeer != excludePeer)
|
|
|
|
|
+ {
|
|
|
|
|
+ netPeer.Send(data, start, length, options);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Start logic thread and listening on available port
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public bool Start()
|
|
|
|
|
+ {
|
|
|
|
|
+ return Start(0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Start logic thread and listening on selected port
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="port">port to listen</param>
|
|
|
|
|
+ public bool Start(int port)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (IsRunning)
|
|
|
|
|
+ {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ _netEventsQueue.Clear();
|
|
|
|
|
+ if (!_socket.Bind(port, ReuseAddress))
|
|
|
|
|
+ return false;
|
|
|
|
|
+
|
|
|
|
|
+ _logicThread.Start();
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Send message without connection
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="message">Raw data</param>
|
|
|
|
|
+ /// <param name="remoteEndPoint">Packet destination</param>
|
|
|
|
|
+ /// <returns>Operation result</returns>
|
|
|
|
|
+ public bool SendUnconnectedMessage(byte[] message, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ return SendUnconnectedMessage(message, 0, message.Length, remoteEndPoint);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Send message without connection
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="writer">Data serializer</param>
|
|
|
|
|
+ /// <param name="remoteEndPoint">Packet destination</param>
|
|
|
|
|
+ /// <returns>Operation result</returns>
|
|
|
|
|
+ public bool SendUnconnectedMessage(NetDataWriter writer, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Send message without connection
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="message">Raw data</param>
|
|
|
|
|
+ /// <param name="start">data start</param>
|
|
|
|
|
+ /// <param name="length">data length</param>
|
|
|
|
|
+ /// <param name="remoteEndPoint">Packet destination</param>
|
|
|
|
|
+ /// <returns>Operation result</returns>
|
|
|
|
|
+ public bool SendUnconnectedMessage(byte[] message, int start, int length, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!IsRunning)
|
|
|
|
|
+ return false;
|
|
|
|
|
+ var packet = _netPacketPool.GetWithData(PacketProperty.UnconnectedMessage, message, start, length);
|
|
|
|
|
+ bool result = SendRawAndRecycle(packet, remoteEndPoint);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public bool SendDiscoveryRequest(NetDataWriter writer, int port)
|
|
|
|
|
+ {
|
|
|
|
|
+ return SendDiscoveryRequest(writer.Data, 0, writer.Length, port);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public bool SendDiscoveryRequest(byte[] data, int port)
|
|
|
|
|
+ {
|
|
|
|
|
+ return SendDiscoveryRequest(data, 0, data.Length, port);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public bool SendDiscoveryRequest(byte[] data, int start, int length, int port)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!IsRunning)
|
|
|
|
|
+ return false;
|
|
|
|
|
+ var packet = _netPacketPool.GetWithData(PacketProperty.DiscoveryRequest, data, start, length);
|
|
|
|
|
+ bool result = _socket.SendBroadcast(packet.RawData, 0, packet.Size, port);
|
|
|
|
|
+ _netPacketPool.Recycle(packet);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public bool SendDiscoveryResponse(NetDataWriter writer, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ return SendDiscoveryResponse(writer.Data, 0, writer.Length, remoteEndPoint);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public bool SendDiscoveryResponse(byte[] data, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ return SendDiscoveryResponse(data, 0, data.Length, remoteEndPoint);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public bool SendDiscoveryResponse(byte[] data, int start, int length, NetEndPoint remoteEndPoint)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!IsRunning)
|
|
|
|
|
+ return false;
|
|
|
|
|
+ var packet = _netPacketPool.GetWithData(PacketProperty.DiscoveryResponse, data, start, length);
|
|
|
|
|
+ bool result = SendRawAndRecycle(packet, remoteEndPoint);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Flush all queued packets of all peers
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public void Flush()
|
|
|
|
|
+ {
|
|
|
|
|
+ lock (_peers)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (int i = 0; i < _peers.Count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ _peers[i].Flush();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Receive all pending events. Call this in game update code
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public void PollEvents()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (UnsyncedEvents)
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+ while (_netEventsQueue.Count > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ NetEvent evt;
|
|
|
|
|
+ lock (_netEventsQueue)
|
|
|
|
|
+ {
|
|
|
|
|
+ evt = _netEventsQueue.Dequeue();
|
|
|
|
|
+ }
|
|
|
|
|
+ ProcessEvent(evt);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Connect to remote host
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="address">Server IP or hostname</param>
|
|
|
|
|
+ /// <param name="port">Server Port</param>
|
|
|
|
|
+ public void Connect(string address, int port)
|
|
|
|
|
+ {
|
|
|
|
|
+ //Create target endpoint
|
|
|
|
|
+ NetEndPoint ep = new NetEndPoint(address, port);
|
|
|
|
|
+ Connect(ep);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Connect to remote host
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="target">Server end point (ip and port)</param>
|
|
|
|
|
+ public void Connect(NetEndPoint target)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!IsRunning)
|
|
|
|
|
+ {
|
|
|
|
|
+ throw new Exception("Client is not running");
|
|
|
|
|
+ }
|
|
|
|
|
+ lock (_peers)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (_peers.ContainsAddress(target) || _peers.Count >= _maxConnections)
|
|
|
|
|
+ {
|
|
|
|
|
+ //Already connected
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //Create reliable connection
|
|
|
|
|
+ //And request connection
|
|
|
|
|
+ var newPeer = new NetPeer(this, target, 0);
|
|
|
|
|
+ _peers.Add(target, newPeer);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Force closes connection and stop all threads.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public void Stop()
|
|
|
|
|
+ {
|
|
|
|
|
+ //Send disconnect packets
|
|
|
|
|
+ lock (_peers)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (int i = 0; i < _peers.Count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ var disconnectPacket = _netPacketPool.Get(PacketProperty.Disconnect, 8);
|
|
|
|
|
+ FastBitConverter.GetBytes(disconnectPacket.RawData, 1, _peers[i].ConnectId);
|
|
|
|
|
+ SendRawAndRecycle(disconnectPacket, _peers[i].EndPoint);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //Clear
|
|
|
|
|
+ ClearPeers();
|
|
|
|
|
+
|
|
|
|
|
+ //Stop
|
|
|
|
|
+ if (IsRunning)
|
|
|
|
|
+ {
|
|
|
|
|
+ _logicThread.Stop();
|
|
|
|
|
+ _socket.Close();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Get first peer. Usefull for Client mode
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <returns></returns>
|
|
|
|
|
+ public NetPeer GetFirstPeer()
|
|
|
|
|
+ {
|
|
|
|
|
+ lock (_peers)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (_peers.Count > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ return _peers[0];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Get copy of current connected peers
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <returns>Array with connected peers</returns>
|
|
|
|
|
+ public NetPeer[] GetPeers()
|
|
|
|
|
+ {
|
|
|
|
|
+ NetPeer[] peers;
|
|
|
|
|
+ lock (_peers)
|
|
|
|
|
+ {
|
|
|
|
|
+ peers = _peers.ToArray();
|
|
|
|
|
+ }
|
|
|
|
|
+ return peers;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Get copy of current connected peers (without allocations)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="peers">List that will contain result</param>
|
|
|
|
|
+ public void GetPeersNonAlloc(List<NetPeer> peers)
|
|
|
|
|
+ {
|
|
|
|
|
+ peers.Clear();
|
|
|
|
|
+ lock (_peers)
|
|
|
|
|
+ {
|
|
|
|
|
+ for(int i = 0; i < _peers.Count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ peers.Add(_peers[i]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Disconnect peer from server
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="peer">peer to disconnect</param>
|
|
|
|
|
+ public void DisconnectPeer(NetPeer peer)
|
|
|
|
|
+ {
|
|
|
|
|
+ DisconnectPeer(peer, null, 0, 0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Disconnect peer from server and send additional data (Size must be less or equal MTU - 8)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="peer">peer to disconnect</param>
|
|
|
|
|
+ /// <param name="data">additional data</param>
|
|
|
|
|
+ public void DisconnectPeer(NetPeer peer, byte[] data)
|
|
|
|
|
+ {
|
|
|
|
|
+ DisconnectPeer(peer, data, 0, data.Length);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Disconnect peer from server and send additional data (Size must be less or equal MTU - 8)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="peer">peer to disconnect</param>
|
|
|
|
|
+ /// <param name="writer">additional data</param>
|
|
|
|
|
+ public void DisconnectPeer(NetPeer peer, NetDataWriter writer)
|
|
|
|
|
+ {
|
|
|
|
|
+ DisconnectPeer(peer, writer.Data, 0, writer.Length);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Disconnect peer from server and send additional data (Size must be less or equal MTU - 8)
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="peer">peer to disconnect</param>
|
|
|
|
|
+ /// <param name="data">additional data</param>
|
|
|
|
|
+ /// <param name="start">data start</param>
|
|
|
|
|
+ /// <param name="count">data length</param>
|
|
|
|
|
+ public void DisconnectPeer(NetPeer peer, byte[] data, int start, int count)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (peer != null && _peers.ContainsAddress(peer.EndPoint))
|
|
|
|
|
+ {
|
|
|
|
|
+ DisconnectPeer(peer, DisconnectReason.DisconnectPeerCalled, 0, true, data, start, count);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+#endif
|