using ServerCore; using ServerBase; using WORLD_META_ID = System.UInt32; namespace ServerCommon; public static class LoadBalanceServerHelper { private const int READ_SERVER_DATA_LIMIT_COUNT = 5; private const int FULL_LIMIT_RATE = 100; public static async Task getAuthLoginToChannelServerInfo( this IWithServerMetrics serverMetrics , EntityBase entityUser , UInt16 worldId = 0 ) { if (0 >= worldId) { worldId = getWorldIdWenServerChange(entityUser); } return await getBalancedServerInfo(serverMetrics, entityUser, ServerType.Channel, ServerMoveType.Auto, worldId); } public static async Task getReturnToServerInfo( this IWithServerMetrics serverMetrics , string toReturnServerName, ServerType toReturnServerType , EntityBase entityUser, ushort worldId = 0 ) { var err_msg = string.Empty; (var result, ServerInfo ? found_server_info) = await serverMetrics.getServerInfoByServerName(toReturnServerName); if (result.isSuccess() && null != found_server_info) { return found_server_info; } found_server_info = await getBalancedServerInfo(serverMetrics, entityUser, toReturnServerType, ServerMoveType.Auto, worldId); if(null != found_server_info) { return found_server_info; } err_msg = $"Not found ServerInfo !!!, in getReturnToServerInfo() : {result.toBasicString()} - {entityUser.toBasicString()}, {serverMetrics.toBasicString()}"; Log.getLogger().error(err_msg); return null; } public static async Task getBalancedServerInfo( this IWithServerMetrics IWithServerMetrics , EntityBase entityUser , ServerType toServerType, ServerMoveType serverMoveType , UInt16 worldId = 0 ) { string err_msg; // 1. 모든 서버 리스트 가져오기 var loaded_servers = await getAllServersPassableEntered(IWithServerMetrics, toServerType, worldId); if (!loaded_servers.Any()) { err_msg = $"ServerMetrics count zero !!! toServerType:{toServerType}, worldId:{worldId} - {entityUser.toBasicString()}"; Log.getLogger().fatal(err_msg); return null; } // 2. 이동 타입에 따른 체크 switch (serverMoveType) { case ServerMoveType.Auto: return await selectServerInfoFromList(loaded_servers.ToList(), entityUser, MetaHelper.GameConfigMeta.AutoMoveRate); case ServerMoveType.Force: return await selectServerInfoFromList(loaded_servers.ToList(), entityUser, FULL_LIMIT_RATE); case ServerMoveType.Return: case ServerMoveType.None: default: return null; } } private static async Task selectServerInfoFromList(IReadOnlyList servers, EntityBase entityUser, int limitRate) { await Task.CompletedTask; ServerInfo? select_server = null; ServerInfo? language_server = null; var check_user_count = -1; var account_attribute = entityUser.getOriginEntityAttribute(); if (null == account_attribute) return null; foreach (var server in servers) { var current_user_count = checkCurrentUserCount(server); // 1. max count 체크 var max_user_count = server.Capacity * limitRate / 100; if (max_user_count <= current_user_count) continue; // 2. current session count 체크 if (check_user_count >= current_user_count) continue; select_server = server; check_user_count = current_user_count; } return select_server ?? language_server; } private static async Task> getAllServersPassableEntered( this IWithServerMetrics serverMetrics , ServerType toServerType, WORLD_META_ID worldMetaId ) { for (var i = 0; i < READ_SERVER_DATA_LIMIT_COUNT; i++) { // 1. 서버 정보 가져오기 (var result, var found_server_infos) = await serverMetrics.getServerInfosByServerType(toServerType, worldMetaId); if (result.isFail()) { continue; } // 2. 서버 최대 세션수 체크 // 서버의 최대 세션수보다 (현재 세션수 + 예약자 수 + 복귀자 수 + Beacon 수)가 크거나 같을때 목록에서 제외시킨다. found_server_infos.RemoveAll(x => checkCurrentUserCount(x) >= x.Capacity); return found_server_infos; } return new List(); } private static UInt16 getWorldIdWenServerChange(EntityBase entityUser) { UInt16 target_world_id = 0; // 1. 기본 Default 정보 확인 if (!MetaData.Instance._GameConfigMetaTable.TryGetValue(ConstantKeyType.DefaultEntryWorldIdWhenLoginToAuth.ToString(), out var found_value)) { var err_msg = $"ConstantKeyType.DefaultEntryWorldIdWhenLoginToAuth not found !!! : worldId:{found_value} - {entityUser.toBasicString()}"; Log.getLogger().fatal(err_msg); return 0; } // 2. LastConnectionChannel 정보 확인 var location_attribute = entityUser.getOriginEntityAttribute(); if (null == location_attribute) return target_world_id; target_world_id = (location_attribute.LastestChannelServerLocation.WorldMetaId == 0) ? Convert.ToUInt16(found_value) : (UInt16)location_attribute.LastestChannelServerLocation.WorldMetaId; return target_world_id; } private static int checkCurrentUserCount(ServerInfo server) => server.Sessions + server.Reservation + server.ReturnCount + server.UgcNpcCount; /// /// *인스턴스 룸을 생성할 인스턴스 서버 고르기*
/// 인스턴스 룸 최대 인원이 들어갈 수 있는 서버 중 수용인원이 가장 많은 서버 선택 ///
/// 인스턴스 서버 목록 /// 생성할 인스턴스 룸 최대 인원 /// 입력값 List<ServerInfo> serverList 의 인덱스, 선택된 서버가 없는 경우 -1 public static int getBestInstanceServerIndexForCreate(in List serverList, int roomCapacity) { int retIndex = -1; int maxCount = 0; foreach (var (server_info, index) in serverList.Select((value, index) => (value, index))) { if (maxCount < server_info.RoomCapacity + roomCapacity && server_info.RoomCapacity + roomCapacity <= server_info.Capacity) { retIndex = index; maxCount = server_info.RoomCapacity + roomCapacity; } } return retIndex; } /// /// *게임 서버 고르기*
/// 서버 수용인원의 80% 되지 않은 서버에서 선택
/// 모든 서버가 80% 이상인 경우 서버 수용인원을 넘지 않은 서버에서 선택 ///
/// 게임 서버 목록 /// 입력값 List<ServerInfo> serverList 의 인덱스, 선택된 서버가 없는 경우 -1 public static int getBestGameServerIndex(List serverList, LanguageType languageType , ref int currentServerIndex) { int server_index = -1; int limit_first = Constant.g_MaxUser * 5 / 10; /* foreach (var serverInfo in serverList.Select((value, index) => new { value, index })) { if (serverInfo.value.Language == languageType && limit_first > serverInfo.value.Sessions) { serverIndex = serverInfo.index; break; } } if (serverIndex == -1) { foreach (var serverInfo in serverList.Select((value, index) => new { value, index })) { if (limit_first > serverInfo.value.Sessions) { serverIndex = serverInfo.index; break; } } } */ if (server_index == -1) { if (currentServerIndex >= serverList.Count) { currentServerIndex = 0; } server_index = currentServerIndex; ++currentServerIndex; } return server_index; } }