using Newtonsoft.Json.Linq; using ServerCore; namespace ServerBase; public class ManagerServerConfig { public int minSize = 1; public int capacity = 2000; } public class RabbitmqConfig { public string HostName { get; private set; } = "localhost"; public string UserName { get; private set; } = "admin"; public string Password { get; private set; } = "admin"; public int Port { get; private set; } = -1; public bool SSL { get; private set; } = false; public void ReadConfig(JToken? jToken) { if (jToken == null) return; HostName = jToken["HostName"]?.Value() ?? HostName; UserName = jToken["UserName"]?.Value() ?? UserName; Password = jToken["Password"]?.Value() ?? Password; Port = jToken["Port"]?.Value() ?? Port; SSL = jToken["SSL"]?.Value() ?? SSL; } } //============================================================================================= // 계정 처리 룰 //============================================================================================= public class AuthRule { //========================================================================================= // 계정 처리 룰 플래그 종류 //========================================================================================= public enum FlagType { None = 0, TestUserAllow = 1, // TestUserCreateData, TestUserInitilData 메타를 참조하여 유저를 생성 및 초기화 한다. BotIdAllow = 2, // Bot id를 허용 한다. (기본값:true(개발용)) TestIdAllow = 3, // Test id를 허용 한다. (기본값:true(개발용)) ClientStandaloneAllow = 4, // 클라이언트가 단독 로그인 시도를 허용 한다. (기본값:true(개발용)) ClientBySsoAccountAuthWithLauncherAllow = 5, // 클라이언트가 통합계정인증 및 런처를 통해 실행되어 로그인 시도를 허용 한다. (기본값:false(개발용))) } public bool TestUserAllow { get; set; } = true; public bool BotIdAllow { get; set; } = true; public bool TestIdAllow { get; set; } = true; public bool ClientStandaloneAllow { get; set; } = true; public bool ClientBySsoAccountAuthWithLauncherAllow { get; set; } = false; // 접속 허용되는 PlatformType 목록 public List PlatformTypeAllows { get; set; } = new List(); } //============================================================================================= // 로드 밸런싱 룰 //============================================================================================= public class LoadBalancingRule { //========================================================================================= // 로드 밸런싱 룰 종류 //========================================================================================= public enum RuleType { None = 0, RoundRobin = 1, // 순서대로 돌아가며 접속을 유도 한다. WeightedRoundRobin = 2, // 가중치가 높은 서버를 우선하여 순서대로 접속 유도 한다. LeastConnection = 3, // 최소 연결 개수 서버를 우선 순위로 접속을 유도 한다. WeightedLeastConnection = 4, // 가중치가 높은 서버를 우선하여 최소 연결 개수 서버를 접속 유도 한다. } //========================================================================================= // 로드 밸런싱 룰 옵션 플래그 종류 //========================================================================================= public enum RuleOptionFlagType { None = 0, UserLanguageBased = 1, // 사용자 언어 서버를 우선 순위로 접속을 유도 한다. } public class Config { public RuleType Rule { get; set; } = RuleType.WeightedRoundRobin; public UInt16 MinRate { get; set; } = 0; public UInt16 MaxRate { get; set; } = 100; public bool UserLanguageBased { get; set; } = false; } public Config AuthToGameRule { get; set; } = new() { Rule = RuleType.WeightedRoundRobin, UserLanguageBased = true }; public Config ChannelToInstanceDungeonRule { get; set; } = new() { Rule = RuleType.WeightedRoundRobin, UserLanguageBased = false }; public Config ChannelToChannelRule { get; set; } = new() { Rule = RuleType.WeightedRoundRobin, UserLanguageBased = false }; public Config InstanceDungeonToChannelRule { get; set; } = new() { Rule = RuleType.WeightedRoundRobin, UserLanguageBased = false }; public Config ToEtcServerRule { get; set; } = new() { Rule = RuleType.WeightedRoundRobin, UserLanguageBased = false }; } //========================================================================================= // NFT 처리 규칙 //========================================================================================= public class NftRule { public bool NftDBAccess { get; set; } = false; public string CPNftForOwnerAllGetUrl { get; set; } = string.Empty; } //========================================================================================= // AI Chat 설정 정보 //========================================================================================= public class AIChatConfig { public string BaseAddress { get; set; } = "https://caliverse-dev.caveduck.io"; public string PrivateKey { get; set; } = "/EbpXsuA4Wi3M9NV11dQj1mtw8FGB42txTUBVfyS2BrdLq7JEwGRFDtmzdDQNMMfByFEX8qJhkpbPZnepTbcCg=="; } //========================================================================================= // Echo System 설정 정보 //========================================================================================= public class EchoSystemConfig { public string BaseAddress { get; set; } = "https://eco-system-dev-rollup-admin-api.caliverse.io"; public string SlackAddress { get; set; } = string.Empty; } //========================================================================================= // Billing 설정 정보 //========================================================================================= public class BillingConfig { public string BaseAddress { get; set; } = "https://dev-api.caliverse.io"; } //========================================================================================= // UGQ API Server 설정 정보 //========================================================================================= public class UgqApiServerConfig { public string ApiServerAddress { get; set; } = "http://ec2-34-222-2-134.us-west-2.compute.amazonaws.com:11000"; public string UrlInGamePrefix { get; set; } = "/api/v1/InGame"; } //============================================================================================= // 서버 주요 설정 정보를 저장하는 클래스 이다. // TODO: ServerConfig => ServerConfig 변경 한다 !!! // ㄱ //============================================================================================= public partial class ServerConfig { public static readonly string ConfigDirectory = "Config"; private bool m_is_loaded_config = false; private UInt16 m_app_param_port = 0; private string m_config_file_path = string.Empty; private JObject? m_loaded_config_nullable; private string m_program_version_path = string.Empty; private Dictionary m_program_versions = new(); private NetworkAddress m_listen_address = new(); private string m_region_id = string.Empty; private UInt16 m_world_id = 0; private UInt32 m_channel_no = 0; private Dictionary m_server_urls = new(); // 언어별 url 링크 // private Dictionary m_server_url_with_langs = new(); public ServerConfig() { } public virtual async Task tryLoadConfig() { var result = new Result(); var err_msg = string.Empty; var config_file_path = getConfigFilePath(); try { if (false == File.Exists(config_file_path)) { err_msg = $"Not exist Config File in path !!! : path:{config_file_path}"; result.setFail(ServerErrorCode.ServerConfigFileNotFound, err_msg); ServerCore.Log.getLogger().error(result.toBasicString()); return result; } var loaded_json = JObject.Parse(File.ReadAllText(config_file_path)); result = await parseConfig(loaded_json); if (result.isFail()) { return result; } m_loaded_config_nullable = loaded_json; m_is_loaded_config = true; } catch (Exception e) { err_msg = $"Exception !!!, Failed to perform !!!, in tryLoadConfig() : exception:{e}"; result.setFail(ServerErrorCode.DotNetException, err_msg); ServerCore.Log.getLogger().error(result.toBasicString()); return result; } return result; } public virtual async Task parseConfig(JObject loadedJosn) { var err_msg = string.Empty; var result = new Result(); LogDir = loadedJosn["LogDir"]?.Value() ?? LogDir; DumpDir = loadedJosn["DumpDir"]?.Value() ?? DumpDir; var listen = loadedJosn["ClientListen"]; if (listen != null) { ClientListenIp = listen["Ip"]?.Value() ?? ClientListenIp; ClientListenPort = listen["Port"]?.Value() ?? ClientListenPort; } MinWorkerThreadCount = loadedJosn["MinWorkerThreadCount"]?.Value() ?? MinWorkerThreadCount; MinIOCThreadCount = loadedJosn["MinIoThreadCount"]?.Value() ?? MinIOCThreadCount; AccountLoginBlockEnable = loadedJosn["AccountLoginBlockEnable"]?.Value() ?? AccountLoginBlockEnable; SingleThreaded = loadedJosn["SingleThreaded"]?.Value() ?? SingleThreaded; Rabbitmq.ReadConfig(loadedJosn["Rabbitmq"]); OfflineMode = loadedJosn["OfflineMode"]?.Value() ?? false; StandaloneMode = loadedJosn["StandaloneMode"]?.Value() ?? StandaloneMode; ServiceType = (loadedJosn["ServiceType"]?.Value() ?? ServiceType); ServiceTypeNew = (loadedJosn["ServiceType"]?.Value()?.toServiceType(global::ServiceType.None) ?? ServiceTypeNew); SsoAccountDb = loadedJosn["SsoAccountDb"]?.Value() ?? SsoAccountDb; SsoAccountAuthJwtSecretKey = loadedJosn["SsoAccountAuthJwtSecretKey"]?.Value() ?? SsoAccountAuthJwtSecretKey; DefaultMaxUser = loadedJosn["DefaultMaxUser"]?.Value() ?? DefaultMaxUser; AccountNftDb = loadedJosn["AccountNftDb"]?.Value() ?? AccountNftDb; Redis = loadedJosn["Redis"]?.Value() ?? Redis; Dynamodb = loadedJosn["Dynamodb"]?.Value() ?? Dynamodb; SessionKeepAliveTimeSec = loadedJosn["SessionKeepAliveTimeSec"]?.Value() ?? SessionKeepAliveTimeSec; EC2TemplateIMG = loadedJosn["EC2TemplateIMG"]?.Value() ?? EC2TemplateIMG; ControlAgentEnable = loadedJosn["ControlAgentEnable"]?.Value() ?? ControlAgentEnable; PerformanceCheckEnable = loadedJosn["PerformanceCheckEnable"]?.Value() ?? PerformanceCheckEnable; BattleSystemEnable = loadedJosn["BattleSystemEnable"]?.Value() ?? BattleSystemEnable; var aws = loadedJosn["AWS"]; if (aws != null) { AWS = new AWSConf { Enable = aws["Enable"]?.Value() ?? false, LocalDynamoDB = aws["LocalDynamoDB"]?.Value() ?? false, AccessKey = aws["AccessKey"]?.Value() ?? "", SecretKey = aws["SecretKey"]?.Value() ?? "", Region = aws["Region"]?.Value() ?? "", MilestoneName = aws["MilestoneName"]?.Value() ?? "" }; if (AWS.Enable && !string.IsNullOrEmpty(AWS.Region)) { setRegionId(AWS.Region); } // Cloud Watch Log if (aws["CloudWatchLog"] is JObject cloud_watch_log) { AWS.CloudWatchLog = new CloudWatchLogConf { Enable = cloud_watch_log["Enable"]?.Value() ?? false, CloudWatchLogGroup = cloud_watch_log["LogGroup"]?.Value() ?? string.Empty, CloudWatchLogNamePattern = cloud_watch_log["LogNamePattern"]?.Value() ?? string.Empty, CloudWatchLogLevel = cloud_watch_log["LogLevel"]?.Value() ?? string.Empty }; var error_code = cloud_watch_log.fillupCloudWatchLogLayout(out var layout); if (error_code.isSuccess()) { AWS.CloudWatchLog.CloudWatchLogLayout = layout; } } // S3 if (aws["S3"] is JObject s3) { AWS.S3 = new S3Conf { ServiceFolderName = s3["ServiceFolderName"]?.Value() ?? string.Empty, MyhomeUgcInfoBucketName = s3["MyhomeUgcInfoBucketName"]?.Value() ?? string.Empty, BeaconAppProfileBucketName = s3["BeaconAppProfileBucketName"]?.Value() ?? string.Empty, }; } } var game_config = loadedJosn["GameConfig"]; if (game_config != null) { GameConfig = new GameConf(); GameConfig.ReservationWaitTimeMSec = game_config["ReservationWaitTimeMSec"]?.Value() ?? 30_000; GameConfig.LoginCacheExpiryTimeMSec = game_config["LoginCacheExpiryTimeMSec"]?.Value() ?? 60 * 60 * 1000; GameConfig.ServerSwitchCacheExpiryTimeMSec = game_config["ServerSwitchCacheExpiryTimeMSec"]?.Value() ?? 5 * 60 * 1000; } var mongoDb = loadedJosn["MongoDb"]; if (mongoDb != null) { MongoDbConfig = new MongoDbConf(); MongoDbConfig.ConnectionString = mongoDb["ConnectionString"]?.Value() ?? string.Empty; MongoDbConfig.DatabaseName = mongoDb["DatabaseName"]?.Value() ?? string.Empty; MongoDbConfig.MinConnectionPoolSize = mongoDb["MinConnectionPoolSize"]?.Value() ?? 0; MongoDbConfig.MaxConnectionPoolSize = mongoDb["MaxConnectionPoolSize"]?.Value() ?? 100; MongoDbConfig.WaitQueueTimeoutSecs = mongoDb["WaitQueueTimeoutSecs"]?.Value() ?? 120; } ClientProgramVersionCheck = loadedJosn["ClientProgramVersionCheck"]?.Value() ?? ClientProgramVersionCheck; m_program_version_path = loadedJosn["ProgramVersionPath"]?.Value() ?? string.Empty; var program_version = loadedJosn["ProgramVersion"]; if (program_version != null) { if (m_program_version_path.isNullOrWhiteSpace()) { err_msg = $"Not found ProgramVersionPath in ServerConfig !!!"; result.setFail(ServerErrorCode.ProgramVersionPathTokenNotFound, err_msg); ServerCore.Log.getLogger().error(result.toBasicString()); return result; } var types = EnumHelper.getValuesWithoutScopeAll(); foreach (var type in types) { var version_file_name = program_version[type.ToString()]?.Value() ?? string.Empty; if (version_file_name.isNullOrWhiteSpace()) { continue; } var full_path = $"{m_program_version_path}{version_file_name}"; if (false == m_program_versions.TryAdd(type, full_path)) { err_msg = $"Already added ProgramVersionType !!! : {type}"; ServerCore.Log.getLogger().warn(result.toBasicString()); continue; } } } CheatCommandAlwaysAllow = loadedJosn["CheatCommandAlwaysAllow"]?.Value() ?? CheatCommandAlwaysAllow; var server_api_url_catalog = loadedJosn["ServerApiUrlCatalog"] as JArray; if (null != server_api_url_catalog) { foreach (var jtoken in server_api_url_catalog) { JObject jobject = (JObject)jtoken; NullReferenceCheckHelper.throwIfNull(jobject, () => $"jobject is null !!!"); foreach (var property in jobject.Properties()) { var server_url_type_string = property.Name; // proto buffer에서 변환할 때, "_"을 제거하기 때문에, 여기서도 "_"를 제거하고 enum을 비교해야 한다. server_url_type_string = server_url_type_string.Replace("_", ""); if (!Enum.TryParse(server_url_type_string, ignoreCase: true, out var server_url_type)) { server_url_type = ServerUrlType.None; } // var server_url_type = server_url_type_string.convertEnumTypeAndValueStringToEnum(ServerUrlType.None); if (ServerUrlType.None == server_url_type) { err_msg = $"Invalid ServerUrlType in ServerConfig !!! : {server_url_type_string}"; result.setFail(ServerErrorCode.ServerUrlTypeInvalid, err_msg); Log.getLogger().error(result.toBasicString()); return result; } var to_add_server_url = new ServerUrl(); to_add_server_url.ServerUrlType = server_url_type; var value = property.Value; // 언어별로 분리된 Link // 클라이언트 측 요청으로 모든 언언어 타입의 url을 담아서 전송, 없으면 ko 전송 if (value is JObject jobj_value) { var langTypes = EnumHelper.getValuesWithoutScopeAll(); var url_with_languages = langTypes.Select(langType => { var url = jobj_value[langType.ToString()]?.Value() ?? jobj_value[LanguageType.Ko.ToString()]?.Value() ?? string.Empty; return new ServerUrlWithLanguage { LangType = langType, TargetUrl = url }; }); to_add_server_url.ServerUrlWithLanguages.AddRange(url_with_languages); ConditionValidCheckHelper.throwIfFalse(url_with_languages.Any(), $"ServerConfig ServerApiUrlCatalog: ko of {server_url_type_string} is null"); } if (false == m_server_urls.TryAdd(server_url_type, to_add_server_url)) { err_msg = $"Already registered ServerUrlType in ServerConfig !!! : {server_url_type_string}"; result.setFail(ServerErrorCode.ServerUrlTypeAlreadyRegistered, err_msg); Log.getLogger().error(result.toBasicString()); return result; } } } } var auth_rule = loadedJosn["AuthRule"]; if (null != auth_rule) { AuthRule.TestUserAllow = auth_rule["TestUserAllow"]?.Value() ?? AuthRule.TestUserAllow; AuthRule.BotIdAllow = auth_rule["BotIdAllow"]?.Value() ?? AuthRule.BotIdAllow; AuthRule.TestIdAllow = auth_rule["TestIdAllow"]?.Value() ?? AuthRule.TestIdAllow; AuthRule.ClientStandaloneAllow = auth_rule["ClientStandaloneAllow"]?.Value() ?? AuthRule.ClientStandaloneAllow; AuthRule.ClientBySsoAccountAuthWithLauncherAllow = auth_rule["ClientBySsoAccountAuthWithLauncherAllow"]?.Value() ?? AuthRule.ClientBySsoAccountAuthWithLauncherAllow; AuthRule.PlatformTypeAllows = auth_rule["PlatformTypeAllows"]?.Value()?.toPlatformTypeAllows() ?? AuthRule.PlatformTypeAllows; } var nft_rule = loadedJosn["NftRule"]; if (null != nft_rule) { NftRule.NftDBAccess = nft_rule["NftDBAccess"]?.Value() ?? NftRule.NftDBAccess; NftRule.CPNftForOwnerAllGetUrl = nft_rule["CPNftForOwnerAllGetUrl"]?.Value() ?? NftRule.CPNftForOwnerAllGetUrl; } var ai_chat = loadedJosn["AIChat"]; if (null != ai_chat) { AIChatConfig.BaseAddress = ai_chat["BaseAddress"]?.Value() ?? string.Empty; AIChatConfig.PrivateKey = ai_chat["PrivateKey"]?.Value() ?? string.Empty; } var echo_system = loadedJosn["EchoSystem"]; if (null != echo_system) { EchoSystemConfig.BaseAddress = echo_system["BaseAddress"]?.Value() ?? string.Empty; EchoSystemConfig.SlackAddress = echo_system["SlackAddress"]?.Value() ?? string.Empty; } var billing = loadedJosn["Billing"]; if (null != billing) { BillingConfig.BaseAddress = billing["BaseAddress"]?.Value() ?? string.Empty; } var uqg_api_server = loadedJosn["Ugq"]; if (null != uqg_api_server) { UgqApiServerConfig.ApiServerAddress = uqg_api_server["ApiServerAddress"]?.Value() ?? string.Empty; UgqApiServerConfig.UrlInGamePrefix = uqg_api_server["UrlInGamePrefix"]?.Value() ?? string.Empty; } var load_balancing_rule = loadedJosn["LoadBalancingRule"]; if (null != load_balancing_rule) { var auth_to_game_rule = load_balancing_rule["AuthToGameRule"]; if (null != auth_to_game_rule) { var rule_value = auth_to_game_rule["Rule"]?.Value(); NullReferenceCheckHelper.throwIfNull(rule_value, () => $"rule_value is null !!!"); var rule_type = EnumHelper.convertEnumValueStringToEnum(rule_value, LoadBalancingRule.RuleType.None); LoadBalancingRule.AuthToGameRule.Rule = rule_type != LoadBalancingRule.RuleType.None ? rule_type : LoadBalancingRule.AuthToGameRule.Rule; LoadBalancingRule.AuthToGameRule.MinRate = auth_to_game_rule["MinRate"]?.Value() ?? LoadBalancingRule.AuthToGameRule.MinRate; LoadBalancingRule.AuthToGameRule.MaxRate = auth_to_game_rule["MaxRate"]?.Value() ?? LoadBalancingRule.AuthToGameRule.MaxRate; LoadBalancingRule.AuthToGameRule.UserLanguageBased = auth_to_game_rule["UserLanguageBased"]?.Value() ?? LoadBalancingRule.AuthToGameRule.UserLanguageBased; } var channel_to_instance_dungeon_rule = load_balancing_rule["ChannelToInstanceDungeonRule"]; if (null != channel_to_instance_dungeon_rule) { var rule_value = channel_to_instance_dungeon_rule["Rule"]?.Value(); NullReferenceCheckHelper.throwIfNull(rule_value, () => $"rule_value is null !!!"); var rule_type = EnumHelper.convertEnumValueStringToEnum(rule_value, LoadBalancingRule.RuleType.None); LoadBalancingRule.ChannelToInstanceDungeonRule.Rule = rule_type != LoadBalancingRule.RuleType.None ? rule_type : LoadBalancingRule.ChannelToInstanceDungeonRule.Rule; LoadBalancingRule.ChannelToInstanceDungeonRule.MinRate = channel_to_instance_dungeon_rule["MinRate"]?.Value() ?? LoadBalancingRule.ChannelToInstanceDungeonRule.MinRate; LoadBalancingRule.ChannelToInstanceDungeonRule.MaxRate = channel_to_instance_dungeon_rule["MaxRate"]?.Value() ?? LoadBalancingRule.ChannelToInstanceDungeonRule.MaxRate; LoadBalancingRule.ChannelToInstanceDungeonRule.UserLanguageBased = channel_to_instance_dungeon_rule["UserLanguageBased"]?.Value() ?? LoadBalancingRule.ChannelToInstanceDungeonRule.UserLanguageBased; } var channel_to_channel_rule = load_balancing_rule["ChannelToChannelRule"]; if (null != channel_to_channel_rule) { var rule_value = channel_to_channel_rule["Rule"]?.Value(); NullReferenceCheckHelper.throwIfNull(rule_value, () => $"rule_value is null !!!"); var rule_type = EnumHelper.convertEnumValueStringToEnum(rule_value, LoadBalancingRule.RuleType.None); LoadBalancingRule.ChannelToChannelRule.Rule = rule_type != LoadBalancingRule.RuleType.None ? rule_type : LoadBalancingRule.ChannelToChannelRule.Rule; LoadBalancingRule.ChannelToChannelRule.MinRate = channel_to_channel_rule["MinRate"]?.Value() ?? LoadBalancingRule.ChannelToChannelRule.MinRate; LoadBalancingRule.ChannelToChannelRule.MaxRate = channel_to_channel_rule["MaxRate"]?.Value() ?? LoadBalancingRule.ChannelToChannelRule.MaxRate; LoadBalancingRule.ChannelToChannelRule.UserLanguageBased = channel_to_channel_rule["UserLanguageBased"]?.Value() ?? LoadBalancingRule.ChannelToChannelRule.UserLanguageBased; } var to_etc_server_rule = load_balancing_rule["ToEtcServerRule"]; if (null != to_etc_server_rule) { var rule_value = to_etc_server_rule["Rule"]?.Value(); NullReferenceCheckHelper.throwIfNull(rule_value, () => $"rule_value is null !!!"); var rule_type = EnumHelper.convertEnumValueStringToEnum(rule_value, LoadBalancingRule.RuleType.None); LoadBalancingRule.ToEtcServerRule.Rule = rule_type != LoadBalancingRule.RuleType.None ? rule_type : LoadBalancingRule.ToEtcServerRule.Rule; LoadBalancingRule.ToEtcServerRule.MinRate = to_etc_server_rule["MinRate"]?.Value() ?? LoadBalancingRule.ToEtcServerRule.MinRate; LoadBalancingRule.ToEtcServerRule.MaxRate = to_etc_server_rule["MaxRate"]?.Value() ?? LoadBalancingRule.ToEtcServerRule.MaxRate; LoadBalancingRule.ToEtcServerRule.UserLanguageBased = to_etc_server_rule["UserLanguageBased"]?.Value() ?? LoadBalancingRule.ToEtcServerRule.UserLanguageBased; } } System.IO.Directory.CreateDirectory(LogDir); System.IO.Directory.CreateDirectory(DumpDir); return await Task.FromResult(result); } public Dictionary getVersionPaths() { return m_program_versions; } public string toBasicString() { return $"ServerConfigFile:{m_config_file_path}"; } }