Reformat, Begin packet refactors
* Reformat solution * Start to work on packet refactors to centralize serialization logic * Expand gitignore
This commit is contained in:
parent
e3c04d2363
commit
7d40531231
|
@ -1,2 +1,37 @@
|
|||
*.swp
|
||||
*.*~
|
||||
project.lock.json
|
||||
.DS_Store
|
||||
*.pyc
|
||||
nupkg/
|
||||
|
||||
.vs/CommunityServerAPI/v17/.suo
|
||||
# Visual Studio Code
|
||||
.vscode
|
||||
|
||||
# Rider
|
||||
.idea
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
msbuild.log
|
||||
msbuild.err
|
||||
msbuild.wrn
|
||||
|
||||
# Visual Studio 2015
|
||||
.vs/
|
||||
|
|
|
@ -1,358 +1,391 @@
|
|||
using BattleBitAPI.Common.Enums;
|
||||
using BattleBitAPI.Common.Extentions;
|
||||
using BattleBitAPI.Common.Serialization;
|
||||
using BattleBitAPI.Networking;
|
||||
using CommunityServerAPI.BattleBitAPI;
|
||||
using System;
|
||||
#region
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace BattleBitAPI.Client
|
||||
using BattleBitAPI.Common.Enums;
|
||||
using BattleBitAPI.Networking;
|
||||
|
||||
using CommunityServerAPI.BattleBitAPI;
|
||||
using CommunityServerAPI.BattleBitAPI.Common.Extentions;
|
||||
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace BattleBitAPI.Client;
|
||||
|
||||
// This class was created mainly for Unity Engine, for this reason, Task async was not implemented.
|
||||
public class Client
|
||||
{
|
||||
// This class was created mainly for Unity Engine, for this reason, Task async was not implemented.
|
||||
public class Client
|
||||
{
|
||||
// ---- Public Variables ----
|
||||
public bool IsConnected { get; private set; }
|
||||
public int GamePort { get; set; } = 30000;
|
||||
public bool IsPasswordProtected { get; set; } = false;
|
||||
public string ServerName { get; set; } = "";
|
||||
public string Gamemode { get; set; } = "";
|
||||
public string Map { get; set; } = "";
|
||||
public MapSize MapSize { get; set; } = MapSize._16vs16;
|
||||
public MapDayNight DayNight { get; set; } = MapDayNight.Day;
|
||||
public int CurrentPlayers { get; set; } = 0;
|
||||
public int InQueuePlayers { get; set; } = 0;
|
||||
public int MaxPlayers { get; set; } = 16;
|
||||
public string LoadingScreenText { get; set; } = "";
|
||||
public string ServerRulesText { get; set; } = "";
|
||||
private string mDestination;
|
||||
private bool mIsConnectingFlag;
|
||||
private byte[] mKeepAliveBuffer;
|
||||
private long mLastPackageReceived;
|
||||
private long mLastPackageSent;
|
||||
private int mPort;
|
||||
private uint mReadPackageSize;
|
||||
private Stream mReadStream;
|
||||
|
||||
// ---- Private Variables ----
|
||||
private TcpClient mSocket;
|
||||
private string mDestination;
|
||||
private int mPort;
|
||||
private byte[] mKeepAliveBuffer;
|
||||
private Common.Serialization.Stream mWriteStream;
|
||||
private Common.Serialization.Stream mReadStream;
|
||||
private uint mReadPackageSize;
|
||||
private long mLastPackageReceived;
|
||||
private long mLastPackageSent;
|
||||
private bool mIsConnectingFlag;
|
||||
// ---- Private Variables ----
|
||||
private TcpClient mSocket;
|
||||
private Stream mWriteStream;
|
||||
|
||||
// ---- Construction ----
|
||||
public Client(string destination, int port)
|
||||
{
|
||||
this.mDestination = destination;
|
||||
this.mPort = port;
|
||||
// ---- Construction ----
|
||||
public Client(string destination, int port)
|
||||
{
|
||||
mDestination = destination;
|
||||
mPort = port;
|
||||
|
||||
this.mWriteStream = new Common.Serialization.Stream()
|
||||
{
|
||||
Buffer = new byte[Const.MaxNetworkPackageSize],
|
||||
InPool = false,
|
||||
ReadPosition = 0,
|
||||
WritePosition = 0,
|
||||
};
|
||||
this.mReadStream = new Common.Serialization.Stream()
|
||||
{
|
||||
Buffer = new byte[Const.MaxNetworkPackageSize],
|
||||
InPool = false,
|
||||
ReadPosition = 0,
|
||||
WritePosition = 0,
|
||||
};
|
||||
this.mKeepAliveBuffer = new byte[4]
|
||||
{
|
||||
0,0,0,0,
|
||||
};
|
||||
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
|
||||
};
|
||||
mKeepAliveBuffer = new byte[4]
|
||||
{
|
||||
0, 0, 0, 0
|
||||
};
|
||||
|
||||
this.mLastPackageReceived = Extentions.TickCount;
|
||||
this.mLastPackageSent = Extentions.TickCount;
|
||||
}
|
||||
mLastPackageReceived = Extensions.TickCount;
|
||||
mLastPackageSent = Extensions.TickCount;
|
||||
}
|
||||
|
||||
// ---- Main Tick ----
|
||||
public void Tick()
|
||||
{
|
||||
//Are we connecting?
|
||||
if (mIsConnectingFlag)
|
||||
return;
|
||||
// ---- Public Variables ----
|
||||
public bool IsConnected { get; private set; }
|
||||
|
||||
//Have we connected?
|
||||
if (!this.IsConnected)
|
||||
{
|
||||
//Attempt to connect to server async.
|
||||
this.mIsConnectingFlag = true;
|
||||
public int GamePort { get; set; } = 30000;
|
||||
|
||||
//Dispose old client if exist.
|
||||
if (this.mSocket != null)
|
||||
{
|
||||
try { this.mSocket.Close(); } catch { }
|
||||
try { this.mSocket.Dispose(); } catch { }
|
||||
this.mSocket = null;
|
||||
}
|
||||
public bool IsPasswordProtected { get; set; } = false;
|
||||
|
||||
//Create new client
|
||||
this.mSocket = new TcpClient();
|
||||
this.mSocket.SendBufferSize = Const.MaxNetworkPackageSize;
|
||||
this.mSocket.ReceiveBufferSize = Const.MaxNetworkPackageSize;
|
||||
public string ServerName { get; set; } = "";
|
||||
|
||||
//Attempt to connect.
|
||||
try
|
||||
{
|
||||
var state = mSocket.BeginConnect(mDestination, mPort, (x) =>
|
||||
{
|
||||
public string Gamemode { get; set; } = "";
|
||||
|
||||
try
|
||||
{
|
||||
//Did we connect?
|
||||
mSocket.EndConnect(x);
|
||||
public string Map { get; set; } = "";
|
||||
|
||||
var networkStream = mSocket.GetStream();
|
||||
public MapSize MapSize { get; set; } = MapSize._16vs16;
|
||||
|
||||
//Prepare our hail package and send it.
|
||||
using (var hail = BattleBitAPI.Common.Serialization.Stream.Get())
|
||||
{
|
||||
hail.Write((byte)NetworkCommuncation.Hail);
|
||||
hail.Write((ushort)this.GamePort);
|
||||
hail.Write(this.IsPasswordProtected);
|
||||
hail.Write(this.ServerName);
|
||||
hail.Write(this.Gamemode);
|
||||
hail.Write(this.Map);
|
||||
hail.Write((byte)this.MapSize);
|
||||
hail.Write((byte)this.DayNight);
|
||||
hail.Write((byte)this.CurrentPlayers);
|
||||
hail.Write((byte)this.InQueuePlayers);
|
||||
hail.Write((byte)this.MaxPlayers);
|
||||
hail.Write(this.LoadingScreenText);
|
||||
hail.Write(this.ServerRulesText);
|
||||
public MapDayNight DayNight { get; set; } = MapDayNight.Day;
|
||||
|
||||
//Send our hail package.
|
||||
networkStream.Write(hail.Buffer, 0, hail.WritePosition);
|
||||
networkStream.Flush();
|
||||
}
|
||||
public int CurrentPlayers { get; set; } = 0;
|
||||
|
||||
//Sadly can not use Task Async here, Unity isn't great with tasks.
|
||||
var watch = Stopwatch.StartNew();
|
||||
public int InQueuePlayers { get; set; } = 0;
|
||||
|
||||
//Read the first byte.
|
||||
NetworkCommuncation 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);
|
||||
}
|
||||
public int MaxPlayers { get; set; } = 16;
|
||||
|
||||
//Were we accepted.
|
||||
if (response == NetworkCommuncation.Accepted)
|
||||
{
|
||||
//We are accepted.
|
||||
this.mIsConnectingFlag = false;
|
||||
this.IsConnected = true;
|
||||
public string LoadingScreenText { get; set; } = "";
|
||||
|
||||
mOnConnectedToServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Did we at least got a response?
|
||||
if (response == NetworkCommuncation.None)
|
||||
throw new Exception("Server did not respond to your connect request.");
|
||||
public string ServerRulesText { get; set; } = "";
|
||||
|
||||
//Try to read our deny reason.
|
||||
if (response == NetworkCommuncation.Denied && mSocket.Available > 0)
|
||||
{
|
||||
string errorString = null;
|
||||
// ---- Main Tick ----
|
||||
public void Tick()
|
||||
{
|
||||
//Are we connecting?
|
||||
if (mIsConnectingFlag)
|
||||
return;
|
||||
|
||||
using (var readStream = BattleBitAPI.Common.Serialization.Stream.Get())
|
||||
{
|
||||
readStream.WritePosition = networkStream.Read(readStream.Buffer, 0, mSocket.Available);
|
||||
if (!readStream.TryReadString(out errorString))
|
||||
errorString = null;
|
||||
}
|
||||
//Have we connected?
|
||||
if (!IsConnected)
|
||||
{
|
||||
//Attempt to connect to server async.
|
||||
mIsConnectingFlag = true;
|
||||
|
||||
if (errorString != null)
|
||||
throw new Exception(errorString);
|
||||
}
|
||||
//Dispose old client if exist.
|
||||
if (mSocket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mSocket.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
mSocket.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
mSocket = null;
|
||||
}
|
||||
|
||||
throw new Exception("Server denied our connect request with an unknown reason.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//Create new client
|
||||
mSocket = new TcpClient();
|
||||
mSocket.SendBufferSize = Const.MaxNetworkPackageSize;
|
||||
mSocket.ReceiveBufferSize = Const.MaxNetworkPackageSize;
|
||||
|
||||
this.mIsConnectingFlag = false;
|
||||
//Attempt to connect.
|
||||
try
|
||||
{
|
||||
var state = mSocket.BeginConnect(mDestination, mPort, (x) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
//Did we connect?
|
||||
mSocket.EndConnect(x);
|
||||
|
||||
mLogError("Unable to connect to API server: " + e.Message);
|
||||
return;
|
||||
}
|
||||
var networkStream = mSocket.GetStream();
|
||||
|
||||
}, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.mIsConnectingFlag = false;
|
||||
}
|
||||
//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);
|
||||
|
||||
//We haven't connected yet.
|
||||
return;
|
||||
}
|
||||
//Send our hail package.
|
||||
networkStream.Write(hail.Buffer, 0, hail.WritePosition);
|
||||
networkStream.Flush();
|
||||
}
|
||||
|
||||
//We are connected at this point.
|
||||
//Sadly can not use Task Async here, Unity isn't great with tasks.
|
||||
var watch = Stopwatch.StartNew();
|
||||
|
||||
try
|
||||
{
|
||||
//Are we still connected on socket level?
|
||||
if (!mSocket.Connected)
|
||||
{
|
||||
mClose("Connection was terminated.");
|
||||
return;
|
||||
}
|
||||
//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);
|
||||
}
|
||||
|
||||
var networkStream = mSocket.GetStream();
|
||||
//Were we accepted.
|
||||
if (response == NetworkCommuncation.Accepted)
|
||||
{
|
||||
//We are accepted.
|
||||
mIsConnectingFlag = false;
|
||||
IsConnected = true;
|
||||
|
||||
//Read network packages.
|
||||
while (mSocket.Available > 0)
|
||||
{
|
||||
this.mLastPackageReceived = Extentions.TickCount;
|
||||
mOnConnectedToServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Did we at least got a response?
|
||||
if (response == NetworkCommuncation.None)
|
||||
throw new Exception("Server did not respond to your connect request.");
|
||||
|
||||
//Do we know the package size?
|
||||
if (this.mReadPackageSize == 0)
|
||||
{
|
||||
const int sizeToRead = 4;
|
||||
int leftSizeToRead = sizeToRead - this.mReadStream.WritePosition;
|
||||
//Try to read our deny reason.
|
||||
if (response == NetworkCommuncation.Denied && mSocket.Available > 0)
|
||||
{
|
||||
string errorString = null;
|
||||
|
||||
int read = networkStream.Read(this.mReadStream.Buffer, this.mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
using (var readStream = Stream.Get())
|
||||
{
|
||||
readStream.WritePosition = networkStream.Read(readStream.Buffer, 0, mSocket.Available);
|
||||
if (!readStream.TryReadString(out errorString))
|
||||
errorString = null;
|
||||
}
|
||||
|
||||
this.mReadStream.WritePosition += read;
|
||||
if (errorString != null)
|
||||
throw new Exception(errorString);
|
||||
}
|
||||
|
||||
//Did we receive the package?
|
||||
if (this.mReadStream.WritePosition >= 4)
|
||||
{
|
||||
//Read the package size
|
||||
this.mReadPackageSize = this.mReadStream.ReadUInt32();
|
||||
throw new Exception("Server denied our connect request with an unknown reason.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
mIsConnectingFlag = false;
|
||||
|
||||
if (this.mReadPackageSize > Const.MaxNetworkPackageSize)
|
||||
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
|
||||
mLogError("Unable to connect to API server: " + e.Message);
|
||||
return;
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
mIsConnectingFlag = false;
|
||||
}
|
||||
|
||||
//Is this keep alive package?
|
||||
if (this.mReadPackageSize == 0)
|
||||
{
|
||||
Console.WriteLine("Keep alive was received.");
|
||||
}
|
||||
//We haven't connected yet.
|
||||
return;
|
||||
}
|
||||
|
||||
//Reset the stream.
|
||||
this.mReadStream.Reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int sizeToRead = (int)mReadPackageSize;
|
||||
int leftSizeToRead = sizeToRead - this.mReadStream.WritePosition;
|
||||
//We are connected at this point.
|
||||
|
||||
int read = networkStream.Read(this.mReadStream.Buffer, this.mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
try
|
||||
{
|
||||
//Are we still connected on socket level?
|
||||
if (!mSocket.Connected)
|
||||
{
|
||||
mClose("Connection was terminated.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.mReadStream.WritePosition += read;
|
||||
var networkStream = mSocket.GetStream();
|
||||
|
||||
//Do we have the package?
|
||||
if (this.mReadStream.WritePosition >= mReadPackageSize)
|
||||
{
|
||||
this.mReadPackageSize = 0;
|
||||
//Read network packages.
|
||||
while (mSocket.Available > 0)
|
||||
{
|
||||
mLastPackageReceived = Extensions.TickCount;
|
||||
|
||||
mExecutePackage(this.mReadStream);
|
||||
//Do we know the package size?
|
||||
if (mReadPackageSize == 0)
|
||||
{
|
||||
const int sizeToRead = 4;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.WritePosition;
|
||||
|
||||
//Reset
|
||||
this.mReadStream.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
var read = networkStream.Read(mReadStream.Buffer, mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
|
||||
//Send the network packages.
|
||||
if (this.mWriteStream.WritePosition > 0)
|
||||
{
|
||||
lock (this.mWriteStream)
|
||||
{
|
||||
if (this.mWriteStream.WritePosition > 0)
|
||||
{
|
||||
networkStream.Write(this.mWriteStream.Buffer, 0, this.mWriteStream.WritePosition);
|
||||
this.mWriteStream.WritePosition = 0;
|
||||
mReadStream.WritePosition += read;
|
||||
|
||||
this.mLastPackageSent = Extentions.TickCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Did we receive the package?
|
||||
if (mReadStream.WritePosition >= 4)
|
||||
{
|
||||
//Read the package size
|
||||
mReadPackageSize = mReadStream.ReadUInt32();
|
||||
|
||||
//Are we timed out?
|
||||
if ((Extentions.TickCount - this.mLastPackageReceived) > Const.NetworkTimeout)
|
||||
throw new Exception("server timedout.");
|
||||
if (mReadPackageSize > Const.MaxNetworkPackageSize)
|
||||
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
|
||||
|
||||
//Send keep alive if needed
|
||||
if ((Extentions.TickCount - this.mLastPackageSent) > Const.NetworkKeepAlive)
|
||||
{
|
||||
//Send keep alive.
|
||||
networkStream.Write(this.mKeepAliveBuffer, 0, 4);
|
||||
//Is this keep alive package?
|
||||
if (mReadPackageSize == 0)
|
||||
Console.WriteLine("Keep alive was received.");
|
||||
|
||||
this.mLastPackageSent = Extentions.TickCount;
|
||||
//Reset the stream.
|
||||
mReadStream.Reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var sizeToRead = (int)mReadPackageSize;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.WritePosition;
|
||||
|
||||
Console.WriteLine("Keep alive was sent.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
mClose(e.Message);
|
||||
}
|
||||
}
|
||||
var read = networkStream.Read(mReadStream.Buffer, mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
|
||||
// ---- Internal ----
|
||||
private void mExecutePackage(Common.Serialization.Stream stream)
|
||||
{
|
||||
var communcation = (NetworkCommuncation)stream.ReadInt8();
|
||||
switch (communcation)
|
||||
{
|
||||
mReadStream.WritePosition += read;
|
||||
|
||||
}
|
||||
}
|
||||
//Do we have the package?
|
||||
if (mReadStream.WritePosition >= mReadPackageSize)
|
||||
{
|
||||
mReadPackageSize = 0;
|
||||
|
||||
// ---- Callbacks ----
|
||||
private void mOnConnectedToServer()
|
||||
{
|
||||
mExecutePackage(mReadStream);
|
||||
|
||||
}
|
||||
private void mOnDisconnectedFromServer(string reason)
|
||||
{
|
||||
}
|
||||
//Reset
|
||||
mReadStream.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Send the network packages.
|
||||
if (mWriteStream.WritePosition > 0)
|
||||
lock (mWriteStream)
|
||||
{
|
||||
if (mWriteStream.WritePosition > 0)
|
||||
{
|
||||
networkStream.Write(mWriteStream.Buffer, 0, mWriteStream.WritePosition);
|
||||
mWriteStream.WritePosition = 0;
|
||||
|
||||
mLastPackageSent = Extensions.TickCount;
|
||||
}
|
||||
}
|
||||
|
||||
//Are we timed out?
|
||||
if (Extensions.TickCount - mLastPackageReceived > Const.NetworkTimeout)
|
||||
throw new Exception("server timedout.");
|
||||
|
||||
//Send keep alive if needed
|
||||
if (Extensions.TickCount - mLastPackageSent > Const.NetworkKeepAlive)
|
||||
{
|
||||
//Send keep alive.
|
||||
networkStream.Write(mKeepAliveBuffer, 0, 4);
|
||||
|
||||
mLastPackageSent = Extensions.TickCount;
|
||||
|
||||
Console.WriteLine("Keep alive was sent.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
mClose(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Internal ----
|
||||
private void mExecutePackage(Stream stream)
|
||||
{
|
||||
var communcation = (NetworkCommuncation)stream.ReadInt8();
|
||||
switch (communcation)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Callbacks ----
|
||||
private void mOnConnectedToServer()
|
||||
{
|
||||
}
|
||||
|
||||
private void mOnDisconnectedFromServer(string reason)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// ---- Private ----
|
||||
private void mLogError(string str)
|
||||
{
|
||||
// ---- Private ----
|
||||
private void mLogError(string str)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
private void mClose(string reason)
|
||||
{
|
||||
if (this.IsConnected)
|
||||
{
|
||||
this.IsConnected = false;
|
||||
private void mClose(string reason)
|
||||
{
|
||||
if (IsConnected)
|
||||
{
|
||||
IsConnected = false;
|
||||
|
||||
//Dispose old client if exist.
|
||||
if (this.mSocket != null)
|
||||
{
|
||||
try { this.mSocket.Close(); } catch { }
|
||||
try { this.mSocket.Dispose(); } catch { }
|
||||
this.mSocket = null;
|
||||
}
|
||||
//Dispose old client if exist.
|
||||
if (mSocket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mSocket.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
mSocket.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
mSocket = null;
|
||||
}
|
||||
|
||||
mOnDisconnectedFromServer(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
mOnDisconnectedFromServer(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
namespace CommunityServerAPI.BattleBitAPI
|
||||
namespace CommunityServerAPI.BattleBitAPI;
|
||||
|
||||
public static class Const
|
||||
{
|
||||
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;
|
||||
// ---- 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;
|
||||
|
||||
// ---- Server Fields ----
|
||||
public const int MinServerNameLength = 5;
|
||||
public const int MaxServerNameLength = 400;
|
||||
// ---- Server Fields ----
|
||||
public const int MinServerNameLength = 5;
|
||||
public const int MaxServerNameLength = 400;
|
||||
|
||||
public const int MinGamemodeNameLength = 2;
|
||||
public const int MaxGamemodeNameLength = 12;
|
||||
public const int MinGamemodeNameLength = 2;
|
||||
public const int MaxGamemodeNameLength = 12;
|
||||
|
||||
public const int MinMapNameLength = 2;
|
||||
public const int MaxMapNameLength = 36;
|
||||
public const int MinMapNameLength = 2;
|
||||
public const int MaxMapNameLength = 36;
|
||||
|
||||
public const int MinLoadingScreenTextLength = 0;
|
||||
public const int MaxLoadingScreenTextLength = 1024 * 8;
|
||||
public const int MinLoadingScreenTextLength = 0;
|
||||
public const int MaxLoadingScreenTextLength = 1024 * 8;
|
||||
|
||||
public const int MinServerRulesTextLength = 0;
|
||||
public const int MaxServerRulesTextLength = 1024 * 8;
|
||||
|
||||
}
|
||||
}
|
||||
public const int MinServerRulesTextLength = 0;
|
||||
public const int MaxServerRulesTextLength = 1024 * 8;
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
namespace BattleBitAPI.Common.Enums
|
||||
namespace BattleBitAPI.Common.Enums;
|
||||
|
||||
public enum MapDayNight : byte
|
||||
{
|
||||
public enum MapDayNight : byte
|
||||
{
|
||||
Day = 0,
|
||||
Night = 1,
|
||||
}
|
||||
}
|
||||
Day = 0,
|
||||
Night = 1
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
namespace BattleBitAPI.Common.Enums
|
||||
namespace BattleBitAPI.Common.Enums;
|
||||
|
||||
public enum MapSize : byte
|
||||
{
|
||||
public enum MapSize : byte
|
||||
{
|
||||
_16vs16 = 0,
|
||||
_32vs32 = 1,
|
||||
_64vs64 = 2,
|
||||
_127vs127 = 3,
|
||||
}
|
||||
}
|
||||
_16vs16 = 0,
|
||||
_32vs32 = 1,
|
||||
_64vs64 = 2,
|
||||
_127vs127 = 3
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#region
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CommunityServerAPI.BattleBitAPI.Common.Extentions;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static long TickCount
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NETCOREAPP
|
||||
return Environment.TickCount64;
|
||||
#else
|
||||
return (long)Environment.TickCount;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe uint ToUInt(this IPAddress address)
|
||||
{
|
||||
#if NETCOREAPP
|
||||
return BitConverter.ToUInt32(address.GetAddressBytes());
|
||||
#else
|
||||
return BitConverter.ToUInt32(address.GetAddressBytes(), 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SafeClose(this TcpClient client)
|
||||
{
|
||||
try
|
||||
{
|
||||
client.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
client.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace BattleBitAPI.Common.Extentions
|
||||
{
|
||||
public static class Extentions
|
||||
{
|
||||
public static long TickCount
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NETCOREAPP
|
||||
return System.Environment.TickCount64;
|
||||
#else
|
||||
return (long)Environment.TickCount;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
public unsafe static uint ToUInt(this IPAddress address)
|
||||
{
|
||||
#if NETCOREAPP
|
||||
return BitConverter.ToUInt32(address.GetAddressBytes());
|
||||
#else
|
||||
return BitConverter.ToUInt32(address.GetAddressBytes(), 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SafeClose(this TcpClient client)
|
||||
{
|
||||
try { client.Close(); } catch { }
|
||||
try { client.Dispose(); } catch { }
|
||||
}
|
||||
public static async Task<int> Read(this NetworkStream networkStream, Serialization.Stream outputStream, int size, CancellationToken token = default)
|
||||
{
|
||||
int read = 0;
|
||||
int readUntil = outputStream.WritePosition + size;
|
||||
|
||||
//Ensure we have space.
|
||||
outputStream.EnsureWriteBufferSize(size);
|
||||
|
||||
//Continue reading until we have the package.
|
||||
while (outputStream.WritePosition < readUntil)
|
||||
{
|
||||
int sizeToRead = readUntil - outputStream.WritePosition;
|
||||
int received = await networkStream.ReadAsync(outputStream.Buffer, outputStream.WritePosition, sizeToRead, token);
|
||||
if (received <= 0)
|
||||
throw new Exception("NetworkStream was closed.");
|
||||
|
||||
read += received;
|
||||
outputStream.WritePosition += received;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
public static async Task<bool> TryRead(this NetworkStream networkStream, Serialization.Stream outputStream, int size, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
int readUntil = outputStream.WritePosition + size;
|
||||
|
||||
//Ensure we have space.
|
||||
outputStream.EnsureWriteBufferSize(size);
|
||||
|
||||
//Continue reading until we have the package.
|
||||
while (outputStream.WritePosition < readUntil)
|
||||
{
|
||||
int sizeToRead = readUntil - outputStream.WritePosition;
|
||||
int received = await networkStream.ReadAsync(outputStream.Buffer, outputStream.WritePosition, sizeToRead, token);
|
||||
if (received <= 0)
|
||||
throw new Exception("NetworkStream was closed.");
|
||||
outputStream.WritePosition += received;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
#region
|
||||
|
||||
using System.Net.Sockets;
|
||||
|
||||
using CommunityServerAPI.BattleBitAPI.Packets;
|
||||
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
|
||||
#endregion
|
||||
|
||||
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)
|
||||
{
|
||||
var read = 0;
|
||||
var readUntil = outputStream.WritePosition + size;
|
||||
|
||||
//Ensure we have space.
|
||||
outputStream.EnsureWriteBufferSize(size);
|
||||
|
||||
//Continue reading until we have the package.
|
||||
while (outputStream.WritePosition < readUntil)
|
||||
{
|
||||
var sizeToRead = readUntil - outputStream.WritePosition;
|
||||
var received = await networkStream.ReadAsync(outputStream.Buffer, outputStream.WritePosition, sizeToRead, token);
|
||||
if (received <= 0)
|
||||
throw new Exception("NetworkStream was closed.");
|
||||
|
||||
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.
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
/// <param name="packet"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<bool> TryWritePacket(this NetworkStream self, BasePacket packet)
|
||||
{
|
||||
using (var stream = Stream.Get())
|
||||
{
|
||||
if (!packet.TryWrite(stream))
|
||||
return false;
|
||||
|
||||
self.Write(stream.Buffer, 0, stream.WritePosition);
|
||||
self.Flush();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task<bool> TryRead(this NetworkStream networkStream, Stream outputStream, int size, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var readUntil = outputStream.WritePosition + size;
|
||||
|
||||
//Ensure we have space.
|
||||
outputStream.EnsureWriteBufferSize(size);
|
||||
|
||||
//Continue reading until we have the package.
|
||||
while (outputStream.WritePosition < readUntil)
|
||||
{
|
||||
var sizeToRead = readUntil - outputStream.WritePosition;
|
||||
var received = await networkStream.ReadAsync(outputStream.Buffer, outputStream.WritePosition, sizeToRead, token);
|
||||
if (received <= 0)
|
||||
throw new Exception("NetworkStream was closed.");
|
||||
outputStream.WritePosition += received;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
namespace BattleBitAPI.Common.Serialization
|
||||
namespace BattleBitAPI.Common.Serialization;
|
||||
|
||||
public interface IStreamSerializable
|
||||
{
|
||||
public interface IStreamSerializable
|
||||
{
|
||||
void Read(Stream ser);
|
||||
void Write(Stream ser);
|
||||
}
|
||||
}
|
||||
void Read(Stream ser);
|
||||
|
||||
void Write(Stream ser);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,70 +1,79 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
namespace BattleBitAPI.Common.Threading;
|
||||
|
||||
namespace BattleBitAPI.Common.Threading
|
||||
public class ThreadSafe<T>
|
||||
{
|
||||
public class ThreadSafe<T>
|
||||
{
|
||||
private System.Threading.ReaderWriterLockSlim mLock;
|
||||
public T Value;
|
||||
private ReaderWriterLockSlim mLock;
|
||||
public T Value;
|
||||
|
||||
public ThreadSafe(T value)
|
||||
{
|
||||
this.Value = value;
|
||||
this.mLock = new System.Threading.ReaderWriterLockSlim(System.Threading.LockRecursionPolicy.SupportsRecursion);
|
||||
}
|
||||
public ThreadSafe(T value)
|
||||
{
|
||||
Value = value;
|
||||
mLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
}
|
||||
|
||||
public SafeWriteHandle GetWriteHandle() => new SafeWriteHandle(this.mLock);
|
||||
public SafeReadHandle GetReadHandle() => new SafeReadHandle(this.mLock);
|
||||
public SafeWriteHandle GetWriteHandle()
|
||||
{
|
||||
return new(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)
|
||||
{
|
||||
using (new SafeWriteHandle(this.mLock))
|
||||
{
|
||||
var oldValue = this.Value;
|
||||
this.Value = newValue;
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
public class SafeWriteHandle : System.IDisposable
|
||||
{
|
||||
private System.Threading.ReaderWriterLockSlim mLock;
|
||||
private bool mDisposed;
|
||||
public SafeWriteHandle(System.Threading.ReaderWriterLockSlim mLock)
|
||||
{
|
||||
this.mLock = mLock;
|
||||
mLock.EnterWriteLock();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
if (mDisposed)
|
||||
return;
|
||||
mDisposed = true;
|
||||
mLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
public class SafeReadHandle : System.IDisposable
|
||||
{
|
||||
private System.Threading.ReaderWriterLockSlim mLock;
|
||||
private bool mDisposed;
|
||||
public SafeReadHandle(System.Threading.ReaderWriterLockSlim mLock)
|
||||
{
|
||||
this.mLock = mLock;
|
||||
mLock.EnterReadLock();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
if (mDisposed)
|
||||
return;
|
||||
mDisposed = true;
|
||||
public SafeReadHandle GetReadHandle()
|
||||
{
|
||||
return new(mLock);
|
||||
}
|
||||
|
||||
mLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
/// <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))
|
||||
{
|
||||
var oldValue = Value;
|
||||
Value = newValue;
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SafeWriteHandle : IDisposable
|
||||
{
|
||||
private bool mDisposed;
|
||||
private ReaderWriterLockSlim mLock;
|
||||
|
||||
public SafeWriteHandle(ReaderWriterLockSlim mLock)
|
||||
{
|
||||
this.mLock = mLock;
|
||||
mLock.EnterWriteLock();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (mDisposed)
|
||||
return;
|
||||
mDisposed = true;
|
||||
mLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public class SafeReadHandle : IDisposable
|
||||
{
|
||||
private bool mDisposed;
|
||||
private ReaderWriterLockSlim mLock;
|
||||
|
||||
public SafeReadHandle(ReaderWriterLockSlim mLock)
|
||||
{
|
||||
this.mLock = mLock;
|
||||
mLock.EnterReadLock();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (mDisposed)
|
||||
return;
|
||||
mDisposed = true;
|
||||
|
||||
mLock.ExitReadLock();
|
||||
}
|
||||
}
|
|
@ -1,13 +1,11 @@
|
|||
namespace BattleBitAPI.Networking
|
||||
namespace BattleBitAPI.Networking;
|
||||
|
||||
public enum NetworkCommuncation : byte
|
||||
{
|
||||
public enum NetworkCommuncation : byte
|
||||
{
|
||||
//Do not use
|
||||
None = 0,
|
||||
//Do not use
|
||||
None = 0,
|
||||
|
||||
Hail = 1,
|
||||
Accepted = 2,
|
||||
Denied = 3,
|
||||
|
||||
}
|
||||
}
|
||||
Hail = 1,
|
||||
Accepted = 2,
|
||||
Denied = 3
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#region
|
||||
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace CommunityServerAPI.BattleBitAPI.Packets;
|
||||
|
||||
public abstract class BasePacket
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempt to write this packet to the provided stream.
|
||||
/// Return true if the write was successful.
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
/// <returns></returns>
|
||||
public abstract bool TryWrite(Stream destination);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#region
|
||||
|
||||
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 MapDayNight DayNight;
|
||||
public string Gamemode;
|
||||
|
||||
public ushort GamePort;
|
||||
public byte InQueuePlayers;
|
||||
public bool IsPasswordProtected;
|
||||
public string LoadingScreenText;
|
||||
public string Map;
|
||||
public MapSize MapSize;
|
||||
public byte MaxPlayers;
|
||||
public string ServerName;
|
||||
public string ServerRulesText;
|
||||
|
||||
public override bool TryWrite(Stream destination)
|
||||
{
|
||||
destination.Write((byte)NetworkCommuncation.Hail);
|
||||
destination.Write((ushort)GamePort);
|
||||
destination.Write(IsPasswordProtected);
|
||||
destination.Write(ServerName);
|
||||
destination.Write(Gamemode);
|
||||
destination.Write(Map);
|
||||
destination.Write((byte)MapSize);
|
||||
destination.Write((byte)DayNight);
|
||||
destination.Write((byte)CurrentPlayers);
|
||||
destination.Write((byte)InQueuePlayers);
|
||||
destination.Write((byte)MaxPlayers);
|
||||
destination.Write(LoadingScreenText);
|
||||
destination.Write(ServerRulesText);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,226 +1,241 @@
|
|||
using System.Net;
|
||||
#region
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
using BattleBitAPI.Common.Enums;
|
||||
using BattleBitAPI.Common.Extentions;
|
||||
using BattleBitAPI.Networking;
|
||||
|
||||
using CommunityServerAPI.BattleBitAPI;
|
||||
using CommunityServerAPI.BattleBitAPI.Common.Extentions;
|
||||
|
||||
namespace BattleBitAPI.Server
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace BattleBitAPI.Server;
|
||||
|
||||
public class GameServer
|
||||
{
|
||||
public class GameServer
|
||||
{
|
||||
// ---- Public Variables ----
|
||||
public TcpClient Socket { get; private set; }
|
||||
// ---- Private Variables ----
|
||||
private byte[] mKeepAliveBuffer;
|
||||
private long mLastPackageReceived;
|
||||
private long mLastPackageSent;
|
||||
private uint mReadPackageSize;
|
||||
private Stream mReadStream;
|
||||
private Stream mWriteStream;
|
||||
|
||||
/// <summary>
|
||||
/// Is game server connected to our server?
|
||||
/// </summary>
|
||||
public bool IsConnected { get; private set; }
|
||||
public IPAddress GameIP { get; private set; }
|
||||
public int GamePort { get; private set; }
|
||||
public bool IsPasswordProtected { get; private set; }
|
||||
public string ServerName { get; private set; }
|
||||
public string Gamemode { get; private set; }
|
||||
public string Map { get; private set; }
|
||||
public MapSize MapSize { get; private set; }
|
||||
public MapDayNight DayNight { get; private set; }
|
||||
public int CurrentPlayers { get; private set; }
|
||||
public int InQueuePlayers { get; private set; }
|
||||
public int MaxPlayers { get; private set; }
|
||||
public string LoadingScreenText { get; private set; }
|
||||
public string ServerRulesText { get; private set; }
|
||||
// ---- 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;
|
||||
Socket = socket;
|
||||
|
||||
/// <summary>
|
||||
/// Reason why connection was terminated.
|
||||
/// </summary>
|
||||
public string TerminationReason { get; private set; }
|
||||
GameIP = iP;
|
||||
GamePort = port;
|
||||
IsPasswordProtected = isPasswordProtected;
|
||||
ServerName = serverName;
|
||||
Gamemode = gamemode;
|
||||
Map = map;
|
||||
MapSize = mapSize;
|
||||
DayNight = dayNight;
|
||||
CurrentPlayers = currentPlayers;
|
||||
InQueuePlayers = inQueuePlayers;
|
||||
MaxPlayers = maxPlayers;
|
||||
LoadingScreenText = loadingScreenText;
|
||||
ServerRulesText = serverRulesText;
|
||||
|
||||
// ---- Private Variables ----
|
||||
private byte[] mKeepAliveBuffer;
|
||||
private Common.Serialization.Stream mWriteStream;
|
||||
private Common.Serialization.Stream mReadStream;
|
||||
private uint mReadPackageSize;
|
||||
private long mLastPackageReceived;
|
||||
private long mLastPackageSent;
|
||||
TerminationReason = string.Empty;
|
||||
|
||||
// ---- 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)
|
||||
{
|
||||
this.IsConnected = true;
|
||||
this.Socket = socket;
|
||||
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
|
||||
};
|
||||
mKeepAliveBuffer = new byte[4]
|
||||
{
|
||||
0, 0, 0, 0
|
||||
};
|
||||
|
||||
this.GameIP = iP;
|
||||
this.GamePort = port;
|
||||
this.IsPasswordProtected = isPasswordProtected;
|
||||
this.ServerName = serverName;
|
||||
this.Gamemode = gamemode;
|
||||
this.Map = map;
|
||||
this.MapSize = mapSize;
|
||||
this.DayNight = dayNight;
|
||||
this.CurrentPlayers = currentPlayers;
|
||||
this.InQueuePlayers = inQueuePlayers;
|
||||
this.MaxPlayers = maxPlayers;
|
||||
this.LoadingScreenText = loadingScreenText;
|
||||
this.ServerRulesText = serverRulesText;
|
||||
mLastPackageReceived = Extensions.TickCount;
|
||||
mLastPackageSent = Extensions.TickCount;
|
||||
}
|
||||
|
||||
this.TerminationReason = string.Empty;
|
||||
// ---- Public Variables ----
|
||||
public TcpClient Socket { get; private set; }
|
||||
|
||||
this.mWriteStream = new Common.Serialization.Stream()
|
||||
{
|
||||
Buffer = new byte[Const.MaxNetworkPackageSize],
|
||||
InPool = false,
|
||||
ReadPosition = 0,
|
||||
WritePosition = 0,
|
||||
};
|
||||
this.mReadStream = new Common.Serialization.Stream()
|
||||
{
|
||||
Buffer = new byte[Const.MaxNetworkPackageSize],
|
||||
InPool = false,
|
||||
ReadPosition = 0,
|
||||
WritePosition = 0,
|
||||
};
|
||||
this.mKeepAliveBuffer = new byte[4]
|
||||
{
|
||||
0,0,0,0,
|
||||
};
|
||||
/// <summary>
|
||||
/// Is game server connected to our server?
|
||||
/// </summary>
|
||||
public bool IsConnected { get; private set; }
|
||||
|
||||
this.mLastPackageReceived = Extentions.TickCount;
|
||||
this.mLastPackageSent = Extentions.TickCount;
|
||||
}
|
||||
public IPAddress GameIP { get; private set; }
|
||||
|
||||
// ---- Tick ----
|
||||
public async Task Tick()
|
||||
{
|
||||
if (!this.IsConnected)
|
||||
return;
|
||||
public int GamePort { get; private set; }
|
||||
|
||||
try
|
||||
{
|
||||
//Are we still connected on socket level?
|
||||
if (!Socket.Connected)
|
||||
{
|
||||
mClose("Connection was terminated.");
|
||||
return;
|
||||
}
|
||||
public bool IsPasswordProtected { get; private set; }
|
||||
|
||||
var networkStream = Socket.GetStream();
|
||||
public string ServerName { get; private set; }
|
||||
|
||||
//Read network packages.
|
||||
while (Socket.Available > 0)
|
||||
{
|
||||
this.mLastPackageReceived = Extentions.TickCount;
|
||||
public string Gamemode { get; private set; }
|
||||
|
||||
//Do we know the package size?
|
||||
if (this.mReadPackageSize == 0)
|
||||
{
|
||||
const int sizeToRead = 4;
|
||||
int leftSizeToRead = sizeToRead - this.mReadStream.WritePosition;
|
||||
public string Map { get; private set; }
|
||||
|
||||
int read = await networkStream.ReadAsync(this.mReadStream.Buffer, this.mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
public MapSize MapSize { get; private set; }
|
||||
|
||||
this.mReadStream.WritePosition += read;
|
||||
public MapDayNight DayNight { get; private set; }
|
||||
|
||||
//Did we receive the package?
|
||||
if (this.mReadStream.WritePosition >= 4)
|
||||
{
|
||||
//Read the package size
|
||||
this.mReadPackageSize = this.mReadStream.ReadUInt32();
|
||||
public int CurrentPlayers { get; private set; }
|
||||
|
||||
if (this.mReadPackageSize > Const.MaxNetworkPackageSize)
|
||||
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
|
||||
public int InQueuePlayers { get; private set; }
|
||||
|
||||
//Is this keep alive package?
|
||||
if (this.mReadPackageSize == 0)
|
||||
{
|
||||
Console.WriteLine("Keep alive was received.");
|
||||
}
|
||||
public int MaxPlayers { get; private set; }
|
||||
|
||||
//Reset the stream.
|
||||
this.mReadStream.Reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int sizeToRead = (int)mReadPackageSize;
|
||||
int leftSizeToRead = sizeToRead - this.mReadStream.WritePosition;
|
||||
public string LoadingScreenText { get; private set; }
|
||||
|
||||
int read = await networkStream.ReadAsync(this.mReadStream.Buffer, this.mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
public string ServerRulesText { get; private set; }
|
||||
|
||||
this.mReadStream.WritePosition += read;
|
||||
/// <summary>
|
||||
/// Reason why connection was terminated.
|
||||
/// </summary>
|
||||
public string TerminationReason { get; private set; }
|
||||
|
||||
//Do we have the package?
|
||||
if (this.mReadStream.WritePosition >= mReadPackageSize)
|
||||
{
|
||||
this.mReadPackageSize = 0;
|
||||
// ---- Tick ----
|
||||
public async Task Tick()
|
||||
{
|
||||
if (!IsConnected)
|
||||
return;
|
||||
|
||||
await mExecutePackage(this.mReadStream);
|
||||
try
|
||||
{
|
||||
//Are we still connected on socket level?
|
||||
if (!Socket.Connected)
|
||||
{
|
||||
mClose("Connection was terminated.");
|
||||
return;
|
||||
}
|
||||
|
||||
//Reset
|
||||
this.mReadStream.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
var networkStream = Socket.GetStream();
|
||||
|
||||
//Send the network packages.
|
||||
if (this.mWriteStream.WritePosition > 0)
|
||||
{
|
||||
lock (this.mWriteStream)
|
||||
{
|
||||
if (this.mWriteStream.WritePosition > 0)
|
||||
{
|
||||
networkStream.Write(this.mWriteStream.Buffer, 0, this.mWriteStream.WritePosition);
|
||||
this.mWriteStream.WritePosition = 0;
|
||||
//Read network packages.
|
||||
while (Socket.Available > 0)
|
||||
{
|
||||
mLastPackageReceived = Extensions.TickCount;
|
||||
|
||||
this.mLastPackageSent = Extentions.TickCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Do we know the package size?
|
||||
if (mReadPackageSize == 0)
|
||||
{
|
||||
const int sizeToRead = 4;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.WritePosition;
|
||||
|
||||
//Are we timed out?
|
||||
if ((Extentions.TickCount - this.mLastPackageReceived) > Const.NetworkTimeout)
|
||||
throw new Exception("Game server timedout.");
|
||||
var read = await networkStream.ReadAsync(mReadStream.Buffer, mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
|
||||
//Send keep alive if needed
|
||||
if ((Extentions.TickCount - this.mLastPackageSent) > Const.NetworkKeepAlive)
|
||||
{
|
||||
//Send keep alive.
|
||||
networkStream.Write(this.mKeepAliveBuffer, 0, 4);
|
||||
mReadStream.WritePosition += read;
|
||||
|
||||
this.mLastPackageSent = Extentions.TickCount;
|
||||
//Did we receive the package?
|
||||
if (mReadStream.WritePosition >= 4)
|
||||
{
|
||||
//Read the package size
|
||||
mReadPackageSize = mReadStream.ReadUInt32();
|
||||
|
||||
Console.WriteLine("Keep alive was sent.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
mClose(e.Message);
|
||||
}
|
||||
}
|
||||
if (mReadPackageSize > Const.MaxNetworkPackageSize)
|
||||
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
|
||||
|
||||
// ---- Internal ----
|
||||
private async Task mExecutePackage(Common.Serialization.Stream stream)
|
||||
{
|
||||
var communcation = (NetworkCommuncation)stream.ReadInt8();
|
||||
switch (communcation)
|
||||
{
|
||||
//Is this keep alive package?
|
||||
if (mReadPackageSize == 0)
|
||||
Console.WriteLine("Keep alive was received.");
|
||||
|
||||
}
|
||||
}
|
||||
//Reset the stream.
|
||||
mReadStream.Reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var sizeToRead = (int)mReadPackageSize;
|
||||
var leftSizeToRead = sizeToRead - mReadStream.WritePosition;
|
||||
|
||||
private void mClose(string reason)
|
||||
{
|
||||
if (this.IsConnected)
|
||||
{
|
||||
this.TerminationReason = reason;
|
||||
this.IsConnected = false;
|
||||
var read = await networkStream.ReadAsync(mReadStream.Buffer, mReadStream.WritePosition, leftSizeToRead);
|
||||
if (read <= 0)
|
||||
throw new Exception("Connection was terminated.");
|
||||
|
||||
this.mWriteStream = null;
|
||||
this.mReadStream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mReadStream.WritePosition += read;
|
||||
|
||||
//Do we have the package?
|
||||
if (mReadStream.WritePosition >= mReadPackageSize)
|
||||
{
|
||||
mReadPackageSize = 0;
|
||||
|
||||
await mExecutePackage(mReadStream);
|
||||
|
||||
//Reset
|
||||
mReadStream.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Send the network packages.
|
||||
if (mWriteStream.WritePosition > 0)
|
||||
lock (mWriteStream)
|
||||
{
|
||||
if (mWriteStream.WritePosition > 0)
|
||||
{
|
||||
networkStream.Write(mWriteStream.Buffer, 0, mWriteStream.WritePosition);
|
||||
mWriteStream.WritePosition = 0;
|
||||
|
||||
mLastPackageSent = Extensions.TickCount;
|
||||
}
|
||||
}
|
||||
|
||||
//Are we timed out?
|
||||
if (Extensions.TickCount - mLastPackageReceived > Const.NetworkTimeout)
|
||||
throw new Exception("Game server timedout.");
|
||||
|
||||
//Send keep alive if needed
|
||||
if (Extensions.TickCount - mLastPackageSent > Const.NetworkKeepAlive)
|
||||
{
|
||||
//Send keep alive.
|
||||
networkStream.Write(mKeepAliveBuffer, 0, 4);
|
||||
|
||||
mLastPackageSent = Extensions.TickCount;
|
||||
|
||||
Console.WriteLine("Keep alive was sent.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
mClose(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Internal ----
|
||||
private async Task mExecutePackage(Stream stream)
|
||||
{
|
||||
var communcation = (NetworkCommuncation)stream.ReadInt8();
|
||||
switch (communcation)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void mClose(string reason)
|
||||
{
|
||||
if (IsConnected)
|
||||
{
|
||||
TerminationReason = reason;
|
||||
IsConnected = false;
|
||||
|
||||
mWriteStream = null;
|
||||
mReadStream = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,359 +1,377 @@
|
|||
using System.Net;
|
||||
#region
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
using BattleBitAPI.Common.Enums;
|
||||
using BattleBitAPI.Common.Extentions;
|
||||
using BattleBitAPI.Common.Serialization;
|
||||
using BattleBitAPI.Networking;
|
||||
|
||||
using CommunityServerAPI.BattleBitAPI;
|
||||
using CommunityServerAPI.BattleBitAPI.Common.Extentions;
|
||||
|
||||
namespace BattleBitAPI.Server
|
||||
using Stream = BattleBitAPI.Common.Serialization.Stream;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace BattleBitAPI.Server;
|
||||
|
||||
public class ServerListener : IDisposable
|
||||
{
|
||||
public class ServerListener : IDisposable
|
||||
{
|
||||
// --- Public ---
|
||||
public bool IsListening { get; private set; }
|
||||
public bool IsDisposed { get; private set; }
|
||||
public int ListeningPort { get; private set; }
|
||||
// --- Private ---
|
||||
private TcpListener mSocket;
|
||||
|
||||
// --- 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; }
|
||||
// --- Construction ---
|
||||
public ServerListener()
|
||||
{
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
// --- Public ---
|
||||
public bool IsListening { get; private set; }
|
||||
|
||||
// --- Private ---
|
||||
private TcpListener mSocket;
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
// --- Construction ---
|
||||
public ServerListener() { }
|
||||
public int ListeningPort { get; private set; }
|
||||
|
||||
// --- Starting ---
|
||||
public void Start(IPAddress bindIP, int port)
|
||||
{
|
||||
if (this.IsDisposed)
|
||||
throw new ObjectDisposedException(this.GetType().FullName);
|
||||
if (bindIP == null)
|
||||
throw new ArgumentNullException(nameof(bindIP));
|
||||
if (IsListening)
|
||||
throw new Exception("Server is already listening.");
|
||||
// --- 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; }
|
||||
|
||||
this.mSocket = new TcpListener(bindIP, port);
|
||||
this.mSocket.Start();
|
||||
/// <summary>
|
||||
/// Fired when a game server connects.
|
||||
/// </summary>
|
||||
public Func<GameServer, Task> OnGameServerConnected { get; set; }
|
||||
|
||||
this.ListeningPort = port;
|
||||
this.IsListening = true;
|
||||
/// <summary>
|
||||
/// Fired when a game server disconnects. Check (server.TerminationReason) to see the reason.
|
||||
/// </summary>
|
||||
public Func<GameServer, Task> OnGameServerDisconnected { get; set; }
|
||||
|
||||
mMainLoop();
|
||||
}
|
||||
public void Start(int port)
|
||||
{
|
||||
Start(IPAddress.Loopback, port);
|
||||
}
|
||||
// --- Disposing ---
|
||||
public void Dispose()
|
||||
{
|
||||
//Already disposed?
|
||||
if (IsDisposed)
|
||||
return;
|
||||
IsDisposed = true;
|
||||
|
||||
// --- Stopping ---
|
||||
public void Stop()
|
||||
{
|
||||
if (this.IsDisposed)
|
||||
throw new ObjectDisposedException(this.GetType().FullName);
|
||||
if (!IsListening)
|
||||
throw new Exception("Already not running.");
|
||||
if (IsListening)
|
||||
Stop();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
mSocket.Stop();
|
||||
}
|
||||
catch { }
|
||||
// --- Starting ---
|
||||
public void Start(IPAddress bindIP, int port)
|
||||
{
|
||||
if (IsDisposed)
|
||||
throw new ObjectDisposedException(GetType().FullName);
|
||||
if (bindIP == null)
|
||||
throw new ArgumentNullException(nameof(bindIP));
|
||||
if (IsListening)
|
||||
throw new Exception("Server is already listening.");
|
||||
|
||||
this.mSocket = null;
|
||||
this.ListeningPort = 0;
|
||||
this.IsListening = true;
|
||||
}
|
||||
mSocket = new TcpListener(bindIP, port);
|
||||
mSocket.Start();
|
||||
|
||||
// --- Main Loop ---
|
||||
private async Task mMainLoop()
|
||||
{
|
||||
while (IsListening)
|
||||
{
|
||||
var client = await mSocket.AcceptTcpClientAsync();
|
||||
mInternalOnClientConnecting(client);
|
||||
}
|
||||
}
|
||||
private async Task mInternalOnClientConnecting(TcpClient client)
|
||||
{
|
||||
var ip = (client.Client.RemoteEndPoint as IPEndPoint).Address;
|
||||
ListeningPort = port;
|
||||
IsListening = true;
|
||||
|
||||
bool allow = true;
|
||||
if (OnGameServerConnecting != null)
|
||||
allow = await OnGameServerConnecting(ip);
|
||||
mMainLoop();
|
||||
}
|
||||
|
||||
if (!allow)
|
||||
{
|
||||
//Connection is not allowed from this IP.
|
||||
client.SafeClose();
|
||||
return;
|
||||
}
|
||||
public void Start(int port)
|
||||
{
|
||||
Start(IPAddress.Loopback, port);
|
||||
}
|
||||
|
||||
GameServer server = null;
|
||||
try
|
||||
{
|
||||
using (CancellationTokenSource source = new CancellationTokenSource(Const.HailConnectTimeout))
|
||||
{
|
||||
using (var readStream = Common.Serialization.Stream.Get())
|
||||
{
|
||||
var networkStream = client.GetStream();
|
||||
// --- Stopping ---
|
||||
public void Stop()
|
||||
{
|
||||
if (IsDisposed)
|
||||
throw new ObjectDisposedException(GetType().FullName);
|
||||
if (!IsListening)
|
||||
throw new Exception("Already not running.");
|
||||
|
||||
//Read package type
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 1, source.Token))
|
||||
throw new Exception("Unable to read the package type");
|
||||
NetworkCommuncation type = (NetworkCommuncation)readStream.ReadInt8();
|
||||
if (type != NetworkCommuncation.Hail)
|
||||
throw new Exception("Incoming package wasn't hail.");
|
||||
}
|
||||
try
|
||||
{
|
||||
mSocket.Stop();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
//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();
|
||||
}
|
||||
mSocket = null;
|
||||
ListeningPort = 0;
|
||||
IsListening = true;
|
||||
}
|
||||
|
||||
//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();
|
||||
}
|
||||
// --- Main Loop ---
|
||||
private async Task mMainLoop()
|
||||
{
|
||||
while (IsListening)
|
||||
{
|
||||
var client = await mSocket.AcceptTcpClientAsync();
|
||||
mInternalOnClientConnecting(client);
|
||||
}
|
||||
}
|
||||
|
||||
//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");
|
||||
private async Task mInternalOnClientConnecting(TcpClient client)
|
||||
{
|
||||
var ip = (client.Client.RemoteEndPoint as IPEndPoint).Address;
|
||||
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinServerNameLength || stringSize > Const.MaxServerNameLength)
|
||||
throw new Exception("Invalid server name size");
|
||||
var allow = true;
|
||||
if (OnGameServerConnecting != null)
|
||||
allow = await OnGameServerConnecting(ip);
|
||||
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the ServerName");
|
||||
if (!allow)
|
||||
{
|
||||
//Connection is not allowed from this IP.
|
||||
client.SafeClose();
|
||||
return;
|
||||
}
|
||||
|
||||
serverName = readStream.ReadString(stringSize);
|
||||
}
|
||||
GameServer server = null;
|
||||
try
|
||||
{
|
||||
using (var source = new CancellationTokenSource(Const.HailConnectTimeout))
|
||||
{
|
||||
using (var readStream = Stream.Get())
|
||||
{
|
||||
var networkStream = client.GetStream();
|
||||
|
||||
//Read the gamemode
|
||||
string gameMode;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 2, source.Token))
|
||||
throw new Exception("Unable to read the gamemode Size");
|
||||
//Read package type
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 1, source.Token))
|
||||
throw new Exception("Unable to read the package type");
|
||||
var type = (NetworkCommuncation)readStream.ReadInt8();
|
||||
if (type != NetworkCommuncation.Hail)
|
||||
throw new Exception("Incoming package wasn't hail.");
|
||||
}
|
||||
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinGamemodeNameLength || stringSize > Const.MaxGamemodeNameLength)
|
||||
throw new Exception("Invalid gamemode size");
|
||||
//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();
|
||||
}
|
||||
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the gamemode");
|
||||
//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();
|
||||
}
|
||||
|
||||
gameMode = readStream.ReadString(stringSize);
|
||||
}
|
||||
//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");
|
||||
|
||||
//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.MinServerNameLength || stringSize > Const.MaxServerNameLength)
|
||||
throw new Exception("Invalid server name 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 ServerName");
|
||||
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the map");
|
||||
serverName = readStream.ReadString(stringSize);
|
||||
}
|
||||
|
||||
gamemap = 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");
|
||||
|
||||
//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();
|
||||
}
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinGamemodeNameLength || stringSize > Const.MaxGamemodeNameLength)
|
||||
throw new Exception("Invalid gamemode size");
|
||||
|
||||
//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();
|
||||
}
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the gamemode");
|
||||
|
||||
//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();
|
||||
}
|
||||
gameMode = readStream.ReadString(stringSize);
|
||||
}
|
||||
|
||||
//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();
|
||||
}
|
||||
//Read the gamemap
|
||||
string gamemap;
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, 2, source.Token))
|
||||
throw new Exception("Unable to read the map size");
|
||||
|
||||
//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();
|
||||
}
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinMapNameLength || stringSize > Const.MaxMapNameLength)
|
||||
throw new Exception("Invalid map size");
|
||||
|
||||
//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");
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the map");
|
||||
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinLoadingScreenTextLength || stringSize > Const.MaxLoadingScreenTextLength)
|
||||
throw new Exception("Invalid server Loading Screen Text Size");
|
||||
gamemap = readStream.ReadString(stringSize);
|
||||
}
|
||||
|
||||
if (stringSize > 0)
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the Loading Screen Text");
|
||||
//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();
|
||||
}
|
||||
|
||||
loadingScreenText = readStream.ReadString(stringSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadingScreenText = string.Empty;
|
||||
}
|
||||
}
|
||||
//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();
|
||||
}
|
||||
|
||||
//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");
|
||||
//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();
|
||||
}
|
||||
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinServerRulesTextLength || stringSize > Const.MaxServerRulesTextLength)
|
||||
throw new Exception("Invalid server Server Rules Text Size");
|
||||
//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();
|
||||
}
|
||||
|
||||
if (stringSize > 0)
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the Server Rules Text");
|
||||
//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();
|
||||
}
|
||||
|
||||
serverRulesText = readStream.ReadString(stringSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
serverRulesText = string.Empty;
|
||||
}
|
||||
}
|
||||
//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");
|
||||
|
||||
server = new GameServer(client, ip, gamePort, isPasswordProtected, serverName, gameMode, gamemap, size, dayNight, currentPlayers, queuePlayers, maxPlayers, loadingScreenText, serverRulesText);
|
||||
int stringSize = readStream.ReadUInt16();
|
||||
if (stringSize < Const.MinLoadingScreenTextLength || stringSize > Const.MaxLoadingScreenTextLength)
|
||||
throw new Exception("Invalid server Loading Screen Text Size");
|
||||
|
||||
//Send accepted notification.
|
||||
networkStream.WriteByte((byte)NetworkCommuncation.Accepted);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
if (stringSize > 0)
|
||||
{
|
||||
readStream.Reset();
|
||||
if (!await networkStream.TryRead(readStream, stringSize, source.Token))
|
||||
throw new Exception("Unable to read the Loading Screen Text");
|
||||
|
||||
var networkStream = client.GetStream();
|
||||
using (var pck = BattleBitAPI.Common.Serialization.Stream.Get())
|
||||
{
|
||||
pck.Write((byte)NetworkCommuncation.Denied);
|
||||
pck.Write(e.Message);
|
||||
loadingScreenText = readStream.ReadString(stringSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadingScreenText = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
//Send denied notification.
|
||||
networkStream.Write(pck.Buffer, 0, pck.WritePosition);
|
||||
}
|
||||
await networkStream.FlushAsync();
|
||||
}
|
||||
catch { }
|
||||
//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);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
|
||||
var networkStream = client.GetStream();
|
||||
using (var pck = Stream.Get())
|
||||
{
|
||||
pck.Write((byte)NetworkCommuncation.Denied);
|
||||
pck.Write(e.Message);
|
||||
|
||||
//Send denied notification.
|
||||
networkStream.Write(pck.Buffer, 0, pck.WritePosition);
|
||||
}
|
||||
await networkStream.FlushAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
client.SafeClose();
|
||||
return;
|
||||
}
|
||||
client.SafeClose();
|
||||
return;
|
||||
}
|
||||
|
||||
//Call the callback.
|
||||
if (OnGameServerConnected != null)
|
||||
await OnGameServerConnected.Invoke(server);
|
||||
//Call the callback.
|
||||
if (OnGameServerConnected != null)
|
||||
await OnGameServerConnected.Invoke(server);
|
||||
|
||||
//Set the buffer sizes.
|
||||
client.ReceiveBufferSize = Const.MaxNetworkPackageSize;
|
||||
client.SendBufferSize = Const.MaxNetworkPackageSize;
|
||||
//Set the buffer sizes.
|
||||
client.ReceiveBufferSize = Const.MaxNetworkPackageSize;
|
||||
client.SendBufferSize = Const.MaxNetworkPackageSize;
|
||||
|
||||
//Join to main server loop.
|
||||
await mHandleGameServer(server);
|
||||
}
|
||||
private async Task mHandleGameServer(GameServer server)
|
||||
{
|
||||
while (server.IsConnected)
|
||||
{
|
||||
await server.Tick();
|
||||
await Task.Delay(1);
|
||||
}
|
||||
//Join to main server loop.
|
||||
await mHandleGameServer(server);
|
||||
}
|
||||
|
||||
if (OnGameServerDisconnected != null)
|
||||
await OnGameServerDisconnected.Invoke(server);
|
||||
}
|
||||
private async Task mHandleGameServer(GameServer server)
|
||||
{
|
||||
while (server.IsConnected)
|
||||
{
|
||||
await server.Tick();
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
// --- Disposing ---
|
||||
public void Dispose()
|
||||
{
|
||||
//Already disposed?
|
||||
if (this.IsDisposed)
|
||||
return;
|
||||
this.IsDisposed = true;
|
||||
|
||||
if (IsListening)
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (OnGameServerDisconnected != null)
|
||||
await OnGameServerDisconnected.Invoke(server);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
87
Program.cs
87
Program.cs
|
@ -1,47 +1,54 @@
|
|||
using BattleBitAPI.Client;
|
||||
using BattleBitAPI.Server;
|
||||
#region
|
||||
|
||||
using System.Net;
|
||||
|
||||
class Program
|
||||
using BattleBitAPI.Client;
|
||||
using BattleBitAPI.Server;
|
||||
|
||||
#endregion
|
||||
|
||||
internal class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
if (Console.ReadLine().Contains("h"))
|
||||
{
|
||||
ServerListener server = new ServerListener();
|
||||
server.OnGameServerConnecting += OnClientConnecting;
|
||||
server.OnGameServerConnected += OnGameServerConnected;
|
||||
server.OnGameServerDisconnected += OnGameServerDisconnected;
|
||||
server.Start(29294);
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
if (Console.ReadLine().Contains("h"))
|
||||
{
|
||||
var server = new ServerListener();
|
||||
server.OnGameServerConnecting += OnClientConnecting;
|
||||
server.OnGameServerConnected += OnGameServerConnected;
|
||||
server.OnGameServerDisconnected += OnGameServerDisconnected;
|
||||
server.Start(29294);
|
||||
|
||||
Thread.Sleep(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Client c = new Client("127.0.0.1", 29294);
|
||||
c.ServerName = "Test Server";
|
||||
c.Gamemode = "TDP";
|
||||
c.Map = "DustyDew";
|
||||
Thread.Sleep(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
var c = new Client("127.0.0.1", 29294);
|
||||
c.ServerName = "Test Server";
|
||||
c.Gamemode = "TDP";
|
||||
c.Map = "DustyDew";
|
||||
|
||||
while (true)
|
||||
{
|
||||
c.Tick();
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
c.Tick();
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> OnClientConnecting(IPAddress ip)
|
||||
{
|
||||
Console.WriteLine(ip + " is connecting.");
|
||||
return true;
|
||||
}
|
||||
private static async Task OnGameServerConnected(GameServer server)
|
||||
{
|
||||
Console.WriteLine("Server " + server.ServerName + " was connected.");
|
||||
}
|
||||
private static async Task OnGameServerDisconnected(GameServer server)
|
||||
{
|
||||
Console.WriteLine("Server " + server.ServerName + " was disconnected. (" + server.TerminationReason + ")");
|
||||
}
|
||||
private static async Task<bool> OnClientConnecting(IPAddress ip)
|
||||
{
|
||||
Console.WriteLine(ip + " is connecting.");
|
||||
return true;
|
||||
}
|
||||
|
||||
private static async Task OnGameServerConnected(GameServer server)
|
||||
{
|
||||
Console.WriteLine("Server " + server.ServerName + " was connected.");
|
||||
}
|
||||
|
||||
private static async Task OnGameServerDisconnected(GameServer server)
|
||||
{
|
||||
Console.WriteLine("Server " + server.ServerName + " was disconnected. (" + server.TerminationReason + ")");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue