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:
2026-07-07 12:02:15 +08:00
commit e9616125ce
958 changed files with 158203 additions and 0 deletions

View File

@ -0,0 +1,308 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Server.DB.Redis;
using System.Collections.Concurrent;
using MrWu.Debug;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using StackExchange.Redis;
using Server.Pack;
using Server.MQ;
using GameData;
namespace Server {
/// <summary>
/// 缓存管理
/// </summary>
public class CaCheManager {
private CaCheManager() {
}
static CaCheManager() {
instance = new CaCheManager();
}
/// <summary>
/// 实例
/// </summary>
public static CaCheManager instance {
get;
private set;
}
private IDatabase DB {
get {
return redisManager.GetDataBase(DbStyle.main, RedisDictionary.cachedb);
}
}
/// <summary>
/// 缓 key 对应 缓存的key Tuple('版本','数据')
/// </summary>
ConcurrentDictionary<string, Tuple<long, string>> caches = new ConcurrentDictionary<string, Tuple<long, string>>();
/// <summary>
/// 获取缓存
/// </summary>
/// <returns></returns>
public Tuple<long, string> GetCache(string key) {
Tuple<long, string> result = null;
if (!caches.TryGetValue(key, out result)) { //缓存没有值
result = UpdateKey(key);
}
if (result == null && !caches.TryGetValue(key, out result)) {
Debug.Warning("没这个缓存数据:" + key);
}
Debug.Log("获取到缓存!" + result);
return result;
}
public Task<Tuple<long, string>> UpdateKeyAsync(string key)
{
return Task.Run(() => UpdateKey(key));
}
/// <summary>
/// 更新缓存_也就是把Redis中的数据读取到内存中来
/// </summary>
/// <param name="key"></param>
public Tuple<long, string> UpdateKey(string key) {
Debug.Info("更新缓存" + key);
var type = redisManager.Getdb(RedisDictionary.cachedb).KeyType(key);
Tuple<long, string> result = null;
Tuple<long, string> value = null;
switch (type) {
case StackExchange.Redis.RedisType.Hash:
value = GetHash(key); //转换成 json ver,data={字典} 字典传输出去
break;
case StackExchange.Redis.RedisType.String:
value = GetString(key); //转换成 json ver,data={字符串}
break;
case StackExchange.Redis.RedisType.List:
value = GetList(key); //转换成 json ver,data={数组}
break;
}
if (!string.IsNullOrEmpty(key)) {
result = caches.AddOrUpdate(key, value, (k, v) => value);
}
//Debug.Info($"得到缓存:{value}");
return result;
}
/// <summary>
/// 获取hash
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
Tuple<long, string> GetHash(string key) {
var tran = DB.CreateTransaction();
var keyvaluestask = tran.HashGetAllAsync(key); //拿数据
var valuestask = tran.StringGetAsync(key + ":ver"); //拿版本
if (tran.Execute()) {
if (keyvaluestask.Result == null || keyvaluestask.Result.Length == 0)
return null;
var keyvalues = keyvaluestask.Result;
long value = -1;
string verResult = valuestask.Result;
if (verResult != null && !long.TryParse(verResult, out value)) {
Debug.Warning("未获取到版本号!" + key);
value = -1;
}
JObject obj = new JObject();
int len = keyvalues.Length;
for (int i = 0; i < len; i++) {
obj[keyvalues[i].Name] = JsonPack.GetData<JObject>(keyvalues[i].Value);
}
//Debug.Info("获取到缓存:" + key + ":" + verResult);
JObject baseobj = new JObject();
baseobj["ver"] = verResult;
baseobj["data"] = obj;
return new Tuple<long, string>(value, JsonPack.GetJson(baseobj));
} else
return null;
}
/// <summary>
/// 获取字符串
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
Tuple<long, string> GetString(string key) {
var tran = DB.CreateTransaction();
var valuetask = tran.StringGetAsync(key);
var vertask = tran.StringGetAsync(key + ":ver");
if (tran.Execute()) {
long ver = -1;
string verResult = vertask.Result;
if (verResult != null && !long.TryParse(verResult, out ver)) {
Debug.Warning("未获取到版本号!" + key);
ver = -1;
}
JObject baseobj = new JObject();
baseobj["ver"] = ver;
baseobj["data"] = (string)valuetask.Result;
return new Tuple<long, string>(ver, JsonPack.GetJson(baseobj));
} else
return null;
}
/// <summary>
/// 获取列表
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
Tuple<long, string> GetList(string key) {
var tran = DB.CreateTransaction();
var valuestask = tran.ListRangeAsync(key);
var vertask = tran.StringGetAsync(key + ":ver");
if (tran.Execute()) {
var values = valuestask.Result;
if (values == null || values.Length == 0)
return null;
long ver = -1;
string verResult = vertask.Result;
if (verResult != null && !long.TryParse(verResult, out ver)) {
Debug.Warning("未获取到版本号!" + key);
ver = -1;
}
JArray jar = new JArray();
int len = values.Length;
for (int i = 0; i < len; i++) {
jar.Add(JsonPack.GetData<JObject>(values[i]));
}
JObject baseobj = new JObject();
baseobj["ver"] = ver;
baseobj["data"] = jar;
return new Tuple<long, string>(ver, JsonPack.GetJson(baseobj));
} else {
return null;
}
}
/// <summary>
/// 检查版本是否达到了最大
/// </summary>
/// <param name="verKey">版本key</param>
/// <param name="task">增加版本的任务</param>
private void CheckVerMax(string verKey, Task<long> task) {
if (task.Result > 9999999999) {
DB.StringSet(verKey, 1);
}
}
/// <summary>
/// 删除缓存
/// </summary>
/// <param name="key"></param>
public bool DelCache(string key) {
var tran = DB.CreateTransaction();
tran.KeyDeleteAsync(key);
tran.KeyDeleteAsync(key + ":ver");
return tran.Execute();
}
/// <summary>
/// 设Hash缓存数据
/// </summary>
/// <param name="key"></param>
/// <param name="field"></param>
/// <param name="value"></param>
public void SetHash(string key, string field, string value) {
var tran = DB.CreateTransaction();
tran.HashSetAsync(key, field, value);
string verKey = key + ":ver";
var taskVer = tran.StringIncrementAsync(verKey);
Debug.Info($"保存缓存数据:{field} {value}");
if (tran.Execute()) {
Debug.Log("保存Hash缓存成功!" + value);
CheckVerMax(verKey, taskVer);
} else {
Debug.Error("保存配置失败!");
}
}
/// <summary>
/// 删除缓存的Hash字段
/// </summary>
/// <param name="key"></param>
/// <param name="field"></param>
public void DelHashField(string key, string field) {
var tran = DB.CreateTransaction();
tran.HashDeleteAsync(key, field);
string verKey = key + ":ver";
var taskVer = tran.StringIncrementAsync(verKey);
if (tran.Execute()) {
Debug.Log("删除Hash子段成功!");
CheckVerMax(verKey, taskVer);
} else {
Debug.Error("删除配置失败!");
}
}
/// <summary>
/// 设置字符串类型的缓存
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void SetString(string key, string value) {
var tran = DB.CreateTransaction();
tran.StringSetAsync(key, value);
string verKey = key + ":ver";
var taskVer = tran.StringIncrementAsync(verKey);
if (tran.Execute()) {
Debug.Log("设置string缓存成功!" + value);
CheckVerMax(verKey, taskVer);
} else {
Debug.Error("保存配置失败!");
}
}
/*
* 列表形式的缓存 下次再拓展
*
*
* */
/// <summary>
/// 发送缓存变化包
/// </summary>
/// <param name="key"></param>
public static void SendCacheChange(string key) {
PackHead head = PackHead.Create(ServerPackAgreement.CacheChange);
head.otherString = new string[] { key };
Debug.Log("发送更新缓存包:");
GlobalMQ.instance.DeliveryAll(GlobalMQ.CreateMessage(head, null), true);
head.Dispose();
}
}
}

