Refactors
* Remove dependence on our own stream type and instead use built-in .NET MemoryStreams and BinaryReaders/Writers. * Finish implementing hail packet serialization refactor * Possible regressions: Is "Position" an adequate replacement for the old Stream type's WritePosition and ReadPosition--independent fields? * Untested but compiles on .NET 7
This commit is contained in:
parent
ad9945a036
commit
12f73f302f
|
@ -1,15 +1,15 @@
|
|||
#region
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
using BattleBitAPI.Common.Enums;
|
||||
using BattleBitAPI.Networking;
|
||||
|
||||
using CommunityServerAPI.BattleBitAPI;
|
||||
using CommunityServerAPI.BattleBitAPI.Common.Extentions;
|
||||
using CommunityServerAPI.BattleBitAPI.Packets;
|
||||
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -37,20 +37,8 @@ public class Client
|
|||
mDestination = destination;
|
||||
mPort = port;
|
||||
|
||||
mWriteStream = new Stream()
|
||||
{
|
||||
Buffer = new byte[Const.MaxNetworkPackageSize],
|
||||
InPool = false,
|
||||
ReadPosition = 0,
|
||||
WritePosition = 0
|
||||
};
|
||||
mReadStream = new Stream()
|
||||
{
|
||||
Buffer = new byte[Const.MaxNetworkPackageSize],
|
||||
InPool = false,
|
||||
ReadPosition = 0,
|
||||
WritePosition = 0
|
||||
};
|
||||
mWriteStream = new MemoryStream();
|
||||
mReadStream = new MemoryStream();
|
||||
mKeepAliveBuffer = new byte[4]
|
||||
{
|
||||
0, 0, 0, 0
|
||||
|
@ -87,6 +75,116 @@ public class Client
|
|||
|
||||
public string ServerRulesText { get; set; } = "";
|
||||
|
||||
protected HailPacket CreateHail()
|
||||
{
|
||||
var packet = new HailPacket()
|
||||
{
|
||||
CurrentPlayers = CurrentPlayers,
|
||||
GamePort = GamePort,
|
||||
IsPasswordProtected = IsPasswordProtected,
|
||||
ServerName = ServerName,
|
||||
Gamemode = Gamemode,
|
||||
Map = Map,
|
||||
MapSize = MapSize,
|
||||
DayNight = DayNight,
|
||||
InQueuePlayers = InQueuePlayers,
|
||||
MaxPlayers = MaxPlayers,
|
||||
LoadingScreenText = LoadingScreenText,
|
||||
ServerRulesText = ServerRulesText
|
||||
};
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to connect the client.
|
||||
/// Blocks the thread until connection succeeds or fails.
|
||||
/// </summary>
|
||||
/// <exception cref="Exception"></exception>
|
||||
protected bool HandleConnectionTick()
|
||||
{
|
||||
//Attempt to connect to server async.
|
||||
mIsConnectingFlag = true;
|
||||
|
||||
//Dispose old client if exist.
|
||||
if (mSocket != null)
|
||||
{
|
||||
mSocket.SafeClose();
|
||||
mSocket = null;
|
||||
}
|
||||
|
||||
//Create new client
|
||||
mSocket = new TcpClient();
|
||||
mSocket.SendBufferSize = Const.MaxNetworkPackageSize;
|
||||
mSocket.ReceiveBufferSize = Const.MaxNetworkPackageSize;
|
||||
|
||||
//Attempt to connect.
|
||||
try
|
||||
{
|
||||
var state = mSocket.BeginConnect(mDestination, mPort, (x) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
//Did we connect?
|
||||
mSocket.EndConnect(x);
|
||||
|
||||
var networkStream = mSocket.GetStream();
|
||||
|
||||
//Prepare our hail package and send it.
|
||||
if (!networkStream.TryWritePacket(CreateHail()))
|
||||
throw new Exception("Failed to send hail packet");
|
||||
|
||||
//Read the first byte.
|
||||
var response = mSocket.AwaitResponse();
|
||||
|
||||
switch (response)
|
||||
{
|
||||
case NetworkCommuncation.Accepted:
|
||||
mIsConnectingFlag = false;
|
||||
IsConnected = true;
|
||||
|
||||
// TODO: Do we really want to invoke OnConnectedToServer in this callback?
|
||||
mOnConnectedToServer();
|
||||
break;
|
||||
case NetworkCommuncation.None:
|
||||
throw new Exception("Server did not respond to your connect request.");
|
||||
case NetworkCommuncation.Denied:
|
||||
if (mSocket.Available <= 0)
|
||||
throw new Exception("Server denied our connect request with an unknown reason.");
|
||||
|
||||
string errorString = null;
|
||||
|
||||
using (var readStream = new MemoryStream())
|
||||
{
|
||||
networkStream.CopyTo(readStream, mSocket.Available);
|
||||
errorString = Encoding.UTF8.GetString(readStream.ToArray());
|
||||
|
||||
if (errorString == string.Empty)
|
||||
errorString = null;
|
||||
}
|
||||
|
||||
if (errorString != null)
|
||||
throw new Exception(errorString);
|
||||
throw new Exception("Server denied our connect request with an unknown reason.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
mIsConnectingFlag = false;
|
||||
|
||||
mLogError("Unable to connect to API server: " + e.Message);
|
||||
}
|
||||
}, null); // mSocket.BeginConnect
|
||||
}
|
||||
catch
|
||||
{
|
||||
mIsConnectingFlag = false;
|
||||
}
|
||||
|
||||
return IsConnected;
|
||||
}
|
||||
|
||||
|
||||
// ---- Main Tick ----
|
||||
public void Tick()
|
||||
{
|
||||
|
@ -97,137 +195,9 @@ public class Client
|
|||
//Have we connected?
|
||||
if (!IsConnected)
|
||||
{
|
||||
//Attempt to connect to server async.
|
||||
mIsConnectingFlag = true;
|
||||
|
||||
//Dispose old client if exist.
|
||||
if (mSocket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mSocket.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
mSocket.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
mSocket = null;
|
||||
}
|
||||
|
||||
//Create new client
|
||||
mSocket = new TcpClient();
|
||||
mSocket.SendBufferSize = Const.MaxNetworkPackageSize;
|
||||
mSocket.ReceiveBufferSize = Const.MaxNetworkPackageSize;
|
||||
|
||||
//Attempt to connect.
|
||||
try
|
||||
{
|
||||
var state = mSocket.BeginConnect(mDestination, mPort, (x) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
//Did we connect?
|
||||
mSocket.EndConnect(x);
|
||||
|
||||
var networkStream = mSocket.GetStream();
|
||||
|
||||
//Prepare our hail package and send it.
|
||||
using (var hail = Stream.Get())
|
||||
{
|
||||
hail.Write((byte)NetworkCommuncation.Hail);
|
||||
hail.Write((ushort)GamePort);
|
||||
hail.Write(IsPasswordProtected);
|
||||
hail.Write(ServerName);
|
||||
hail.Write(Gamemode);
|
||||
hail.Write(Map);
|
||||
hail.Write((byte)MapSize);
|
||||
hail.Write((byte)DayNight);
|
||||
hail.Write((byte)CurrentPlayers);
|
||||
hail.Write((byte)InQueuePlayers);
|
||||
hail.Write((byte)MaxPlayers);
|
||||
hail.Write(LoadingScreenText);
|
||||
hail.Write(ServerRulesText);
|
||||
|
||||
//Send our hail package.
|
||||
networkStream.Write(hail.Buffer, 0, hail.WritePosition);
|
||||
networkStream.Flush();
|
||||
}
|
||||
|
||||
//Sadly can not use Task Async here, Unity isn't great with tasks.
|
||||
var watch = Stopwatch.StartNew();
|
||||
|
||||
//Read the first byte.
|
||||
var response = NetworkCommuncation.None;
|
||||
while (watch.ElapsedMilliseconds < Const.HailConnectTimeout)
|
||||
{
|
||||
if (mSocket.Available > 0)
|
||||
{
|
||||
var data = networkStream.ReadByte();
|
||||
if (data >= 0)
|
||||
{
|
||||
response = (NetworkCommuncation)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
//Were we accepted.
|
||||
if (response == NetworkCommuncation.Accepted)
|
||||
{
|
||||
//We are accepted.
|
||||
mIsConnectingFlag = false;
|
||||
IsConnected = true;
|
||||
|
||||
mOnConnectedToServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Did we at least got a response?
|
||||
if (response == NetworkCommuncation.None)
|
||||
throw new Exception("Server did not respond to your connect request.");
|
||||
|
||||
//Try to read our deny reason.
|
||||
if (response == NetworkCommuncation.Denied && mSocket.Available > 0)
|
||||
{
|
||||
string errorString = null;
|
||||
|
||||
using (var readStream = Stream.Get())
|
||||
{
|
||||
readStream.WritePosition = networkStream.Read(readStream.Buffer, 0, mSocket.Available);
|
||||
if (!readStream.TryReadString(out errorString))
|
||||
errorString = null;
|
||||
}
|
||||
|
||||
if (errorString != null)
|
||||
throw new Exception(errorString);
|
||||
}
|
||||
|
||||
throw new Exception("Server denied our connect request with an unknown reason.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
mIsConnectingFlag = false;
|
||||
|
||||
mLogError("Unable to connect to API server: " + e.Message);
|
||||
return;
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
mIsConnectingFlag = false;
|
||||
}
|
||||
|
||||
//We haven't connected yet.
|
||||
return;
|
||||
if (!HandleConnectionTick())
|
||||
// Connection failed
|
||||
return;
|
||||
}
|
||||
|
||||
//We are connected at this point.
|
||||
|
@ -252,63 +222,63 @@ public class Client
|
|||
if (mReadPackageSize == 0)
|
||||
{
|
||||
const int sizeToRead = 4;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.WritePosition;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.Position;
|
||||
|
||||
var read = networkStream.Read(mReadStream.Buffer, mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
networkStream.CopyTo(mReadStream, (int) leftSizeToRead);
|
||||
//if (read <= 0)
|
||||
// throw new Exception("Connection was terminated.");
|
||||
|
||||
mReadStream.WritePosition += read;
|
||||
//mReadStream.Position += read;
|
||||
|
||||
//Did we receive the package?
|
||||
if (mReadStream.WritePosition >= 4)
|
||||
if (mReadStream.Position >= 4)
|
||||
{
|
||||
//Read the package size
|
||||
mReadPackageSize = mReadStream.ReadUInt32();
|
||||
using (var binaryreader = new BinaryReader(mReadStream))
|
||||
{
|
||||
//Read the package size
|
||||
mReadPackageSize = binaryreader.ReadUInt32();
|
||||
|
||||
if (mReadPackageSize > Const.MaxNetworkPackageSize)
|
||||
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
|
||||
|
||||
//Is this keep alive package?
|
||||
if (mReadPackageSize == 0)
|
||||
Console.WriteLine("Keep alive was received.");
|
||||
if (mReadPackageSize > Const.MaxNetworkPackageSize)
|
||||
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
|
||||
|
||||
//Is this keep alive package?
|
||||
if (mReadPackageSize == 0)
|
||||
Console.WriteLine("Keep alive was received.");
|
||||
}
|
||||
//Reset the stream.
|
||||
mReadStream.Reset();
|
||||
mReadStream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var sizeToRead = (int)mReadPackageSize;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.WritePosition;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.Position;
|
||||
|
||||
var read = networkStream.Read(mReadStream.Buffer, mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
|
||||
mReadStream.WritePosition += read;
|
||||
networkStream.CopyTo(mReadStream, (int)leftSizeToRead);
|
||||
//if (read <= 0)
|
||||
// throw new Exception("Connection was terminated.");
|
||||
|
||||
//Do we have the package?
|
||||
if (mReadStream.WritePosition >= mReadPackageSize)
|
||||
if (mReadStream.Position >= mReadPackageSize)
|
||||
{
|
||||
mReadPackageSize = 0;
|
||||
|
||||
mExecutePackage(mReadStream);
|
||||
|
||||
//Reset
|
||||
mReadStream.Reset();
|
||||
mReadStream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Send the network packages.
|
||||
if (mWriteStream.WritePosition > 0)
|
||||
if (mWriteStream.Position > 0)
|
||||
lock (mWriteStream)
|
||||
{
|
||||
if (mWriteStream.WritePosition > 0)
|
||||
if (mWriteStream.Position > 0)
|
||||
{
|
||||
networkStream.Write(mWriteStream.Buffer, 0, mWriteStream.WritePosition);
|
||||
mWriteStream.WritePosition = 0;
|
||||
mWriteStream.CopyTo(networkStream, (int) mWriteStream.Position);
|
||||
mWriteStream.Position = 0;
|
||||
|
||||
mLastPackageSent = Extensions.TickCount;
|
||||
}
|
||||
|
@ -338,9 +308,12 @@ public class Client
|
|||
// ---- Internal ----
|
||||
private void mExecutePackage(Stream stream)
|
||||
{
|
||||
var communcation = (NetworkCommuncation)stream.ReadInt8();
|
||||
switch (communcation)
|
||||
using (var reader = new BinaryReader(stream))
|
||||
{
|
||||
var communcation = (NetworkCommuncation)reader.ReadByte();
|
||||
switch (communcation)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,24 +3,24 @@
|
|||
public static class Const
|
||||
{
|
||||
// ---- Networking ----
|
||||
/// <summary>
|
||||
/// Maximum data size for a single package. 4MB is default.
|
||||
/// </summary>
|
||||
public const int MaxNetworkPackageSize = 1024 * 1024 * 4; //4mb
|
||||
/// <summary>
|
||||
/// How long should server/client wait until connection is determined as timed out when no packages
|
||||
/// is being sent for long time.
|
||||
/// </summary>
|
||||
public const int NetworkTimeout = 60 * 1000; //60 seconds
|
||||
/// <summary>
|
||||
/// How frequently client/server will send keep alive to each other when no message is being sent
|
||||
/// to each other for a while.
|
||||
/// </summary>
|
||||
public const int NetworkKeepAlive = 15 * 1000; //15 seconds
|
||||
/// <summary>
|
||||
/// How long server/client will wait other side to send their hail/initial package. In miliseconds.
|
||||
/// </summary>
|
||||
public const int HailConnectTimeout = 2 * 1000;
|
||||
/// <summary>
|
||||
/// Maximum data size for a single package. 4MB is default.
|
||||
/// </summary>
|
||||
public const int MaxNetworkPackageSize = 1024 * 1024 * 4; //4mb
|
||||
/// <summary>
|
||||
/// How long should server/client wait until connection is determined as timed out when no packages
|
||||
/// is being sent for long time.
|
||||
/// </summary>
|
||||
public const int NetworkTimeout = 60 * 1000; //60 seconds
|
||||
/// <summary>
|
||||
/// How frequently client/server will send keep alive to each other when no message is being sent
|
||||
/// to each other for a while.
|
||||
/// </summary>
|
||||
public const int NetworkKeepAlive = 15 * 1000; //15 seconds
|
||||
/// <summary>
|
||||
/// How long server/client will wait other side to send their hail/initial package. In miliseconds.
|
||||
/// </summary>
|
||||
public const int HailConnectTimeout = 2 * 1000;
|
||||
|
||||
// ---- Server Fields ----
|
||||
public const int MinServerNameLength = 5;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
namespace CommunityServerAPI.BattleBitAPI.Common.Extentions;
|
||||
|
||||
public static class BinaryReaderExtensions
|
||||
{
|
||||
|
||||
}
|
|
@ -1,10 +1,14 @@
|
|||
#region
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using BattleBitAPI.Common.Enums;
|
||||
using BattleBitAPI.Networking;
|
||||
|
||||
using CommunityServerAPI.BattleBitAPI.Packets;
|
||||
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -12,69 +16,124 @@ namespace CommunityServerAPI.BattleBitAPI.Common.Extentions;
|
|||
|
||||
public static class NetworkStreamExtensions
|
||||
{
|
||||
public static async Task<int> Read(this NetworkStream networkStream, Stream outputStream, int size, CancellationToken token = default)
|
||||
public static async Task<int> Read(this Stream networkStream, Stream outputStream, int size, CancellationToken token = default)
|
||||
{
|
||||
var read = 0;
|
||||
var readUntil = outputStream.WritePosition + size;
|
||||
var readUntil = outputStream.Position + size;
|
||||
|
||||
//Ensure we have space.
|
||||
outputStream.EnsureWriteBufferSize(size);
|
||||
//outputStream.EnsureWriteBufferSize(size);
|
||||
|
||||
//Continue reading until we have the package.
|
||||
while (outputStream.WritePosition < readUntil)
|
||||
while (outputStream.Position < readUntil)
|
||||
{
|
||||
var sizeToRead = readUntil - outputStream.WritePosition;
|
||||
var received = await networkStream.ReadAsync(outputStream.Buffer, outputStream.WritePosition, sizeToRead, token);
|
||||
var sizeToRead = readUntil - outputStream.Position;
|
||||
var buffer = new byte[sizeToRead];
|
||||
var received = await networkStream.ReadAsync(buffer, (int) outputStream.Position, (int) sizeToRead, token);
|
||||
if (received <= 0)
|
||||
throw new Exception("NetworkStream was closed.");
|
||||
|
||||
outputStream.Write(buffer);
|
||||
|
||||
read += received;
|
||||
outputStream.WritePosition += received;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the provided packet and send it down the network stream.
|
||||
/// Returns false if an error occured, true otherwise.
|
||||
/// Serialize the provided packet and send it down the network stream.
|
||||
/// Returns false if an error occured, true otherwise.
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
/// <param name="packet"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<bool> TryWritePacket(this NetworkStream self, BasePacket packet)
|
||||
public static bool TryWritePacket(this NetworkStream self, BasePacket packet)
|
||||
{
|
||||
using (var stream = Stream.Get())
|
||||
using (var stream = new BinaryWriter(self))
|
||||
{
|
||||
if (!packet.TryWrite(stream))
|
||||
if (!packet.TryWrite(stream, CancellationToken.None))
|
||||
return false;
|
||||
|
||||
self.Write(stream.Buffer, 0, stream.WritePosition);
|
||||
self.Flush();
|
||||
}
|
||||
self.Flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task<bool> TryRead(this NetworkStream networkStream, Stream outputStream, int size, CancellationToken token = default)
|
||||
public static int TryReadSigned(this Stream self, CancellationToken token, int size)
|
||||
{
|
||||
using (var readStream = new BinaryReader(self))
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 1: return readStream.ReadByte();
|
||||
case 2: return readStream.ReadInt16();
|
||||
case 4: return readStream.ReadInt32();
|
||||
}
|
||||
throw new Exception($"Invalid int size {size}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// !! This blocks the current thread !!
|
||||
/// Await a response code from the other end of the socket.
|
||||
/// Returns "None" if the connection timed out.
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
/// <returns></returns>
|
||||
public static NetworkCommuncation AwaitResponse(this TcpClient self)
|
||||
{
|
||||
var watch = Stopwatch.StartNew();
|
||||
while (watch.ElapsedMilliseconds < Const.HailConnectTimeout)
|
||||
{
|
||||
if (self.Available > 0)
|
||||
{
|
||||
var data = self.GetStream().ReadByte();
|
||||
if (data >= 0)
|
||||
{
|
||||
return (NetworkCommuncation)data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
return NetworkCommuncation.None;
|
||||
}
|
||||
|
||||
public static byte[] ToByteArray(this Stream self)
|
||||
{
|
||||
var buffer = new byte[self.Length];
|
||||
self.Write(buffer);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static async Task<bool> TryRead(this Stream networkStream, Stream outputStream, int size, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var readUntil = outputStream.WritePosition + size;
|
||||
|
||||
|
||||
var read = 0;
|
||||
var readUntil = outputStream.Position + size;
|
||||
|
||||
//Ensure we have space.
|
||||
outputStream.EnsureWriteBufferSize(size);
|
||||
//outputStream.EnsureWriteBufferSize(size);
|
||||
|
||||
//Continue reading until we have the package.
|
||||
while (outputStream.WritePosition < readUntil)
|
||||
while (outputStream.Position < readUntil)
|
||||
{
|
||||
var sizeToRead = readUntil - outputStream.WritePosition;
|
||||
var received = await networkStream.ReadAsync(outputStream.Buffer, outputStream.WritePosition, sizeToRead, token);
|
||||
var sizeToRead = readUntil - outputStream.Position;
|
||||
var buffer = new byte[sizeToRead];
|
||||
var received = await networkStream.ReadAsync(buffer, (int)outputStream.Position, (int)sizeToRead, token);
|
||||
if (received <= 0)
|
||||
throw new Exception("NetworkStream was closed.");
|
||||
outputStream.WritePosition += received;
|
||||
}
|
||||
|
||||
outputStream.Write(buffer);
|
||||
|
||||
read += received;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
|
|
|
@ -1,745 +0,0 @@
|
|||
#region
|
||||
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
using CommunityServerAPI.BattleBitAPI.Common.Extentions;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace BattleBitAPI.Common.Serialization;
|
||||
|
||||
public class Stream : IDisposable
|
||||
{
|
||||
public const int DefaultBufferSize = 1024 * 512;
|
||||
|
||||
#if BIGENDIAN
|
||||
public static readonly bool IsLittleEndian = false;
|
||||
#else
|
||||
public static readonly bool IsLittleEndian = true;
|
||||
#endif
|
||||
|
||||
public byte[] Buffer;
|
||||
public int WritePosition;
|
||||
public int ReadPosition;
|
||||
public bool InPool;
|
||||
|
||||
public bool CanRead(int size)
|
||||
{
|
||||
var readableLenght = WritePosition - ReadPosition;
|
||||
return readableLenght >= size;
|
||||
}
|
||||
|
||||
public void EnsureWriteBufferSize(int requiredSize)
|
||||
{
|
||||
var bufferLenght = Buffer.Length;
|
||||
|
||||
var leftSpace = bufferLenght - WritePosition;
|
||||
if (leftSpace < requiredSize)
|
||||
{
|
||||
var newSize = bufferLenght + Math.Max(requiredSize, 1024);
|
||||
Array.Resize(ref Buffer, newSize);
|
||||
}
|
||||
}
|
||||
|
||||
// -------- Write ------
|
||||
public void Write(byte value)
|
||||
{
|
||||
EnsureWriteBufferSize(1);
|
||||
Buffer[WritePosition] = value;
|
||||
WritePosition += 1;
|
||||
}
|
||||
|
||||
public void Write(bool value)
|
||||
{
|
||||
EnsureWriteBufferSize(1);
|
||||
Buffer[WritePosition] = value ? (byte)1 : (byte)0;
|
||||
WritePosition += 1;
|
||||
}
|
||||
|
||||
public unsafe void Write(short value)
|
||||
{
|
||||
EnsureWriteBufferSize(2);
|
||||
fixed (byte* ptr = &Buffer[WritePosition])
|
||||
{
|
||||
*(short*)ptr = value;
|
||||
}
|
||||
WritePosition += 2;
|
||||
}
|
||||
|
||||
public unsafe void Write(ushort value)
|
||||
{
|
||||
EnsureWriteBufferSize(2);
|
||||
fixed (byte* ptr = &Buffer[WritePosition])
|
||||
{
|
||||
*(ushort*)ptr = value;
|
||||
}
|
||||
WritePosition += 2;
|
||||
}
|
||||
|
||||
public unsafe void Write(int value)
|
||||
{
|
||||
EnsureWriteBufferSize(4);
|
||||
fixed (byte* ptr = &Buffer[WritePosition])
|
||||
{
|
||||
*(int*)ptr = value;
|
||||
}
|
||||
WritePosition += 4;
|
||||
}
|
||||
|
||||
public unsafe void Write(uint value)
|
||||
{
|
||||
EnsureWriteBufferSize(4);
|
||||
fixed (byte* ptr = &Buffer[WritePosition])
|
||||
{
|
||||
*(uint*)ptr = value;
|
||||
}
|
||||
WritePosition += 4;
|
||||
}
|
||||
|
||||
public unsafe void Write(long value)
|
||||
{
|
||||
EnsureWriteBufferSize(8);
|
||||
fixed (byte* ptr = &Buffer[WritePosition])
|
||||
{
|
||||
*(long*)ptr = value;
|
||||
}
|
||||
WritePosition += 8;
|
||||
}
|
||||
|
||||
public unsafe void Write(ulong value)
|
||||
{
|
||||
EnsureWriteBufferSize(8);
|
||||
fixed (byte* ptr = &Buffer[WritePosition])
|
||||
{
|
||||
*(ulong*)ptr = value;
|
||||
}
|
||||
WritePosition += 8;
|
||||
}
|
||||
|
||||
public unsafe void Write(decimal value)
|
||||
{
|
||||
EnsureWriteBufferSize(16);
|
||||
fixed (byte* ptr = &Buffer[WritePosition])
|
||||
{
|
||||
*(decimal*)ptr = value;
|
||||
}
|
||||
WritePosition += 16;
|
||||
}
|
||||
|
||||
public unsafe void Write(double value)
|
||||
{
|
||||
EnsureWriteBufferSize(16);
|
||||
fixed (byte* ptr = &Buffer[WritePosition])
|
||||
{
|
||||
*(double*)ptr = value;
|
||||
}
|
||||
WritePosition += 16;
|
||||
}
|
||||
|
||||
public unsafe void Write(float value)
|
||||
{
|
||||
var intValue = *(int*)&value;
|
||||
EnsureWriteBufferSize(4);
|
||||
fixed (byte* ptr = &Buffer[WritePosition])
|
||||
{
|
||||
*(int*)ptr = intValue;
|
||||
}
|
||||
WritePosition += 4;
|
||||
}
|
||||
|
||||
public unsafe void Write(string value)
|
||||
{
|
||||
var charCount = value.Length;
|
||||
fixed (char* strPtr = value)
|
||||
{
|
||||
var size = Encoding.UTF8.GetByteCount(strPtr, charCount);
|
||||
EnsureWriteBufferSize(size + 2);
|
||||
|
||||
|
||||
fixed (byte* buffPtr = &Buffer[WritePosition])
|
||||
{
|
||||
*(ushort*)buffPtr = (ushort)size;
|
||||
Encoding.UTF8.GetBytes(strPtr, charCount, buffPtr + 2, size);
|
||||
}
|
||||
WritePosition += size + 2;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Write(DateTime value)
|
||||
{
|
||||
var utc = value.ToUniversalTime();
|
||||
|
||||
EnsureWriteBufferSize(8);
|
||||
fixed (byte* ptr = &Buffer[WritePosition])
|
||||
{
|
||||
*(long*)ptr = utc.Ticks;
|
||||
}
|
||||
WritePosition += 8;
|
||||
}
|
||||
|
||||
public unsafe void Write(IPAddress value)
|
||||
{
|
||||
var ip = value.ToUInt();
|
||||
Write(ip);
|
||||
}
|
||||
|
||||
public unsafe void Write(IPEndPoint value)
|
||||
{
|
||||
var ip = value.Address.ToUInt();
|
||||
|
||||
Write(ip);
|
||||
Write((ushort)value.Port);
|
||||
}
|
||||
|
||||
public unsafe void WriteRaw(string value)
|
||||
{
|
||||
var charCount = value.Length;
|
||||
fixed (char* strPtr = value)
|
||||
{
|
||||
var size = Encoding.UTF8.GetByteCount(strPtr, charCount);
|
||||
EnsureWriteBufferSize(size);
|
||||
|
||||
fixed (byte* buffPtr = &Buffer[WritePosition])
|
||||
{
|
||||
Encoding.UTF8.GetBytes(strPtr, charCount, buffPtr, size);
|
||||
}
|
||||
WritePosition += size;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Write<T>(T value) where T : IStreamSerializable
|
||||
{
|
||||
value.Write(this);
|
||||
}
|
||||
|
||||
public void Write(byte[] source, int sourceIndex, int length)
|
||||
{
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
EnsureWriteBufferSize(length);
|
||||
Array.Copy(source, sourceIndex, Buffer, WritePosition, length);
|
||||
WritePosition += length;
|
||||
}
|
||||
|
||||
public void Write(Stream source)
|
||||
{
|
||||
EnsureWriteBufferSize(source.WritePosition);
|
||||
Array.Copy(source.Buffer, 0, Buffer, WritePosition, source.WritePosition);
|
||||
WritePosition += source.WritePosition;
|
||||
}
|
||||
|
||||
public unsafe void WriteAt(byte value, int position)
|
||||
{
|
||||
Buffer[position] = value;
|
||||
}
|
||||
|
||||
public unsafe void WriteAt(short value, int position)
|
||||
{
|
||||
fixed (byte* ptr = &Buffer[position])
|
||||
{
|
||||
*(short*)ptr = value;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void WriteAt(ushort value, int position)
|
||||
{
|
||||
fixed (byte* ptr = &Buffer[position])
|
||||
{
|
||||
*(ushort*)ptr = value;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void WriteAt(int value, int position)
|
||||
{
|
||||
fixed (byte* ptr = &Buffer[position])
|
||||
{
|
||||
*(int*)ptr = value;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void WriteAt(uint value, int position)
|
||||
{
|
||||
fixed (byte* ptr = &Buffer[position])
|
||||
{
|
||||
*(uint*)ptr = value;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void WriteAt(long value, int position)
|
||||
{
|
||||
fixed (byte* ptr = &Buffer[position])
|
||||
{
|
||||
*(long*)ptr = value;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void WriteAt(ulong value, int position)
|
||||
{
|
||||
fixed (byte* ptr = &Buffer[position])
|
||||
{
|
||||
*(ulong*)ptr = value;
|
||||
}
|
||||
}
|
||||
|
||||
// -------- Read ------
|
||||
public byte ReadInt8()
|
||||
{
|
||||
var value = Buffer[ReadPosition];
|
||||
ReadPosition++;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool ReadBool()
|
||||
{
|
||||
var value = Buffer[ReadPosition];
|
||||
ReadPosition++;
|
||||
|
||||
return value == 1;
|
||||
}
|
||||
|
||||
public unsafe short ReadInt16()
|
||||
{
|
||||
short value = 0;
|
||||
|
||||
fixed (byte* pbyte = &Buffer[ReadPosition])
|
||||
{
|
||||
if (ReadPosition % 2 == 0)
|
||||
{
|
||||
value = *(short*)pbyte;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsLittleEndian)
|
||||
value = (short)(*pbyte | (*(pbyte + 1) << 8));
|
||||
else
|
||||
value = (short)((*pbyte << 8) | *(pbyte + 1));
|
||||
}
|
||||
}
|
||||
|
||||
ReadPosition += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
public unsafe ushort ReadUInt16()
|
||||
{
|
||||
ushort value = 0;
|
||||
|
||||
fixed (byte* pbyte = &Buffer[ReadPosition])
|
||||
{
|
||||
if (ReadPosition % 2 == 0)
|
||||
{
|
||||
value = *(ushort*)pbyte;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsLittleEndian)
|
||||
value = (ushort)(*pbyte | (*(pbyte + 1) << 8));
|
||||
else
|
||||
value = (ushort)((*pbyte << 8) | *(pbyte + 1));
|
||||
}
|
||||
}
|
||||
|
||||
ReadPosition += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
public unsafe int ReadInt32()
|
||||
{
|
||||
var value = 0;
|
||||
fixed (byte* pbyte = &Buffer[ReadPosition])
|
||||
{
|
||||
if (ReadPosition % 4 == 0)
|
||||
{
|
||||
value = *(int*)pbyte;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsLittleEndian)
|
||||
value = (int)(*pbyte | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24));
|
||||
else
|
||||
value = (int)((*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | *(pbyte + 3));
|
||||
}
|
||||
}
|
||||
ReadPosition += 4;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public unsafe uint ReadUInt32()
|
||||
{
|
||||
uint value = 0;
|
||||
fixed (byte* pbyte = &Buffer[ReadPosition])
|
||||
{
|
||||
if (ReadPosition % 4 == 0)
|
||||
{
|
||||
value = *(uint*)pbyte;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsLittleEndian)
|
||||
value = (uint)(*pbyte | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24));
|
||||
else
|
||||
value = (uint)((*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | *(pbyte + 3));
|
||||
}
|
||||
}
|
||||
ReadPosition += 4;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public unsafe long ReadInt64()
|
||||
{
|
||||
long value = 0;
|
||||
fixed (byte* pbyte = &Buffer[ReadPosition])
|
||||
{
|
||||
if (ReadPosition % 8 == 0)
|
||||
{
|
||||
value = *(long*)pbyte;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsLittleEndian)
|
||||
{
|
||||
var i1 = *pbyte | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24);
|
||||
var i2 = *(pbyte + 4) | (*(pbyte + 5) << 8) | (*(pbyte + 6) << 16) | (*(pbyte + 7) << 24);
|
||||
value = (uint)i1 | ((long)i2 << 32);
|
||||
}
|
||||
else
|
||||
{
|
||||
var i1 = (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | *(pbyte + 3);
|
||||
var i2 = (*(pbyte + 4) << 24) | (*(pbyte + 5) << 16) | (*(pbyte + 6) << 8) | *(pbyte + 7);
|
||||
value = (uint)i2 | ((long)i1 << 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
ReadPosition += 8;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public unsafe ulong ReadUInt64()
|
||||
{
|
||||
ulong value = 0;
|
||||
fixed (byte* pbyte = &Buffer[ReadPosition])
|
||||
{
|
||||
if (ReadPosition % 8 == 0)
|
||||
{
|
||||
value = *(ulong*)pbyte;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsLittleEndian)
|
||||
{
|
||||
var i1 = *pbyte | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24);
|
||||
var i2 = *(pbyte + 4) | (*(pbyte + 5) << 8) | (*(pbyte + 6) << 16) | (*(pbyte + 7) << 24);
|
||||
value = (uint)i1 | ((ulong)i2 << 32);
|
||||
}
|
||||
else
|
||||
{
|
||||
var i1 = (*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | *(pbyte + 3);
|
||||
var i2 = (*(pbyte + 4) << 24) | (*(pbyte + 5) << 16) | (*(pbyte + 6) << 8) | *(pbyte + 7);
|
||||
value = (uint)i2 | ((ulong)i1 << 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
ReadPosition += 8;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public unsafe decimal ReadInt128()
|
||||
{
|
||||
decimal value = 0;
|
||||
fixed (byte* ptr = &Buffer[ReadPosition])
|
||||
{
|
||||
value = *(decimal*)ptr;
|
||||
}
|
||||
ReadPosition += 16;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public unsafe double ReadDouble()
|
||||
{
|
||||
double value = 0;
|
||||
fixed (byte* ptr = &Buffer[ReadPosition])
|
||||
{
|
||||
value = *(double*)ptr;
|
||||
}
|
||||
ReadPosition += 16;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public unsafe float ReadFloat()
|
||||
{
|
||||
var value = 0;
|
||||
fixed (byte* pbyte = &Buffer[ReadPosition])
|
||||
{
|
||||
if (ReadPosition % 4 == 0)
|
||||
{
|
||||
value = *(int*)pbyte;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsLittleEndian)
|
||||
value = (int)(*pbyte | (*(pbyte + 1) << 8) | (*(pbyte + 2) << 16) | (*(pbyte + 3) << 24));
|
||||
else
|
||||
value = (int)((*pbyte << 24) | (*(pbyte + 1) << 16) | (*(pbyte + 2) << 8) | *(pbyte + 3));
|
||||
}
|
||||
}
|
||||
ReadPosition += 4;
|
||||
|
||||
return *(float*)&value;
|
||||
}
|
||||
|
||||
public DateTime ReadDateTime()
|
||||
{
|
||||
var value = ReadInt64();
|
||||
try
|
||||
{
|
||||
return new DateTime(value, DateTimeKind.Utc);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryReadDateTime(out DateTime time)
|
||||
{
|
||||
var value = ReadInt64();
|
||||
try
|
||||
{
|
||||
time = new DateTime(value, DateTimeKind.Utc);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
time = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public unsafe IPAddress ReadIPAddress()
|
||||
{
|
||||
var ip = ReadUInt32();
|
||||
return new IPAddress(ip);
|
||||
}
|
||||
|
||||
public unsafe IPEndPoint ReadIPEndPoint()
|
||||
{
|
||||
var ip = ReadUInt32();
|
||||
var port = ReadUInt16();
|
||||
|
||||
return new IPEndPoint(ip, port);
|
||||
}
|
||||
|
||||
public T Read<T>() where T : IStreamSerializable
|
||||
{
|
||||
T value = default;
|
||||
value.Read(this);
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte[] ReadByteArray(int lenght)
|
||||
{
|
||||
if (lenght == 0)
|
||||
return new byte[0];
|
||||
|
||||
var newBuffer = new byte[lenght];
|
||||
Array.Copy(Buffer, ReadPosition, newBuffer, 0, lenght);
|
||||
ReadPosition += lenght;
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
public void ReadTo(byte[] buffer, int offset, int size)
|
||||
{
|
||||
Array.Copy(Buffer, ReadPosition, buffer, offset, size);
|
||||
ReadPosition += size;
|
||||
}
|
||||
|
||||
public unsafe string ReadString(int size)
|
||||
{
|
||||
string str;
|
||||
|
||||
#if NETCOREAPP
|
||||
fixed (byte* ptr = &Buffer[ReadPosition])
|
||||
{
|
||||
str = Encoding.UTF8.GetString(ptr, size);
|
||||
}
|
||||
#else
|
||||
str = System.Text.Encoding.UTF8.GetString(Buffer, ReadPosition, size);
|
||||
#endif
|
||||
ReadPosition += size;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public unsafe bool TryReadString(out string str)
|
||||
{
|
||||
if (!CanRead(2))
|
||||
{
|
||||
str = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var size = 0;
|
||||
fixed (byte* ptr = &Buffer[ReadPosition])
|
||||
{
|
||||
size = *(ushort*)ptr;
|
||||
}
|
||||
ReadPosition += 2;
|
||||
|
||||
if (!CanRead(size))
|
||||
{
|
||||
str = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NETCOREAPP
|
||||
fixed (byte* ptr = &Buffer[ReadPosition])
|
||||
{
|
||||
str = Encoding.UTF8.GetString(ptr, size);
|
||||
}
|
||||
#else
|
||||
str = System.Text.Encoding.UTF8.GetString(Buffer, ReadPosition, size);
|
||||
#endif
|
||||
|
||||
ReadPosition += size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public unsafe bool TryReadString(out string str, int maximumSize = ushort.MaxValue)
|
||||
{
|
||||
if (!CanRead(2))
|
||||
{
|
||||
str = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var size = 0;
|
||||
fixed (byte* ptr = &Buffer[ReadPosition])
|
||||
{
|
||||
size = *(ushort*)ptr;
|
||||
}
|
||||
ReadPosition += 2;
|
||||
|
||||
if (size > maximumSize)
|
||||
{
|
||||
str = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CanRead(size))
|
||||
{
|
||||
str = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NETCOREAPP
|
||||
fixed (byte* ptr = &Buffer[ReadPosition])
|
||||
{
|
||||
str = Encoding.UTF8.GetString(ptr, size);
|
||||
}
|
||||
#else
|
||||
str = System.Text.Encoding.UTF8.GetString(Buffer, ReadPosition, size);
|
||||
#endif
|
||||
|
||||
ReadPosition += size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public unsafe bool TrySkipString()
|
||||
{
|
||||
if (!CanRead(2))
|
||||
return false;
|
||||
|
||||
var size = 0;
|
||||
fixed (byte* ptr = &Buffer[ReadPosition])
|
||||
{
|
||||
size = *(ushort*)ptr;
|
||||
}
|
||||
ReadPosition += 2;
|
||||
|
||||
if (!CanRead(size))
|
||||
return false;
|
||||
|
||||
ReadPosition += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int NumberOfBytesReadable => WritePosition - ReadPosition;
|
||||
|
||||
public void SkipWriting(int size)
|
||||
{
|
||||
EnsureWriteBufferSize(size);
|
||||
WritePosition += size;
|
||||
}
|
||||
|
||||
public void SkipReading(int size)
|
||||
{
|
||||
ReadPosition += size;
|
||||
}
|
||||
|
||||
// -------- Finalizing ------
|
||||
public byte[] AsByteArrayData()
|
||||
{
|
||||
var data = new byte[WritePosition];
|
||||
Array.Copy(Buffer, 0, data, 0, WritePosition);
|
||||
return data;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ReadPosition = 0;
|
||||
WritePosition = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (InPool)
|
||||
return;
|
||||
InPool = true;
|
||||
|
||||
lock (mPool)
|
||||
{
|
||||
mPool.Enqueue(this);
|
||||
}
|
||||
}
|
||||
|
||||
// ------- Pool -----
|
||||
private static Queue<Stream> mPool = new(1024 * 256);
|
||||
|
||||
public static Stream Get()
|
||||
{
|
||||
lock (mPool)
|
||||
{
|
||||
if (mPool.Count > 0)
|
||||
{
|
||||
var item = mPool.Dequeue();
|
||||
item.WritePosition = 0;
|
||||
item.ReadPosition = 0;
|
||||
item.InPool = false;
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return new Stream()
|
||||
{
|
||||
Buffer = new byte[DefaultBufferSize],
|
||||
InPool = false,
|
||||
ReadPosition = 0,
|
||||
WritePosition = 0
|
||||
};
|
||||
}
|
||||
}
|
|
@ -13,20 +13,20 @@ public class ThreadSafe<T>
|
|||
|
||||
public SafeWriteHandle GetWriteHandle()
|
||||
{
|
||||
return new(mLock);
|
||||
return new SafeWriteHandle(mLock);
|
||||
}
|
||||
|
||||
public SafeReadHandle GetReadHandle()
|
||||
{
|
||||
return new(mLock);
|
||||
return new SafeReadHandle(mLock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swaps current value with new value and returns old one.
|
||||
/// </summary>
|
||||
/// <param name="newValue"></param>
|
||||
/// <returns>Old value</returns>
|
||||
public T SwapValue(T newValue)
|
||||
/// <summary>
|
||||
/// Swaps current value with new value and returns old one.
|
||||
/// </summary>
|
||||
/// <param name="newValue"></param>
|
||||
/// <returns>Old value</returns>
|
||||
public T SwapValue(T newValue)
|
||||
{
|
||||
using (new SafeWriteHandle(mLock))
|
||||
{
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#region
|
||||
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CommunityServerAPI.BattleBitAPI.Packets;
|
||||
|
@ -14,5 +12,13 @@ public abstract class BasePacket
|
|||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
/// <returns></returns>
|
||||
public abstract bool TryWrite(Stream destination);
|
||||
public abstract bool TryWrite(BinaryWriter destination, CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to read this packet type from the stream.
|
||||
/// Returns true if the read was successful.
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public abstract bool TryRead(BinaryReader source, CancellationToken token);
|
||||
}
|
||||
|
|
|
@ -3,29 +3,27 @@
|
|||
using BattleBitAPI.Common.Enums;
|
||||
using BattleBitAPI.Networking;
|
||||
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CommunityServerAPI.BattleBitAPI.Packets;
|
||||
|
||||
public class HailPacket : BasePacket
|
||||
{
|
||||
public byte CurrentPlayers;
|
||||
public int CurrentPlayers;
|
||||
public MapDayNight DayNight;
|
||||
public string Gamemode;
|
||||
|
||||
public ushort GamePort;
|
||||
public byte InQueuePlayers;
|
||||
public int GamePort;
|
||||
public int InQueuePlayers;
|
||||
public bool IsPasswordProtected;
|
||||
public string LoadingScreenText;
|
||||
public string Map;
|
||||
public MapSize MapSize;
|
||||
public byte MaxPlayers;
|
||||
public int MaxPlayers;
|
||||
public string ServerName;
|
||||
public string ServerRulesText;
|
||||
|
||||
public override bool TryWrite(Stream destination)
|
||||
public override bool TryWrite(BinaryWriter destination, CancellationToken token)
|
||||
{
|
||||
destination.Write((byte)NetworkCommuncation.Hail);
|
||||
destination.Write((ushort)GamePort);
|
||||
|
@ -43,4 +41,53 @@ public class HailPacket : BasePacket
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TODO: DO we want exceptions here, or do we just want to return false?
|
||||
/// Having exceptions goes against general .NET semantics, but it'd be good for debugging.
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public override bool TryRead(BinaryReader source, CancellationToken token)
|
||||
{
|
||||
//Read port
|
||||
GamePort = source.ReadUInt16();
|
||||
|
||||
//Read is Port protected
|
||||
IsPasswordProtected = source.ReadBoolean();
|
||||
|
||||
// TODO: Can this be used as a DOS attack with large strings?
|
||||
// Ensure that the length of the hail packet is capped.
|
||||
ServerName = source.ReadString();
|
||||
|
||||
if (ServerName.Length < Const.MinServerNameLength || ServerName.Length > Const.MaxServerNameLength)
|
||||
throw new Exception("Invalid server name size");
|
||||
|
||||
//Read the gamemode
|
||||
Gamemode = source.ReadString();
|
||||
if (Gamemode.Length < Const.MinGamemodeNameLength || Gamemode.Length > Const.MaxGamemodeNameLength)
|
||||
throw new Exception("Invalid gamemode size");
|
||||
|
||||
Map = source.ReadString();
|
||||
if (Map.Length < Const.MinMapNameLength || Map.Length > Const.MaxMapNameLength)
|
||||
throw new Exception("Invalid map size");
|
||||
|
||||
MapSize = (MapSize)source.ReadByte();
|
||||
DayNight = (MapDayNight)source.ReadByte();
|
||||
CurrentPlayers = source.ReadByte();
|
||||
InQueuePlayers = source.ReadByte();
|
||||
MaxPlayers = source.ReadByte();
|
||||
|
||||
LoadingScreenText = source.ReadString();
|
||||
if (LoadingScreenText.Length < Const.MinLoadingScreenTextLength || LoadingScreenText.Length > Const.MaxLoadingScreenTextLength)
|
||||
throw new Exception("Invalid server Loading Screen Text Size");
|
||||
|
||||
ServerRulesText = source.ReadString();
|
||||
if (ServerRulesText.Length < Const.MinServerRulesTextLength || ServerRulesText.Length > Const.MaxServerRulesTextLength)
|
||||
throw new Exception("Invalid server Server Rules Text Size");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,15 +9,13 @@ using BattleBitAPI.Networking;
|
|||
using CommunityServerAPI.BattleBitAPI;
|
||||
using CommunityServerAPI.BattleBitAPI.Common.Extentions;
|
||||
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace BattleBitAPI.Server;
|
||||
|
||||
public class GameServer
|
||||
{
|
||||
// ---- Private Variables ----
|
||||
// ---- Private Variables ----
|
||||
private byte[] mKeepAliveBuffer;
|
||||
private long mLastPackageReceived;
|
||||
private long mLastPackageSent;
|
||||
|
@ -25,7 +23,7 @@ public class GameServer
|
|||
private Stream mReadStream;
|
||||
private Stream mWriteStream;
|
||||
|
||||
// ---- Constrction ----
|
||||
// ---- Constrction ----
|
||||
public GameServer(TcpClient socket, IPAddress iP, int port, bool isPasswordProtected, string serverName, string gamemode, string map, MapSize mapSize, MapDayNight dayNight, int currentPlayers, int inQueuePlayers, int maxPlayers, string loadingScreenText, string serverRulesText)
|
||||
{
|
||||
IsConnected = true;
|
||||
|
@ -47,20 +45,8 @@ public class GameServer
|
|||
|
||||
TerminationReason = string.Empty;
|
||||
|
||||
mWriteStream = new Stream()
|
||||
{
|
||||
Buffer = new byte[Const.MaxNetworkPackageSize],
|
||||
InPool = false,
|
||||
ReadPosition = 0,
|
||||
WritePosition = 0
|
||||
};
|
||||
mReadStream = new Stream()
|
||||
{
|
||||
Buffer = new byte[Const.MaxNetworkPackageSize],
|
||||
InPool = false,
|
||||
ReadPosition = 0,
|
||||
WritePosition = 0
|
||||
};
|
||||
mWriteStream = new MemoryStream();
|
||||
mReadStream = new MemoryStream();
|
||||
mKeepAliveBuffer = new byte[4]
|
||||
{
|
||||
0, 0, 0, 0
|
||||
|
@ -70,13 +56,13 @@ public class GameServer
|
|||
mLastPackageSent = Extensions.TickCount;
|
||||
}
|
||||
|
||||
// ---- Public Variables ----
|
||||
// ---- Public Variables ----
|
||||
public TcpClient Socket { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is game server connected to our server?
|
||||
/// </summary>
|
||||
public bool IsConnected { get; private set; }
|
||||
/// <summary>
|
||||
/// Is game server connected to our server?
|
||||
/// </summary>
|
||||
public bool IsConnected { get; private set; }
|
||||
|
||||
public IPAddress GameIP { get; private set; }
|
||||
|
||||
|
@ -104,10 +90,10 @@ public class GameServer
|
|||
|
||||
public string ServerRulesText { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reason why connection was terminated.
|
||||
/// </summary>
|
||||
public string TerminationReason { get; private set; }
|
||||
/// <summary>
|
||||
/// Reason why connection was terminated.
|
||||
/// </summary>
|
||||
public string TerminationReason { get; private set; }
|
||||
|
||||
// ---- Tick ----
|
||||
public async Task Tick()
|
||||
|
@ -135,63 +121,63 @@ public class GameServer
|
|||
if (mReadPackageSize == 0)
|
||||
{
|
||||
const int sizeToRead = 4;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.WritePosition;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.Position;
|
||||
|
||||
var read = await networkStream.ReadAsync(mReadStream.Buffer, mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
networkStream.CopyTo(mReadStream, (int) leftSizeToRead);
|
||||
if (leftSizeToRead <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
|
||||
mReadStream.WritePosition += read;
|
||||
|
||||
//Did we receive the package?
|
||||
if (mReadStream.WritePosition >= 4)
|
||||
if (mReadStream.Position >= 4)
|
||||
{
|
||||
//Read the package size
|
||||
mReadPackageSize = mReadStream.ReadUInt32();
|
||||
using (var reader = new BinaryReader(mReadStream))
|
||||
{
|
||||
//Read the package size
|
||||
mReadPackageSize = reader.ReadUInt32();
|
||||
|
||||
if (mReadPackageSize > Const.MaxNetworkPackageSize)
|
||||
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
|
||||
if (mReadPackageSize > Const.MaxNetworkPackageSize)
|
||||
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
|
||||
|
||||
//Is this keep alive package?
|
||||
if (mReadPackageSize == 0)
|
||||
Console.WriteLine("Keep alive was received.");
|
||||
//Is this keep alive package?
|
||||
if (mReadPackageSize == 0)
|
||||
Console.WriteLine("Keep alive was received.");
|
||||
}
|
||||
|
||||
//Reset the stream.
|
||||
mReadStream.Reset();
|
||||
mReadStream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var sizeToRead = (int)mReadPackageSize;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.WritePosition;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.Position;
|
||||
|
||||
var read = await networkStream.ReadAsync(mReadStream.Buffer, mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
if (leftSizeToRead <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
|
||||
mReadStream.WritePosition += read;
|
||||
networkStream.CopyTo(mReadStream, (int) leftSizeToRead);
|
||||
|
||||
//Do we have the package?
|
||||
if (mReadStream.WritePosition >= mReadPackageSize)
|
||||
if (mReadStream.Position >= mReadPackageSize)
|
||||
{
|
||||
mReadPackageSize = 0;
|
||||
|
||||
await mExecutePackage(mReadStream);
|
||||
|
||||
//Reset
|
||||
mReadStream.Reset();
|
||||
mReadStream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Send the network packages.
|
||||
if (mWriteStream.WritePosition > 0)
|
||||
if (mWriteStream.Position > 0)
|
||||
lock (mWriteStream)
|
||||
{
|
||||
if (mWriteStream.WritePosition > 0)
|
||||
if (mWriteStream.Position > 0)
|
||||
{
|
||||
networkStream.Write(mWriteStream.Buffer, 0, mWriteStream.WritePosition);
|
||||
mWriteStream.WritePosition = 0;
|
||||
mWriteStream.CopyTo(networkStream, (int) mWriteStream.Position);
|
||||
mWriteStream.Seek( 0, SeekOrigin.Begin );
|
||||
|
||||
mLastPackageSent = Extensions.TickCount;
|
||||
}
|
||||
|
@ -221,9 +207,12 @@ public class GameServer
|
|||
// ---- Internal ----
|
||||
private async Task mExecutePackage(Stream stream)
|
||||
{
|
||||
var communcation = (NetworkCommuncation)stream.ReadInt8();
|
||||
switch (communcation)
|
||||
using (var reader = new BinaryReader(stream))
|
||||
{
|
||||
var communcation = (NetworkCommuncation)reader.ReadByte();
|
||||
switch (communcation)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,4 +227,4 @@ public class GameServer
|
|||
mReadStream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,7 @@ using BattleBitAPI.Networking;
|
|||
|
||||
using CommunityServerAPI.BattleBitAPI;
|
||||
using CommunityServerAPI.BattleBitAPI.Common.Extentions;
|
||||
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
using CommunityServerAPI.BattleBitAPI.Packets;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -17,40 +16,40 @@ namespace BattleBitAPI.Server;
|
|||
|
||||
public class ServerListener : IDisposable
|
||||
{
|
||||
// --- Private ---
|
||||
// --- Private ---
|
||||
private TcpListener mSocket;
|
||||
|
||||
// --- Construction ---
|
||||
// --- Construction ---
|
||||
public ServerListener()
|
||||
{
|
||||
}
|
||||
|
||||
// --- Public ---
|
||||
// --- Public ---
|
||||
public bool IsListening { get; private set; }
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public int ListeningPort { get; private set; }
|
||||
|
||||
// --- Events ---
|
||||
/// <summary>
|
||||
/// Fired when an attempt made to connect to the server.
|
||||
/// Connection will be allowed if function returns true, otherwise will be blocked.
|
||||
/// Default, any connection attempt will be accepted.
|
||||
/// </summary>
|
||||
public Func<IPAddress, Task<bool>> OnGameServerConnecting { get; set; }
|
||||
// --- Events ---
|
||||
/// <summary>
|
||||
/// Fired when an attempt made to connect to the server.
|
||||
/// Connection will be allowed if function returns true, otherwise will be blocked.
|
||||
/// Default, any connection attempt will be accepted.
|
||||
/// </summary>
|
||||
public Func<IPAddress, Task<bool>> OnGameServerConnecting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a game server connects.
|
||||
/// </summary>
|
||||
public Func<GameServer, Task> OnGameServerConnected { get; set; }
|
||||
/// <summary>
|
||||
/// Fired when a game server connects.
|
||||
/// </summary>
|
||||
public Func<GameServer, Task> OnGameServerConnected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a game server disconnects. Check (server.TerminationReason) to see the reason.
|
||||
/// </summary>
|
||||
public Func<GameServer, Task> OnGameServerDisconnected { get; set; }
|
||||
/// <summary>
|
||||
/// Fired when a game server disconnects. Check (server.TerminationReason) to see the reason.
|
||||
/// </summary>
|
||||
public Func<GameServer, Task> OnGameServerDisconnected { get; set; }
|
||||
|
||||
// --- Disposing ---
|
||||
// --- Disposing ---
|
||||
public void Dispose()
|
||||
{
|
||||
//Already disposed?
|
||||
|
@ -137,191 +136,42 @@ public class ServerListener : IDisposable
|
|||
{
|
||||
using (var source = new CancellationTokenSource(Const.HailConnectTimeout))
|
||||
{
|
||||
using (var readStream = Stream.Get())
|
||||
using (var readStream = new MemoryStream())
|
||||
{
|
||||
var networkStream = client.GetStream();
|
||||
|
||||
//Read package type
|
||||
using (var reader = new BinaryReader(readStream))
|
||||
{
|
||||
readStream.Reset();
|
||||
readStream.Seek(0, SeekOrigin.Begin);
|
||||
if (!await networkStream.TryRead(readStream, 1, source.Token))
|
||||
throw new Exception("Unable to read the package type");
|
||||
var type = (NetworkCommuncation)readStream.ReadInt8();
|
||||
var type = (NetworkCommuncation)reader.ReadByte();
|
||||
if (type != NetworkCommuncation.Hail)
|
||||
throw new Exception("Incoming package wasn't hail.");
|
||||
|
||||
var packet = new HailPacket();
|
||||
if (!packet.TryRead(reader, CancellationToken.None))
|
||||
throw new Exception("Failed to deserialize packet");
|
||||
|
||||
server = new GameServer(client,
|
||||
ip,
|
||||
packet.GamePort,
|
||||
packet.IsPasswordProtected,
|
||||
packet.ServerName,
|
||||
packet.Gamemode,
|
||||
packet.Map,
|
||||
packet.MapSize,
|
||||
packet.DayNight,
|
||||
packet.CurrentPlayers,
|
||||
packet.InQueuePlayers,
|
||||
packet.MaxPlayers,
|
||||
packet.LoadingScreenText,
|
||||
packet.ServerRulesText);
|
||||
|
||||
//Send accepted notification.
|
||||
networkStream.WriteByte((byte)NetworkCommuncation.Accepted);
|
||||
}
|
||||
|
||||
//Read port
|
||||
int gamePort;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 2, source.Token))
|
||||
throw new Exception("Unable to read the Port");
|
||||
gamePort = readStream.ReadUInt16();
|
||||
}
|
||||
|
||||
//Read is Port protected
|
||||
bool isPasswordProtected;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 1, source.Token))
|
||||
throw new Exception("Unable to read the IsPasswordProtected");
|
||||
isPasswordProtected = readStream.ReadBool();
|
||||
}
|
||||
|
||||
//Read the server name
|
||||
string serverName;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 2, source.Token))
|
||||
throw new Exception("Unable to read the ServerName Size");
|
||||
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinServerNameLength || stringSize > Const.MaxServerNameLength)
|
||||
throw new Exception("Invalid server name size");
|
||||
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the ServerName");
|
||||
|
||||
serverName = readStream.ReadString(stringSize);
|
||||
}
|
||||
|
||||
//Read the gamemode
|
||||
string gameMode;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 2, source.Token))
|
||||
throw new Exception("Unable to read the gamemode Size");
|
||||
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinGamemodeNameLength || stringSize > Const.MaxGamemodeNameLength)
|
||||
throw new Exception("Invalid gamemode size");
|
||||
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the gamemode");
|
||||
|
||||
gameMode = readStream.ReadString(stringSize);
|
||||
}
|
||||
|
||||
//Read the gamemap
|
||||
string gamemap;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 2, source.Token))
|
||||
throw new Exception("Unable to read the map size");
|
||||
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinMapNameLength || stringSize > Const.MaxMapNameLength)
|
||||
throw new Exception("Invalid map size");
|
||||
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the map");
|
||||
|
||||
gamemap = readStream.ReadString(stringSize);
|
||||
}
|
||||
|
||||
//Read the mapSize
|
||||
MapSize size;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 1, source.Token))
|
||||
throw new Exception("Unable to read the MapSize");
|
||||
size = (MapSize)readStream.ReadInt8();
|
||||
}
|
||||
|
||||
//Read the day night
|
||||
MapDayNight dayNight;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 1, source.Token))
|
||||
throw new Exception("Unable to read the MapDayNight");
|
||||
dayNight = (MapDayNight)readStream.ReadInt8();
|
||||
}
|
||||
|
||||
//Current Players
|
||||
int currentPlayers;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 1, source.Token))
|
||||
throw new Exception("Unable to read the Current Players");
|
||||
currentPlayers = readStream.ReadInt8();
|
||||
}
|
||||
|
||||
//Queue Players
|
||||
int queuePlayers;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 1, source.Token))
|
||||
throw new Exception("Unable to read the Queue Players");
|
||||
queuePlayers = readStream.ReadInt8();
|
||||
}
|
||||
|
||||
//Max Players
|
||||
int maxPlayers;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 1, source.Token))
|
||||
throw new Exception("Unable to read the Max Players");
|
||||
maxPlayers = readStream.ReadInt8();
|
||||
}
|
||||
|
||||
//Read Loading Screen Text
|
||||
string loadingScreenText;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 2, source.Token))
|
||||
throw new Exception("Unable to read the Loading Screen Text Size");
|
||||
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinLoadingScreenTextLength || stringSize > Const.MaxLoadingScreenTextLength)
|
||||
throw new Exception("Invalid server Loading Screen Text Size");
|
||||
|
||||
if (stringSize > 0)
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the Loading Screen Text");
|
||||
|
||||
loadingScreenText = readStream.ReadString(stringSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadingScreenText = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
//Read Server Rules Text
|
||||
string serverRulesText;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 2, source.Token))
|
||||
throw new Exception("Unable to read the Server Rules Text Size");
|
||||
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinServerRulesTextLength || stringSize > Const.MaxServerRulesTextLength)
|
||||
throw new Exception("Invalid server Server Rules Text Size");
|
||||
|
||||
if (stringSize > 0)
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the Server Rules Text");
|
||||
|
||||
serverRulesText = readStream.ReadString(stringSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
serverRulesText = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
server = new GameServer(client, ip, gamePort, isPasswordProtected, serverName, gameMode, gamemap, size, dayNight, currentPlayers, queuePlayers, maxPlayers, loadingScreenText, serverRulesText);
|
||||
|
||||
//Send accepted notification.
|
||||
networkStream.WriteByte((byte)NetworkCommuncation.Accepted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -332,13 +182,10 @@ public class ServerListener : IDisposable
|
|||
Console.WriteLine(e.Message);
|
||||
|
||||
var networkStream = client.GetStream();
|
||||
using (var pck = Stream.Get())
|
||||
using (var pck = new BinaryWriter(networkStream))
|
||||
{
|
||||
pck.Write((byte)NetworkCommuncation.Denied);
|
||||
pck.Write(e.Message);
|
||||
|
||||
//Send denied notification.
|
||||
networkStream.Write(pck.Buffer, 0, pck.WritePosition);
|
||||
}
|
||||
await networkStream.FlushAsync();
|
||||
}
|
||||
|
@ -374,4 +221,4 @@ public class ServerListener : IDisposable
|
|||
if (OnGameServerDisconnected != null)
|
||||
await OnGameServerDisconnected.Invoke(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue