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.CreatePartyInstanceReq), typeof(CreatePartyInstancePacketHandler), typeof(GameLoginListener))] public class CreatePartyInstancePacketHandler : PacketRecvHandler { private void send_S2C_ACK_CREATE_PARTY_INSTANCE(Player owner, Result result, string spendItemGuid) { var ack_packet = new ClientToGame { Response = new ClientToGameRes { ErrorCode = result.ErrorCode, CreatePartyInstanceRes = new() } }; if (result.isSuccess()) { var spent = new ItemGuidCount(); spent.ItemGuid = spendItemGuid; spent.ItemCount = 1; ack_packet.Response.CreatePartyInstanceRes.Items.Add(spent); } 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, () => $"entity_player is null !!!"); // 1. 기본 정보 체크 var request = (recvMessage as ClientToGame)?.Request.CreatePartyInstanceReq; if (null == request) { err_msg = $"Failed to get request type !!! : {nameof(ClientToGame.Request.CreatePartyInstanceReq)}"; result.setFail(ServerErrorCode.InvalidArgument, err_msg); Log.getLogger().error(result.toBasicString()); send_S2C_ACK_CREATE_PARTY_INSTANCE(entity_player, result, string.Empty); 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_CREATE_PARTY_INSTANCE(entity_player, result, string.Empty); return result; } var global_party = GameServerApp.getServerLogic().findGlobalEntity(); NullReferenceCheckHelper.throwIfNull(global_party, () => $"global_party is null !!! - {entity_player.toBasicString()}"); var party = global_party.getParty(personal_party.getPartyGuid()); NullReferenceCheckHelper.throwIfNull(party, () => $"party is null !!! - {entity_player.toBasicString()}"); // 3. 파티장 체크 var party_action = party.getEntityAction(); NullReferenceCheckHelper.throwIfNull(party_action, () => $"party_action is null !!! - {entity_player.toBasicString()}"); var is_leader = party_action.isLeader(entity_player.getUserGuid()); if (!is_leader) { err_msg = $"Failed to create party instance !!! : not party leader - {entity_player.getUserGuid()}"; result.setFail(ServerErrorCode.NotPartyLeader, err_msg); Log.getLogger().error(err_msg); send_S2C_ACK_CREATE_PARTY_INSTANCE(entity_player, result, string.Empty); return result; } // 4. Party Instance 생성 처리 result = await entity_player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "CreatePartyInstance", createPartyInstanceDelegate); if (result.isFail()) { send_S2C_ACK_CREATE_PARTY_INSTANCE(entity_player, result, string.Empty); } return result; async Task createPartyInstanceDelegate() => await createPartyInstanceAsync(entity_player, request, party); } private async Task createPartyInstanceAsync(Player owner, ClientToGameReq.Types.CreatePartyInstanceReq request, GlobalPartyDetail party) { var result = new Result(); string err_msg; // 1. party instance 정보 획득 var instance_info = getPartyInstanceInfo(request); if (instance_info.result.isFail() || instance_info.concert_data == null) return instance_info.result; // 2. Instance 생성 var instance_room_id = await InstanceRoomHandler.CreatePartyInstance(party.PartyGuid, instance_info.instance_id); if (string.IsNullOrEmpty(instance_room_id)) { err_msg = "Failed to create party instance !!!"; result.setFail(ServerErrorCode.CreateRoomFail, err_msg); Log.getLogger().error(err_msg); return result; } // 3. 파티 채널 생성권 소모 (result, var item) = await spendCreatePartyInstanceItem(owner); if(result.isFail()) return result; // 4. 파티에 party instance 저장 ( PartyInstanceInfo ) var party_info_action = party.getEntityAction(); NullReferenceCheckHelper.throwIfNull(party_info_action, () => $"party_info_action is null !!! - {owner.toBasicString()}"); var start_time = DateTime.UtcNow.AddSeconds(MetaHelper.GameConfigMeta.PartyConcertWaitingTimeSec).ToTimestamp(); var end_time = start_time.ToDateTime().AddSeconds(instance_info.concert_data.ConcertLength).ToTimestamp(); result = await party_info_action.registerPartyInstance(instance_info.instance_id, instance_room_id, start_time, end_time, 0, true); if (result.isFail()) return result; var batch = new QueryBatchEx( owner, LogActionType.CreatePartyInstance, GameServerApp.getServerLogic().getDynamoDbClient()); batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); batch.addQuery(new QueryFinal()); // 5. Business Log 기록 writeBusinessLog(batch, party, item); return await QueryHelper.sendQueryAndBusinessLog(batch); } private (Result result, int instance_id, MetaAssets.InstanceConcertMetaData? concert_data) getPartyInstanceInfo(ClientToGameReq.Types.CreatePartyInstanceReq request) { var result = new Result(); string err_msg; // 1. Instance ID 획득 (result, var room_map_tree, _) = MapHelper.tryGetRoomMapTree(request.LandId, request.Floor, request.BuildingId); if (result.isFail() || room_map_tree == null) { err_msg = $"Failed to tryGetRoomMapTree() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return (result, 0, null); } var instance_id = room_map_tree.InstanceMetaId; // 2. Indun data 획득 if (!MetaData.Instance._IndunTable.TryGetValue(instance_id, out var indun_data)) { err_msg = $"Failed to create party instance !!! : Not Found IndunData. instanceId:{instance_id}"; result.setFail(ServerErrorCode.InstanceMetaDataNotFound, err_msg); Log.getLogger().error(err_msg); return (result, 0, null); } // 3. indun data 체크 if (indun_data.placeType() != EPlaceType.Concert || indun_data.OverLimit == 0) { err_msg = $"Failed to create party instance !!! : invalid instance info. instanceId:{instance_id}"; result.setFail(ServerErrorCode.NotUsablePlace, err_msg); Log.getLogger().error(err_msg); return (result, 0, null); } // 4. Concert 정보 획득 if (MetaData.Instance._ConcertTable.TryGetValue(indun_data.MapId, out var concert_data) == false) { err_msg = $"Failed to create party instance !!! : not found Concert id:{indun_data.MapId}"; result.setFail(ServerErrorCode.NotFoundTable, err_msg); Log.getLogger().error(err_msg); return (result, 0, null); } return (result, instance_id, concert_data); } private async Task<(Result, Item?)> spendCreatePartyInstanceItem(Player owner) { var inventory_action = owner.getEntityAction(); NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!! - {owner.toBasicString()}"); (var result, var delete_instance_items) = await inventory_action.tryDeleteItemByMetaId((uint)ServerCommon.Constant.PARTY_INSTANCE_CREATE_ITEM_ID, (ushort)MetaHelper.GameConfigMeta.ItemAmountPartyInstance); if (result.isFail()) return (result, null); var item = delete_instance_items.FirstOrDefault(); return (result, item); } private void writeBusinessLog(QueryBatchBase queryBatchBase, GlobalPartyDetail party, Item? delItem) { // 1. 파티 정보 var party_log_data = PartyBusinessLogHelper.toPartyLogData(party.PartyGuid, false); var party_business_log = new PartyBusinessLog(party_log_data); queryBatchBase.appendBusinessLog(party_business_log); // 2. 파티 인스턴스 정보 var party_instance_log_data = PartyBusinessLogHelper.toPartyInstanceLogData(party.PartyGuid, true); var party_instance_business_log = new PartyInstanceBusinessLog(party_instance_log_data); queryBatchBase.appendBusinessLog(party_instance_business_log); } }