View File

@ -0,0 +1,846 @@
/*
* 由SharpDevelop创建。
* 用户: Administrator
* 日期: 2018-09-13
* 时间: 17:14
*
* 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
*/
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using GameData;
using MrWu.Debug;
using MrWu.RabbitMQ;
using Server.Data.Module;
using Server.DB.Redis;
using Server.DB.Sql;
using Server.MQ;
using Server.Pack;
using Server.Config;
using Server.Core;
using Server.Net;
namespace Server
{
/// <summary>
/// 全局管理
/// </summary>
public abstract partial class GlobalManager : ITimerProcessor, IServer
{
/// <summary>
/// 是否需要重新加载配置
/// </summary>
public bool IsReloadTable;
/// <summary>
/// 服务器单元工厂
/// </summary>
public static ServerUtilFactory Factory;
private static GlobalManager mInstance;
/// <summary>
/// 每个服务器只能管理一个服务,单例模式
/// </summary>
public static GlobalManager instance
{
get
{
if (mInstance == null)
mInstance = Factory.GetGlobalManager();
return mInstance;
}
}
#region
/// <summary>
/// 是否自杀
/// </summary>
public bool isKill { get; private set; }
/// <summary>
/// 是否可以开战,关闭服务器前先禁止,然后再关闭服务器
/// </summary>
public bool IsCanWar = true;
/// <summary>
/// 客户端要得到的配置
/// </summary>
public ConfigHead ClientConfig;
/// <summary>
/// 服务器信息
/// </summary>
protected ServerInfo m_serverinfo;
/// <summary>
/// 服务器信息
/// </summary>
public virtual ServerInfo serverinfo
{
get
{
if (m_serverinfo == null)
m_serverinfo = new ServerInfo();
return m_serverinfo;
}
}
#endregion
/// <summary>
/// 获取配置头
/// </summary>
/// <returns></returns>
protected virtual ConfigHead GetConfigHead()
{
ConfigHead cfh = new ConfigHead();
cfh.style = 1;
cfh.id = ServerConfigManager.Instance.NodeId;
cfh.num = ServerConfigManager.Instance.NodeNum;
cfh.maxSVer = ServerConfigManager.Instance.MaxVer;
cfh.minSVer = ServerConfigManager.Instance.MinVer;
cfh.maxCver = ServerConfigManager.Instance.MaxCVer;
cfh.minCver = ServerConfigManager.Instance.MinCVer;
return cfh;
}
/// <summary>
/// 设置系统时间间隔
/// </summary>
/// <param name="t"></param>
[DllImport("winmm")]
protected static extern void timeBeginPeriod(int t);
/// <summary>
/// 恢复系统时间间隔
/// </summary>
/// <param name="t"></param>
[DllImport("winmm")]
protected static extern void timeEndPeriod(int t);
/// <summary>
/// 定时器管理
/// </summary>
private TimerManager timermgr;
/// <summary>
/// 更新服务器信息的时间
/// </summary>
public int updateServerInfo_time;
/// <summary>
/// 更新服务器信息
/// </summary>
protected virtual void UpdateServerInfo(bool isStart = false)
{
timeBeginPeriod(1);
serverinfo.moduleId = myUtil.id;
serverinfo.nodenum = myUtil.nodeNum;
if (isStart)
serverinfo.startTime = DateTime.Now;
serverinfo.serverName = ServerName;
serverinfo.dopackCnt = 0;
serverinfo.minfps = minfps;
serverinfo.fps = Fps;
serverinfo.maxRuntime = maxruntiming;
serverinfo.ConnectCnt = UserSessionsInfo.GetAllSessionCount();
serverinfo.BindConnectCnt = UserSessionsInfo.GetBindSessionCount();
updateServerInfo_time = ServerConfigManager.Instance.UpdateServerInfoTime;
}
/// <summary>
/// 更新
/// </summary>
protected virtual void Update()
{
TimeInfo.Instance.Update();
// long key = Debug.StartTiming();
GameNetDispatcher.Instance.Update();
GlobalDispatcher.Instance.Update();
GameDispatcher.Instance.Update();
// double runtime = Debug.GetRunTime(key);
// if (runtime > 200)
// {
// Debug.Error($"Update: 处理慢:{runtime}");
// }
}
/// <summary>
/// 启动数据
/// </summary>
/// <returns></returns>
protected virtual bool Init()
{
return true;
}
private void DayChange()
{
GameDispatcher.Instance.Dispatch(GameMsgId.DayChange);
}
/// <summary>
/// fps计数
/// </summary>
private int fpscount = -1;
/// <summary>
/// 帧率
/// </summary>
public int Fps { get; protected set; }
private int m_minfps = -1;
/// <summary>
/// 最小fps
/// </summary>
public int minfps
{
get { return m_minfps; }
protected set { m_minfps = value; }
}
/// <summary>
/// 最大运行时间
/// </summary>
public double maxruntiming { get; protected set; }
private System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
/// <summary>
/// 发送配置包裹
/// </summary>
protected virtual void SendConfigPack()
{
}
/// <summary>
/// 处理模块死亡信号
/// </summary>
/// <param name="node"></param>
/// <param name="nodenum"></param>
protected void DoModuleDieSign(object param)
{
if (param is ServerNode serverNode)
{
Debug.Info("模块死亡包处理!");
PackHead head = PackHead.Create();
head.packlx = ServerPackAgreement.ModuleDie;
Message msg = GlobalMQ.CreateMessage(head, serverNode);
GlobalMQ.instance.AddMessage(msg);
head.Dispose();
}
}
/// <summary>
/// 发送开启节点包
/// </summary>
protected virtual void SendOpenNode()
{
//注册模块
PackHead head = PackHead.Create();
head.packlx = ServerPackAgreement.openNode;
var data = this.myType != ModuleType.WatchDog ? MonitoringAppInfo : null;
GlobalMQ.instance.DeliveryAll(GlobalMQ.CreateMessage(head, data), true);
head.Dispose();
}
/// <summary>
/// 发送关闭节点包
/// </summary>
protected void SendCloseNode()
{
PackHead head = PackHead.Create();
head.packlx = ServerPackAgreement.closeNode;
var data = this.myType != ModuleType.WatchDog ? MonitoringAppInfo : null;
GlobalMQ.instance.DeliveryAll(GlobalMQ.CreateMessage(head, data), true);
head.Dispose();
}
//自身主机信息应该是不变的。 暂存这个信息
MonitoringAppInfo MonitoringAppInfo =>
MonitoringAppInfo.GetCurrentApplicateInfo(this.myUtil.onlyId, this.myUtil.id, this.myUtil.nodeNum);
/// <summary>
/// 退出游戏
/// </summary>
public virtual void Quit()
{
state = ServerState.Stoping;
}
#region ITimerProcessor
/// <summary>
/// 100毫秒定时器
/// </summary>
public virtual void OnTime(int Interval)
{
//Debug.Log("秒级定时器:" + "," + System.DateTime.Now.Ticks);
}
/// <summary>
/// 记录天
/// </summary>
int Day = 0;
/// <summary>
/// 秒级定时器
/// </summary>
public virtual void SecondTime()
{
updateServerInfo_time--;
if (updateServerInfo_time == 0)
{
UpdateServerInfo();
}
DoFps();
if (Day != DateTime.Now.Day)
{
Day = DateTime.Now.Day;
DayInit();
}
}
#endregion
/// <summary>
/// 每一天初始化一次
/// </summary>
public virtual void DayInit()
{
}
/// <summary>
/// 处理Fps的赋值
/// </summary>
protected virtual void DoFps()
{
if (minfps < 0 || minfps > fpscount)
{
minfps = fpscount;
//Debug.Warning("fps最低记录:" + minfps);
}
Fps = fpscount;
fpscount = 0;
}
/// <summary>
/// 缓存变化
/// </summary>
/// <param name="msg"></param>
protected virtual void CacheChange(WaitBeMsg msg)
{
var head = msg.head;
if (head.otherString == null || head.otherString.Length == 0)
return;
Debug.Info("处理缓存更新!" + head.otherString[0]);
CaCheManager.instance.UpdateKeyAsync(head.otherString[0]);
}
/// <summary>
/// 处理其他
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
protected virtual bool HandleOtherServerPack(WaitBeMsg msg) => false;
/// <summary>
/// 处理模块死亡信号——意外死亡
/// </summary>
/// <param name="msg"></param>
protected virtual void ModuleDie(WaitBeMsg msg)
{
var data = JsonPack.GetData<ServerNode>(msg.jsondata);
if (data == null)
Debug.Error("数据错误!");
else
Debug.Warning("模块死亡信号:" + data.id + "," + data.nodeNum);
}
/// <summary>
///
/// </summary>
protected virtual void Ping(PackHead head, string jsondata)
{
ModuleUtile util = JsonPack.GetData<ModuleUtile>(jsondata);
if (util == null)
Debug.Error("收到网络监测包数据为空!");
head.packlx = ServerPackAgreement.Pong;
GlobalMQ.instance.DeliveryOne(new ServerNode(util.id, util.nodeNum), GlobalMQ.CreateMessage(head, myUtil),
true);
Debug.Info("发送探测包:" + myUtil.ToString());
}
/// <summary>
///
/// </summary>
/// <param name="head"></param>
/// <param name="jsondata"></param>
protected virtual void Pong(PackHead head, string jsondata)
{
ModuleUtile util = JsonPack.GetData<ModuleUtile>(jsondata);
if (util == null)
Debug.Error("收到网络探测回包数据为空!");
else
Debug.Info("Pong : " + util.ToString());
}
/// <summary>
/// 心跳处理包
/// </summary>
/// <param name="msg">消息</param>
protected virtual void Heart(WaitBeMsg msg)
{
PackHead head = msg.head;
ServerNode node = head.sendutil;
if (node == null)
{
Debug.Error("心跳包错误,没有发送者的描述!");
return;
}
if (node.Equals(myUtil)) //自己的包不处理
return;
//ServerHeart.instanece.DoHeartAsync(node.id, node.nodeNum);
}
/// <summary>
/// 节点开启信号
/// </summary>
/// <param name="msg">消息</param>
[System.Diagnostics.DebuggerNonUserCode]
protected virtual void RecvOpenNode(WaitBeMsg msg)
{
PackHead head = msg.head;
ModuleUtile node = head.sendutil;
if (node == null)
{
Debug.Error("节点开启信号包错误,没有发送者的描述!");
return;
}
if (node.Equals(myUtil))
{
return;
}
//Debug.Info($"接收到模块开启:{node.ToString()}");
RountingManager.instance.AddModule(node.id, node.name);
if (node.id == (int)ModuleType.ConfigModule)
{
SendConfigPack();
}
if (head.otherString == null || head.otherString.Length == 0 || head.otherString[0] != "ok")
{
head.packlx = ServerPackAgreement.openNode;
head.otherString = new string[] { "ok" };
var data = this.myType != ModuleType.WatchDog ? MonitoringAppInfo : null;
var sendmsg = GlobalMQ.CreateMessage(head, data, null, -1, -1, null, null);
GlobalMQ.instance.DeliveryOne(node, sendmsg, true);
}
ServerHeart.instanece.AddModule(node.id, node.name, node.nodeNum);
}
/// <summary>
/// 节点关闭信号_正常关闭
/// </summary>
/// <param name="msg">消息</param>
protected virtual void RecvCloseNode(WaitBeMsg msg)
{
ModuleUtile node = msg.head.sendutil;
if (node == null)
{
Debug.Error("收到节点关闭信号,没有发送者的描述!");
return;
}
if (node.Equals(myUtil))
{
//是本模块
if (node.onlyId != myUtil.onlyId)
{
return;
}
if (state == ServerState.Runing)
{
//检测到已经死了
//直接退出
Debug.Error("收到死完包!直接退出!");
state = ServerState.Error;
return;
}
else
return;
}
//模块死亡信号
ServerHeart.instanece.RemoveDie(node.id, node.nodeNum, true);
Debug.Warning("收到模块关闭信号:" + node);
}
/// <summary>
/// 处理命令
/// </summary>
protected virtual void DoCommand(WaitBeMsg msg)
{
string[] cmds = msg.head.otherString;
if (cmds == null || cmds.Length <= 0)
return;
Debug.Info("收到命令" + cmds[0]);
switch (cmds[0])
{
case "exit":
Quit();
break;
case "test":
break;
case "onlineModules": //在线的所有模块
Debug.Log("所有的在线模块");
break;
case "redislocktest":
break;
}
}
/// <summary>
/// DistributedLock
/// </summary>
protected DistributedLock dtl { get; } = new DistributedLock(1000);
#region IServer
/// <summary>
/// 服务器状态
/// </summary>
public ServerState state { get; set; } = ServerState.Close;
/// <summary>
/// 服务器名称
/// </summary>
public string ServerName
{
get
{
return ServerConfigManager.Instance.ServerName;
}
}
/// <summary>
/// 我的模块单元
/// </summary>
public ModuleUtile myUtil { get; protected set; }
private ModuleType mMyType;
/// <summary>
/// 模块类型
/// </summary>
public ModuleType myType
{
get { return mMyType; }
}
/// <summary>
/// 消息处理器
/// </summary>
public MessageProcessor msgProcessor;
/// <summary>
/// 其他配置初始化
/// </summary>
public virtual void OtherConfigInit()
{
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 50; i++)
{
stringBuilder.Append('0');
}
if (ServerConfigManager.Instance.GameConfig != null)
{
ServerConfigManager.Instance.GameConfig.GameGz = stringBuilder.ToString();
if (ServerConfigManager.Instance.TingCnt > 0)
{
foreach (var ting in ServerConfigManager.Instance.Tings)
{
ting.TingGz = stringBuilder.ToString();
}
}
}
}
/// <summary>
/// 检查配置表是否有问题
/// </summary>
/// <returns></returns>
public virtual bool CheckConfig()
{
return true;
}
/// <summary>
/// 启动
/// </summary>
public bool Start()
{
//启动中
state = ServerState.Starting;
mMyType = (ModuleType)ServerConfigManager.Instance.NodeId;
myUtil = new ModuleUtile(ServerConfigManager.Instance.NodeId,
ServerConfigManager.Instance.NodeNum,
Enum.GetName(myType.GetType(), myType),
ServerConfigManager.Instance.MaxVer);
TimeInfo.Instance.DayChange += DayChange;
//序列化预热
PreLoadMessagePack();
GlobalDispatcher.Instance.Dispatch(GlobalMsgId.GameNameReadSuccess, ServerConfigManager.Instance.ServerName);
Debug.Info($"Start ServerName:{ServerConfigManager.Instance.ServerName}");
//数据库初始化
GameDB.Instance.Init(SqlConfigCategory.Instance.GetDBSqlConfig());
//redis启动
redisManager.Start();
ClientConfig = GetConfigHead();
//存储自己的模块信息
if (!redisManager.db.HashExists(RedisDictionary.moduleNames, ServerConfigManager.Instance.NodeId.ToString()))
redisManager.db.HashSet(RedisDictionary.moduleNames, ServerConfigManager.Instance.NodeId, myUtil.name);
//心跳开始,并检查模块是否在线
if (!ServerHeart.instanece.Start(myType, ServerConfigManager.Instance.NodeNum))
{
Debug.ImportantLog("启动失败,模块在线!");
return false;
}
MessagePool.tag = "" + myUtil.id + "-" + myUtil.nodeNum + "-" + myUtil.ver + "-" + myUtil.name;
//模块死亡事件监听
GlobalDispatcher.Instance.AddListener(GlobalMsgId.ModuleNodeDie, DoModuleDieSign);
//创建消息处理
Factory.CreateGlobalMQFactory().CreateGlobalMQ();
//启动rabbit
if (!GlobalMQ.instance.StartModule(RabbitConfigCategory.Instance.GetRabbitMQConfig(), myUtil))
{
Debug.Error("启动失败:A71134F4-EA97-44C0-A305-E35966622FEB");
return false;
}
msgProcessor = Factory.CreateMessageProcessor();
new UserSessionsManager().Register();
//重要数据启动
if (!Init())
{
return false;
}
Debug.ImportantLog("init over!!!");
//发送开启信号
SendOpenNode();
//定时器管理
timermgr = new TimerManager(ServerConfigManager.Instance.OnTime);
timermgr.Start();
//更改状态为运行中
state = ServerState.Runing;
//更新服务器信息
UpdateServerInfo(true);
//开始发送心跳包
ServerHeart.instanece.BeginHeart(Factory.CreateHeartPack(myUtil));
return true;
}
private StringBuilder logs = new StringBuilder();
private int GcCount = 0;
/// <summary>
/// 预热 MessagePack
/// </summary>
protected virtual void PreLoadMessagePack()
{
}
private void ReLoadTable()
{
TableManager.Instance.ReLoadTableData();
try
{
ReLoadTableInit();
Debug.ImportantLog("配置重新加载成功!");
}
catch (Exception e)
{
Debug.Error("重载配置出错!" + e.Message);
TableManager.Instance.RollBack();
}
}
protected virtual void ReLoadTableInit()
{
}
/// <summary>
/// 主线程
/// </summary>
public void Loop()
{
if (state == ServerState.Runing)
{
try
{
logs.Clear();
watch.Restart();
//watch.Restart();
if (IsReloadTable)
{
IsReloadTable = false;
ReLoadTable();
}
if (timermgr.GetMillScoend())
{
#if DEBUG
var key = Debug.StartTiming();
logs.AppendLine($"GlobalManager Loop DoTime begin");
#endif
OnTime(timermgr.Interval);
#if DEBUG
logs.AppendLine($"GlobalManager Loop DoTime end, time: {Debug.GetRunTime(key)}");
#endif
}
if (timermgr.GetSecoend())
{
#if DEBUG
var key = Debug.StartTiming();
logs.AppendLine($"GlobalManager Loop DoTimeSecond begin");
#endif
SecondTime();
#if DEBUG
logs.AppendLine($"GlobalManager Loop DoTimeSecond end, time: {Debug.GetRunTime(key)}");
#endif
}
//消息处理
msgProcessor.Run(logs);
#if DEBUG
var updateKey = Debug.StartTiming();
#endif
fpscount++;
Update();
#if DEBUG
logs.AppendLine($"Update Loop time:{Debug.GetRunTime(updateKey)}");
#endif
watch.Stop();
double runtime = watch.ElapsedMilliseconds; //.ElapsedMilliseconds;
if (runtime >= 100)
{
logs.AppendLine(
$"GlobalManager Loop end, now: {DateTime.Now}, runtime: {runtime} / {watch.Elapsed.TotalMilliseconds}");
Debug.Error($"Loop runtime warning: " + logs.ToString());
}
if (runtime > maxruntiming)
maxruntiming = runtime;
}
catch (Exception e)
{
Debug.Error("主线程错误:" + e.ToString());
}
}
else if (state == ServerState.Stoping)
{
Close();
}
}
/// <summary>
/// 阻塞检查,直到正常退出
/// </summary>
public virtual void Close()
{
UUIDManager.Instance.Close();
UserSessionsManager.Instance.Dispose();
timermgr.Stop();
SendCloseNode();
ServerHeart.instanece.Close();
//ServerManager.instance.Close();
if (GlobalMQ.instance != null)
GlobalMQ.instance.Close();
//保存道具日志
PropManager.Instance.Dispose();
state = ServerState.Stop;
Debug.Log("模块正常退出!");
}
#endregion
/// <summary>
/// 收到获取在线人数的请求
/// </summary>
/// <param name="msg"></param>
protected virtual void ReportPlayerOnLineData(WaitBeMsg msg)
{
}
}
}

