593 lines
23 KiB
C#
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();
|
|
}
|
|
}
|