1003 lines
34 KiB
C#
1003 lines
34 KiB
C#
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<MODULE_ID, IModule> m_registered_mudules = new();
|
|
private List<IModule> m_initialized_modules = new();
|
|
|
|
private TaskCompletionSource<bool> 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<Type, RedisRequestSharedBase> m_redis_global_shared_caches = new();
|
|
private readonly ConcurrentDictionary<Type, IRule> m_rules = new();
|
|
|
|
|
|
// 전역적으로 관리해야 하는 엔티티들의 목록 !!!
|
|
private ConcurrentDictionary<Type, EntityBase> m_global_entities = new();
|
|
// 전역적으로 관리해야 하는 EntityTicker 목록 !!!
|
|
private ConcurrentDictionary<Type, EntityTicker> 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<Result> 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<Result> 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<TModule>(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<Result> 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<Result> 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<Result> 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<Result> 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<RedisRequestSharedBase> onAppendRedisGlobalSharedCacheAll()
|
|
{
|
|
return new List<RedisRequestSharedBase>();
|
|
}
|
|
|
|
public RedisRequestSharedBase? findRedisGlobalSharedRequest<TRedisGlobalSharedRequest>()
|
|
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<Result> 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<EntityBase> onAppendGlobalEntityAll()
|
|
{
|
|
// 전역적으로 관리할 EntityBase를 상속 받은 클래스를 정의하여 List<EntityBase> 에 추가 한다.
|
|
// EntityBase.onInit() 호출이 될때 전역적 객체의 역할에 필요로 하는 기반 정보를 모두 준비할 수 있어야 한다.
|
|
// 그 결과가 실패시 서버는 종료되어야 한다 !!!
|
|
// 본 구조를 클래스 설계시 궁금한 부분이 있으면 슬랙을 통해 문의 바랍니다. !!! - kangms
|
|
|
|
return new List<EntityBase>();
|
|
}
|
|
|
|
public TEntity? findGlobalEntity<TEntity>()
|
|
where TEntity : EntityBase
|
|
{
|
|
m_global_entities.TryGetValue(typeof(TEntity), out var found_global_entity);
|
|
return found_global_entity as TEntity;
|
|
}
|
|
|
|
public bool addGlobalEntity<TEntity>(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<Result> 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<Result> onRegisterModuleAll()
|
|
{
|
|
return await Task.FromResult(new Result());
|
|
}
|
|
|
|
protected virtual List<IRule> onAppendRuleAll()
|
|
{
|
|
return new List<IRule>();
|
|
}
|
|
|
|
protected virtual async Task<Result> 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<TRule>()
|
|
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<Result> 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<Result> 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<Result> 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<Result> 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<TEntityTicker>()
|
|
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<Task>();
|
|
|
|
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<T>(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<T>(session, msg);
|
|
}
|
|
|
|
public bool onSendPacket<T>(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<T>(sessions, msg);
|
|
}
|
|
|
|
public async Task<Result> onCallProtocolHandler<T>(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<bool> onSetAccountLoginBlock(bool isBlock)
|
|
{
|
|
await Task.CompletedTask;
|
|
var block_enable = getAccountLoginBlockEnable();
|
|
return false != block_enable.set(isBlock);
|
|
}
|
|
|
|
public virtual async Task<bool> 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<bool> 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);
|
|
}
|