using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; using MetaAssets; using static ClientToGameRes.Types; namespace GameServer.PacketHandler; [PacketHandler(typeof(ClientToGameReq), typeof(ClientToGameReq.Types.C2GS_REQ_GAIN_LAND_PROFIT), typeof(GainLandProfitPacketHandler), typeof(GameLoginListener))] internal class GainLandProfitPacketHandler : PacketRecvHandler { public static bool send_S2C_ACK_GAIN_LAND_PROFIT(Player owner, Result result, GS2C_ACK_GAIN_LAND_PROFIT res) { var ack_packet = new ClientToGame(); ack_packet.Response = new ClientToGameRes(); ack_packet.Response.ErrorCode = result.ErrorCode; ack_packet.Response.AckGainLandProfit = res; if (false == GameServerApp.getServerLogic().onSendPacket(owner, ack_packet)) { return false; } return true; } public override async Task onProcessPacket(ISession entityWithSession, Google.Protobuf.IMessage recvMessage) { var result = new Result(); var err_msg = string.Empty; var player = entityWithSession as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); var server_logic = GameServerApp.getServerLogic(); var req_msg = recvMessage as ClientToGame; NullReferenceCheckHelper.throwIfNull(req_msg, () => $"req_msg is null !!! - {player.toBasicString()}"); var request = req_msg.Request.ReqGainLandProfit; var res = new GS2C_ACK_GAIN_LAND_PROFIT(); if (!MetaData.Instance._LandTable.TryGetValue(request.LandMetaId, out var land_meta_data)) { err_msg = $"Failed to MetaData.TryGetValue() !!! : LandMetaId:{request.LandMetaId} - {player.toBasicString()}"; result.setFail(ServerErrorCode.LandMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } if (!MapManager.Instance.GetLandMapTree(request.LandMetaId, out var land_map_tree)) { err_msg = $"Failed to GetLandMapTree() !!! : LandMetaId:{request.LandMetaId} - {player.toBasicString()}"; result.setFail(ServerErrorCode.LandMapTreeDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } var building_map_tree = land_map_tree.ChildBuildingMapTree; if (building_map_tree == null) { err_msg = $"Not Exist LandMapTree ChildBuilding !!! : LandMap:{land_map_tree.LandMapFileName} - {player.toBasicString()}"; result.setFail(ServerErrorCode.LandMapTreeChildBuildingNotFound, err_msg); Log.getLogger().error(result.toBasicString()); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } var owned_land_agent_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(owned_land_agent_action, () => $"owned_land_agent_action is null !!! - {player.toBasicString()}"); if (!owned_land_agent_action.isOwnedLand(request.LandMetaId)) { err_msg = $"Not Owned Land !!! : landMetaId:{request.LandMetaId} - {player.toBasicString()}"; result.setFail(ServerErrorCode.OwnedLandNotFound, err_msg); Log.getLogger().error(result.toBasicString()); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } var building_manager = server_logic.getBuildingManager(); if (!building_manager.tryGetBuilding(building_map_tree.BuildingMetaId, out var building)) { err_msg = $"Failed to tryGetBuilding() !!! : buildingMetaId:{building_map_tree.BuildingMetaId} - {player.toBasicString()}"; result.setFail(ServerErrorCode.BuildingNotFound, err_msg); Log.getLogger().error(result.toBasicString()); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } if (!MetaData.Instance._RentalfeeTable.TryGetValue((land_meta_data.Editor, land_meta_data.LandSize), out var rentalfee_meta_data)) { err_msg = $"Failed to MetaData.TryGetValue() !!! : Editor:{land_meta_data.Editor}, Size:{land_meta_data.LandSize} - {player.toBasicString()}"; result.setFail(ServerErrorCode.RentalfeeMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } var building_profit_agent_action = building.getEntityAction(); NullReferenceCheckHelper.throwIfNull(building_profit_agent_action, () => $"building_profit_agent_action is null !!! - {player.toBasicString()}"); // 랜드 수익 획득 var gain_profits = new Dictionary(); var modify_profits = new Dictionary(); using (building_profit_agent_action.getAsyncLock().Lock()) { var building_profits = new List(); if (request.Floor == 0) { building_profits = building_profit_agent_action.getAllBuildingProfit(); } else { if (!building_profit_agent_action.tryGetBuildingProfit(request.Floor, out var buildingProfit)) { err_msg = $"Failed to tryGetBuildingProfit() !!! : floor:{request.Floor} - {player.toBasicString()}"; result.setFail(ServerErrorCode.BuildingNotFound, err_msg); Log.getLogger().error(result.toBasicString()); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } building_profits.Add(buildingProfit); } foreach (var building_profit in building_profits) { var building_profit_action = building_profit.getEntityAction(); NullReferenceCheckHelper.throwIfNull(building_profit_action, () => $"building_profit_action is null !!! - {player.toBasicString()}"); var floor = building_profit_action.getFloor(); var profits = building_profit_action.getProfits(); foreach (var (profit_type, profit_amount) in profits) { if (profit_amount <= 0) continue; gain_profits.TryGetValue(profit_type, out var gain_amount); gain_amount += profit_amount; gain_profits[profit_type] = gain_amount; modify_profits[building_profit] = (floor, profit_type, -profit_amount); } } if (modify_profits.Count <= 0) { send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } var building_profit_business_logs = new List(); var building_profit_update_item_query_contexts = new List(); // 랜드 수익 DB foreach (var (building_profit, (floor, currency_type, delta_amount)) in modify_profits) { (result, var building_profit_update_item_query_context) = BuildingProfitHelper.tryMakeUpdateItemRequestFromBuildingProfit(building_map_tree.BuildingMetaId, floor, currency_type, delta_amount); if (result.isFail()) { err_msg = $"Failed to tryMakeUpdateItemRequestFromBuildingProfit() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } NullReferenceCheckHelper.throwIfNull(building_profit_update_item_query_context, () => $"building_profit_update_item_query_context is null !!! - {player.toBasicString()}"); var profit_action = building_profit.getEntityAction(); NullReferenceCheckHelper.throwIfNull(profit_action, () => $"profit_action is null !!! - {player.toBasicString()}"); var current_profit = profit_action.getProfit(currency_type); var building_profit_log_info = BuildingProfitBusinessLogHelper.toBuildingProfitLogInfo(building_map_tree.BuildingMetaId, floor, currency_type, AmountDeltaType.Consume, delta_amount, current_profit); var building_profit_business_log = new BuildingProfitBusinessLog(building_profit_log_info); building_profit_business_logs.Add(building_profit_business_log); building_profit_update_item_query_contexts.Add(building_profit_update_item_query_context); } // Building Profit History (result, var building_profit_history, var building_profit_history_doc) = await BuildingProfitHistoryHelper.tryMakeBuildingProfitHistory(building, building_map_tree.BuildingMetaId, request.Floor, DateTime.UtcNow, ProfitHistoryType.Gain, gain_profits); if (result.isFail()) { err_msg = $"Failed to tryMakeBuildingProfitHistory() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } NullReferenceCheckHelper.throwIfNull(building_profit_history, () => $"building_profit_history is null !!!"); NullReferenceCheckHelper.throwIfNull(building_profit_history_doc, () => $"building_profit_history_doc is null !!!"); // Money var transaction_name = "GainLandProfit"; var fn_transaction_runner = async delegate () { var result = new Result(); var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); NullReferenceCheckHelper.throwIfNull(found_transaction_runner, () => $"found_transaction_runner is null !!! - {player.toBasicString()}"); var money_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(money_action, () => $"money_action is null !!! - {player.toBasicString()}"); foreach (var (profit_type, profit_amount) in gain_profits) { var gain_amount = profit_amount * (100 - rentalfee_meta_data.DutyRate) / 100; result = await money_action.changeMoney(profit_type, gain_amount); if (result.isFail()) { err_msg = $"Failed to changeMoney() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } if (profit_type == CurrencyType.Calium) { var calium_storage_entity = server_logic.findGlobalEntity(); NullReferenceCheckHelper.throwIfNull(calium_storage_entity, () => $"calium_storage_entity is null !!! - {player.toBasicString()}"); var calium_event_action = calium_storage_entity.getEntityAction(); NullReferenceCheckHelper.throwIfNull(calium_event_action, () => $"calium_event_action is null !!! - {player.toBasicString()}"); var trans_id = found_transaction_runner.getTransId(); var burn_calium_amount = profit_amount * rentalfee_meta_data.DutyRate / 100; result = await calium_event_action.sendCaliumBurnEvent(player, trans_id, player.getUserNickname(), transaction_name, -burn_calium_amount); if (result.isFail()) { err_msg = $"Failed to sendCaliumBurnEvent() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } } } var batch = new QueryBatchEx(player, LogActionType.GainLandProfit, server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); batch.addQuery(new DBQWithItemRequestQueryContext(building_profit_update_item_query_contexts)); batch.addQuery(new DBQEntityWrite(building_profit_history_doc)); batch.addQuery(new QueryFinal()); } batch.appendBusinessLogs(building_profit_business_logs); result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) { send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } res.CommonResult = found_transaction_runner.getCommonResult(); return result; }; result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, transaction_name, fn_transaction_runner); if (result.isFail()) { err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); return result; } // 랜드 수익 Memory foreach (var (building_profit, (floor, currency_type, currency_amount)) in modify_profits) { var building_profit_action = building_profit.getEntityAction(); NullReferenceCheckHelper.throwIfNull(building_profit_action, () => $"building_profit_action is null !!! - {player.toBasicString()}"); building_profit_action.modifyProfit(currency_type, currency_amount); var floor_profit = building_profit.toFloorProfitInfo(); res.FloorProfits.Add(floor, floor_profit); } var building_profit_history_agent_action = building.getEntityAction(); NullReferenceCheckHelper.throwIfNull(building_profit_history_agent_action, () => $"building_profit_history_agent_action is null !!!"); building_profit_history_agent_action.addBuildingProfitHistory(building_profit_history); send_S2C_ACK_GAIN_LAND_PROFIT(player, result, res); // 전 서버 동기화 BuildingProfitNotifyHelper.send_GS2GS_NTF_MODIFY_BUILDING_PROFIT(building_map_tree.BuildingMetaId, modify_profits.Values.ToList()); BuildingProfitHistoryNotifyHelper.send_GS2GS_NTF_ADD_BUILDING_PROFIT_HISTORY(building_profit_history); } return result; } }