View File

@ -0,0 +1,57 @@
using System;
using Server.Data.Module;
using GameData;
namespace Server
{
/// <summary>
/// 服务管理
/// </summary>
public interface IServer
{
/// <summary>
/// 服务器状态
/// </summary>
ServerState state {
get;
set;
}
/// <summary>
/// 服务器名称
/// </summary>
string ServerName {
get;
}
/// <summary>
/// 模块单元
/// </summary>
ModuleUtile myUtil {
get;
}
/// <summary>
/// 模块类型
/// </summary>
ModuleType myType {
get;
}
/// <summary>
/// 服务器启动
/// </summary>
bool Start();
/// <summary>
/// 主进程
/// </summary>
void Loop();
/// <summary>
/// 服务器关闭
/// </summary>
void Close();
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Server
{
/// <summary>
/// 定时器处理者
/// </summary>
public interface ITimerProcessor
{
/// <summary>
/// 定时器
/// </summary>
/// <param name="Interval">定时器时间间隔 单位毫秒</param>
void OnTime(int Interval);
/// <summary>
/// 秒级定时器
/// </summary>
void SecondTime();
}
}

View File

@ -0,0 +1,96 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
/// <summary>
/// JsonExtensions
/// </summary>
public static class JsonExtensions {
/// <summary>
/// TryGetValue
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jobj"></param>
/// <param name="propertyName"></param>
/// <param name="valueIfnotexist"></param>
/// <returns></returns>
public static T GetValueOrDefault<T>(this JObject jobj, string propertyName, T valueIfnotexist = default(T)) {
if (jobj.TryGetValue(propertyName, out var value)) {
return value.ToObject<T>();
}
return valueIfnotexist;
}
/// <summary>
/// TryGetValue
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jobj"></param>
/// <param name="propertyName"></param>
/// <param name="tokenType"></param>
/// <param name="valueIfnotexist"></param>
/// <returns></returns>
public static T GetValueOrDefault<T>(this JObject jobj, string propertyName, JTokenType tokenType, T valueIfnotexist = default(T)) {
if (jobj.TryGetValue(propertyName, out var value) && value.Type == tokenType) {
return value.ToObject<T>();
}
return valueIfnotexist;
}
/// <summary>
/// TryGetValue
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jobj"></param>
/// <param name="propertyName"></param>
/// <param name="tokenType"></param>
/// <param name="outValue"></param>
/// <returns></returns>
public static bool TryGetValue<T>(this JObject jobj, string propertyName, JTokenType tokenType, out T outValue) {
outValue = default(T);
if (jobj.TryGetValue(propertyName, out var value) && value.Type == tokenType) {
outValue = value.ToObject<T>();
return true;
}
return false;
}
/// <summary>
///
/// </summary>
/// <param name="instance"></param>
/// <returns></returns>
public static JToken ToJson(this object instance) => JToken.FromObject(instance);
/// <summary>
///
/// </summary>
/// <param name="instance"></param>
/// <returns></returns>
public static string ToJsonString(this object instance) => JToken.FromObject(instance).ToString(Formatting.None);
/// <summary>
/// parse json string then Deserialze to object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str"></param>
/// <returns></returns>
public static T ParseJsonToObject<T>(this string str) => JToken.Parse(str).ToObject<T>();
/// <summary>
///
/// </summary>
/// <param name="instance"></param>
/// <returns></returns>
public static JObject ToJObject(this object instance) => JObject.FromObject(instance);
/// <summary>
///
/// </summary>
/// <param name="instances"></param>
/// <returns></returns>
public static JArray ToJArray(this IEnumerable instances) => JArray.FromObject(instances);
}

View File

@ -0,0 +1,93 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using ActorCore;
using NetWorkMessage;
using Server.Net;
namespace Server
{
public class MainActor : Actor
{
public override SchedulerType SchedulerType => SchedulerType.Main;
public override MailBoxType MailBoxType => MailBoxType.UnOrderedMessage;
public static ActorId MainActorId
{
get;
private set;
}
protected virtual bool IsOrderMessage => true;
public MainActor(byte moduleId, byte nodeNum) : base(moduleId, nodeNum, ActorTypeId.Main)
{
MainActorId = new ActorId(moduleId, nodeNum, ActorTypeId.Main);
}
public override void RegisterHandle()
{
this.RegisterMessageHandle(typeof(ClientMessage),OuterMessageHandle);
this.RegisterMessageHandle(typeof(UserPropChangeMessage),OnUserPropChangeMessage);
this.RegisterMessageHandle(typeof(UserCurrencyChangeMessage),OnUserCurrencyChangeMessage);
this.RegisterRPCHandle(typeof(RpcTestRequest),OnRpcTestRequest);
MessageDispatcher.Instance.Start(IsOrderMessage,CoroutineLockManager);
}
private Task OuterMessageHandle(ActorId fromActorId,IMessage message)
{
if (message is ClientMessage clientMessage)
{
GameMessageDispatcher.Instance.Dispatch(clientMessage.OpCode, clientMessage);
}
return Task.CompletedTask;
}
private Task OnUserPropChangeMessage(ActorId fromActorId,IMessage message)
{
if (message is UserPropChangeMessage userPropChangeMessage)
{
GameDispatcher.Instance.Dispatch(GameMsgId.UserPropChange,userPropChangeMessage);
}
return Task.CompletedTask;
}
private Task OnUserCurrencyChangeMessage(ActorId formActorId, IMessage message)
{
if (message is UserCurrencyChangeMessage userCurrencyChangeMessage)
{
GameDispatcher.Instance.Dispatch(GameMsgId.UserCurrencyChange,userCurrencyChangeMessage);
}
return Task.CompletedTask;
}
private Task OnRpcTestRequest(ActorId fromActorId,IRequest request,IResponse response)
{
if (request is RpcTestRequest rpcTestRequest && response is RpcTestResponse rpcTestResponse)
{
rpcTestResponse.Id = rpcTestRequest.Id;
}
return Task.CompletedTask;
}
protected override void Update()
{
if (UserSessionManager.Instance != null)
{
UserSessionManager.Instance.Update();
}
//处理客户端消息
MessageDispatcher.Instance.Handle();
}
public override void Dispose()
{
base.Dispose();
}
}
}

View File

@ -0,0 +1,226 @@
using System;
using System.Text;
using Server.MQ;
using MrWu.Debug;
using Server.Pack;
using Server.Data.Module;
using GameData;
using Server.Config;
namespace Server
{
public partial class GlobalManager
{
/// <summary>
/// 消息处理器
/// </summary>
public class MessageProcessor
{
private GlobalManager instance
{
get { return GlobalManager.instance; }
}
/// <summary>
/// 处理包的数量
/// </summary>
public long dopackCnt { get; protected set; }
private long DopackRunKey = 0;
/// <summary>
/// 消息处理
/// </summary>
public void Run(StringBuilder logs)
{
int cnt = GlobalMQ.instance.GteMessageCnt();
int dealCnt = 0;
while (dealCnt < cnt)
{
if (dealCnt > 500)
{
Debug.Error("处理的包太多");
break;
}
dealCnt++;
var msg = GlobalMQ.instance.GetMessage();
if (msg == null)
{
break;
}
if (TestValue.IsPrintLog)
{
DopackRunKey = Debug.StartTiming();
}
DoMessage(msg, logs);
if (!msg.isAdd && msg.AutoAck) //不循环处理 并且不是梳理完自动确认
WaitBeMsgPool.PutWaiteBeMsg(ref msg);
if (DopackRunKey > 0)
{
double runtime = Debug.GetRunTime(DopackRunKey);
DopackRunKey = 0;
if (runtime > 1000)
{
Debug.ImportantLog($"DoMessage 慢:{runtime}");
}
}
}
}
/// <summary>
/// 处理包
/// </summary>
protected void DoMessage(WaitBeMsg wbm, StringBuilder logStringBuilder)
{
dopackCnt++;
#if DEBUG
var key = Debug.StartTiming();
#endif
try
{
wbm.isAdd = false;
if (wbm.head.packlx < 0)
{
#if DEBUG
logStringBuilder.AppendLine($"DoPackBase begin, packlx: {wbm.head.packlx}");
#endif
DoPackBase(wbm, logStringBuilder);
}
else
{
#if DEBUG
logStringBuilder.AppendLine($"DoPack begin, packlx: {wbm.head.packlx}");
#endif
DoPack(wbm, logStringBuilder);
}
}
catch (Exception e)
{
#if DEBUG
logStringBuilder.AppendLine($"DoMessage Exception: {e}");
#endif
Debug.Error("处理吧包发生错误!" + e.ToString());
wbm.Ack();
return;
}
finally
{
#if DEBUG
logStringBuilder.AppendLine($"finally, time: {Debug.GetRunTime(key)}");
#endif
}
if (wbm.isAdd)
GlobalMQ.instance.AddMessage(wbm);
else
{
if (wbm.AutoAck && !wbm.noAck && !wbm.isAck && wbm.deliveryTag > 0)
wbm.Ack();
}
//Debug.Info("直到处理用的时间:" + (DateTime.Now - wbm.logTime).TotalMilliseconds);
}
/// <summary>
/// 服务器基本消息_分发处理
/// </summary>
/// <param name="msg"></param>
/// <param name="logStringBuilder"></param>
/// <returns></returns>
protected virtual bool DoPackBase(WaitBeMsg msg, StringBuilder logStringBuilder)
{
switch (msg.head.packlx)
{
case ServerPackAgreement.command:
instance.DoCommand(msg);
break;
case ServerPackAgreement.nodeHeart: //节点心跳
instance.Heart(msg);
break;
case ServerPackAgreement.openNode: //节点开启
instance.RecvOpenNode(msg);
break;
case ServerPackAgreement.closeNode: //节点正常关闭
instance.RecvCloseNode(msg);
break;
case ServerPackAgreement.KillMe: //关闭自身 -只能发一个包
instance.isKill = true;
break;
case ServerPackAgreement.NoStartWar: //禁止开战
Debug.Info("接收到禁止开战包!");
instance.IsCanWar = false;
break;
case ServerPackAgreement.CacheChange:
Debug.Info("接收到缓存变化包!");
instance.CacheChange(msg);
break;
case ServerPackAgreement.Ping:
instance.Ping(msg.head, msg.jsondata);
break;
case ServerPackAgreement.Pong:
instance.Pong(msg.head, msg.jsondata);
break;
case ServerPackAgreement.ModuleDie: //模块检测死亡包
instance.ModuleDie(msg);
break;
case ServerPackAgreement.PackTest: //包测试
break;
case ServerPackAgreement.ReportPlayerOnLineDataReq:
instance.ReportPlayerOnLineData(msg);
break;
default:
return instance.HandleOtherServerPack(msg); // false;
}
return true;
}
/// <summary>
/// 处理模块包裹
/// </summary>
/// <param name="msg">路由</param>
/// <param name="logStringBuilder"></param>
protected virtual void DoPack(WaitBeMsg msg, StringBuilder logStringBuilder)
{
if (!CheckClientVer(msg.head))
return;
}
/// <summary>
/// 检查客户端版本
/// </summary>
/// <param name="head"></param>
protected virtual bool CheckClientVer(PackHead head)
{
if (head.packlx != GamePackAgreement.TransFerHttp &&
head.packlx != GamePackAgreement.TransFerPack &&
head.clientVer < ServerConfigManager.Instance.MinCVer)
{
head.transfer = GamePackAgreement.MandatoryUpdate;
var md = head.Util;
if (md == null)
return true;
if (md.id == (int)ModuleType.HttpModule)
head.packlx = ServerPackAgreement.TransFerHttp;
else
head.packlx = ServerPackAgreement.TransFerPack;
GlobalMQ.instance.DeliveryOne(md, GlobalMQ.CreateMessage(head), true);
return false;
}
return true;
}
}
}
}

View File

@ -0,0 +1,148 @@
/********************************
*
* 作者:徐贞卫
* 创建时间: 2019-10-16 13:38:05
*
********************************/
using System;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
namespace Server {
/// <summary>
/// 监控服务应用信息
/// </summary>
public class MonitoringAppInfo : IEquatable<MonitoringAppInfo> {
static DateTime unixbase { get; } = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime();
/// <summary>
/// 网卡硬件地址
/// </summary>
public string[] MacAddress { get; set; }
/// <summary>
/// 系统已启动时间(单位:秒)
/// </summary>
public int SysStartTime { get; set; }
/// <summary>
/// 进程PID
/// </summary>
public int Pid { get; set; }
/// <summary>
/// 服务应用完整路径
/// </summary>
public string FullFileName { get; set; }
/// <summary>
/// 模块ID标识符
/// </summary>
public string UniqueId { get; set; }
/// <summary>
/// 模块ID
/// </summary>
public int ModuleId { get; set; }
/// <summary>
/// 节点编号
/// </summary>
public int NodeNumber { get; set; }
/// <summary>
/// GetListViewItem
/// </summary>
/// <returns>GetListViewItem</returns>
public System.Windows.Forms.ListViewItem GetListViewItem() {
if(viewItem == null) {
viewItem = new System.Windows.Forms.ListViewItem(
new string[] { ModuleId.ToString(), NodeNumber.ToString(), UniqueId, FullFileName }) { Tag = this };
}
return viewItem;
}
private System.Windows.Forms.ListViewItem viewItem;
/// <summary>
/// 当前应用信息
/// </summary>
/// <param name="uniqueId">模块ID标识符</param>
/// <param name="moduleId">模块ID</param>
/// <param name="nodeNumber">节点编号</param>
/// <returns>当前进程的应用信息</returns>
public static MonitoringAppInfo GetCurrentApplicateInfo(string uniqueId, int moduleId, int nodeNumber) {
//时间戳
var now = (int)DateTime.Now.Subtract(unixbase).TotalSeconds;
//获取当前进程
var process = Process.GetCurrentProcess();
var mainModule = process.MainModule;
var macAddress = GetNetcardMacaddress();
var sysStartTime = now - (Environment.TickCount / 1000);
var pId = process.Id;
var fullFileName = mainModule.FileName;
return new MonitoringAppInfo {
MacAddress = macAddress,
SysStartTime = sysStartTime,
Pid = pId,
FullFileName = fullFileName,
UniqueId = uniqueId,
ModuleId = moduleId,
NodeNumber = nodeNumber
};
}
/// <summary>
/// 获取本机所有工作的以太网卡物理地址
/// </summary>
/// <returns></returns>
public static string[] GetNetcardMacaddress() {
if (MacAddresss == null) {
var networkAdapters = NetworkInterface.GetAllNetworkInterfaces().Where(p => p.NetworkInterfaceType == NetworkInterfaceType.Ethernet && p.OperationalStatus == OperationalStatus.Up);
MacAddresss = networkAdapters.Select(p => p.GetPhysicalAddress()).Select(p => p.ToString()).ToArray();
}
return MacAddresss;
}
static string[] MacAddresss = null;
/// <summary>
/// 指示当前对象是否等于同一类型的另一个对象
/// </summary>
/// <param name="other">一个与此对象进行比较的对象。</param>
/// <returns>如果当前对象等于 other 参数,则为 true否则为 false。</returns>
public bool Equals(MonitoringAppInfo other) {
if(other == null) {
return false;
}
if(Pid != other.Pid) {
return false;
}
if(ModuleId != other.ModuleId) {
return false;
}
if(NodeNumber != other.NodeNumber) {
return false;
}
if(!string.Equals(FullFileName, other.FullFileName, StringComparison.InvariantCultureIgnoreCase)) {
return false;
}
return true;
}
/// <summary>
/// ToString
/// </summary>
/// <returns></returns>
public override string ToString() {
return $"模块ID:{ModuleId}, 节点ID:{NodeNumber},路径:{FullFileName}";
}
}
}

View File

@ -0,0 +1,163 @@
using GameData;
using Server.MQ;
using MrWu.Debug;
namespace Server
{
/// <summary>
/// 发包管理
/// </summary>
public static class SendManager {
/// <summary>
/// 0.不启用
/// 1.服务器维护中,请稍后再试!
/// 2.请求超时!请稍后再试!
/// 3.服务器爆满!请稍后再试!
/// 4.房间号错误,无此房间!
/// 5.进入失败!密码错误!
/// 6.房间已开战,进入失败!
/// 7.房间已满员,进入失败!
/// 8.检测到相同ip账号进入失败!
/// 9.未获取到您的ip,进入失败!
/// 10.进入失败,必须微信登录或者微信绑定才能进入此房间!
/// 11.进入失败,必须是微信绑定的账号才能进入此房间!
/// 12.房卡不足创建失败!
/// 13.数据错误,请重新登录!
/// 14.登录过期,请重新登录!
/// 15.用户名或密码为空
/// 16.用户名或密码错误
/// 17.对方不在线
/// 18.您在朋友房中,不能重复创建!
/// 19.您已经在朋友房中了!
/// 20.朋友房规则错误!
/// 21.房卡不足创建失败!
/// 22.竞技比赛名称必须是3-8个字符串组成!
/// 23.竞技比赛名称非法!
/// 24.创建失败!请稍后再试!
/// 25.创建的竞技比赛已达上限!
/// 26.竞技比赛公告过长!
/// 27.竞技比赛公告包含特殊字符!
/// 28.竞技比赛名称已被使用!
/// 29.数据错误!
/// 30.您不在此竞技比赛!
/// 31.您的权限不足!
/// 32.修改失败,公告过长或有特殊字符!
/// 33.房卡不足,创建失败!
/// 34.充值失败!
/// 35.由于管理员设置,您无法加入该游戏!
/// 36.房卡不足加入失败!
/// 37.未知错误,服务器报错!!!
/// 38.竞技比赛房间,无法手动加入!",
/// 39.操作失败,您在游戏中无法进行仓库操作!,
/// 40.仓库密码错误
/// 41.您在游戏中无法进行存取金币操作
/// 42.金币操作数额过大。
/// 43.您身上的金币不足,取出失败
/// 44.您身上的游戏币超过限额,无法取出!
/// 45.您身上的游戏币不足,存入失败
/// 46.您的金币不足,赠送失败!
/// 47.操作失败,请稍后重新发起!
/// 48.赠送的金币必须大于1
/// 49.金币不足,购买失败!
/// 50.操作失败,请稍后再试!
/// 51.卖出失败!
/// 52.不能卖出身上的物品!
/// 53.保存失败!
/// 54.玩法已被修改或者删除!
/// 55.防作弊检查,进入房间失败!
/// 56.创建失败,未检测到ip地址!
/// 57.暂时微信用户才可创建房间,绑定后再试!
/// 58.防作弊房间需开启定位才可创建!
/// 59.未获取到设备信息,请稍后再试!
/// 60.超时未准备,您已被踢出房间!
/// 61.您在别处登录游戏!
/// 62.等级不满6级不能使用该功能!
/// 63.密码修改失败!
/// 64.旧密码错误!
/// 65.验证码不正确!
/// 66.参数不正确,必须包含联系方式、登录名、登录密码及预留的联系信息!
/// 67.新密码格式不对!
/// 68.房卡不足,充值失败!
/// 69.没有这个玩家,赠送失败!
/// 70."没有这个用户,请检查手机号是否输入正确!"
/// 71."没有这个用户,请检查用户名是否正确!",
/// 72."该用户未绑定手机,请联系客服修改密码!"
/// 73."加入失败!"
/// 74."不存在这个房间!"
/// 75."解散成功!"
/// 76."已存在相同名称的战队!"
/// 77."金币不足,创建战队失败!"
/// 78.""不存在该战队!"
/// 79."超时未准备,您已被踢出房间!"
/// </summary>
/// <param name="head"></param>
/// <param name="style"></param>
/// <param name="tiptext"></param>
public static void SendErrotInfo_Http(PackHead head, int style, int tiptext) {
head.transfer = GamePackAgreement.pack_error;
head.packlx = GamePackAgreement.TransFerHttp;
TipInfo errPack = new TipInfo(style, tiptext);
ServerNode node = head.Util;
GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, errPack), true);
}
/// <summary>
/// 发送游戏币超上限提示
/// </summary>
/// <param name="userid"></param>
/// <param name="node"></param>
public static void SendMoneyUpper(int userid, ServerNode node) {
PackHead head = new PackHead();
head.userid = userid;
head.packlx = GamePackAgreement.TransFerPack;
head.transfer = GamePackAgreement.MoneyUpper;
GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head), true);
}
/// <summary>
/// 发送自定义错误提示信息
/// </summary>
/// <param name="head"></param>
/// <param name="style"></param>
/// <param name="tiptext"></param>
public static void SendErrotInfo_Http(PackHead head, int style, string tiptext) {
head.transfer = GamePackAgreement.autoTipInfo;
head.packlx = GamePackAgreement.TransFerHttp;
head.otherString = new string[] { tiptext };
ServerNode node = head.Util;
GlobalMQ.instance.DeliveryOne(node, GlobalMQ.CreateMessage(head, null), true);
}
/// <summary>
/// 发送一个在游戏中的包给客服端
/// </summary>
/// <param name="userid">userid</param>
/// <param name="game_serid">游戏服务器id</param>
/// <param name="clickgame_serid">点击的游戏服务器id</param>
/// <param name="friendRoom">朋友房房号</param>
/// <param name="style">类型 1.提示进入 2.让客户端立即进入</param>
/// <param name="head">SocketNode Socket节点</param>
public static void SendInGameInfo_Http(int userid, int game_serid, int clickgame_serid, int friendRoom, int style, PackHead head) {
if (head == null) {
Debug.Warning("SendInGameInfo_Http params error head is null");
return;
}
PlayerGameInfo pgi = new PlayerGameInfo(game_serid, friendRoom,style, clickgame_serid);
head.packlx = GamePackAgreement.TransFerHttp;
head.transfer = GamePackAgreement.InGameInfo;
head.userid = userid;
GlobalMQ.instance.DeliveryOne(head.Util, GlobalMQ.CreateMessage(head, pgi), true);
}
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Server
{
/// <summary>
/// 服务器状态
/// </summary>
public enum ServerState
{
/// <summary>
/// 关闭状态
/// </summary>
Close = 0,
/// <summary>
/// 启动中
/// </summary>
Starting = 1,
/// <summary>
/// 运行中
/// </summary>
Runing = 2,
/// <summary>
/// 停止中
/// </summary>
Stoping = 3,
/// <summary>
/// 停止
/// </summary>
Stop = 4,
/// <summary>
/// 服务器意外终止
/// </summary>
Error = 5
}
}

