Compare commits

...
5 Commits
Author SHA1 Message Date
derek.holloway d7581fbdeb Null check instead of initilizing empty work 2026-08-10 07:26:18 -07:00
derek.holloway c8a0aef289 Get CLI to a base state 2026-08-07 13:20:32 -07:00
derek.holloway 068c88de4b Update the CLI example 2026-08-07 13:00:23 -07:00
derek.holloway 30feb15821 Fix all of the nullable issues 2026-08-07 13:00:04 -07:00
derek.holloway 9d97ad0975 fix build.yaml 2026-08-07 08:00:47 -07:00
19 changed files with 249 additions and 142 deletions

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
name: Build and publish mistserver
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: alpine-linux
steps:
- name: checkout
uses: actions/checkout@v4
- name: Restore Dependencies
run: dotnet restore ./MistoxServer
- name: Dotnet Publish
run: dotnet publish ./MistoxServer --configuration Release --output ./publish --no-restore
- name: Archive Production Artifacts
run: |
cd ./publish
zip -r ../mistserver-${{ github.ref_name }}.zip .
cd ..
# 2. Publish directly to Gitea Releases
- name: Create Gitea Release
uses: https://gitea.com/actions/release-action@main
with:
# Automatically uses the tag string pushed (e.g., v1.2.3)
name: "Release ${{ github.ref_name }}"
# Change this text to your desired version description (Supports Markdown)
body: |
## Mistserver Version ${{ github.ref_name }}
Automated deployment build compiled on Alpine Linux.
### Changelog
- Compiled via Gitea Actions
- Run ID: ${{ github.run_id }}
# Target the zipped artifact we generated in step 1
files: |
mistserver-*.zip
# Uses Gitea's automatically injected repository token for authorization
api_key: '${{ secrets.GITHUB_TOKEN }}'
+60
View File
@@ -0,0 +1,60 @@
using System;
using System.Threading.Tasks;
using Mistox;
using Mistox.Client;
class clientExample {
public MistoxClient? clientObject;
void onServerConnected() {
Console.WriteLine("Connected to server sucessfully");
}
void onClientConnected(mUDPClient user) {
Console.WriteLine("User connected to server with username: " + user.UserName);
}
void onClientJoined(mUDPClient user) {
Console.WriteLine("User is now in range of you and UDP connections started with user : " + user.UserName);
}
void onReceive(mUDPClient? user, dynamic data) {
switch (data){
case MessagePacket packet:
if (user == null) {
Console.WriteLine($"MSG From [Serverx]: {packet.Data}");
} else {
Console.WriteLine($"MSG From [{user.UserName}]: {packet.Data}");
}
break;
default:
break;
}
}
void onClientLeft(mUDPClient user) {
Console.WriteLine("User is no longer in range of you and UDP connections ended with user : " + user.UserName);
}
void onClientDisconnected(mUDPClient user) {
Console.WriteLine("User disconnected from server with username: " + user.UserName);
}
void onServerDisconnected() {
Console.WriteLine("Disconnected from server");
}
public async Task StartClient(string userName, string host, int port, string password) {
clientObject = GameServer.newClient(userName, host, port, password);
if (clientObject == null) return;
clientObject.onServerConnected += onServerConnected;
clientObject.onUserConnected += onClientConnected;
clientObject.onUserJoined += onClientJoined;
clientObject.onDataReceived += onReceive;
clientObject.onUserLeft += onClientLeft;
clientObject.onUserDisconnected += onClientDisconnected;
clientObject.onServerDisconnected += onServerDisconnected;
}
}
-15
View File
@@ -1,15 +0,0 @@
The servers default port is 25566
TCPServer.cs
Void TCPServer() Initilizes the server object on the default game port of 25566
Void SendAll(string Data) Send data to all the clients
Event onReceived Fires when data is received from a client
TCPClient.cs
Void TCPClient(String ServerIP) Initilizes the client object
Void Send(String Data) Sends data to the server
Event onReceived Fires when data is received from the server
UDPClient.cs
+1
View File
@@ -9,6 +9,7 @@
<Company>Mistox.net</Company> <Company>Mistox.net</Company>
<Product>Mistox Game Server</Product> <Product>Mistox Game Server</Product>
<Description>Game Server for Mistox Games and partners</Description> <Description>Game Server for Mistox Games and partners</Description>
<Nullable>enable</Nullable>
<SignAssembly>false</SignAssembly> <SignAssembly>false</SignAssembly>
</PropertyGroup> </PropertyGroup>
+42 -83
View File
@@ -1,85 +1,48 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using MistoxServer;
namespace MistoxHolePunch { namespace MistoxHolePunch {
enum Mode {
Server,
Client
}
class Program { class Program {
static IMistoxServer serverObj; static serverExample? server;
static bool running = true; static clientExample? client;
static Mode mode;
void onConnected( object sender, EventArgs e ) { async Task StartServer(string name, int port) {
// sender and e are always null server = new serverExample();
// put connected functions in here await server.StartServer(name, port);
} // On a game this sleep loop isnt needed
if (server.serverObject == null) return;
void slowReceive( object obj, EventArgs e ) { while (server.serverObject.Alive) {
if ( serverObj is ServerInterface ) { // becuase this is a console app if we dont keep the main thread alive the app will close
// If ServerMode is passive obj is byte[] and Send takes in byte[] await Task.Delay(5000);
// If Servermode is Athoritiative obj is the Class type sent and Send takes in any Class type except generic object
// Check to make sure data is correct before relaying
// Also perform server specific checks in here
// IE player didnt teleport or is shooting from 20 feet from his body
serverObj.Send( obj, SendType.SlowUpdate );
}
Console.Write( "Received TCP : " );
Console.WriteLine( obj );
}
void fastReceive( object obj, EventArgs e ) {
if( serverObj is ServerInterface ) {
// If ServerMode is passive obj is byte[] and Send takes in byte[]
// If Servermode is Athoritiative obj is the Class type sent and Send takes in any Class type except generic object
// Check to make sure data is correct before relaying
// Also perform server specific checks in here
// IE player didnt teleport or is shooting from 20 feet from his body
serverObj.Send( obj, SendType.FastUpdate );
}
Console.Write( "Received UDP : " );
Console.WriteLine( obj );
}
void onDisconnected( object sender, EventArgs e ) {
// sender and e are always null
// put disconnected functions in here
}
static string host = "example.com";
static int port = 7300;
static ServerMode mode = ServerMode.Passive;
async Task RunServer() {
serverObj = mServer.newServer( Convert.ToInt32( port ), mode );
serverObj.onConnected += onConnected;
serverObj.onSlowReceive += slowReceive;
serverObj.onFastReceive += fastReceive;
serverObj.onDisconnected += onDisconnected;
while ( running ) {
// Stop this thread for 1 second so the CPU isnt 100%
// All the real work is on different threads
// While loop needs to exist so our main thread doesnt close
await Task.Delay( 1000 );
} }
} }
async Task RunClient() { async Task StartClient(string username, string host, int port, string password) {
serverObj = mServer.newClient( host, Convert.ToInt32( port ) ); client = new clientExample();
serverObj.onConnected += onConnected; await client.StartClient(username, host, port, password);
serverObj.onSlowReceive += slowReceive; // On a game this sleep loop isnt needed
serverObj.onFastReceive += fastReceive; if (client.clientObject == null) return;
serverObj.onDisconnected += onDisconnected; while (client.clientObject.Alive) {
while( running ) { // becuase this is a console app if we dont keep the main thread alive the app will close
// Stop this thread for 1 second so the CPU isnt 100% await Task.Delay(5000);
// All the real work is on different threads
// While loop needs to exist so our main thread doesnt close
await Task.Delay( 1000 );
} }
} }
static async Task Main(string[] args) { async Task Main(string[] args) {
string Task = args.Length > 0 ? args[0].ToLower() : null; string Job = args.Length > 0 ? args[0].ToLower() : "";
string host = "";
string name = "";
string pass = "";
int port = 0;
for( int i = 0; i < args.Length; i++ ) { for( int i = 0; i < args.Length; i++ ) {
string cur = args[i].ToLower(); string cur = args[i].ToLower();
@@ -87,25 +50,21 @@ namespace MistoxHolePunch {
host = args [i + 1]; host = args [i + 1];
} else if( cur == "/p" || cur == "-p" ) { } else if( cur == "/p" || cur == "-p" ) {
port = Convert.ToInt32( args [i + 1] ); port = Convert.ToInt32( args [i + 1] );
} else if( cur == "/a" || cur == "-a" ) { } else if( cur == "/n" || cur == "-n" ) {
mode = ServerMode.Authoritative; name = args [i + 1];
} else if( cur == "/u" || cur == "-u" ) {
pass = args [i + 1];
} }
} }
if (Task == "/?" || Task == "--help") { if (Job == "/?" || Job == "--help") {
Console.WriteLine(HelpDocumentation.HelpText); Console.WriteLine(HelpDocumentation.HelpText);
} else if (Task == "/s" || Task == "-s") { } else if (Job == "/s" || Job == "-s") {
Program prog = new Program(); await StartServer(name, port);
await prog.RunServer(); } else if (Job == "/c" || Job == "-c") {
} else if (Task == "/c" || Task == "-c") { await StartClient(name, host, port, pass);
Program prog = new Program();
await prog.RunClient();
} else { } else {
host = "example.com"; await StartServer("example.com", 4500);
port = 1500;
Program prog = new Program();
//prog.RunClient();
await prog.RunServer();
} }
} }
+35
View File
@@ -0,0 +1,35 @@
using System;
using System.Threading.Tasks;
using Mistox;
using Mistox.Server;
class serverExample {
public MistoxServer? serverObject;
void onClientConnected(ServerConnection user) {
Console.WriteLine("User connected with username: " + user.UserName);
}
void onReceive(ServerConnection user, dynamic data) {
switch (data){
case MessagePacket packet when user.IsAuthenticated:
Console.WriteLine($"MSG From [{user.UserName}]: {packet.Data}");
break;
default:
break;
}
}
void onClientDisconnected(ServerConnection user) {
Console.WriteLine("User disconnected with username: " + user.UserName);
}
public async Task StartServer(string serverName, int port) {
serverObject = GameServer.newServer(serverName, port);
serverObject.onUserConnected += onClientConnected;
serverObject.onDataReceived += onReceive;
serverObject.onUserDisconnected += onClientDisconnected;
}
}
+6
View File
@@ -0,0 +1,6 @@
using Mistox.Attributes;
[mPacket]
class MessagePacket {
public string Data { get; set; } = "";
}
+25 -19
View File
@@ -20,7 +20,7 @@ namespace Mistox.Client {
public Vector3 Position { get; set; } = new Vector3(0, 0, 0); // My world position public Vector3 Position { get; set; } = new Vector3(0, 0, 0); // My world position
public float MaxNetworkRenderDistance { get; private set; } = 0; // The distance for connections public float MaxNetworkRenderDistance { get; private set; } = 0; // The distance for connections
public mTCPClient ServerConnection {get; private set; } // Global TCP Server Connection public mTCPClient? ServerConnection {get; private set; } // Global TCP Server Connection
public UdpClient ClientSocket { get; private set; } // Global UDP Sender public UdpClient ClientSocket { get; private set; } // Global UDP Sender
public UDPReceiver UdpReceiver { get; private set; } // Global UDP Receiver public UDPReceiver UdpReceiver { get; private set; } // Global UDP Receiver
@@ -87,13 +87,17 @@ namespace Mistox.Client {
if (!clientConnections.ContainsKey(remoteHost)) { if (!clientConnections.ContainsKey(remoteHost)) {
mUDPClient newConnection = new mUDPClient(this, remoteHost); mUDPClient newConnection = new mUDPClient(this, remoteHost);
clientConnections[remoteHost] = newConnection; clientConnections[remoteHost] = newConnection;
InvokeOnUserJoined(newConnection);
} }
} }
// Remove users that left // Remove users that left
foreach (IPEndPoint key in clientConnections.Keys) { foreach (IPEndPoint key in clientConnections.Keys) {
if (!activeIps.Contains(key.Address)) { if (!activeIps.Contains(key.Address)) {
clientConnections.TryRemove(key, out _); mUDPClient? user;
clientConnections.TryRemove(key, out user);
if (user == null) return;
InvokeOnUserLeft(user);
} }
} }
@@ -116,6 +120,7 @@ namespace Mistox.Client {
async Task TryAuth() { async Task TryAuth() {
Console.WriteLine("Attempting to authenticate against the server..."); Console.WriteLine("Attempting to authenticate against the server...");
if (ServerConnection == null) return;
await ServerConnection.Send(new UserAuthPacket { await ServerConnection.Send(new UserAuthPacket {
UserName = UserName, UserName = UserName,
ConnectionKey = ConnectionKey ConnectionKey = ConnectionKey
@@ -134,7 +139,7 @@ namespace Mistox.Client {
///////////////////////////// /////////////////////////////
// When (self) connects to the server // When (self) connects to the server
public event Action onServerConnected; public event Action? onServerConnected;
public void InvokeOnServerConnected(){ public void InvokeOnServerConnected(){
Alive = true; Alive = true;
Task.Run(async () => { Task.Run(async () => {
@@ -146,20 +151,20 @@ namespace Mistox.Client {
} }
// When any user connects to the server (self) hasn't seen yet // When any user connects to the server (self) hasn't seen yet
public event Action<string> onUserJoined; public event Action<mUDPClient>? onUserJoined;
public void InvokeOnUserJoined(string UserName){ public void InvokeOnUserJoined(mUDPClient User){
onUserJoined?.Invoke(UserName); onUserJoined?.Invoke(User);
} }
// When user is within range of (self) and network rendering starts // When user is within range of (self) and network rendering starts
public event Action<mUDPClient> onUserConnected; public event Action<mUDPClient>? onUserConnected;
public void InvokeOnUserConnected(mUDPClient ConnectedUser){ public void InvokeOnUserConnected(mUDPClient User){
onUserConnected?.Invoke(ConnectedUser); onUserConnected?.Invoke(User);
} }
// When generic data is received // When generic data is received
public event Action<dynamic> onDataReceived; public event Action<mUDPClient?, dynamic>? onDataReceived;
public void InvokeOnReceived(dynamic DataPacket, mUDPClient remote = null){ public void InvokeOnReceived(dynamic DataPacket, mUDPClient? remote = null){
switch (DataPacket as object) { switch (DataPacket as object) {
case HeartbeatPacket packet: case HeartbeatPacket packet:
HeartbeatReceived(packet); HeartbeatReceived(packet);
@@ -173,25 +178,25 @@ namespace Mistox.Client {
Console.WriteLine("Received a null packet."); Console.WriteLine("Received a null packet.");
break; break;
default: default:
onDataReceived?.Invoke(DataPacket); onDataReceived?.Invoke(remote, DataPacket);
break; break;
} }
} }
// When user is no longer in range of (self) and network rendering stops // When user is no longer in range of (self) and network rendering stops
public event Action<mUDPClient> onUserDisconnected; public event Action<mUDPClient>? onUserDisconnected;
public void InvokeOnUserDisconnected(mUDPClient DisconnectedUser){ public void InvokeOnUserDisconnected(mUDPClient User){
onUserDisconnected?.Invoke(DisconnectedUser); onUserDisconnected?.Invoke(User);
} }
// When any user disconnects from the server that isn't (self) // When any user disconnects from the server that isn't (self)
public event Action<string> onUserLeft; public event Action<mUDPClient>? onUserLeft;
public void InvokeOnUserLeft(string UserName){ public void InvokeOnUserLeft(mUDPClient User){
onUserLeft?.Invoke(UserName); onUserLeft?.Invoke(User);
} }
// When (self) disconnects to the server // When (self) disconnects to the server
public event Action onServerDisconnected; public event Action? onServerDisconnected;
public void InvokeOnServerDisconnected(){ public void InvokeOnServerDisconnected(){
Alive = false; Alive = false;
onServerDisconnected?.Invoke(); onServerDisconnected?.Invoke();
@@ -219,6 +224,7 @@ namespace Mistox.Client {
} }
public async Task SendServer<Packet>(Packet data) { public async Task SendServer<Packet>(Packet data) {
if (ServerConnection == null) return;
await ServerConnection.Send( data ); await ServerConnection.Send( data );
} }
} }
+3 -2
View File
@@ -48,10 +48,11 @@ namespace Mistox.Client {
await ns.ReadExactlyAsync(payloadBuffer, 0, Header.DataLength); await ns.ReadExactlyAsync(payloadBuffer, 0, Header.DataLength);
// Create the dataobject // Create the dataobject
dynamic dataobject = DataPacket.Deserialize( Header, payloadBuffer.AsMemory(DataPacket.HeaderLength, Header.DataLength) ); object? dataobject = DataPacket.Deserialize( Header, payloadBuffer.AsMemory(DataPacket.HeaderLength, Header.DataLength) );
if (dataobject != null) {
// Return the object // Return the object
_parent.InvokeOnReceived(dataobject); _parent.InvokeOnReceived(dataobject);
}
} }
} catch (Exception e) { } catch (Exception e) {
+6 -4
View File
@@ -21,9 +21,9 @@ namespace Mistox.Client {
UdpReceiveResult udpPacket = await _parent.ClientSocket.ReceiveAsync(); UdpReceiveResult udpPacket = await _parent.ClientSocket.ReceiveAsync();
// Try find the user // Try find the user
mUDPClient client = null; mUDPClient? client = null;
bool clientExists = _parent.clientConnections.TryGetValue(udpPacket.RemoteEndPoint, out client); _parent.clientConnections.TryGetValue(udpPacket.RemoteEndPoint, out client);
if (clientExists) { if (client != null) {
// Create the data readers // Create the data readers
ReadOnlyMemory<byte> dataMemory = udpPacket.Buffer.AsMemory(); ReadOnlyMemory<byte> dataMemory = udpPacket.Buffer.AsMemory();
@@ -39,10 +39,12 @@ namespace Mistox.Client {
ReadOnlyMemory<byte> data = dataMemory.Slice(DataPacket.HeaderLength, Header.DataLength); ReadOnlyMemory<byte> data = dataMemory.Slice(DataPacket.HeaderLength, Header.DataLength);
// Extract the object // Extract the object
dynamic buildObj = DataPacket.Deserialize( Header, data ); object? buildObj = DataPacket.Deserialize( Header, data );
if (buildObj != null) {
// Return the object // Return the object
_parent.InvokeOnReceived( buildObj, client ); _parent.InvokeOnReceived( buildObj, client );
}
} }
} catch( Exception e ) { } catch( Exception e ) {
+1 -1
View File
@@ -19,7 +19,7 @@ namespace Mistox {
return new MistoxServer(listenerAddress, serverName); return new MistoxServer(listenerAddress, serverName);
} }
public static MistoxClient newClient( string UserName, string ServerIPOrHostName, int Port, string ConnectionKey = "" ) { public static MistoxClient? newClient( string UserName, string ServerIPOrHostName, int Port, string ConnectionKey = "" ) {
// Build the network packet list // Build the network packet list
NetworkPacketDictionary.BuildNetworkPacketList(); NetworkPacketDictionary.BuildNetworkPacketList();
+1
View File
@@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+3 -3
View File
@@ -72,13 +72,13 @@ namespace Mistox.Server {
// Events // Events
///////////////////////////// /////////////////////////////
public event Action<ServerConnection> onUserConnected; public event Action<ServerConnection>? onUserConnected;
public void InvokeUserConnected( ServerConnection user ){ public void InvokeUserConnected( ServerConnection user ){
Connections[user.UserName] = user; Connections[user.UserName] = user;
onUserConnected?.Invoke( user ); onUserConnected?.Invoke( user );
} }
public event Action<ServerConnection, object> onDataReceived; public event Action<ServerConnection, object>? onDataReceived;
public void InvokeDataReceived( ServerConnection user, dynamic data ){ public void InvokeDataReceived( ServerConnection user, dynamic data ){
switch (data) { switch (data) {
case UserAuthPacket packet when !user.IsAuthenticated: case UserAuthPacket packet when !user.IsAuthenticated:
@@ -101,7 +101,7 @@ namespace Mistox.Server {
} }
} }
public event Action<ServerConnection> onUserDisconnected; public event Action<ServerConnection>? onUserDisconnected;
public void InvokeUserDisconnected( ServerConnection user ){ public void InvokeUserDisconnected( ServerConnection user ){
Connections.Remove(user.UserName, out _); Connections.Remove(user.UserName, out _);
onUserDisconnected?.Invoke( user ); onUserDisconnected?.Invoke( user );
+3 -2
View File
@@ -55,10 +55,11 @@ namespace Mistox.Server {
await ns.ReadExactlyAsync(payloadBuffer, 0, Header.DataLength, _cts.Token); await ns.ReadExactlyAsync(payloadBuffer, 0, Header.DataLength, _cts.Token);
// Create the dataobject // Create the dataobject
dynamic dataobject = DataPacket.Deserialize( Header, payloadBuffer.AsMemory(DataPacket.HeaderLength) ); object? dataobject = DataPacket.Deserialize( Header, payloadBuffer.AsMemory(DataPacket.HeaderLength) );
if (dataobject != null) {
// Return the object // Return the object
_parent.InvokeDataReceived(this, dataobject); _parent.InvokeDataReceived(this, dataobject);
}
} }
} catch (Exception e) { } catch (Exception e) {
+3 -2
View File
@@ -32,8 +32,8 @@ namespace Mistox.Server {
continue; continue;
} }
IPEndPoint remote = client.Client.RemoteEndPoint as IPEndPoint; IPEndPoint? remote = client.Client.RemoteEndPoint as IPEndPoint;
if (remote != null) {
string connectionID = Guid.NewGuid().ToString(); string connectionID = Guid.NewGuid().ToString();
_parent.Connections[connectionID] = new ServerConnection( client, _parent, remote) { _parent.Connections[connectionID] = new ServerConnection( client, _parent, remote) {
UserName = connectionID UserName = connectionID
@@ -41,6 +41,7 @@ namespace Mistox.Server {
Console.WriteLine( "New User Connected" ); Console.WriteLine( "New User Connected" );
} }
}
Dispose(); Dispose();
} }
+2 -2
View File
@@ -46,10 +46,10 @@ namespace Mistox {
return writer.WrittenSpan.ToArray(); return writer.WrittenSpan.ToArray();
} }
public static object Deserialize( DataPacketHeader Header, ReadOnlyMemory<byte> Data ) { public static object? Deserialize( DataPacketHeader Header, ReadOnlyMemory<byte> Data ) {
// Get the type from the tag // Get the type from the tag
if (!NetworkPacketDictionary.PacketLookupTable.TryGetValue(Header.Type, out Type resolvedType)) { if (!NetworkPacketDictionary.PacketLookupTable.TryGetValue(Header.Type, out Type? resolvedType)) {
throw new Exception($"Unknown packet type ID: {Header.Type}"); throw new Exception($"Unknown packet type ID: {Header.Type}");
} }
@@ -27,7 +27,7 @@ namespace Mistox.Attributes {
foreach (Assembly assembly in assemblies) { foreach (Assembly assembly in assemblies) {
Type[] allClasses = assembly.GetTypes(); Type[] allClasses = assembly.GetTypes();
foreach( Type cur in allClasses) { foreach( Type cur in allClasses) {
mPacket attribute = cur.GetCustomAttribute<mPacket>(false); mPacket? attribute = cur.GetCustomAttribute<mPacket>(false);
if (attribute != null) { if (attribute != null) {
// Generate a deterministic 16-bit ID based on the Type's FullName // Generate a deterministic 16-bit ID based on the Type's FullName
+8 -8
View File
@@ -25,31 +25,31 @@ namespace Mistox.ResevedPackets {
// Tag to make sure the object on the other side is the same object i want to update here // Tag to make sure the object on the other side is the same object i want to update here
// Need to map these Object ID's to a dictionary to make sure I only send network updates when there is a change // Need to map these Object ID's to a dictionary to make sure I only send network updates when there is a change
public ushort ObjectId { get; set; } public ushort ObjectId { get; set; } = 0;
// Packet payload data that im matching for in the dictionary // Packet payload data that im matching for in the dictionary
public byte[] InnerPayload { get; set; } public byte[] InnerPayload { get; set; } = {};
} }
public class UserTableObject { public class UserTableObject {
public string UserName { get; set; } public string UserName { get; set; } = string.Empty;
public IPAddress IpAddress { get; set; } public IPAddress IpAddress { get; set; } = new([]);
public Vector3 Position { get; set; } public Vector3 Position { get; set; } = new();
} }
// Not implemented // Not implemented
// Packet sent to all clients when a global position update occurrs // Packet sent to all clients when a global position update occurrs
[mPacket] [mPacket]
public class HeartbeatPacket { public class HeartbeatPacket {
public Vector3 Position { get; set; } public Vector3 Position { get; set; } = new();
public List<UserTableObject> ServerList { get; set; } public List<UserTableObject> ServerList { get; set; } = new();
} }
// Not implimented // Not implimented
// Packet sent to all clients when a users TCP stream disconnects from the server // Packet sent to all clients when a users TCP stream disconnects from the server
[mPacket] [mPacket]
public class UserDisconnectedPacket { public class UserDisconnectedPacket {
public string UserName { get; set; } public string UserName { get; set; } = string.Empty;
} }
} }
+1
View File
@@ -10,6 +10,7 @@ The server receive thread needs to push the user it was received from up the cha
8. Each client can figure out what connections it needs to make or drop via the list Done 8. Each client can figure out what connections it needs to make or drop via the list Done
10. When the TCP connection drops update the heartbeat list so all the clients remove the connection Done 10. When the TCP connection drops update the heartbeat list so all the clients remove the connection Done
11. Build a network cache table on the clients so obj's only send updates and not repeat packets 11. Build a network cache table on the clients so obj's only send updates and not repeat packets
12. User joined and left events are not implimented
Set the network bubble distance [ render distance ] on the server and pass to the clients Set the network bubble distance [ render distance ] on the server and pass to the clients