using DeepCore; using DeepCore.GameEvent; using DeepCore.GameEvent.Message; using DeepCore.IO; using DeepCore.Log; using DeepCore.Statistics; using DeepCore.Threading; using DeepCrystal.ORM; using DeepCrystal.RPC; using DeepCrystal.Sql; using DeepMMO.Protocol; using DeepMMO.Protocol.Client; using DeepMMO.Server.Area; using DeepMMO.Server.Connect; using DeepMMO.Server.GameEvent; using DeepMMO.Server.Logic.Model; using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; namespace DeepMMO.Server.Logic { public partial class LogicService : IService { public static int SAVE_EXPECT_TIME_LIMIT = 200; public static int LOAD_EXPECT_TIME_LIMIT = 200; public static TimeStatisticsRecoder Statistics { get; private set; } = new TimeStatisticsRecoder("LogicStatistics"); /// /// 账号ID. /// public string accountID { get; private set; } /// /// 服务器ID. /// public string serverID { get; private set; } public string serverGroupID { get; private set; } public string sessionName { get; private set; } public string sessionNode { get; private set; } public string roleID { get; private set; } public string roleDigitID { get => roleModule.GetRoleData().digitID; } public ClientInfo clientInfo { get; private set; } public IRemoteService session { get; private set; } public LanguageManager Language { get => roleModule.Language; } public EventManager EventMgr { get; private set; } public IMappingAdapter DBAdapter { get; private set; } public event Action OnSessionReconnect; public event Action OnBeforeSaveData; public event Action OnClientEntered; private IDisposable mEventTimer; private IDisposable mSaveDataTimer; public bool IsClientEntered { get; private set; } public override ServiceProperties Properties { get { var ret = base.Properties; ret.IsConcurrent = false; return ret; } } public LogicService(ServiceStartInfo start) : base(start) { this.sessionName = start.Config["sessionName"].ToString(); this.sessionNode = start.Config["sessionNode"].ToString(); this.accountID = start.Config["accountID"].ToString(); this.serverID = start.Config["serverID"].ToString(); this.roleID = start.Config["roleID"].ToString(); this.serverGroupID = start.Config["serverGroupID"].ToString(); this.clientInfo = new ClientInfo(start.Config); this.DBAdapter = ORMFactory.Instance.DefaultAdapter; } protected override async Task OnStartAsync() { var stopwatch = Stopwatch.StartNew(); try { this.session = await base.Provider.GetAsync(new RemoteAddress(sessionName, sessionNode)); this.OnCreateModules(); await this.OnModulesStartAsync(); await this.OnModulesStartedAsync(); this.EventMgr = EventManagerFactory.Instance.CreateEventManager("Player", roleID); if (EventMgr != null) { EventMgr.PutObject("Service", this); EventMgr.Start(); mEventTimer = Provider.CreateTimer(OnEventManagerTick, this, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(TimerConfig.timer_sec_EventUpdateTime)); } { //定期存数据. int interval = TimerConfig.timer_minute_SaveDataTimer; interval = Math.Max(5, interval); this.mSaveDataTimer = Provider.CreateTimer( OnFlushDataTickAsync, this, TimeSpan.FromMinutes(interval)); } if (stopwatch.ElapsedMilliseconds > LOAD_EXPECT_TIME_LIMIT) { log.Warn("LogicService : OnStartAsync Use Time = " + stopwatch.Elapsed); } } catch (Exception hzdsb) { hzdsb.PrintStackTrace(); } finally { stopwatch.Stop(); } } private void OnEventManagerTick(object state) { EventMgr?.Update(); } private Task OnFlushDataTickAsync(object state) { return this.OnModulesSaveDataAsync(); } protected override async Task OnStopAsync(ServiceStopInfo reason) { IsClientEntered = false; mSaveDataTimer?.Dispose(); await OnModulesStopAsync(); EventMgr?.Dispose(); if (reason.Event != ServiceStopInfo.ShutdownEvent.START_ERROR) { await OnModulesSaveDataAsync(); } await OnModulesStopedAsync(); } protected override void OnDisposed() { this.OnModulesDispose(); OnSessionReconnect = null; OnBeforeSaveData = null; OnClientEntered = null; mEventTimer?.Dispose(); EventMgr = null; } //--------------------------------------------------------------------------------------------------- #region Modules public RoleModule roleModule { get; protected set; } public AreaModule areaModule { get; protected set; } private List modules = new List(); protected virtual void OnCreateModules() { this.roleModule = RegistModule(new RoleModule(this)); this.areaModule = RegistModule(new AreaModule(this)); } protected T RegistModule(T module) where T : ILogicModule { modules.Add(module); return module; } private async Task OnModulesStartAsync() { using (var list = CollectionObjectPool.AllocList(modules)) { foreach (var module in list) { try { await module.OnStartAsync(); } catch (Exception err) { log.Error(ErrorBaseInfo() + err.Message, err); throw; } } } } private async Task OnModulesStartedAsync() { using (var list = CollectionObjectPool.AllocList(modules)) { foreach (var module in list) { try { await module.OnStartedAsync(); } catch (Exception err) { log.Error(err.Message, err); } } } } private async Task OnModulesSaveDataAsync() { var stopwatch = Stopwatch.StartNew(); try { OnBeforeSaveData?.Invoke(); if (ORMFactory.IsTest) { var watch_exe = CUtils.TickTimeMS; //var exe = new SyncTaskExecutor(); var trans = DBAdapter.CreateExecutableObjectTransaction(this); using (var list = CollectionObjectPool.AllocList(modules)) { foreach (var module in list) { var watch = CUtils.TickTimeMS; try { module.OnSaveData(trans); } catch (Exception err) { log.Error(err.Message, err); } finally { Statistics.LogTime($"{module.GetType().Name} : OnSaveData", CUtils.TickTimeMS - watch); } } } var test = new TestTransaction(CFiles.CurrentSubDir("/test_orm/"), this.roleID, trans, ORMFactory.Instance.DefaultAdapter, this); await trans.ExecuteAsync().ContinueWith(t => { Statistics.LogTime($"{GetType().Name} : OnModulesSaveDataAsync", CUtils.TickTimeMS - watch_exe); }); await test.CheckAsync(false); } else { var watch_exe = CUtils.TickTimeMS; var trans = DBAdapter.CreateExecutableObjectTransaction(this); using (var list = CollectionObjectPool.AllocList(modules)) { foreach (var module in list) { var watch = CUtils.TickTimeMS; try { module.OnSaveData(trans); } catch (Exception err) { log.Error(err.Message, err); } finally { Statistics.LogTime($"{module.GetType().Name} : OnSaveData", CUtils.TickTimeMS - watch); } } } await trans.ExecuteAsync().ContinueWith(t => { Statistics.LogTime($"{GetType().Name} : OnModulesSaveDataAsync", CUtils.TickTimeMS - watch_exe); }); } } finally { stopwatch.Stop(); //if (DeepCore.Log.Logger.SHOW_LOG) { if (stopwatch.ElapsedMilliseconds > SAVE_EXPECT_TIME_LIMIT) { log.Warn(" Warn LogicService : OnModulesSaveDataAsync Flush Time = " + stopwatch.Elapsed); } else { log.Debug("Debug LogicService : OnModulesSaveDataAsync Flush Time = " + stopwatch.Elapsed); } } } } private async Task OnModulesStopAsync() { using (var list = CollectionObjectPool.AllocList(modules)) { list.Reverse(); foreach (var module in list) { try { await module.OnStopAsync(); } catch (Exception err) { log.Error(err.Message, err); } } } } private async Task OnModulesStopedAsync() { using (var list = CollectionObjectPool.AllocList(modules)) { list.Reverse(); foreach (var module in list) { try { await module.OnStopedAsync(); } catch (Exception err) { log.Error(err.Message, err); } } } } private async Task NotifyModulesClientEnterGameAsync() { IsClientEntered = true; OnClientEntered?.Invoke(); using (var list = CollectionObjectPool.AllocList(modules)) { foreach (var module in list) { try { await module.OnClientEnterGameAsync(); } catch (Exception err) { log.Error(err.Message, err); } } } } private void OnModulesDispose() { using (var list = CollectionObjectPool.AllocList(modules)) { list.Reverse(); foreach (var module in list) { try { module.Dispose(); } catch (Exception err) { log.Error(err.Message, err); } } } modules.Clear(); } private void OnModulesSessionReconnect() { using (var list = CollectionObjectPool.AllocList(modules)) { foreach (var module in list) { try { module.OnSessionReconnect(); } catch (Exception err) { log.Error(err.Message, err); } } } } private void OnModulesSessionDisconnect() { using (var list = CollectionObjectPool.AllocList(modules)) { foreach (var module in list) { try { module.OnSessionDisconnect(); } catch (Exception err) { log.Error(err.Message, err); } } } } private string ErrorBaseInfo() { return string.Format("ServerID:{0}|AccountID:{1}|RoleID:{2} ", serverID, accountID, roleID); } #endregion //--------------------------------------------------------------------------------------------------- #region Area /// /// Area通知逻辑需要传送操作,一般是踩到场景传送点 /// /// [RpcHandler(typeof(RoleNeedTransportNotify), ServerNames.AreaServiceType)] public void area_rpc_Handle(RoleNeedTransportNotify tp) { var task = areaModule.RequestTransportAsync(tp); } /// /// Area通知逻辑服无缝切场景 /// /// [RpcHandler(typeof(RoleCrossMapNotify), ServerNames.AreaServiceType)] public void area_rpc_Handle(RoleCrossMapNotify notify) { areaModule.RequestCrossMapAsync(notify).NoWait(); } [RpcHandler(typeof(AreaGameOverNotify), ServerNames.AreaServiceType)] public void area_rpc_Handle(AreaGameOverNotify notify) { areaModule.DoAreaGameOverNotify(notify); } #endregion //--------------------------------------------------------------------------------------------------- #region Session /// /// 玩家断开连接 /// /// [RpcHandler(typeof(SessionDisconnectNotify), ServerNames.SessionServiceType)] public virtual void session_rpc_Handle(SessionDisconnectNotify disconnect) { var area = areaModule.currentArea; if (area != null) { disconnect.roleID = this.roleID; area.Invoke(disconnect); } this.OnModulesSessionDisconnect();; } /// /// 玩家重新连接 /// /// [RpcHandler(typeof(SessionReconnectNotify), ServerNames.SessionServiceType)] public virtual void session_rpc_Handle(SessionReconnectNotify reconnect) { this.clientInfo = new ClientInfo(reconnect.config); var area = areaModule.currentArea; if (area != null) { reconnect.roleID = this.roleID; area.Invoke(reconnect); } this.OnModulesSessionReconnect(); OnSessionReconnect?.Invoke(); } [RpcHandler(typeof(SessionBeginLeaveRequest), typeof(SessionBeginLeaveResponse), ServerNames.SessionServiceType)] public virtual void session_rpc_Handle(SessionBeginLeaveRequest disconnect, OnRpcReturn cb) { var area = areaModule.currentArea; if (area != null) { area.Call(disconnect, cb); } else { cb(new SessionBeginLeaveResponse() { s2c_code = Response.CODE_ERROR }); } } [RpcHandler(typeof(ClientEnterGameRequest), typeof(ClientEnterGameResponse), ServerNames.SessionServiceType)] public void client_rpc_Handle(ClientEnterGameRequest enter, OnRpcReturn cb) { cb(new ClientEnterGameResponse() { s2c_role = this.roleModule.ToClientRoleData() }); Provider.Execute(NotifyModulesClientEnterGameAsync); } /// /// 测试客户端Ping,Pong /// [RpcHandler(typeof(ClientPing), typeof(ClientPong))] public virtual void client_rpc_Handle(ClientPing ping, OnRpcReturn cb) { #if TEST session.Invoke(new LogicTimeNotify() { index = 0, time = ping.time }); session.Invoke(new LogicTimeNotify() { index = 1, time = ping.time }); cb(new ClientPong() { s2c_code = (ping.time.Millisecond % 2 == 0) ? Response.CODE_OK : Response.CODE_ERROR, s2c_msg = DateTime.Now.ToString(), time = ping.time }); session.Invoke(new LogicTimeNotify() { index = 2, time = ping.time }); session.Invoke(new LogicTimeNotify() { index = 3, time = ping.time }); #else cb(new ClientPong() { s2c_code = Response.CODE_OK, time = ping.time }); #endif } [RpcHandler(typeof(ServerGameEventNotify))] public virtual void rpc_event_notify(ServerGameEventNotify ntf) { if (EventMgr != null && (string.IsNullOrEmpty(ntf.ServerGroupID) || ntf.ServerGroupID == serverGroupID)) { EventMgr.OnReceiveMessage(EventMessage.FromBytes(ntf.EventMessageData)); } } [RpcHandler(typeof(ClientGameEventNotify))] public virtual void client_rpc_notify(ClientGameEventNotify ntf) { var msg = EventMessage.FromBytes(ntf.EventMessageData); if (msg is NamedEventMessage nameMsg) { nameMsg.From = EventMgr?.Address; } var address = EventManagerAddress.Parse(msg.From); address = new EventManagerAddress("Client", address.UUID); msg.From = address.Address; EventManager.MessageBroker.Publish(ntf.To, EventMgr, msg); } [RpcHandler(typeof(ClientGetZoneInfoSnapRequest), typeof(ClientGetZoneInfoSnapResponse))] public virtual Task client_rpc_Handle(ClientGetZoneInfoSnapRequest req) { return areaModule.DoClientGetZoneInfoSnapRequest(req); } [RpcHandler(typeof(ClientChangeZoneLineRequest), typeof(ClientChangeZoneLineResponse))] public virtual Task client_rpc_Handle(ClientChangeZoneLineRequest req) { return areaModule.DoClientChangeZoneLineRequest(req); } #endregion public class ClientInfo { public string Os { get; private set; } public string Network { get; private set; } public string AppVersion { get; private set; } public string AppVersionCode { get; private set; } public string OsVersion { get; private set; } public string SdkVersion { get; private set; } public string DeviceBrand { get; private set; } public string DeviceModel { get; private set; } public string DeviceScreen { get; private set; } public string Mac { get; private set; } public string Imei { get; private set; } public string Uuid { get; private set; } public string PackageName { get; private set; } public string BuildNumber { get; private set; } public string Carrier { get; private set; } public string Iccid { get; private set; } public string Imsi { get; private set; } public string Idfa { get; private set; } public string ClientIp { get; private set; } public string DeviceID { get; private set; } public string Channel { get; private set; } public string SDKName { get; private set; } public string PlatformAccount { get; private set; } public ClientInfo(HashMap config) { this.Os = config["deviceType"]?.ToString(); this.Os = SQLFactory.CurrentFactory.EscapeString(Os); this.Network = config["network"]?.ToString(); this.Network = SQLFactory.CurrentFactory.EscapeString(Network); this.AppVersion = config["clientVersion"]?.ToString(); this.AppVersion = SQLFactory.CurrentFactory.EscapeString(AppVersion); this.AppVersionCode = null; this.OsVersion = null; this.SdkVersion = config["sdkVersion"]?.ToString(); this.SdkVersion = SQLFactory.CurrentFactory.EscapeString(SdkVersion); this.DeviceBrand = null; this.DeviceModel = config["deviceModel"]?.ToString(); this.DeviceModel = SQLFactory.CurrentFactory.EscapeString(DeviceModel); this.DeviceScreen = null; this.Mac = config["deviceId"]?.ToString(); this.Mac = SQLFactory.CurrentFactory.EscapeString(Mac); this.Imei = null; this.Uuid = config["roleID"]?.ToString(); this.Uuid = SQLFactory.CurrentFactory.EscapeString(Uuid); this.PackageName = null; this.BuildNumber = null; this.Carrier = null; this.Iccid = null; this.Imsi = null; this.Idfa = config["deviceId"]?.ToString(); this.Idfa = SQLFactory.CurrentFactory.EscapeString(Idfa); this.ClientIp = config["clientIp"]?.ToString(); this.ClientIp = SQLFactory.CurrentFactory.EscapeString(ClientIp); this.DeviceID = config["deviceId"]?.ToString(); this.DeviceID = SQLFactory.CurrentFactory.EscapeString(DeviceID); this.Channel = config["channel"]?.ToString(); this.Channel = SQLFactory.CurrentFactory.EscapeString(Channel); if (string.IsNullOrEmpty(Channel)) this.Channel = "0"; this.SDKName = config["sdkName"]?.ToString(); this.SDKName = SQLFactory.CurrentFactory.EscapeString(SDKName); this.PlatformAccount = config["platformAccount"]?.ToString(); } } //--------------------------------------------------------------------------------------------------- } }