Files
caliverse_server/ServerCommon/Helper/LoadBalanceServerHelper.cs
2025-05-01 07:20:41 +09:00

234 lines
9.1 KiB
C#

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<ServerInfo?> 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<ServerInfo?> 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<ServerInfo?> 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<ServerInfo?> selectServerInfoFromList(IReadOnlyList<ServerInfo> 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<AccountAttribute>();
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<IEnumerable<ServerInfo>> 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<ServerInfo>();
}
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<LocationAttribute>();
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;
/// <summary>
/// *인스턴스 룸을 생성할 인스턴스 서버 고르기*<br/>
/// 인스턴스 룸 최대 인원이 들어갈 수 있는 서버 중 수용인원이 가장 많은 서버 선택
/// </summary>
/// <param name="serverList">인스턴스 서버 목록</param>
/// <param name="roomCapacity">생성할 인스턴스 룸 최대 인원</param>
/// <returns>입력값 List&lt;ServerInfo&gt; serverList 의 인덱스, 선택된 서버가 없는 경우 -1</returns>
public static int getBestInstanceServerIndexForCreate(in List<ServerInfo> 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;
}
/// <summary>
/// *게임 서버 고르기*<br/>
/// 서버 수용인원의 80% 되지 않은 서버에서 선택<br/>
/// 모든 서버가 80% 이상인 경우 서버 수용인원을 넘지 않은 서버에서 선택
/// </summary>
/// <param name="serverList">게임 서버 목록</param>
/// <returns>입력값 List&lt;ServerInfo&gt; serverList 의 인덱스, 선택된 서버가 없는 경우 -1</returns>
public static int getBestGameServerIndex(List<ServerInfo> 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;
}
}