using Nettention.Proud; using Google.Protobuf; using Microsoft.Extensions.Configuration; using ServerCore; using ServerBase; using ServerCommon; using ServerControlCenter; using MODULE_ID = System.UInt32; using WORLD_META_ID = System.UInt32; using CHANNEL_NO = System.UInt32; namespace GameServer; public partial class GameServerLogic : ServerLogicBase, IWithPacketNamespaceVerifier, IWithConfiguration, IWithServerMetrics, IWithLogActor { private readonly PlayerManager m_player_manager = new(); private readonly SystemMetaMailManager m_system_mail_manager = new(); private readonly ChannelManager m_channel_manager = new(); private readonly NoticeChatManager m_notice_chat_manager = new(); private readonly SeasonPassManager m_season_pass_manager = new(); private readonly UgqApiManager m_ugq_api_manager = new(); private readonly LandManager m_land_manager = new(); private readonly BuildingManager m_building_manager = new(); private ReservationManager? m_reservation_manager { get; set; } private ReturnManager? m_return_manager { get; set; } public ChatCommand ChatCommand { get; private set; } = new ChatCommand(); private Map m_map = new(); public GameServerLogic(ServerConfig config) : base(config) { } public Result mergeConfiguration(IConfiguration configuration) { var result = new Result(); var key_options = configuration; var port = ushort.Parse(key_options["port"] ?? "0"); var config_file = key_options["config"]; var server_config = getServerConfig(); server_config.setAppParamPort(port); server_config.setConfigFilePath($"{"./" + ServerConfig.ConfigDirectory + "/" + config_file}"); return result; } protected override string onGetDumpFilename() { return getServerName() + "_" + getServerConfig().getAppParamPort(); } public override async Task onInit() { var result = new Result(); var err_msg = string.Empty; result = await base.onInit(); if (result.isFail()) { err_msg = $"Failed to onInit() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return result; } await onSetAccountLoginBlock(getServerConfig().AccountLoginBlockEnable); MapDataTable.Instance.Load(); MapManager.Instance.Load(); m_reservation_manager = new ReservationManager(); m_return_manager = new ReturnManager(); return result; } protected override 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; } base.setServerName(server_type.toServerName(server_config.toClientListenIP(), listen_port, (int)getWorldId(), (int)getChannelNo())); return result; } protected override Result onLoadMetaDatas() { return ServerCommon.ServerLogicHelper.loadMetaDatas(this); } protected override async Task onRegisterModuleAll() { var result = new Result(); var config = getServerConfig(); var load_config_info = config.getLoadedConfig(); NullReferenceCheckHelper.throwIfNull(load_config_info, () => $"load_config_info is null !!! - {toBasicString()}"); { result = await tryCreateAndRegisterModule(() => { var config_param = new DynamoDbClient.ConfigParam(); result = config_param.tryReadFromJsonOrDefault(load_config_info); if(result.isFail()) { return (null, result); } var module_context = new ModuleContext((MODULE_ID)ModuleId.DynamoDbConnector , 0, 0, config_param); var created_module = new DynamoDbClient(module_context); return (created_module, result); }); } { result = await tryCreateAndRegisterModule(() => { var config_param = new RedisConnector.ConfigParam(); result = config_param.tryReadFromJsonOrDefault(load_config_info); if (result.isFail()) { return (null, result); } var module_context = new ModuleContext( (MODULE_ID)ModuleId.RedisConnector , 0, 0, config_param ); var created_module = new RedisConnector(module_context); return (created_module, result); }); } { result = await tryCreateAndRegisterModule(() => { var found_redis_connector = getModule((MODULE_ID)ModuleId.RedisConnector); if(null == found_redis_connector) { return ( null , new Result() { ErrorCode = ServerErrorCode.ModuleNotFound , ResultString = $"RedisConnector not found !!! : moduleId:{ModuleId.RedisConnector}" } ); } var config_param = new RedisWithLuaScriptExecutor.ConfigParam(); result = config_param.tryReadFromJsonOrDefault(load_config_info); if (result.isFail()) { return (null, result); } var module_context = new ModuleContext((MODULE_ID)ModuleId.RedisWithLuaScriptExecutor , 0, 0, config_param); var created_module = new RedisWithLuaScriptExecutor(found_redis_connector, module_context); return (created_module, result); }); } { result = await tryCreateAndRegisterModule(() => { var config_param = new RabbitMqConnector.ConfigParam(); result = config_param.tryReadFromJsonOrDefault(load_config_info); if (result.isFail()) { return (null, result); } config_param.ServiceName = getServerName(); var module_context = new ModuleContext( (MODULE_ID)ModuleId.RabbitMqConnector , 0, 0, config_param ); var created_module = new RabbitMQ4Game(module_context); config_param.fnServerMessageRecvFromConsumer = created_module.onRecvProtocol; return (created_module, result); }); } { result = await tryCreateAndRegisterModule(() => { var config_param = new MongoDbConnector.ConfigParam(); result = config_param.tryReadFromJsonOrDefault(load_config_info); if (result.isFail()) { return (null, result); } var module_context = new ModuleContext((MODULE_ID)ModuleId.MongoDbConnector , 0, 0, config_param); var created_module = new MongoDbConnector(module_context); return (created_module, result); }); } { result = await tryCreateAndRegisterModule(() => { var config_param = new S3Connector.ConfigParam(); result = config_param.tryReadFromJsonOrDefault(load_config_info); if (result.isFail()) { return (null, result); } var module_context = new ModuleContext((MODULE_ID)ModuleId.S3Connector , 0, 0, config_param); var created_module = new S3Connector(module_context); return (created_module, result); }); } { result = await tryCreateAndRegisterModule(() => { var config_param = new GameLoginListener.ConfigParam(); result = config_param.tryReadFromJsonOrDefault(load_config_info); if (result.isFail()) { return (null, result); } config_param.ListenIp = config.toClientListenIP(); config_param.ListenPort = config.toClientListenPort(); var module_context = new ModuleContext( (MODULE_ID)ModuleId.ProudNetListener , 0, 0, config_param ); var created_module = new GameLoginListener(this, module_context); created_module.FnSendPacketToHost = onSendPacketToHost; created_module.FnSendPacketToHosts = onSendPacketToHosts; return (created_module, result); }); } return result; } public override async Task onTaskServerInfoSyncTick() { var result = await syncServerInfoToCache(); if (result.isFail()) { Log.getLogger().error($"Failed to syncServerInfoToCache() !!! : {result.toBasicString()} - {toBasicString()}"); } } protected override List onAppendRuleAll() { return new List() { new InventoryRule(), }; } public override async Task onCreateTickerAll() { var result = new Result(); result = await this.createTimeEventForMinuteTicker(); if (result.isFail()) { Log.getLogger().error($"Failed to createTimeEventForMinuteTicker() !!! : {result.toBasicString()} - {toBasicString()}"); return result; } var server_type = getServerType().toServerType(); var entity_ticker_initializers = new Initializers(); if (ServerType.Channel == server_type) { entity_ticker_initializers.appendInitializer(new ChannelUpdateTicker(ServerCommon.Constant.CHANNEL_UPDATE_TIME, null)); entity_ticker_initializers.appendInitializer(new ShopProductCheckTicker(ServerCommon.Constant.SHOP_PRODUCT_CHECK_INTERVAL_MSEC, null)); entity_ticker_initializers.appendInitializer(new CaliumEventTicker(ServerCommon.Constant.CALIUM_EVENT_CHECK_INTERVAL_MSEC, null)); entity_ticker_initializers.appendInitializer(new CaliumStorageRetryTicker(ServerCommon.Constant.CALIUM_RETRY_CHECK_INTERVAL_MSEC, null)); entity_ticker_initializers.appendInitializer(new BuildingUpdateTicker(ServerCommon.Constant.BUILDING_UPDATE_INTERVAL_MSEC, null)); entity_ticker_initializers.appendInitializer(new LandAuctionReservationConfigureTicker(ServerCommon.Constant.LAND_AUCTION_RESERVATION_CONFIGURE_INTERVAL_MSEC, null)); entity_ticker_initializers.appendInitializer(new LandAuctionCheckTicker(ServerCommon.Constant.LAND_AUCTION_CHECK_INTERVAL_MSEC, null)); } entity_ticker_initializers.appendInitializer(new EntityUpdateTicker(ServerCommon.Constant.ENTITY_UPDATE_INTERVAL_MSEC, null)); entity_ticker_initializers.appendInitializer(new EventUpdateTicker(ServerCommon.Constant.EVENT_UPDATE_INTERVAL_3_SEC, null)); entity_ticker_initializers.appendInitializer(new UserLoginCacheRefreshTicker(ServerCommon.Constant.KEEP_LOGIN_UPDATE_TIME, null)); entity_ticker_initializers.appendInitializer(new NoticeChatTicker(ServerCommon.Constant.NOTICE_CHAT_UPDATE_TIME, null)); entity_ticker_initializers.appendInitializer(new PartyCacheRefreshTicker(ServerCommon.Constant.KEEP_PARTY_TIME / 2, null)); entity_ticker_initializers.appendInitializer(new ReservationCheckTicker(ServerCommon.Constant.RESERVATION_INTERVAL_MSEC, null)); entity_ticker_initializers.appendInitializer(new NormalQuestCheckTicker(ServerCommon.Constant.NORMAL_QUEST_CHECK_INTERVAL_MSEC, null)); entity_ticker_initializers.appendInitializer(new SystemMailCheckTicker(ServerCommon.Constant.SYSTEM_MAIL_CHECK_INTERVAL_MSEC, null)); result = await entity_ticker_initializers.init("EntityTickers"); if (result.isFail()) { Log.getLogger().error($"Failed to init() !!! : {result.toBasicString()} - {toBasicString()}"); return result; } foreach (var initializer in entity_ticker_initializers.getInitializers()) { var entity_ticker = initializer as EntityTicker; NullReferenceCheckHelper.throwIfNull(entity_ticker, () => $"entity_ticker is null !!! - {toBasicString()}"); result = registerEntityTicker(entity_ticker); if (result.isFail()) { return result; } } result = await onInitServerKeyToCache((int)getWorldId()); if (result.isFail()) { return result; } return result; } public async Task setupServerMetrics() { var result = await ServerBase.ServerMetricsHelper.setupDefaultServerMetrics(this, (MODULE_ID)ModuleId.RedisConnector); if (result.isFail()) { return result; } return result; } protected override async Task onStartServer() { var result = new Result(); var err_msg = string.Empty; var server_name = getServerName(); var config = getServerConfig(); if (getServerType().toServerType() == ServerType.Channel) { config.setChannelNo(getChannelNo()); config.setWorldId((ushort)getWorldId()); } MetaTextStringFuncHelper.registerFuncAll(); var proud_net_listener = this.getProudNetListener(); proud_net_listener.changeMaxConnectionCount(config.DefaultMaxUser); if (config.NftRule.NftDBAccess) { result = await MySqlConnectorHelper.simpleTryConnectToDb(config.AccountNftDb); if (result.isFail()) { err_msg = $"Failed to connect NFT Db !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return result; } } result = await tryLoadGlobalContent(); if(result.isFail()) { Log.getLogger().error(result.toBasicString()); return result; } result = await base.onStartServer(); if (result.isFail()) { return result; } result = await proud_net_listener.startListen(); if (result.isFail()) { return result; } if (getServerType().toServerType() == ServerType.Channel) { if (ServerCommon.MetaData.Instance._WorldMetaTable.TryGetValue((int)getWorldId(), out var worldMetaData) == false) { err_msg = $"Failed to onInit() !!! : Failed to read WorldMetaData !!! worldMetaId:{getWorldId()}"; result.setFail(ServerErrorCode.NotFoundTable, err_msg); Log.getLogger().error(result.toBasicString()); return result; } if (false == await m_map.onInit(worldMetaData.MapPath, getChannelNo().ToString())) { err_msg = $"Failed to onInit() !!!"; result.setFail(ServerErrorCode.NotFoundTable, err_msg); Log.getLogger().error(result.toBasicString()); return result; } await FarmingHelper.tryFinalyzeUncompletedFarmingByMap(m_map); } return result; } private async Task tryLoadGlobalContent() { var result = new Result(); result = await m_system_mail_manager.LoadDB(); { if (result.isFail()) return result; } result = await m_notice_chat_manager.LoadDB(); { if (result.isFail()) return result; } result = await m_land_manager.loadLands(); { if (result.isFail()) return result; } result = await m_building_manager.loadBuildings(); { if (result.isFail()) return result; } result = await LandAuctionManager.It.tryLoadLandAuctionAll(); { if (result.isFail()) return result; } m_season_pass_manager.Init(); if (getServerConfig().OfflineMode == false) { await AIChatServerConnector.onInit(); } await m_ugq_api_manager.onInit(); // calium converter entity var calium_storage_entity = findGlobalEntity(); NullReferenceCheckHelper.throwIfNull(calium_storage_entity, () => $"CaliumStorageEntity is null !!!"); var calium_storage_action = calium_storage_entity.getEntityAction(); NullReferenceCheckHelper.throwIfNull(calium_storage_action, () => $"CaliumStorageAction is null !!!"); result = await calium_storage_action.initStorageAttribute(); { if (result.isFail()) return result; } // ugcnpcrank entity var ugc_npc_rank_entity = findGlobalEntity(); NullReferenceCheckHelper.throwIfNull(ugc_npc_rank_entity, () => $"UgcNpcRankEntity is null !!!"); var ugc_npc_rank_action = ugc_npc_rank_entity.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_rank_action, () => $"UgcNpcRankManageAction is null !!!"); result = await ugc_npc_rank_action.initRank(); { if (result.isFail()) return result; } //Battle 처리 var server_logic = GameServerApp.getServerLogic(); var server_config = server_logic.getServerConfig(); bool is_battle_system_active = server_config.BattleSystemEnable; if (is_battle_system_active) { await BattleInstanceManager.It.onInit(); } return result; } protected override List onAppendGlobalEntityAll() { var list = new List(); list.Add(new GlobalParty()); list.Add(new UgcNpcRankEntity()); list.Add(new CaliumStorageEntity()); return list; } public bool onSendPacketToHost(Nettention.Proud.HostID hostId, RmiContext rmiContext, IMessage msg) { var client_to_game = msg as ClientToGame; NullReferenceCheckHelper.throwIfNull(client_to_game, () => $"client_to_game is null !!! - {toBasicString()}"); var proud_net_listener = this.getProudNetListener(); var found_proxy = proud_net_listener.findProxy(); ; NullReferenceCheckHelper.throwIfNull(found_proxy, () => $"found_proxy is null !!! - {toBasicString()}"); return found_proxy.Message(hostId, RmiContext.ReliableSend, client_to_game); } public bool onSendPacketToHosts(Nettention.Proud.HostID[] hostIds, RmiContext rmiContext, IMessage msg) { var client_to_game = msg as ClientToGame; NullReferenceCheckHelper.throwIfNull(client_to_game, () => $"client_to_game is null !!! - {toBasicString()}"); var proud_net_listener = this.getProudNetListener(); var found_proxy = proud_net_listener.findProxy(); ; NullReferenceCheckHelper.throwIfNull(found_proxy, () => $"found_proxy is null !!! - {toBasicString()}"); return found_proxy.Message(hostIds, rmiContext, client_to_game); } public bool isSetupCompleted() { return ServerBase.ServerMetricsHelper.isSetupCompleted(this); } public async Task syncServerInfoToCache() { var result = new Result(); var proud_net_listener = this.getProudNetListener(); var server_name = getServerName(); var server_config = getServerConfig(); var listen_ip = server_config.toClientListenIP(); var listen_port = server_config.toClientListenPort(); var user_count = proud_net_listener.getEntityWithSessions().Count; var reservation_count = 0; var ugc_npc_count = 0; var return_count = 0; var awsInstanceId = getInstanceId(); int room_capacity = 0; if (getServerType().toServerType() == ServerType.Indun) { room_capacity = InstanceRoomManager.Instance.Capacity; } result = await ServerBase.ServerMetricsHelper.syncServerInfoToCache( this , server_name , listen_ip, listen_port , user_count, proud_net_listener.getMaxConnectionCount() , reservation_count, return_count , ugc_npc_count , awsInstanceId , (Int32)getWorldId() , (Int32)getChannelNo() , room_capacity ); if (result.isFail()) { return result; } return result; } public bool isValidPacketNamespace(string toCheckNamespace, IPacketCommand packetCommand) { var packet_namespace = "GameServer.PacketHandler"; if ( null != toCheckNamespace && true == toCheckNamespace.Contains(packet_namespace)) { return true; } else { ServerCore.Log.getLogger().error($"Invalid GameServer PacketNamespace !!!, not included Namespace : {packet_namespace} ⊆ {toCheckNamespace}, packetCommnad:{packetCommand.toBasicString()}"); } return false; } public WORLD_META_ID getWorldId() { var config = getConfiguration(); return config.getWorldId(); } public CHANNEL_NO getChannelNo() { var config = getConfiguration(); return config.getChannelNo(); } public ILogActor toLogActor() { return this.toLogActor(); } }