服务端跟随新用户槽位 更改管理 ,以及对象池

This commit is contained in:
sin365 2024-12-27 14:00:52 +08:00
parent ff3c511895
commit 5caff2dbda
8 changed files with 515 additions and 35 deletions

View File

@ -0,0 +1,401 @@
using System.Reflection;
using System.Text;
namespace AxibugEmuOnline.Server
{
public static class ObjectPoolAuto
{
/************************************************************************************************************************/
/// <summary>
/// 获取或者创建一个新的
/// </summary>
/// <remarks>Remember to <see cref="Release{T}(T)"/> 需要回收参见这个</remarks>
public static T Acquire<T>()
where T : class, new()
=> ObjectPool<T>.Acquire();
/// <summary>
/// 获取或者创建一个新的
/// </summary>
/// <remarks>Remember to <see cref="Release{T}(T)"/> 需要回收参见这个</remarks>
public static void Acquire<T>(out T item)
where T : class, new()
=> item = ObjectPool<T>.Acquire();
/************************************************************************************************************************/
/// <summary>
/// 回收对象
/// </summary>
public static void Release<T>(T item)
where T : class, new()
=> ObjectPool<T>.Release(item);
/// <summary>
/// 回收对象
/// </summary>
public static void Release<T>(ref T item) where T : class, new()
{
if (item != null)
{
ObjectPool<T>.Release(item);
item = null;
}
}
/************************************************************************************************************************/
public const string
NotClearError = " They must be cleared before being released to the pool and not modified after that.";
/************************************************************************************************************************/
/// <summary>
/// 获取或创建List
/// </summary>
/// <remarks>Remember to <see cref="Release{T}(List{T})"/> 回收参见此方法</remarks>
public static List<T> AcquireList<T>()
{
var list = ObjectPool<List<T>>.Acquire();
AppSrv.g_Log.Assert(list.Count == 0, "A pooled list is not empty." + NotClearError);
return list;
}
/// <summary>
/// 回收List
/// </summary>
public static void Release<T>(List<T> list)
{
list.Clear();
ObjectPool<List<T>>.Release(list);
}
/************************************************************************************************************************/
/// <summary>
/// 获取或创建Queue
/// </summary>
/// <remarks>Remember to <see cref="Release{T}(Queue{T})"/> 回收参见此方法</remarks>
public static Queue<T> AcquireQueue<T>()
{
var queue = ObjectPool<Queue<T>>.Acquire();
AppSrv.g_Log.Assert(queue.Count == 0, "A pooled list is not empty." + NotClearError);
return queue;
}
/// <summary>
/// 回收Queue
/// </summary>
public static void Release<T>(Queue<T> list)
{
list.Clear();
ObjectPool<Queue<T>>.Release(list);
}
/************************************************************************************************************************/
/// <summary>
/// 获取或创建HashSet
/// </summary>
public static HashSet<T> AcquireSet<T>()
{
var set = ObjectPool<HashSet<T>>.Acquire();
AppSrv.g_Log.Assert(set.Count == 0, "A pooled set is not empty." + NotClearError);
return set;
}
/// <summary>
/// 释放HashSet
/// </summary>
public static void Release<T>(HashSet<T> set)
{
set.Clear();
ObjectPool<HashSet<T>>.Release(set);
}
/************************************************************************************************************************/
/// <summary>
/// 获取一个字符串StringBuilder
/// </summary>
/// <remarks>Remember to <see cref="Release(StringBuilder)"/>回收参见这个</remarks>
public static StringBuilder AcquireStringBuilder()
{
var builder = ObjectPool<StringBuilder>.Acquire();
AppSrv.g_Log.Assert(builder.Length == 0, $"A pooled {nameof(StringBuilder)} is not empty." + NotClearError);
return builder;
}
/// <summary>
/// 回收 StringBuilder
/// </summary>
public static void Release(StringBuilder builder)
{
builder.Length = 0;
ObjectPool<StringBuilder>.Release(builder);
}
/// <summary>
/// 回收 StringBuilder
/// </summary>
public static string ReleaseToString(this StringBuilder builder)
{
var result = builder.ToString();
Release(builder);
return result;
}
/************************************************************************************************************************/
private static class Cache<T>
{
public static readonly Dictionary<MethodInfo, KeyValuePair<Func<T>, T>>
Results = new Dictionary<MethodInfo, KeyValuePair<Func<T>, T>>();
}
/// <summary>
/// 此方法主要用于频繁绘制缓存比如说GUI绘制
/// </summary>
public static T GetCachedResult<T>(Func<T> function)
{
var method = function.Method;
if (!Cache<T>.Results.TryGetValue(method, out var result))
{
result = new KeyValuePair<Func<T>, T>(function, function());
Cache<T>.Results.Add(method, result);
}
else if (result.Key != function)
{
AppSrv.g_Log.Warning(
$"{nameof(GetCachedResult)}<{typeof(T).Name}>" +
$" was previously called on {method.Name} with a different target." +
" This likely means that a new delegate is being passed into every call" +
" so it can't actually return the same cached object.");
}
return result.Value;
}
/************************************************************************************************************************/
public static class Disposable
{
/************************************************************************************************************************/
/// <summary>
/// Calls <see cref="ObjectPool{T}.Disposable.Acquire"/> to get a spare <see cref="List{T}"/> if
/// </summary>
public static IDisposable Acquire<T>(out T item)
where T : class, new()
=> ObjectPool<T>.Disposable.Acquire(out item);
/************************************************************************************************************************/
/// <summary>
/// Calls <see cref="ObjectPool{T}.Disposable.Acquire"/> to get a spare <see cref="List{T}"/> if
/// </summary>
public static IDisposable AcquireList<T>(out List<T> list)
{
var disposable = ObjectPool<List<T>>.Disposable.Acquire(out list, onRelease: (l) => l.Clear());
AppSrv.g_Log.Assert(list.Count == 0, "A pooled list is not empty." + NotClearError);
return disposable;
}
/************************************************************************************************************************/
/// <summary>
/// Calls <see cref="ObjectPool{T}.Disposable.Acquire"/> to get a spare <see cref="HashSet{T}"/> if
/// </summary>
public static IDisposable AcquireSet<T>(out HashSet<T> set)
{
var disposable = ObjectPool<HashSet<T>>.Disposable.Acquire(out set, onRelease: (s) => s.Clear());
AppSrv.g_Log.Assert(set.Count == 0, "A pooled set is not empty." + NotClearError);
return disposable;
}
/************************************************************************************************************************/
}
/************************************************************************************************************************/
}
public static class ObjectPool<T> where T : class, new()
{
/************************************************************************************************************************/
private static readonly List<T>
Items = new List<T>();
/************************************************************************************************************************/
/// <summary>The number of spare items currently in the pool.</summary>
public static int Count
{
get => Items.Count;
set
{
var count = Items.Count;
if (count < value)
{
if (Items.Capacity < value)
Items.Capacity = NextPowerOfTwo(value);
do
{
Items.Add(new T());
count++;
}
while (count < value);
}
else if (count > value)
{
Items.RemoveRange(value, count - value);
}
}
}
public static int NextPowerOfTwo(int value)
{
if (value <= 0)
{
throw new ArgumentException("Value must be greater than zero.");
}
int powerOfTwo = 1;
while (powerOfTwo < value)
{
powerOfTwo <<= 1; // equivalent to multiplying by 2
}
return powerOfTwo;
}
/************************************************************************************************************************/
/// <summary>
/// If the <see cref="Count"/> is less than the specified value, this method increases it to that value by
/// creating new objects.
/// </summary>
public static void SetMinCount(int count)
{
if (Count < count)
Count = count;
}
/************************************************************************************************************************/
/// <summary>The <see cref="List{T}.Capacity"/> of the internal list of spare items.</summary>
public static int Capacity
{
get => Items.Capacity;
set
{
if (Items.Count > value)
Items.RemoveRange(value, Items.Count - value);
Items.Capacity = value;
}
}
/************************************************************************************************************************/
/// <summary>Returns a spare item if there are any, or creates a new one.</summary>
/// <remarks>Remember to <see cref="Release(T)"/> it when you are done.</remarks>
public static T Acquire()
{
var count = Items.Count;
if (count == 0)
{
return new T();
}
else
{
count--;
var item = Items[count];
Items.RemoveAt(count);
return item;
}
}
/************************************************************************************************************************/
/// <summary>Adds the `item` to the list of spares so it can be reused.</summary>
public static void Release(T item)
{
Items.Add(item);
}
/************************************************************************************************************************/
/// <summary>Returns a description of the state of this pool.</summary>
public static string GetDetails()
{
return
$"{typeof(T).Name}" +
$" ({nameof(Count)} = {Items.Count}" +
$", {nameof(Capacity)} = {Items.Capacity}" +
")";
}
/************************************************************************************************************************/
/// <summary>
/// An <see cref="IDisposable"/> system to allow pooled objects to be acquired and released within <c>using</c>
/// statements instead of needing to manually release everything.
/// </summary>
public sealed class Disposable : IDisposable
{
/************************************************************************************************************************/
private static readonly List<Disposable> LazyStack = new List<Disposable>();
private static int _ActiveDisposables;
private T _Item;
private Action<T> _OnRelease;
/************************************************************************************************************************/
private Disposable() { }
/// <summary>
/// Calls <see cref="ObjectPool{T}.Acquire"/> to set the `item` and returns an <see cref="IDisposable"/>
/// that will call <see cref="Release(T)"/> on the `item` when disposed.
/// </summary>
public static IDisposable Acquire(out T item, Action<T> onRelease = null)
{
Disposable disposable;
if (LazyStack.Count <= _ActiveDisposables)
{
LazyStack.Add(disposable = new Disposable());
}
else
{
disposable = LazyStack[_ActiveDisposables];
}
_ActiveDisposables++;
disposable._Item = item = ObjectPool<T>.Acquire();
disposable._OnRelease = onRelease;
return disposable;
}
/************************************************************************************************************************/
void IDisposable.Dispose()
{
_OnRelease?.Invoke(_Item);
Release(_Item);
_ActiveDisposables--;
}
/************************************************************************************************************************/
}
}
}

