Files
caliverse_server/GameServer/GameServerLogic.cs
2025-05-01 07:20:41 +09:00

593 lines
23 KiB
C#

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<Result> 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<Result> 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<RedisConnector>((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<IRule> onAppendRuleAll()
{
return new List<IRule>()
{
new InventoryRule(),
};
}
public override async Task<Result> 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<Result> setupServerMetrics()
{
var result = await ServerBase.ServerMetricsHelper.setupDefaultServerMetrics<RedisConnector>(this, (MODULE_ID)ModuleId.RedisConnector);
if (result.isFail())
{
return result;
}
return result;
}
protected override async Task<Result> 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<Result> 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<CaliumStorageEntity>();
NullReferenceCheckHelper.throwIfNull(calium_storage_entity, () => $"CaliumStorageEntity is null !!!");
var calium_storage_action = calium_storage_entity.getEntityAction<CaliumStorageAction>();
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<UgcNpcRankEntity>();
NullReferenceCheckHelper.throwIfNull(ugc_npc_rank_entity, () => $"UgcNpcRankEntity is null !!!");
var ugc_npc_rank_action = ugc_npc_rank_entity.getEntityAction<UgcNpcRankManageAction>();
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<EntityBase> onAppendGlobalEntityAll()
{
var list = new List<EntityBase>();
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<PClientToGame.Proxy>(); ;
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<PClientToGame.Proxy>(); ;
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<Result> 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();
}
}