From ed32423670e21d88c76f8a094bddf9d9f1a79698 Mon Sep 17 00:00:00 2001 From: Ryan Yappert Date: Fri, 27 Sep 2024 01:22:06 -0700 Subject: [PATCH 1/2] Basic guards on changing jobs/getting quest rewards with a full box. --- .../Characters/JobManager.cs | 17 ++++++++++- .../Handler/JobChangeJobHandler.cs | 28 +++++++++++-------- .../Handler/PawnCreatePawnHandler.cs | 17 +++++++++++ .../Handler/QuestGetRewardBoxItemHandler.cs | 10 +++++++ 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/Arrowgene.Ddon.GameServer/Characters/JobManager.cs b/Arrowgene.Ddon.GameServer/Characters/JobManager.cs index d2a3226f7..9efd29ab6 100644 --- a/Arrowgene.Ddon.GameServer/Characters/JobManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/JobManager.cs @@ -31,11 +31,26 @@ public JobManager(DdonGameServer server) _Server = server; } - public (IPacketStructure jobRes, IPacketStructure itemNtc, IPacketStructure jobNtc) SetJob(GameClient client, CharacterCommon common, JobId jobId, DbConnection? connectionIn = null) + public (IPacketStructure? jobRes, IPacketStructure? itemNtc, IPacketStructure? jobNtc) SetJob(GameClient client, CharacterCommon common, JobId jobId, DbConnection? connectionIn = null) { // TODO: Reject job change if there's no primary and secondary weapon for the new job in storage // (or give a lvl 1 weapon for free?) + var totalSlots = common.Equipment.GetItems(EquipType.Performance) + .Concat(common.Equipment.GetItems(EquipType.Visual)) + .Where(x => x != null) + .ToList() + .Count; + if (totalSlots > client.Character.Storage.GetStorage(StorageType.StorageBoxNormal).EmptySlots()) + { + return (new S2CJobChangeJobRes() + { + Error = (uint)ErrorCode.ERROR_CODE_JOBCHANGE_ITEM_CAPACITY_OVER + }, + null, + null); + } + JobId oldJobId = common.Job; common.Job = jobId; diff --git a/Arrowgene.Ddon.GameServer/Handler/JobChangeJobHandler.cs b/Arrowgene.Ddon.GameServer/Handler/JobChangeJobHandler.cs index 40c246f7c..7ce2b6c8e 100644 --- a/Arrowgene.Ddon.GameServer/Handler/JobChangeJobHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/JobChangeJobHandler.cs @@ -1,15 +1,11 @@ +using Arrowgene.Ddon.GameServer.Characters; using Arrowgene.Ddon.Server; -using Arrowgene.Ddon.Server.Network; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Logging; -using Arrowgene.Ddon.Shared.Network; -using Arrowgene.Ddon.GameServer.Characters; -using Arrowgene.Ddon.Shared.Entity.Structure; -using Arrowgene.Ddon.Shared.Entity; namespace Arrowgene.Ddon.GameServer.Handler { - public class JobChangeJobHandler : StructurePacketHandler + public class JobChangeJobHandler : GameRequestPacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(JobChangeJobHandler)); @@ -20,22 +16,30 @@ public JobChangeJobHandler(DdonGameServer server) : base(server) jobManager = server.JobManager; } - public override void Handle(GameClient client, StructurePacket packet) + public override S2CJobChangeJobRes Handle(GameClient client, C2SJobChangeJobReq request) { (S2CJobChangeJobRes jobRes, S2CItemUpdateCharacterItemNtc itemNtc, S2CJobChangeJobNtc jobNtc) jobResult = (null, null, null); Server.Database.ExecuteInTransaction(connection => { jobResult = ((S2CJobChangeJobRes, S2CItemUpdateCharacterItemNtc, S2CJobChangeJobNtc)) - jobManager.SetJob(client, client.Character, packet.Structure.JobId, connection); + jobManager.SetJob(client, client.Character, request.JobId, connection); }); - foreach (GameClient otherClient in Server.ClientLookup.GetAll()) + if (jobResult.jobNtc != null) { - otherClient.Send(jobResult.jobNtc); + foreach (GameClient otherClient in Server.ClientLookup.GetAll()) + { + otherClient.Send(jobResult.jobNtc); + } } - client.Send(jobResult.itemNtc); - client.Send(jobResult.jobRes); + + if (jobResult.itemNtc != null) + { + client.Send(jobResult.itemNtc); + } + + return jobResult.jobRes; } } } diff --git a/Arrowgene.Ddon.GameServer/Handler/PawnCreatePawnHandler.cs b/Arrowgene.Ddon.GameServer/Handler/PawnCreatePawnHandler.cs index adb012b34..429f7c00c 100644 --- a/Arrowgene.Ddon.GameServer/Handler/PawnCreatePawnHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/PawnCreatePawnHandler.cs @@ -22,6 +22,12 @@ public PawnCreatePawnHandler(DdonGameServer server) : base(server) public override S2CPawnCreatePawnRes Handle(GameClient client, C2SPawnCreatePawnReq request) { + // I hate hardcoding this but people legitimately keep finding ways to break this. + if (request.SlotNo > 3) + { + throw new ResponseErrorException(ErrorCode.ERROR_CODE_PAWN_CREATE_NUM_OVER); + } + if (request.SlotNo == 1) { const byte myPawnSlotNum = 2; @@ -62,6 +68,17 @@ public override S2CPawnCreatePawnRes Handle(GameClient client, C2SPawnCreatePawn Error = (uint)ErrorCode.ERROR_CODE_CHARACTER_ITEM_NOT_FOUND }; } + else + { + client.Send(new S2CItemUpdateCharacterItemNtc() + { + UpdateType = ItemNoticeType.CreatePawn, + UpdateItemList = new() + { + result + } + }); + } } Pawn pawn = new Pawn(client.Character.CharacterId) diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxItemHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxItemHandler.cs index 1ac74a457..d114a74ed 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxItemHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxItemHandler.cs @@ -62,6 +62,16 @@ public override S2CQuestGetRewardBoxItemRes Handle(GameClient client, C2SQuestGe } } + var slotCount = coalescedRewards + .Where(x => !Server.ItemManager.IsItemWalletPoint(x.Value.ItemId) && x.Value.Num > 0) + .ToList() + .Count; + + if (slotCount > client.Character.Storage.GetStorage(StorageType.StorageBoxNormal).EmptySlots()) + { + throw new ResponseErrorException(ErrorCode.ERROR_CODE_ITEM_STORAGE_OVERFLOW); + } + foreach (var rewardUID in packet.GetRewardBoxItemList.Select(x => x.UID).Distinct().ToList()) { var reward = coalescedRewards[rewardUID]; From 348b1860a50432036ce48d4e70f39fbbbde6c750 Mon Sep 17 00:00:00 2001 From: Ryan Yappert Date: Thu, 3 Oct 2024 13:18:35 -0700 Subject: [PATCH 2/2] Review comments. --- .../Characters/JobManager.cs | 39 +++++++++++++++---- .../Handler/JobChangePawnJobHandler.cs | 14 +++++-- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Arrowgene.Ddon.GameServer/Characters/JobManager.cs b/Arrowgene.Ddon.GameServer/Characters/JobManager.cs index 9efd29ab6..20d948604 100644 --- a/Arrowgene.Ddon.GameServer/Characters/JobManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/JobManager.cs @@ -31,17 +31,12 @@ public JobManager(DdonGameServer server) _Server = server; } - public (IPacketStructure? jobRes, IPacketStructure? itemNtc, IPacketStructure? jobNtc) SetJob(GameClient client, CharacterCommon common, JobId jobId, DbConnection? connectionIn = null) + public (IPacketStructure jobRes, IPacketStructure? itemNtc, IPacketStructure? jobNtc) SetJob(GameClient client, CharacterCommon common, JobId jobId, DbConnection? connectionIn = null) { // TODO: Reject job change if there's no primary and secondary weapon for the new job in storage // (or give a lvl 1 weapon for free?) - var totalSlots = common.Equipment.GetItems(EquipType.Performance) - .Concat(common.Equipment.GetItems(EquipType.Visual)) - .Where(x => x != null) - .ToList() - .Count; - if (totalSlots > client.Character.Storage.GetStorage(StorageType.StorageBoxNormal).EmptySlots()) + if (!HasEmptySlotsForTemplateSwap(client, common, common.Job, jobId)) { return (new S2CJobChangeJobRes() { @@ -293,6 +288,36 @@ private List SwapEquipmentAndStorage(GameClient client, C return itemUpdateResultList; } + private static bool HasEmptySlotsForTemplateSwap(GameClient client, CharacterCommon common, JobId oldJobId, JobId newJobId) + { + var availableSlots = client.Character.Storage.GetStorage(StorageType.StorageBoxNormal).EmptySlots(); + + var neededSlots = common.Equipment.GetItems(EquipType.Performance) + .Concat(common.Equipment.GetItems(EquipType.Visual)) + .Where(x => x != null) + .ToList() + .Count; + + // If the item isn't moving, it doesn't need a space in the box. + foreach (var equipType in new List(){ EquipType.Performance, EquipType.Visual }) + { + List oldEquipment = common.Equipment.GetItems(equipType); + List newEquipmentTemplate = common.EquipmentTemplate.GetEquipment(newJobId, equipType); + + for (int i = 0; i < oldEquipment.Count; i++) + { + if (oldEquipment[i] != null && newEquipmentTemplate[i] != null && oldEquipment[i] == newEquipmentTemplate[i]) + { + neededSlots--; + } + } + } + + // TODO: Account for one-to-one swaps, as well as jewelry shuffling order. + + return neededSlots <= availableSlots; + } + public void UnlockSkill(IDatabase database, GameClient client, CharacterCommon character, JobId job, uint skillId, byte skillLv) { // Check if there is a learned skill of the same ID (This unlock is a level upgrade) diff --git a/Arrowgene.Ddon.GameServer/Handler/JobChangePawnJobHandler.cs b/Arrowgene.Ddon.GameServer/Handler/JobChangePawnJobHandler.cs index 3db354900..9806641b5 100644 --- a/Arrowgene.Ddon.GameServer/Handler/JobChangePawnJobHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/JobChangePawnJobHandler.cs @@ -30,11 +30,19 @@ public override void Handle(GameClient client, StructurePacket