Compare commits
5
Commits
531fd2572f
...
6931846a64
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6931846a64 | ||
|
|
b439805f3e | ||
|
|
6f910e969a | ||
|
|
875a5ae3e9 | ||
|
|
3992c3debc |
No files matched your search
@@ -1,40 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Mistox.Client {
|
|
||||||
|
|
||||||
public class MistoxServerClient {
|
|
||||||
|
|
||||||
public string ServerName { get; set; } = ""; // The Name of the server
|
|
||||||
public bool Alive { get; set; } = true; // Is the server running
|
|
||||||
public int Port { get; set; } = 9126; // What Port to bind on
|
|
||||||
|
|
||||||
|
|
||||||
public event Action onServerConnected;
|
|
||||||
public void InvokeOnServerConnected(){
|
|
||||||
Alive = true;
|
|
||||||
onServerConnected?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action<UDPEndpoint> onUserConnected;
|
|
||||||
public void InvokeOnUserConnected(UDPEndpoint ConnectedUser){
|
|
||||||
onUserConnected?.Invoke(ConnectedUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action<dynamic> onDataReceived;
|
|
||||||
public void InvokeOnReceived(dynamic DataPacket){
|
|
||||||
onDataReceived?.Invoke(DataPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action<UDPEndpoint> onUserDisconnected;
|
|
||||||
public void InvokeOnUserDisconnected(UDPEndpoint DisconnectedUser){
|
|
||||||
onUserDisconnected?.Invoke(DisconnectedUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action onServerDisconnected;
|
|
||||||
public void InvokeOnServerDisconnected(){
|
|
||||||
Alive = false;
|
|
||||||
onServerDisconnected?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace Mistox.Client {
|
|
||||||
public class UDPEndpoint {
|
|
||||||
public string UserName { get; set; } = ""; // The Players UserName
|
|
||||||
public bool Alive { get; set; } = true; // Is the connection alive
|
|
||||||
public mUDPClient Connection { get; set; } // The UDP Client Object
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using Mistox.Attributes;
|
|
||||||
|
|
||||||
[mPacket(0xFF)]
|
|
||||||
public class UserConnectionPacket {
|
|
||||||
string UserName { get; set; }
|
|
||||||
long RemoteIP { get; set; }
|
|
||||||
int Port { get; set; }
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Net;
|
||||||
|
using Mistox.ResevedPackets;
|
||||||
|
|
||||||
|
namespace Mistox.Client {
|
||||||
|
|
||||||
|
public class MistoxClient {
|
||||||
|
|
||||||
|
public string ServerName { get; set; } = ""; // The Name of the server
|
||||||
|
public bool Alive { get; set; } = true; // Is the server running
|
||||||
|
public mTCPClient ServerConnection;
|
||||||
|
public ConcurrentDictionary<string, mUDPClient> clientConnections;
|
||||||
|
|
||||||
|
public MistoxClient(IPEndPoint Server) {
|
||||||
|
Console.WriteLine( "The client is initilized and trying to connect to the server at ip : " + Server.Address );
|
||||||
|
try {
|
||||||
|
ServerConnection = new mTCPClient( this, Server );
|
||||||
|
Console.WriteLine( "Connected to the server sucessfully" );
|
||||||
|
} catch( Exception e ) {
|
||||||
|
Console.WriteLine( "An error has occured with the connection to the server. Error { " );
|
||||||
|
Console.WriteLine( e.ToString() );
|
||||||
|
Console.WriteLine( "}" );
|
||||||
|
}
|
||||||
|
Alive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UserConnected(UserConnectedPacket packet) {
|
||||||
|
mUDPClient user = new mUDPClient(this, packet.ClientIPAddress, packet.ClientPort) {
|
||||||
|
UserName = packet.UserName
|
||||||
|
};
|
||||||
|
clientConnections[packet.UserName] = user;
|
||||||
|
InvokeOnUserConnected(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UserDisconnected(UserDisconnectedPacket packet) {
|
||||||
|
mUDPClient copy = clientConnections[packet.UserName];
|
||||||
|
clientConnections[packet.UserName].Alive = false;
|
||||||
|
clientConnections[packet.UserName].Dispose();
|
||||||
|
clientConnections[packet.UserName] = null;
|
||||||
|
InvokeOnUserDisconnected( copy );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events
|
||||||
|
|
||||||
|
public event Action onServerConnected;
|
||||||
|
public void InvokeOnServerConnected(){
|
||||||
|
Alive = true;
|
||||||
|
onServerConnected?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<mUDPClient> onUserConnected;
|
||||||
|
public void InvokeOnUserConnected(mUDPClient ConnectedUser){
|
||||||
|
onUserConnected?.Invoke(ConnectedUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<dynamic> onDataReceived;
|
||||||
|
public void InvokeOnReceived(dynamic DataPacket){
|
||||||
|
switch (DataPacket as object) {
|
||||||
|
case UserConnectedPacket packet:
|
||||||
|
UserConnected(packet);
|
||||||
|
break;
|
||||||
|
case UserDisconnectedPacket packet:
|
||||||
|
UserDisconnected(packet);
|
||||||
|
break;
|
||||||
|
case null:
|
||||||
|
Console.WriteLine("Received a null packet.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
onDataReceived?.Invoke(DataPacket);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<mUDPClient> onUserDisconnected;
|
||||||
|
public void InvokeOnUserDisconnected(mUDPClient DisconnectedUser){
|
||||||
|
onUserDisconnected?.Invoke(DisconnectedUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action onServerDisconnected;
|
||||||
|
public void InvokeOnServerDisconnected(){
|
||||||
|
Alive = false;
|
||||||
|
onServerDisconnected?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,23 +9,24 @@ using System.Threading.Tasks;
|
|||||||
namespace Mistox.Client {
|
namespace Mistox.Client {
|
||||||
public class mTCPClient : IDisposable {
|
public class mTCPClient : IDisposable {
|
||||||
|
|
||||||
TcpClient Server;
|
TcpClient _connection;
|
||||||
|
MistoxClient _parent;
|
||||||
|
|
||||||
public mTCPClient( IPEndPoint ServerAddress ) {
|
public mTCPClient( MistoxClient parent, IPEndPoint ServerAddress ) {
|
||||||
Server = new TcpClient( AddressFamily.InterNetwork );
|
_connection = new TcpClient( AddressFamily.InterNetwork );
|
||||||
Server.NoDelay = true;
|
_connection.NoDelay = true;
|
||||||
Server.Connect( ServerAddress );
|
_connection.Connect( ServerAddress );
|
||||||
ClientInterface.ServerClientInstance.InvokeOnServerConnected();
|
_parent.InvokeOnServerConnected();
|
||||||
Thread RThread = new Thread(async () => { await ReceiveThread(); });
|
Thread RThread = new Thread(async () => { await ReceiveThread(); });
|
||||||
RThread.Start();
|
RThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task ReceiveThread() {
|
async Task ReceiveThread() {
|
||||||
using( NetworkStream ns = Server.GetStream() ) {
|
using( NetworkStream ns = _connection.GetStream() ) {
|
||||||
try {
|
try {
|
||||||
byte[] headerBuffer = new byte[DataPacket.HeaderLength];
|
byte[] headerBuffer = new byte[DataPacket.HeaderLength];
|
||||||
byte[] payloadBuffer = new byte[65535]; // Upper limit for safety
|
byte[] payloadBuffer = new byte[65535]; // Upper limit for safety
|
||||||
while( ClientInterface.ServerClientInstance.Alive ) {
|
while( _parent.Alive ) {
|
||||||
|
|
||||||
// Read the header off
|
// Read the header off
|
||||||
await ns.ReadExactlyAsync(headerBuffer, 0, 3);
|
await ns.ReadExactlyAsync(headerBuffer, 0, 3);
|
||||||
@@ -42,20 +43,15 @@ namespace Mistox.Client {
|
|||||||
// Read off the data
|
// Read off the data
|
||||||
await ns.ReadExactlyAsync(payloadBuffer, 0, dataLength);
|
await ns.ReadExactlyAsync(payloadBuffer, 0, dataLength);
|
||||||
|
|
||||||
// Get the type from the tag
|
|
||||||
if (!DataPacket.ServerToClientPackets.TryGetValue(packetbyte, out Type resolvedType)) {
|
|
||||||
throw new Exception($"Unknown packet type ID: {packetbyte}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the dataobject
|
// Create the dataobject
|
||||||
dynamic dataobject = DataPacket.Deserialize( resolvedType, dataLength, payloadBuffer.AsMemory(DataPacket.HeaderLength, dataLength) );
|
dynamic dataobject = DataPacket.Deserialize( packetbyte, dataLength, payloadBuffer.AsMemory(DataPacket.HeaderLength, dataLength) );
|
||||||
|
|
||||||
// Return the object
|
// Return the object
|
||||||
ClientInterface.ServerClientInstance.InvokeOnReceived(dataobject);
|
_parent.InvokeOnReceived(dataobject);
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ClientInterface.ServerClientInstance.Alive = false;
|
_parent.Alive = false;
|
||||||
Dispose();
|
Dispose();
|
||||||
Console.WriteLine("TCP Receive exception: " + e.Message);
|
Console.WriteLine("TCP Receive exception: " + e.Message);
|
||||||
}
|
}
|
||||||
@@ -64,13 +60,13 @@ namespace Mistox.Client {
|
|||||||
|
|
||||||
public async Task Send<T>(T packet) {
|
public async Task Send<T>(T packet) {
|
||||||
byte[] data = DataPacket.Serialize( packet );
|
byte[] data = DataPacket.Serialize( packet );
|
||||||
await Server.GetStream().WriteAsync( data, 0, data.Length );
|
await _connection.GetStream().WriteAsync( data, 0, data.Length );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
ClientInterface.ServerClientInstance.InvokeOnServerDisconnected();
|
_parent.InvokeOnServerDisconnected();
|
||||||
Server.Close();
|
_connection.Close();
|
||||||
Server = null;
|
_connection = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,28 +9,33 @@ using System.Threading.Tasks;
|
|||||||
namespace Mistox.Client {
|
namespace Mistox.Client {
|
||||||
public class mUDPClient : IDisposable {
|
public class mUDPClient : IDisposable {
|
||||||
|
|
||||||
Socket udpClient;
|
public string UserName { get; set; } = ""; // The remote players UserName
|
||||||
IPEndPoint remoteHost;
|
public bool Alive { get; set; } = true; // Is the connection alive
|
||||||
|
|
||||||
public mUDPClient( long RemoteIP, int Port ) {
|
Socket _udpClient;
|
||||||
remoteHost = new IPEndPoint( RemoteIP, Port );
|
IPEndPoint _remoteHost;
|
||||||
udpClient = new Socket( AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp );
|
MistoxClient _parent;
|
||||||
udpClient.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.ReuseAddress, true );
|
|
||||||
udpClient.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, 128 );
|
public mUDPClient( MistoxClient parent, long RemoteIP, int Port ) {
|
||||||
udpClient.Bind( new IPEndPoint( IPAddress.Any, Port ) );
|
_parent = parent;
|
||||||
ClientInterface.ServerClientInstance.Alive = true;
|
_remoteHost = new IPEndPoint( RemoteIP, Port );
|
||||||
|
_udpClient = new Socket( AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp );
|
||||||
|
_udpClient.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.ReuseAddress, true );
|
||||||
|
_udpClient.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, 128 );
|
||||||
|
_udpClient.Bind( new IPEndPoint( IPAddress.Any, Port ) );
|
||||||
|
_parent.Alive = true;
|
||||||
Thread Client = new Thread(async() => { await ReceiveThread(); });
|
Thread Client = new Thread(async() => { await ReceiveThread(); });
|
||||||
Client.Start();
|
Client.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task ReceiveThread() {
|
async Task ReceiveThread() {
|
||||||
byte[] buffer = new byte[65535];
|
byte[] buffer = new byte[65535];
|
||||||
while( ClientInterface.ServerClientInstance.Alive ) {
|
while( _parent.Alive ) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// Read the data out of the UDP packet with a buffer that has the maximum possible UDP packet size
|
// Read the data out of the UDP packet with a buffer that has the maximum possible UDP packet size
|
||||||
// As UDP reads the whole next packet then drops. Im guaranteed to receive 1 packet at a time
|
// As UDP reads the whole next packet then drops. Im guaranteed to receive 1 packet at a time
|
||||||
int bytesRead = await udpClient.ReceiveAsync( buffer );
|
int bytesRead = await _udpClient.ReceiveAsync( buffer );
|
||||||
|
|
||||||
// Create the data readers
|
// Create the data readers
|
||||||
ReadOnlyMemory<byte> dataMemory = buffer.AsMemory(0, bytesRead);
|
ReadOnlyMemory<byte> dataMemory = buffer.AsMemory(0, bytesRead);
|
||||||
@@ -41,16 +46,11 @@ namespace Mistox.Client {
|
|||||||
ushort DataLength = (ushort)((dataPacket[1] << 8) | dataPacket[2]);
|
ushort DataLength = (ushort)((dataPacket[1] << 8) | dataPacket[2]);
|
||||||
ReadOnlyMemory<byte> data = dataMemory.Slice(DataPacket.HeaderLength, DataLength);
|
ReadOnlyMemory<byte> data = dataMemory.Slice(DataPacket.HeaderLength, DataLength);
|
||||||
|
|
||||||
// Get the type from the tag
|
|
||||||
if (!DataPacket.ClientToClientPackets.TryGetValue(dataType, out Type resolvedType)) {
|
|
||||||
throw new Exception($"Unknown packet type ID: {dataType}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the object
|
// Extract the object
|
||||||
dynamic buildObj = DataPacket.Deserialize( resolvedType, DataLength, data );
|
dynamic buildObj = DataPacket.Deserialize( dataType, DataLength, data );
|
||||||
|
|
||||||
// Return the object
|
// Return the object
|
||||||
ClientInterface.ServerClientInstance.InvokeOnReceived( buildObj );
|
_parent.InvokeOnReceived( buildObj );
|
||||||
|
|
||||||
} catch( Exception e ) {
|
} catch( Exception e ) {
|
||||||
Console.WriteLine( "UDP Receive exception : " + e.Message );
|
Console.WriteLine( "UDP Receive exception : " + e.Message );
|
||||||
@@ -60,13 +60,13 @@ namespace Mistox.Client {
|
|||||||
|
|
||||||
public async Task Send<T>( T Data ) {
|
public async Task Send<T>( T Data ) {
|
||||||
byte[] byteData = DataPacket.Serialize( Data );
|
byte[] byteData = DataPacket.Serialize( Data );
|
||||||
await udpClient.SendToAsync( byteData, SocketFlags.None, remoteHost );
|
await _udpClient.SendToAsync( byteData, SocketFlags.None, _remoteHost );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
ClientInterface.ServerClientInstance.Alive = false;
|
_parent.Alive = false;
|
||||||
udpClient.Close();
|
_udpClient.Close();
|
||||||
udpClient = null;
|
_udpClient = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -8,28 +7,16 @@ using Mistox.Client;
|
|||||||
namespace Mistox {
|
namespace Mistox {
|
||||||
public class ClientInterface {
|
public class ClientInterface {
|
||||||
|
|
||||||
public static MistoxServerClient ServerClientInstance;
|
public static MistoxClient serverInstance;
|
||||||
public static mTCPClient serverConnection;
|
|
||||||
public static ConcurrentDictionary<string, UDPEndpoint> clientConnections;
|
|
||||||
|
|
||||||
public event Action onServerConnected;
|
public event Action onServerConnected;
|
||||||
public event Action<UDPEndpoint> onUserConnected;
|
public event Action<mUDPClient> onUserConnected;
|
||||||
public event Action<dynamic> onDataReceived;
|
public event Action<dynamic> onDataReceived;
|
||||||
public event Action<UDPEndpoint> onUserDisconnected;
|
public event Action<mUDPClient> onUserDisconnected;
|
||||||
public event Action onServerDisconnected;
|
public event Action onServerDisconnected;
|
||||||
|
|
||||||
public ClientInterface( string IpOrHostName, int Port ) {
|
public ClientInterface( string IpOrHostName, int Port ) {
|
||||||
|
|
||||||
// Wire up the Mistox server client
|
|
||||||
ServerClientInstance = new MistoxServerClient() {
|
|
||||||
Port = Port
|
|
||||||
};
|
|
||||||
ServerClientInstance.onServerConnected += onServerConnected;
|
|
||||||
ServerClientInstance.onUserConnected += (UDPEndpoint endpoint) => { onUserConnected?.Invoke(endpoint); };
|
|
||||||
ServerClientInstance.onDataReceived += (dynamic data) => { onDataReceived?.Invoke(data); };
|
|
||||||
ServerClientInstance.onUserDisconnected += (UDPEndpoint endpoint) => { onUserDisconnected?.Invoke(endpoint); };
|
|
||||||
ServerClientInstance.onServerDisconnected += onServerDisconnected;
|
|
||||||
|
|
||||||
// Get Server IP
|
// Get Server IP
|
||||||
IPHostEntry host = Dns.GetHostEntry( IpOrHostName );
|
IPHostEntry host = Dns.GetHostEntry( IpOrHostName );
|
||||||
IPEndPoint ServerDenstination = new IPEndPoint(0, 0);
|
IPEndPoint ServerDenstination = new IPEndPoint(0, 0);
|
||||||
@@ -38,30 +25,28 @@ namespace Mistox {
|
|||||||
ServerDenstination = new IPEndPoint( entry, Port );
|
ServerDenstination = new IPEndPoint( entry, Port );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Console.WriteLine( "The client is initilized and trying to connect to the server at ip : " + ServerDenstination.Address );
|
|
||||||
|
|
||||||
// Make a TCP connection to the server
|
// Start and wire up the Mistox server client
|
||||||
try {
|
serverInstance = new MistoxClient( ServerDenstination );
|
||||||
serverConnection = new mTCPClient( ServerDenstination );
|
serverInstance.onServerConnected += onServerConnected;
|
||||||
} catch( Exception e ) {
|
serverInstance.onUserConnected += (mUDPClient endpoint) => { onUserConnected?.Invoke(endpoint); };
|
||||||
Console.WriteLine( "An error has occured with the connection to the server. Error { " );
|
serverInstance.onDataReceived += (dynamic data) => { onDataReceived?.Invoke(data); };
|
||||||
Console.WriteLine( e.ToString() );
|
serverInstance.onUserDisconnected += (mUDPClient endpoint) => { onUserDisconnected?.Invoke(endpoint); };
|
||||||
Console.WriteLine( "}" );
|
serverInstance.onServerDisconnected += onServerDisconnected;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendAllClients<Packet>(Packet data) {
|
public async Task SendAllClients<Packet>(Packet data) {
|
||||||
foreach(KeyValuePair<string, UDPEndpoint> cur in clientConnections){
|
foreach(KeyValuePair<string, mUDPClient> cur in serverInstance.clientConnections){
|
||||||
await cur.Value.Connection.Send( data );
|
await cur.Value.Send( data );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendClient<Packet>(string UserName, Packet data) {
|
public async Task SendClient<Packet>(string UserName, Packet data) {
|
||||||
await clientConnections[UserName].Connection.Send( data );
|
await serverInstance.clientConnections[UserName].Send( data );
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendServer<Packet>(Packet data) {
|
public async Task SendServer<Packet>(Packet data) {
|
||||||
await serverConnection.Send( data );
|
await serverInstance.ServerConnection.Send( data );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using Mistox.Attributes;
|
||||||
|
|
||||||
namespace Mistox {
|
namespace Mistox {
|
||||||
public class mServer {
|
public class mServer {
|
||||||
|
|
||||||
public static ServerInterface newServer( string serverName, int port ) {
|
public static ServerInterface newServer( string serverName, int port ) {
|
||||||
DataPacket.BuildNetworkPacketList();
|
NetworkPacketDictionary.BuildNetworkPacketList();
|
||||||
return new ServerInterface(port, serverName);
|
return new ServerInterface(port, serverName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ClientInterface newClient( string ServerIPOrHostName, int Port ) {
|
public static ClientInterface newClient( string ServerIPOrHostName, int Port ) {
|
||||||
DataPacket.BuildNetworkPacketList();
|
NetworkPacketDictionary.BuildNetworkPacketList();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
IPAddress[] remoteAddress = Dns.GetHostAddresses(ServerIPOrHostName);
|
IPAddress[] remoteAddress = Dns.GetHostAddresses(ServerIPOrHostName);
|
||||||
for( int i=0; i< remoteAddress.Length; i++ ) {
|
for( int i=0; i< remoteAddress.Length; i++ ) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -8,44 +7,36 @@ using Mistox.Server;
|
|||||||
namespace Mistox {
|
namespace Mistox {
|
||||||
public class ServerInterface {
|
public class ServerInterface {
|
||||||
|
|
||||||
public static MistoxServer ServerInstance; // Server Data
|
public static ServerEntity ServerInstance; // Server Data
|
||||||
public static ServerListener Listener; // Server Listener
|
|
||||||
public static ConcurrentDictionary<string, TcpClientConnection> Connections; // Server Connections
|
|
||||||
|
|
||||||
public event Action<TcpClientConnection> onUserConnected; // User connected event handler
|
public event Action<ServerConnection> onUserConnected; // User connected event handler
|
||||||
public event Action<dynamic> onDataReceived; // Data received event handler
|
public event Action<dynamic> onDataReceived; // Data received event handler
|
||||||
public event Action<TcpClientConnection> onUserDisconnected; // User disconnected event handler
|
public event Action<ServerConnection> onUserDisconnected; // User disconnected event handler
|
||||||
|
|
||||||
public ServerInterface( int port, string serverName ) {
|
public ServerInterface( int port, string serverName ) {
|
||||||
|
|
||||||
|
IPEndPoint listenerAddress = new IPEndPoint( IPAddress.Any, port );
|
||||||
|
|
||||||
// Initilize the server
|
// Initilize the server
|
||||||
ServerInstance = new MistoxServer() {
|
ServerInstance = new ServerEntity(listenerAddress);
|
||||||
ServerName = serverName,
|
ServerInstance.ServerName = serverName;
|
||||||
BindAddress = IPAddress.Any,
|
|
||||||
Port = port,
|
|
||||||
MaxClients = 10
|
|
||||||
};
|
|
||||||
Connections = new ConcurrentDictionary<string, TcpClientConnection>();
|
|
||||||
|
|
||||||
// Setup the event handlers
|
// Setup the event handlers
|
||||||
ServerInstance.onUserConnected += (TcpClientConnection connectedUser) => { onUserConnected?.Invoke(connectedUser); };
|
ServerInstance.onUserConnected += (ServerConnection connectedUser) => { onUserConnected?.Invoke(connectedUser); };
|
||||||
ServerInstance.onDataReceived += (dynamic data) => { onDataReceived?.Invoke(data); };
|
ServerInstance.onDataReceived += (dynamic data) => { onDataReceived?.Invoke(data); };
|
||||||
ServerInstance.onUserDisconnected += (TcpClientConnection disconnectedUser) => { onUserDisconnected?.Invoke(disconnectedUser); };
|
ServerInstance.onUserDisconnected += (ServerConnection disconnectedUser) => { onUserDisconnected?.Invoke(disconnectedUser); };
|
||||||
|
|
||||||
// Start listening for connections and wire up the server
|
|
||||||
Listener = new ServerListener();
|
|
||||||
|
|
||||||
Console.WriteLine( "The Server is initilized and waiting for clients to connect at port : " + port );
|
Console.WriteLine( "The Server is initilized and waiting for clients to connect at port : " + port );
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendAllClients<Packet>(Packet data) {
|
public async Task SendAllClients<Packet>(Packet data) {
|
||||||
foreach( KeyValuePair<string, TcpClientConnection> cur in Connections ) {
|
foreach( KeyValuePair<string, ServerConnection> cur in ServerInstance.Connections ) {
|
||||||
await cur.Value.Connection.Send(data);
|
await cur.Value.Send(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendClient<Packet>(string UserName, Packet data) {
|
public async Task SendClient<Packet>(string UserName, Packet data) {
|
||||||
await Connections[UserName].Connection.Send( data );
|
await ServerInstance.Connections[UserName].Send( data );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace Mistox.Server {
|
|
||||||
|
|
||||||
public class MistoxServer {
|
|
||||||
public string ServerName { get; set; } = ""; // The name of the server
|
|
||||||
public IPAddress BindAddress { get; set; } = IPAddress.Any; // What IP to bind on
|
|
||||||
public int Port { get; set; } = 9126; // What Port to bind on
|
|
||||||
public int MaxClients { get; set; } = 10; // The max players per server
|
|
||||||
public bool Alive { get; set; } = true; // Is the server running
|
|
||||||
|
|
||||||
public event Action<TcpClientConnection> onUserConnected;
|
|
||||||
public void InvokeUserConnected( TcpClientConnection user ){ onUserConnected?.Invoke( user ); }
|
|
||||||
public event Action<dynamic> onDataReceived;
|
|
||||||
public void InvokeDataReceived( dynamic data ){ onDataReceived?.Invoke( data ); }
|
|
||||||
public event Action<TcpClientConnection> onUserDisconnected;
|
|
||||||
public void InvokeUserDisconnected( TcpClientConnection user ){ onUserDisconnected?.Invoke( user ); }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Mistox.Server {
|
|
||||||
public class TcpClientConnection {
|
|
||||||
public string UserName { get; set; } = ""; // The Players UserName
|
|
||||||
public bool IsAuthenticated { get; set; } = false; // Has the player authenticated
|
|
||||||
public Vector3 Position { get; set; } // The ingame position of the player
|
|
||||||
public float ConnectionRange { get; set; } // The ingame distance players need to be to be connected together
|
|
||||||
public DateTime LastHeartbeat { get; set; } = DateTime.UtcNow; // Connection heartbeat for integrity
|
|
||||||
public bool Connected { get; set; } = true; // Is the connection valid
|
|
||||||
public IPAddress RemoteAddress { get; set; } // The remote users IP Address
|
|
||||||
public TCPConnection Connection { get; set; } // The TCP Server Object
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Mistox.Server {
|
||||||
|
|
||||||
|
public class ServerEntity {
|
||||||
|
|
||||||
|
// Class Definitions
|
||||||
|
|
||||||
|
public string ServerName { get; set; } = ""; // The name of the server
|
||||||
|
public int MaxClients { get; set; } = 10; // The max players per server
|
||||||
|
public float ConnectionRange { get; set; } = 40; // InGame distance for valid conections
|
||||||
|
public int ConnectionTimeoutSeconds { get; set; } = 10; // If no reply in time kill the connection
|
||||||
|
public bool Alive { get; set; } = true; // Is the server running
|
||||||
|
|
||||||
|
ServerListener Listener { get; set;}
|
||||||
|
public ConcurrentDictionary<string, ServerConnection> Connections;
|
||||||
|
|
||||||
|
public ServerEntity(IPEndPoint ListenerAddress) {
|
||||||
|
Listener = new ServerListener(ListenerAddress, this);
|
||||||
|
Connections = new ConcurrentDictionary<string, ServerConnection>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
public event Action<ServerConnection> onUserConnected;
|
||||||
|
public void InvokeUserConnected( ServerConnection user ){
|
||||||
|
onUserConnected?.Invoke( user );
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<dynamic> onDataReceived;
|
||||||
|
public void InvokeDataReceived( dynamic data ){
|
||||||
|
onDataReceived?.Invoke( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<ServerConnection> onUserDisconnected;
|
||||||
|
public void InvokeUserDisconnected( ServerConnection user ){
|
||||||
|
onUserDisconnected?.Invoke( user );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,19 +1,30 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using System.Numerics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Mistox.Server {
|
namespace Mistox.Server {
|
||||||
public class TCPConnection : IDisposable {
|
public class ServerConnection : IDisposable {
|
||||||
|
|
||||||
|
public string UserName { get; set; } = ""; // The Players UserName
|
||||||
|
public bool IsAuthenticated { get; set; } = false; // Has the player authenticated
|
||||||
|
public Vector3 Position { get; set; } // The ingame position of the player
|
||||||
|
public DateTime LastHeartbeat { get; set; } = DateTime.UtcNow; // Connection heartbeat for integrity
|
||||||
|
public bool Connected { get; set; } = true; // Is the connection valid
|
||||||
|
|
||||||
|
ServerEntity _parent;
|
||||||
TcpClient _client;
|
TcpClient _client;
|
||||||
TcpClientConnection _connection;
|
IPEndPoint _remoteAddress;
|
||||||
|
|
||||||
public TCPConnection( TcpClient tcpClient, string ConnectionID ) {
|
public ServerConnection( TcpClient tcpClient, ServerEntity parent, IPEndPoint remote ) {
|
||||||
|
_parent = parent;
|
||||||
_client = tcpClient;
|
_client = tcpClient;
|
||||||
|
_remoteAddress = remote;
|
||||||
_client.NoDelay = true;
|
_client.NoDelay = true;
|
||||||
_connection = ServerInterface.Connections[ConnectionID];
|
|
||||||
ServerInterface.ServerInstance.InvokeUserConnected( ServerInterface.Connections[ConnectionID] );
|
ServerInterface.ServerInstance.InvokeUserConnected( this );
|
||||||
// Start the receive thread
|
// Start the receive thread
|
||||||
Thread receiveThread = new Thread(async () => await ReceiveThread() );
|
Thread receiveThread = new Thread(async () => await ReceiveThread() );
|
||||||
receiveThread.Start();
|
receiveThread.Start();
|
||||||
@@ -24,7 +35,16 @@ namespace Mistox.Server {
|
|||||||
try {
|
try {
|
||||||
byte[] headerBuffer = new byte[DataPacket.HeaderLength];
|
byte[] headerBuffer = new byte[DataPacket.HeaderLength];
|
||||||
byte[] payloadBuffer = new byte[65535]; // Upper limit for safety
|
byte[] payloadBuffer = new byte[65535]; // Upper limit for safety
|
||||||
while( _connection.Connected ) {
|
while( Connected && _parent.Alive ) {
|
||||||
|
|
||||||
|
// Tick the last heartbeat or drop
|
||||||
|
DateTime now = DateTime.UtcNow;
|
||||||
|
if (LastHeartbeat.AddSeconds(_parent.ConnectionTimeoutSeconds) >= now) {
|
||||||
|
Connected = false;
|
||||||
|
Dispose();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LastHeartbeat = DateTime.UtcNow;
|
||||||
|
|
||||||
// Read the header off
|
// Read the header off
|
||||||
await ns.ReadExactlyAsync(headerBuffer, 0, 3);
|
await ns.ReadExactlyAsync(headerBuffer, 0, 3);
|
||||||
@@ -41,20 +61,15 @@ namespace Mistox.Server {
|
|||||||
// Read off the data
|
// Read off the data
|
||||||
await ns.ReadExactlyAsync(payloadBuffer, 0, dataLength);
|
await ns.ReadExactlyAsync(payloadBuffer, 0, dataLength);
|
||||||
|
|
||||||
// Get the type from the tag
|
|
||||||
if (!DataPacket.ServerToClientPackets.TryGetValue(packetbyte, out Type resolvedType)) {
|
|
||||||
throw new Exception($"Unknown packet type ID: {packetbyte}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the dataobject
|
// Create the dataobject
|
||||||
dynamic dataobject = DataPacket.Deserialize( resolvedType, dataLength, payloadBuffer.AsMemory(DataPacket.HeaderLength, dataLength) );
|
dynamic dataobject = DataPacket.Deserialize( packetbyte, dataLength, payloadBuffer.AsMemory(DataPacket.HeaderLength, dataLength) );
|
||||||
|
|
||||||
// Return the object
|
// Return the object
|
||||||
ServerInterface.ServerInstance.InvokeDataReceived(dataobject);
|
_parent.InvokeDataReceived(dataobject);
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ClientInterface.ServerClientInstance.Alive = false;
|
_parent.Alive = false;
|
||||||
Dispose();
|
Dispose();
|
||||||
Console.WriteLine("TCP Receive exception: " + e.Message);
|
Console.WriteLine("TCP Receive exception: " + e.Message);
|
||||||
}
|
}
|
||||||
@@ -67,10 +82,11 @@ namespace Mistox.Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
_connection.Connected = false;
|
Connected = false;
|
||||||
ServerInterface.ServerInstance.InvokeUserDisconnected( _connection );
|
_parent.InvokeUserDisconnected( this );
|
||||||
ServerInterface.ServerInstance.Alive = false;
|
Connected = false;
|
||||||
ServerInterface.Connections.TryRemove( _connection.UserName, out _ );
|
_parent.Alive = false;
|
||||||
|
// Remove this item from the Dictionary on the Server for garbage collection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,30 +9,31 @@ using System.Threading.Tasks;
|
|||||||
namespace Mistox.Server {
|
namespace Mistox.Server {
|
||||||
public class ServerListener : IDisposable {
|
public class ServerListener : IDisposable {
|
||||||
|
|
||||||
|
ServerEntity _parent;
|
||||||
|
IPEndPoint _bindAddress;
|
||||||
TcpListener _Listener;
|
TcpListener _Listener;
|
||||||
|
|
||||||
public ServerListener() {
|
public ServerListener(IPEndPoint listenerAddress, ServerEntity parent) {
|
||||||
|
_bindAddress = listenerAddress;
|
||||||
|
_parent = parent;
|
||||||
Thread ConnectionThread = new Thread(async () => await ListenerThread() );
|
Thread ConnectionThread = new Thread(async () => await ListenerThread() );
|
||||||
ConnectionThread.Start();
|
ConnectionThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task ListenerThread() {
|
async Task ListenerThread() {
|
||||||
_Listener = new TcpListener( ServerInterface.ServerInstance.BindAddress, ServerInterface.ServerInstance.Port );
|
_Listener = new TcpListener( _bindAddress );
|
||||||
_Listener.Start();
|
_Listener.Start();
|
||||||
while( ServerInterface.ServerInstance.Alive ) {
|
while( _parent.Alive ) {
|
||||||
TcpClient client = await _Listener.AcceptTcpClientAsync();
|
TcpClient client = await _Listener.AcceptTcpClientAsync();
|
||||||
|
|
||||||
if (ServerInterface.Connections.Count + 1 > ServerInterface.ServerInstance.MaxClients) {
|
if (_parent.Connections.Count + 1 > _parent.MaxClients) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPEndPoint remote = client.Client.RemoteEndPoint as IPEndPoint;
|
IPEndPoint remote = client.Client.RemoteEndPoint as IPEndPoint;
|
||||||
string connectionID = Guid.NewGuid().ToString();
|
string connectionID = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
ServerInterface.Connections[connectionID] = new TcpClientConnection() {
|
_parent.Connections[connectionID] = new ServerConnection( client, _parent, remote );
|
||||||
Connection = new TCPConnection( client, connectionID ),
|
|
||||||
RemoteAddress = remote.Address
|
|
||||||
};
|
|
||||||
|
|
||||||
Console.WriteLine( "New User Connected" );
|
Console.WriteLine( "New User Connected" );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using MessagePack;
|
using MessagePack;
|
||||||
using Mistox.Attributes;
|
using Mistox.Attributes;
|
||||||
@@ -43,42 +42,30 @@ namespace Mistox {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object Deserialize( Type PacketType, ushort DataLength, ReadOnlyMemory<byte> Data ) {
|
public static object Deserialize( byte PacketByte, ushort DataLength, ReadOnlyMemory<byte> Data ) {
|
||||||
|
|
||||||
|
// Get the type from the tag
|
||||||
|
if (!NetworkPacketDictionary.ServerToClientPackets.TryGetValue(PacketByte, out Type resolvedType)) {
|
||||||
|
throw new Exception($"Unknown packet type ID: {PacketByte}");
|
||||||
|
}
|
||||||
|
|
||||||
// Sanity check: Ensure the packet we received actually has the payload bytes it claims to have
|
// Sanity check: Ensure the packet we received actually has the payload bytes it claims to have
|
||||||
if ( DataLength <= Data.Length ) {
|
if ( DataLength < Data.Length ) {
|
||||||
throw new Exception("Malformed packet: Payload size mismatch.");
|
throw new Exception("Malformed packet: Payload size mismatch.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Create the low-level reader struct directly over the memory slice
|
// Make sure the data is exactly the size requested
|
||||||
var reader = new MessagePackReader(Data);
|
ReadOnlyMemory<byte> payloadSlice = Data.Slice(0, DataLength);
|
||||||
|
|
||||||
|
// Create the low-level reader struct directly over the memory slice
|
||||||
|
var reader = new MessagePackReader(payloadSlice);
|
||||||
|
|
||||||
// Extract the object
|
// Extract the object
|
||||||
dynamic obj = MessagePackSerializer.Deserialize( PacketType, ref reader, MessagePackSerializerOptions.Standard );
|
dynamic obj = MessagePackSerializer.Deserialize( resolvedType, ref reader, MessagePackSerializerOptions.Standard );
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Fills out the NetworkPacket Lookup Dictionary
|
|
||||||
public static readonly Dictionary<byte, Type> ClientToClientPackets = new();
|
|
||||||
public static readonly Dictionary<byte, Type> ServerToClientPackets = new();
|
|
||||||
public static void BuildNetworkPacketList() {
|
|
||||||
Type[] allClasses = Assembly.GetExecutingAssembly().GetTypes();
|
|
||||||
foreach( Type cur in allClasses) {
|
|
||||||
mPacket attribute = cur.GetCustomAttribute<mPacket>(false);
|
|
||||||
if (attribute != null) {
|
|
||||||
if ( attribute.PacketUse.HasFlag(PacketUsage.ClientToClient) ) {
|
|
||||||
ClientToClientPackets[attribute.ID] = cur;
|
|
||||||
} else if ( attribute.PacketUse.HasFlag(PacketUsage.ClientToServer) ) {
|
|
||||||
ServerToClientPackets[attribute.ID] = cur;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Mistox.Attributes {
|
||||||
|
|
||||||
|
public class NetworkPacketDictionary {
|
||||||
|
|
||||||
|
// Fills out the NetworkPacket Lookup Dictionary
|
||||||
|
public static readonly Dictionary<byte, Type> ClientToClientPackets = new();
|
||||||
|
public static readonly Dictionary<byte, Type> ServerToClientPackets = new();
|
||||||
|
public static void BuildNetworkPacketList() {
|
||||||
|
Type[] allClasses = Assembly.GetExecutingAssembly().GetTypes();
|
||||||
|
foreach( Type cur in allClasses) {
|
||||||
|
mPacket attribute = cur.GetCustomAttribute<mPacket>(false);
|
||||||
|
if (attribute != null) {
|
||||||
|
if ( attribute.PacketUse.HasFlag(PacketUsage.ClientToClient) ) {
|
||||||
|
ClientToClientPackets[attribute.ID] = cur;
|
||||||
|
} else if ( attribute.PacketUse.HasFlag(PacketUsage.ClientToServer) ) {
|
||||||
|
ServerToClientPackets[attribute.ID] = cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using Mistox.Attributes;
|
||||||
|
|
||||||
|
namespace Mistox.ResevedPackets {
|
||||||
|
|
||||||
|
[mPacket(0xFF)]
|
||||||
|
public class EntityPacket {
|
||||||
|
public ushort ObjectId { get; set; }
|
||||||
|
public byte[] InnerPayload { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[mPacket(0xFE)]
|
||||||
|
public class UserConnectedPacket {
|
||||||
|
public string UserName { get; set; }
|
||||||
|
public long ClientIPAddress { get; set; }
|
||||||
|
public int ClientPort { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[mPacket(0xFD)]
|
||||||
|
public class UserDisconnectedPacket {
|
||||||
|
public string UserName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in new issue
Block a user