View File

@ -50,7 +50,10 @@ namespace AxibugEmuOnline.Server.Event
}
else
{
eventDic.Add(evt, new List<Delegate>() { callback });
//eventDic.Add(evt, new List<Delegate>() { callback });
List<Delegate> delList = ObjectPoolAuto.AcquireList<Delegate>();
delList.Add(callback);
eventDic.Add(evt, delList);
}
}
#endregion
@ -92,7 +95,12 @@ namespace AxibugEmuOnline.Server.Event
if (eventDic.ContainsKey(evt))
{
eventDic[evt].Remove(callback);
if (eventDic[evt].Count == 0) eventDic.Remove(evt);
if (eventDic[evt].Count == 0)
{
//eventDic.Remove(evt);
ObjectPoolAuto.Release(eventDic[evt]);
eventDic.Remove(evt);
}
}
}
#endregion

View File

@ -20,12 +20,12 @@ namespace AxibugEmuOnline.Server
public static void InitServer(int port)
{
g_Log = new LogManager();
Config.LoadConfig();
Haoyue_SQLPoolManager.InitConnMgr();
g_Tick = new TickManager();
g_ClientMgr = new ClientManager();
g_ClientMgr.Init(45000, 120);
g_Log = new LogManager();
g_Login = new LoginManager();
g_Chat = new ChatManager();
g_UserMgr = new UserManager();

View File

@ -1,10 +1,8 @@
using AxibugEmuOnline.Server.Common;
using AxibugEmuOnline.Server.Event;
using AxibugEmuOnline.Server.NetWork;
using AxibugProtobuf;
using MySql.Data.MySqlClient;
using System.Net.Sockets;
using static AxibugEmuOnline.Server.RoomManager;
namespace AxibugEmuOnline.Server.Manager
{

View File

@ -1,4 +1,7 @@
namespace AxibugEmuOnline.Server.Manager
using AxibugProtobuf;
using static Mysqlx.Expect.Open.Types;
namespace AxibugEmuOnline.Server.Manager
{
public class LogManager
{
@ -19,6 +22,14 @@
//Console.WriteLine(str);
}
public void Assert(bool conditional, string message)
{
if (!conditional)
{
Debug(message);
}
}
public void Warning(string str)
{
Console.WriteLine(str);

View File

@ -4,6 +4,7 @@ using AxibugEmuOnline.Server.NetWork;
using AxibugProtobuf;
using MySql.Data.MySqlClient;
using Org.BouncyCastle.Crypto.Parameters;
using System.Collections.Generic;
using System.Data;
using System.Net.Sockets;
using System.Runtime.InteropServices;
@ -70,6 +71,8 @@ namespace AxibugEmuOnline.Server
{
if (mDictRoom.ContainsKey(RoomID))
{
mDictRoom[RoomID].Dispose();
mDictRoom.Remove(RoomID);
mKeyRoomList.Remove(RoomID);
}
@ -83,16 +86,14 @@ namespace AxibugEmuOnline.Server
return data;
}
public List<Data_RoomData> GetRoomList()
public void GetRoomList(ref List<Data_RoomData> roomList)
{
lock (mDictRoom)
{
List<Data_RoomData> temp = new List<Data_RoomData>();
foreach (var room in mDictRoom)
{
temp.AddRange(mDictRoom.Values);
roomList.AddRange(mDictRoom.Values);
}
return temp;
}
}
@ -177,9 +178,11 @@ namespace AxibugEmuOnline.Server
Protobuf_Room_List msg = ProtoBufHelper.DeSerizlize<Protobuf_Room_List>(reqData);
Protobuf_Room_List_RESP resp = new Protobuf_Room_List_RESP();
List<Data_RoomData> temp = GetRoomList();
List<Data_RoomData> temp = ObjectPoolAuto.AcquireList<Data_RoomData>();
GetRoomList(ref temp);
foreach (var room in temp)
resp.RoomMiniInfoList.Add(GetProtoDataRoom(room));
ObjectPoolAuto.Release(temp);
AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomList, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp));
}
public void CmdRoomGetScreen(Socket sk, byte[] reqData)
@ -513,12 +516,15 @@ namespace AxibugEmuOnline.Server
RoomMiniInfo = GetProtoDataRoom(room)
};
List<ClientInfo> userlist = room.GetAllPlayerClientList();
List<ClientInfo> userlist = ObjectPoolAuto.AcquireList<ClientInfo>();
room.GetAllPlayerClientList(ref userlist);
foreach (ClientInfo _c in userlist)
{
AppSrv.g_ClientMgr.ClientSend(_c, (int)CommandID.CmdRoomMyRoomStateChanged, (int)ErrorCode.ErrorOk, ProtoBufHelper.Serizlize(resp));
}
ObjectPoolAuto.Release(userlist);
}
/// <summary>
@ -527,7 +533,10 @@ namespace AxibugEmuOnline.Server
/// <param name="room"></param>
public void SendRoomStepChange(Data_RoomData room)
{
List<ClientInfo> roomClient = room.GetAllPlayerClientList();
List<ClientInfo> roomClient = ObjectPoolAuto.AcquireList<ClientInfo>();
room.GetAllPlayerClientList(ref roomClient);
switch (room.GameState)
{
case RoomGameState.WaitRawUpdate:
@ -562,6 +571,8 @@ namespace AxibugEmuOnline.Server
}
break;
}
ObjectPoolAuto.Release(roomClient);
}
#region
@ -591,7 +602,7 @@ namespace AxibugEmuOnline.Server
#endregion
}
public class Data_RoomData
public class Data_RoomData : IDisposable
{
public int RoomID { get; private set; }
public int GameRomID { get; private set; }
@ -612,9 +623,13 @@ namespace AxibugEmuOnline.Server
public uint mCurrServerFrameId = 0;
public ServerInputSnapShot mCurrInputData;
public Queue<(uint, ServerInputSnapShot)> mInputQueue;
public List<double> send2time;
const int SynLimitOnSec = 63;
object synInputLock = new object();
//TODO
public Dictionary<int, Queue<byte[]>> mDictPlayerIdx2SendQueue;
//public Dictionary<int, Queue<byte[]>> mDictPlayerIdx2SendQueue;
public RoomGameState GameState
{
get { return mGameState; }
@ -641,6 +656,7 @@ namespace AxibugEmuOnline.Server
public uint SrvForwardFrames { get; set; }
public void Init(int roomID, int gameRomID, string roomHash, long hostUId, bool bloadState = false)
{
Dispose();
RoomID = roomID;
GameRomID = gameRomID;
RomHash = roomHash;
@ -658,12 +674,39 @@ namespace AxibugEmuOnline.Server
PlayerSlot[i].Init(i);
//PlayerReadyState = new bool[4];
SynUIDs = new List<long>();//广播角色列表
SynUIDs = ObjectPoolAuto.AcquireList<long>();//new List<long>();//广播角色列表
GameState = RoomGameState.NoneGameState;
mCurrInputData = new ServerInputSnapShot();
mInputQueue = new Queue<(uint, ServerInputSnapShot)>();
mDictPlayerIdx2SendQueue = new Dictionary<int, Queue<byte[]>>();
mInputQueue = ObjectPoolAuto.AcquireQueue<(uint, ServerInputSnapShot)>();
// new Queue<(uint, ServerInputSnapShot)>();
//mDictPlayerIdx2SendQueue = new Dictionary<int, Queue<byte[]>>();
send2time = ObjectPoolAuto.AcquireList<double>();
}
/// <summary>
/// 房间释放时,需要调用
/// </summary>
public void Dispose()
{
if (SynUIDs != null)
{
ObjectPoolAuto.Release(SynUIDs);
SynUIDs = null;
}
if (mInputQueue != null)
{
ObjectPoolAuto.Release(mInputQueue);
mInputQueue = null;
}
if (send2time != null)
{
ObjectPoolAuto.Release(send2time);
send2time = null;
}
}
public Dictionary<uint, uint> GetSlotDataByUID(long uid)
{
Dictionary<uint, uint> slotIdx2JoyIdx = new Dictionary<uint, uint>();
@ -736,13 +779,13 @@ namespace AxibugEmuOnline.Server
else if (Player2_UID == uid) bHad = true;
else if (Player3_UID == uid) bHad = true;
else if (Player4_UID == uid) bHad = true;
if (bHad)
if (!bHad)
SynUIDs.RemoveAt(i);
}
if (!SynUIDs.Contains(Player1_UID)) SynUIDs.Add(Player1_UID);
if (!SynUIDs.Contains(Player2_UID)) SynUIDs.Add(Player2_UID);
if (!SynUIDs.Contains(Player3_UID)) SynUIDs.Add(Player3_UID);
if (!SynUIDs.Contains(Player4_UID)) SynUIDs.Add(Player4_UID);
if (Player1_UID > 0 && !SynUIDs.Contains(Player1_UID)) SynUIDs.Add(Player1_UID);
if (Player2_UID > 0 && !SynUIDs.Contains(Player2_UID)) SynUIDs.Add(Player2_UID);
if (Player3_UID > 0 && !SynUIDs.Contains(Player3_UID)) SynUIDs.Add(Player3_UID);
if (Player4_UID > 0 && !SynUIDs.Contains(Player4_UID)) SynUIDs.Add(Player4_UID);
}
#region
@ -790,7 +833,7 @@ namespace AxibugEmuOnline.Server
public void SetPlayerSlotData(ClientInfo _c, ref readonly Dictionary<uint, uint> newSlotIdx2JoyIdx)
{
Dictionary<uint, uint> oldSlotIdx2JoyIdx = GetSlotDataByUID(_c.UID);
HashSet<uint> diffSlotIdxs = new HashSet<uint>();
HashSet<uint> diffSlotIdxs = ObjectPoolAuto.AcquireSet<uint>();// new HashSet<uint>();
foreach (var old in oldSlotIdx2JoyIdx)
{
uint old_slotIdx = old.Key;
@ -830,6 +873,8 @@ namespace AxibugEmuOnline.Server
//更新需要同步的UID
UpdateSynUIDs();
_c.RoomState.SetRoomData(this.RoomID);
ObjectPoolAuto.Release(diffSlotIdxs);
}
public void RemovePlayer(ClientInfo _c)
{
@ -873,9 +918,8 @@ namespace AxibugEmuOnline.Server
return true;
}
public List<ClientInfo> GetAllPlayerClientList()
public void GetAllPlayerClientList(ref List<ClientInfo> list)
{
List<ClientInfo> list = new List<ClientInfo>();
List<long> Uids = SynUIDs;
foreach (long uid in Uids)
{
@ -883,7 +927,6 @@ namespace AxibugEmuOnline.Server
continue;
list.Add(_c);
}
return list;
}
void SetInputBySlotIdxJoyIdx(uint SlotIdx, uint LocalJoyIdx, ServerInputSnapShot clieninput)
@ -902,7 +945,10 @@ namespace AxibugEmuOnline.Server
}
public void UpdateRoomForwardNum()
{
List<ClientInfo> playerlist = GetAllPlayerClientList();
List<ClientInfo> playerlist = ObjectPoolAuto.AcquireList<ClientInfo>();
GetAllPlayerClientList(ref playerlist);
double maxNetDelay = 0;
for (int i = 0; i < playerlist.Count; i++)
{
@ -914,13 +960,15 @@ namespace AxibugEmuOnline.Server
if (SrvForwardFrames < 2)
SrvForwardFrames = 2;
//AppSrv.g_Log.Debug($"服务器提前跑帧数Max(2,({maxNetDelay} / {0.016f}) + {MustTaskFrame}) = {SrvForwardFrames}");
ObjectPoolAuto.Release(playerlist);
}
#region
void StartNewTick()
{
mInputQueue.Clear();
mDictPlayerIdx2SendQueue.Clear();
//mDictPlayerIdx2SendQueue.Clear();
mCurrServerFrameId = 0;
mCurrInputData.all = 1;
@ -951,8 +999,6 @@ namespace AxibugEmuOnline.Server
ulong LastTestSend = 0;
internal ulong LastTestRecv;
public List<double> send2time = new List<double>();
const int SynLimitOnSec = 63;
/// <summary>
/// 广播数据
@ -978,7 +1024,8 @@ namespace AxibugEmuOnline.Server
if (!flagInitList)
{
flagInitList = true;
temp = new List<(uint frameId, ServerInputSnapShot inputdata)>();
//temp = new List<(uint frameId, ServerInputSnapShot inputdata)>();
temp = ObjectPoolAuto.AcquireList<(uint frameId, ServerInputSnapShot inputdata)>();
}
temp.Add(mInputQueue.Dequeue());
send2time.Add(timeNow);
@ -1010,6 +1057,8 @@ namespace AxibugEmuOnline.Server
// AppSrv.g_Log.Debug($" {DateTime.Now.ToString("hh:mm:ss.fff")} SynInput=> RoomID->{RoomID} ServerFrameID->{mCurrServerFrameId} SynUIDs=>{string.Join(",", SynUIDs)} ");
//}
}
ObjectPoolAuto.Release(temp);
}
bool CheckRoomStateChange(int oldPlayerCount, int newPlayerCount)
@ -1174,6 +1223,7 @@ namespace AxibugEmuOnline.Server
forwaFrame = targetFrame - mCurrServerFrameId;
return forwaFrame > 0;
}
#endregion
}

