diff --git a/common/repositories/inventory_snapshots_repository.h b/common/repositories/inventory_snapshots_repository.h index 88671cdd7..674e8bb1b 100644 --- a/common/repositories/inventory_snapshots_repository.h +++ b/common/repositories/inventory_snapshots_repository.h @@ -81,7 +81,7 @@ public: return -1; } - auto &row = results.begin(); + auto row = results.begin(); const int64 count = Strings::ToBigInt(row[0]); if (count > std::numeric_limits::max()) { diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 98f02fada..3ebd0e97e 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -746,7 +746,7 @@ bool SharedDatabase::GetInventory(Client *c) inst->SetColor(color); } - if (charges > std::numeric_limits::max()) { + if (charges >= std::numeric_limits::max()) { inst->SetCharges(-1); } else if (charges == 0 && inst->IsStackable()) { // Stackable items need a minimum charge of 1 remain moveable. diff --git a/common/strings.cpp b/common/strings.cpp index 29bef8993..a4ea9f3da 100644 --- a/common/strings.cpp +++ b/common/strings.cpp @@ -961,14 +961,14 @@ bool Strings::IsValidJson(const std::string &json) return result; } -std::string Strings::Implode(const std::string& glue, std::unordered_set src) +std::string Strings::Implode(const std::string& glue, const std::unordered_set& src) { if (src.empty()) { return {}; } - std::ostringstream output; - std::unordered_set::iterator src_iter; + std::ostringstream output; + std::unordered_set::const_iterator src_iter; for (src_iter = src.begin(); src_iter != src.end(); src_iter++) { output << *src_iter << glue; diff --git a/common/strings.h b/common/strings.h index ab2e71011..ef7d93440 100644 --- a/common/strings.h +++ b/common/strings.h @@ -77,7 +77,7 @@ public: static std::string Escape(const std::string &s); static std::string GetBetween(const std::string &s, std::string start_delim, std::string stop_delim); static std::string Implode(const std::string& glue, std::vector src); - static std::string Implode(const std::string& glue, std::unordered_set src); + static std::string Implode(const std::string& glue, const std::unordered_set& src); static std::string Join(const std::vector &ar, const std::string &delim); static std::string Join(const std::vector &ar, const std::string &delim); static std::string MillisecondsToTime(int duration); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 19ff065a0..399c47f48 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -4700,8 +4700,9 @@ bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst) int32 quantity; }; - std::vector queue; - auto quantity = inst->GetCharges(); + std::vector queue; + std::vector empty_bag_slots; + auto quantity = inst->GetCharges(); for (int i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { if (quantity == 0) { @@ -4710,7 +4711,6 @@ bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst) auto inv_inst = GetInv().GetItem(i); if (!inv_inst) { - // Empty general slot — skip for now; will fall back to free_id after scanning for stacks continue; } @@ -4723,12 +4723,12 @@ bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst) } auto bag_inst = GetInv().GetItem(base_slot_id + bag_slot); - if (!bag_inst) { - // Empty bag slot — skip for now; will fall back to free_id after scanning for stacks + if (!bag_inst && inv_inst->GetItem()->BagSize >= inst->GetItem()->Size) { + empty_bag_slots.push_back(base_slot_id + bag_slot); continue; } - if (bag_inst->IsStackable() && bag_inst->GetID() == inst->GetID()) { + if (bag_inst && bag_inst->IsStackable() && bag_inst->GetID() == inst->GetID()) { auto stack_size = bag_inst->GetItem()->StackSize; auto bag_inst_quantity = bag_inst->GetCharges(); int16 temp_slot = base_slot_id + bag_slot; @@ -4749,15 +4749,30 @@ bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst) } if (!queue.empty()) { + bool success = true; database.TransactionBegin(); for (auto const &i: queue) { auto bag_inst = GetInv().GetItem(i.slot_id); if (!bag_inst) { LogError("Client inventory error occurred. Character ID {} Slot_ID {}", CharacterID(), i.slot_id); - continue; + success = false; + break; } bag_inst->SetCharges(i.quantity + bag_inst->GetCharges()); - PutItemInInventory(i.slot_id, *bag_inst, true); + if (!PutItemInInventory(i.slot_id, *bag_inst, true)) { + LogError( + "Failed to save stacked item to inventory. Character ID {} Slot_ID {}", + CharacterID(), + i.slot_id + ); + success = false; + break; + } + } + + if (!success) { + database.TransactionRollback(); + return false; } database.TransactionCommit(); @@ -4768,13 +4783,19 @@ bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst) } inst->SetCharges(quantity); + for (auto slot_id : empty_bag_slots) { + if (PutItemInInventory(slot_id, *inst, true)) { + return true; + } + } + if (free_id != INVALID_INDEX && !EQ::ValueWithin(free_id, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END) && PutItemInInventory(free_id, *inst, true)) { return true; } - LogError("Could not find enough room"); + LogError("Could not find enough room for item {} (quantity {}) for character {}", inst->GetItem()->Name, quantity, CharacterID()); return false; } diff --git a/zone/mob.h b/zone/mob.h index 4f5b27b73..c0970f25c 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1968,7 +1968,7 @@ private: gender = in.gender; race = in.race; base_gender = in.base_gender; - base_race = in.race; + base_race = in.base_race; use_model = in.use_model; class_ = in.class_; bodytype = in.bodytype; diff --git a/zone/parcels.cpp b/zone/parcels.cpp index 12281aa67..75d2f4b36 100644 --- a/zone/parcels.cpp +++ b/zone/parcels.cpp @@ -453,7 +453,6 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in) cpc.quantity = item->GetCharges() >= 0 ? item->GetCharges() : 1; cpc.evolve_amount = item->GetEvolveCurrentAmount(); - cpc.quantity = item->GetCharges() >= 0 ? item->GetCharges() : 1; all_entries.push_back(cpc); } CharacterParcelsContainersRepository::InsertMany(database, all_entries);