Client side socket implementation finish.

This commit is contained in:
MrOkiDoki 2023-03-03 23:00:54 +03:00
parent c638227376
commit e3c04d2363
14 changed files with 310 additions and 124 deletions

Binary file not shown.

View File

@ -1,4 +1,5 @@
using BattleBitAPI.Common.Enums; using BattleBitAPI.Common.Enums;
using BattleBitAPI.Common.Extentions;
using BattleBitAPI.Common.Serialization; using BattleBitAPI.Common.Serialization;
using BattleBitAPI.Networking; using BattleBitAPI.Networking;
using CommunityServerAPI.BattleBitAPI; using CommunityServerAPI.BattleBitAPI;
@ -13,24 +14,30 @@ namespace BattleBitAPI.Client
public class Client public class Client
{ {
// ---- Public Variables ---- // ---- Public Variables ----
public bool IsConnected { get; set; } public bool IsConnected { get; private set; }
public int GamePort { get; set; } public int GamePort { get; set; } = 30000;
public bool IsPasswordProtected { get; set; } public bool IsPasswordProtected { get; set; } = false;
public string ServerName { get; set; } public string ServerName { get; set; } = "";
public string Gamemode { get; set; } public string Gamemode { get; set; } = "";
public string Map { get; set; } public string Map { get; set; } = "";
public MapSize MapSize { get; set; } public MapSize MapSize { get; set; } = MapSize._16vs16;
public MapDayNight DayNight { get; set; } public MapDayNight DayNight { get; set; } = MapDayNight.Day;
public int CurrentPlayers { get; set; } public int CurrentPlayers { get; set; } = 0;
public int InQueuePlayers { get; set; } public int InQueuePlayers { get; set; } = 0;
public int MaxPlayers { get; set; } public int MaxPlayers { get; set; } = 16;
public string LoadingScreenText { get; set; } public string LoadingScreenText { get; set; } = "";
public string ServerRulesText { get; set; } public string ServerRulesText { get; set; } = "";
// ---- Private Variables ---- // ---- Private Variables ----
private TcpClient mClient; private TcpClient mSocket;
private string mDestination; private string mDestination;
private int mPort; 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 bool mIsConnectingFlag;
// ---- Construction ---- // ---- Construction ----
@ -38,6 +45,28 @@ namespace BattleBitAPI.Client
{ {
this.mDestination = destination; this.mDestination = destination;
this.mPort = port; this.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,
};
this.mLastPackageReceived = Extentions.TickCount;
this.mLastPackageSent = Extentions.TickCount;
} }
// ---- Main Tick ---- // ---- Main Tick ----
@ -54,111 +83,121 @@ namespace BattleBitAPI.Client
this.mIsConnectingFlag = true; this.mIsConnectingFlag = true;
//Dispose old client if exist. //Dispose old client if exist.
if (this.mClient != null) if (this.mSocket != null)
{ {
try { this.mClient.Close(); } catch { } try { this.mSocket.Close(); } catch { }
try { this.mClient.Dispose(); } catch { } try { this.mSocket.Dispose(); } catch { }
this.mClient = null; this.mSocket = null;
} }
//Create new client //Create new client
this.mClient = new TcpClient(); this.mSocket = new TcpClient();
this.mClient.SendBufferSize = Const.MaxNetworkPackageSize; this.mSocket.SendBufferSize = Const.MaxNetworkPackageSize;
this.mClient.ReceiveBufferSize = Const.MaxNetworkPackageSize; this.mSocket.ReceiveBufferSize = Const.MaxNetworkPackageSize;
//Attempt to connect. //Attempt to connect.
var state = mClient.BeginConnect(mDestination, mPort, (x) => 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 = 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);
//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.
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);
}
//Were we accepted.
if (response == NetworkCommuncation.Accepted)
{
//We are accepted.
this.mIsConnectingFlag = false;
this.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 = BattleBitAPI.Common.Serialization.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)
{
this.mIsConnectingFlag = false;
mLogError("Unable to connect to API server: " + e.Message);
return;
}
}, null);
}
catch
{ {
this.mIsConnectingFlag = false; this.mIsConnectingFlag = false;
}
try
{
//Did we connect?
mClient.EndConnect(x);
var networkStream = mClient.GetStream();
//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);
//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.
NetworkCommuncation response = NetworkCommuncation.None;
while (watch.ElapsedMilliseconds < Const.HailConnectTimeout)
{
if (mClient.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.
this.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 && mClient.Available > 0)
{
string errorString = null;
using (var readStream = BattleBitAPI.Common.Serialization.Stream.Get())
{
readStream.WritePosition = networkStream.Read(readStream.Buffer, 0, mClient.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)
{
mLogError("Unable to connect to API server: " + e.Message);
return;
}
}, null);
//We haven't connected yet. //We haven't connected yet.
return; return;
@ -166,6 +205,121 @@ namespace BattleBitAPI.Client
//We are connected at this point. //We are connected at this point.
try
{
//Are we still connected on socket level?
if (!mSocket.Connected)
{
mClose("Connection was terminated.");
return;
}
var networkStream = mSocket.GetStream();
//Read network packages.
while (mSocket.Available > 0)
{
this.mLastPackageReceived = Extentions.TickCount;
//Do we know the package size?
if (this.mReadPackageSize == 0)
{
const int sizeToRead = 4;
int leftSizeToRead = sizeToRead - this.mReadStream.WritePosition;
int read = networkStream.Read(this.mReadStream.Buffer, this.mReadStream.WritePosition, leftSizeToRead);
if (read <= 0)
throw new Exception("Connection was terminated.");
this.mReadStream.WritePosition += read;
//Did we receive the package?
if (this.mReadStream.WritePosition >= 4)
{
//Read the package size
this.mReadPackageSize = this.mReadStream.ReadUInt32();
if (this.mReadPackageSize > Const.MaxNetworkPackageSize)
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
//Is this keep alive package?
if (this.mReadPackageSize == 0)
{
Console.WriteLine("Keep alive was received.");
}
//Reset the stream.
this.mReadStream.Reset();
}
}
else
{
int sizeToRead = (int)mReadPackageSize;
int leftSizeToRead = sizeToRead - this.mReadStream.WritePosition;
int read = networkStream.Read(this.mReadStream.Buffer, this.mReadStream.WritePosition, leftSizeToRead);
if (read <= 0)
throw new Exception("Connection was terminated.");
this.mReadStream.WritePosition += read;
//Do we have the package?
if (this.mReadStream.WritePosition >= mReadPackageSize)
{
this.mReadPackageSize = 0;
mExecutePackage(this.mReadStream);
//Reset
this.mReadStream.Reset();
}
}
}
//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;
this.mLastPackageSent = Extentions.TickCount;
}
}
}
//Are we timed out?
if ((Extentions.TickCount - this.mLastPackageReceived) > Const.NetworkTimeout)
throw new Exception("server timedout.");
//Send keep alive if needed
if ((Extentions.TickCount - this.mLastPackageSent) > Const.NetworkKeepAlive)
{
//Send keep alive.
networkStream.Write(this.mKeepAliveBuffer, 0, 4);
this.mLastPackageSent = Extentions.TickCount;
Console.WriteLine("Keep alive was sent.");
}
}
catch (Exception e)
{
mClose(e.Message);
}
}
// ---- Internal ----
private void mExecutePackage(Common.Serialization.Stream stream)
{
var communcation = (NetworkCommuncation)stream.ReadInt8();
switch (communcation)
{
}
} }
// ---- Callbacks ---- // ---- Callbacks ----
@ -183,18 +337,18 @@ namespace BattleBitAPI.Client
{ {
} }
private void mCloseConnection(string reason) private void mClose(string reason)
{ {
if (this.IsConnected) if (this.IsConnected)
{ {
this.IsConnected = false; this.IsConnected = false;
//Dispose old client if exist. //Dispose old client if exist.
if (this.mClient != null) if (this.mSocket != null)
{ {
try { this.mClient.Close(); } catch { } try { this.mSocket.Close(); } catch { }
try { this.mClient.Dispose(); } catch { } try { this.mSocket.Dispose(); } catch { }
this.mClient = null; this.mSocket = null;
} }
mOnDisconnectedFromServer(reason); mOnDisconnectedFromServer(reason);

View File

@ -83,6 +83,9 @@ namespace BattleBitAPI.Server
{ {
0,0,0,0, 0,0,0,0,
}; };
this.mLastPackageReceived = Extentions.TickCount;
this.mLastPackageSent = Extentions.TickCount;
} }
// ---- Tick ---- // ---- Tick ----
@ -128,6 +131,12 @@ namespace BattleBitAPI.Server
if (this.mReadPackageSize > Const.MaxNetworkPackageSize) if (this.mReadPackageSize > Const.MaxNetworkPackageSize)
throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'"); throw new Exception("Incoming package was larger than 'Conts.MaxNetworkPackageSize'");
//Is this keep alive package?
if (this.mReadPackageSize == 0)
{
Console.WriteLine("Keep alive was received.");
}
//Reset the stream. //Reset the stream.
this.mReadStream.Reset(); this.mReadStream.Reset();
} }
@ -182,6 +191,8 @@ namespace BattleBitAPI.Server
networkStream.Write(this.mKeepAliveBuffer, 0, 4); networkStream.Write(this.mKeepAliveBuffer, 0, 4);
this.mLastPackageSent = Extentions.TickCount; this.mLastPackageSent = Extentions.TickCount;
Console.WriteLine("Keep alive was sent.");
} }
} }
catch (Exception e) catch (Exception e)