View File

@ -0,0 +1,65 @@
using System;
using Server.MQ;
using Server.Module;
using Server.Data;
using Server.Pack;
using Server.Data.Module;
using GameData;
using System.Xml;
using System.Collections.Generic;
namespace Server
{
/// <summary>
/// 服务单元工厂
/// </summary>
public abstract class ServerUtilFactory
{
private IGlobalFactory GlobalMQFactory;
/// <summary>
/// 创建消息管理
/// </summary>
public virtual IGlobalFactory CreateGlobalMQFactory() {
if (GlobalMQFactory == null)
GlobalMQFactory = new GlobalMQ.Factory();
return GlobalMQFactory;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public abstract GlobalManager GetGlobalManager();
/// <summary>
/// 创建一些信息使用
/// </summary>
/// <param name="ObjectName"></param>
/// <returns></returns>
public abstract Object Produce(string ObjectName);
/// <summary>
/// 创建心跳包
/// </summary>
/// <returns></returns>
public virtual string CreateHeartPack(ModuleUtile util) {
PackHead head = PackHead.Create();
head.sendutil = util;
head.packlx = ServerPackAgreement.nodeHeart;
string result = JsonPack.GetJsonByPack(head, null);
head.Dispose();
return result;
}
/// <summary>
/// 创建消息处理器
/// </summary>
/// <returns></returns>
public virtual GlobalManager.MessageProcessor CreateMessageProcessor() {
return new GlobalManager.MessageProcessor();
}
}
}

View File

@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using MrWu.Debug;
using System.Threading;
namespace Server
{
/// <summary>
/// 定时器标记管理
/// </summary>
public class TimerManager
{
/// <summary>
/// 是否触发定时器事件
/// </summary>
private volatile bool isTimerMillEvt;
/// <summary>
/// 秒级定时器
/// </summary>
private volatile bool isSecondTimeEvt;
private volatile int mInterval;
/// <summary>
/// 是否在运行中
/// </summary>
public bool isRun {
get;
private set;
}
/// <summary>
/// 触发定时器的时间间隔
/// </summary>
public int Interval {
get {
return mInterval;
}
}
/// <summary>
/// 设置定时器的时间间隔
/// </summary>
/// <param name="Interval"></param>
public void SetInterval(int Interval) {
if (Interval < 1) {
Debug.Error("不能设置这么小的事件间隔:" + Interval);
return;
}
mInterval = Interval;
}
/// <summary>
/// 构造
/// </summary>
/// <param name="Interval">定时器的时间间隔</param>
public TimerManager(int Interval) {
if (Interval < 1)
throw new ArgumentOutOfRangeException("Interval 必须为大于 1 的值!");
this.mInterval = Interval;
}
/// <summary>
/// 秒级定时器
/// </summary>
private Timer SecondTimer;
/// <summary>
/// 毫秒级定时器
/// </summary>
private Timer MillTimer;
/// <summary>
/// 开始定时器的触发
/// </summary>
public void Start() {
if (SecondTimer == null) {
SecondTimer = new Timer(SecoendTime, null, 0, 1000);
MillTimer = new Timer(MillTime, null, 0, Interval);
} else {
SecondTimer.Change(0, 1000);
MillTimer.Change(0, 100);
}
}
/// <summary>
/// 停止定时器的触发
/// </summary>
public void Stop() {
SecondTimer.Change(-1, 1000);
MillTimer.Change(-1, Interval);
}
private void SecoendTime(object state) {
isSecondTimeEvt = true;
}
private void MillTime(object state) {
isTimerMillEvt = true;
}
/// <summary>
/// 获取是否需要定时器触发
/// </summary>
/// <returns></returns>
public bool GetSecoend() {
if (isSecondTimeEvt) {
isSecondTimeEvt = false;
return true;
}
return false;
}
/// <summary>
/// 获取是否需要触发毫秒定时器
/// </summary>
/// <returns></returns>
public bool GetMillScoend() {
if (isTimerMillEvt) {
isTimerMillEvt = false;
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,282 @@
using System;
using System.Collections.Generic;
using Server.DB.Redis;
using GameData;
using MrWu.Debug;
using StackExchange.Redis;
using Server.Data.ClubDef;
using GameDAL.FriendRoom;
using System.Linq;
using System.Threading.Tasks;
using MrWu.Basic;
namespace Server {
/// <summary>
/// 玩法管理
/// </summary>
public class WayManager {
/* 玩法只需要存储桌子的唯一码具体的数据到Room 类里面拿
*
* 1.加入玩法房间
* 2.创建玩法房间
* 3.退出玩法房间
*
* 4.玩法被删除。-> 主动解散房间
*
*
*
*
* */
/// <summary>
/// 数据库Idx
/// </summary>
public const int DbIdx = 6;
/// <summary>
/// 实例
/// </summary>
public static WayManager instance {
get;
private set;
}
private WayManager() { }
static WayManager() {
instance = new WayManager();
}
/// <summary>
/// 数据库代理
/// </summary>
public IDatabaseProxy databaseProxy {
get {
return redisManager.Getdb(DbIdx);
}
}
/// <summary>
/// redis数据事务操作
/// </summary>
public IDatabase db {
get {
return redisManager.GetDataBase(DbStyle.main, DbIdx);
}
}
/// <summary>
/// 基本Key
/// </summary>
public const string BaseKey = "ClubWay:";
/// <summary>
/// 获取桌子信息的Key值
/// </summary>
/// <param name="clubid">竞技比赛id</param>
/// <param name="wayid">玩法id</param>
public string GetDeskInfoKey(int clubid, string wayid) {
return $"{BaseKey}{clubid}:{wayid}";
}
/// <summary>
///保存玩法的桌子
/// </summary>
/// <param name="clubid">竞技比赛id</param>
/// <param name="wayid">玩法id</param>
/// <param name="weiyima">唯一码</param>
public void SaveWayDesk(int clubid, string wayid, string weiyima) {
string key = GetDeskInfoKey(clubid, wayid);
//暂时 Hash 的 Value 不存放数据, 没啥可以存的。留给以后扩展
databaseProxy.HashSet(key, weiyima, 0);
// WayDeskChangeSend(clubid, wayid, weiyima, 0);
}
/// <summary>
/// 删除玩法的桌子信息
/// </summary>
/// <param name="clubid"></param>
/// <param name="wayid"></param>
/// <param name="weiyima"></param>
public void DelDeskInfo(int clubid, string wayid, string weiyima) {
string key = GetDeskInfoKey(clubid, wayid);
//WayDeskChangeSend(clubid, wayid, weiyima, 1);
databaseProxy.HashDelete(key, weiyima);
}
/// <summary>
/// 删除玩法
/// </summary>
/// <param name="clubid">竞技比赛id</param>
/// <param name="wayid">玩法id</param>
public void DelWay(int clubid, string wayid) {
string key = GetDeskInfoKey(clubid, wayid);
//写这两句是要干嘛 玩法是竞技比赛发起的,发给竞技比赛多余
//var weiyimaList = GetDesksWeiYiMa(clubid, wayid).Select(p => p.Name).ToList();
//weiyimaList.ForEach(weiyima => WayDeskChangeSend(clubid, wayid, weiyima, 1));
databaseProxy.KeyDelete(key);
}
public Task<DeskInfo> LoadDeskInfoAsync(string weiyima)
{
return Task.Run(() => LoadDeskInfo(weiyima));
}
/// <summary>
/// 加载桌子信息
/// </summary>
/// <param name="weiyima">唯一码</param>
/// <returns></returns>
public DeskInfo LoadDeskInfo(string weiyima)
{
//去Room 类里面找到加载DeskInfo 方法补充起来
DeskInfo deskinfo = null;
if(!Room.LoadRoom(weiyima, out deskinfo)) {
Debug.Info("未加载成功!");
return null;
}
if(deskinfo == null) {
Debug.Info("加载成功, 却没有桌子信息???");
}
return deskinfo;
}
// /// <summary>
// /// 加载某个玩法的所有桌子信息
// /// </summary>
// /// <returns></returns>
// public List<DeskInfo> LoadDeskInfo(int clubid, string wayid) {
// string key = GetDeskInfoKey(clubid, wayid);
//
// return LoadDeskInfoInternal(key);
// }
public Task<List<DeskInfo>> LoadDeskInfoAsync(int clubId)
{
return Task.Run(() => LoadDeskInfo(clubId));
}
/// <summary>
/// 获取竞技比赛的所有桌子
/// </summary>
/// <param name="clubid"></param>
/// <returns></returns>
public List<DeskInfo> LoadDeskInfo(int clubid) {
string pattern = GetDeskInfoKey(clubid, "*");
//获取此club所有玩法的key
var clubKeys = redisManager.Keys(pattern, DbIdx, 1000,DbStyle.main);
//将当前club的所有玩法及每玩法的所有桌;
int len = clubKeys.Count;
List<DeskInfo> infos = new List<DeskInfo>();
for (int i = 0; i < len; i++) {
infos.AddRange(LoadDeskInfoInternal(clubKeys[i]));
}
return infos;
}
List<DeskInfo> LoadDeskInfoInternal(RedisKey key) {
List<DeskInfo> desks = new List<DeskInfo>();
//获取此key下所有唯一码
var weiyimaArray = databaseProxy.HashGetAll(key);
int len = weiyimaArray.Length;
DeskInfo info;
for (int i = 0; i < len; i++) {
info = LoadDeskInfo(weiyimaArray[i].Name);
if (info != null)
desks.Add(info);
}
return desks;
// return weiyimaArray.Select(weiyima => LoadDeskInfo(weiyima)).Where(deskInfo => deskInfo != null).ToList();
}
/// <summary>
/// 获取这个玩法的所有的桌子的唯一码
/// </summary>
/// <param name="clubid"></param>
/// <param name="wayid"></param>
/// <returns>唯一码 玩家人数</returns>
public HashEntry[] GetDesksWeiYiMa(int clubid, string wayid) {
return databaseProxy.HashGetAll(GetDeskInfoKey(clubid, wayid));
}
/// <summary>
/// 竞技比赛玩家变化
/// </summary>
/// <param name="clubid">竞技比赛id</param>
/// <param name="wayid">玩法id</param>
/// <param name="weiyima">桌子的唯一码</param>
/// <param name="isInc">是否是增量 true 增 false 减</param>
public ClubWayPlayer ClubWayPlayerChange(int clubid, string wayid, string weiyima, bool isInc) {
//组建ClubWayPlayer
string key = GetDeskInfoKey(clubid, wayid);
ClubWayPlayer cwp = new ClubWayPlayer();
cwp.clubid = clubid;
cwp.wayid = wayid;
cwp.weiyima = weiyima;
if (isInc)
cwp.playerCnt = (int)databaseProxy.HashIncrement(key, weiyima);
else
cwp.playerCnt = (int)databaseProxy.HashDecrement(key, weiyima);
//cwp.playerCnt = (int)databaseProxy.HashIncrement(GetDeskInfoKey(clubid, wayid), weiyima, isInc ? 1 : -1);
return cwp;
/*
ClubWayPlayer cwp = new ClubWayPlayer();
cwp.clubid = clubid;
cwp.wayid = wayid;
string key = GetKey(clubid, wayid);
var datas = redisManager.db.SortedSetRangeByRankWithScores(key, 0, -1, Order.Descending);
if (datas == null) {
cwp.deskCnt = 0;
cwp.playerCnt = 0;
} else {
int len = datas.Length;
for (int i = 0; i < len; i++) {
if (datas[i].Score < minCnt) {
cwp.playerCnt = (int)datas[i].Score;
break;
}
}
cwp.deskCnt = len;
}
return cwp;
*/
}
/// <summary>
/// 玩法的桌子变化 数据发送
/// </summary>
/// <param name="clubid">竞技比赛id</param>
/// <param name="wayid">玩法id</param>
/// <param name="weiyima">桌子的唯一码</param>
/// <param name="style">类型 0新增 1删除 2更新</param>
public void WayDeskChangeSend(int clubid, string wayid, string weiyima, int style) {
//组建 WayDeskChange 包裹发送给竞技比赛
var wdc = new Data.WayDeskChange {
clubid = clubid,
wayid = wayid,
deskweiyima = weiyima,
style = style
};
PackHead head = PackHead.Create(Pack.ServerPackAgreement.WayDeskChange);
MQ.GlobalMQ.instance.DeliveryEveryOne((int)Data.Module.ModuleType.ClubModule, MQ.GlobalMQ.CreateMessage(head, wdc), true);
head.Dispose();
}
}
}