using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Collections.Concurrent; using Microsoft.Extensions.Configuration; using NLog.Config; using MySqlConnector; using Nettention.Proud; using StackExchange.Redis; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.DocumentModel; using Amazon.DynamoDBv2.Model; using Microsoft.Extensions.Options; using MongoDB.Driver; using Google.Protobuf; using ServerCore; using ServerBase; using ServerControlCenter; using MODULE_ID = System.UInt32; namespace ServerBase; public abstract partial class ServerLogicBase : IServerLogic { private ServerProgramVersion m_server_program_version = new(); private string m_server_type = string.Empty; private string m_server_name = string.Empty; private ServerConfig? m_server_config = null; private IConfigurationRoot? m_configuration_root = null; private ConfigManager? m_config_manager = null; private ConcurrentDictionary m_registered_mudules = new(); private List m_initialized_modules = new(); private TaskCompletionSource m_run_tcs = new(); private readonly CancellationTokenSource m_cancel_token = new(); private PeriodicTaskTimer? m_server_info_notify_ticker; private Task? m_server_info_notify_task_nullable; private readonly ConcurrentDictionary m_redis_global_shared_caches = new(); private readonly ConcurrentDictionary m_rules = new(); // 전역적으로 관리해야 하는 엔티티들의 목록 !!! private ConcurrentDictionary m_global_entities = new(); // 전역적으로 관리해야 하는 EntityTicker 목록 !!! private ConcurrentDictionary m_entity_tickers = new(); public string m_server_instanceid = string.Empty; public ServerLogicBase(ServerConfig config) { m_server_config = config; ServerLogicApp.setServerLogicApp(this); } public ServerLogicBase(ConfigManager configManager) { m_config_manager = configManager; ServerLogicApp.setServerLogicApp(this); } public async Task tryCreateAndRegisterModule(Func<(IModule?, Result)> toCreateAction, bool initializeImmediately = false) { var err_msg = string.Empty; var result = new Result(); (var module, result) = toCreateAction(); if(result.isFail()) { err_msg = $"Failed to toCreateAction() !!!, in tryCreateAndAddModule() : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().error(err_msg); return result; } NullReferenceCheckHelper.throwIfNull(module, () => $"module is null !!! - {toBasicString()}"); var module_id = module.getModuleContext().getModuleId(); result = await registerModule(module, initializeImmediately); if (result.isFail()) { err_msg = $"Failed to registerModule() !!!, in tryCreateAndAddModule() : {result.toBasicString()}, moduleId:{module_id} - {toBasicString()}"; Log.getLogger().error(err_msg); return result; } return result; } public async Task registerModule(IModule moduleObject, bool initializeImmediately = false) { var err_msg = string.Empty; var result = new Result(); var module_id = moduleObject.getModuleContext().getModuleId(); if (false == m_registered_mudules.TryAdd(module_id, moduleObject)) { err_msg = $"Failed to TryAdd() !!!, in registerModule(), Already registred : moduleId:{module_id} - {toBasicString()}"; result.setFail(ServerErrorCode.ModuleAlreadyRegistred, err_msg); Log.getLogger().error(result.toBasicString()); return result; } if (true == m_initialized_modules.Exists(x => x.getModuleContext().getModuleId() == module_id)) { err_msg = $"Already added initialzed Modules !!!, in registerModule() : moduleId:{module_id} - {toBasicString()}"; result.setFail(ServerErrorCode.ModuleAlreadyAddInitializeModule, err_msg); Log.getLogger().error(result.toBasicString()); return result; } m_initialized_modules.Add(moduleObject); if (true == initializeImmediately) { var initializer = moduleObject as IInitializer; NullReferenceCheckHelper.throwIfNull(initializer, () => $"initializer is null !!! - {toBasicString()}"); result = await initializer.onInit(); if (result.isFail()) { err_msg = $"Failed to Initializer.onInit() !!!, in registerModule() : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().error(err_msg); return result; } } return result; } public void unregisterModule(MODULE_ID moduleId) { var err_msg = string.Empty; var result = new Result(); if (m_registered_mudules.TryRemove(moduleId, out var removed_module)) { err_msg = $"Failed to TryRemove() !!!, in unregisterModule() : moduleId:{moduleId} - {toBasicString()}"; Log.getLogger().warn(err_msg); return; } NullReferenceCheckHelper.throwIfNull(removed_module, () => $"removed_module is null !!! - {toBasicString()}"); if (false == m_initialized_modules.Remove(removed_module)) { err_msg = $"Failed to Remove() !!!, in unregisterModule() : moduleId:{moduleId} - {toBasicString()}"; Log.getLogger().error(err_msg); } } public TModule getModule(MODULE_ID targetModuleId) where TModule : class, IModule { m_registered_mudules.TryGetValue(targetModuleId, out var found_module); NullReferenceCheckHelper.throwIfNull(found_module, () => $"found_module is null !!!, Not registered Module : moduleId:{targetModuleId} - {toBasicString()}"); var casted_module = found_module as TModule; NullReferenceCheckHelper.throwIfNull(casted_module, () => $"casted_module is null !!!, Failed to cast Module : TModule:{typeof(TModule)} - moduleId:{targetModuleId}, {toBasicString()}"); return casted_module; } //============================================================================================= // for IServerLogic.getModule() //============================================================================================= public IModule getModule(MODULE_ID targetModuleId) { m_registered_mudules.TryGetValue(targetModuleId, out var found_module); NullReferenceCheckHelper.throwIfNull(found_module, () => $"found_module is null !!!, Not registered Module : moduleId:{targetModuleId} - {toBasicString()}"); return found_module; } public async Task startModuleAll() { var result = new Result(); m_initialized_modules.OrderBy(x => { return x.getModuleContext().getSortStartOrderNo(); }); foreach (var module in m_initialized_modules) { result = await module.startModule(); if(result.isFail()) { return result; } } return result; } public async Task stopModuleAll() { var result = new Result(); m_initialized_modules.OrderBy(x => { return x.getModuleContext().getSortStopOrderNo(); }); foreach (var module in m_initialized_modules) { result = await module.stopModule(); if (result.isFail()) { return result; } } return result; } //============================================================================================= // 서버 로직을 초기화 한다 !!!, onRunServer() 내에서 호출 된다 !!! //============================================================================================= public virtual async Task onInit() { var err_msg = string.Empty; var result = new Result(); if (false == Log.isInitialized()) { err_msg = $"The log is not initialized !!!, in ServiceLogicBase.onInit() !!! - {toBasicString()}"; result.setFail(ServerErrorCode.NlogNotInitialized, err_msg); return result; } Log.getLogger().info($"Log.initLog() Start !!! : {toBasicString()}"); var with_configuration = this as IWithConfiguration; if (null != with_configuration) { result = with_configuration.mergeConfiguration(getConfiguration()); if (result.isFail()) { return result; } } var config = getServerConfig(); result = await config.tryLoadConfig(); if (result.isFail()) { return result; } DotNetDumpHelper.init(config.DumpDir, onGetDumpFilename()); result = onCreateServerName(); if (result.isFail()) { err_msg = $"Failed to onCreateServerName() !!! : {result.toBasicString()}"; Log.getLogger().fatal(err_msg); return result; } result = await onConfigureCloudEnvironment(); if (result.isFail()) { err_msg = $"Failed to onConfigureCloudEnvironment() !!! : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().fatal(err_msg); return result; } LogicThread.start(config.SingleThreaded); ServerConfigHelper.init(config); result = onLoadMetaDatas(); if (result.isFail()) { err_msg = $"Failed to onLoadMetaDatas() !!! : {result.toBasicString()}"; Log.getLogger().fatal(err_msg); return result; } result = await onRegisterModuleAll(); if (result.isFail()) { err_msg = $"Failed to onRegisterModuleAll() !!! : {result.toBasicString()}"; Log.getLogger().fatal(err_msg); return result; } var to_initializers = new Initializers(); foreach (var module in m_initialized_modules) { var initializer = module as IInitializer; if(null == initializer) { continue; } to_initializers.appendInitializer(initializer); } result = await to_initializers.init("Initialize Module All"); if (result.isFail()) { Log.getLogger().error($"Failed to Initializers.init() !!! : {result.toBasicString()} - {toBasicString()}"); return result; } result = await onInitGlobalEntities(); if (result.isFail()) { Log.getLogger().error($"Failed to onInitGlobalEntities() !!! : {result.toBasicString()} - {toBasicString()}"); return result; } result = await onInitRules(); if (result.isFail()) { Log.getLogger().error($"Failed to onInitRules() !!! : {result.toBasicString()} - {toBasicString()}"); return result; } result = onInitFirewall(); if (result.isFail()) { err_msg = $"Failed to onInitFirewall() !!! : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().fatal(err_msg); return result; } if (false == onLoadProgramVersionInfo()) { err_msg = $"Failed to onLoadProgramVersionInfo() !!! - {toBasicString()}"; result.setFail(ServerErrorCode.ProgramVersionLoadFailed, err_msg); Log.getLogger().error(result.toBasicString()); return result; } Log.getLogger().info($"Success ServiceLogicBase.onInit() - {toBasicString()}"); return result; } protected abstract Result onLoadMetaDatas(); public virtual async Task onCreateServerInfoSyncTask() { var result = new Result(); m_server_info_notify_ticker = new PeriodicTaskTimer( GetType().Name , ServerBase.Constant.SERVER_INFO_NOTIFY_INTERVAL_MSEC , m_cancel_token, onTaskServerInfoSyncTick); m_server_info_notify_task_nullable = m_server_info_notify_ticker.getTask(); Log.getLogger().info($"Success ServerLogicBase.onCreateTask() - {toBasicString()}"); return await Task.FromResult(result); } public virtual async Task onTaskServerInfoSyncTick() { await Task.CompletedTask; } protected virtual List onAppendRedisGlobalSharedCacheAll() { return new List(); } public RedisRequestSharedBase? findRedisGlobalSharedRequest() where TRedisGlobalSharedRequest : RedisRequestSharedBase { m_redis_global_shared_caches.TryGetValue(typeof(TRedisGlobalSharedRequest), out var found_redis_global_shard_request); return found_redis_global_shard_request; } protected virtual async Task onInitGlobalEntities() { var result = new Result(); var err_msg = string.Empty; var to_append_global_entities = onAppendGlobalEntityAll(); foreach (var entity in to_append_global_entities) { if (false == m_global_entities.TryAdd(entity.GetType(), entity)) { err_msg = $"Failed to TryAdd() !!!, duplcated EntityBase : duplicatedEntityBase:{entity.getTypeName()} - {toBasicString()}"; result.setFail(ServerErrorCode.RedisGlobalEntityDuplicated, err_msg); Log.getLogger().error(result.toBasicString()); return result; } } var to_init_global_entities = new Initializers(); foreach (var each in m_global_entities) { to_init_global_entities.appendInitializer(each.Value); } result = await to_init_global_entities.init("GlobalEntities"); if (result.isFail()) { err_msg = $"Failed to init() GlobalEntities !!! : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().fatal(err_msg); return result; } Log.getLogger().info($"Success ServerLogicBase.onInitGlobalEntities() - {toBasicString()}"); return result; } protected virtual List onAppendGlobalEntityAll() { // 전역적으로 관리할 EntityBase를 상속 받은 클래스를 정의하여 List 에 추가 한다. // EntityBase.onInit() 호출이 될때 전역적 객체의 역할에 필요로 하는 기반 정보를 모두 준비할 수 있어야 한다. // 그 결과가 실패시 서버는 종료되어야 한다 !!! // 본 구조를 클래스 설계시 궁금한 부분이 있으면 슬랙을 통해 문의 바랍니다. !!! - kangms return new List(); } public TEntity? findGlobalEntity() where TEntity : EntityBase { m_global_entities.TryGetValue(typeof(TEntity), out var found_global_entity); return found_global_entity as TEntity; } public bool addGlobalEntity(TEntity toAddEntityBase) where TEntity : EntityBase { return m_global_entities.TryAdd(toAddEntityBase.GetType(), toAddEntityBase); } protected virtual void onInitBusinessLog() { BusinessLogger.setup( new NLogAppender() , new JsonText() , LogTransToOutputType.TransToMultyLine ); Log.getLogger().info($"Success ServerLogicBase.onInitBusinessLog() - {toBasicString()}"); } protected virtual Result onCreateServerName() { var result = new Result(); var err_msg = string.Empty; var server_type_name = getServerType(); var server_type = server_type_name.toServerType(); if (false == server_type.isValidServerType()) { err_msg = $"ServerType invalid !!! : {server_type.ToString()}"; result.setFail(ServerErrorCode.ServerTypeInvalid, err_msg); return result; } var server_config = getServerConfig(); var listen_port = server_config.toClientListenPort(); if (listen_port <= 0) { err_msg = $"Client listen port invalid !!! : appParamPort:{listen_port}, configListenPort:{server_config.ClientListenPort} - {server_type.ToString()}"; result.setFail(ServerErrorCode.ClientListenPortInvalid, err_msg); return result; } setServerName(server_type.toServerName(server_config.toClientListenIP(), listen_port)); return result; } protected virtual async Task onConfigureCloudEnvironment() { var result = new Result(); var config = getServerConfig(); if (config.AWS.CloudWatchLog.Enable) { result = setupCloudwatchLog(config); if (result.isFail()) { return result; } } if (config.AWS.Enable) { m_server_instanceid = await AwsHelper.getAwsInstanceId(); } return result; } private Result setupCloudwatchLog(ServerConfig serverConfig) { var result = new Result(); var err_msg = string.Empty; if (false == CloudWatchLog.isOpened()) { return result; } if (false == CloudWatchLog.setup( serverConfig.AWS.CloudWatchLog.CloudWatchLogGroup , serverConfig.AWS.CloudWatchLog.CloudWatchLogNamePattern , serverConfig.AWS.CloudWatchLog.CloudWatchLogLevel , serverConfig.AWS.Region , serverConfig.AWS.AccessKey , serverConfig.AWS.SecretKey , serverConfig.AWS.CloudWatchLog.CloudWatchLogLayout)) { err_msg = $"Failed to CloudWatchLog.setup() !!! - {toBasicString()}"; result.setFail(ServerErrorCode.NlogWithAwsCloudWatchSetupFailed, err_msg); Log.getLogger().fatal(result.toBasicString()); return result; } return result; } private void onNLogConfigurationChanged(object? sender, LoggingConfigurationChangedEventArgs e) { var result = new Result(); var err_msg = string.Empty; var server_config = getServerConfig(); if (null == server_config) { // Log.shutdown() 호출시 타이밍에 따라 server_config가 null인 경우가 있다 !!! - kangms return; } if (server_config.AWS.CloudWatchLog.Enable) { result = setupCloudwatchLog(server_config); if (result.isFail()) { err_msg = $"Failed to setupCloudwatchLog() !!! in onNLogConfigurationChanged() : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().fatal(err_msg); } } } protected virtual async Task onRegisterModuleAll() { return await Task.FromResult(new Result()); } protected virtual List onAppendRuleAll() { return new List(); } protected virtual async Task onInitRules() { var result = new Result(); var err_msg = string.Empty; var to_append_rules = onAppendRuleAll(); foreach(var rule in to_append_rules) { if (false == m_rules.TryAdd(rule.GetType(), rule)) { err_msg = $"Failed to TryAdd() !!!, duplcated RuleType : duplicatedRuleType:{rule.getTypeName()} - {toBasicString()}"; result.setFail(ServerErrorCode.RuleTypeDuplicated, err_msg); Log.getLogger().error(err_msg); return result; } } var to_init_ruels = new Initializers(); foreach(var each in m_rules) { to_init_ruels.appendInitializer(each.Value); } return await to_init_ruels.init("Rules"); } public TRule? findRule() where TRule : class { if(false == m_rules.TryGetValue(typeof(TRule), out var rule)) { return null; } return rule as TRule; } protected virtual Result onInitFirewall() { var result = new Result(); var err_msg = string.Empty; var server_config = getServerConfig(); // 사용자 로그인 차단 여부를 설정 한다. (활성화:true/비활성화:false), 관제 시스템을 통해 변경 가능 하다 !!! - kangms m_account_login_block_enable.set(server_config.AccountLoginBlockEnable); return result; } protected virtual bool onLoadProgramVersionInfo() { var config = getServerConfig(); var version_paths = config.getVersionPaths(); if(version_paths == null) { var err_msg = $"Failed to getProgramVersion() !!!, not found ProgramVersion !!! - {toBasicString()}"; Log.getLogger().error(err_msg); return false; } var result = new Result(); foreach(var each in version_paths) { var version_type = each.Key; var version_file_path = each.Value; (result, var build_version, var revision) = VersionHelper.tryReadVersion(version_file_path); if(result.isFail()) { var err_msg = $"Failed to tryReadVersion() !!! : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().error(err_msg); return false; } result = m_server_program_version.appendVersion(version_type, build_version, revision); if(result.isFail()) { var err_msg = $"Failed to appendVersion() !!! : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().error(err_msg); return false; } } return true; } private void setThreadPool() { var config = getServerConfig(); System.Threading.ThreadPool.GetMinThreads(out var minWorker, out var minIOCP); Log.getLogger().info($"Application configured Thread : minWorkerThreadCount:{minWorker}, minIOCPThreadCount:{minIOCP} - {toBasicString()}"); var min_worker = config.MinWorkerThreadCount <= 0 ? minWorker : config.MinWorkerThreadCount; var min_ioc = config.MinIOCThreadCount <= 0 ? minIOCP : config.MinIOCThreadCount; if (false == System.Threading.ThreadPool.SetMinThreads(min_worker, min_ioc)) { Log.getLogger().error($"Failed to SetMinThreads() : minWorkerThreadCount:{min_worker}, minIOCPThreadCount:{min_ioc} - {toBasicString()}"); return; } Log.getLogger().info($"Success Change Thread Count : minWorkerThreadCount:{min_worker}, minIOCPThreadCount:{min_ioc} - {toBasicString()}"); } //============================================================================================= // 서버 로직을 시작 한다. !!! //============================================================================================= public virtual async Task onRunServer() { var result = new Result(); var err_msg = string.Empty; setServerStopEvent(); Log.initLog(getServerName(), "Developer", onNLogConfigurationChanged); onInitBusinessLog(); result = await onInit(); if (result.isFail()) { Log.getLogger().error($"Failed to ServerLogicBase.onInit() !!!, in onRunServer() : {result.toBasicString()} - {toBasicString()}"); return result; } setThreadPool(); result = await startModuleAll(); if (result.isFail()) { return result; } var server_Metrics = this as IWithServerMetrics; if (null != server_Metrics) { result = await server_Metrics.setupServerMetrics(); if (result.isFail()) { return result; } } result = await onCreateTickerAll(); if (result.isFail()) { Log.getLogger().error($"Failed to ServerLogicBase.onCreateTickerAll() !!!, in onRunServer() : {result.toBasicString()} - {toBasicString()}"); return result; } result = await onStartServer(); if(result.isFail()) { Log.getLogger().error($"Failed to ServerLogicBase.onStartServer() !!!, in onRunServer() : {result.toBasicString()} - {toBasicString()}"); return result; } var running_task = onRunning(); if (running_task == null) { err_msg = $"Failed to ServiceLogicBase.onRunning() !!! - {toBasicString()}"; result.setFail(ServerErrorCode.ServerOnRunningFailed, err_msg); Log.getLogger().error(result.toBasicString()); return result; } await running_task; return result; } protected virtual Task? onRunning() { return m_run_tcs?.Task; } private void setServerStopEvent() { //===================================================================================== // SIGINT 콘솔 시그널 발생시 (프로세스 종료) : 서버 종료 처리 등록 //===================================================================================== Console.CancelKeyPress += async (s, e) => { await stopServerByExitEvent(); }; //===================================================================================== // Process 종료시 : 서버 종료 처리 등록 //===================================================================================== AppDomain.CurrentDomain.ProcessExit += async (s, e) => { await stopServerByExitEvent(); }; } protected virtual async Task onStartServer() { var err_msg = string.Empty; var result = new Result(); var server_Metrics = this as IWithServerMetrics; if(null != server_Metrics) { await onCreateServerInfoSyncTask(); } Log.getLogger().info($"Running Server : {getServerName()} - {toBasicString()}"); await NamedPipeMonitor.ChangeServerStatus(ServerStatus.Running); return result; } public virtual async Task onStopServer() { Log.getLogger().info("Call onStopServer() !!!"); var server_Metrics = this as IWithServerMetrics; if (null != server_Metrics) { if(true == server_Metrics.isSetupCompleted()) { var server_metrics_request = server_Metrics.getServerMetricsCacheRequest(); NullReferenceCheckHelper.throwIfNull(server_metrics_request, () => $"server_metrics_request is null - {toBasicString()}"); var handler_type = (uint)ServerMetricsCacheRequest.TriggerType.ServerMetrics_RemoveByServerName; await server_metrics_request.tryRunTriggerHandler(handler_type, getServerName()); } } await NamedPipeMonitor.StopNamedPipeService(); await stopModuleAll(); m_cancel_token.Cancel(); Log.getLogger().info("onStopServer() - stopEntityTickerAll() !!!"); await stopEntityTickerAll(); if (null != m_server_info_notify_task_nullable) { m_server_info_notify_task_nullable.Wait(); } Log.getLogger().info("onStopServer() - m_server_info_notify_task_nullable.Wait() !!!"); m_run_tcs?.SetResult(true); Log.getLogger().info("onStopServer finish !!!"); } private async Task stopServerByExitEvent() { await onStopServer(); LogicThread.join(); } public virtual async Task onInitServerKeyToCache(int worldId) { var result = new Result(); var err_msg = string.Empty; var server_name = getServerName(); var server_type = getServerType().toServerType(); var server_Metrics = this as IWithServerMetrics; if (null != server_Metrics) { var server_metrics_request = server_Metrics.getServerMetricsCacheRequest(); NullReferenceCheckHelper.throwIfNull(server_metrics_request, () => $"server_metrics_request is null - {toBasicString()}"); var handler_type = (uint)ServerMetricsCacheRequest.TriggerType.ServerMetrics_Init; var with_result_value = await server_metrics_request.tryRunTriggerHandler(handler_type, server_name, server_type, worldId); if (with_result_value.Result.isFail()) { err_msg = $"Failed to tryRunTriggerHandler() !!! : handlerType:{handler_type}, {with_result_value.Result.toBasicString()} - {toBasicString()}"; Log.getLogger().error(err_msg); return with_result_value.Result; } } return result; } public virtual async Task onCreateTickerAll() { await Task.CompletedTask; Log.getLogger().warn($"No Ticker object has been created !!!, in onCreateTickerAll() - {toBasicString()}"); return new Result(); } public Result registerEntityTicker(EntityTicker entityTicker) { var result = new Result(); var err_msg = string.Empty; if (false == m_entity_tickers.TryAdd(entityTicker.GetType(), entityTicker)) { err_msg = $"Already registered EntityTicker !!!"; result.setFail(ServerErrorCode.EntityTickerAlreadyRegistered, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return result; } public Result unregisterEntityTicker(EntityTicker entityTicker) { var result = new Result(); var err_msg = string.Empty; if (false == m_entity_tickers.TryRemove(entityTicker.GetType(), out var removed_entity_ticker)) { err_msg = $"Not found EntityTicker !!! : {entityTicker.toBasicString()}"; result.setFail(ServerErrorCode.EntityTickerNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return result; } public TEntityTicker? findEntityTicker() where TEntityTicker : EntityTicker { var result = new Result(); var err_msg = string.Empty; var type = typeof(TEntityTicker); if (false == m_entity_tickers.TryGetValue(type, out var found_entity_ticker)) { err_msg = $"Not found EntityTicker !!! : {type.Name}"; result.setFail(ServerErrorCode.EntityTickerNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return null; } return found_entity_ticker as TEntityTicker; } protected Task stopEntityTickerAll() { var waiting_tasks = new List(); foreach(var each in m_entity_tickers) { var timer = each.Value.getPeriodicTaskTimer(); if(null == timer) { continue; } timer.cancelTimer(); waiting_tasks.Add(timer.getTask()); } Log.getLogger().info("onStopServer - stopEntityTickerAll() - start !!!"); Task.WaitAll(waiting_tasks.ToArray()); Log.getLogger().info("onStopServer - stopEntityTickerAll() - end !!!"); return Task.CompletedTask; } public bool onSendPacket(IEntityWithSession session, T msg) where T : class, IMessage { var listen_session_base = this.getListenSessionBase(); NullReferenceCheckHelper.throwIfNull(listen_session_base, () => $"listen_session_base is null !!! - {toBasicString()}"); return listen_session_base.onSendPacket(session, msg); } public bool onSendPacket(IEntityWithSession[] sessions, T msg) where T : class, IMessage { var listen_session_base = this.getListenSessionBase(); NullReferenceCheckHelper.throwIfNull(listen_session_base, () => $"listen_session_base is null !!! - {toBasicString()}"); return listen_session_base.onSendPacket(sessions, msg); } public async Task onCallProtocolHandler(IEntityWithSession session, T recvProtocol) where T : Google.Protobuf.IMessage { var listen_session_base = session.getListenSessionBase(); NullReferenceCheckHelper.throwIfNull(listen_session_base, () => $"listen_session_base is null !!! - {toBasicString()}"); return await listen_session_base.onCallProtocolHandler(session, recvProtocol); } protected virtual string onGetDumpFilename() { NullReferenceCheckHelper.throwIfNull(m_server_config, () => $"m_server_config is null !!!"); return getServerName() + "_" + m_server_config.getAppParamPort(); } public string toBasicString() { return $"ServerType:{getServerType()}, ServerName:{getServerName()}"; } protected virtual async Task onSetAccountLoginBlock(bool isBlock) { await Task.CompletedTask; var block_enable = getAccountLoginBlockEnable(); return false != block_enable.set(isBlock); } public virtual async Task onBlockAccountLogin() { var is_success = await onSetAccountLoginBlock(true); if (false == is_success) { Log.getLogger().error($"Failed to block account login !!! : currBlockState:{getAccountLoginBlockEnable().Value}"); } return is_success; } public virtual async Task onCancelAccountLoginBlock() { var is_success = await onSetAccountLoginBlock(false); if (false == is_success) { Log.getLogger().error($"Failed to cancel Block account login !!! : currBlockState:{getAccountLoginBlockEnable().Value}"); } return is_success; } private AtomicBool m_account_login_block_enable = new(false); }