View File

@ -301,6 +301,8 @@ namespace BattleBitAPI.Server
{ {
try try
{ {
Console.WriteLine(e.Message);
var networkStream = client.GetStream(); var networkStream = client.GetStream();
using (var pck = BattleBitAPI.Common.Serialization.Stream.Get()) using (var pck = BattleBitAPI.Common.Serialization.Stream.Get())
{ {

View File

@ -1,28 +1,47 @@
using BattleBitAPI.Server; using BattleBitAPI.Client;
using BattleBitAPI.Server;
using System.Net; using System.Net;
class Program class Program
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
ServerListener server = new ServerListener(); if (Console.ReadLine().Contains("h"))
server.OnGameServerConnecting += OnClientConnecting; {
server.OnGameServerConnected += OnGameServerConnected; ServerListener server = new ServerListener();
server.OnGameServerDisconnected += OnGameServerDisconnected; server.OnGameServerConnecting += OnClientConnecting;
server.OnGameServerConnected += OnGameServerConnected;
server.OnGameServerDisconnected += OnGameServerDisconnected;
server.Start(29294);
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";
while (true)
{
c.Tick();
Thread.Sleep(1);
}
}
} }
private static async Task<bool> OnClientConnecting(IPAddress ip) private static async Task<bool> OnClientConnecting(IPAddress ip)
{ {
Console.WriteLine(ip + " is connecting.");
return true; return true;
} }
private static async Task OnGameServerConnected(GameServer server) private static async Task OnGameServerConnected(GameServer server)
{ {
Console.WriteLine("Server "+server.ServerName+" was connected."); Console.WriteLine("Server " + server.ServerName + " was connected.");
} }
private static async Task OnGameServerDisconnected(GameServer server) private static async Task OnGameServerDisconnected(GameServer server)
{ {
Console.WriteLine("Server " + server.ServerName + " was disconnected."); Console.WriteLine("Server " + server.ServerName + " was disconnected. (" + server.TerminationReason + ")");
} }
} }