Files
caliverse_server/ServerCommon/Contents/ReservationEnterToServer/ReservationManagerBase.cs
2025-05-01 07:20:41 +09:00

251 lines
11 KiB
C#

using System.Collections.Concurrent;
using System.Diagnostics;
using System.Numerics;
using ServerCore;
using ServerBase;
using USER_GUID = System.String;
namespace ServerCommon;
public class ReservationManagerBase
{
public const int RESERVATION_TASK_DELAY_SEC = 10;
protected readonly ReservationUserManager m_reservation_user_manager = new();
protected readonly ReservationMessageManager m_reservation_message_manager = new();
private ServerLogicBase? m_server_logic { get; set; }
private ConcurrentDictionary<string, Task> m_cancelled_tasks { get; set; } = new();
public virtual void onInit(ServerLogicBase serverLogic)
{
m_server_logic = serverLogic;
m_reservation_user_manager.onInit(serverLogic);
}
public int getReservedUserCount() => m_reservation_user_manager.Count;
public ReservationMessageManager getMessageManager() => m_reservation_message_manager;
public async Task checkLimitReservedUsers() => await m_reservation_user_manager.checkLimitReservedUsers();
public async Task<Result> checkReservation(USER_GUID userGuid)
{
await Task.CompletedTask;
var result = new Result();
var check = m_reservation_user_manager.checkReserved(userGuid);
if (false == check)
{
var err_msg = $"Failed to check reservation enter to server !!! : UserGuid - {userGuid}";
Log.getLogger().error(err_msg);
result.setFail(ServerErrorCode.InvalidReservationUser, err_msg);
return result;
}
m_reservation_user_manager.deleteReserved(userGuid);
return result;
}
public async Task<ServerMessage.Types.GS2GS_ACK_RESERVATION_ENTER_TO_SERVER?> registerReservationEnterToServer(ServerMessage.Types.GS2GS_REQ_RESERVATION_ENTER_TO_SERVER message, string destServerName)
{
var result = new Result();
var stopwatch = new Stopwatch();
long elapsed_msec;
var event_tid = Guid.NewGuid().ToString();
stopwatch.Start();
var next_task = new Task<ServerMessage.Types.GS2GS_ACK_RESERVATION_ENTER_TO_SERVER?>(() => getMessageManager().ackTask(message.RequestUserGuid));
ServerMessage.Types.GS2GS_ACK_RESERVATION_ENTER_TO_SERVER? reserved = null;
// 1. task manager 에 등록
var is_add = m_reservation_message_manager.addReservationMessage(message.RequestUserGuid, message, next_task);
if (false == is_add)
{
var err_msg = $"fail to add reservation message: userGuid[{message.RequestUserGuid}] message[{message}]";
result.setFail(ServerErrorCode.FailedToReservationEnter, err_msg);
Log.getLogger().error(err_msg);
return null;
}
// 2. server manager 전송
var server_message = new ServerMessage();
server_message.ReqReservationEnterToServer = message;
NullReferenceCheckHelper.throwIfNull(m_server_logic, () => $"m_server_logic is null !!!");
var rabbit_mq = m_server_logic.getRabbitMqConnector() as RabbitMqConnector;
NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit_mq is null !!!");
rabbit_mq.SendMessage(destServerName, server_message);
try
{
reserved = await next_task.WaitAsync(TimeSpan.FromSeconds(RESERVATION_TASK_DELAY_SEC));
// 3. 예약 실패 체크
if (null == reserved || reserved.Result.isFail())
{
Log.getLogger().warn($"Failed to reservation enter to game server!!! - {reserved?.Result.toBasicString() ?? string.Empty}");
elapsed_msec = stopwatch.ElapsedMilliseconds;
stopwatch.Stop();
Log.getLogger().debug($"registerReservationEnterToServer Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsed_msec}, UserGuid:{message.RequestUserGuid}, srcServer:{message.RequestServerName}, destServer:{destServerName}");
return null;
}
}
catch (Exception e)
{
// Task Cancelled
deleteReservationEnterToServer(message.RequestUserGuid);
Log.getLogger().warn($"Failed to reservation enter to game server!!! - {e}");
elapsed_msec = stopwatch.ElapsedMilliseconds;
stopwatch.Stop();
Log.getLogger().debug($"registerReservationEnterToServer Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsed_msec}, UserGuid:{message.RequestUserGuid}, srcServer:{message.RequestServerName}, destServer:{destServerName}");
return null;
}
elapsed_msec = stopwatch.ElapsedMilliseconds;
stopwatch.Stop();
Log.getLogger().debug($"registerReservationEnterToServer Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsed_msec}, UserGuid:{message.RequestUserGuid}, srcServer:{message.RequestServerName}, destServer:{destServerName}");
return reserved;
}
public async Task<Result> cancelReservationEnterToServer(USER_GUID userGuid, string destServerName)
{
var result = new Result();
NullReferenceCheckHelper.throwIfNull(m_server_logic, () => $"m_server_logic is null !!!");
var server_message = new ServerMessage();
server_message.ReqReservationCancelToServer = new();
server_message.ReqReservationCancelToServer.RequestServerName = m_server_logic.getServerName();
server_message.ReqReservationCancelToServer.RequestUserGuid = userGuid;
// 1. 취소 전송
var rabbit_mq = m_server_logic.getRabbitMqConnector() as RabbitMqConnector;
NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit_mq is null !!!");
rabbit_mq.SendMessage(destServerName, server_message);
// 2. 응답 대기
var task = new Task(() =>
{
Log.getLogger().debug($"cancelReservationEnterToServer: ack cancel reservation - userGuid[{userGuid}]");
m_cancelled_tasks.Remove(userGuid, out _);
});
m_cancelled_tasks.TryAdd(userGuid, task);
try
{
await task.WaitAsync(TimeSpan.FromSeconds(RESERVATION_TASK_DELAY_SEC));
}
catch (Exception e)
{
Log.getLogger().debug($"cancelReservationEnterToServer: exception cancel reservation - userGuid[{userGuid}] / {e}");
m_cancelled_tasks.Remove(userGuid, out _);
}
return result;
}
public async Task ackCancelProcess(string userGuid)
{
if (!m_cancelled_tasks.TryGetValue(userGuid, out var task)) return;
task.Start();
await Task.CompletedTask;
}
private void deleteReservationEnterToServer(USER_GUID userGuid) => m_reservation_message_manager.deleteReservationMessage(userGuid);
public async Task cancelProcess(ServerMessage.Types.GS2GS_REQ_RESERVATION_CANCEL_TO_SERVER message)
{
m_reservation_user_manager.deleteReserved(message.RequestUserGuid);
await Task.CompletedTask;
}
public async Task ackProcess(ServerMessage.Types.GS2GS_ACK_RESERVATION_ENTER_TO_SERVER message)
=> await m_reservation_message_manager.onReceivedReservationEnterToServer(message);
public async Task reqProcess(ServerMessage.Types.GS2GS_REQ_RESERVATION_ENTER_TO_SERVER request)
{
var stopwatch = new Stopwatch();
var event_tid = Guid.NewGuid().ToString();
stopwatch.Start();
var server_message = new ServerMessage();
server_message.AckReservationEnterToServer = new();
server_message.AckReservationEnterToServer.ReservationUserGuid = request.RequestUserGuid;
server_message.AckReservationEnterToServer.Result = new();
// 1. 조건 체크
var is_check_condition = await checkReservationCondition(request.MoveType, request.RequestUserGuid);
if (false == is_check_condition)
{
server_message.AckReservationEnterToServer.Result.setFail(ServerErrorCode.FailedToReserveEnterCondition, $"Failed to reservation enter !!! : check condition failed {nameof(reqProcess)}");
sendAckReservationEnterToServer(server_message, request.RequestServerName);
return;
}
// 2. 예약 하기
Log.getLogger().debug($"add reserve user : userGuid[{request.RequestUserGuid}]");
var is_reserved = m_reservation_user_manager.AddReserved(request.RequestUserGuid);
if (false == is_reserved)
{
server_message.AckReservationEnterToServer.Result.setFail(ServerErrorCode.FailedToReservationEnter, $"Failed to reserve enter !!! - {nameof(reqProcess)}");
sendAckReservationEnterToServer(server_message, request.RequestServerName);
return;
}
// 4. 예약 결과 전송
server_message.AckReservationEnterToServer.Result.setSuccess();
NullReferenceCheckHelper.throwIfNull(m_server_logic, () => $"m_server_logic is null !!!");
server_message.AckReservationEnterToServer.ReservationServerName = m_server_logic.getServerName();
sendAckReservationEnterToServer(server_message, request.RequestServerName);
var elapsed_msec = stopwatch.ElapsedMilliseconds;
stopwatch.Stop();
Log.getLogger().debug($"ReservationEnterToServer reqProcess Stopwatch Stop: ETID:{event_tid}, ElapsedMSec:{elapsed_msec}, UserGuid:{request.RequestUserGuid}, srcServer:{request.RequestServerName}, destServer:{m_server_logic.getServerName()}");
}
protected virtual async Task<bool> checkReservationCondition(ServerMoveType moveType, USER_GUID reservationUserGuid) => await Task.FromResult(true);
protected virtual async Task<int> checkFreeSessionCount() => await Task.FromResult(0);
protected bool calculateConnectionRate(int freeCount)
{
var rate = MetaHelper.GameConfigMeta.AutoMoveRate;
var allow_count = freeCount - 1;
if (allow_count <= 0) return false;
NullReferenceCheckHelper.throwIfNull(m_server_logic, () => $"m_server_logic is null !!!");
var proud_net_listener = m_server_logic.getProudNetListener();
var current_rate = ((float)(proud_net_listener.getMaxConnectionCount() - allow_count) / proud_net_listener.getMaxConnectionCount()) * 100;
return current_rate <= rate;
}
private void sendAckReservationEnterToServer(ServerMessage message, string destServerName)
{
NullReferenceCheckHelper.throwIfNull(m_server_logic, () => $"m_server_logic is null !!!");
var rabbit_mq = m_server_logic.getRabbitMqConnector() as RabbitMqConnector;
NullReferenceCheckHelper.throwIfNull(rabbit_mq, () => $"rabbit_mq is null !!!");
rabbit_mq.SendMessage(destServerName, message);
}
}