Skip to content

Commit

Permalink
added chunked ChunkedServerSideReducablePlayerArray
Browse files Browse the repository at this point in the history
  • Loading branch information
dooly123 committed Jan 9, 2025
1 parent 9f69990 commit 73aadd2
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 60 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using Basis.Network.Core;
using Basis.Network.Core.Compression;
using Basis.Scripts.Networking.Compression;
using BasisNetworkCore;
using LiteNetLib;
using LiteNetLib.Utils;
using static SerializableBasis;
public partial class BasisServerReductionSystem
{
// Default interval in milliseconds for the timer
public static Configuration Configuration;
public static ConcurrentDictionary<NetPeer, SyncedToPlayerPulse> PlayerSync = new ConcurrentDictionary<NetPeer, SyncedToPlayerPulse>();
public static ConcurrentDictionary<int, SyncedToPlayerPulse> PlayerSync = new ConcurrentDictionary<int, SyncedToPlayerPulse>();
/// <summary>
/// add the new client
/// then update all existing clients arrays
Expand All @@ -22,12 +23,13 @@ public partial class BasisServerReductionSystem
public static void AddOrUpdatePlayer(NetPeer playerID, ServerSideSyncPlayerMessage playerToUpdate, NetPeer serverSideSyncPlayer)
{
//stage 1 lets update whoever send us this datas last player information
if (PlayerSync.TryGetValue(serverSideSyncPlayer, out SyncedToPlayerPulse playerData))
if (PlayerSync.TryGetValue(serverSideSyncPlayer.Id, out SyncedToPlayerPulse playerData))
{
playerData.lastPlayerInformation = playerToUpdate;
playerData.Position = BasisNetworkCompressionExtensions.DecompressAndProcessAvatar(playerToUpdate);
}
//ok now we can try to schedule sending out this data!
if (PlayerSync.TryGetValue(playerID, out playerData))
if (PlayerSync.TryGetValue(playerID.Id, out playerData))
{
// Update the player's message
playerData.SupplyNewData(playerID, playerToUpdate, serverSideSyncPlayer);
Expand All @@ -37,31 +39,40 @@ public static void AddOrUpdatePlayer(NetPeer playerID, ServerSideSyncPlayerMessa
//first time request create said data!
playerData = new SyncedToPlayerPulse
{
// playerID = playerID,
queuedPlayerMessages = new ConcurrentDictionary<NetPeer, ServerSideReducablePlayer>(),
// playerID = playerID,
lastPlayerInformation = playerToUpdate,
Position = BasisNetworkCompressionExtensions.DecompressAndProcessAvatar(playerToUpdate),
};
//ok now we can try to schedule sending out this data!
if (PlayerSync.TryAdd(playerID, playerData))
if (PlayerSync.TryAdd(playerID.Id, playerData))
{ // Update the player's message
playerData.SupplyNewData(playerID, playerToUpdate, serverSideSyncPlayer);
}
}
}
public static void RemovePlayer(NetPeer playerID)
{
if (PlayerSync.TryRemove(playerID, out SyncedToPlayerPulse pulse))
if (PlayerSync.TryRemove(playerID.Id, out SyncedToPlayerPulse pulse))
{
foreach (ServerSideReducablePlayer player in pulse.queuedPlayerMessages.Values)
for (int i = 0; i < 1024; i++)
{
player.timer.Dispose();
ServerSideReducablePlayer player = pulse.ChunkedServerSideReducablePlayerArray.GetPlayer(i);
if (player != null)
{
player.timer.Dispose();
pulse.ChunkedServerSideReducablePlayerArray.SetPlayer(i, null);
}
}
}
foreach (SyncedToPlayerPulse player in PlayerSync.Values)
SyncedToPlayerPulse[] Index = PlayerSync.Values.ToArray();
for (int i = 0; i < Index.Length; i++)
{
if (player.queuedPlayerMessages.TryRemove(playerID, out ServerSideReducablePlayer reduceablePlayer))
SyncedToPlayerPulse player = Index[i];
ServerSideReducablePlayer SSRP = player.ChunkedServerSideReducablePlayerArray.GetPlayer(i);
if (SSRP != null)
{
reduceablePlayer.timer.Dispose();
SSRP.timer.Dispose();
pulse.ChunkedServerSideReducablePlayerArray.SetPlayer(i, null);
}
}
}
Expand All @@ -73,12 +84,13 @@ public class SyncedToPlayerPulse
// The player ID to which the data is being sent
// public NetPeer playerID;
public ServerSideSyncPlayerMessage lastPlayerInformation;
public Vector3 Position;
/// <summary>
/// Dictionary to hold queued messages for each player.
/// Key: Player ID, Value: Server-side player data
/// </summary>
public ConcurrentDictionary<NetPeer, ServerSideReducablePlayer> queuedPlayerMessages = new ConcurrentDictionary<NetPeer, ServerSideReducablePlayer>();
public ChunkedBoolArray SyncBoolArray = new ChunkedBoolArray(64);
public ChunkedServerSideReducablePlayerArray ChunkedServerSideReducablePlayerArray = new ChunkedServerSideReducablePlayerArray(64);
/// <summary>
/// Supply new data to a specific player.
/// </summary>
Expand All @@ -87,12 +99,14 @@ public class SyncedToPlayerPulse
/// <param name="serverSidePlayer"></param>
public void SupplyNewData(NetPeer playerID, ServerSideSyncPlayerMessage serverSideSyncPlayerMessage, NetPeer serverSidePlayer)
{
if (queuedPlayerMessages.TryGetValue(serverSidePlayer, out ServerSideReducablePlayer playerData))
ServerSideReducablePlayer playerData = ChunkedServerSideReducablePlayerArray.GetPlayer(serverSidePlayer.Id);
if (playerData != null)
{
// Update the player's message
playerData.serverSideSyncPlayerMessage = serverSideSyncPlayerMessage;
playerData.Position = BasisNetworkCompressionExtensions.DecompressAndProcessAvatar(serverSideSyncPlayerMessage);
SyncBoolArray.SetBool(serverSidePlayer.Id, true);
queuedPlayerMessages[serverSidePlayer] = playerData;
ChunkedServerSideReducablePlayerArray.SetPlayer(serverSidePlayer.Id, playerData);
}
else
{
Expand All @@ -112,77 +126,67 @@ public void AddPlayer(NetPeer playerID, ServerSideSyncPlayerMessage serverSideSy
ClientPayload clientPayload = new ClientPayload
{
localClient = playerID,
dataCameFromThisUser = serverSidePlayer
dataCameFromThisUser = serverSidePlayer.Id
};
serverSideSyncPlayerMessage.interval = (byte)Configuration.BSRSMillisecondDefaultInterval;
ServerSideReducablePlayer newPlayer = new ServerSideReducablePlayer
{
serverSideSyncPlayerMessage = serverSideSyncPlayerMessage,
timer = new System.Threading.Timer(SendPlayerData, clientPayload, Configuration.BSRSMillisecondDefaultInterval, Configuration.BSRSMillisecondDefaultInterval),
timer = new ManagedTimer(SendPlayerData, clientPayload, Configuration.BSRSMillisecondDefaultInterval, Configuration.BSRSMillisecondDefaultInterval),
Writer = new NetDataWriter(true, 204),
Position = BasisNetworkCompressionExtensions.DecompressAndProcessAvatar(serverSideSyncPlayerMessage),
};
SendPlayerData(clientPayload);
SyncBoolArray.SetBool(serverSidePlayer.Id, true);
queuedPlayerMessages[serverSidePlayer] = newPlayer;
}

/// <summary>
/// Removes a player from the queue and disposes of their timer.
/// </summary>
/// <param name="playerID">The ID of the player to remove</param>
public void RemovePlayer(NetPeer playerID)
{
if (queuedPlayerMessages.TryRemove(playerID, out ServerSideReducablePlayer playerData))
{
// Dispose of the timer to free resources
playerData.timer.Dispose();
}
ChunkedServerSideReducablePlayerArray.SetPlayer(serverSidePlayer.Id, newPlayer);
}
public struct ClientPayload
{
public NetPeer localClient;
public NetPeer dataCameFromThisUser;
public int dataCameFromThisUser;
}
/// <summary>
/// Callback function to send player data at regular intervals.
/// </summary>
/// <param name="state">The player ID (passed from the timer)</param>
private void SendPlayerData(object state)
{
if (state is ClientPayload playerID)
var playerID = (ClientPayload)state;
if (SyncBoolArray.GetBool(playerID.dataCameFromThisUser))
{
if (SyncBoolArray.GetBool(playerID.dataCameFromThisUser.Id))
ServerSideReducablePlayer playerData = ChunkedServerSideReducablePlayerArray.GetPlayer(playerID.dataCameFromThisUser);
if (playerData != null)
{
if (queuedPlayerMessages.TryGetValue(playerID.dataCameFromThisUser, out ServerSideReducablePlayer playerData))
if (PlayerSync.TryGetValue(playerID.localClient.Id, out SyncedToPlayerPulse pulse))
{
if (PlayerSync.TryGetValue(playerID.localClient, out SyncedToPlayerPulse pulse))
try
{
try
// Calculate the distance between the two points
float activeDistance = Distance(pulse.Position, playerData.Position);
// Adjust the timer interval based on the new syncRateMultiplier
int adjustedInterval = (int)(Configuration.BSRSMillisecondDefaultInterval * (Configuration.BSRBaseMultiplier + (activeDistance * Configuration.BSRSIncreaseRate)));
if (adjustedInterval > byte.MaxValue)
{
Vector3 from = BasisNetworkCompressionExtensions.DecompressAndProcessAvatar(pulse.lastPlayerInformation);
Vector3 to = BasisNetworkCompressionExtensions.DecompressAndProcessAvatar(playerData.serverSideSyncPlayerMessage);
// Calculate the distance between the two points
float activeDistance = Distance(from, to);
// Adjust the timer interval based on the new syncRateMultiplier
int adjustedInterval = (int)(Configuration.BSRSMillisecondDefaultInterval * (Configuration.BSRBaseMultiplier + (activeDistance * Configuration.BSRSIncreaseRate)));
if (adjustedInterval > byte.MaxValue)
{
adjustedInterval = byte.MaxValue;
}
// Console.WriteLine("Adjusted Interval is" + adjustedInterval);
playerData.timer.Change(adjustedInterval, adjustedInterval);
//how long does this data need to last for
playerData.serverSideSyncPlayerMessage.interval = (byte)adjustedInterval;
playerData.serverSideSyncPlayerMessage.Serialize(playerData.Writer);
playerID.localClient.Send(playerData.Writer, BasisNetworkCommons.MovementChannel, DeliveryMethod.Sequenced);
playerData.Writer.Reset();
adjustedInterval = byte.MaxValue;
}
catch (Exception e)
if (playerData.serverSideSyncPlayerMessage.interval != adjustedInterval)
{
BNL.LogError("Server Reduction System Encounter Isssue " + e.Message + " " + e.StackTrace);
// Console.WriteLine("Adjusted Interval is" + adjustedInterval);
playerData.timer.Change(adjustedInterval, adjustedInterval);
}
//how long does this data need to last for
playerData.serverSideSyncPlayerMessage.interval = (byte)adjustedInterval;
playerData.serverSideSyncPlayerMessage.Serialize(playerData.Writer);
playerID.localClient.Send(playerData.Writer, BasisNetworkCommons.MovementChannel, DeliveryMethod.Sequenced);
playerData.Writer.Reset();
}
catch (Exception e)
{
BNL.LogError("Server Reduction System Encounter Isssue " + e.Message + " " + e.StackTrace);
}
SyncBoolArray.SetBool(playerID.dataCameFromThisUser.Id, false);
}
}
SyncBoolArray.SetBool(playerID.dataCameFromThisUser, false);
}
}
}
Expand All @@ -194,10 +198,38 @@ public static float Distance(Vector3 pointA, Vector3 pointB)
/// <summary>
/// Structure representing a player's server-side data that can be reduced.
/// </summary>
public struct ServerSideReducablePlayer
public class ServerSideReducablePlayer
{
public System.Threading.Timer timer;//create a new timer
public ManagedTimer timer;//create a new timer
public ServerSideSyncPlayerMessage serverSideSyncPlayerMessage;
public NetDataWriter Writer;
public Vector3 Position;
}
public class ManagedTimer : IDisposable
{
private Timer _timer;
public bool IsDisposed = false;

public ManagedTimer(TimerCallback callback, object state, int dueTime, int period)
{
_timer = new System.Threading.Timer(callback, state, dueTime, period);
}
public void Dispose()
{
if (!IsDisposed)
{
_timer.Dispose();
IsDisposed = true;
}
}

public bool Change(int dueTime, int period)
{
if (IsDisposed)
{
throw new ObjectDisposedException(nameof(ManagedTimer));
}
return _timer.Change(dueTime, period);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using static BasisServerReductionSystem;

public class ChunkedBoolArray
{
Expand Down Expand Up @@ -60,3 +61,63 @@ public bool GetBool(int index)
}
}
}
public class ChunkedServerSideReducablePlayerArray
{
private readonly object[] _chunkLocks; // Locks for each chunk
private readonly ServerSideReducablePlayer[][] _chunks; // Array divided into chunks
private readonly int _chunkSize; // Number of elements in each chunk
private readonly int _numChunks; // Number of chunks
public const int TotalSize = 1024; // Total size of the array

// Precomputed array size
private readonly int _totalSize;

public ChunkedServerSideReducablePlayerArray(int chunkSize = 256)
{
if (TotalSize <= 0)
throw new ArgumentOutOfRangeException(nameof(TotalSize), "Total size must be greater than zero.");
if (chunkSize <= 0)
throw new ArgumentOutOfRangeException(nameof(chunkSize), "Chunk size must be greater than zero.");

_chunkSize = chunkSize;
_numChunks = (int)Math.Ceiling((double)TotalSize / chunkSize);
_totalSize = _chunkSize * _numChunks;

_chunks = new ServerSideReducablePlayer[_numChunks][];
_chunkLocks = new object[_numChunks];

for (int i = 0; i < _numChunks; i++)
{
_chunks[i] = new ServerSideReducablePlayer[chunkSize];
_chunkLocks[i] = new object();
}
}

public void SetPlayer(int index, ServerSideReducablePlayer player)
{
if (index < 0 || index >= _totalSize)
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");

int chunkIndex = index / _chunkSize;
int localIndex = index % _chunkSize;

lock (_chunkLocks[chunkIndex])
{
_chunks[chunkIndex][localIndex] = player;
}
}

public ServerSideReducablePlayer GetPlayer(int index)
{
if (index < 0 || index >= _totalSize)
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");

int chunkIndex = index / _chunkSize;
int localIndex = index % _chunkSize;

lock (_chunkLocks[chunkIndex])
{
return _chunks[chunkIndex][localIndex];
}
}
}

0 comments on commit 73aadd2

Please sign in to comment.