feat: initial commit - HJHA game server full source
6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) + game logic (GameFix/GameDAL/ServerData) + network layer (NetWorkMessage) + data layer (ObjectModel) + utilities (MrWu/Core/Config/CloudAPI/dll) + adapters (zyxAdapter/base) .NET 8.0 C# solution, 16 projects, 958 source files
This commit is contained in:
250
ServerCore/Actor/Base/Actor.cs
Normal file
250
ServerCore/Actor/Base/Actor.cs
Normal file
@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MrWu.Debug;
|
||||
using NetWorkMessage;
|
||||
using Server.Core;
|
||||
|
||||
namespace ActorCore
|
||||
{
|
||||
public abstract class Actor : IActor, ITimerManager, ICoroutineLockManager, IMailBox
|
||||
{
|
||||
public ActorId ActorId { get; private set; }
|
||||
|
||||
public bool IsRegister { get; set; }
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public abstract SchedulerType SchedulerType { get; }
|
||||
|
||||
public ThreadSynchronizationContext ThreadSynchronizationContext { get; private set; }
|
||||
|
||||
private const int TIMEOUT_TIME = 40 * 1000;
|
||||
|
||||
private uint RpcId;
|
||||
|
||||
public int Id => ActorId.ActorTypeId;
|
||||
|
||||
private readonly MailBox MailBox;
|
||||
|
||||
protected readonly CoroutineLockManager CoroutineLockManager;
|
||||
|
||||
private readonly TimerManager TimerManager;
|
||||
|
||||
public Actor(byte moduleId, byte nodeNum, byte actorTypeId)
|
||||
{
|
||||
this.ActorId = new ActorId(moduleId, nodeNum, actorTypeId);
|
||||
this.ThreadSynchronizationContext = new ThreadSynchronizationContext(actorTypeId);
|
||||
this.CoroutineLockManager = new CoroutineLockManager();
|
||||
this.TimerManager = new TimerManager();
|
||||
this.MailBox = new MailBox(this);
|
||||
}
|
||||
|
||||
void IActor.IUpdate()
|
||||
{
|
||||
MailBox.Update();
|
||||
CoroutineLockManager.Update();
|
||||
TimerManager.Update();
|
||||
Update();
|
||||
}
|
||||
|
||||
void IActor.ILateUpdate()
|
||||
{
|
||||
LateUpdate();
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void LateUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (IsRegister)
|
||||
{
|
||||
//如果这个没有正确被Manager管理,这里将会移除错误的Id
|
||||
ActorMessageQueue.Instance.RemoveQueue(this.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public void Send(ActorId actorId, IMessage message)
|
||||
{
|
||||
MailBox.Send(actorId, (MessageObject)message);
|
||||
}
|
||||
|
||||
public void Reply(ActorId actorId, IResponse response)
|
||||
{
|
||||
MailBox.Send(actorId, (MessageObject)response);
|
||||
}
|
||||
|
||||
public async Task<IResponse> Call(ActorId actorId, IRequest request)
|
||||
{
|
||||
IResponse response;
|
||||
if (actorId.Process == this.ActorId.Process)
|
||||
{
|
||||
//内部Rpc
|
||||
response = await CallInner(actorId, request);
|
||||
}
|
||||
else
|
||||
{
|
||||
//外部Rpc
|
||||
NetInnerRequest netInnerRequest = NetInnerRequest.Create();
|
||||
netInnerRequest.FromActorId = this.ActorId;
|
||||
netInnerRequest.ActorId = actorId;
|
||||
netInnerRequest.MessageObject = request;
|
||||
|
||||
NetInnerResponse innerResponse = (NetInnerResponse)await CallInner(
|
||||
new ActorId(this.ActorId.Process, ActorTypeId.NetInner),
|
||||
netInnerRequest);
|
||||
|
||||
//把错误码传递给响应体
|
||||
if (innerResponse.MessageObject == null)
|
||||
{
|
||||
innerResponse.MessageObject =
|
||||
MessageHelper.CreateResponse(request.GetType(), request.RpcId, innerResponse.Error);
|
||||
}
|
||||
|
||||
response = innerResponse.MessageObject;
|
||||
innerResponse.Dispose();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private async Task<IResponse> CallInner(ActorId actorId, IRequest request)
|
||||
{
|
||||
uint rpcId = GetRpcId();
|
||||
request.RpcId = rpcId;
|
||||
|
||||
if (actorId == default)
|
||||
{
|
||||
throw new Exception($"acotr id is 0:{request}");
|
||||
}
|
||||
|
||||
IResponse response;
|
||||
Type requestType = request.GetType();
|
||||
MessageSenderStruct messageSenderStruct = new MessageSenderStruct(actorId, requestType);
|
||||
Debug.Log($"CallInner:{request.GetType()} {rpcId}");
|
||||
if (!MailBox.AddActorRpcResponse(rpcId, messageSenderStruct))
|
||||
{
|
||||
Debug.Error("RpcId 竟然有重复的现象!!!");
|
||||
response = MessageHelper.CreateResponse(requestType, rpcId, NetErrorCode.ERR_RpcIdAddFail);
|
||||
return response;
|
||||
}
|
||||
|
||||
if (!ActorMessageQueue.Instance.Send(this.ActorId, actorId, (MessageObject)request))
|
||||
{
|
||||
response = MessageHelper.CreateResponse(requestType, rpcId, NetErrorCode.ERR_NotFoundActor);
|
||||
return response;
|
||||
}
|
||||
|
||||
async Task Timeout()
|
||||
{
|
||||
await WaitAsync(TIMEOUT_TIME);
|
||||
|
||||
if (!MailBox.RemoveActorRpcResponse(rpcId, out MessageSenderStruct action))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//超时
|
||||
IResponse timeOutResponse = MessageHelper.CreateResponse(requestType, rpcId, NetErrorCode.ERR_Timeout);
|
||||
action.SetResult(timeOutResponse);
|
||||
}
|
||||
|
||||
_ = Timeout();
|
||||
|
||||
long beginTime = TimeInfo.Instance.FrameTime;
|
||||
|
||||
response = await messageSenderStruct.Wait();
|
||||
long endTime = TimeInfo.Instance.FrameTime;
|
||||
long costTime = endTime - beginTime;
|
||||
if (costTime > 200)
|
||||
{
|
||||
Debug.Warning($"actor rpc time > 200: {costTime} {requestType.FullName}");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private uint GetRpcId()
|
||||
{
|
||||
return ++RpcId;
|
||||
}
|
||||
|
||||
#region ITimerManager
|
||||
|
||||
public Task WaitTillAsync(long tillTIme)
|
||||
{
|
||||
return TimerManager.WaitTillAsync(tillTIme);
|
||||
}
|
||||
|
||||
public Task WaitFrameAsync()
|
||||
{
|
||||
return TimerManager.WaitFrameAsync();
|
||||
}
|
||||
|
||||
public Task WaitAsync(long time)
|
||||
{
|
||||
return TimerManager.WaitAsync(time);
|
||||
}
|
||||
|
||||
public long NewOnceTimer(long tillTime, Action<object> action, object args = null)
|
||||
{
|
||||
return TimerManager.NewOnceTimer(tillTime, action, args);
|
||||
}
|
||||
|
||||
public long NewFrameTimer(Action<object> action, object args = null)
|
||||
{
|
||||
return TimerManager.NewFrameTimer(action, args);
|
||||
}
|
||||
|
||||
public long NewRepeatedTimer(long time, Action<object> action, object args = null)
|
||||
{
|
||||
return TimerManager.NewRepeatedTimer(time, action, args);
|
||||
}
|
||||
|
||||
public bool Remove(ref long id)
|
||||
{
|
||||
return TimerManager.Remove(ref id);
|
||||
}
|
||||
|
||||
#endregion ITimerManager
|
||||
|
||||
#region ICoroutineLockManager
|
||||
|
||||
public async Task<CoroutineLock> Wait(int coroutineLockType, long key, int time = 600000)
|
||||
{
|
||||
return await CoroutineLockManager.Wait(coroutineLockType, key, time);
|
||||
}
|
||||
|
||||
#endregion ICoroutineLockManager
|
||||
|
||||
#region IMailBox
|
||||
|
||||
public abstract MailBoxType MailBoxType { get; }
|
||||
|
||||
public virtual int HandleMaxMessageCount => 200;
|
||||
|
||||
public virtual void RegisterHandle()
|
||||
{
|
||||
}
|
||||
|
||||
public void RegisterMessageHandle(Type type, ActorHandle handle)
|
||||
{
|
||||
MailBox.RegisterMessageHandle(type, handle);
|
||||
}
|
||||
|
||||
public void RegisterRPCHandle(Type type, ActorRpcHandle rpcHandle)
|
||||
{
|
||||
MailBox.RegisterRPCHandle(type, rpcHandle);
|
||||
}
|
||||
|
||||
#endregion IMailBox
|
||||
}
|
||||
}
|
||||
109
ServerCore/Actor/Base/ActorId.cs
Normal file
109
ServerCore/Actor/Base/ActorId.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using System;
|
||||
|
||||
namespace ActorCore
|
||||
{
|
||||
/// <summary>
|
||||
/// ActorID
|
||||
/// </summary>
|
||||
public struct ActorId
|
||||
{
|
||||
/// <summary>
|
||||
/// 模块Id
|
||||
/// </summary>
|
||||
public ProcessInfo Process;
|
||||
|
||||
/// <summary>
|
||||
/// ActorType 类型
|
||||
/// </summary>
|
||||
public byte ActorTypeId;
|
||||
|
||||
public byte ModuleId => Process.ModuleId;
|
||||
|
||||
public byte NodeNum => Process.NodeNum;
|
||||
|
||||
public ActorId(ProcessInfo process, byte actorTypeId)
|
||||
{
|
||||
this.Process = process;
|
||||
this.ActorTypeId = actorTypeId;
|
||||
}
|
||||
|
||||
public ActorId(byte moduleId, byte nodeNum, byte actorTypeId)
|
||||
{
|
||||
this.Process = new ProcessInfo(moduleId, nodeNum);
|
||||
ActorTypeId = actorTypeId;
|
||||
}
|
||||
|
||||
public int GetChannelId()
|
||||
{
|
||||
return ModuleId * 10 + NodeNum;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{ModuleId}:{NodeNum}:{ActorTypeId}";
|
||||
}
|
||||
|
||||
public static bool operator == (ActorId left, ActorId right)
|
||||
{
|
||||
return left.Process == right.Process && left.ActorTypeId == right.ActorTypeId;
|
||||
}
|
||||
|
||||
public static bool operator !=(ActorId left, ActorId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ModuleId * 1000 + NodeNum * 100 + ActorTypeId;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ProcessInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 模块Id
|
||||
/// </summary>
|
||||
public byte ModuleId;
|
||||
|
||||
/// <summary>
|
||||
/// 节点编号
|
||||
/// </summary>
|
||||
public byte NodeNum;
|
||||
|
||||
public ProcessInfo(byte moduleId, byte nodeNum)
|
||||
{
|
||||
ModuleId = moduleId;
|
||||
NodeNum = nodeNum;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{ModuleId}:{NodeNum}";
|
||||
}
|
||||
|
||||
public bool Equals(ProcessInfo other)
|
||||
{
|
||||
return this.ModuleId == other.ModuleId && this.NodeNum == other.NodeNum;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is ProcessInfo)
|
||||
{
|
||||
return Equals((ProcessInfo)obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool operator == (ProcessInfo left, ProcessInfo right)
|
||||
{
|
||||
return left.NodeNum == right.NodeNum && left.ModuleId == right.ModuleId;
|
||||
}
|
||||
|
||||
public static bool operator != (ProcessInfo left, ProcessInfo right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
}
|
||||
128
ServerCore/Actor/Base/ActorManager.cs
Normal file
128
ServerCore/Actor/Base/ActorManager.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using MrWu.Debug;
|
||||
using Server.Core;
|
||||
|
||||
namespace ActorCore
|
||||
{
|
||||
public enum SchedulerType
|
||||
{
|
||||
Main = 0,
|
||||
Thread,
|
||||
ThreadPool,
|
||||
Max
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actor 管理器
|
||||
/// </summary>
|
||||
public class ActorManager : Singleton<ActorManager>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 当前进程信息
|
||||
/// </summary>
|
||||
public ProcessInfo ProcessInfo
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前的Actor
|
||||
/// </summary>
|
||||
[ThreadStatic]
|
||||
public static IActor Current;
|
||||
|
||||
private IScheduler[] _schedulers = new IScheduler[(int)SchedulerType.Max];
|
||||
|
||||
private ConcurrentDictionary<int, IActor> _actors = new ConcurrentDictionary<int, IActor>();
|
||||
|
||||
private MainThreadScheduler _mainThreadScheduler;
|
||||
|
||||
private List<int> actorIds = new List<int>();
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
_mainThreadScheduler = new MainThreadScheduler(this);
|
||||
_schedulers[(int)SchedulerType.Main] = _mainThreadScheduler;
|
||||
_schedulers[(int)SchedulerType.Thread] = new ThreadScheduler(this);
|
||||
_schedulers[(int)SchedulerType.ThreadPool] = new ThreadPoolScheduler(this);
|
||||
}
|
||||
|
||||
public void Init(ProcessInfo processInfo)
|
||||
{
|
||||
this.ProcessInfo = processInfo;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
this._mainThreadScheduler.Update();
|
||||
}
|
||||
|
||||
public void LateUpdate()
|
||||
{
|
||||
this._mainThreadScheduler.LateUpdate();
|
||||
}
|
||||
|
||||
protected override void Destroy()
|
||||
{
|
||||
foreach (var scheduler in _schedulers)
|
||||
{
|
||||
scheduler.Dispose();
|
||||
}
|
||||
|
||||
foreach (var kv in _actors)
|
||||
{
|
||||
kv.Value.Dispose();
|
||||
}
|
||||
|
||||
_actors.Clear();
|
||||
}
|
||||
|
||||
public void RegisterActor(IActor actor)
|
||||
{
|
||||
if (!_actors.TryAdd(actor.Id,actor))
|
||||
{
|
||||
Debug.Error($"actor already existed, actorTypeId:{actor.Id}");
|
||||
return;
|
||||
}
|
||||
|
||||
ActorMessageQueue.Instance.AddQueue(actor.Id);
|
||||
actor.IsRegister = true;
|
||||
_schedulers[(int)actor.SchedulerType].Add(actor.Id);
|
||||
actor.RegisterHandle();
|
||||
_actorIds[actor.ActorId.ActorTypeId] = actor.ActorId;
|
||||
actorIds.Add(actor.Id);
|
||||
}
|
||||
|
||||
public IActor Get(int actorTypeId)
|
||||
{
|
||||
this._actors.TryGetValue(actorTypeId, out IActor actor);
|
||||
return actor;
|
||||
}
|
||||
|
||||
public int Count()
|
||||
{
|
||||
return this._actors.Count;
|
||||
}
|
||||
|
||||
public IEnumerable<int> GetActorIds()
|
||||
{
|
||||
return actorIds;
|
||||
}
|
||||
|
||||
private Dictionary<int,ActorId> _actorIds = new Dictionary<int, ActorId>();
|
||||
|
||||
public ActorId GetActorId(int actorTypeId)
|
||||
{
|
||||
if (_actorIds.TryGetValue(actorTypeId,out ActorId actorId))
|
||||
{
|
||||
return actorId;
|
||||
}
|
||||
|
||||
return default(ActorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
77
ServerCore/Actor/Base/ActorMessageQueue.cs
Normal file
77
ServerCore/Actor/Base/ActorMessageQueue.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using MrWu.Debug;
|
||||
using NetWorkMessage;
|
||||
using Server.Core;
|
||||
|
||||
namespace ActorCore
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前进程的消息队列,也可以给进程内其他Actor发送消息
|
||||
/// </summary>
|
||||
public class ActorMessageQueue : Singleton<ActorMessageQueue>
|
||||
{
|
||||
/// <summary>
|
||||
/// 接收到的Actor消息暂存在这里,等待Actor进行处理
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<int, ConcurrentQueue<MessageInfo>> m_actorMessageQueue =
|
||||
new ConcurrentDictionary<int, ConcurrentQueue<MessageInfo>>();
|
||||
|
||||
/// <summary>
|
||||
/// 取出待处理消息
|
||||
/// </summary>
|
||||
/// <param name="actorTypeId"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <param name="list"></param>
|
||||
public void Fetch(int actorTypeId, int count, List<MessageInfo> list)
|
||||
{
|
||||
if (!this.m_actorMessageQueue.TryGetValue(actorTypeId,out var queue))
|
||||
{
|
||||
Debug.Error($"没有这个ActorType:{actorTypeId}");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (!queue.TryDequeue(out MessageInfo message))
|
||||
{
|
||||
break;
|
||||
}
|
||||
list.Add(message);
|
||||
}
|
||||
|
||||
//Debug.Log($"拿走后还剩多少:{queue.Count}");
|
||||
}
|
||||
|
||||
public void AddQueue(int actorTypeId)
|
||||
{
|
||||
var queue = new ConcurrentQueue<MessageInfo>();
|
||||
this.m_actorMessageQueue[actorTypeId] = queue;
|
||||
}
|
||||
|
||||
public void RemoveQueue(int actorTypeId)
|
||||
{
|
||||
this.m_actorMessageQueue.TryRemove(actorTypeId, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 给内部Actor发包
|
||||
/// </summary>
|
||||
/// <param name="fromActorId"></param>
|
||||
/// <param name="actorId"></param>
|
||||
/// <param name="messageObject"></param>
|
||||
/// <returns></returns>
|
||||
public bool Send(ActorId fromActorId,ActorId actorId,MessageObject messageObject)
|
||||
{
|
||||
if (!m_actorMessageQueue.TryGetValue(actorId.ActorTypeId,out var queue))
|
||||
{
|
||||
Debug.Error($"发送失败,没有这个Actor! {actorId}");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Debug.Info($"添加到内部消息:{fromActorId} {actorId}");
|
||||
queue.Enqueue(new MessageInfo(){FormActorId = fromActorId,MessageObject = messageObject});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
ServerCore/Actor/Base/ActorTypeId.cs
Normal file
31
ServerCore/Actor/Base/ActorTypeId.cs
Normal file
@ -0,0 +1,31 @@
|
||||
namespace ActorCore
|
||||
{
|
||||
public static class ActorTypeId
|
||||
{
|
||||
/// <summary>
|
||||
/// 主Actor 主线程用
|
||||
/// </summary>
|
||||
public const int Main = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 内网Actor,用来给内网模块通讯
|
||||
/// </summary>
|
||||
public const int NetInner = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 外网Actor, 用来发送给外网模块
|
||||
/// </summary>
|
||||
public const int OuterNet = 2;
|
||||
|
||||
/// <summary>
|
||||
/// 用户网络Actor 处理用户的包
|
||||
/// </summary>
|
||||
public const int UserNet = 3;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏中心用户统计
|
||||
/// </summary>
|
||||
public const int GameCenterUserStatics = 4;
|
||||
|
||||
}
|
||||
}
|
||||
65
ServerCore/Actor/Base/IActor.cs
Normal file
65
ServerCore/Actor/Base/IActor.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using NetWorkMessage;
|
||||
using Server.Core;
|
||||
|
||||
namespace ActorCore
|
||||
{
|
||||
/// <summary>
|
||||
/// IActor
|
||||
/// </summary>
|
||||
public interface IActor : IDisposable
|
||||
{
|
||||
ActorId ActorId
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否被Manager管理
|
||||
/// </summary>
|
||||
bool IsRegister
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
bool IsDisposed
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
SchedulerType SchedulerType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 由ActorManager管理,其他地方不可以设置
|
||||
/// </summary>
|
||||
int Id
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 线程同步上下文
|
||||
/// </summary>
|
||||
ThreadSynchronizationContext ThreadSynchronizationContext
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
void RegisterHandle();
|
||||
|
||||
void IUpdate();
|
||||
|
||||
void ILateUpdate();
|
||||
|
||||
/// <summary>
|
||||
/// 发送消息
|
||||
/// </summary>
|
||||
/// <param name="actorId"></param>
|
||||
/// <param name="message"></param>
|
||||
void Send(ActorId actorId, IMessage message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user