using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; using MetaAssets; namespace GameServer.PacketHandler; [PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.JoinPartyInstanceReq), typeof(JoinPartyInstancePacketHandler), typeof(GameLoginListener))] public class JoinPartyInstancePacketHandler : PacketRecvHandler { private static void send_S2C_ACK_JOIN_PARTY_INSTANCE(Player owner, Result result, ClientToGameRes.Types.JoinPartyInstanceRes? response) { var ack_packet = new ClientToGame { Response = new ClientToGameRes { ErrorCode = result.ErrorCode, JoinPartyInstanceRes = new() } }; if (result.isSuccess()) { ack_packet.Response.JoinPartyInstanceRes = response; } GameServerApp.getServerLogic().onSendPacket(owner, ack_packet); } public override async Task onProcessPacket(ISession entityWithSession, IMessage recvMessage) { var result = new Result(); string err_msg; var entity_player = entityWithSession as Player; NullReferenceCheckHelper.throwIfNull(entity_player, () => "player is null !!!"); // 1. 기본 정보 체크 var request = (recvMessage as ClientToGame)?.Request.JoinPartyInstanceReq; if (null == request) { err_msg = $"Failed to get request type !!! : {nameof(ClientToGame.Request.JoinPartyInstanceReq)}"; result.setFail(ServerErrorCode.InvalidArgument, err_msg); Log.getLogger().error(result.toBasicString()); send_S2C_ACK_JOIN_PARTY_INSTANCE(entity_player, result, null); return result; } // 2. 소속 파티 Guid 조회 var personal_party_action = entity_player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(personal_party_action, () => $"personal_party_action is null !!! - {entity_player.toBasicString()}"); var personal_party = personal_party_action.getPersonalParty(); if (null == personal_party) { err_msg = $"Failed to get party info !!! - not party member - {entity_player.getUserGuid()}"; result.setFail(ServerErrorCode.NotParty, err_msg); Log.getLogger().error(err_msg); send_S2C_ACK_JOIN_PARTY_INSTANCE(entity_player, result, null); return result; } var global_party = GameServerApp.getServerLogic().findGlobalEntity(); NullReferenceCheckHelper.throwIfNull(global_party, () => "global_party is null !!!"); var party = global_party.getParty(personal_party.getPartyGuid()); NullReferenceCheckHelper.throwIfNull(party, () => $"party is null !!! - party guid: {personal_party.getPartyGuid()}"); // 3. Party Instance 정보 조회 var party_instance_attribute = party.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(party_instance_attribute, () => $"party_instance_attribute is null !!! - {entity_player.toBasicString()} / {party.toBasicString()}"); if (party_instance_attribute.InstanceId <= 0) { err_msg = $"Failed to join party instance !!! - instance id is null - {party_instance_attribute.InstanceId}"; result.setFail(ServerErrorCode.EmptyPartyInstanceId, err_msg); Log.getLogger().error(err_msg); send_S2C_ACK_JOIN_PARTY_INSTANCE(entity_player, result, null); return result; } // 4. Party Instance 입장 요청 result = await joinPartyInstance(entity_player, party_instance_attribute.InstanceId, party_instance_attribute.RoomId); if (result.isFail()) { send_S2C_ACK_JOIN_PARTY_INSTANCE(entity_player, result, null); } return result; } private async Task joinPartyInstance(Player player, int instanceId, string roomId) { var result = new Result(); string err_msg; var server_logic = GameServerApp.getServerLogic(); var res = new ClientToGameRes.Types.JoinPartyInstanceRes(); // 1. instance 입장 티켓 확인 result = player.checkInstanceAccess(instanceId); if (result.isFail()) { err_msg = $"Failed to join party instance !!! : no exist entered ticket - instanceMetaId:{instanceId}"; result.setFail(ServerErrorCode.NotExistInstanceTicket, err_msg); Log.getLogger().error(err_msg); return result; } // 2. join instance var instance_room_id = await InstanceRoomHandler.JoinPartyInstance(player.getUserGuid(), roomId); if (instance_room_id == string.Empty) { err_msg = $"Failed to join party instance !!! - {instanceId}"; result.setFail(ServerErrorCode.JoinInstanceFail, err_msg); Log.getLogger().error(err_msg); return result; } // 3. room 정보 획득 var instance_room_storage = new InstanceRoomStorage(); instance_room_storage.Init(server_logic.getRedisDb(), ""); var instanceRoom = await instance_room_storage.GetInstanceRoomInfo(instance_room_id); if (instanceRoom == null) { err_msg = $"Not found InstanceRoomInfo. roomId:{instance_room_id}"; result.setFail(ServerErrorCode.NotExistRoomInfoForEnter, err_msg); Log.getLogger().error(result.toBasicString()); return result; } var serverName = ServerType.Indun.toServerName(instanceRoom.InstanceAddress, (ushort)instanceRoom.InstancePort); // 4. 이동 예약 요청 var message = new ServerMessage.Types.GS2GS_REQ_RESERVATION_ENTER_TO_SERVER(); message.MoveType = ServerMoveType.Force; message.RequestServerName = server_logic.getServerName(); message.RequestUserGuid = player.getUserGuid(); var reserved = await server_logic.getReservationManager().registerReservationEnterToServer(message, serverName); // 5. 예약 실패 체크 if (null == reserved) { err_msg = $"Failed to reservation enter to game server!!! - {nameof(JoinInstancePacketHandler)}"; Log.getLogger().error(err_msg); result.setFail(ServerErrorCode.FailedToReservationEnter, err_msg); send_S2C_ACK_JOIN_PARTY_INSTANCE(player, result, null); return result; } // 6. 이동 처리 result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "JoinPartyInstance", joinPartyInstanceDelegate); if (result.isFail()) { err_msg = $"Failed to transactionRunner !! : {result.ResultString}"; Log.getLogger().error(err_msg); } return result; async Task joinPartyInstanceDelegate() => await joinPartyInstanceAsync(player, serverName, roomId, instanceRoom); } public async Task joinPartyInstanceAsync(Player player, string destServerName, string roomId, InstanceRoomInfo instanceRoom) { var server_logic = GameServerApp.getServerLogic(); var result = new Result(); string err_msg; // 1. 위치 정보 수정 var location_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); result = await location_action.tryMoveToIndun(instanceRoom); if (result.isFail()) { err_msg = $"Fail to tryMoveToIndun"; Log.getLogger().error(err_msg); send_S2C_ACK_JOIN_PARTY_INSTANCE(player, result, null); return result; } if (!MetaData.Instance._IndunTable.TryGetValue(instanceRoom.InstanceId, out var indun_meta_data)) { err_msg = $"Failed to TryGetValue() !!! : instanceMetaId:{instanceRoom.InstanceId} : {this.getTypeName()}"; result.setFail(ServerErrorCode.InstanceMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); send_S2C_ACK_JOIN_PARTY_INSTANCE(player, result, null); return result; } var buff_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(buff_action, () => $"buff_action is null !!! - {player.toBasicString()}"); result = await buff_action.MoveServer(indun_meta_data.placeType()); if (result.isFail()) { send_S2C_ACK_JOIN_PARTY_INSTANCE(player, result, null); return result; } // 2. otp 획득 (result, var reserved_to_switch_server) = await ServerConnectionSwitchHelper.startServerSwitch(player, server_logic.getRedisConnector() , destServerName); if (result.isFail() || null == reserved_to_switch_server) { err_msg = $"Fail to startServerSwitch() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); send_S2C_ACK_JOIN_PARTY_INSTANCE(player, result, null); return result; } var game_login_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(game_login_action, () => $"game_login_action is null !!! - {player.toBasicString()}"); var login_cache = game_login_action.getLoginCacheRequest()?.getLoginCache(); NullReferenceCheckHelper.throwIfNull(login_cache, () => $"Login Cache is null !!! - player: {player.toBasicString()}"); login_cache.ReservedToSwitchServer = reserved_to_switch_server; var res = new ClientToGameRes.Types.JoinPartyInstanceRes(); res.InstanceServerAddr = instanceRoom.InstanceAddress; res.InstanceServerPort = instanceRoom.InstancePort; res.Otp = reserved_to_switch_server.OneTimeKey; res.RoomId = roomId; // 3. DB 갱신 var batch = new QueryBatchEx(player, LogActionType.JoinPartyInstance, server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); batch.addQuery(new QueryFinal()); } writeBusinessLog(batch, player.getUserGuid(), roomId); result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) { err_msg = $"Failed to write for reserve enter GameServer !!! - {player.toBasicString()}"; result.setFail(ServerErrorCode.NoServerConnectable, err_msg); Log.getLogger().error(err_msg); return result; } send_S2C_ACK_JOIN_PARTY_INSTANCE(player, result, res); return result; } private void writeBusinessLog(QueryBatchBase queryBatchBase, string userGuid, string roomId) { var party_guid = PartyHelper.getPartyGuidFromPartyInstanceRoomId(roomId); if (string.IsNullOrEmpty(party_guid)) return; // 1. 파티 인스턴스 정보 var party_instance_log_data = PartyBusinessLogHelper.toPartyInstanceLogData(party_guid, false); var party_instance_business_log = new PartyInstanceBusinessLog(party_instance_log_data); queryBatchBase.appendBusinessLog(party_instance_business_log); // 2. 파티 멤버 정보 var party_member_log_data = PartyBusinessLogHelper.toPartyMemberLogData(party_guid, userGuid, PartyMemberActionType.PartyInstanceJoin); var party_member_business_log = new PartyMemberBusinessLog(party_member_log_data); queryBatchBase.appendBusinessLog(party_member_business_log); } }