Luke Stampfli
4 年前
当前提交
b6a4ffea
共有 77 个文件被更改,包括 8248 次插入 和 0 次删除
-
8Packages/packages-lock.json
-
5Packages/com.unity.multiplayer.transport.litenet/CHANGELOG.md
-
7Packages/com.unity.multiplayer.transport.litenet/CHANGELOG.md.meta
-
21Packages/com.unity.multiplayer.transport.litenet/LICENSE.md
-
7Packages/com.unity.multiplayer.transport.litenet/LICENSE.md.meta
-
1Packages/com.unity.multiplayer.transport.litenet/README.md
-
7Packages/com.unity.multiplayer.transport.litenet/README.md.meta
-
8Packages/com.unity.multiplayer.transport.litenet/Runtime.meta
-
8Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib.meta
-
31Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/BaseChannel.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/BaseChannel.cs.meta
-
137Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/ConnectionRequest.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/ConnectionRequest.cs.meta
-
224Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/INetEventListener.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/INetEventListener.cs.meta
-
8Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Layers.meta
-
36Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Layers/Crc32cLayer.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Layers/Crc32cLayer.cs.meta
-
15Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Layers/PacketLayerBase.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Layers/PacketLayerBase.cs.meta
-
59Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Layers/XorEncryptLayer.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Layers/XorEncryptLayer.cs.meta
-
7Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/LiteNetLib.csproj.meta
-
245Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NatPunchModule.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NatPunchModule.cs.meta
-
72Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetConstants.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetConstants.cs.meta
-
92Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetDebug.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetDebug.cs.meta
-
1001Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetManager.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetManager.cs.meta
-
276Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetPacket.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetPacket.cs.meta
-
74Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetPacketPool.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetPacketPool.cs.meta
-
1001Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetPeer.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetPeer.cs.meta
-
483Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetSocket.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetSocket.cs.meta
-
39Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetStatistics.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetStatistics.cs.meta
-
197Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetUtils.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetUtils.cs.meta
-
323Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/ReliableChannel.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/ReliableChannel.cs.meta
-
99Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/SequencedChannel.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/SequencedChannel.cs.meta
-
8Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils.meta
-
111Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/CRC32C.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/CRC32C.cs.meta
-
118Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/FastBitConverter.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/FastBitConverter.cs.meta
-
8Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/INetSerializable.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/INetSerializable.cs.meta
-
694Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NetDataReader.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NetDataReader.cs.meta
-
382Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NetDataWriter.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NetDataWriter.cs.meta
-
314Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NetPacketProcessor.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NetPacketProcessor.cs.meta
-
752Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NetSerializer.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NetSerializer.cs.meta
-
426Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NtpPacket.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NtpPacket.cs.meta
-
175Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NtpRequest.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/Utils/NtpRequest.cs.meta
-
415Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLibTransport.cs
-
11Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLibTransport.cs.meta
-
16Packages/com.unity.multiplayer.transport.litenet/Runtime/com.unity.multiplayer.transport.litenet.asmdef
-
7Packages/com.unity.multiplayer.transport.litenet/Runtime/com.unity.multiplayer.transport.litenet.asmdef.meta
-
16Packages/com.unity.multiplayer.transport.litenet/package.json
-
7Packages/com.unity.multiplayer.transport.litenet/package.json.meta
|
|||
Changelog |
|||
All notable changes to this package will be documented in this file. The format is based on Keep a Changelog |
|||
|
|||
[0.0.1-preview.1] - 2020-10-05 |
|||
This is the first release of Enet MLAPI Package |
|
|||
fileFormatVersion: 2 |
|||
guid: 28f71d7dc1ed3bc4ba12892aa139f9e6 |
|||
TextScriptImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
MIT License |
|||
|
|||
Copyright (c) 2018, 2019 Albin Corén |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|
|||
fileFormatVersion: 2 |
|||
guid: 0112cf41b55769347836bdb4bfc2f66e |
|||
TextScriptImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Enet transport for MLAPI |
|
|||
fileFormatVersion: 2 |
|||
guid: 859976d97fd97dc48adc4bc7ff818f45 |
|||
TextScriptImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 76d634f7c88c1da4cb3ea69d7bae43bb |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 22e683408a542064b9a7e0670cba52a2 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
internal abstract class BaseChannel |
|||
{ |
|||
public BaseChannel Next; |
|||
protected readonly NetPeer Peer; |
|||
protected readonly Queue<NetPacket> OutgoingQueue; |
|||
|
|||
protected BaseChannel(NetPeer peer) |
|||
{ |
|||
Peer = peer; |
|||
OutgoingQueue = new Queue<NetPacket>(64); |
|||
} |
|||
|
|||
public int PacketsInQueue |
|||
{ |
|||
get { return OutgoingQueue.Count; } |
|||
} |
|||
|
|||
public void AddToQueue(NetPacket packet) |
|||
{ |
|||
lock (OutgoingQueue) |
|||
OutgoingQueue.Enqueue(packet); |
|||
} |
|||
|
|||
public abstract void SendNextPackets(); |
|||
public abstract bool ProcessPacket(NetPacket packet); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a74c865f121743d479838192cd3b2e06 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Net; |
|||
using System.Threading; |
|||
using LiteNetLib.Utils; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
internal enum ConnectionRequestResult |
|||
{ |
|||
None, |
|||
Accept, |
|||
Reject, |
|||
RejectForce |
|||
} |
|||
|
|||
public class ConnectionRequest |
|||
{ |
|||
private readonly NetManager _listener; |
|||
private int _used; |
|||
|
|||
public readonly NetDataReader Data; |
|||
|
|||
internal ConnectionRequestResult Result { get; private set; } |
|||
internal long ConnectionTime; |
|||
internal byte ConnectionNumber; |
|||
public readonly IPEndPoint RemoteEndPoint; |
|||
|
|||
private bool TryActivate() |
|||
{ |
|||
return Interlocked.CompareExchange(ref _used, 1, 0) == 0; |
|||
} |
|||
|
|||
internal void UpdateRequest(NetConnectRequestPacket connRequest) |
|||
{ |
|||
if (connRequest.ConnectionTime >= ConnectionTime) |
|||
{ |
|||
ConnectionTime = connRequest.ConnectionTime; |
|||
ConnectionNumber = connRequest.ConnectionNumber; |
|||
} |
|||
} |
|||
|
|||
internal ConnectionRequest( |
|||
long connectionId, |
|||
byte connectionNumber, |
|||
NetDataReader netDataReader, |
|||
IPEndPoint endPoint, |
|||
NetManager listener) |
|||
{ |
|||
ConnectionTime = connectionId; |
|||
ConnectionNumber = connectionNumber; |
|||
RemoteEndPoint = endPoint; |
|||
Data = netDataReader; |
|||
_listener = listener; |
|||
} |
|||
|
|||
public NetPeer AcceptIfKey(string key) |
|||
{ |
|||
if (!TryActivate()) |
|||
return null; |
|||
try |
|||
{ |
|||
if (Data.GetString() == key) |
|||
Result = ConnectionRequestResult.Accept; |
|||
} |
|||
catch |
|||
{ |
|||
NetDebug.WriteError("[AC] Invalid incoming data"); |
|||
} |
|||
if (Result == ConnectionRequestResult.Accept) |
|||
return _listener.OnConnectionSolved(this, null, 0, 0); |
|||
|
|||
Result = ConnectionRequestResult.Reject; |
|||
_listener.OnConnectionSolved(this, null, 0, 0); |
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Accept connection and get new NetPeer as result
|
|||
/// </summary>
|
|||
/// <returns>Connected NetPeer</returns>
|
|||
public NetPeer Accept() |
|||
{ |
|||
if (!TryActivate()) |
|||
return null; |
|||
Result = ConnectionRequestResult.Accept; |
|||
return _listener.OnConnectionSolved(this, null, 0, 0); |
|||
} |
|||
|
|||
public void Reject(byte[] rejectData, int start, int length, bool force) |
|||
{ |
|||
if (!TryActivate()) |
|||
return; |
|||
Result = force ? ConnectionRequestResult.RejectForce : ConnectionRequestResult.Reject; |
|||
_listener.OnConnectionSolved(this, rejectData, start, length); |
|||
} |
|||
|
|||
public void Reject(byte[] rejectData, int start, int length) |
|||
{ |
|||
Reject(rejectData, start, length, false); |
|||
} |
|||
|
|||
|
|||
public void RejectForce(byte[] rejectData, int start, int length) |
|||
{ |
|||
Reject(rejectData, start, length, true); |
|||
} |
|||
|
|||
public void RejectForce() |
|||
{ |
|||
Reject(null, 0, 0, true); |
|||
} |
|||
|
|||
public void RejectForce(byte[] rejectData) |
|||
{ |
|||
Reject(rejectData, 0, rejectData.Length, true); |
|||
} |
|||
|
|||
public void RejectForce(NetDataWriter rejectData) |
|||
{ |
|||
Reject(rejectData.Data, 0, rejectData.Length, true); |
|||
} |
|||
|
|||
public void Reject() |
|||
{ |
|||
Reject(null, 0, 0, false); |
|||
} |
|||
|
|||
public void Reject(byte[] rejectData) |
|||
{ |
|||
Reject(rejectData, 0, rejectData.Length, false); |
|||
} |
|||
|
|||
public void Reject(NetDataWriter rejectData) |
|||
{ |
|||
Reject(rejectData.Data, 0, rejectData.Length, false); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: ab9cb220487967e4ba764971b6eb9fac |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
/// <summary>
|
|||
/// Type of message that you receive in OnNetworkReceiveUnconnected event
|
|||
/// </summary>
|
|||
public enum UnconnectedMessageType |
|||
{ |
|||
BasicMessage, |
|||
Broadcast |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disconnect reason that you receive in OnPeerDisconnected event
|
|||
/// </summary>
|
|||
public enum DisconnectReason |
|||
{ |
|||
ConnectionFailed, |
|||
Timeout, |
|||
HostUnreachable, |
|||
NetworkUnreachable, |
|||
RemoteConnectionClose, |
|||
DisconnectPeerCalled, |
|||
ConnectionRejected, |
|||
InvalidProtocol, |
|||
UnknownHost, |
|||
Reconnect, |
|||
PeerToPeerConnection |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Additional information about disconnection
|
|||
/// </summary>
|
|||
public struct DisconnectInfo |
|||
{ |
|||
/// <summary>
|
|||
/// Additional info why peer disconnected
|
|||
/// </summary>
|
|||
public DisconnectReason Reason; |
|||
|
|||
/// <summary>
|
|||
/// Error code (if reason is SocketSendError or SocketReceiveError)
|
|||
/// </summary>
|
|||
public SocketError SocketErrorCode; |
|||
|
|||
/// <summary>
|
|||
/// Additional data that can be accessed (only if reason is RemoteConnectionClose)
|
|||
/// </summary>
|
|||
public NetPacketReader 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="socketError">Socket error</param>
|
|||
void OnNetworkError(IPEndPoint endPoint, SocketError socketError); |
|||
|
|||
/// <summary>
|
|||
/// Received some data
|
|||
/// </summary>
|
|||
/// <param name="peer">From peer</param>
|
|||
/// <param name="reader">DataReader containing all received data</param>
|
|||
/// <param name="deliveryMethod">Type of received packet</param>
|
|||
void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod); |
|||
|
|||
/// <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 response)</param>
|
|||
void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader 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); |
|||
|
|||
/// <summary>
|
|||
/// On peer connection requested
|
|||
/// </summary>
|
|||
/// <param name="request">Request information (EndPoint, internal id, additional data)</param>
|
|||
void OnConnectionRequest(ConnectionRequest request); |
|||
} |
|||
|
|||
public interface IDeliveryEventListener |
|||
{ |
|||
/// <summary>
|
|||
/// On reliable message delivered
|
|||
/// </summary>
|
|||
/// <param name="peer"></param>
|
|||
/// <param name="userData"></param>
|
|||
void OnMessageDelivered(NetPeer peer, object userData); |
|||
} |
|||
|
|||
public class EventBasedNetListener : INetEventListener, IDeliveryEventListener |
|||
{ |
|||
public delegate void OnPeerConnected(NetPeer peer); |
|||
public delegate void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo); |
|||
public delegate void OnNetworkError(IPEndPoint endPoint, SocketError socketError); |
|||
public delegate void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod); |
|||
public delegate void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType); |
|||
public delegate void OnNetworkLatencyUpdate(NetPeer peer, int latency); |
|||
public delegate void OnConnectionRequest(ConnectionRequest request); |
|||
public delegate void OnDeliveryEvent(NetPeer peer, object userData); |
|||
|
|||
public event OnPeerConnected PeerConnectedEvent; |
|||
public event OnPeerDisconnected PeerDisconnectedEvent; |
|||
public event OnNetworkError NetworkErrorEvent; |
|||
public event OnNetworkReceive NetworkReceiveEvent; |
|||
public event OnNetworkReceiveUnconnected NetworkReceiveUnconnectedEvent; |
|||
public event OnNetworkLatencyUpdate NetworkLatencyUpdateEvent; |
|||
public event OnConnectionRequest ConnectionRequestEvent; |
|||
public event OnDeliveryEvent DeliveryEvent; |
|||
|
|||
public void ClearPeerConnectedEvent() |
|||
{ |
|||
PeerConnectedEvent = null; |
|||
} |
|||
|
|||
public void ClearPeerDisconnectedEvent() |
|||
{ |
|||
PeerDisconnectedEvent = null; |
|||
} |
|||
|
|||
public void ClearNetworkErrorEvent() |
|||
{ |
|||
NetworkErrorEvent = null; |
|||
} |
|||
|
|||
public void ClearNetworkReceiveEvent() |
|||
{ |
|||
NetworkReceiveEvent = null; |
|||
} |
|||
|
|||
public void ClearNetworkReceiveUnconnectedEvent() |
|||
{ |
|||
NetworkReceiveUnconnectedEvent = null; |
|||
} |
|||
|
|||
public void ClearNetworkLatencyUpdateEvent() |
|||
{ |
|||
NetworkLatencyUpdateEvent = null; |
|||
} |
|||
|
|||
public void ClearConnectionRequestEvent() |
|||
{ |
|||
ConnectionRequestEvent = null; |
|||
} |
|||
|
|||
public void ClearDeliveryEvent() |
|||
{ |
|||
DeliveryEvent = null; |
|||
} |
|||
|
|||
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(IPEndPoint endPoint, SocketError socketErrorCode) |
|||
{ |
|||
if (NetworkErrorEvent != null) |
|||
NetworkErrorEvent(endPoint, socketErrorCode); |
|||
} |
|||
|
|||
void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) |
|||
{ |
|||
if (NetworkReceiveEvent != null) |
|||
NetworkReceiveEvent(peer, reader, deliveryMethod); |
|||
} |
|||
|
|||
void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) |
|||
{ |
|||
if (NetworkReceiveUnconnectedEvent != null) |
|||
NetworkReceiveUnconnectedEvent(remoteEndPoint, reader, messageType); |
|||
} |
|||
|
|||
void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency) |
|||
{ |
|||
if (NetworkLatencyUpdateEvent != null) |
|||
NetworkLatencyUpdateEvent(peer, latency); |
|||
} |
|||
|
|||
void INetEventListener.OnConnectionRequest(ConnectionRequest request) |
|||
{ |
|||
if (ConnectionRequestEvent != null) |
|||
ConnectionRequestEvent(request); |
|||
} |
|||
|
|||
void IDeliveryEventListener.OnMessageDelivered(NetPeer peer, object userData) |
|||
{ |
|||
if (DeliveryEvent != null) |
|||
DeliveryEvent(peer, userData); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 28881074a6c781a439d38403a3182c2b |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: aebc2d698872c56429dc96aa2ddcd270 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using LiteNetLib.Utils; |
|||
using System; |
|||
|
|||
namespace LiteNetLib.Layers |
|||
{ |
|||
public sealed class Crc32cLayer : PacketLayerBase |
|||
{ |
|||
public Crc32cLayer() : base(CRC32C.ChecksumSize) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public override void ProcessInboundPacket(ref byte[] data, ref int length) |
|||
{ |
|||
if (length < NetConstants.HeaderSize + CRC32C.ChecksumSize) |
|||
{ |
|||
NetDebug.WriteError("[NM] DataReceived size: bad!"); |
|||
return; |
|||
} |
|||
|
|||
int checksumPoint = length - CRC32C.ChecksumSize; |
|||
if (CRC32C.Compute(data, 0, checksumPoint) != BitConverter.ToUInt32(data, checksumPoint)) |
|||
{ |
|||
NetDebug.Write("[NM] DataReceived checksum: bad!"); |
|||
return; |
|||
} |
|||
length -= CRC32C.ChecksumSize; |
|||
} |
|||
|
|||
public override void ProcessOutBoundPacket(ref byte[] data, ref int offset, ref int length) |
|||
{ |
|||
FastBitConverter.GetBytes(data, length, CRC32C.Compute(data, offset, length)); |
|||
length += CRC32C.ChecksumSize; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d658f1c6b66f5684780b95233d5fe00b |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
namespace LiteNetLib.Layers |
|||
{ |
|||
public abstract class PacketLayerBase |
|||
{ |
|||
public readonly int ExtraPacketSizeForLayer; |
|||
|
|||
protected PacketLayerBase(int extraPacketSizeForLayer) |
|||
{ |
|||
ExtraPacketSizeForLayer = extraPacketSizeForLayer; |
|||
} |
|||
|
|||
public abstract void ProcessInboundPacket(ref byte[] data, ref int length); |
|||
public abstract void ProcessOutBoundPacket(ref byte[] data, ref int offset, ref int length); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: aba74192fa2edf74cac4992b18f7bc84 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Text; |
|||
|
|||
namespace LiteNetLib.Layers |
|||
{ |
|||
public class XorEncryptLayer : PacketLayerBase |
|||
{ |
|||
private byte[] _byteKey; |
|||
|
|||
public XorEncryptLayer() : base(0) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public XorEncryptLayer(byte[] key) : this() |
|||
{ |
|||
SetKey(key); |
|||
} |
|||
|
|||
public XorEncryptLayer(string key) : this() |
|||
{ |
|||
SetKey(key); |
|||
} |
|||
|
|||
public void SetKey(string key) |
|||
{ |
|||
_byteKey = Encoding.UTF8.GetBytes(key); |
|||
} |
|||
|
|||
public void SetKey(byte[] key) |
|||
{ |
|||
if (_byteKey == null || _byteKey.Length != key.Length) |
|||
_byteKey = new byte[key.Length]; |
|||
Buffer.BlockCopy(key, 0, _byteKey, 0, key.Length); |
|||
} |
|||
|
|||
public override void ProcessInboundPacket(ref byte[] data, ref int length) |
|||
{ |
|||
if (_byteKey == null) |
|||
return; |
|||
for (var i = 0; i < length; i++) |
|||
{ |
|||
var offset = i % _byteKey.Length; |
|||
data[i] = (byte)(data[i] ^ _byteKey[offset]); |
|||
} |
|||
} |
|||
|
|||
public override void ProcessOutBoundPacket(ref byte[] data, ref int offset, ref int length) |
|||
{ |
|||
if (_byteKey == null) |
|||
return; |
|||
var cur = offset; |
|||
for (var i = 0; i < length; i++, cur++) |
|||
{ |
|||
data[cur] = (byte)(data[cur] ^ _byteKey[i % _byteKey.Length]); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0aca7a11bf01a5146a4e435b448bf342 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 11c9a199da28e0144b82a7c9dbd440be |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using LiteNetLib.Utils; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
public enum NatAddressType |
|||
{ |
|||
Internal, |
|||
External |
|||
} |
|||
|
|||
public interface INatPunchListener |
|||
{ |
|||
void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); |
|||
void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token); |
|||
} |
|||
|
|||
public class EventBasedNatPunchListener : INatPunchListener |
|||
{ |
|||
public delegate void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); |
|||
public delegate void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token); |
|||
|
|||
public event OnNatIntroductionRequest NatIntroductionRequest; |
|||
public event OnNatIntroductionSuccess NatIntroductionSuccess; |
|||
|
|||
void INatPunchListener.OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token) |
|||
{ |
|||
if(NatIntroductionRequest != null) |
|||
NatIntroductionRequest(localEndPoint, remoteEndPoint, token); |
|||
} |
|||
|
|||
void INatPunchListener.OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token) |
|||
{ |
|||
if (NatIntroductionSuccess != null) |
|||
NatIntroductionSuccess(targetEndPoint, type, token); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Module for UDP NAT Hole punching operations. Can be accessed from NetManager
|
|||
/// </summary>
|
|||
public sealed class NatPunchModule |
|||
{ |
|||
struct RequestEventData |
|||
{ |
|||
public IPEndPoint LocalEndPoint; |
|||
public IPEndPoint RemoteEndPoint; |
|||
public string Token; |
|||
} |
|||
|
|||
struct SuccessEventData |
|||
{ |
|||
public IPEndPoint TargetEndPoint; |
|||
public NatAddressType Type; |
|||
public string Token; |
|||
} |
|||
|
|||
class NatIntroduceRequestPacket |
|||
{ |
|||
public IPEndPoint Internal { get; set; } |
|||
public string Token { get; set; } |
|||
} |
|||
|
|||
class NatIntroduceResponsePacket |
|||
{ |
|||
public IPEndPoint Internal { get; set; } |
|||
public IPEndPoint External { get; set; } |
|||
public string Token { get; set; } |
|||
} |
|||
|
|||
class NatPunchPacket |
|||
{ |
|||
public string Token { get; set; } |
|||
public bool IsExternal { get; set; } |
|||
} |
|||
|
|||
private readonly NetSocket _socket; |
|||
private readonly Queue<RequestEventData> _requestEvents = new Queue<RequestEventData>(); |
|||
private readonly Queue<SuccessEventData> _successEvents = new Queue<SuccessEventData>(); |
|||
private readonly NetDataReader _cacheReader = new NetDataReader(); |
|||
private readonly NetDataWriter _cacheWriter = new NetDataWriter(); |
|||
private readonly NetPacketProcessor _netPacketProcessor = new NetPacketProcessor(MaxTokenLength); |
|||
private INatPunchListener _natPunchListener; |
|||
public const int MaxTokenLength = 256; |
|||
|
|||
internal NatPunchModule(NetSocket socket) |
|||
{ |
|||
_socket = socket; |
|||
_netPacketProcessor.SubscribeReusable<NatIntroduceResponsePacket>(OnNatIntroductionResponse); |
|||
_netPacketProcessor.SubscribeReusable<NatIntroduceRequestPacket, IPEndPoint>(OnNatIntroductionRequest); |
|||
_netPacketProcessor.SubscribeReusable<NatPunchPacket, IPEndPoint>(OnNatPunch); |
|||
} |
|||
|
|||
internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet) |
|||
{ |
|||
lock (_cacheReader) |
|||
{ |
|||
_cacheReader.SetSource(packet.RawData, NetConstants.HeaderSize, packet.Size); |
|||
_netPacketProcessor.ReadAllPackets(_cacheReader, senderEndPoint); |
|||
} |
|||
} |
|||
|
|||
public void Init(INatPunchListener listener) |
|||
{ |
|||
_natPunchListener = listener; |
|||
} |
|||
|
|||
private void Send<T>(T packet, IPEndPoint target) where T : class, new() |
|||
{ |
|||
SocketError errorCode = 0; |
|||
_cacheWriter.Reset(); |
|||
_cacheWriter.Put((byte)PacketProperty.NatMessage); |
|||
_netPacketProcessor.Write(_cacheWriter, packet); |
|||
_socket.SendTo(_cacheWriter.Data, 0, _cacheWriter.Length, target, ref errorCode); |
|||
} |
|||
|
|||
public void NatIntroduce( |
|||
IPEndPoint hostInternal, |
|||
IPEndPoint hostExternal, |
|||
IPEndPoint clientInternal, |
|||
IPEndPoint clientExternal, |
|||
string additionalInfo) |
|||
{ |
|||
var req = new NatIntroduceResponsePacket |
|||
{ |
|||
Token = additionalInfo |
|||
}; |
|||
|
|||
//First packet (server) send to client
|
|||
req.Internal = hostInternal; |
|||
req.External = hostExternal; |
|||
Send(req, clientExternal); |
|||
|
|||
//Second packet (client) send to server
|
|||
req.Internal = clientInternal; |
|||
req.External = clientExternal; |
|||
Send(req, hostExternal); |
|||
} |
|||
|
|||
public void PollEvents() |
|||
{ |
|||
if (_natPunchListener == null || (_successEvents.Count == 0 && _requestEvents.Count == 0)) |
|||
return; |
|||
lock (_successEvents) |
|||
{ |
|||
while (_successEvents.Count > 0) |
|||
{ |
|||
var evt = _successEvents.Dequeue(); |
|||
_natPunchListener.OnNatIntroductionSuccess( |
|||
evt.TargetEndPoint, |
|||
evt.Type, |
|||
evt.Token); |
|||
} |
|||
} |
|||
lock (_requestEvents) |
|||
{ |
|||
while (_requestEvents.Count > 0) |
|||
{ |
|||
var evt = _requestEvents.Dequeue(); |
|||
_natPunchListener.OnNatIntroductionRequest(evt.LocalEndPoint, evt.RemoteEndPoint, evt.Token); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void SendNatIntroduceRequest(string host, int port, string additionalInfo) |
|||
{ |
|||
SendNatIntroduceRequest(NetUtils.MakeEndPoint(host, port), additionalInfo); |
|||
} |
|||
|
|||
public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo) |
|||
{ |
|||
//prepare outgoing data
|
|||
string networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv4); |
|||
if (string.IsNullOrEmpty(networkIp)) |
|||
{ |
|||
networkIp = NetUtils.GetLocalIp(LocalAddrType.IPv6); |
|||
} |
|||
|
|||
Send( |
|||
new NatIntroduceRequestPacket |
|||
{ |
|||
Internal = NetUtils.MakeEndPoint(networkIp, _socket.LocalPort), |
|||
Token = additionalInfo |
|||
}, |
|||
masterServerEndPoint); |
|||
} |
|||
|
|||
//We got request and must introduce
|
|||
private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint) |
|||
{ |
|||
lock (_requestEvents) |
|||
{ |
|||
_requestEvents.Enqueue(new RequestEventData |
|||
{ |
|||
LocalEndPoint = req.Internal, |
|||
RemoteEndPoint = senderEndPoint, |
|||
Token = req.Token |
|||
}); |
|||
} |
|||
} |
|||
|
|||
//We got introduce and must punch
|
|||
private void OnNatIntroductionResponse(NatIntroduceResponsePacket req) |
|||
{ |
|||
NetDebug.Write(NetLogLevel.Trace, "[NAT] introduction received"); |
|||
|
|||
// send internal punch
|
|||
var punchPacket = new NatPunchPacket {Token = req.Token}; |
|||
Send(punchPacket, req.Internal); |
|||
NetDebug.Write(NetLogLevel.Trace, "[NAT] internal punch sent to " + req.Internal); |
|||
|
|||
// hack for some routers
|
|||
SocketError errorCode = 0; |
|||
_socket.Ttl = 2; |
|||
_socket.SendTo(new[] { (byte)PacketProperty.Empty }, 0, 1, req.External, ref errorCode); |
|||
|
|||
// send external punch
|
|||
_socket.Ttl = NetConstants.SocketTTL; |
|||
punchPacket.IsExternal = true; |
|||
Send(punchPacket, req.External); |
|||
NetDebug.Write(NetLogLevel.Trace, "[NAT] external punch sent to " + req.External); |
|||
} |
|||
|
|||
//We got punch and can connect
|
|||
private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint) |
|||
{ |
|||
//Read info
|
|||
NetDebug.Write(NetLogLevel.Trace, "[NAT] punch received from {0} - additional info: {1}", |
|||
senderEndPoint, req.Token); |
|||
|
|||
//Release punch success to client; enabling him to Connect() to Sender if token is ok
|
|||
lock (_successEvents) |
|||
{ |
|||
_successEvents.Enqueue(new SuccessEventData |
|||
{ |
|||
TargetEndPoint = senderEndPoint, |
|||
Type = req.IsExternal ? NatAddressType.External : NatAddressType.Internal, |
|||
Token = req.Token |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: a7d2dee632c79624ea2b1909415f2755 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
namespace LiteNetLib |
|||
{ |
|||
/// <summary>
|
|||
/// Sending method type
|
|||
/// </summary>
|
|||
public enum DeliveryMethod : byte |
|||
{ |
|||
/// <summary>
|
|||
/// Unreliable. Packets can be dropped, can be duplicated, can arrive without order.
|
|||
/// </summary>
|
|||
Unreliable = 4, |
|||
|
|||
/// <summary>
|
|||
/// Reliable. Packets won't be dropped, won't be duplicated, can arrive without order.
|
|||
/// </summary>
|
|||
ReliableUnordered = 0, |
|||
|
|||
/// <summary>
|
|||
/// Unreliable. Packets can be dropped, won't be duplicated, will arrive in order.
|
|||
/// </summary>
|
|||
Sequenced = 1, |
|||
|
|||
/// <summary>
|
|||
/// Reliable and ordered. Packets won't be dropped, won't be duplicated, will arrive in order.
|
|||
/// </summary>
|
|||
ReliableOrdered = 2, |
|||
|
|||
/// <summary>
|
|||
/// Reliable only last packet. Packets can be dropped (except the last one), won't be duplicated, will arrive in order.
|
|||
/// </summary>
|
|||
ReliableSequenced = 3 |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Network constants. Can be tuned from sources for your purposes.
|
|||
/// </summary>
|
|||
public static class NetConstants |
|||
{ |
|||
//can be tuned
|
|||
public const int DefaultWindowSize = 64; |
|||
public const int SocketBufferSize = 1024 * 1024; //1mb
|
|||
public const int SocketTTL = 255; |
|||
|
|||
public const int HeaderSize = 1; |
|||
public const int ChanneledHeaderSize = 4; |
|||
public const int FragmentHeaderSize = 6; |
|||
public const int FragmentedHeaderTotalSize = ChanneledHeaderSize + FragmentHeaderSize; |
|||
public const ushort MaxSequence = 32768; |
|||
public const ushort HalfMaxSequence = MaxSequence / 2; |
|||
|
|||
//protocol
|
|||
internal const int ProtocolId = 11; |
|||
internal const int MaxUdpHeaderSize = 68; |
|||
|
|||
internal static readonly int[] PossibleMtu = |
|||
{ |
|||
576 - MaxUdpHeaderSize, //minimal
|
|||
1232 - MaxUdpHeaderSize, |
|||
1460 - MaxUdpHeaderSize, //google cloud
|
|||
1472 - MaxUdpHeaderSize, //VPN
|
|||
1492 - MaxUdpHeaderSize, //Ethernet with LLC and SNAP, PPPoE (RFC 1042)
|
|||
1500 - MaxUdpHeaderSize //Ethernet II (RFC 1191)
|
|||
}; |
|||
|
|||
internal static readonly int MaxPacketSize = PossibleMtu[PossibleMtu.Length - 1]; |
|||
|
|||
//peer specific
|
|||
public const byte MaxConnectionNumber = 4; |
|||
|
|||
public const int PacketPoolSize = 1000; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: fff3c8002deb1964b8adfb05e6d70f9f |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Diagnostics; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
public class InvalidPacketException : ArgumentException |
|||
{ |
|||
public InvalidPacketException(string message) : base(message) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
public class TooBigPacketException : InvalidPacketException |
|||
{ |
|||
public TooBigPacketException(string message) : base(message) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
public enum NetLogLevel |
|||
{ |
|||
Warning, |
|||
Error, |
|||
Trace, |
|||
Info |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Interface to implement for your own logger
|
|||
/// </summary>
|
|||
public interface INetLogger |
|||
{ |
|||
void WriteNet(NetLogLevel level, string str, params object[] args); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Static class for defining your own LiteNetLib logger instead of Console.WriteLine
|
|||
/// or Debug.Log if compiled with UNITY flag
|
|||
/// </summary>
|
|||
public static class NetDebug |
|||
{ |
|||
public static INetLogger Logger = null; |
|||
private static readonly object DebugLogLock = new object(); |
|||
private static void WriteLogic(NetLogLevel logLevel, string str, params object[] args) |
|||
{ |
|||
lock (DebugLogLock) |
|||
{ |
|||
if (Logger == null) |
|||
{ |
|||
#if UNITY_4 || UNITY_5 || UNITY_5_3_OR_NEWER
|
|||
UnityEngine.Debug.Log(string.Format(str, args)); |
|||
#else
|
|||
Console.WriteLine(str, args); |
|||
#endif
|
|||
} |
|||
else |
|||
{ |
|||
Logger.WriteNet(logLevel, str, args); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Conditional("DEBUG_MESSAGES")] |
|||
internal static void Write(string str, params object[] args) |
|||
{ |
|||
WriteLogic(NetLogLevel.Trace, str, args); |
|||
} |
|||
|
|||
[Conditional("DEBUG_MESSAGES")] |
|||
internal static void Write(NetLogLevel level, string str, params object[] args) |
|||
{ |
|||
WriteLogic(level, str, args); |
|||
} |
|||
|
|||
[Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")] |
|||
internal static void WriteForce(string str, params object[] args) |
|||
{ |
|||
WriteLogic(NetLogLevel.Trace, str, args); |
|||
} |
|||
|
|||
[Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")] |
|||
internal static void WriteForce(NetLogLevel level, string str, params object[] args) |
|||
{ |
|||
WriteLogic(level, str, args); |
|||
} |
|||
|
|||
internal static void WriteError(string str, params object[] args) |
|||
{ |
|||
WriteLogic(NetLogLevel.Error, str, args); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 164aa985149ec81468dbb9bed6d746f0 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetManager.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 68a7d55eeb581134cb7700df070236f7 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Net; |
|||
using LiteNetLib.Utils; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
internal enum PacketProperty : byte |
|||
{ |
|||
Unreliable, |
|||
Channeled, |
|||
Ack, |
|||
Ping, |
|||
Pong, |
|||
ConnectRequest, |
|||
ConnectAccept, |
|||
Disconnect, |
|||
UnconnectedMessage, |
|||
MtuCheck, |
|||
MtuOk, |
|||
Broadcast, |
|||
Merged, |
|||
ShutdownOk, |
|||
PeerNotFound, |
|||
InvalidProtocol, |
|||
NatMessage, |
|||
Empty |
|||
} |
|||
|
|||
internal sealed class NetPacket |
|||
{ |
|||
private static readonly int LastProperty = Enum.GetValues(typeof(PacketProperty)).Length; |
|||
private static readonly int[] HeaderSizes; |
|||
|
|||
static NetPacket() |
|||
{ |
|||
HeaderSizes = new int[LastProperty+1]; |
|||
for (int i = 0; i < HeaderSizes.Length; i++) |
|||
{ |
|||
switch ((PacketProperty)i) |
|||
{ |
|||
case PacketProperty.Channeled: |
|||
case PacketProperty.Ack: |
|||
HeaderSizes[i] = NetConstants.ChanneledHeaderSize; |
|||
break; |
|||
case PacketProperty.Ping: |
|||
HeaderSizes[i] = NetConstants.HeaderSize + 2; |
|||
break; |
|||
case PacketProperty.ConnectRequest: |
|||
HeaderSizes[i] = NetConnectRequestPacket.HeaderSize; |
|||
break; |
|||
case PacketProperty.ConnectAccept: |
|||
HeaderSizes[i] = NetConnectAcceptPacket.Size; |
|||
break; |
|||
case PacketProperty.Disconnect: |
|||
HeaderSizes[i] = NetConstants.HeaderSize + 8; |
|||
break; |
|||
case PacketProperty.Pong: |
|||
HeaderSizes[i] = NetConstants.HeaderSize + 10; |
|||
break; |
|||
default: |
|||
HeaderSizes[i] = NetConstants.HeaderSize; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
//Header
|
|||
public PacketProperty Property |
|||
{ |
|||
get { return (PacketProperty)(RawData[0] & 0x1F); } |
|||
set { RawData[0] = (byte)((RawData[0] & 0xE0) | (byte)value); } |
|||
} |
|||
|
|||
public byte ConnectionNumber |
|||
{ |
|||
get { return (byte)((RawData[0] & 0x60) >> 5); } |
|||
set { RawData[0] = (byte) ((RawData[0] & 0x9F) | (value << 5)); } |
|||
} |
|||
|
|||
public ushort Sequence |
|||
{ |
|||
get { return BitConverter.ToUInt16(RawData, 1); } |
|||
set { FastBitConverter.GetBytes(RawData, 1, value); } |
|||
} |
|||
|
|||
public bool IsFragmented |
|||
{ |
|||
get { return (RawData[0] & 0x80) != 0; } |
|||
} |
|||
|
|||
public void MarkFragmented() |
|||
{ |
|||
RawData[0] |= 0x80; //set first bit
|
|||
} |
|||
|
|||
public byte ChannelId |
|||
{ |
|||
get { return RawData[3]; } |
|||
set { RawData[3] = value; } |
|||
} |
|||
|
|||
public ushort FragmentId |
|||
{ |
|||
get { return BitConverter.ToUInt16(RawData, 4); } |
|||
set { FastBitConverter.GetBytes(RawData, 4, value); } |
|||
} |
|||
|
|||
public ushort FragmentPart |
|||
{ |
|||
get { return BitConverter.ToUInt16(RawData, 6); } |
|||
set { FastBitConverter.GetBytes(RawData, 6, value); } |
|||
} |
|||
|
|||
public ushort FragmentsTotal |
|||
{ |
|||
get { return BitConverter.ToUInt16(RawData, 8); } |
|||
set { FastBitConverter.GetBytes(RawData, 8, value); } |
|||
} |
|||
|
|||
//Data
|
|||
public byte[] RawData; |
|||
public int Size; |
|||
|
|||
//Delivery
|
|||
public object UserData; |
|||
|
|||
//Pool node
|
|||
public NetPacket Next; |
|||
|
|||
public NetPacket(int size) |
|||
{ |
|||
RawData = new byte[size]; |
|||
Size = size; |
|||
} |
|||
|
|||
public NetPacket(PacketProperty property, int size) |
|||
{ |
|||
size += GetHeaderSize(property); |
|||
RawData = new byte[size]; |
|||
Property = property; |
|||
Size = size; |
|||
} |
|||
|
|||
public static int GetHeaderSize(PacketProperty property) |
|||
{ |
|||
return HeaderSizes[(int)property]; |
|||
} |
|||
|
|||
public int GetHeaderSize() |
|||
{ |
|||
return HeaderSizes[RawData[0] & 0x1F]; |
|||
} |
|||
|
|||
//Packet constructor from byte array
|
|||
public bool FromBytes(byte[] data, int start, int packetSize) |
|||
{ |
|||
//Reading property
|
|||
byte property = (byte)(data[start] & 0x1F); |
|||
bool fragmented = (data[start] & 0x80) != 0; |
|||
int headerSize = HeaderSizes[property]; |
|||
|
|||
if (property > LastProperty || packetSize < headerSize || |
|||
(fragmented && packetSize < headerSize + NetConstants.FragmentHeaderSize) || |
|||
data.Length < start + packetSize) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
Buffer.BlockCopy(data, start, RawData, 0, packetSize); |
|||
Size = (ushort)packetSize; |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
internal sealed class NetConnectRequestPacket |
|||
{ |
|||
public const int HeaderSize = 14; |
|||
public readonly long ConnectionTime; |
|||
public readonly byte ConnectionNumber; |
|||
public readonly byte[] TargetAddress; |
|||
public readonly NetDataReader Data; |
|||
|
|||
private NetConnectRequestPacket(long connectionTime, byte connectionNumber, byte[] targetAddress, NetDataReader data) |
|||
{ |
|||
ConnectionTime = connectionTime; |
|||
ConnectionNumber = connectionNumber; |
|||
TargetAddress = targetAddress; |
|||
Data = data; |
|||
} |
|||
|
|||
public static int GetProtocolId(NetPacket packet) |
|||
{ |
|||
return BitConverter.ToInt32(packet.RawData, 1); |
|||
} |
|||
|
|||
public static NetConnectRequestPacket FromData(NetPacket packet) |
|||
{ |
|||
if (packet.ConnectionNumber >= NetConstants.MaxConnectionNumber) |
|||
return null; |
|||
|
|||
//Getting new id for peer
|
|||
long connectionId = BitConverter.ToInt64(packet.RawData, 5); |
|||
|
|||
//Get target address
|
|||
int addrSize = packet.RawData[13]; |
|||
if (addrSize != 16 && addrSize != 28) |
|||
return null; |
|||
byte[] addressBytes = new byte[addrSize]; |
|||
Buffer.BlockCopy(packet.RawData, 14, addressBytes, 0, addrSize); |
|||
|
|||
// Read data and create request
|
|||
var reader = new NetDataReader(null, 0, 0); |
|||
if (packet.Size > HeaderSize+addrSize) |
|||
reader.SetSource(packet.RawData, HeaderSize + addrSize, packet.Size); |
|||
|
|||
return new NetConnectRequestPacket(connectionId, packet.ConnectionNumber, addressBytes, reader); |
|||
} |
|||
|
|||
public static NetPacket Make(NetDataWriter connectData, SocketAddress addressBytes, long connectId) |
|||
{ |
|||
//Make initial packet
|
|||
var packet = new NetPacket(PacketProperty.ConnectRequest, connectData.Length+addressBytes.Size); |
|||
|
|||
//Add data
|
|||
FastBitConverter.GetBytes(packet.RawData, 1, NetConstants.ProtocolId); |
|||
FastBitConverter.GetBytes(packet.RawData, 5, connectId); |
|||
packet.RawData[13] = (byte)addressBytes.Size; |
|||
for (int i = 0; i < addressBytes.Size; i++) |
|||
packet.RawData[14+i] = addressBytes[i]; |
|||
Buffer.BlockCopy(connectData.Data, 0, packet.RawData, 14+addressBytes.Size, connectData.Length); |
|||
return packet; |
|||
} |
|||
} |
|||
|
|||
internal sealed class NetConnectAcceptPacket |
|||
{ |
|||
public const int Size = 11; |
|||
public readonly long ConnectionId; |
|||
public readonly byte ConnectionNumber; |
|||
public readonly bool IsReusedPeer; |
|||
|
|||
private NetConnectAcceptPacket(long connectionId, byte connectionNumber, bool isReusedPeer) |
|||
{ |
|||
ConnectionId = connectionId; |
|||
ConnectionNumber = connectionNumber; |
|||
IsReusedPeer = isReusedPeer; |
|||
} |
|||
|
|||
public static NetConnectAcceptPacket FromData(NetPacket packet) |
|||
{ |
|||
if (packet.Size > Size) |
|||
return null; |
|||
|
|||
long connectionId = BitConverter.ToInt64(packet.RawData, 1); |
|||
//check connect num
|
|||
byte connectionNumber = packet.RawData[9]; |
|||
if (connectionNumber >= NetConstants.MaxConnectionNumber) |
|||
return null; |
|||
//check reused flag
|
|||
byte isReused = packet.RawData[10]; |
|||
if (isReused > 1) |
|||
return null; |
|||
|
|||
return new NetConnectAcceptPacket(connectionId, connectionNumber, isReused == 1); |
|||
} |
|||
|
|||
public static NetPacket Make(long connectId, byte connectNum, bool reusedPeer) |
|||
{ |
|||
var packet = new NetPacket(PacketProperty.ConnectAccept, 0); |
|||
FastBitConverter.GetBytes(packet.RawData, 1, connectId); |
|||
packet.RawData[9] = connectNum; |
|||
packet.RawData[10] = (byte)(reusedPeer ? 1 : 0); |
|||
return packet; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 9bbfda42bbf0c64468c913e03e91b6f5 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Threading; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
internal sealed class NetPacketPool |
|||
{ |
|||
private NetPacket _head; |
|||
private int _count; |
|||
|
|||
public NetPacket GetWithData(PacketProperty property, byte[] data, int start, int length) |
|||
{ |
|||
int headerSize = NetPacket.GetHeaderSize(property); |
|||
NetPacket packet = GetPacket(length + headerSize); |
|||
packet.Property = property; |
|||
Buffer.BlockCopy(data, start, packet.RawData, headerSize, length); |
|||
return packet; |
|||
} |
|||
|
|||
//Get packet with size
|
|||
public NetPacket GetWithProperty(PacketProperty property, int size) |
|||
{ |
|||
NetPacket packet = GetPacket(size + NetPacket.GetHeaderSize(property)); |
|||
packet.Property = property; |
|||
return packet; |
|||
} |
|||
|
|||
public NetPacket GetWithProperty(PacketProperty property) |
|||
{ |
|||
NetPacket packet = GetPacket(NetPacket.GetHeaderSize(property)); |
|||
packet.Property = property; |
|||
return packet; |
|||
} |
|||
|
|||
public NetPacket GetPacket(int size) |
|||
{ |
|||
if (size > NetConstants.MaxPacketSize) |
|||
return new NetPacket(size); |
|||
|
|||
NetPacket packet; |
|||
do |
|||
{ |
|||
packet = _head; |
|||
if (packet == null) |
|||
return new NetPacket(size); |
|||
} while (packet != Interlocked.CompareExchange(ref _head, packet.Next, packet)); |
|||
|
|||
_count--; |
|||
packet.Size = size; |
|||
if (packet.RawData.Length < size) |
|||
packet.RawData = new byte[size]; |
|||
return packet; |
|||
} |
|||
|
|||
public void Recycle(NetPacket packet) |
|||
{ |
|||
if (packet.RawData.Length > NetConstants.MaxPacketSize || _count >= NetConstants.PacketPoolSize) |
|||
{ |
|||
//Don't pool big packets. Save memory
|
|||
return; |
|||
} |
|||
|
|||
_count++; |
|||
|
|||
//Clean fragmented flag
|
|||
packet.RawData[0] = 0; |
|||
|
|||
do |
|||
{ |
|||
packet.Next = _head; |
|||
} while (packet.Next != Interlocked.CompareExchange(ref _head, packet, packet.Next)); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: c9de9278957067a47b6fb32e6edcf6a8 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Packages/com.unity.multiplayer.transport.litenet/Runtime/LiteNetLib/NetPeer.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 7d8b1e2f1951cd64fa5f3ddaf623f006 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#if UNITY_5_3_OR_NEWER
|
|||
#define UNITY
|
|||
#if UNITY_IOS && !UNITY_EDITOR
|
|||
using UnityEngine; |
|||
#endif
|
|||
#endif
|
|||
#if NETSTANDARD || NETCOREAPP
|
|||
using System.Runtime.InteropServices; |
|||
#endif
|
|||
|
|||
using System; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using System.Threading; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
#if UNITY_IOS && !UNITY_EDITOR
|
|||
public class UnitySocketFix : MonoBehaviour |
|||
{ |
|||
internal IPAddress BindAddrIPv4; |
|||
internal IPAddress BindAddrIPv6; |
|||
internal bool Reuse; |
|||
internal IPv6Mode IPv6; |
|||
internal int Port; |
|||
internal bool Paused; |
|||
internal NetSocket Socket; |
|||
|
|||
private void Update() |
|||
{ |
|||
if (Socket == null) |
|||
Destroy(gameObject); |
|||
} |
|||
|
|||
private void OnApplicationPause(bool pause) |
|||
{ |
|||
if (Socket == null) |
|||
return; |
|||
if (pause) |
|||
{ |
|||
Paused = true; |
|||
Socket.Close(true); |
|||
} |
|||
else if (Paused) |
|||
{ |
|||
if (!Socket.Bind(BindAddrIPv4, BindAddrIPv6, Port, Reuse, IPv6)) |
|||
{ |
|||
NetDebug.WriteError("[S] Cannot restore connection \"{0}\",\"{1}\" port {2}", BindAddrIPv4, BindAddrIPv6, Port); |
|||
Socket.OnErrorRestore(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
|
|||
internal interface INetSocketListener |
|||
{ |
|||
void OnMessageReceived(byte[] data, int length, SocketError errorCode, IPEndPoint remoteEndPoint); |
|||
} |
|||
|
|||
internal sealed class NetSocket |
|||
{ |
|||
public const int ReceivePollingTime = 500000; //0.5 second
|
|||
private Socket _udpSocketv4; |
|||
private Socket _udpSocketv6; |
|||
private Thread _threadv4; |
|||
private Thread _threadv6; |
|||
private readonly INetSocketListener _listener; |
|||
private const int SioUdpConnreset = -1744830452; //SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12
|
|||
private static readonly IPAddress MulticastAddressV6 = IPAddress.Parse("ff02::1"); |
|||
internal static readonly bool IPv6Support; |
|||
#if UNITY_IOS && !UNITY_EDITOR
|
|||
private UnitySocketFix _unitySocketFix; |
|||
|
|||
public void OnErrorRestore() |
|||
{ |
|||
Close(false); |
|||
_listener.OnMessageReceived(null, 0, SocketError.NotConnected, new IPEndPoint(0,0)); |
|||
} |
|||
#endif
|
|||
public int LocalPort { get; private set; } |
|||
public volatile bool IsRunning; |
|||
|
|||
public short Ttl |
|||
{ |
|||
get |
|||
{ |
|||
#if UNITY_SWITCH
|
|||
return 0; |
|||
#else
|
|||
if (_udpSocketv4.AddressFamily == AddressFamily.InterNetworkV6) |
|||
return (short)_udpSocketv4.GetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.HopLimit); |
|||
return _udpSocketv4.Ttl; |
|||
#endif
|
|||
} |
|||
set |
|||
{ |
|||
#if !UNITY_SWITCH
|
|||
if (_udpSocketv4.AddressFamily == AddressFamily.InterNetworkV6) |
|||
_udpSocketv4.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.HopLimit, value); |
|||
else |
|||
_udpSocketv4.Ttl = value; |
|||
#endif
|
|||
} |
|||
} |
|||
|
|||
static NetSocket() |
|||
{ |
|||
#if DISABLE_IPV6 || (!UNITY_EDITOR && ENABLE_IL2CPP && !UNITY_2018_3_OR_NEWER)
|
|||
IPv6Support = false; |
|||
#elif !UNITY_2019_1_OR_NEWER && !UNITY_2018_4_OR_NEWER && (!UNITY_EDITOR && ENABLE_IL2CPP && UNITY_2018_3_OR_NEWER)
|
|||
string version = UnityEngine.Application.unityVersion; |
|||
IPv6Support = Socket.OSSupportsIPv6 && int.Parse(version.Remove(version.IndexOf('f')).Split('.')[2]) >= 6; |
|||
#elif UNITY_2018_2_OR_NEWER
|
|||
IPv6Support = Socket.OSSupportsIPv6; |
|||
#elif UNITY
|
|||
#pragma warning disable 618
|
|||
IPv6Support = Socket.SupportsIPv6; |
|||
#pragma warning restore 618
|
|||
#else
|
|||
IPv6Support = Socket.OSSupportsIPv6; |
|||
#endif
|
|||
} |
|||
|
|||
public NetSocket(INetSocketListener listener) |
|||
{ |
|||
_listener = listener; |
|||
} |
|||
|
|||
private bool IsActive() |
|||
{ |
|||
#if UNITY_IOS && !UNITY_EDITOR
|
|||
var unitySocketFix = _unitySocketFix; //save for multithread
|
|||
if (unitySocketFix != null && unitySocketFix.Paused) |
|||
return false; |
|||
#endif
|
|||
return IsRunning; |
|||
} |
|||
|
|||
private void ReceiveLogic(object state) |
|||
{ |
|||
Socket socket = (Socket)state; |
|||
EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0); |
|||
byte[] receiveBuffer = new byte[NetConstants.MaxPacketSize]; |
|||
|
|||
while (IsActive()) |
|||
{ |
|||
int result; |
|||
|
|||
//Reading data
|
|||
try |
|||
{ |
|||
if (socket.Available == 0 && !socket.Poll(ReceivePollingTime, SelectMode.SelectRead)) |
|||
continue; |
|||
result = socket.ReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, |
|||
ref bufferEndPoint); |
|||
} |
|||
catch (SocketException ex) |
|||
{ |
|||
switch (ex.SocketErrorCode) |
|||
{ |
|||
#if UNITY_IOS && !UNITY_EDITOR
|
|||
case SocketError.NotConnected: |
|||
#endif
|
|||
case SocketError.Interrupted: |
|||
case SocketError.NotSocket: |
|||
return; |
|||
case SocketError.ConnectionReset: |
|||
case SocketError.MessageSize: |
|||
case SocketError.TimedOut: |
|||
NetDebug.Write(NetLogLevel.Trace, "[R]Ignored error: {0} - {1}", |
|||
(int)ex.SocketErrorCode, ex.ToString()); |
|||
break; |
|||
default: |
|||
NetDebug.WriteError("[R]Error code: {0} - {1}", (int)ex.SocketErrorCode, |
|||
ex.ToString()); |
|||
_listener.OnMessageReceived(null, 0, ex.SocketErrorCode, (IPEndPoint)bufferEndPoint); |
|||
break; |
|||
} |
|||
continue; |
|||
} |
|||
catch (ObjectDisposedException) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
//All ok!
|
|||
NetDebug.Write(NetLogLevel.Trace, "[R]Received data from {0}, result: {1}", bufferEndPoint.ToString(), result); |
|||
_listener.OnMessageReceived(receiveBuffer, result, 0, (IPEndPoint)bufferEndPoint); |
|||
} |
|||
} |
|||
|
|||
public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool reuseAddress, IPv6Mode ipv6Mode) |
|||
{ |
|||
if (IsActive()) |
|||
return false; |
|||
bool dualMode = ipv6Mode == IPv6Mode.DualMode && IPv6Support; |
|||
|
|||
_udpSocketv4 = new Socket( |
|||
dualMode ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, |
|||
SocketType.Dgram, |
|||
ProtocolType.Udp); |
|||
|
|||
if (!BindSocket(_udpSocketv4, new IPEndPoint(dualMode ? addressIPv6 : addressIPv4, port), reuseAddress, ipv6Mode)) |
|||
return false; |
|||
|
|||
LocalPort = ((IPEndPoint) _udpSocketv4.LocalEndPoint).Port; |
|||
|
|||
#if UNITY_IOS && !UNITY_EDITOR
|
|||
if (_unitySocketFix == null) |
|||
{ |
|||
var unityFixObj = new GameObject("LiteNetLib_UnitySocketFix"); |
|||
GameObject.DontDestroyOnLoad(unityFixObj); |
|||
_unitySocketFix = unityFixObj.AddComponent<UnitySocketFix>(); |
|||
_unitySocketFix.Socket = this; |
|||
_unitySocketFix.BindAddrIPv4 = addressIPv4; |
|||
_unitySocketFix.BindAddrIPv6 = addressIPv6; |
|||
_unitySocketFix.Reuse = reuseAddress; |
|||
_unitySocketFix.Port = LocalPort; |
|||
_unitySocketFix.IPv6 = ipv6Mode; |
|||
} |
|||
else |
|||
{ |
|||
_unitySocketFix.Paused = false; |
|||
} |
|||
#endif
|
|||
if (dualMode) |
|||
_udpSocketv6 = _udpSocketv4; |
|||
|
|||
IsRunning = true; |
|||
_threadv4 = new Thread(ReceiveLogic) |
|||
{ |
|||
Name = "SocketThreadv4(" + LocalPort + ")", |
|||
IsBackground = true |
|||
}; |
|||
_threadv4.Start(_udpSocketv4); |
|||
|
|||
//Check IPv6 support
|
|||
if (!IPv6Support || ipv6Mode != IPv6Mode.SeparateSocket) |
|||
return true; |
|||
|
|||
_udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); |
|||
//Use one port for two sockets
|
|||
if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort), reuseAddress, ipv6Mode)) |
|||
{ |
|||
_threadv6 = new Thread(ReceiveLogic) |
|||
{ |
|||
Name = "SocketThreadv6(" + LocalPort + ")", |
|||
IsBackground = true |
|||
}; |
|||
_threadv6.Start(_udpSocketv6); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress, IPv6Mode ipv6Mode) |
|||
{ |
|||
//Setup socket
|
|||
socket.ReceiveTimeout = 500; |
|||
socket.SendTimeout = 500; |
|||
socket.ReceiveBufferSize = NetConstants.SocketBufferSize; |
|||
socket.SendBufferSize = NetConstants.SocketBufferSize; |
|||
#if !UNITY || UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
|
|||
#if NETSTANDARD || NETCOREAPP
|
|||
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
#endif
|
|||
try |
|||
{ |
|||
socket.IOControl(SioUdpConnreset, new byte[] { 0 }, null); |
|||
} |
|||
catch |
|||
{ |
|||
//ignored
|
|||
} |
|||
#endif
|
|||
|
|||
try |
|||
{ |
|||
socket.ExclusiveAddressUse = !reuseAddress; |
|||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuseAddress); |
|||
} |
|||
catch |
|||
{ |
|||
//Unity with IL2CPP throws an exception here, it doesn't matter in most cases so just ignore it
|
|||
} |
|||
if (socket.AddressFamily == AddressFamily.InterNetwork) |
|||
{ |
|||
Ttl = NetConstants.SocketTTL; |
|||
|
|||
#if NETSTANDARD || NETCOREAPP
|
|||
if(!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) |
|||
#endif
|
|||
try { socket.DontFragment = true; } |
|||
catch (SocketException e) |
|||
{ |
|||
NetDebug.WriteError("[B]DontFragment error: {0}", e.SocketErrorCode); |
|||
} |
|||
|
|||
try { socket.EnableBroadcast = true; } |
|||
catch (SocketException e) |
|||
{ |
|||
NetDebug.WriteError("[B]Broadcast error: {0}", e.SocketErrorCode); |
|||
} |
|||
} |
|||
else //IPv6 specific
|
|||
{ |
|||
if (ipv6Mode == IPv6Mode.DualMode) |
|||
{ |
|||
try |
|||
{ |
|||
//Disable IPv6 only mode
|
|||
socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false); |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
NetDebug.WriteError("[B]Bind exception (dualmode setting): {0}", e.ToString()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
//Bind
|
|||
try |
|||
{ |
|||
socket.Bind(ep); |
|||
NetDebug.Write(NetLogLevel.Trace, "[B]Successfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port); |
|||
|
|||
//join multicast
|
|||
if (socket.AddressFamily == AddressFamily.InterNetworkV6) |
|||
{ |
|||
try |
|||
{ |
|||
#if !UNITY
|
|||
socket.SetSocketOption( |
|||
SocketOptionLevel.IPv6, |
|||
SocketOptionName.AddMembership, |
|||
new IPv6MulticastOption(MulticastAddressV6)); |
|||
#endif
|
|||
} |
|||
catch (Exception) |
|||
{ |
|||
// Unity3d throws exception - ignored
|
|||
} |
|||
} |
|||
} |
|||
catch (SocketException bindException) |
|||
{ |
|||
switch (bindException.SocketErrorCode) |
|||
{ |
|||
//IPv6 bind fix
|
|||
case SocketError.AddressAlreadyInUse: |
|||
if (socket.AddressFamily == AddressFamily.InterNetworkV6 && ipv6Mode != IPv6Mode.DualMode) |
|||
{ |
|||
try |
|||
{ |
|||
//Set IPv6Only
|
|||
socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, true); |
|||
socket.Bind(ep); |
|||
} |
|||
#if UNITY_2018_3_OR_NEWER
|
|||
catch (SocketException ex) |
|||
{ |
|||
|
|||
//because its fixed in 2018_3
|
|||
NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", ex.ToString(), ex.SocketErrorCode); |
|||
#else
|
|||
catch(SocketException) |
|||
{ |
|||
#endif
|
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
break; |
|||
//hack for iOS (Unity3D)
|
|||
case SocketError.AddressFamilyNotSupported: |
|||
return true; |
|||
} |
|||
NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", bindException.ToString(), bindException.SocketErrorCode); |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
public bool SendBroadcast(byte[] data, int offset, int size, int port) |
|||
{ |
|||
if (!IsActive()) |
|||
return false; |
|||
bool broadcastSuccess = false; |
|||
bool multicastSuccess = false; |
|||
try |
|||
{ |
|||
broadcastSuccess = _udpSocketv4.SendTo( |
|||
data, |
|||
offset, |
|||
size, |
|||
SocketFlags.None, |
|||
new IPEndPoint(IPAddress.Broadcast, port)) > 0; |
|||
|
|||
if (_udpSocketv6 != null) |
|||
{ |
|||
multicastSuccess = _udpSocketv6.SendTo( |
|||
data, |
|||
offset, |
|||
size, |
|||
SocketFlags.None, |
|||
new IPEndPoint(MulticastAddressV6, port)) > 0; |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
NetDebug.WriteError("[S][MCAST]" + ex); |
|||
return broadcastSuccess; |
|||
} |
|||
return broadcastSuccess || multicastSuccess; |
|||
} |
|||
|
|||
public int SendTo(byte[] data, int offset, int size, IPEndPoint remoteEndPoint, ref SocketError errorCode) |
|||
{ |
|||
if (!IsActive()) |
|||
return 0; |
|||
try |
|||
{ |
|||
var socket = _udpSocketv4; |
|||
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support) |
|||
socket = _udpSocketv6; |
|||
int result = socket.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint); |
|||
NetDebug.Write(NetLogLevel.Trace, "[S]Send packet to {0}, result: {1}", remoteEndPoint, result); |
|||
return result; |
|||
} |
|||
catch (SocketException ex) |
|||
{ |
|||
switch (ex.SocketErrorCode) |
|||
{ |
|||
case SocketError.NoBufferSpaceAvailable: |
|||
case SocketError.Interrupted: |
|||
return 0; |
|||
case SocketError.MessageSize: //do nothing
|
|||
break; |
|||
default: |
|||
NetDebug.WriteError("[S]" + ex); |
|||
break; |
|||
} |
|||
errorCode = ex.SocketErrorCode; |
|||
return -1; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
NetDebug.WriteError("[S]" + ex); |
|||
return -1; |
|||
} |
|||
} |
|||
|
|||
public void Close(bool suspend) |
|||
{ |
|||
if (!suspend) |
|||
{ |
|||
IsRunning = false; |
|||
#if UNITY_IOS && !UNITY_EDITOR
|
|||
_unitySocketFix.Socket = null; |
|||
_unitySocketFix = null; |
|||
#endif
|
|||
} |
|||
//cleanup dual mode
|
|||
if (_udpSocketv4 == _udpSocketv6) |
|||
_udpSocketv6 = null; |
|||
|
|||
if (_udpSocketv4 != null) |
|||
_udpSocketv4.Close(); |
|||
if (_udpSocketv6 != null) |
|||
_udpSocketv6.Close(); |
|||
_udpSocketv4 = null; |
|||
_udpSocketv6 = null; |
|||
|
|||
if (_threadv4 != null && _threadv4 != Thread.CurrentThread) |
|||
_threadv4.Join(); |
|||
if (_threadv6 != null && _threadv6 != Thread.CurrentThread) |
|||
_threadv6.Join(); |
|||
_threadv4 = null; |
|||
_threadv6 = null; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 1dbea90b528ba0448a61b7222dab0657 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
namespace LiteNetLib |
|||
{ |
|||
public sealed class NetStatistics |
|||
{ |
|||
public ulong PacketsSent; |
|||
public ulong PacketsReceived; |
|||
public ulong BytesSent; |
|||
public ulong BytesReceived; |
|||
public ulong PacketLoss; |
|||
public ulong PacketLossPercent |
|||
{ |
|||
get { return PacketsSent == 0 ? 0 : PacketLoss * 100 / PacketsSent; } |
|||
} |
|||
|
|||
public ulong SequencedPacketLoss; |
|||
|
|||
public void Reset() |
|||
{ |
|||
PacketsSent = 0; |
|||
PacketsReceived = 0; |
|||
BytesSent = 0; |
|||
BytesReceived = 0; |
|||
PacketLoss = 0; |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return |
|||
string.Format( |
|||
"BytesReceived: {0}\nPacketsReceived: {1}\nBytesSent: {2}\nPacketsSent: {3}\nPacketLoss: {4}\nPacketLossPercent: {5}\n", |
|||
BytesReceived, |
|||
PacketsReceived, |
|||
BytesSent, |
|||
PacketsSent, |
|||
PacketLoss, |
|||
PacketLossPercent); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: bdf18575c3cbb17488b16fe4463e64c4 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using System.Net.NetworkInformation; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
/// <summary>
|
|||
/// Address type that you want to receive from NetUtils.GetLocalIp method
|
|||
/// </summary>
|
|||
[Flags] |
|||
public enum LocalAddrType |
|||
{ |
|||
IPv4 = 1, |
|||
IPv6 = 2, |
|||
All = IPv4 | IPv6 |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Some specific network utilities
|
|||
/// </summary>
|
|||
public static class NetUtils |
|||
{ |
|||
public static IPEndPoint MakeEndPoint(string hostStr, int port) |
|||
{ |
|||
return new IPEndPoint(ResolveAddress(hostStr), port); |
|||
} |
|||
|
|||
public static IPAddress ResolveAddress(string hostStr) |
|||
{ |
|||
if(hostStr == "localhost") |
|||
return IPAddress.Loopback; |
|||
|
|||
IPAddress ipAddress; |
|||
if (!IPAddress.TryParse(hostStr, out ipAddress)) |
|||
{ |
|||
if (NetSocket.IPv6Support) |
|||
ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetworkV6); |
|||
if (ipAddress == null) |
|||
ipAddress = ResolveAddress(hostStr, AddressFamily.InterNetwork); |
|||
} |
|||
if (ipAddress == null) |
|||
throw new ArgumentException("Invalid address: " + hostStr); |
|||
|
|||
return ipAddress; |
|||
} |
|||
|
|||
private static IPAddress ResolveAddress(string hostStr, AddressFamily addressFamily) |
|||
{ |
|||
IPAddress[] addresses = ResolveAddresses(hostStr); |
|||
foreach (IPAddress ip in addresses) |
|||
{ |
|||
if (ip.AddressFamily == addressFamily) |
|||
{ |
|||
return ip; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
private static IPAddress[] ResolveAddresses(string hostStr) |
|||
{ |
|||
#if NETSTANDARD || NETCOREAPP
|
|||
var hostTask = Dns.GetHostEntryAsync(hostStr); |
|||
hostTask.GetAwaiter().GetResult(); |
|||
var host = hostTask.Result; |
|||
#else
|
|||
var host = Dns.GetHostEntry(hostStr); |
|||
#endif
|
|||
return host.AddressList; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get all local ip addresses
|
|||
/// </summary>
|
|||
/// <param name="addrType">type of address (IPv4, IPv6 or both)</param>
|
|||
/// <returns>List with all local ip addresses</returns>
|
|||
public static List<string> GetLocalIpList(LocalAddrType addrType) |
|||
{ |
|||
List<string> targetList = new List<string>(); |
|||
GetLocalIpList(targetList, addrType); |
|||
return targetList; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get all local ip addresses (non alloc version)
|
|||
/// </summary>
|
|||
/// <param name="targetList">result list</param>
|
|||
/// <param name="addrType">type of address (IPv4, IPv6 or both)</param>
|
|||
public static void GetLocalIpList(IList<string> targetList, LocalAddrType addrType) |
|||
{ |
|||
bool ipv4 = (addrType & LocalAddrType.IPv4) == LocalAddrType.IPv4; |
|||
bool ipv6 = (addrType & LocalAddrType.IPv6) == LocalAddrType.IPv6; |
|||
try |
|||
{ |
|||
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) |
|||
{ |
|||
//Skip loopback and disabled network interfaces
|
|||
if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || |
|||
ni.OperationalStatus != OperationalStatus.Up) |
|||
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) |
|||
{ |
|||
IPAddress[] addresses = ResolveAddresses(Dns.GetHostName()); |
|||
foreach (IPAddress ip in addresses) |
|||
{ |
|||
if((ipv4 && ip.AddressFamily == AddressFamily.InterNetwork) || |
|||
(ipv6 && ip.AddressFamily == AddressFamily.InterNetworkV6)) |
|||
targetList.Add(ip.ToString()); |
|||
} |
|||
} |
|||
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>(); |
|||
/// <summary>
|
|||
/// Get first detected local ip address
|
|||
/// </summary>
|
|||
/// <param name="addrType">type of address (IPv4, IPv6 or both)</param>
|
|||
/// <returns>IP address if available. Else - string.Empty</returns>
|
|||
public static string GetLocalIp(LocalAddrType addrType) |
|||
{ |
|||
lock (IpList) |
|||
{ |
|||
IpList.Clear(); |
|||
GetLocalIpList(IpList, addrType); |
|||
return IpList.Count == 0 ? string.Empty : IpList[0]; |
|||
} |
|||
} |
|||
|
|||
// ===========================================
|
|||
// Internal and debug log related stuff
|
|||
// ===========================================
|
|||
internal static void PrintInterfaceInfos() |
|||
{ |
|||
NetDebug.WriteForce(NetLogLevel.Info, "IPv6Support: {0}", NetSocket.IPv6Support); |
|||
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) |
|||
{ |
|||
NetDebug.WriteForce( |
|||
NetLogLevel.Info, |
|||
"Interface: {0}, Type: {1}, Ip: {2}, OpStatus: {3}", |
|||
ni.Name, |
|||
ni.NetworkInterfaceType.ToString(), |
|||
ip.Address.ToString(), |
|||
ni.OperationalStatus.ToString()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
NetDebug.WriteForce(NetLogLevel.Info, "Error while getting interface infos: {0}", e.ToString()); |
|||
} |
|||
} |
|||
|
|||
internal static int RelativeSequenceNumber(int number, int expected) |
|||
{ |
|||
return (number - expected + NetConstants.MaxSequence + NetConstants.HalfMaxSequence) % NetConstants.MaxSequence - NetConstants.HalfMaxSequence; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 9ba71ed4d3dcdf747abc2f5fc7a2a6dd |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
internal sealed class ReliableChannel : BaseChannel |
|||
{ |
|||
private struct PendingPacket |
|||
{ |
|||
private NetPacket _packet; |
|||
private long _timeStamp; |
|||
private bool _isSent; |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return _packet == null ? "Empty" : _packet.Sequence.ToString(); |
|||
} |
|||
|
|||
public void Init(NetPacket packet) |
|||
{ |
|||
_packet = packet; |
|||
_isSent = false; |
|||
} |
|||
|
|||
public void TrySend(long currentTime, NetPeer peer) |
|||
{ |
|||
if (_packet == null) |
|||
return; |
|||
if (_isSent) //check send time
|
|||
{ |
|||
double resendDelay = peer.ResendDelay * TimeSpan.TicksPerMillisecond; |
|||
double packetHoldTime = currentTime - _timeStamp; |
|||
if (packetHoldTime < resendDelay) |
|||
return; |
|||
NetDebug.Write("[RC]Resend: {0} > {1}", (int)packetHoldTime, resendDelay); |
|||
} |
|||
_timeStamp = currentTime; |
|||
_isSent = true; |
|||
peer.SendUserData(_packet); |
|||
} |
|||
|
|||
public bool Clear(NetPeer peer) |
|||
{ |
|||
if (_packet != null) |
|||
{ |
|||
peer.RecycleAndDeliver(_packet); |
|||
_packet = null; |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
private readonly NetPacket _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 bool _mustSendAcks; |
|||
|
|||
private readonly DeliveryMethod _deliveryMethod; |
|||
private readonly bool _ordered; |
|||
private readonly int _windowSize; |
|||
private const int BitsInByte = 8; |
|||
private readonly byte _id; |
|||
|
|||
public ReliableChannel(NetPeer peer, bool ordered, byte id) : base(peer) |
|||
{ |
|||
_id = id; |
|||
_windowSize = NetConstants.DefaultWindowSize; |
|||
_ordered = ordered; |
|||
_pendingPackets = new PendingPacket[_windowSize]; |
|||
for (int i = 0; i < _pendingPackets.Length; i++) |
|||
_pendingPackets[i] = new PendingPacket(); |
|||
|
|||
if (_ordered) |
|||
{ |
|||
_deliveryMethod = DeliveryMethod.ReliableOrdered; |
|||
_receivedPackets = new NetPacket[_windowSize]; |
|||
} |
|||
else |
|||
{ |
|||
_deliveryMethod = DeliveryMethod.ReliableUnordered; |
|||
_earlyReceived = new bool[_windowSize]; |
|||
} |
|||
|
|||
_localWindowStart = 0; |
|||
_localSeqence = 0; |
|||
_remoteSequence = 0; |
|||
_remoteWindowStart = 0; |
|||
_outgoingAcks = new NetPacket(PacketProperty.Ack, (_windowSize - 1) / BitsInByte + 2) {ChannelId = id}; |
|||
} |
|||
|
|||
//ProcessAck in packet
|
|||
private void ProcessAck(NetPacket packet) |
|||
{ |
|||
if (packet.Size != _outgoingAcks.Size) |
|||
{ |
|||
NetDebug.Write("[PA]Invalid acks packet size"); |
|||
return; |
|||
} |
|||
|
|||
ushort ackWindowStart = packet.Sequence; |
|||
int windowRel = NetUtils.RelativeSequenceNumber(_localWindowStart, ackWindowStart); |
|||
if (ackWindowStart >= NetConstants.MaxSequence || windowRel < 0) |
|||
{ |
|||
NetDebug.Write("[PA]Bad window start"); |
|||
return; |
|||
} |
|||
|
|||
//check relevance
|
|||
if (windowRel >= _windowSize) |
|||
{ |
|||
NetDebug.Write("[PA]Old acks"); |
|||
return; |
|||
} |
|||
|
|||
byte[] acksData = packet.RawData; |
|||
lock (_pendingPackets) |
|||
{ |
|||
for (int pendingSeq = _localWindowStart; |
|||
pendingSeq != _localSeqence; |
|||
pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) |
|||
{ |
|||
int rel = NetUtils.RelativeSequenceNumber(pendingSeq, ackWindowStart); |
|||
if (rel >= _windowSize) |
|||
{ |
|||
NetDebug.Write("[PA]REL: " + rel); |
|||
break; |
|||
} |
|||
|
|||
int pendingIdx = pendingSeq % _windowSize; |
|||
int currentByte = NetConstants.ChanneledHeaderSize + pendingIdx / BitsInByte; |
|||
int currentBit = pendingIdx % BitsInByte; |
|||
if ((acksData[currentByte] & (1 << currentBit)) == 0) |
|||
{ |
|||
#if DEBUG
|
|||
Peer.Statistics.PacketLoss++; |
|||
#else
|
|||
if (Peer.NetManager.EnableStatistics) |
|||
{ |
|||
Peer.Statistics.PacketLoss++; |
|||
} |
|||
#endif
|
|||
|
|||
//Skip false ack
|
|||
NetDebug.Write("[PA]False ack: {0}", pendingSeq); |
|||
continue; |
|||
} |
|||
|
|||
if (pendingSeq == _localWindowStart) |
|||
{ |
|||
//Move window
|
|||
_localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence; |
|||
} |
|||
|
|||
//clear packet
|
|||
if (_pendingPackets[pendingIdx].Clear(Peer)) |
|||
NetDebug.Write("[PA]Removing reliableInOrder ack: {0} - true", pendingSeq); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public override void SendNextPackets() |
|||
{ |
|||
if (_mustSendAcks) |
|||
{ |
|||
_mustSendAcks = false; |
|||
NetDebug.Write("[RR]SendAcks"); |
|||
lock(_outgoingAcks) |
|||
Peer.SendUserData(_outgoingAcks); |
|||
} |
|||
|
|||
long currentTime = DateTime.UtcNow.Ticks; |
|||
lock (_pendingPackets) |
|||
{ |
|||
//get packets from queue
|
|||
lock (OutgoingQueue) |
|||
{ |
|||
while (OutgoingQueue.Count > 0) |
|||
{ |
|||
int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart); |
|||
if (relate >= _windowSize) |
|||
break; |
|||
|
|||
var netPacket = OutgoingQueue.Dequeue(); |
|||
netPacket.Sequence = (ushort) _localSeqence; |
|||
netPacket.ChannelId = _id; |
|||
_pendingPackets[_localSeqence % _windowSize].Init(netPacket); |
|||
_localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence; |
|||
} |
|||
} |
|||
//send
|
|||
for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) |
|||
_pendingPackets[pendingSeq % _windowSize].TrySend(currentTime, Peer); |
|||
} |
|||
} |
|||
|
|||
//Process incoming packet
|
|||
public override bool ProcessPacket(NetPacket packet) |
|||
{ |
|||
if (packet.Property == PacketProperty.Ack) |
|||
{ |
|||
ProcessAck(packet); |
|||
return false; |
|||
} |
|||
int seq = packet.Sequence; |
|||
if (seq >= NetConstants.MaxSequence) |
|||
{ |
|||
NetDebug.Write("[RR]Bad sequence"); |
|||
return false; |
|||
} |
|||
|
|||
int relate = NetUtils.RelativeSequenceNumber(seq, _remoteWindowStart); |
|||
int relateSeq = NetUtils.RelativeSequenceNumber(seq, _remoteSequence); |
|||
|
|||
if (relateSeq > _windowSize) |
|||
{ |
|||
NetDebug.Write("[RR]Bad sequence"); |
|||
return false; |
|||
} |
|||
|
|||
//Drop bad packets
|
|||
if (relate < 0) |
|||
{ |
|||
//Too old packet doesn't ack
|
|||
NetDebug.Write("[RR]ReliableInOrder too old"); |
|||
return false; |
|||
} |
|||
if (relate >= _windowSize * 2) |
|||
{ |
|||
//Some very new packet
|
|||
NetDebug.Write("[RR]ReliableInOrder too new"); |
|||
return false; |
|||
} |
|||
|
|||
//If very new - move window
|
|||
int ackIdx; |
|||
int ackByte; |
|||
int ackBit; |
|||
lock (_outgoingAcks) |
|||
{ |
|||
if (relate >= _windowSize) |
|||
{ |
|||
//New window position
|
|||
int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence; |
|||
_outgoingAcks.Sequence = (ushort) newWindowStart; |
|||
|
|||
//Clean old data
|
|||
while (_remoteWindowStart != newWindowStart) |
|||
{ |
|||
ackIdx = _remoteWindowStart % _windowSize; |
|||
ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte; |
|||
ackBit = ackIdx % BitsInByte; |
|||
_outgoingAcks.RawData[ackByte] &= (byte) ~(1 << ackBit); |
|||
_remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence; |
|||
} |
|||
} |
|||
|
|||
//Final stage - process valid packet
|
|||
//trigger acks send
|
|||
_mustSendAcks = true; |
|||
ackIdx = seq % _windowSize; |
|||
ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte; |
|||
ackBit = ackIdx % BitsInByte; |
|||
if ((_outgoingAcks.RawData[ackByte] & (1 << ackBit)) != 0) |
|||
{ |
|||
NetDebug.Write("[RR]ReliableInOrder duplicate"); |
|||
return false; |
|||
} |
|||
|
|||
//save ack
|
|||
_outgoingAcks.RawData[ackByte] |= (byte) (1 << ackBit); |
|||
} |
|||
|
|||
//detailed check
|
|||
if (seq == _remoteSequence) |
|||
{ |
|||
NetDebug.Write("[RR]ReliableInOrder packet succes"); |
|||
Peer.AddReliablePacket(_deliveryMethod, packet); |
|||
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; |
|||
|
|||
if (_ordered) |
|||
{ |
|||
NetPacket p; |
|||
while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null) |
|||
{ |
|||
//process holden packet
|
|||
_receivedPackets[_remoteSequence % _windowSize] = null; |
|||
Peer.AddReliablePacket(_deliveryMethod, p); |
|||
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
while (_earlyReceived[_remoteSequence % _windowSize]) |
|||
{ |
|||
//process early packet
|
|||
_earlyReceived[_remoteSequence % _windowSize] = false; |
|||
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
//holden packet
|
|||
if (_ordered) |
|||
{ |
|||
_receivedPackets[ackIdx] = packet; |
|||
} |
|||
else |
|||
{ |
|||
_earlyReceived[ackIdx] = true; |
|||
Peer.AddReliablePacket(_deliveryMethod, packet); |
|||
} |
|||
return true; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: c27d3fcb06848ee46b913dc9029ce2cd |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
|
|||
namespace LiteNetLib |
|||
{ |
|||
internal sealed class SequencedChannel : BaseChannel |
|||
{ |
|||
private int _localSequence; |
|||
private ushort _remoteSequence; |
|||
private readonly bool _reliable; |
|||
private NetPacket _lastPacket; |
|||
private readonly NetPacket _ackPacket; |
|||
private bool _mustSendAck; |
|||
private readonly byte _id; |
|||
private long _lastPacketSendTime; |
|||
|
|||
public SequencedChannel(NetPeer peer, bool reliable, byte id) : base(peer) |
|||
{ |
|||
_id = id; |
|||
_reliable = reliable; |
|||
if (_reliable) |
|||
_ackPacket = new NetPacket(PacketProperty.Ack, 0) {ChannelId = id}; |
|||
} |
|||
|
|||
public override void SendNextPackets() |
|||
{ |
|||
if (_reliable && OutgoingQueue.Count == 0) |
|||
{ |
|||
long currentTime = DateTime.UtcNow.Ticks; |
|||
long packetHoldTime = currentTime - _lastPacketSendTime; |
|||
if (packetHoldTime < Peer.ResendDelay * TimeSpan.TicksPerMillisecond) |
|||
return; |
|||
var packet = _lastPacket; |
|||
if (packet != null) |
|||
{ |
|||
_lastPacketSendTime = currentTime; |
|||
Peer.SendUserData(packet); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
lock (OutgoingQueue) |
|||
{ |
|||
while (OutgoingQueue.Count > 0) |
|||
{ |
|||
NetPacket packet = OutgoingQueue.Dequeue(); |
|||
_localSequence = (_localSequence + 1) % NetConstants.MaxSequence; |
|||
packet.Sequence = (ushort)_localSequence; |
|||
packet.ChannelId = _id; |
|||
Peer.SendUserData(packet); |
|||
|
|||
if (_reliable && OutgoingQueue.Count == 0) |
|||
{ |
|||
_lastPacketSendTime = DateTime.UtcNow.Ticks; |
|||
_lastPacket = packet; |
|||
} |
|||
else |
|||
{ |
|||
Peer.NetManager.NetPacketPool.Recycle(packet); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (_reliable && _mustSendAck) |
|||
{ |
|||
_mustSendAck = false; |
|||
_ackPacket.Sequence = _remoteSequence; |
|||
Peer.SendUserData(_ackPacket); |
|||
} |
|||
} |
|||
|
|||
public override bool ProcessPacket(NetPacket packet) |
|||
{ |
|||
if (packet.IsFragmented) |
|||
return false; |
|||
if (packet.Property == PacketProperty.Ack) |
|||
{ |
|||
if (_reliable && _lastPacket != null && packet.Sequence == _lastPacket.Sequence) |
|||
_lastPacket = null; |
|||
return false; |
|||
} |
|||
int relative = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence); |
|||
bool packetProcessed = false; |
|||
if (packet.Sequence < NetConstants.MaxSequence && relative > 0) |
|||
{ |
|||
Peer.Statistics.PacketLoss += (ulong)(relative - 1); |
|||
_remoteSequence = packet.Sequence; |
|||
Peer.NetManager.CreateReceiveEvent( |
|||
packet, |
|||
_reliable ? DeliveryMethod.ReliableSequenced : DeliveryMethod.Sequenced, |
|||
NetConstants.ChanneledHeaderSize, |
|||
Peer); |
|||
packetProcessed = true; |
|||
} |
|||
_mustSendAck = true; |
|||
return packetProcessed; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 80bbd04014ae8cb48ab6513dc3b32646 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: b42820447c8a8454e9f0fa10fa865fa2 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#if NETCOREAPP3_0
|
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics.X86; |
|||
#endif
|
|||
|
|||
namespace LiteNetLib.Utils |
|||
{ |
|||
//Implementation from Crc32.NET
|
|||
public static class CRC32C |
|||
{ |
|||
public const int ChecksumSize = 4; |
|||
private const uint Poly = 0x82F63B78u; |
|||
private static readonly uint[] Table; |
|||
|
|||
static CRC32C() |
|||
{ |
|||
#if NETCOREAPP3_0
|
|||
if(Sse42.IsSupported) |
|||
return; |
|||
#endif
|
|||
Table = new uint[16 * 256]; |
|||
for (uint i = 0; i < 256; i++) |
|||
{ |
|||
uint res = i; |
|||
for (int t = 0; t < 16; t++) |
|||
{ |
|||
for (int k = 0; k < 8; k++) |
|||
res = (res & 1) == 1 ? Poly ^ (res >> 1) : (res >> 1); |
|||
Table[t * 256 + i] = res; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compute CRC32C for data
|
|||
/// </summary>
|
|||
/// <param name="input">input data</param>
|
|||
/// <param name="offset">offset</param>
|
|||
/// <param name="length">length</param>
|
|||
/// <returns>CRC32C checksum</returns>
|
|||
public static uint Compute(byte[] input, int offset, int length) |
|||
{ |
|||
uint crcLocal = uint.MaxValue; |
|||
#if NETCOREAPP3_0
|
|||
if (Sse42.IsSupported) |
|||
{ |
|||
var data = new ReadOnlySpan<byte>(input, offset, length); |
|||
int processed = 0; |
|||
if (Sse42.X64.IsSupported && data.Length > sizeof(ulong)) |
|||
{ |
|||
processed = data.Length / sizeof(ulong) * sizeof(ulong); |
|||
var ulongs = MemoryMarshal.Cast<byte, ulong>(data.Slice(0, processed)); |
|||
ulong crclong = crcLocal; |
|||
for (int i = 0; i < ulongs.Length; i++) |
|||
{ |
|||
crclong = Sse42.X64.Crc32(crclong, ulongs[i]); |
|||
} |
|||
|
|||
crcLocal = (uint)crclong; |
|||
} |
|||
else if (data.Length > sizeof(uint)) |
|||
{ |
|||
processed = data.Length / sizeof(uint) * sizeof(uint); |
|||
var uints = MemoryMarshal.Cast<byte, uint>(data.Slice(0, processed)); |
|||
for (int i = 0; i < uints.Length; i++) |
|||
{ |
|||
crcLocal = Sse42.Crc32(crcLocal, uints[i]); |
|||
} |
|||
} |
|||
|
|||
for (int i = processed; i < data.Length; i++) |
|||
{ |
|||
crcLocal = Sse42.Crc32(crcLocal, data[i]); |
|||
} |
|||
|
|||
return crcLocal ^ uint.MaxValue; |
|||
} |
|||
#endif
|
|||
while (length >= 16) |
|||
{ |
|||
var a = Table[(3 * 256) + input[offset + 12]] |
|||
^ Table[(2 * 256) + input[offset + 13]] |
|||
^ Table[(1 * 256) + input[offset + 14]] |
|||
^ Table[(0 * 256) + input[offset + 15]]; |
|||
|
|||
var b = Table[(7 * 256) + input[offset + 8]] |
|||
^ Table[(6 * 256) + input[offset + 9]] |
|||
^ Table[(5 * 256) + input[offset + 10]] |
|||
^ Table[(4 * 256) + input[offset + 11]]; |
|||
|
|||
var c = Table[(11 * 256) + input[offset + 4]] |
|||
^ Table[(10 * 256) + input[offset + 5]] |
|||
^ Table[(9 * 256) + input[offset + 6]] |
|||
^ Table[(8 * 256) + input[offset + 7]]; |
|||
|
|||
var d = Table[(15 * 256) + ((byte)crcLocal ^ input[offset])] |
|||
^ Table[(14 * 256) + ((byte)(crcLocal >> 8) ^ input[offset + 1])] |
|||
^ Table[(13 * 256) + ((byte)(crcLocal >> 16) ^ input[offset + 2])] |
|||
^ Table[(12 * 256) + ((crcLocal >> 24) ^ input[offset + 3])]; |
|||
|
|||
crcLocal = d ^ c ^ b ^ a; |
|||
offset += 16; |
|||
length -= 16; |
|||
} |
|||
while (--length >= 0) |
|||
crcLocal = Table[(byte)(crcLocal ^ input[offset++])] ^ crcLocal >> 8; |
|||
return crcLocal ^ uint.MaxValue; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b9bc296b45361e44fb4b61c5c7f8ca43 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace 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); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: db75e586fc1fe264e9bc12949520676c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
namespace LiteNetLib.Utils |
|||
{ |
|||
public interface INetSerializable |
|||
{ |
|||
void Serialize(NetDataWriter writer); |
|||
void Deserialize(NetDataReader reader); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 067110f4d769ffe43959f64f7a32a178 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Net; |
|||
using System.Text; |
|||
|
|||
namespace LiteNetLib.Utils |
|||
{ |
|||
public class NetDataReader |
|||
{ |
|||
protected byte[] _data; |
|||
protected int _position; |
|||
protected int _dataSize; |
|||
private int _offset; |
|||
|
|||
public byte[] RawData |
|||
{ |
|||
get { return _data; } |
|||
} |
|||
|
|||
public int RawDataSize |
|||
{ |
|||
get { return _dataSize; } |
|||
} |
|||
|
|||
public int UserDataOffset |
|||
{ |
|||
get { return _offset; } |
|||
} |
|||
|
|||
public int UserDataSize |
|||
{ |
|||
get { return _dataSize - _offset; } |
|||
} |
|||
|
|||
public bool IsNull |
|||
{ |
|||
get { return _data == null; } |
|||
} |
|||
|
|||
public int Position |
|||
{ |
|||
get { return _position; } |
|||
} |
|||
|
|||
public bool EndOfData |
|||
{ |
|||
get { return _position == _dataSize; } |
|||
} |
|||
|
|||
public int AvailableBytes |
|||
{ |
|||
get { return _dataSize - _position; } |
|||
} |
|||
|
|||
public void SkipBytes(int count) |
|||
{ |
|||
_position += count; |
|||
} |
|||
|
|||
public void SetSource(NetDataWriter dataWriter) |
|||
{ |
|||
_data = dataWriter.Data; |
|||
_position = 0; |
|||
_offset = 0; |
|||
_dataSize = dataWriter.Length; |
|||
} |
|||
|
|||
public void SetSource(byte[] source) |
|||
{ |
|||
_data = source; |
|||
_position = 0; |
|||
_offset = 0; |
|||
_dataSize = source.Length; |
|||
} |
|||
|
|||
public void SetSource(byte[] source, int offset) |
|||
{ |
|||
_data = source; |
|||
_position = offset; |
|||
_offset = offset; |
|||
_dataSize = source.Length; |
|||
} |
|||
|
|||
public void SetSource(byte[] source, int offset, int maxSize) |
|||
{ |
|||
_data = source; |
|||
_position = offset; |
|||
_offset = offset; |
|||
_dataSize = maxSize; |
|||
} |
|||
|
|||
public NetDataReader() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public NetDataReader(NetDataWriter writer) |
|||
{ |
|||
SetSource(writer); |
|||
} |
|||
|
|||
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 IPEndPoint GetNetEndPoint() |
|||
{ |
|||
string host = GetString(1000); |
|||
int port = GetInt(); |
|||
return NetUtils.MakeEndPoint(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]; |
|||
Buffer.BlockCopy(_data, _position, arr, 0, size); |
|||
_position += size; |
|||
return arr; |
|||
} |
|||
|
|||
public ushort[] GetUShortArray() |
|||
{ |
|||
ushort size = BitConverter.ToUInt16(_data, _position); |
|||
_position += 2; |
|||
var arr = new ushort[size]; |
|||
Buffer.BlockCopy(_data, _position, arr, 0, size * 2); |
|||
_position += size * 2; |
|||
return arr; |
|||
} |
|||
|
|||
public short[] GetShortArray() |
|||
{ |
|||
ushort size = BitConverter.ToUInt16(_data, _position); |
|||
_position += 2; |
|||
var arr = new short[size]; |
|||
Buffer.BlockCopy(_data, _position, arr, 0, size * 2); |
|||
_position += size * 2; |
|||
return arr; |
|||
} |
|||
|
|||
public long[] GetLongArray() |
|||
{ |
|||
ushort size = BitConverter.ToUInt16(_data, _position); |
|||
_position += 2; |
|||
var arr = new long[size]; |
|||
Buffer.BlockCopy(_data, _position, arr, 0, size * 8); |
|||
_position += size * 8; |
|||
return arr; |
|||
} |
|||
|
|||
public ulong[] GetULongArray() |
|||
{ |
|||
ushort size = BitConverter.ToUInt16(_data, _position); |
|||
_position += 2; |
|||
var arr = new ulong[size]; |
|||
Buffer.BlockCopy(_data, _position, arr, 0, size * 8); |
|||
_position += size * 8; |
|||
return arr; |
|||
} |
|||
|
|||
public int[] GetIntArray() |
|||
{ |
|||
ushort size = BitConverter.ToUInt16(_data, _position); |
|||
_position += 2; |
|||
var arr = new int[size]; |
|||
Buffer.BlockCopy(_data, _position, arr, 0, size * 4); |
|||
_position += size * 4; |
|||
return arr; |
|||
} |
|||
|
|||
public uint[] GetUIntArray() |
|||
{ |
|||
ushort size = BitConverter.ToUInt16(_data, _position); |
|||
_position += 2; |
|||
var arr = new uint[size]; |
|||
Buffer.BlockCopy(_data, _position, arr, 0, size * 4); |
|||
_position += size * 4; |
|||
return arr; |
|||
} |
|||
|
|||
public float[] GetFloatArray() |
|||
{ |
|||
ushort size = BitConverter.ToUInt16(_data, _position); |
|||
_position += 2; |
|||
var arr = new float[size]; |
|||
Buffer.BlockCopy(_data, _position, arr, 0, size * 4); |
|||
_position += size * 4; |
|||
return arr; |
|||
} |
|||
|
|||
public double[] GetDoubleArray() |
|||
{ |
|||
ushort size = BitConverter.ToUInt16(_data, _position); |
|||
_position += 2; |
|||
var arr = new double[size]; |
|||
Buffer.BlockCopy(_data, _position, arr, 0, size * 8); |
|||
_position += size * 8; |
|||
return arr; |
|||
} |
|||
|
|||
public string[] GetStringArray() |
|||
{ |
|||
ushort size = BitConverter.ToUInt16(_data, _position); |
|||
_position += 2; |
|||
var arr = new string[size]; |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
arr[i] = GetString(); |
|||
} |
|||
return arr; |
|||
} |
|||
|
|||
public string[] GetStringArray(int maxStringLength) |
|||
{ |
|||
ushort size = BitConverter.ToUInt16(_data, _position); |
|||
_position += 2; |
|||
var arr = new string[size]; |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
arr[i] = GetString(maxStringLength); |
|||
} |
|||
return arr; |
|||
} |
|||
|
|||
public bool GetBool() |
|||
{ |
|||
bool res = _data[_position] > 0; |
|||
_position += 1; |
|||
return res; |
|||
} |
|||
|
|||
public char GetChar() |
|||
{ |
|||
char result = BitConverter.ToChar(_data, _position); |
|||
_position += 2; |
|||
return result; |
|||
} |
|||
|
|||
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 ArraySegment<byte> GetRemainingBytesSegment() |
|||
{ |
|||
ArraySegment<byte> segment = new ArraySegment<byte>(_data, _position, AvailableBytes); |
|||
_position = _data.Length; |
|||
return segment; |
|||
} |
|||
|
|||
public T Get<T>() where T : INetSerializable, new() |
|||
{ |
|||
var obj = new T(); |
|||
obj.Deserialize(this); |
|||
return obj; |
|||
} |
|||
|
|||
public byte[] GetRemainingBytes() |
|||
{ |
|||
byte[] outgoingData = new byte[AvailableBytes]; |
|||
Buffer.BlockCopy(_data, _position, outgoingData, 0, AvailableBytes); |
|||
_position = _data.Length; |
|||
return outgoingData; |
|||
} |
|||
|
|||
public void GetBytes(byte[] destination, int start, int count) |
|||
{ |
|||
Buffer.BlockCopy(_data, _position, destination, start, count); |
|||
_position += count; |
|||
} |
|||
|
|||
public void GetBytes(byte[] destination, int count) |
|||
{ |
|||
Buffer.BlockCopy(_data, _position, destination, 0, count); |
|||
_position += count; |
|||
} |
|||
|
|||
public sbyte[] GetSBytesWithLength() |
|||
{ |
|||
int length = GetInt(); |
|||
sbyte[] outgoingData = new sbyte[length]; |
|||
Buffer.BlockCopy(_data, _position, outgoingData, 0, length); |
|||
_position += length; |
|||
return outgoingData; |
|||
} |
|||
|
|||
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 char PeekChar() |
|||
{ |
|||
return BitConverter.ToChar(_data, _position); |
|||
} |
|||
|
|||
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
|
|||
|
|||
#region TryGetMethods
|
|||
public bool TryGetByte(out byte result) |
|||
{ |
|||
if (AvailableBytes >= 1) |
|||
{ |
|||
result = GetByte(); |
|||
return true; |
|||
} |
|||
result = 0; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetSByte(out sbyte result) |
|||
{ |
|||
if (AvailableBytes >= 1) |
|||
{ |
|||
result = GetSByte(); |
|||
return true; |
|||
} |
|||
result = 0; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetBool(out bool result) |
|||
{ |
|||
if (AvailableBytes >= 1) |
|||
{ |
|||
result = GetBool(); |
|||
return true; |
|||
} |
|||
result = false; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetChar(out char result) |
|||
{ |
|||
if (AvailableBytes >= 2) |
|||
{ |
|||
result = GetChar(); |
|||
return true; |
|||
} |
|||
result = '\0'; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetShort(out short result) |
|||
{ |
|||
if (AvailableBytes >= 2) |
|||
{ |
|||
result = GetShort(); |
|||
return true; |
|||
} |
|||
result = 0; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetUShort(out ushort result) |
|||
{ |
|||
if (AvailableBytes >= 2) |
|||
{ |
|||
result = GetUShort(); |
|||
return true; |
|||
} |
|||
result = 0; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetInt(out int result) |
|||
{ |
|||
if (AvailableBytes >= 4) |
|||
{ |
|||
result = GetInt(); |
|||
return true; |
|||
} |
|||
result = 0; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetUInt(out uint result) |
|||
{ |
|||
if (AvailableBytes >= 4) |
|||
{ |
|||
result = GetUInt(); |
|||
return true; |
|||
} |
|||
result = 0; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetLong(out long result) |
|||
{ |
|||
if (AvailableBytes >= 8) |
|||
{ |
|||
result = GetLong(); |
|||
return true; |
|||
} |
|||
result = 0; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetULong(out ulong result) |
|||
{ |
|||
if (AvailableBytes >= 8) |
|||
{ |
|||
result = GetULong(); |
|||
return true; |
|||
} |
|||
result = 0; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetFloat(out float result) |
|||
{ |
|||
if (AvailableBytes >= 4) |
|||
{ |
|||
result = GetFloat(); |
|||
return true; |
|||
} |
|||
result = 0; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetDouble(out double result) |
|||
{ |
|||
if (AvailableBytes >= 8) |
|||
{ |
|||
result = GetDouble(); |
|||
return true; |
|||
} |
|||
result = 0; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetString(out string result) |
|||
{ |
|||
if (AvailableBytes >= 4) |
|||
{ |
|||
var bytesCount = PeekInt(); |
|||
if (AvailableBytes >= bytesCount + 4) |
|||
{ |
|||
result = GetString(); |
|||
return true; |
|||
} |
|||
} |
|||
result = null; |
|||
return false; |
|||
} |
|||
|
|||
public bool TryGetStringArray(out string[] result) |
|||
{ |
|||
ushort size; |
|||
if (!TryGetUShort(out size)) |
|||
{ |
|||
result = null; |
|||
return false; |
|||
} |
|||
|
|||
result = new string[size]; |
|||
for (int i = 0; i < size; i++) |
|||
{ |
|||
if (!TryGetString(out result[i])) |
|||
{ |
|||
result = null; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
public bool TryGetBytesWithLength(out byte[] result) |
|||
{ |
|||
if (AvailableBytes >= 4) |
|||
{ |
|||
var length = PeekInt(); |
|||
if (length >= 0 && AvailableBytes >= length + 4) |
|||
{ |
|||
result = GetBytesWithLength(); |
|||
return true; |
|||
} |
|||
} |
|||
result = null; |
|||
return false; |
|||
} |
|||
#endregion
|
|||
|
|||
public void Clear() |
|||
{ |
|||
_position = 0; |
|||
_dataSize = 0; |
|||
_data = null; |
|||
} |
|||
} |
|||
} |
|||
|
|
|||
fileFormatVersion: 2 |
|||
guid: d531661040bfd9942b93adcf122d67a2 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Net; |
|||
using System.Text; |
|||
|
|||
namespace LiteNetLib.Utils |
|||
{ |
|||
public class NetDataWriter |
|||
{ |
|||
protected byte[] _data; |
|||
protected int _position; |
|||
private const int InitialSize = 64; |
|||
private readonly bool _autoResize; |
|||
|
|||
public int Capacity |
|||
{ |
|||
get { return _data.Length; } |
|||
} |
|||
|
|||
public NetDataWriter() : this(true, InitialSize) |
|||
{ |
|||
} |
|||
|
|||
public NetDataWriter(bool autoResize) : this(autoResize, InitialSize) |
|||
{ |
|||
} |
|||
|
|||
public NetDataWriter(bool autoResize, int initialSize) |
|||
{ |
|||
_data = new byte[initialSize]; |
|||
_autoResize = autoResize; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates NetDataWriter from existing ByteArray
|
|||
/// </summary>
|
|||
/// <param name="bytes">Source byte array</param>
|
|||
/// <param name="copy">Copy array to new location or use existing</param>
|
|||
public static NetDataWriter FromBytes(byte[] bytes, bool copy) |
|||
{ |
|||
if (copy) |
|||
{ |
|||
var netDataWriter = new NetDataWriter(true, bytes.Length); |
|||
netDataWriter.Put(bytes); |
|||
return netDataWriter; |
|||
} |
|||
return new NetDataWriter(true, 0) {_data = bytes}; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates NetDataWriter from existing ByteArray (always copied data)
|
|||
/// </summary>
|
|||
/// <param name="bytes">Source byte array</param>
|
|||
/// <param name="offset">Offset of array</param>
|
|||
/// <param name="length">Length of array</param>
|
|||
public static NetDataWriter FromBytes(byte[] bytes, int offset, int length) |
|||
{ |
|||
var netDataWriter = new NetDataWriter(true, bytes.Length); |
|||
netDataWriter.Put(bytes, offset, length); |
|||
return netDataWriter; |
|||
} |
|||
|
|||
public static NetDataWriter FromString(string value) |
|||
{ |
|||
var netDataWriter = new NetDataWriter(); |
|||
netDataWriter.Put(value); |
|||
return netDataWriter; |
|||
} |
|||
|
|||
public void ResizeIfNeed(int newSize) |
|||
{ |
|||
int len = _data.Length; |
|||
if (len < newSize) |
|||
{ |
|||
while (len < newSize) |
|||
len *= 2; |
|||
Array.Resize(ref _data, len); |
|||
} |
|||
} |
|||
|
|||
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(char value) |
|||
{ |
|||
if (_autoResize) |
|||
ResizeIfNeed(_position + 2); |
|||
FastBitConverter.GetBytes(_data, _position, value); |
|||
_position += 2; |
|||
} |
|||
|
|||
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 PutSBytesWithLength(sbyte[] data, int offset, int length) |
|||
{ |
|||
if (_autoResize) |
|||
ResizeIfNeed(_position + length + 4); |
|||
FastBitConverter.GetBytes(_data, _position, length); |
|||
Buffer.BlockCopy(data, offset, _data, _position + 4, length); |
|||
_position += length + 4; |
|||
} |
|||
|
|||
public void PutSBytesWithLength(sbyte[] data) |
|||
{ |
|||
if (_autoResize) |
|||
ResizeIfNeed(_position + data.Length + 4); |
|||
FastBitConverter.GetBytes(_data, _position, data.Length); |
|||
Buffer.BlockCopy(data, 0, _data, _position + 4, data.Length); |
|||
_position += data.Length + 4; |
|||
} |
|||
|
|||
public void PutBytesWithLength(byte[] data, int offset, int length) |
|||
{ |
|||
if (_autoResize) |
|||
ResizeIfNeed(_position + length + 4); |
|||
FastBitConverter.GetBytes(_data, _position, length); |
|||
Buffer.BlockCopy(data, offset, _data, _position + 4, length); |
|||
_position += length + 4; |
|||
} |
|||
|
|||
public void PutBytesWithLength(byte[] data) |
|||
{ |
|||
if (_autoResize) |
|||
ResizeIfNeed(_position + data.Length + 4); |
|||
FastBitConverter.GetBytes(_data, _position, data.Length); |
|||
Buffer.BlockCopy(data, 0, _data, _position + 4, data.Length); |
|||
_position += data.Length + 4; |
|||
} |
|||
|
|||
public void Put(bool value) |
|||
{ |
|||
if (_autoResize) |
|||
ResizeIfNeed(_position + 1); |
|||
_data[_position] = (byte)(value ? 1 : 0); |
|||
_position++; |
|||
} |
|||
|
|||
private void PutArray(Array arr, int sz) |
|||
{ |
|||
ushort length = arr == null ? (ushort) 0 : (ushort)arr.Length; |
|||
sz *= length; |
|||
if (_autoResize) |
|||
ResizeIfNeed(_position + sz + 2); |
|||
FastBitConverter.GetBytes(_data, _position, length); |
|||
if (arr != null) |
|||
Buffer.BlockCopy(arr, 0, _data, _position + 2, sz); |
|||
_position += sz + 2; |
|||
} |
|||
|
|||
public void PutArray(float[] value) |
|||
{ |
|||
PutArray(value, 4); |
|||
} |
|||
|
|||
public void PutArray(double[] value) |
|||
{ |
|||
PutArray(value, 8); |
|||
} |
|||
|
|||
public void PutArray(long[] value) |
|||
{ |
|||
PutArray(value, 8); |
|||
} |
|||
|
|||
public void PutArray(ulong[] value) |
|||
{ |
|||
PutArray(value, 8); |
|||
} |
|||
|
|||
public void PutArray(int[] value) |
|||
{ |
|||
PutArray(value, 4); |
|||
} |
|||
|
|||
public void PutArray(uint[] value) |
|||
{ |
|||
PutArray(value, 4); |
|||
} |
|||
|
|||
public void PutArray(ushort[] value) |
|||
{ |
|||
PutArray(value, 2); |
|||
} |
|||
|
|||
public void PutArray(short[] value) |
|||
{ |
|||
PutArray(value, 2); |
|||
} |
|||
|
|||
public void PutArray(bool[] value) |
|||
{ |
|||
PutArray(value, 1); |
|||
} |
|||
|
|||
public void PutArray(string[] value) |
|||
{ |
|||
ushort len = value == null ? (ushort)0 : (ushort)value.Length; |
|||
Put(len); |
|||
for (int i = 0; i < len; 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(IPEndPoint endPoint) |
|||
{ |
|||
Put(endPoint.Address.ToString()); |
|||
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; |
|||
} |
|||
|
|||
public void Put<T>(T obj) where T : INetSerializable |
|||
{ |
|||
obj.Serialize(this); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 69c77411d55e98941802ca35835dd3da |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LiteNetLib.Utils |
|||
{ |
|||
public class NetPacketProcessor |
|||
{ |
|||
private static class HashCache<T> |
|||
{ |
|||
public static bool Initialized; |
|||
public static ulong Id; |
|||
} |
|||
|
|||
protected delegate void SubscribeDelegate(NetDataReader reader, object userData); |
|||
private readonly NetSerializer _netSerializer; |
|||
private readonly Dictionary<ulong, SubscribeDelegate> _callbacks = new Dictionary<ulong, SubscribeDelegate>(); |
|||
private readonly NetDataWriter _netDataWriter = new NetDataWriter(); |
|||
|
|||
public NetPacketProcessor() |
|||
{ |
|||
_netSerializer = new NetSerializer(); |
|||
} |
|||
|
|||
public NetPacketProcessor(int maxStringLength) |
|||
{ |
|||
_netSerializer = new NetSerializer(maxStringLength); |
|||
} |
|||
|
|||
//FNV-1 64 bit hash
|
|||
protected virtual ulong GetHash<T>() |
|||
{ |
|||
if(HashCache<T>.Initialized) |
|||
return HashCache<T>.Id; |
|||
|
|||
ulong hash = 14695981039346656037UL; //offset
|
|||
string typeName = typeof(T).FullName; |
|||
for (var i = 0; i < typeName.Length; i++) |
|||
{ |
|||
hash = hash ^ typeName[i]; |
|||
hash *= 1099511628211UL; //prime
|
|||
} |
|||
HashCache<T>.Initialized = true; |
|||
HashCache<T>.Id = hash; |
|||
return hash; |
|||
} |
|||
|
|||
protected virtual SubscribeDelegate GetCallbackFromData(NetDataReader reader) |
|||
{ |
|||
var hash = reader.GetULong(); |
|||
SubscribeDelegate action; |
|||
if (!_callbacks.TryGetValue(hash, out action)) |
|||
{ |
|||
throw new ParseException("Undefined packet in NetDataReader"); |
|||
} |
|||
return action; |
|||
} |
|||
|
|||
protected virtual void WriteHash<T>(NetDataWriter writer) |
|||
{ |
|||
writer.Put(GetHash<T>()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Register nested property type
|
|||
/// </summary>
|
|||
/// <typeparam name="T">INetSerializable structure</typeparam>
|
|||
public void RegisterNestedType<T>() where T : struct, INetSerializable |
|||
{ |
|||
_netSerializer.RegisterNestedType<T>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Register nested property type
|
|||
/// </summary>
|
|||
/// <param name="writeDelegate"></param>
|
|||
/// <param name="readDelegate"></param>
|
|||
public void RegisterNestedType<T>(Action<NetDataWriter, T> writeDelegate, Func<NetDataReader, T> readDelegate) |
|||
{ |
|||
_netSerializer.RegisterNestedType<T>(writeDelegate, readDelegate); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Register nested property type
|
|||
/// </summary>
|
|||
/// <typeparam name="T">INetSerializable class</typeparam>
|
|||
public void RegisterNestedType<T>(Func<T> constructor) where T : class, INetSerializable |
|||
{ |
|||
_netSerializer.RegisterNestedType(constructor); |
|||
} |
|||
|
|||
/// <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>
|
|||
/// <exception cref="ParseException">Malformed packet</exception>
|
|||
public void ReadAllPackets(NetDataReader reader, object 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>
|
|||
/// <exception cref="ParseException">Malformed packet</exception>
|
|||
public void ReadPacket(NetDataReader reader) |
|||
{ |
|||
ReadPacket(reader, null); |
|||
} |
|||
|
|||
public void Send<T>(NetPeer peer, T packet, DeliveryMethod options) where T : class, new() |
|||
{ |
|||
_netDataWriter.Reset(); |
|||
Write(_netDataWriter, packet); |
|||
peer.Send(_netDataWriter, options); |
|||
} |
|||
|
|||
public void SendNetSerializable<T>(NetPeer peer, T packet, DeliveryMethod options) where T : INetSerializable |
|||
{ |
|||
_netDataWriter.Reset(); |
|||
WriteNetSerializable(_netDataWriter, packet); |
|||
peer.Send(_netDataWriter, options); |
|||
} |
|||
|
|||
public void Send<T>(NetManager manager, T packet, DeliveryMethod options) where T : class, new() |
|||
{ |
|||
_netDataWriter.Reset(); |
|||
Write(_netDataWriter, packet); |
|||
manager.SendToAll(_netDataWriter, options); |
|||
} |
|||
|
|||
public void SendNetSerializable<T>(NetManager manager, T packet, DeliveryMethod options) where T : INetSerializable |
|||
{ |
|||
_netDataWriter.Reset(); |
|||
WriteNetSerializable(_netDataWriter, packet); |
|||
manager.SendToAll(_netDataWriter, options); |
|||
} |
|||
|
|||
public void Write<T>(NetDataWriter writer, T packet) where T : class, new() |
|||
{ |
|||
WriteHash<T>(writer); |
|||
_netSerializer.Serialize(writer, packet); |
|||
} |
|||
|
|||
public void WriteNetSerializable<T>(NetDataWriter writer, T packet) where T : INetSerializable |
|||
{ |
|||
WriteHash<T>(writer); |
|||
packet.Serialize(writer); |
|||
} |
|||
|
|||
public byte[] Write<T>(T packet) where T : class, new() |
|||
{ |
|||
_netDataWriter.Reset(); |
|||
WriteHash<T>(_netDataWriter); |
|||
_netSerializer.Serialize(_netDataWriter, packet); |
|||
return _netDataWriter.CopyData(); |
|||
} |
|||
|
|||
public byte[] WriteNetSerializable<T>(T packet) where T : INetSerializable |
|||
{ |
|||
_netDataWriter.Reset(); |
|||
WriteHash<T>(_netDataWriter); |
|||
packet.Serialize(_netDataWriter); |
|||
return _netDataWriter.CopyData(); |
|||
} |
|||
|
|||
/// <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>
|
|||
/// <exception cref="ParseException">Malformed packet</exception>
|
|||
public void ReadPacket(NetDataReader reader, object userData) |
|||
{ |
|||
GetCallbackFromData(reader)(reader, 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 instead of slow Activator.CreateInstance</param>
|
|||
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
|
|||
public void Subscribe<T>(Action<T> onReceive, Func<T> packetConstructor) where T : class, new() |
|||
{ |
|||
_netSerializer.Register<T>(); |
|||
_callbacks[GetHash<T>()] = (reader, userData) => |
|||
{ |
|||
var reference = packetConstructor(); |
|||
_netSerializer.Deserialize(reader, reference); |
|||
onReceive(reference); |
|||
}; |
|||
} |
|||
|
|||
/// <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 instead of slow Activator.CreateInstance</param>
|
|||
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
|
|||
public void Subscribe<T, TUserData>(Action<T, TUserData> onReceive, Func<T> packetConstructor) where T : class, new() |
|||
{ |
|||
_netSerializer.Register<T>(); |
|||
_callbacks[GetHash<T>()] = (reader, userData) => |
|||
{ |
|||
var reference = packetConstructor(); |
|||
_netSerializer.Deserialize(reader, reference); |
|||
onReceive(reference, (TUserData)userData); |
|||
}; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Register and subscribe to packet receive event
|
|||
/// This method 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>
|
|||
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
|
|||
public void SubscribeReusable<T>(Action<T> onReceive) where T : class, new() |
|||
{ |
|||
_netSerializer.Register<T>(); |
|||
var reference = new T(); |
|||
_callbacks[GetHash<T>()] = (reader, userData) => |
|||
{ |
|||
_netSerializer.Deserialize(reader, reference); |
|||
onReceive(reference); |
|||
}; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Register and subscribe to packet receive event
|
|||
/// This method 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>
|
|||
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
|
|||
public void SubscribeReusable<T, TUserData>(Action<T, TUserData> onReceive) where T : class, new() |
|||
{ |
|||
_netSerializer.Register<T>(); |
|||
var reference = new T(); |
|||
_callbacks[GetHash<T>()] = (reader, userData) => |
|||
{ |
|||
_netSerializer.Deserialize(reader, reference); |
|||
onReceive(reference, (TUserData)userData); |
|||
}; |
|||
} |
|||
|
|||
public void SubscribeNetSerializable<T, TUserData>( |
|||
Action<T, TUserData> onReceive, |
|||
Func<T> packetConstructor) where T : INetSerializable |
|||
{ |
|||
_callbacks[GetHash<T>()] = (reader, userData) => |
|||
{ |
|||
var pkt = packetConstructor(); |
|||
pkt.Deserialize(reader); |
|||
onReceive(pkt, (TUserData)userData); |
|||
}; |
|||
} |
|||
|
|||
public void SubscribeNetSerializable<T>( |
|||
Action<T> onReceive, |
|||
Func<T> packetConstructor) where T : INetSerializable |
|||
{ |
|||
_callbacks[GetHash<T>()] = (reader, userData) => |
|||
{ |
|||
var pkt = packetConstructor(); |
|||
pkt.Deserialize(reader); |
|||
onReceive(pkt); |
|||
}; |
|||
} |
|||
|
|||
public void SubscribeNetSerializable<T, TUserData>( |
|||
Action<T, TUserData> onReceive) where T : INetSerializable, new() |
|||
{ |
|||
var reference = new T(); |
|||
_callbacks[GetHash<T>()] = (reader, userData) => |
|||
{ |
|||
reference.Deserialize(reader); |
|||
onReceive(reference, (TUserData)userData); |
|||
}; |
|||
} |
|||
|
|||
public void SubscribeNetSerializable<T>( |
|||
Action<T> onReceive) where T : INetSerializable, new() |
|||
{ |
|||
var reference = new T(); |
|||
_callbacks[GetHash<T>()] = (reader, userData) => |
|||
{ |
|||
reference.Deserialize(reader); |
|||
onReceive(reference); |
|||
}; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Remove any subscriptions by type
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Packet type</typeparam>
|
|||
/// <returns>true if remove is success</returns>
|
|||
public bool RemoveSubscription<T>() |
|||
{ |
|||
return _callbacks.Remove(GetHash<T>()); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 9b0c0e8aa2c23504fbc252cd239cddbc |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Reflection; |
|||
using System.Collections.Generic; |
|||
using System.Net; |
|||
|
|||
namespace LiteNetLib.Utils |
|||
{ |
|||
public class InvalidTypeException : ArgumentException |
|||
{ |
|||
public InvalidTypeException(string message) : base(message) { } |
|||
} |
|||
|
|||
public class ParseException : Exception |
|||
{ |
|||
public ParseException(string message) : base(message) { } |
|||
} |
|||
|
|||
public class NetSerializer |
|||
{ |
|||
private enum CallType |
|||
{ |
|||
Basic, |
|||
Array, |
|||
List |
|||
} |
|||
|
|||
private abstract class FastCall<T> |
|||
{ |
|||
public CallType Type; |
|||
public virtual void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) { Type = type; } |
|||
public abstract void Read(T inf, NetDataReader r); |
|||
public abstract void Write(T inf, NetDataWriter w); |
|||
public abstract void ReadArray(T inf, NetDataReader r); |
|||
public abstract void WriteArray(T inf, NetDataWriter w); |
|||
public abstract void ReadList(T inf, NetDataReader r); |
|||
public abstract void WriteList(T inf, NetDataWriter w); |
|||
} |
|||
|
|||
private abstract class FastCallSpecific<TClass, TProperty> : FastCall<TClass> |
|||
{ |
|||
protected Func<TClass, TProperty> Getter; |
|||
protected Action<TClass, TProperty> Setter; |
|||
protected Func<TClass, TProperty[]> GetterArr; |
|||
protected Action<TClass, TProperty[]> SetterArr; |
|||
protected Func<TClass, List<TProperty>> GetterList; |
|||
protected Action<TClass, List<TProperty>> SetterList; |
|||
|
|||
public override void ReadArray(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); } |
|||
public override void WriteArray(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: " + typeof(TProperty) + "[]"); } |
|||
public override void ReadList(TClass inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); } |
|||
public override void WriteList(TClass inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List<" + typeof(TProperty) + ">"); } |
|||
|
|||
protected TProperty[] ReadArrayHelper(TClass inf, NetDataReader r) |
|||
{ |
|||
ushort count = r.GetUShort(); |
|||
var arr = GetterArr(inf); |
|||
arr = arr == null || arr.Length != count ? new TProperty[count] : arr; |
|||
SetterArr(inf, arr); |
|||
return arr; |
|||
} |
|||
|
|||
protected TProperty[] WriteArrayHelper(TClass inf, NetDataWriter w) |
|||
{ |
|||
var arr = GetterArr(inf); |
|||
w.Put((ushort)arr.Length); |
|||
return arr; |
|||
} |
|||
|
|||
protected List<TProperty> ReadListHelper(TClass inf, NetDataReader r, out int len) |
|||
{ |
|||
len = r.GetUShort(); |
|||
var list = GetterList(inf); |
|||
if (list == null) |
|||
{ |
|||
list = new List<TProperty>(len); |
|||
SetterList(inf, list); |
|||
} |
|||
return list; |
|||
} |
|||
|
|||
protected List<TProperty> WriteListHelper(TClass inf, NetDataWriter w, out int len) |
|||
{ |
|||
var list = GetterList(inf); |
|||
if (list == null) |
|||
{ |
|||
len = 0; |
|||
w.Put(0); |
|||
return null; |
|||
} |
|||
len = list.Count; |
|||
w.Put((ushort)len); |
|||
return list; |
|||
} |
|||
|
|||
public override void Init(MethodInfo getMethod, MethodInfo setMethod, CallType type) |
|||
{ |
|||
base.Init(getMethod, setMethod, type); |
|||
switch (type) |
|||
{ |
|||
case CallType.Array: |
|||
GetterArr = (Func<TClass, TProperty[]>)Delegate.CreateDelegate(typeof(Func<TClass, TProperty[]>), getMethod); |
|||
SetterArr = (Action<TClass, TProperty[]>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty[]>), setMethod); |
|||
break; |
|||
case CallType.List: |
|||
GetterList = (Func<TClass, List<TProperty>>)Delegate.CreateDelegate(typeof(Func<TClass, List<TProperty>>), getMethod); |
|||
SetterList = (Action<TClass, List<TProperty>>)Delegate.CreateDelegate(typeof(Action<TClass, List<TProperty>>), setMethod); |
|||
break; |
|||
default: |
|||
Getter = (Func<TClass, TProperty>)Delegate.CreateDelegate(typeof(Func<TClass, TProperty>), getMethod); |
|||
Setter = (Action<TClass, TProperty>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty>), setMethod); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private abstract class FastCallSpecificAuto<TClass, TProperty> : FastCallSpecific<TClass, TProperty> |
|||
{ |
|||
protected abstract void ElementRead(NetDataReader r, out TProperty prop); |
|||
protected abstract void ElementWrite(NetDataWriter w, ref TProperty prop); |
|||
|
|||
public override void Read(TClass inf, NetDataReader r) |
|||
{ |
|||
TProperty elem; |
|||
ElementRead(r, out elem); |
|||
Setter(inf, elem); |
|||
} |
|||
|
|||
public override void Write(TClass inf, NetDataWriter w) |
|||
{ |
|||
var elem = Getter(inf); |
|||
ElementWrite(w, ref elem); |
|||
} |
|||
|
|||
public override void ReadArray(TClass inf, NetDataReader r) |
|||
{ |
|||
var arr = ReadArrayHelper(inf, r); |
|||
for (int i = 0; i < arr.Length; i++) |
|||
ElementRead(r, out arr[i]); |
|||
} |
|||
|
|||
public override void WriteArray(TClass inf, NetDataWriter w) |
|||
{ |
|||
var arr = WriteArrayHelper(inf, w); |
|||
for (int i = 0; i < arr.Length; i++) |
|||
ElementWrite(w, ref arr[i]); |
|||
} |
|||
} |
|||
|
|||
private sealed class FastCallStatic<TClass, TProperty> : FastCallSpecific<TClass, TProperty> |
|||
{ |
|||
private readonly Action<NetDataWriter, TProperty> _writer; |
|||
private readonly Func<NetDataReader, TProperty> _reader; |
|||
|
|||
public FastCallStatic(Action<NetDataWriter, TProperty> write, Func<NetDataReader, TProperty> read) |
|||
{ |
|||
_writer = write; |
|||
_reader = read; |
|||
} |
|||
|
|||
public override void Read(TClass inf, NetDataReader r) { Setter(inf, _reader(r)); } |
|||
public override void Write(TClass inf, NetDataWriter w) { _writer(w, Getter(inf)); } |
|||
|
|||
public override void ReadList(TClass inf, NetDataReader r) |
|||
{ |
|||
int len; |
|||
var list = ReadListHelper(inf, r, out len); |
|||
int listCount = list.Count; |
|||
if (len > listCount) |
|||
{ |
|||
for (int i = 0; i < listCount; i++) |
|||
list[i] = _reader(r); |
|||
for (int i = listCount; i < len; i++) |
|||
list.Add(_reader(r)); |
|||
return; |
|||
} |
|||
if (len < listCount) |
|||
list.RemoveRange(len, listCount - len); |
|||
for (int i = 0; i < len; i++) |
|||
list[i] = _reader(r); |
|||
} |
|||
|
|||
public override void WriteList(TClass inf, NetDataWriter w) |
|||
{ |
|||
int len; |
|||
var list = WriteListHelper(inf, w, out len); |
|||
for (int i = 0; i < len; i++) |
|||
_writer(w, list[i]); |
|||
} |
|||
|
|||
public override void ReadArray(TClass inf, NetDataReader r) |
|||
{ |
|||
var arr = ReadArrayHelper(inf, r); |
|||
int len = arr.Length; |
|||
for (int i = 0; i < len; i++) |
|||
arr[i] = _reader(r); |
|||
} |
|||
|
|||
public override void WriteArray(TClass inf, NetDataWriter w) |
|||
{ |
|||
var arr = WriteArrayHelper(inf, w); |
|||
int len = arr.Length; |
|||
for (int i = 0; i < len; i++) |
|||
_writer(w, arr[i]); |
|||
} |
|||
} |
|||
|
|||
private sealed class FastCallStruct<TClass, TProperty> : FastCallSpecific<TClass, TProperty> where TProperty : struct, INetSerializable |
|||
{ |
|||
private TProperty _p; |
|||
|
|||
public override void Read(TClass inf, NetDataReader r) |
|||
{ |
|||
_p.Deserialize(r); |
|||
Setter(inf, _p); |
|||
} |
|||
|
|||
public override void Write(TClass inf, NetDataWriter w) |
|||
{ |
|||
_p = Getter(inf); |
|||
_p.Serialize(w); |
|||
} |
|||
|
|||
public override void ReadList(TClass inf, NetDataReader r) |
|||
{ |
|||
int len; |
|||
var list = ReadListHelper(inf, r, out len); |
|||
int listCount = list.Count; |
|||
if (len > listCount) |
|||
{ |
|||
for (int i = 0; i < listCount; i++) |
|||
list[i].Deserialize(r); |
|||
for (int i = listCount; i < len; i++) |
|||
{ |
|||
var itm = default(TProperty); |
|||
itm.Deserialize(r); |
|||
list.Add(itm); |
|||
} |
|||
return; |
|||
} |
|||
if(len < listCount) |
|||
list.RemoveRange(len, listCount - len); |
|||
for (int i = 0; i < len; i++) |
|||
list[i].Deserialize(r); |
|||
} |
|||
|
|||
public override void WriteList(TClass inf, NetDataWriter w) |
|||
{ |
|||
int len; |
|||
var list = WriteListHelper(inf, w, out len); |
|||
for (int i = 0; i < len; i++) |
|||
list[i].Serialize(w); |
|||
} |
|||
|
|||
public override void ReadArray(TClass inf, NetDataReader r) |
|||
{ |
|||
var arr = ReadArrayHelper(inf, r); |
|||
int len = arr.Length; |
|||
for (int i = 0; i < len; i++) |
|||
arr[i].Deserialize(r); |
|||
} |
|||
|
|||
public override void WriteArray(TClass inf, NetDataWriter w) |
|||
{ |
|||
var arr = WriteArrayHelper(inf, w); |
|||
int len = arr.Length; |
|||
for (int i = 0; i < len; i++) |
|||
arr[i].Serialize(w); |
|||
} |
|||
} |
|||
|
|||
private sealed class FastCallClass<TClass, TProperty> : FastCallSpecific<TClass, TProperty> where TProperty : class, INetSerializable |
|||
{ |
|||
private readonly Func<TProperty> _constructor; |
|||
public FastCallClass(Func<TProperty> constructor) { _constructor = constructor; } |
|||
|
|||
public override void Read(TClass inf, NetDataReader r) |
|||
{ |
|||
var p = _constructor(); |
|||
p.Deserialize(r); |
|||
Setter(inf, p); |
|||
} |
|||
|
|||
public override void Write(TClass inf, NetDataWriter w) |
|||
{ |
|||
var p = Getter(inf); |
|||
if(p != null) |
|||
p.Serialize(w); |
|||
} |
|||
|
|||
public override void ReadList(TClass inf, NetDataReader r) |
|||
{ |
|||
int len; |
|||
var list = ReadListHelper(inf, r, out len); |
|||
int listCount = list.Count; |
|||
if (len > listCount) |
|||
{ |
|||
for (int i = 0; i < listCount; i++) |
|||
list[i].Deserialize(r); |
|||
for (int i = listCount; i < len; i++) |
|||
{ |
|||
var itm = _constructor(); |
|||
itm.Deserialize(r); |
|||
list.Add(itm); |
|||
} |
|||
return; |
|||
} |
|||
if (len < listCount) |
|||
list.RemoveRange(len, listCount - len); |
|||
for (int i = 0; i < len; i++) |
|||
list[i].Deserialize(r); |
|||
} |
|||
|
|||
public override void WriteList(TClass inf, NetDataWriter w) |
|||
{ |
|||
int len; |
|||
var list = WriteListHelper(inf, w, out len); |
|||
for (int i = 0; i < len; i++) |
|||
list[i].Serialize(w); |
|||
} |
|||
|
|||
public override void ReadArray(TClass inf, NetDataReader r) |
|||
{ |
|||
var arr = ReadArrayHelper(inf, r); |
|||
int len = arr.Length; |
|||
for (int i = 0; i < len; i++) |
|||
{ |
|||
arr[i] = _constructor(); |
|||
arr[i].Deserialize(r); |
|||
} |
|||
} |
|||
|
|||
public override void WriteArray(TClass inf, NetDataWriter w) |
|||
{ |
|||
var arr = WriteArrayHelper(inf, w); |
|||
int len = arr.Length; |
|||
for (int i = 0; i < len; i++) |
|||
arr[i].Serialize(w); |
|||
} |
|||
} |
|||
|
|||
private class IntSerializer<T> : FastCallSpecific<T, int> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetInt()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetIntArray()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class UIntSerializer<T> : FastCallSpecific<T, uint> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUInt()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUIntArray()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class ShortSerializer<T> : FastCallSpecific<T, short> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetShort()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetShortArray()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class UShortSerializer<T> : FastCallSpecific<T, ushort> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetUShort()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetUShortArray()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class LongSerializer<T> : FastCallSpecific<T, long> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetLong()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetLongArray()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class ULongSerializer<T> : FastCallSpecific<T, ulong> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetULong()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetULongArray()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class ByteSerializer<T> : FastCallSpecific<T, byte> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetByte()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBytesWithLength()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutBytesWithLength(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class SByteSerializer<T> : FastCallSpecific<T, sbyte> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetSByte()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetSBytesWithLength()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutSBytesWithLength(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class FloatSerializer<T> : FastCallSpecific<T, float> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetFloat()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetFloatArray()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class DoubleSerializer<T> : FastCallSpecific<T, double> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetDouble()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetDoubleArray()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class BoolSerializer<T> : FastCallSpecific<T, bool> |
|||
{ |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetBool()); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetBoolArray()); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf)); } |
|||
} |
|||
|
|||
private class CharSerializer<T> : FastCallSpecificAuto<T, char> |
|||
{ |
|||
protected override void ElementWrite(NetDataWriter w, ref char prop) { w.Put(prop); } |
|||
protected override void ElementRead(NetDataReader r, out char prop) { prop = r.GetChar(); } |
|||
} |
|||
|
|||
private class IPEndPointSerializer<T> : FastCallSpecificAuto<T, IPEndPoint> |
|||
{ |
|||
protected override void ElementWrite(NetDataWriter w, ref IPEndPoint prop) { w.Put(prop); } |
|||
protected override void ElementRead(NetDataReader r, out IPEndPoint prop) { prop = r.GetNetEndPoint(); } |
|||
} |
|||
|
|||
private class StringSerializer<T> : FastCallSpecific<T, string> |
|||
{ |
|||
private readonly int _maxLength; |
|||
public StringSerializer(int maxLength) { _maxLength = maxLength > 0 ? maxLength : short.MaxValue; } |
|||
public override void Read(T inf, NetDataReader r) { Setter(inf, r.GetString(_maxLength)); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put(Getter(inf), _maxLength); } |
|||
public override void ReadArray(T inf, NetDataReader r) { SetterArr(inf, r.GetStringArray(_maxLength)); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { w.PutArray(GetterArr(inf), _maxLength); } |
|||
} |
|||
|
|||
private class EnumByteSerializer<T> : FastCall<T> |
|||
{ |
|||
protected readonly PropertyInfo Property; |
|||
protected readonly Type PropertyType; |
|||
public EnumByteSerializer(PropertyInfo property, Type propertyType) |
|||
{ |
|||
Property = property; |
|||
PropertyType = propertyType; |
|||
} |
|||
public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetByte()), null); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put((byte)Property.GetValue(inf, null)); } |
|||
public override void ReadArray(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: Enum[]"); } |
|||
public override void WriteArray(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: Enum[]"); } |
|||
public override void ReadList(T inf, NetDataReader r) { throw new InvalidTypeException("Unsupported type: List<Enum>"); } |
|||
public override void WriteList(T inf, NetDataWriter w) { throw new InvalidTypeException("Unsupported type: List<Enum>"); } |
|||
} |
|||
|
|||
private class EnumIntSerializer<T> : EnumByteSerializer<T> |
|||
{ |
|||
public EnumIntSerializer(PropertyInfo property, Type propertyType) : base(property, propertyType) { } |
|||
public override void Read(T inf, NetDataReader r) { Property.SetValue(inf, Enum.ToObject(PropertyType, r.GetInt()), null); } |
|||
public override void Write(T inf, NetDataWriter w) { w.Put((int)Property.GetValue(inf, null)); } |
|||
} |
|||
|
|||
private sealed class ClassInfo<T> |
|||
{ |
|||
public static ClassInfo<T> Instance; |
|||
private readonly FastCall<T>[] _serializers; |
|||
private readonly int _membersCount; |
|||
|
|||
public ClassInfo(List<FastCall<T>> serializers) |
|||
{ |
|||
_membersCount = serializers.Count; |
|||
_serializers = serializers.ToArray(); |
|||
} |
|||
|
|||
public void Write(T obj, NetDataWriter writer) |
|||
{ |
|||
for (int i = 0; i < _membersCount; i++) |
|||
{ |
|||
var s = _serializers[i]; |
|||
if (s.Type == CallType.Basic) |
|||
s.Write(obj, writer); |
|||
else if (s.Type == CallType.Array) |
|||
s.WriteArray(obj, writer); |
|||
else |
|||
s.WriteList(obj, writer); |
|||
} |
|||
} |
|||
|
|||
public void Read(T obj, NetDataReader reader) |
|||
{ |
|||
for (int i = 0; i < _membersCount; i++) |
|||
{ |
|||
var s = _serializers[i]; |
|||
if (s.Type == CallType.Basic) |
|||
s.Read(obj, reader); |
|||
else if(s.Type == CallType.Array) |
|||
s.ReadArray(obj, reader); |
|||
else |
|||
s.ReadList(obj, reader); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private abstract class CustomType |
|||
{ |
|||
public abstract FastCall<T> Get<T>(); |
|||
} |
|||
|
|||
private sealed class CustomTypeStruct<TProperty> : CustomType where TProperty : struct, INetSerializable |
|||
{ |
|||
public override FastCall<T> Get<T>() { return new FastCallStruct<T, TProperty>(); } |
|||
} |
|||
|
|||
private sealed class CustomTypeClass<TProperty> : CustomType where TProperty : class, INetSerializable |
|||
{ |
|||
private readonly Func<TProperty> _constructor; |
|||
public CustomTypeClass(Func<TProperty> constructor) { _constructor = constructor; } |
|||
public override FastCall<T> Get<T>() { return new FastCallClass<T, TProperty>(_constructor); } |
|||
} |
|||
|
|||
private sealed class CustomTypeStatic<TProperty> : CustomType |
|||
{ |
|||
private readonly Action<NetDataWriter, TProperty> _writer; |
|||
private readonly Func<NetDataReader, TProperty> _reader; |
|||
public CustomTypeStatic(Action<NetDataWriter, TProperty> writer, Func<NetDataReader, TProperty> reader) |
|||
{ |
|||
_writer = writer; |
|||
_reader = reader; |
|||
} |
|||
public override FastCall<T> Get<T>() { return new FastCallStatic<T, TProperty>(_writer, _reader); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Register custom property type
|
|||
/// </summary>
|
|||
/// <typeparam name="T">INetSerializable structure</typeparam>
|
|||
public void RegisterNestedType<T>() where T : struct, INetSerializable |
|||
{ |
|||
_registeredTypes.Add(typeof(T), new CustomTypeStruct<T>()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Register custom property type
|
|||
/// </summary>
|
|||
/// <typeparam name="T">INetSerializable class</typeparam>
|
|||
public void RegisterNestedType<T>(Func<T> constructor) where T : class, INetSerializable |
|||
{ |
|||
_registeredTypes.Add(typeof(T), new CustomTypeClass<T>(constructor)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Register custom property type
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Any packet</typeparam>
|
|||
/// <param name="writer">custom type writer</param>
|
|||
/// <param name="reader">custom type reader</param>
|
|||
public void RegisterNestedType<T>(Action<NetDataWriter, T> writer, Func<NetDataReader, T> reader) |
|||
{ |
|||
_registeredTypes.Add(typeof(T), new CustomTypeStatic<T>(writer, reader)); |
|||
} |
|||
|
|||
private NetDataWriter _writer; |
|||
private readonly int _maxStringLength; |
|||
private readonly Dictionary<Type, CustomType> _registeredTypes = new Dictionary<Type, CustomType>(); |
|||
|
|||
public NetSerializer() : this(0) |
|||
{ |
|||
} |
|||
|
|||
public NetSerializer(int maxStringLength) |
|||
{ |
|||
_maxStringLength = maxStringLength; |
|||
} |
|||
|
|||
private ClassInfo<T> RegisterInternal<T>() |
|||
{ |
|||
if (ClassInfo<T>.Instance != null) |
|||
return ClassInfo<T>.Instance; |
|||
|
|||
Type t = typeof(T); |
|||
var props = t.GetProperties( |
|||
BindingFlags.Instance | |
|||
BindingFlags.Public | |
|||
BindingFlags.GetProperty | |
|||
BindingFlags.SetProperty); |
|||
var serializers = new List<FastCall<T>>(); |
|||
for (int i = 0; i < props.Length; i++) |
|||
{ |
|||
var property = props[i]; |
|||
var propertyType = property.PropertyType; |
|||
|
|||
var elementType = propertyType.IsArray ? propertyType.GetElementType() : propertyType; |
|||
var callType = propertyType.IsArray ? CallType.Array : CallType.Basic; |
|||
|
|||
if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>)) |
|||
{ |
|||
elementType = propertyType.GetGenericArguments()[0]; |
|||
callType = CallType.List; |
|||
} |
|||
|
|||
var getMethod = property.GetGetMethod(); |
|||
var setMethod = property.GetSetMethod(); |
|||
if (getMethod == null || setMethod == null) |
|||
continue; |
|||
|
|||
FastCall<T> serialzer = null; |
|||
if (propertyType.IsEnum) |
|||
{ |
|||
var underlyingType = Enum.GetUnderlyingType(propertyType); |
|||
if (underlyingType == typeof(byte)) |
|||
serialzer = new EnumByteSerializer<T>(property, propertyType); |
|||
else if (underlyingType == typeof(int)) |
|||
serialzer = new EnumIntSerializer<T>(property, propertyType); |
|||
else |
|||
throw new InvalidTypeException("Not supported enum underlying type: " + underlyingType.Name); |
|||
} |
|||
else if (elementType == typeof(string)) |
|||
serialzer = new StringSerializer<T>(_maxStringLength); |
|||
else if (elementType == typeof(bool)) |
|||
serialzer = new BoolSerializer<T>(); |
|||
else if (elementType == typeof(byte)) |
|||
serialzer = new ByteSerializer<T>(); |
|||
else if (elementType == typeof(sbyte)) |
|||
serialzer = new SByteSerializer<T>(); |
|||
else if (elementType == typeof(short)) |
|||
serialzer = new ShortSerializer<T>(); |
|||
else if (elementType == typeof(ushort)) |
|||
serialzer = new UShortSerializer<T>(); |
|||
else if (elementType == typeof(int)) |
|||
serialzer = new IntSerializer<T>(); |
|||
else if (elementType == typeof(uint)) |
|||
serialzer = new UIntSerializer<T>(); |
|||
else if (elementType == typeof(long)) |
|||
serialzer = new LongSerializer<T>(); |
|||
else if (elementType == typeof(ulong)) |
|||
serialzer = new ULongSerializer<T>(); |
|||
else if (elementType == typeof(float)) |
|||
serialzer = new FloatSerializer<T>(); |
|||
else if (elementType == typeof(double)) |
|||
serialzer = new DoubleSerializer<T>(); |
|||
else if (elementType == typeof(char)) |
|||
serialzer = new CharSerializer<T>(); |
|||
else if (elementType == typeof(IPEndPoint)) |
|||
serialzer = new IPEndPointSerializer<T>(); |
|||
else |
|||
{ |
|||
CustomType customType; |
|||
_registeredTypes.TryGetValue(elementType, out customType); |
|||
if (customType != null) |
|||
serialzer = customType.Get<T>(); |
|||
} |
|||
|
|||
if (serialzer != null) |
|||
{ |
|||
serialzer.Init(getMethod, setMethod, callType); |
|||
serializers.Add(serialzer); |
|||
} |
|||
else |
|||
{ |
|||
throw new InvalidTypeException("Unknown property type: " + propertyType.FullName); |
|||
} |
|||
} |
|||
ClassInfo<T>.Instance = new ClassInfo<T>(serializers); |
|||
return ClassInfo<T>.Instance; |
|||
} |
|||
|
|||
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
|
|||
public void Register<T>() |
|||
{ |
|||
RegisterInternal<T>(); |
|||
} |
|||
|
|||
/// <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>
|
|||
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
|
|||
public T Deserialize<T>(NetDataReader reader) where T : class, new() |
|||
{ |
|||
var info = RegisterInternal<T>(); |
|||
var result = new T(); |
|||
try |
|||
{ |
|||
info.Read(result, reader); |
|||
} |
|||
catch |
|||
{ |
|||
return null; |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
/// <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>
|
|||
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
|
|||
public bool Deserialize<T>(NetDataReader reader, T target) where T : class, new() |
|||
{ |
|||
var info = RegisterInternal<T>(); |
|||
try |
|||
{ |
|||
info.Read(target, reader); |
|||
} |
|||
catch |
|||
{ |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Serialize object to NetDataWriter (fast)
|
|||
/// </summary>
|
|||
/// <param name="writer">Serialization target NetDataWriter</param>
|
|||
/// <param name="obj">Object to serialize</param>
|
|||
/// <exception cref="InvalidTypeException"><typeparamref name="T"/>'s fields are not supported, or it has no fields</exception>
|
|||
public void Serialize<T>(NetDataWriter writer, T obj) where T : class, new() |
|||
{ |
|||
RegisterInternal<T>().Write(obj, writer); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Serialize object to byte array
|
|||
/// </summary>
|
|||
/// <param name="obj">Object to serialize</param>
|
|||
/// <returns>byte array with serialized data</returns>
|
|||
public byte[] Serialize<T>(T obj) where T : class, new() |
|||
{ |
|||
if (_writer == null) |
|||
_writer = new NetDataWriter(); |
|||
_writer.Reset(); |
|||
Serialize(_writer, obj); |
|||
return _writer.CopyData(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 15d07ef2204a4b04a9b6a94a1b7e61ae |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
|
|||
namespace LiteNetLib.Utils |
|||
{ |
|||
/// <summary>
|
|||
/// Represents RFC4330 SNTP packet used for communication to and from a network time server.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// Most applications should just use the <see cref="NtpPacket.CorrectionOffset" /> property.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The same data structure represents both request and reply packets.
|
|||
/// Request and reply differ in which properties are set and to what values.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// The only real property is <see cref="NtpPacket.Bytes" />.
|
|||
/// All other properties read from and write to the underlying byte array
|
|||
/// with the exception of <see cref="NtpPacket.DestinationTimestamp" />,
|
|||
/// which is not part of the packet on network and it is instead set locally after receiving the packet.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// Copied from <a href="https://guerrillantp.machinezoo.com/">GuerrillaNtp project</a>
|
|||
/// with permission from Robert Vazan (@robertvazan) under MIT license, see https://github.com/RevenantX/LiteNetLib/pull/236
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
public class NtpPacket |
|||
{ |
|||
private static readonly DateTime Epoch = new DateTime(1900, 1, 1); |
|||
|
|||
/// <summary>
|
|||
/// Gets RFC4330-encoded SNTP packet.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Byte array containing RFC4330-encoded SNTP packet. It is at least 48 bytes long.
|
|||
/// </value>
|
|||
/// <remarks>
|
|||
/// This is the only real property. All other properties except
|
|||
/// <see cref="NtpPacket.DestinationTimestamp" /> read from or write to this byte array.
|
|||
/// </remarks>
|
|||
public byte[] Bytes { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the leap second indicator.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Leap second warning, if any. Special value
|
|||
/// <see cref="NtpLeapIndicator.AlarmCondition" /> indicates unsynchronized server clock.
|
|||
/// Default is <see cref="NtpLeapIndicator.NoWarning" />.
|
|||
/// </value>
|
|||
/// <remarks>
|
|||
/// Only servers fill in this property. Clients can consult this property for possible leap second warning.
|
|||
/// </remarks>
|
|||
public NtpLeapIndicator LeapIndicator |
|||
{ |
|||
get { return (NtpLeapIndicator)((Bytes[0] & 0xC0) >> 6); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets protocol version number.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// SNTP protocol version. Default is 4, which is the latest version at the time of this writing.
|
|||
/// </value>
|
|||
/// <remarks>
|
|||
/// In request packets, clients should leave this property at default value 4.
|
|||
/// Servers usually reply with the same protocol version.
|
|||
/// </remarks>
|
|||
public int VersionNumber |
|||
{ |
|||
get { return (Bytes[0] & 0x38) >> 3; } |
|||
private set { Bytes[0] = (byte)((Bytes[0] & ~0x38) | value << 3); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets SNTP packet mode, i.e. whether this is client or server packet.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// SNTP packet mode. Default is <see cref="NtpMode.Client" /> in newly created packets.
|
|||
/// Server reply should have this property set to <see cref="NtpMode.Server" />.
|
|||
/// </value>
|
|||
public NtpMode Mode |
|||
{ |
|||
get { return (NtpMode)(Bytes[0] & 0x07); } |
|||
private set { Bytes[0] = (byte)((Bytes[0] & ~0x07) | (int)value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets server's distance from the reference clock.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// <para>
|
|||
/// Distance from the reference clock. This property is set only in server reply packets.
|
|||
/// Servers connected directly to reference clock hardware set this property to 1.
|
|||
/// Statum number is incremented by 1 on every hop down the NTP server hierarchy.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// Special value 0 indicates that this packet is a Kiss-o'-Death message
|
|||
/// with kiss code stored in <see cref="NtpPacket.ReferenceId" />.
|
|||
/// </para>
|
|||
/// </value>
|
|||
public int Stratum { get { return Bytes[1]; } } |
|||
|
|||
/// <summary>
|
|||
/// Gets server's preferred polling interval.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Polling interval in log2 seconds, e.g. 4 stands for 16s and 17 means 131,072s.
|
|||
/// </value>
|
|||
public int Poll { get { return Bytes[2]; } } |
|||
|
|||
/// <summary>
|
|||
/// Gets the precision of server clock.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Clock precision in log2 seconds, e.g. -20 for microsecond precision.
|
|||
/// </value>
|
|||
public int Precision { get { return (sbyte)Bytes[3]; } } |
|||
|
|||
/// <summary>
|
|||
/// Gets the total round-trip delay from the server to the reference clock.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Round-trip delay to the reference clock. Normally a positive value smaller than one second.
|
|||
/// </value>
|
|||
public TimeSpan RootDelay { get { return GetTimeSpan32(4); } } |
|||
|
|||
/// <summary>
|
|||
/// Gets the estimated error in time reported by the server.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Estimated error in time reported by the server. Normally a positive value smaller than one second.
|
|||
/// </value>
|
|||
public TimeSpan RootDispersion { get { return GetTimeSpan32(8); } } |
|||
|
|||
/// <summary>
|
|||
/// Gets the ID of the time source used by the server or Kiss-o'-Death code sent by the server.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// <para>
|
|||
/// ID of server's time source or Kiss-o'-Death code.
|
|||
/// Purpose of this property depends on value of <see cref="NtpPacket.Stratum" /> property.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// Stratum 1 servers write here one of several special values that describe the kind of hardware clock they use.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// Stratum 2 and lower servers set this property to IPv4 address of their upstream server.
|
|||
/// If upstream server has IPv6 address, the address is hashed, because it doesn't fit in this property.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// When server sets <see cref="NtpPacket.Stratum" /> to special value 0,
|
|||
/// this property contains so called kiss code that instructs the client to stop querying the server.
|
|||
/// </para>
|
|||
/// </value>
|
|||
public uint ReferenceId { get { return GetUInt32BE(12); } } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the time when the server clock was last set or corrected.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Time when the server clock was last set or corrected or <c>null</c> when not specified.
|
|||
/// </value>
|
|||
/// <remarks>
|
|||
/// This Property is usually set only by servers. It usually lags server's current time by several minutes,
|
|||
/// so don't use this property for time synchronization.
|
|||
/// </remarks>
|
|||
public DateTime? ReferenceTimestamp { get { return GetDateTime64(16); } } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the time when the client sent its request.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// This property is <c>null</c> in request packets.
|
|||
/// In reply packets, it is the time when the client sent its request.
|
|||
/// Servers copy this value from <see cref="NtpPacket.TransmitTimestamp" />
|
|||
/// that they find in received request packet.
|
|||
/// </value>
|
|||
/// <seealso cref="NtpPacket.CorrectionOffset" />
|
|||
/// <seealso cref="NtpPacket.RoundTripTime" />
|
|||
public DateTime? OriginTimestamp { get { return GetDateTime64(24); } } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the time when the request was received by the server.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// This property is <c>null</c> in request packets.
|
|||
/// In reply packets, it is the time when the server received client request.
|
|||
/// </value>
|
|||
/// <seealso cref="NtpPacket.CorrectionOffset" />
|
|||
/// <seealso cref="NtpPacket.RoundTripTime" />
|
|||
public DateTime? ReceiveTimestamp { get { return GetDateTime64(32); } } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the time when the packet was sent.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Time when the packet was sent. It should never be <c>null</c>.
|
|||
/// Default value is <see cref="System.DateTime.UtcNow" />.
|
|||
/// </value>
|
|||
/// <remarks>
|
|||
/// This property must be set by both clients and servers.
|
|||
/// </remarks>
|
|||
/// <seealso cref="NtpPacket.CorrectionOffset" />
|
|||
/// <seealso cref="NtpPacket.RoundTripTime" />
|
|||
public DateTime? TransmitTimestamp { get { return GetDateTime64(40); } private set { SetDateTime64(40, value); } } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the time of reception of response SNTP packet on the client.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Time of reception of response SNTP packet on the client. It is <c>null</c> in request packets.
|
|||
/// </value>
|
|||
/// <remarks>
|
|||
/// This property is not part of the protocol and has to be set when reply packet is received.
|
|||
/// </remarks>
|
|||
/// <seealso cref="NtpPacket.CorrectionOffset" />
|
|||
/// <seealso cref="NtpPacket.RoundTripTime" />
|
|||
public DateTime? DestinationTimestamp { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the round-trip time to the server.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Time the request spent traveling to the server plus the time the reply spent traveling back.
|
|||
/// This is calculated from timestamps in the packet as <c>(t1 - t0) + (t3 - t2)</c>
|
|||
/// where t0 is <see cref="NtpPacket.OriginTimestamp" />,
|
|||
/// t1 is <see cref="NtpPacket.ReceiveTimestamp" />,
|
|||
/// t2 is <see cref="NtpPacket.TransmitTimestamp" />,
|
|||
/// and t3 is <see cref="NtpPacket.DestinationTimestamp" />.
|
|||
/// This property throws an exception in request packets.
|
|||
/// </value>
|
|||
public TimeSpan RoundTripTime |
|||
{ |
|||
get |
|||
{ |
|||
CheckTimestamps(); |
|||
return (ReceiveTimestamp.Value - OriginTimestamp.Value) + (DestinationTimestamp.Value - TransmitTimestamp.Value); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the offset that should be added to local time to synchronize it with server time.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// Time difference between server and client. It should be added to local time to get server time.
|
|||
/// It is calculated from timestamps in the packet as <c>0.5 * ((t1 - t0) - (t3 - t2))</c>
|
|||
/// where t0 is <see cref="NtpPacket.OriginTimestamp" />,
|
|||
/// t1 is <see cref="NtpPacket.ReceiveTimestamp" />,
|
|||
/// t2 is <see cref="NtpPacket.TransmitTimestamp" />,
|
|||
/// and t3 is <see cref="NtpPacket.DestinationTimestamp" />.
|
|||
/// This property throws an exception in request packets.
|
|||
/// </value>
|
|||
public TimeSpan CorrectionOffset |
|||
{ |
|||
get |
|||
{ |
|||
CheckTimestamps(); |
|||
return TimeSpan.FromTicks(((ReceiveTimestamp.Value - OriginTimestamp.Value) - (DestinationTimestamp.Value - TransmitTimestamp.Value)).Ticks / 2); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes default request packet.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Properties <see cref="NtpPacket.Mode" /> and <see cref="NtpPacket.VersionNumber" />
|
|||
/// are set appropriately for request packet. Property <see cref="NtpPacket.TransmitTimestamp" />
|
|||
/// is set to <see cref="System.DateTime.UtcNow" />.
|
|||
/// </remarks>
|
|||
public NtpPacket() : this(new byte[48]) |
|||
{ |
|||
Mode = NtpMode.Client; |
|||
VersionNumber = 4; |
|||
TransmitTimestamp = DateTime.UtcNow; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes packet from received data.
|
|||
/// </summary>
|
|||
internal NtpPacket(byte[] bytes) |
|||
{ |
|||
if (bytes.Length < 48) |
|||
throw new ArgumentException("SNTP reply packet must be at least 48 bytes long.", "bytes"); |
|||
Bytes = bytes; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes packet from data received from a server.
|
|||
/// </summary>
|
|||
/// <param name="bytes">Data received from the server.</param>
|
|||
/// <param name="destinationTimestamp">Utc time of reception of response SNTP packet on the client.</param>
|
|||
/// <returns></returns>
|
|||
public static NtpPacket FromServerResponse(byte[] bytes, DateTime destinationTimestamp) |
|||
{ |
|||
return new NtpPacket(bytes) { DestinationTimestamp = destinationTimestamp }; |
|||
} |
|||
|
|||
internal void ValidateRequest() |
|||
{ |
|||
if (Mode != NtpMode.Client) |
|||
throw new InvalidOperationException("This is not a request SNTP packet."); |
|||
if (VersionNumber == 0) |
|||
throw new InvalidOperationException("Protocol version of the request is not specified."); |
|||
if (TransmitTimestamp == null) |
|||
throw new InvalidOperationException("TransmitTimestamp must be set in request packet."); |
|||
} |
|||
|
|||
internal void ValidateReply() |
|||
{ |
|||
if (Mode != NtpMode.Server) |
|||
throw new InvalidOperationException("This is not a reply SNTP packet."); |
|||
if (VersionNumber == 0) |
|||
throw new InvalidOperationException("Protocol version of the reply is not specified."); |
|||
if (Stratum == 0) |
|||
throw new InvalidOperationException(string.Format("Received Kiss-o'-Death SNTP packet with code 0x{0:x}.", ReferenceId)); |
|||
if (LeapIndicator == NtpLeapIndicator.AlarmCondition) |
|||
throw new InvalidOperationException("SNTP server has unsynchronized clock."); |
|||
CheckTimestamps(); |
|||
} |
|||
|
|||
private void CheckTimestamps() |
|||
{ |
|||
if (OriginTimestamp == null) |
|||
throw new InvalidOperationException("Origin timestamp is missing."); |
|||
if (ReceiveTimestamp == null) |
|||
throw new InvalidOperationException("Receive timestamp is missing."); |
|||
if (TransmitTimestamp == null) |
|||
throw new InvalidOperationException("Transmit timestamp is missing."); |
|||
if (DestinationTimestamp == null) |
|||
throw new InvalidOperationException("Destination timestamp is missing."); |
|||
} |
|||
|
|||
private DateTime? GetDateTime64(int offset) |
|||
{ |
|||
var field = GetUInt64BE(offset); |
|||
if (field == 0) |
|||
return null; |
|||
return new DateTime(Epoch.Ticks + Convert.ToInt64(field * (1.0 / (1L << 32) * 10000000.0))); |
|||
} |
|||
|
|||
private void SetDateTime64(int offset, DateTime? value) |
|||
{ |
|||
SetUInt64BE(offset, value == null ? 0 : Convert.ToUInt64((value.Value.Ticks - Epoch.Ticks) * (0.0000001 * (1L << 32)))); |
|||
} |
|||
|
|||
private TimeSpan GetTimeSpan32(int offset) |
|||
{ |
|||
return TimeSpan.FromSeconds(GetInt32BE(offset) / (double)(1 << 16)); |
|||
} |
|||
|
|||
private ulong GetUInt64BE(int offset) |
|||
{ |
|||
return SwapEndianness(BitConverter.ToUInt64(Bytes, offset)); |
|||
} |
|||
|
|||
private void SetUInt64BE(int offset, ulong value) |
|||
{ |
|||
FastBitConverter.GetBytes(Bytes, offset, SwapEndianness(value)); |
|||
} |
|||
|
|||
private int GetInt32BE(int offset) |
|||
{ |
|||
return (int)GetUInt32BE(offset); |
|||
} |
|||
|
|||
private uint GetUInt32BE(int offset) |
|||
{ |
|||
return SwapEndianness(BitConverter.ToUInt32(Bytes, offset)); |
|||
} |
|||
|
|||
private static uint SwapEndianness(uint x) |
|||
{ |
|||
return ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24); |
|||
} |
|||
|
|||
private static ulong SwapEndianness(ulong x) |
|||
{ |
|||
return ((ulong)SwapEndianness((uint)x) << 32) | SwapEndianness((uint)(x >> 32)); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents leap second warning from the server that instructs the client to add or remove leap second.
|
|||
/// </summary>
|
|||
/// <seealso cref="NtpPacket.LeapIndicator" />
|
|||
public enum NtpLeapIndicator |
|||
{ |
|||
/// <summary>
|
|||
/// No leap second warning. No action required.
|
|||
/// </summary>
|
|||
NoWarning, |
|||
|
|||
/// <summary>
|
|||
/// Warns the client that the last minute of the current day has 61 seconds.
|
|||
/// </summary>
|
|||
LastMinuteHas61Seconds, |
|||
|
|||
/// <summary>
|
|||
/// Warns the client that the last minute of the current day has 59 seconds.
|
|||
/// </summary>
|
|||
LastMinuteHas59Seconds, |
|||
|
|||
/// <summary>
|
|||
/// Special value indicating that the server clock is unsynchronized and the returned time is unreliable.
|
|||
/// </summary>
|
|||
AlarmCondition |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Describes SNTP packet mode, i.e. client or server.
|
|||
/// </summary>
|
|||
/// <seealso cref="NtpPacket.Mode" />
|
|||
public enum NtpMode |
|||
{ |
|||
/// <summary>
|
|||
/// Identifies client-to-server SNTP packet.
|
|||
/// </summary>
|
|||
Client = 3, |
|||
|
|||
/// <summary>
|
|||
/// Identifies server-to-client SNTP packet.
|
|||
/// </summary>
|
|||
Server = 4, |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 17522d9782736174d811ad894ecf20be |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
|
|||
#if NETSTANDARD || NETCOREAPP
|
|||
using System.Threading.Tasks; |
|||
#endif
|
|||
|
|||
namespace LiteNetLib.Utils |
|||
{ |
|||
/// <summary>
|
|||
/// Make NTP request.
|
|||
/// <para>
|
|||
/// 1. Create the object by <see cref="Create(IPEndPoint,Action<NtpPacket>)"/> method.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// 2. Use <see cref="Send"/> method to send requests.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// 3. Call <see cref="Close"/> to release the socket AFTER you have received the response or some timeout.
|
|||
/// If you close the socket too early, you may miss the response.
|
|||
/// </para>
|
|||
/// </summary>
|
|||
public sealed class NtpRequest : INetSocketListener |
|||
{ |
|||
public const int DefaultPort = 123; |
|||
|
|||
private readonly NetSocket _socket; |
|||
private readonly Action<NtpPacket> _onRequestComplete; |
|||
private readonly IPEndPoint _ntpEndPoint; |
|||
|
|||
/// <summary>
|
|||
/// Initialize object, open socket.
|
|||
/// </summary>
|
|||
/// <param name="endPoint">NTP Server endpoint</param>
|
|||
/// <param name="onRequestComplete">callback (called from other thread!)</param>
|
|||
private NtpRequest(IPEndPoint endPoint, Action<NtpPacket> onRequestComplete) |
|||
{ |
|||
_ntpEndPoint = endPoint; |
|||
_onRequestComplete = onRequestComplete; |
|||
|
|||
// Create and start socket
|
|||
_socket = new NetSocket(this); |
|||
_socket.Bind( |
|||
IPAddress.Any, |
|||
IPAddress.IPv6Any, |
|||
0, |
|||
false, |
|||
endPoint.AddressFamily == AddressFamily.InterNetworkV6 ? IPv6Mode.SeparateSocket : IPv6Mode.Disabled); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the requests for NTP server, open socket.
|
|||
/// </summary>
|
|||
/// <param name="endPoint">NTP Server address.</param>
|
|||
/// <param name="onRequestComplete">callback (called from other thread!)</param>
|
|||
public static NtpRequest Create(IPEndPoint endPoint, Action<NtpPacket> onRequestComplete) |
|||
{ |
|||
return new NtpRequest(endPoint, onRequestComplete); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the requests for NTP server (default port), open socket.
|
|||
/// </summary>
|
|||
/// <param name="ipAddress">NTP Server address.</param>
|
|||
/// <param name="onRequestComplete">callback (called from other thread!)</param>
|
|||
public static NtpRequest Create(IPAddress ipAddress, Action<NtpPacket> onRequestComplete) |
|||
{ |
|||
IPEndPoint endPoint = new IPEndPoint(ipAddress, DefaultPort); |
|||
return Create(endPoint, onRequestComplete); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the requests for NTP server, open socket.
|
|||
/// </summary>
|
|||
/// <param name="ntpServerAddress">NTP Server address.</param>
|
|||
/// <param name="port">port</param>
|
|||
/// <param name="onRequestComplete">callback (called from other thread!)</param>
|
|||
public static NtpRequest Create(string ntpServerAddress, int port, Action<NtpPacket> onRequestComplete) |
|||
{ |
|||
IPEndPoint endPoint = NetUtils.MakeEndPoint(ntpServerAddress, port); |
|||
return Create(endPoint, onRequestComplete); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the requests for NTP server (default port), open socket.
|
|||
/// </summary>
|
|||
/// <param name="ntpServerAddress">NTP Server address.</param>
|
|||
/// <param name="onRequestComplete">callback (called from other thread!)</param>
|
|||
public static NtpRequest Create(string ntpServerAddress, Action<NtpPacket> onRequestComplete) |
|||
{ |
|||
IPEndPoint endPoint = NetUtils.MakeEndPoint(ntpServerAddress, DefaultPort); |
|||
return Create(endPoint, onRequestComplete); |
|||
} |
|||
|
|||
#if NETSTANDARD || NETCOREAPP
|
|||
/// <summary>
|
|||
/// Requests asynchronously NTP server for time offset
|
|||
/// </summary>
|
|||
/// <param name="ntpServerAddress">NTP Server address.</param>
|
|||
/// <returns>Scheduled task</returns>
|
|||
public static async Task<NtpPacket> RequestAsync(string ntpServerAddress) |
|||
{ |
|||
var t = new TaskCompletionSource<NtpPacket>(); |
|||
|
|||
await Task.Run(() => |
|||
{ |
|||
NtpRequest request = null; |
|||
request = Create(ntpServerAddress, (ntpPacket) => |
|||
{ |
|||
request.Close(); |
|||
t.SetResult(ntpPacket); |
|||
}); |
|||
request.Send(); |
|||
}); |
|||
|
|||
return await t.Task; |
|||
} |
|||
#endif
|
|||
|
|||
/// <summary>
|
|||
/// Send request to the NTP server calls callback (if success).
|
|||
/// In case of error the callback is called with null param.
|
|||
/// </summary>
|
|||
public void Send() |
|||
{ |
|||
SocketError errorCode = 0; |
|||
var packet = new NtpPacket(); |
|||
packet.ValidateRequest(); // not necessary
|
|||
byte[] sendData = packet.Bytes; |
|||
var sendCount = _socket.SendTo(sendData, 0, sendData.Length, _ntpEndPoint, ref errorCode); |
|||
if (errorCode != 0 || sendCount != sendData.Length) |
|||
{ |
|||
_onRequestComplete(null); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Close socket.
|
|||
/// </summary>
|
|||
public void Close() |
|||
{ |
|||
_socket.Close(false); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle received data: transform bytes to NtpPacket, close socket and call the callback.
|
|||
/// </summary>
|
|||
void INetSocketListener.OnMessageReceived(byte[] data, int length, SocketError errorCode, IPEndPoint remoteEndPoint) |
|||
{ |
|||
DateTime destinationTimestamp = DateTime.UtcNow; |
|||
if (!remoteEndPoint.Equals(_ntpEndPoint)) |
|||
return; |
|||
|
|||
if (length < 48) |
|||
{ |
|||
NetDebug.Write(NetLogLevel.Trace, "NTP response too short: {}", length); |
|||
_onRequestComplete(null); |
|||
return; |
|||
} |
|||
|
|||
NtpPacket packet = NtpPacket.FromServerResponse(data, destinationTimestamp); |
|||
try |
|||
{ |
|||
packet.ValidateReply(); |
|||
} |
|||
catch (InvalidOperationException ex) |
|||
{ |
|||
NetDebug.Write(NetLogLevel.Trace, "NTP response error: {}", ex.Message); |
|||
packet = null; |
|||
} |
|||
_onRequestComplete(packet); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: ac0e7ca5f9a65c54b92bdfe59c6a5486 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using LiteNetLib; |
|||
using MLAPI.Transports; |
|||
using MLAPI.Transports.Tasks; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using UnityEngine; |
|||
|
|||
namespace LiteNetLibTransport |
|||
{ |
|||
public class LiteNetLibTransport : Transport, INetEventListener |
|||
{ |
|||
private enum HostType |
|||
{ |
|||
None, |
|||
Server, |
|||
Client |
|||
} |
|||
|
|||
public struct LiteChannel |
|||
{ |
|||
public byte number; |
|||
public DeliveryMethod method; |
|||
} |
|||
|
|||
public struct Event |
|||
{ |
|||
public NetEventType type; |
|||
public ulong clientId; |
|||
public string channelName; |
|||
public NetPacketReader packetReader; |
|||
public DateTime dateTime; |
|||
} |
|||
|
|||
[Tooltip("The port to listen on (if server) or connect to (if client)")] |
|||
public ushort Port = 7777; |
|||
[Tooltip("The address to connect to as client; ignored if server")] |
|||
public string Address = "127.0.0.1"; |
|||
[Tooltip("Interval between ping packets used for detecting latency and checking connection, in seconds")] |
|||
public float PingInterval = 1f; |
|||
[Tooltip("Maximum duration for a connection to survive without receiving packets, in seconds")] |
|||
public float DisconnectTimeout = 5f; |
|||
[Tooltip("Delay between connection attempts, in seconds")] |
|||
public float ReconnectDelay = 0.5f; |
|||
[Tooltip("Maximum connection attempts before client stops and reports a disconnection")] |
|||
public int MaxConnectAttempts = 10; |
|||
public TransportChannel[] channels = new TransportChannel[0]; |
|||
[Tooltip("Size of default buffer for decoding incoming packets, in bytes")] |
|||
public int MessageBufferSize = 1024 * 5; |
|||
[Tooltip("Simulated chance for a packet to be \"lost\", from 0 (no simulation) to 100 percent")] |
|||
public int SimulatePacketLossChance = 0; |
|||
[Tooltip("Simulated minimum additional latency for packets in milliseconds (0 for no simulation)")] |
|||
public int SimulateMinLatency = 0; |
|||
[Tooltip("Simulated maximum additional latency for packets in milliseconds (0 for no simulation")] |
|||
public int SimulateMaxLatency = 0; |
|||
|
|||
private readonly Dictionary<ulong, NetPeer> peers = new Dictionary<ulong, NetPeer>(); |
|||
private readonly Dictionary<string, LiteChannel> liteChannels = new Dictionary<string, LiteChannel>(); |
|||
|
|||
private NetManager netManager; |
|||
private Queue<Event> eventQueue = new Queue<Event>(); |
|||
|
|||
private byte[] messageBuffer; |
|||
private WeakReference temporaryBufferReference; |
|||
|
|||
public override ulong ServerClientId => 0; |
|||
private HostType hostType; |
|||
private static readonly ArraySegment<byte> emptyArraySegment = new ArraySegment<byte>(); |
|||
|
|||
private SocketTask connectTask; |
|||
|
|||
private void OnValidate() |
|||
{ |
|||
PingInterval = Math.Max(0, PingInterval); |
|||
DisconnectTimeout = Math.Max(0, DisconnectTimeout); |
|||
ReconnectDelay = Math.Max(0, ReconnectDelay); |
|||
MaxConnectAttempts = Math.Max(0, MaxConnectAttempts); |
|||
MessageBufferSize = Math.Max(0, MessageBufferSize); |
|||
SimulatePacketLossChance = Math.Min(100, Math.Max(0, SimulatePacketLossChance)); |
|||
SimulateMinLatency = Math.Max(0, SimulateMinLatency); |
|||
SimulateMaxLatency = Math.Max(SimulateMinLatency, SimulateMaxLatency); |
|||
} |
|||
|
|||
private void Update() |
|||
{ |
|||
netManager?.PollEvents(); |
|||
} |
|||
|
|||
public override bool IsSupported => Application.platform != RuntimePlatform.WebGLPlayer; |
|||
|
|||
public override void Send(ulong clientId, ArraySegment<byte> data, string channelName) |
|||
{ |
|||
LiteChannel channel = liteChannels[channelName]; |
|||
|
|||
if (peers.ContainsKey(clientId)) |
|||
{ |
|||
peers[clientId].Send(data.Array, data.Offset, data.Count, channel.method); |
|||
} |
|||
} |
|||
|
|||
public override NetEventType PollEvent(out ulong clientId, out string channelName, out ArraySegment<byte> payload, out float receiveTime) |
|||
{ |
|||
payload = emptyArraySegment; |
|||
clientId = 0; |
|||
channelName = null; |
|||
receiveTime = Time.realtimeSinceStartup; |
|||
|
|||
if (eventQueue.Count > 0) |
|||
{ |
|||
Event @event = eventQueue.Dequeue(); |
|||
|
|||
clientId = @event.clientId; |
|||
channelName = @event.channelName; |
|||
receiveTime = Time.realtimeSinceStartup - ((float)DateTime.UtcNow.Subtract(@event.dateTime).TotalSeconds); |
|||
|
|||
if (@event.packetReader != null) |
|||
{ |
|||
int size = @event.packetReader.UserDataSize; |
|||
byte[] data = messageBuffer; |
|||
|
|||
if (size > messageBuffer.Length) |
|||
{ |
|||
if (temporaryBufferReference != null && temporaryBufferReference.IsAlive && ((byte[])temporaryBufferReference.Target).Length >= size) |
|||
{ |
|||
data = (byte[])temporaryBufferReference.Target; |
|||
} |
|||
else |
|||
{ |
|||
data = new byte[size]; |
|||
temporaryBufferReference = new WeakReference(data); |
|||
} |
|||
} |
|||
|
|||
Buffer.BlockCopy(@event.packetReader.RawData, @event.packetReader.UserDataOffset, data, 0, size); |
|||
|
|||
payload = new ArraySegment<byte>(data, 0, size); |
|||
@event.packetReader.Recycle(); |
|||
} |
|||
|
|||
return @event.type; |
|||
} |
|||
|
|||
return NetEventType.Nothing; |
|||
} |
|||
|
|||
public override SocketTasks StartClient() |
|||
{ |
|||
SocketTask task = SocketTask.Working; |
|||
|
|||
if (hostType != HostType.None) |
|||
{ |
|||
throw new InvalidOperationException("Already started as " + hostType); |
|||
} |
|||
|
|||
hostType = HostType.Client; |
|||
|
|||
netManager.Start(); |
|||
|
|||
NetPeer peer = netManager.Connect(Address, Port, string.Empty); |
|||
|
|||
if (peer.Id != 0) |
|||
{ |
|||
throw new InvalidPacketException("Server peer did not have id 0: " + peer.Id); |
|||
} |
|||
|
|||
peers[(ulong)peer.Id] = peer; |
|||
|
|||
return task.AsTasks(); |
|||
} |
|||
|
|||
public override SocketTasks StartServer() |
|||
{ |
|||
if (hostType != HostType.None) |
|||
{ |
|||
throw new InvalidOperationException("Already started as " + hostType); |
|||
} |
|||
|
|||
hostType = HostType.Server; |
|||
|
|||
bool success = netManager.Start(Port); |
|||
|
|||
return new SocketTask() |
|||
{ |
|||
IsDone = true, |
|||
Message = null, |
|||
SocketError = SocketError.SocketError, |
|||
State = null, |
|||
Success = success, |
|||
TransportCode = -1, |
|||
TransportException = null |
|||
}.AsTasks(); |
|||
} |
|||
|
|||
public override void DisconnectRemoteClient(ulong clientId) |
|||
{ |
|||
if (peers.ContainsKey(clientId)) |
|||
{ |
|||
peers[clientId].Disconnect(); |
|||
} |
|||
} |
|||
|
|||
public override void DisconnectLocalClient() |
|||
{ |
|||
netManager.Flush(); |
|||
netManager.DisconnectAll(); |
|||
peers.Clear(); |
|||
} |
|||
|
|||
public override ulong GetCurrentRtt(ulong clientId) |
|||
{ |
|||
if (!peers.ContainsKey(clientId)) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
return (ulong)peers[clientId].Ping * 2; |
|||
} |
|||
|
|||
public override void Shutdown() |
|||
{ |
|||
netManager.Flush(); |
|||
netManager.Stop(); |
|||
peers.Clear(); |
|||
|
|||
hostType = HostType.None; |
|||
} |
|||
|
|||
public override void Init() |
|||
{ |
|||
liteChannels.Clear(); |
|||
MapChannels(MLAPI_CHANNELS); |
|||
MapChannels(channels); |
|||
AddRpcResponseChannels(); |
|||
|
|||
if (liteChannels.Count > 64) |
|||
{ |
|||
throw new InvalidOperationException("LiteNetLib supports up to 64 channels, got: " + liteChannels.Count); |
|||
} |
|||
|
|||
messageBuffer = new byte[MessageBufferSize]; |
|||
|
|||
netManager = new NetManager(this) |
|||
{ |
|||
PingInterval = SecondsToMilliseconds(PingInterval), |
|||
DisconnectTimeout = SecondsToMilliseconds(DisconnectTimeout), |
|||
ReconnectDelay = SecondsToMilliseconds(ReconnectDelay), |
|||
MaxConnectAttempts = MaxConnectAttempts, |
|||
SimulatePacketLoss = SimulatePacketLossChance > 0, |
|||
SimulationPacketLossChance = SimulatePacketLossChance, |
|||
SimulateLatency = SimulateMaxLatency > 0, |
|||
SimulationMinLatency = SimulateMinLatency, |
|||
SimulationMaxLatency = SimulateMaxLatency |
|||
}; |
|||
} |
|||
|
|||
private void MapChannels(TransportChannel[] channels) |
|||
{ |
|||
byte id = (byte)liteChannels.Count; |
|||
|
|||
for (int i = 0; i < channels.Length; i++) |
|||
{ |
|||
liteChannels.Add(channels[i].Name, new LiteChannel() |
|||
{ |
|||
number = id++, |
|||
method = ConvertChannelType(channels[i].Type) |
|||
}); |
|||
} |
|||
} |
|||
|
|||
private void AddRpcResponseChannels() |
|||
{ |
|||
byte id = (byte)liteChannels.Count; |
|||
|
|||
foreach (DeliveryMethod method in Enum.GetValues(typeof(DeliveryMethod)) as DeliveryMethod[]) |
|||
{ |
|||
liteChannels.Add("LITENETLIB_RESPONSE_" + method.ToString(), new LiteChannel() |
|||
{ |
|||
number = id++, |
|||
method = method |
|||
}); |
|||
} |
|||
} |
|||
|
|||
private DeliveryMethod ConvertChannelType(ChannelType type) |
|||
{ |
|||
switch (type) |
|||
{ |
|||
case ChannelType.Unreliable: |
|||
{ |
|||
return DeliveryMethod.Unreliable; |
|||
} |
|||
case ChannelType.UnreliableSequenced: |
|||
{ |
|||
return DeliveryMethod.Sequenced; |
|||
} |
|||
case ChannelType.Reliable: |
|||
{ |
|||
return DeliveryMethod.ReliableUnordered; |
|||
} |
|||
case ChannelType.ReliableSequenced: |
|||
{ |
|||
return DeliveryMethod.ReliableOrdered; |
|||
} |
|||
case ChannelType.ReliableFragmentedSequenced: |
|||
{ |
|||
return DeliveryMethod.ReliableOrdered; |
|||
} |
|||
default: |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(type), type, null); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void INetEventListener.OnPeerConnected(NetPeer peer) |
|||
{ |
|||
if (connectTask != null) |
|||
{ |
|||
connectTask.Success = true; |
|||
connectTask.IsDone = true; |
|||
connectTask = null; |
|||
} |
|||
|
|||
Event @event = new Event() |
|||
{ |
|||
dateTime = DateTime.UtcNow, |
|||
type = NetEventType.Connect, |
|||
clientId = GetMLAPIClientId(peer) |
|||
}; |
|||
|
|||
peers[@event.clientId] = peer; |
|||
|
|||
eventQueue.Enqueue(@event); |
|||
} |
|||
|
|||
void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) |
|||
{ |
|||
if (connectTask != null) |
|||
{ |
|||
connectTask.Success = false; |
|||
connectTask.IsDone = true; |
|||
connectTask = null; |
|||
} |
|||
|
|||
Event @event = new Event() |
|||
{ |
|||
dateTime = DateTime.UtcNow, |
|||
type = NetEventType.Disconnect, |
|||
clientId = GetMLAPIClientId(peer) |
|||
}; |
|||
|
|||
peers.Remove(@event.clientId); |
|||
|
|||
eventQueue.Enqueue(@event); |
|||
} |
|||
|
|||
void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketError) |
|||
{ |
|||
// Ignore
|
|||
if (connectTask != null) |
|||
{ |
|||
connectTask.SocketError = socketError; |
|||
connectTask.IsDone = true; |
|||
connectTask = null; |
|||
} |
|||
} |
|||
|
|||
void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod) |
|||
{ |
|||
Event @event = new Event() |
|||
{ |
|||
dateTime = DateTime.UtcNow, |
|||
type = NetEventType.Data, |
|||
clientId = GetMLAPIClientId(peer), |
|||
packetReader = reader, |
|||
channelName = "LITENETLIB_RESPONSE_" + deliveryMethod.ToString() |
|||
}; |
|||
|
|||
eventQueue.Enqueue(@event); |
|||
} |
|||
|
|||
void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) |
|||
{ |
|||
// Ignore
|
|||
} |
|||
|
|||
void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency) |
|||
{ |
|||
// Ignore
|
|||
} |
|||
|
|||
void INetEventListener.OnConnectionRequest(ConnectionRequest request) |
|||
{ |
|||
request.Accept(); |
|||
} |
|||
|
|||
private ulong GetMLAPIClientId(NetPeer peer) |
|||
{ |
|||
ulong clientId = (ulong)peer.Id; |
|||
|
|||
if (hostType == HostType.Server) |
|||
{ |
|||
clientId += 1; |
|||
} |
|||
|
|||
return clientId; |
|||
} |
|||
|
|||
private static int SecondsToMilliseconds(float seconds) |
|||
{ |
|||
return (int)Mathf.Ceil(seconds * 1000); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: db28424c2ae12f64da25c9ecccded6b1 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
{ |
|||
"name": "LiteNEt MLAPI Transport", |
|||
"rootNamespace": "", |
|||
"references": [ |
|||
"Unity.Multiplayer.MLAPI.Runtime" |
|||
], |
|||
"includePlatforms": [], |
|||
"excludePlatforms": [], |
|||
"allowUnsafeCode": false, |
|||
"overrideReferences": false, |
|||
"precompiledReferences": [], |
|||
"autoReferenced": false, |
|||
"defineConstraints": [], |
|||
"versionDefines": [], |
|||
"noEngineReferences": false |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 255e067ad7348fc4682b169db21e7c05 |
|||
AssemblyDefinitionImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
{ |
|||
"name": "com.unity.multiplayer.transport.litenet", |
|||
"displayName": "LiteNet Transport for MLAPI", |
|||
"version": "0.0.1-preview.1", |
|||
"unity": "2019.4", |
|||
"unityRelease": "8f1", |
|||
"description": "LiteNet Transport for MLAPI", |
|||
"keywords": [ |
|||
"unity" |
|||
], |
|||
"dependencies": { |
|||
"com.unity.multiplayer.mlapi": "0.0.1-preview.1" |
|||
}, |
|||
"type": "library", |
|||
"hideInEditor": false |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 1dab2b0d4030b264683f7d356ef17655 |
|||
PackageManifestImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue