diff --git a/Basis Server/BasisNetworkClient/BasisNetworkClient.cs b/Basis Server/BasisNetworkClient/BasisNetworkClient.cs
index 753b9203a..f0a21dd27 100644
--- a/Basis Server/BasisNetworkClient/BasisNetworkClient.cs
+++ b/Basis Server/BasisNetworkClient/BasisNetworkClient.cs
@@ -1,4 +1,5 @@
using Basis.Network.Core;
+using BasisNetworkCore;
using LiteNetLib;
using LiteNetLib.Utils;
using static Basis.Network.Core.Serializable.SerializableBasis;
@@ -37,12 +38,13 @@ public static NetPeer StartClient(string IP, int port, ReadyMessage ReadyMessage
UnsyncedEvents = true,
};
client.Start();
- NetDataWriter Writer = new NetDataWriter();
+ NetDataWriter Writer = NetDataWriterPool.GetWriter();
//this is the only time we dont put key!
Writer.Put(BasisNetworkVersion.ServerVersion);
AuthenticationMessage.Serialize(Writer);
ReadyMessage.Serialize(Writer);
peer = client.Connect(IP, port, Writer);
+ NetDataWriterPool.ReturnWriter(Writer);
return peer;
}
else
diff --git a/Basis Server/BasisNetworkClientConsole/BasisNetworkClientConsole/Program.cs b/Basis Server/BasisNetworkClientConsole/BasisNetworkClientConsole/Program.cs
index f526b6ffa..e87e547ec 100644
--- a/Basis Server/BasisNetworkClientConsole/BasisNetworkClientConsole/Program.cs
+++ b/Basis Server/BasisNetworkClientConsole/BasisNetworkClientConsole/Program.cs
@@ -1,21 +1,28 @@
+using Basis.Network.Core;
+using Basis.Scripts.Networking.Compression;
+using BasisNetworkCore;
+using LiteNetLib;
+using LiteNetLib.Utils;
+using System.Reflection.PortableExecutable;
+using System.Runtime.Intrinsics.X86;
using System.Text;
using static Basis.Network.Core.Serializable.SerializableBasis;
+using static BasisNetworkPrimitiveCompression;
using static SerializableBasis;
namespace Basis
{
class Program
{
+ public static string Password = "default_password";
+ private static readonly object nameLock = new object(); // To synchronize name generation
+ public static NetPeer LocalPLayer;
public static void Main(string[] args)
{
// Set up global exception handlers
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
- // Get the path to the config.xml file in the application's directory
- string configFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.xml");
-
-
// Create a cancellation token source
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
@@ -25,32 +32,37 @@ public static void Main(string[] args)
{
try
{
+ // Generate random UUID and player name
+ string randomUUID = GenerateFakeUUID();
+ string randomPlayerName = GenerateRandomPlayerName();
+
ReadyMessage RM = new ReadyMessage
{
playerMetaDataMessage = new PlayerMetaDataMessage()
};
- RM.playerMetaDataMessage.playerDisplayName = "Fake User";
- RM.playerMetaDataMessage.playerUUID = "UUID Test";
+ RM.playerMetaDataMessage.playerDisplayName = randomPlayerName;
+ RM.playerMetaDataMessage.playerUUID = randomUUID;
RM.clientAvatarChangeMessage = new ClientAvatarChangeMessage
{
- byteArray = new byte[13]
+ byteArray = new byte[0],
+ loadMode = 2
};
RM.localAvatarSyncMessage = new LocalAvatarSyncMessage
{
- array = new byte[LocalAvatarSyncMessage.AvatarSyncSize]
+ array = AvatarMessage,
};
AuthenticationMessage Authmessage = new AuthenticationMessage
{
- bytes = Encoding.UTF8.GetBytes("default_password")
+ bytes = Encoding.UTF8.GetBytes(Password)
};
BasisNetworkClient.AuthenticationMessage = Authmessage;
- BasisNetworkClient.StartClient("localhost", 4296, RM,true);
- BNL.Log("Connecting!");
+ LocalPLayer = BasisNetworkClient.StartClient("localhost", 4296, RM, true);
+ BasisNetworkClient.listener.NetworkReceiveEvent += NetworkReceiveEvent;
+ BNL.Log($"Connecting! Player Name: {randomPlayerName}, UUID: {randomUUID}");
}
catch (Exception ex)
{
BNL.LogError($"Server encountered an error: {ex.Message} {ex.StackTrace}");
- // Optionally, handle server restart or log critical errors
}
}, cancellationToken);
@@ -72,14 +84,104 @@ public static void Main(string[] args)
}
BNL.Log("Server shut down successfully.");
};
-
+ ushort[] UshortArray = new ushort[LocalAvatarSyncMessage.StoredBones];
// Keep the application running
while (true)
{
- Thread.Sleep(15000);
+ // SendMovement();
+ Thread.Sleep(33);
+ }
+ }
+
+ private static void NetworkReceiveEvent(NetPeer peer, NetPacketReader Reader, byte channel, DeliveryMethod deliveryMethod)
+ {
+
+ //loop back index 0 (0 being real player)
+ if (peer.Id == 0)
+ {
+ if (BasisNetworkCommons.MovementChannel == channel)
+ {
+ ServerSideSyncPlayerMessage SSM = new ServerSideSyncPlayerMessage();
+ SSM.Deserialize(Reader);
+ NetDataWriter Writer = NetDataWriterPool.GetWriter();
+ SSM.avatarSerialization.Serialize(Writer);
+ LocalPLayer.Send(Writer, BasisNetworkCommons.MovementChannel, deliveryMethod);
+ NetDataWriterPool.ReturnWriter(Writer);
+ }
+ else
+ {
+ if(BasisNetworkCommons.FallChannel == channel)
+ {
+ if (deliveryMethod == DeliveryMethod.Unreliable)
+ {
+ if (Reader.TryGetByte(out byte Byte))
+ {
+ NetworkReceiveEvent(peer, Reader, Byte, deliveryMethod);
+ }
+ else
+ {
+ BNL.LogError($"Unknown channel no data remains: {channel} " + Reader.AvailableBytes);
+ Reader.Recycle();
+ }
+ }
+ else
+ {
+ BNL.LogError($"Unknown channel: {channel} " + Reader.AvailableBytes);
+ Reader.Recycle();
+ }
+ }
+ }
+
+ }
+ }
+
+ public static byte[] AvatarMessage = new byte[LocalAvatarSyncMessage.AvatarSyncSize];
+ public static Vector3 Position = new Vector3(0,0,0);
+ public static Quaternion Rotation = new Quaternion(0,0,0,1);
+ public static float[] FloatArray = new float[LocalAvatarSyncMessage.StoredBones];
+ public static ushort[] UshortArray = new ushort[LocalAvatarSyncMessage.StoredBones];
+ public static void SendMovement()
+ {
+ if (LocalPLayer != null)
+ {
+ int Offset = 0;
+ WriteVectorFloatToBytes(Position, ref AvatarMessage, ref Offset);
+ WriteQuaternionToBytes(Rotation, ref AvatarMessage, ref Offset, RotationCompression);
+ WriteUShortsToBytes(UshortArray, ref AvatarMessage, ref Offset);
+ LocalPLayer.Send(AvatarMessage, BasisNetworkCommons.MovementChannel, DeliveryMethod.Sequenced);
}
}
+ public static ushort Compress(float value, float MinValue, float MaxValue, float valueDiffence)
+ {
+ // Clamp the value to ensure it's within the specified range
+ value = Math.Clamp(value, MinValue, MaxValue);
+
+ // Map the float value to the ushort range
+ float normalized = (value - MinValue) / (valueDiffence); // 0..1
+ return (ushort)(normalized * ushortRangeDifference);//+ UShortMin (its always zero)
+ }
+ private const ushort UShortMin = ushort.MinValue; // 0
+ private const ushort UShortMax = ushort.MaxValue; // 65535
+ private const ushort ushortRangeDifference = UShortMax - UShortMin;
+ public static BasisRangedUshortFloatData RotationCompression = new BasisRangedUshortFloatData(-1f, 1f, 0.001f);
+ public static void WriteUShortsToBytes(ushort[] values, ref byte[] bytes, ref int offset)
+ {
+ EnsureSize(ref bytes, offset + LengthUshortBytes);
+ // Manually copy ushort values as bytes
+ for (int index = 0; index < LocalAvatarSyncMessage.StoredBones; index++)
+ {
+ WriteUShortToBytes(values[index], ref bytes, ref offset);
+ }
+ }
+ // Manual ushort to bytes conversion (without BitConverter)
+ private unsafe static void WriteUShortToBytes(ushort value, ref byte[] bytes, ref int offset)
+ {
+ // Manually write the bytes
+ bytes[offset] = (byte)(value & 0xFF);
+ bytes[offset + 1] = (byte)((value >> 8) & 0xFF);
+ offset += 2;
+ }
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
@@ -103,5 +205,138 @@ private static void TaskScheduler_UnobservedTaskException(object sender, Unobser
}
e.SetObserved(); // Prevents the application from crashing
}
+
+ private static string GenerateFakeUUID()
+ {
+ // Generate a fake UUID-like string
+ Guid guid = Guid.NewGuid();
+ return guid.ToString();
+ }
+
+ private static string GenerateRandomPlayerName()
+ {
+ // Thread-safe unique player name generation
+ lock (nameLock)
+ {
+ string[] adjectives = { "Swift", "Brave", "Clever", "Fierce", "Nimble", "Silent", "Bold", "Lucky", "Strong", "Mighty", "Sneaky", "Fearless", "Wise", "Vicious", "Daring" };
+ string[] nouns = { "Warrior", "Hunter", "Mage", "Rogue", "Paladin", "Shaman", "Knight", "Archer", "Monk", "Druid", "Assassin", "Sorcerer", "Ranger", "Guardian", "Berserker" };
+ string[] titles = { "the Swift", "the Bold", "the Silent", "the Brave", "the Fierce", "the Wise", "the Protector", "the Shadow", "the Flame", "the Phantom" };
+
+ // Colors with their corresponding names and hex codes for Unity's Rich Text
+ (string Name, string Hex)[] colors =
+ {
+ ("Red", "#FF0000"),
+ ("Blue", "#0000FF"),
+ ("Green", "#008000"),
+ ("Yellow", "#FFFF00"),
+ ("Black", "#000000"),
+ ("White", "#FFFFFF"),
+ ("Silver", "#C0C0C0"),
+ ("Golden", "#FFD700"),
+ ("Crimson", "#DC143C"),
+ ("Azure", "#007FFF"),
+ ("Emerald", "#50C878"),
+ ("Amber", "#FFBF00")
+ };
+
+ string[] animals = { "Wolf", "Tiger", "Eagle", "Dragon", "Lion", "Bear", "Hawk", "Panther", "Raven", "Serpent", "Fox", "Falcon" };
+
+ Random random = new Random();
+
+ // Randomly select one element from each array
+ string adjective = adjectives[random.Next(adjectives.Length)];
+ string noun = nouns[random.Next(nouns.Length)];
+ string title = titles[random.Next(titles.Length)];
+ var color = colors[random.Next(colors.Length)];
+ string animal = animals[random.Next(animals.Length)];
+
+ // Combine elements with rich text for the color
+ string colorText = $"{color.Name}";
+ string generatedName = $"{adjective}{noun} {title} of the {colorText} {animal}";
+
+ // Ensure uniqueness by appending a counter
+ return $"{generatedName}";
+ }
+ }
+ public static void WriteVectorFloatToBytes(Vector3 values, ref byte[] bytes, ref int offset)
+ {
+ EnsureSize(ref bytes, offset + 12);
+ WriteFloatToBytes(values.x, ref bytes, ref offset);//4
+ WriteFloatToBytes(values.y, ref bytes, ref offset);//8
+ WriteFloatToBytes(values.z, ref bytes, ref offset);//12
+ }
+
+ private unsafe static void WriteFloatToBytes(float value, ref byte[] bytes, ref int offset)
+ {
+ // Convert the float to a uint using its bitwise representation
+ uint intValue = *((uint*)&value);
+
+ // Manually write the bytes
+ bytes[offset] = (byte)(intValue & 0xFF);
+ bytes[offset + 1] = (byte)((intValue >> 8) & 0xFF);
+ bytes[offset + 2] = (byte)((intValue >> 16) & 0xFF);
+ bytes[offset + 3] = (byte)((intValue >> 24) & 0xFF);
+ offset += 4;
+ }
+ public static int LengthUshortBytes = LocalAvatarSyncMessage.StoredBones * 2; // Initialize LengthBytes first
+ // Object pool for byte arrays to avoid allocation during runtime
+ private static readonly ObjectPool byteArrayPool = new ObjectPool(() => new byte[LengthUshortBytes]);
+ // Ensure the byte array is large enough to hold the data
+ private static void EnsureSize(ref byte[] bytes, int requiredSize)
+ {
+ if (bytes == null || bytes.Length < requiredSize)
+ {
+ // Reuse pooled byte arrays
+ bytes = byteArrayPool.Get();
+ Array.Resize(ref bytes, requiredSize);
+ }
+ }
+
+ // Ensure the byte array is large enough for reading
+ private static void EnsureSize(byte[] bytes, int requiredSize)
+ {
+ if (bytes.Length < requiredSize)
+ {
+ throw new ArgumentException("Byte array is too small for the required size. Current Size is " + bytes.Length + " But Required " + requiredSize);
+ }
+ }
+ // Manual conversion of quaternion to bytes (without BitConverter)
+ public static void WriteQuaternionToBytes(Quaternion rotation, ref byte[] bytes, ref int offset, BasisRangedUshortFloatData compressor)
+ {
+ EnsureSize(ref bytes, offset + 14);
+ ushort compressedW = compressor.Compress(rotation.value.w);
+
+ // Write the quaternion's components
+ WriteFloatToBytes(rotation.value.x, ref bytes, ref offset);
+ WriteFloatToBytes(rotation.value.y, ref bytes, ref offset);
+ WriteFloatToBytes(rotation.value.z, ref bytes, ref offset);
+
+ // Write the compressed 'w' component
+ bytes[offset] = (byte)(compressedW & 0xFF); // Low byte
+ bytes[offset + 1] = (byte)((compressedW >> 8) & 0xFF); // High byte
+ offset += 2;
+ }
+ // Object pool for byte arrays to avoid allocation during runtime
+ private class ObjectPool
+ {
+ private readonly Func createFunc;
+ private readonly Stack pool;
+
+ public ObjectPool(Func createFunc)
+ {
+ this.createFunc = createFunc;
+ this.pool = new Stack();
+ }
+
+ public T Get()
+ {
+ return pool.Count > 0 ? pool.Pop() : createFunc();
+ }
+
+ public void Return(T item)
+ {
+ pool.Push(item);
+ }
+ }
}
}
diff --git a/Basis Server/BasisNetworkCore/BasisPlayerArray.cs b/Basis Server/BasisNetworkCore/BasisPlayerArray.cs
new file mode 100644
index 000000000..a553529b2
--- /dev/null
+++ b/Basis Server/BasisNetworkCore/BasisPlayerArray.cs
@@ -0,0 +1,54 @@
+using LiteNetLib;
+using System;
+
+namespace BasisNetworkCore
+{
+ public class BasisPlayerArray
+ {
+ private static readonly object PlayerArrayLock = new object();
+ private static NetPeer[] PlayerArray = new NetPeer[4096];
+ private static int PlayerCount = 0;
+
+ // Reusable snapshot buffer
+ private static NetPeer[] SnapshotBuffer = new NetPeer[4096];
+
+ public static void AddPlayer(NetPeer player)
+ {
+ lock (PlayerArrayLock)
+ {
+ if (PlayerCount < PlayerArray.Length)
+ {
+ PlayerArray[PlayerCount++] = player;
+ }
+ }
+ }
+
+ public static void RemovePlayer(NetPeer player)
+ {
+ lock (PlayerArrayLock)
+ {
+ for (int i = 0; i < PlayerCount; i++)
+ {
+ if (PlayerArray[i] == player)
+ {
+ PlayerArray[i] = PlayerArray[--PlayerCount]; // Swap with last element
+ PlayerArray[PlayerCount] = null; // Clear the last slot
+ break;
+ }
+ }
+ }
+ }
+
+ public static ReadOnlySpan GetSnapshot()
+ {
+ lock (PlayerArrayLock)
+ {
+ // Copy current players into the reusable snapshot buffer
+ Array.Copy(PlayerArray, 0, SnapshotBuffer, 0, PlayerCount);
+
+ // Return a span of the active players
+ return new ReadOnlySpan(SnapshotBuffer, 0, PlayerCount);
+ }
+ }
+ }
+}
diff --git a/Basis Server/BasisNetworkCore/NetDataWriterPool.cs b/Basis Server/BasisNetworkCore/NetDataWriterPool.cs
new file mode 100644
index 000000000..3df2eab6d
--- /dev/null
+++ b/Basis Server/BasisNetworkCore/NetDataWriterPool.cs
@@ -0,0 +1,108 @@
+using LiteNetLib.Utils;
+using System.Collections.Concurrent;
+
+namespace BasisNetworkCore
+{
+ public static class NetDataWriterPool
+ {
+ private static readonly ConcurrentBag _pool = new ConcurrentBag();
+ private static readonly ConcurrentDictionary> _sizeBuckets = new ConcurrentDictionary>();
+ private static readonly int _maxPoolSize = 1000; // Max size of the pool, adjust as needed
+ private static readonly int _maxBucketSize = 100; // Max size for size-specific buckets, adjust as needed
+
+ ///
+ /// Retrieves a NetDataWriter from the pool or creates a new one if none are available.
+ /// Optionally checks for a specific size bucket.
+ ///
+ /// The desired size (capacity) of the NetDataWriter buffer.
+ /// A NetDataWriter instance.
+ public static NetDataWriter GetWriter(int desiredSize = 0)
+ {
+ // Try to find a writer with the desired size
+ if (desiredSize > 0 && _sizeBuckets.TryGetValue(desiredSize, out var bucket) && bucket.TryTake(out var writer))
+ {
+ writer.Reset(); // Ensure the writer is cleared before reuse.
+ return writer;
+ }
+
+ // If no match is found, get a regular writer
+ if (_pool.TryTake(out var writerFromPool))
+ {
+ writerFromPool.Reset(); // Clear any data from previous use
+ return writerFromPool;
+ }
+
+ return new NetDataWriter(true, desiredSize); // Create a new one if the pool is empty
+ }
+
+ ///
+ /// Returns a NetDataWriter to the pool for future reuse.
+ ///
+ /// The NetDataWriter to return.
+ public static void ReturnWriter(NetDataWriter writer)
+ {
+ writer.Reset(); // Clear data for the next use.
+ int size = writer.Capacity; // Track the capacity of the writer
+
+ // Only add the writer if the pool hasn't exceeded the maximum size
+ if (_pool.Count < _maxPoolSize)
+ {
+ _pool.Add(writer);
+
+ // Optionally, place in a specific size bucket if it hasn't exceeded the max size
+ if (size > 0 && _sizeBuckets.TryGetValue(size, out var sizeBucket) && sizeBucket.Count < _maxBucketSize)
+ {
+ sizeBucket.Add(writer);
+ }
+ else if (size > 0)
+ {
+ // Create a new bucket if none exists
+ var newBucket = new ConcurrentBag();
+ newBucket.Add(writer);
+ _sizeBuckets[size] = newBucket;
+ }
+ }
+ else
+ {
+ // If the pool is too large, consider discarding the writer or disposing of it
+ //dispose baby! writer.Dispose(); // Uncomment if disposable
+ BNL.LogError("Exceeding Pool Count!");
+ }
+ }
+
+ ///
+ /// Checks if the pool has any writers with the desired size.
+ ///
+ /// The size to check for.
+ /// True if a writer of the desired size exists in the pool; otherwise, false.
+ public static bool HasWriterOfSize(int desiredSize)
+ {
+ return _sizeBuckets.ContainsKey(desiredSize) && _sizeBuckets[desiredSize].Count > 0;
+ }
+
+ ///
+ /// Periodically cleans up the pool to prevent unbounded growth.
+ ///
+ public static void CleanUp()
+ {
+ // Clean up the pool if it exceeds a maximum size or remove unused writers
+ if (_pool.Count > _maxPoolSize)
+ {
+ // For example, remove items in excess
+ while (_pool.Count > _maxPoolSize)
+ {
+ _pool.TryTake(out _);
+ }
+ }
+
+ // Clean up size buckets if they are too large
+ foreach (var bucket in _sizeBuckets.Values)
+ {
+ while (bucket.Count > _maxBucketSize)
+ {
+ bucket.TryTake(out _);
+ }
+ }
+ }
+ }
+}
diff --git a/Basis Server/BasisNetworkServer/BasisNetworkServer.cs b/Basis Server/BasisNetworkServer/BasisNetworkServer.cs
index 66d46a169..0527839a6 100644
--- a/Basis Server/BasisNetworkServer/BasisNetworkServer.cs
+++ b/Basis Server/BasisNetworkServer/BasisNetworkServer.cs
@@ -3,6 +3,7 @@
using Basis.Network.Server.Auth;
using Basis.Network.Server.Generic;
using Basis.Network.Server.Ownership;
+using BasisNetworkCore;
using LiteNetLib;
using LiteNetLib.Utils;
using System;
@@ -11,7 +12,6 @@
using System.Linq;
using System.Net;
using System.Net.Sockets;
-using System.Threading;
using System.Threading.Tasks;
using static Basis.Network.Core.Serializable.SerializableBasis;
using static Basis.Network.Server.Generic.BasisSavedState;
@@ -60,6 +60,7 @@ private static void SetupServer(Configuration configuration)
DisconnectTimeout = configuration.DisconnectTimeout,
PacketPoolSize = 2000,
UnsyncedEvents = true,
+
};
StartListening(configuration);
@@ -184,6 +185,7 @@ private static void HandleConnectionRequest(ConnectionRequest request)
NetPeer newPeer = request.Accept();
if (Peers.TryAdd((ushort)newPeer.Id, newPeer))
{
+ BasisPlayerArray.AddPlayer(newPeer);
BNL.Log($"Peer connected: {newPeer.Id}");
ReadyMessage readyMessage = new ReadyMessage();
readyMessage.Deserialize(request.Data);
@@ -215,6 +217,7 @@ private static void HandlePeerDisconnected(NetPeer peer, DisconnectInfo info)
ushort id = (ushort)peer.Id;
ClientDisconnect(id, Peers);
+ BasisPlayerArray.RemovePlayer(peer);
if (Peers.TryRemove(id, out _))
{
BNL.Log($"Peer removed: {id}");
@@ -278,7 +281,7 @@ private static void HandleNetworkReceiveEvent(NetPeer peer, NetPacketReader read
reader.Recycle();
break;
case BasisNetworkCommons.SceneChannel:
- BasisNetworkingGeneric.HandleScene(reader, deliveryMethod, peer, Peers);
+ BasisNetworkingGeneric.HandleScene(reader, deliveryMethod, peer);
reader.Recycle();
break;
case BasisNetworkCommons.AvatarChangeMessage:
@@ -286,11 +289,11 @@ private static void HandleNetworkReceiveEvent(NetPeer peer, NetPacketReader read
reader.Recycle();
break;
case BasisNetworkCommons.OwnershipTransfer:
- BasisNetworkOwnership.OwnershipTransfer(reader, peer, Peers);
+ BasisNetworkOwnership.OwnershipTransfer(reader, peer);
reader.Recycle();
break;
case BasisNetworkCommons.OwnershipResponse:
- BasisNetworkOwnership.OwnershipResponse(reader, peer, Peers);
+ BasisNetworkOwnership.OwnershipResponse(reader, peer);
reader.Recycle();
break;
case BasisNetworkCommons.AudioRecipients:
@@ -318,22 +321,26 @@ private static void HandleNetworkReceiveEvent(NetPeer peer, NetPacketReader read
#region Utility Methods
private static void RejectWithReason(ConnectionRequest request, string reason)
{
- NetDataWriter writer = new NetDataWriter();
+ NetDataWriter writer = NetDataWriterPool.GetWriter();
writer.Put(reason);
request.Reject(writer);
BNL.LogError($"Rejected: {reason}");
+ NetDataWriterPool.ReturnWriter(writer);
}
public static void ClientDisconnect(ushort leaving, ConcurrentDictionary authenticatedClients)
{
- NetDataWriter writer = new NetDataWriter();
+ NetDataWriter writer = NetDataWriterPool.GetWriter(sizeof(ushort));
writer.Put(leaving);
foreach (var client in authenticatedClients.Values)
{
if (client.Id != leaving)
+ {
client.Send(writer, BasisNetworkCommons.Disconnection, DeliveryMethod.ReliableOrdered);
+ }
}
+ NetDataWriterPool.ReturnWriter(writer);
}
#endregion
private static void SendAvatarMessageToClients(NetPacketReader Reader, NetPeer Peer)
@@ -349,9 +356,10 @@ private static void SendAvatarMessageToClients(NetPacketReader Reader, NetPeer P
}
};
BasisSavedState.AddLastData(Peer, ClientAvatarChangeMessage);
- NetDataWriter Writer = new NetDataWriter();
+ NetDataWriter Writer = NetDataWriterPool.GetWriter();
serverAvatarChangeMessage.Serialize(Writer);
- BroadcastMessageToClients(Writer, BasisNetworkCommons.AvatarChangeMessage, Peer, Peers);
+ BroadcastMessageToClients(Writer, BasisNetworkCommons.AvatarChangeMessage, Peer,BasisPlayerArray.GetSnapshot());
+ NetDataWriterPool.ReturnWriter(Writer);
}
private static void UpdateVoiceReceivers(NetPacketReader Reader, NetPeer Peer)
{
@@ -404,38 +412,41 @@ private static void SendVoiceMessageToClients(ServerAudioSegmentMessage audioSeg
{
playerID = (ushort)sender.Id
};
- NetDataWriter NetDataWriter = new NetDataWriter();
+ NetDataWriter NetDataWriter = NetDataWriterPool.GetWriter();
audioSegment.Serialize(NetDataWriter);
// BNL.Log("Sending Voice Data To Clients");
- BroadcastMessageToClients(NetDataWriter, channel, endPoints, DeliveryMethod.Sequenced);
+ BroadcastMessageToClients(NetDataWriter, channel,ref endPoints, DeliveryMethod.Sequenced);
+ NetDataWriterPool.ReturnWriter(NetDataWriter);
}
else
{
BNL.Log("Error unable to find " + sender.Id + " in the data store!");
}
}
- public static void BroadcastMessageToClients(NetDataWriter Reader, byte channel, NetPeer sender, ConcurrentDictionary authenticatedClients, DeliveryMethod deliveryMethod = DeliveryMethod.Sequenced)
+ public static void BroadcastMessageToClients(NetDataWriter Reader, byte channel, NetPeer sender, ReadOnlySpan authenticatedClients, DeliveryMethod deliveryMethod = DeliveryMethod.Sequenced)
{
- IEnumerable> clientsExceptSender = authenticatedClients.Where(client => client.Value.Id != sender.Id);
-
- foreach (KeyValuePair client in clientsExceptSender)
+ foreach (NetPeer client in authenticatedClients)
{
- client.Value.Send(Reader, channel, deliveryMethod);
+ if (client.Id != sender.Id)
+ {
+ client.Send(Reader, channel, deliveryMethod);
+ }
}
}
- public static void BroadcastMessageToClients(NetDataWriter Reader, byte channel, List authenticatedClients, DeliveryMethod deliveryMethod = DeliveryMethod.Sequenced)
+ public static void BroadcastMessageToClients(NetDataWriter Reader, byte channel, ReadOnlySpan authenticatedClients, DeliveryMethod deliveryMethod = DeliveryMethod.Sequenced)
{
- int count = authenticatedClients.Count;
+ int count = authenticatedClients.Length;
for (int index = 0; index < count; index++)
{
authenticatedClients[index].Send(Reader, channel, deliveryMethod);
}
}
- public static void BroadcastMessageToClients(NetDataWriter Reader, byte channel, ConcurrentDictionary authenticatedClients, DeliveryMethod deliveryMethod = DeliveryMethod.Sequenced)
+ public static void BroadcastMessageToClients(NetDataWriter Reader, byte channel,ref List authenticatedClients, DeliveryMethod deliveryMethod = DeliveryMethod.Sequenced)
{
- foreach (KeyValuePair client in authenticatedClients)
+ int count = authenticatedClients.Count;
+ for (int index = 0; index < count; index++)
{
- client.Value.Send(Reader, channel, deliveryMethod);
+ authenticatedClients[index].Send(Reader, channel, deliveryMethod);
}
}
private static void HandleAvatarMovement(NetPacketReader Reader, NetPeer Peer)
@@ -443,7 +454,8 @@ private static void HandleAvatarMovement(NetPacketReader Reader, NetPeer Peer)
LocalAvatarSyncMessage LocalAvatarSyncMessage = new LocalAvatarSyncMessage();
LocalAvatarSyncMessage.Deserialize(Reader);
BasisSavedState.AddLastData(Peer, LocalAvatarSyncMessage);
- foreach (NetPeer client in Peers.Values)
+ ReadOnlySpan Peers = BasisPlayerArray.GetSnapshot();
+ foreach (NetPeer client in Peers)
{
if (client.Id == Peer.Id)
{
@@ -480,17 +492,20 @@ public static ServerReadyMessage LoadInitialState(NetPeer authClient, ReadyMessa
}
private static void NotifyExistingClients(ServerReadyMessage serverSideSyncPlayerMessage, NetPeer authClient)
{
- NetDataWriter Writer = new NetDataWriter();
+ NetDataWriter Writer = NetDataWriterPool.GetWriter();
serverSideSyncPlayerMessage.Serialize(Writer);
- IEnumerable clientsToNotify = Peers.Values.Where(client => client != authClient);
-
- string ClientIds = string.Empty;
- foreach (NetPeer client in clientsToNotify)
+ ReadOnlySpan Peers = BasisPlayerArray.GetSnapshot();
+ // string ClientIds = string.Empty;
+ foreach (NetPeer client in Peers)
{
- ClientIds += $" | {client.Id}";
- client.Send(Writer, BasisNetworkCommons.CreateRemotePlayer, DeliveryMethod.ReliableOrdered);
+ if (client != authClient)
+ {
+ // ClientIds += $" | {client.Id}";
+ client.Send(Writer, BasisNetworkCommons.CreateRemotePlayer, DeliveryMethod.ReliableOrdered);
+ }
}
- BNL.Log($"Sent Remote Spawn request to {ClientIds}");
+ NetDataWriterPool.ReturnWriter(Writer);
+ // BNL.Log($"Sent Remote Spawn request to {ClientIds}");
}
private static void SendClientListToNewClient(NetPeer authClient)
{
@@ -537,9 +552,10 @@ private static void SendClientListToNewClient(NetPeer authClient)
{
serverSidePlayer = copied.ToArray(),
};
- NetDataWriter Writer = new NetDataWriter();
+ NetDataWriter Writer = NetDataWriterPool.GetWriter();
remoteMessages.Serialize(Writer);
BNL.Log($"Sending list of clients to {authClient.Id}");
authClient.Send(Writer, BasisNetworkCommons.CreateRemotePlayers, DeliveryMethod.ReliableOrdered);
+ NetDataWriterPool.ReturnWriter(Writer);
}
}
diff --git a/Basis Server/BasisNetworkServer/BasisNetworking/BasisNetworkOwnership.cs b/Basis Server/BasisNetworkServer/BasisNetworking/BasisNetworkOwnership.cs
index 927e03daa..9787b43e0 100644
--- a/Basis Server/BasisNetworkServer/BasisNetworking/BasisNetworkOwnership.cs
+++ b/Basis Server/BasisNetworkServer/BasisNetworking/BasisNetworkOwnership.cs
@@ -1,4 +1,5 @@
using Basis.Network.Core;
+using BasisNetworkCore;
using DarkRift.Basis_Common.Serializable;
using LiteNetLib;
using LiteNetLib.Utils;
@@ -14,7 +15,7 @@ public static class BasisNetworkOwnership
public static ConcurrentDictionary ownershipByObjectId = new ConcurrentDictionary();
public static readonly object LockObject = new object(); // For synchronized multi-step operations
- public static void OwnershipResponse(NetPacketReader Reader, NetPeer Peer, ConcurrentDictionary allClients)
+ public static void OwnershipResponse(NetPacketReader Reader, NetPeer Peer)
{
try
{
@@ -24,11 +25,12 @@ public static void OwnershipResponse(NetPacketReader Reader, NetPeer Peer, Concu
//the goal here is to make it so ownership understanding has to be requested.
//once a ownership has been requested there good for life or when a ownership switch happens.
NetworkRequestNewOrExisting(ownershipTransferMessage, out ushort currentOwner);
- NetDataWriter Writer = new NetDataWriter();
+ NetDataWriter Writer = NetDataWriterPool.GetWriter();
ownershipTransferMessage.playerIdMessage.playerID = currentOwner;
ownershipTransferMessage.Serialize(Writer);
BNL.Log("OwnershipResponse " + currentOwner + " for " + ownershipTransferMessage.playerIdMessage);
Peer.Send(Writer, BasisNetworkCommons.OwnershipResponse, DeliveryMethod.ReliableSequenced);
+ NetDataWriterPool.ReturnWriter(Writer);
}
catch (Exception ex)
{
@@ -38,14 +40,14 @@ public static void OwnershipResponse(NetPacketReader Reader, NetPeer Peer, Concu
///
/// Handles the ownership transfer for all clients with proper error handling.
///
- public static void OwnershipTransfer(NetPacketReader Reader, NetPeer Peer, ConcurrentDictionary allClients)
+ public static void OwnershipTransfer(NetPacketReader Reader, NetPeer Peer)
{
try
{
OwnershipTransferMessage ownershipTransferMessage = new OwnershipTransferMessage();
ownershipTransferMessage.Deserialize(Reader);
ushort ClientId = (ushort)Peer.Id;
- NetDataWriter Writer = new NetDataWriter();
+ NetDataWriter Writer = NetDataWriterPool.GetWriter();
//all clients need to know about a ownership switch
if (SwitchOwnership(ownershipTransferMessage.ownershipID, ClientId))
{
@@ -53,7 +55,7 @@ public static void OwnershipTransfer(NetPacketReader Reader, NetPeer Peer, Concu
ownershipTransferMessage.Serialize(Writer);
BNL.Log("OwnershipResponse " + ownershipTransferMessage.ownershipID + " for " + ownershipTransferMessage.playerIdMessage);
- BasisNetworkServer.BroadcastMessageToClients(Writer, BasisNetworkCommons.OwnershipTransfer, allClients, DeliveryMethod.ReliableSequenced);
+ BasisNetworkServer.BroadcastMessageToClients(Writer, BasisNetworkCommons.OwnershipTransfer,BasisPlayerArray.GetSnapshot(), DeliveryMethod.ReliableSequenced);
}
else
{
@@ -64,6 +66,7 @@ public static void OwnershipTransfer(NetPacketReader Reader, NetPeer Peer, Concu
ownershipTransferMessage.Serialize(Writer);
Peer.Send(Writer, BasisNetworkCommons.OwnershipTransfer, DeliveryMethod.ReliableSequenced);
}
+ NetDataWriterPool.ReturnWriter(Writer);
}
catch (Exception ex)
{
diff --git a/Basis Server/BasisNetworkServer/BasisNetworking/BasisNetworkingGeneric.cs b/Basis Server/BasisNetworkServer/BasisNetworking/BasisNetworkingGeneric.cs
index f9e204677..38efe0c55 100644
--- a/Basis Server/BasisNetworkServer/BasisNetworking/BasisNetworkingGeneric.cs
+++ b/Basis Server/BasisNetworkServer/BasisNetworking/BasisNetworkingGeneric.cs
@@ -1,14 +1,16 @@
using Basis.Network.Core;
+using BasisNetworkCore;
using LiteNetLib;
using LiteNetLib.Utils;
using System.Collections.Concurrent;
+using System.Collections.Generic;
using static SerializableBasis;
namespace Basis.Network.Server.Generic
{
public static class BasisNetworkingGeneric
{
- public static void HandleScene(NetPacketReader Reader, DeliveryMethod DeliveryMethod, NetPeer sender, ConcurrentDictionary allClients)
+ public static void HandleScene(NetPacketReader Reader, DeliveryMethod DeliveryMethod, NetPeer sender)
{
SceneDataMessage SceneDataMessage = new SceneDataMessage();
SceneDataMessage.Deserialize(Reader);
@@ -25,7 +27,7 @@ public static void HandleScene(NetPacketReader Reader, DeliveryMethod DeliveryMe
}
};
byte Channel = BasisNetworkCommons.SceneChannel;
- NetDataWriter Writer = new NetDataWriter();
+ NetDataWriter Writer = NetDataWriterPool.GetWriter();
if (DeliveryMethod == DeliveryMethod.Unreliable)
{
Writer.Put(Channel);
@@ -34,16 +36,16 @@ public static void HandleScene(NetPacketReader Reader, DeliveryMethod DeliveryMe
serverSceneDataMessage.Serialize(Writer);
if (SceneDataMessage.recipientsSize != 0)
{
- ConcurrentDictionary targetedClients = new ConcurrentDictionary();
+ List targetedClients = new List();
int recipientsLength = SceneDataMessage.recipientsSize;
- // BNL.Log("Query Recipients " + recipientsLength);
+ // BNL.Log("Query Recipients " + recipientsLength);
for (int index = 0; index < recipientsLength; index++)
{
if (BasisNetworkServer.Peers.TryGetValue(SceneDataMessage.recipients[index], out NetPeer client))
{
BNL.Log("Found Peer! " + SceneDataMessage.recipients[index]);
- targetedClients.TryAdd((ushort)client.Id, client);
+ targetedClients.Add(client);
}
else
{
@@ -53,14 +55,15 @@ public static void HandleScene(NetPacketReader Reader, DeliveryMethod DeliveryMe
if (targetedClients.Count > 0)
{
- // BNL.Log("Sending out Target Clients " + targetedClients.Count);
- BasisNetworkServer.BroadcastMessageToClients(Writer, Channel, targetedClients, DeliveryMethod);
+ // BNL.Log("Sending out Target Clients " + targetedClients.Count);
+ BasisNetworkServer.BroadcastMessageToClients(Writer, Channel,ref targetedClients, DeliveryMethod);
}
}
else
{
- BasisNetworkServer.BroadcastMessageToClients(Writer, Channel, sender, BasisNetworkServer.Peers, DeliveryMethod);
+ BasisNetworkServer.BroadcastMessageToClients(Writer, Channel, sender,BasisPlayerArray.GetSnapshot(), DeliveryMethod);
}
+ NetDataWriterPool.ReturnWriter(Writer);
}
public static void HandleAvatar(NetPacketReader Reader, DeliveryMethod DeliveryMethod, NetPeer sender)
{
@@ -80,7 +83,7 @@ public static void HandleAvatar(NetPacketReader Reader, DeliveryMethod DeliveryM
}
};
byte Channel = BasisNetworkCommons.AvatarChannel;
- NetDataWriter Writer = new NetDataWriter();
+ NetDataWriter Writer = NetDataWriterPool.GetWriter();
if (DeliveryMethod == DeliveryMethod.Unreliable)
{
Writer.Put(Channel);
@@ -89,7 +92,7 @@ public static void HandleAvatar(NetPacketReader Reader, DeliveryMethod DeliveryM
serverAvatarDataMessage.Serialize(Writer);
if (avatarDataMessage.recipientsSize != 0)
{
- ConcurrentDictionary targetedClients = new ConcurrentDictionary();
+ List targetedClients = new List();
int recipientsLength = avatarDataMessage.recipientsSize;
// BNL.Log("Query Recipients " + recipientsLength);
@@ -98,7 +101,7 @@ public static void HandleAvatar(NetPacketReader Reader, DeliveryMethod DeliveryM
if (BasisNetworkServer.Peers.TryGetValue(avatarDataMessage.recipients[index], out NetPeer client))
{
// BNL.Log("Found Peer! " + avatarDataMessage.recipients[index]);
- targetedClients.TryAdd((ushort)client.Id, client);
+ targetedClients.Add(client);
}
else
{
@@ -109,13 +112,14 @@ public static void HandleAvatar(NetPacketReader Reader, DeliveryMethod DeliveryM
if (targetedClients.Count > 0)
{
//BNL.Log("Sending out Target Clients " + targetedClients.Count);
- BasisNetworkServer.BroadcastMessageToClients(Writer, Channel, targetedClients, DeliveryMethod);
+ BasisNetworkServer.BroadcastMessageToClients(Writer, Channel,ref targetedClients, DeliveryMethod);
}
}
else
{
- BasisNetworkServer.BroadcastMessageToClients(Writer, Channel, sender, BasisNetworkServer.Peers, DeliveryMethod);
+ BasisNetworkServer.BroadcastMessageToClients(Writer, Channel, sender, BasisPlayerArray.GetSnapshot(), DeliveryMethod);
}
+ NetDataWriterPool.ReturnWriter(Writer);
}
}
}
diff --git a/Basis Server/BasisNetworkServer/BasisNetworking/BasisServerReductionSystem.cs b/Basis Server/BasisNetworkServer/BasisNetworking/BasisServerReductionSystem.cs
index 076f8e06a..d9dde0caa 100644
--- a/Basis Server/BasisNetworkServer/BasisNetworking/BasisServerReductionSystem.cs
+++ b/Basis Server/BasisNetworkServer/BasisNetworking/BasisServerReductionSystem.cs
@@ -3,6 +3,7 @@
using Basis.Network.Core;
using Basis.Network.Core.Compression;
using Basis.Scripts.Networking.Compression;
+using BasisNetworkCore;
using LiteNetLib;
using LiteNetLib.Utils;
using static SerializableBasis;
@@ -172,7 +173,7 @@ private void SendPlayerData(object state)
{
Console.WriteLine("Unable to find Pulse for LocalClient Wont Do Interval Adjust");
}
- NetDataWriter Writer = new NetDataWriter();
+ NetDataWriter Writer = NetDataWriterPool.GetWriter();
if (playerData.serverSideSyncPlayerMessage.avatarSerialization.array == null || playerData.serverSideSyncPlayerMessage.avatarSerialization.array.Length == 0)
{
Console.WriteLine("Unable to send out Avatar Data Was null or Empty!");
@@ -183,14 +184,14 @@ private void SendPlayerData(object state)
playerID.localClient.Send(Writer, BasisNetworkCommons.MovementChannel, DeliveryMethod.Sequenced);
}
playerData.newDataExists = false;
+ NetDataWriterPool.ReturnWriter(Writer);
}
}
}
}
public static float Distance(Vector3 pointA, Vector3 pointB)
{
- Vector3 difference = pointB - pointA;
- return difference.SquaredMagnitude();
+ return (pointB - pointA).SquaredMagnitude(); // Avoid intermediate objects if possible
}
///
diff --git a/Basis Server/LiteNetLib/PooledPacket.cs b/Basis Server/LiteNetLib/PooledPacket.cs
index 26ef7bd82..1d2daa746 100644
--- a/Basis Server/LiteNetLib/PooledPacket.cs
+++ b/Basis Server/LiteNetLib/PooledPacket.cs
@@ -1,4 +1,4 @@
-namespace LiteNetLib
+namespace LiteNetLib
{
public readonly ref struct PooledPacket
{