View File

@ -31,7 +31,10 @@ namespace AxibugEmuOnline.Server.NetWork
}
else
{
netEventDic.Add(cmd, new List<Delegate>() { callback });
//netEventDic.Add(cmd, new List<Delegate>() { callback });
List<Delegate> delList = ObjectPoolAuto.AcquireList<Delegate>();
delList.Add(callback);
netEventDic.Add(cmd, delList);
}
}
#endregion
@ -49,7 +52,12 @@ namespace AxibugEmuOnline.Server.NetWork
if (netEventDic.ContainsKey(cmd))
{
netEventDic[cmd].Remove(callback);
if (netEventDic[cmd].Count == 0) netEventDic.Remove(cmd);
if (netEventDic[cmd].Count == 0)
{
//netEventDic.Remove(cmd);
ObjectPoolAuto.Release(netEventDic[cmd]);
netEventDic.Remove(cmd);
}
}
}
#endregion

View File

@ -21,7 +21,10 @@ namespace AxibugEmuOnline.Server
{
case "rlist":
{
var roomlist = AppSrv.g_Room.GetRoomList();
List<Data_RoomData> roomlist = ObjectPoolAuto.AcquireList<Data_RoomData>();
AppSrv.g_Room.GetRoomList(ref roomlist);
AppSrv.g_Log.Info($"RoomCount:{roomlist.Count}");
foreach (var room in roomlist)
{
@ -55,6 +58,7 @@ namespace AxibugEmuOnline.Server
}
}
}
ObjectPoolAuto.Release(roomlist);
}
break;
case "list":