Merge remote-tracking branch 'origin/codex/bazaar-offline-trading-rework' into copilot/sub-pr-80-again

# Conflicts:
#	zone/inventory.cpp
This commit is contained in:
copilot-swe-agent[bot]
2026-03-20 02:46:49 +00:00
7 changed files with 37 additions and 17 deletions
@@ -81,7 +81,7 @@ public:
return -1; return -1;
} }
auto &row = results.begin(); auto row = results.begin();
const int64 count = Strings::ToBigInt(row[0]); const int64 count = Strings::ToBigInt(row[0]);
if (count > std::numeric_limits<int>::max()) { if (count > std::numeric_limits<int>::max()) {
+1 -1
View File
@@ -746,7 +746,7 @@ bool SharedDatabase::GetInventory(Client *c)
inst->SetColor(color); inst->SetColor(color);
} }
if (charges > std::numeric_limits<int16>::max()) { if (charges >= std::numeric_limits<int16>::max()) {
inst->SetCharges(-1); inst->SetCharges(-1);
} else if (charges == 0 && inst->IsStackable()) { } else if (charges == 0 && inst->IsStackable()) {
// Stackable items need a minimum charge of 1 remain moveable. // Stackable items need a minimum charge of 1 remain moveable.
+3 -3
View File
@@ -961,14 +961,14 @@ bool Strings::IsValidJson(const std::string &json)
return result; return result;
} }
std::string Strings::Implode(const std::string& glue, std::unordered_set<std::string> src) std::string Strings::Implode(const std::string& glue, const std::unordered_set<std::string>& src)
{ {
if (src.empty()) { if (src.empty()) {
return {}; return {};
} }
std::ostringstream output; std::ostringstream output;
std::unordered_set<std::string>::iterator src_iter; std::unordered_set<std::string>::const_iterator src_iter;
for (src_iter = src.begin(); src_iter != src.end(); src_iter++) { for (src_iter = src.begin(); src_iter != src.end(); src_iter++) {
output << *src_iter << glue; output << *src_iter << glue;
+1 -1
View File
@@ -77,7 +77,7 @@ public:
static std::string Escape(const std::string &s); 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 GetBetween(const std::string &s, std::string start_delim, std::string stop_delim);
static std::string Implode(const std::string& glue, std::vector<std::string> src); static std::string Implode(const std::string& glue, std::vector<std::string> src);
static std::string Implode(const std::string& glue, std::unordered_set<std::string> src); static std::string Implode(const std::string& glue, const std::unordered_set<std::string>& src);
static std::string Join(const std::vector<std::string> &ar, const std::string &delim); static std::string Join(const std::vector<std::string> &ar, const std::string &delim);
static std::string Join(const std::vector<uint32_t> &ar, const std::string &delim); static std::string Join(const std::vector<uint32_t> &ar, const std::string &delim);
static std::string MillisecondsToTime(int duration); static std::string MillisecondsToTime(int duration);
+30 -9
View File
@@ -4700,8 +4700,9 @@ bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst)
int32 quantity; int32 quantity;
}; };
std::vector<temp> queue; std::vector<temp> queue;
auto quantity = inst->GetCharges(); std::vector<int16> empty_bag_slots;
auto quantity = inst->GetCharges();
for (int i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { for (int i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) {
if (quantity == 0) { if (quantity == 0) {
@@ -4710,7 +4711,6 @@ bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst)
auto inv_inst = GetInv().GetItem(i); auto inv_inst = GetInv().GetItem(i);
if (!inv_inst) { if (!inv_inst) {
// Empty general slot — skip for now; will fall back to free_id after scanning for stacks
continue; continue;
} }
@@ -4723,12 +4723,12 @@ bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst)
} }
auto bag_inst = GetInv().GetItem(base_slot_id + bag_slot); auto bag_inst = GetInv().GetItem(base_slot_id + bag_slot);
if (!bag_inst) { if (!bag_inst && inv_inst->GetItem()->BagSize >= inst->GetItem()->Size) {
// Empty bag slot — skip for now; will fall back to free_id after scanning for stacks empty_bag_slots.push_back(base_slot_id + bag_slot);
continue; 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 stack_size = bag_inst->GetItem()->StackSize;
auto bag_inst_quantity = bag_inst->GetCharges(); auto bag_inst_quantity = bag_inst->GetCharges();
int16 temp_slot = base_slot_id + bag_slot; int16 temp_slot = base_slot_id + bag_slot;
@@ -4749,15 +4749,30 @@ bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst)
} }
if (!queue.empty()) { if (!queue.empty()) {
bool success = true;
database.TransactionBegin(); database.TransactionBegin();
for (auto const &i: queue) { for (auto const &i: queue) {
auto bag_inst = GetInv().GetItem(i.slot_id); auto bag_inst = GetInv().GetItem(i.slot_id);
if (!bag_inst) { if (!bag_inst) {
LogError("Client inventory error occurred. Character ID {} Slot_ID {}", CharacterID(), i.slot_id); 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()); 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(); database.TransactionCommit();
@@ -4768,13 +4783,19 @@ bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst)
} }
inst->SetCharges(quantity); inst->SetCharges(quantity);
for (auto slot_id : empty_bag_slots) {
if (PutItemInInventory(slot_id, *inst, true)) {
return true;
}
}
if (free_id != INVALID_INDEX && if (free_id != INVALID_INDEX &&
!EQ::ValueWithin(free_id, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END) && !EQ::ValueWithin(free_id, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END) &&
PutItemInInventory(free_id, *inst, true)) { PutItemInInventory(free_id, *inst, true)) {
return 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; return false;
} }
+1 -1
View File
@@ -1968,7 +1968,7 @@ private:
gender = in.gender; gender = in.gender;
race = in.race; race = in.race;
base_gender = in.base_gender; base_gender = in.base_gender;
base_race = in.race; base_race = in.base_race;
use_model = in.use_model; use_model = in.use_model;
class_ = in.class_; class_ = in.class_;
bodytype = in.bodytype; bodytype = in.bodytype;
-1
View File
@@ -453,7 +453,6 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in)
cpc.quantity = item->GetCharges() >= 0 ? item->GetCharges() : 1; cpc.quantity = item->GetCharges() >= 0 ? item->GetCharges() : 1;
cpc.evolve_amount = item->GetEvolveCurrentAmount(); cpc.evolve_amount = item->GetEvolveCurrentAmount();
cpc.quantity = item->GetCharges() >= 0 ? item->GetCharges() : 1;
all_entries.push_back(cpc); all_entries.push_back(cpc);
} }
CharacterParcelsContainersRepository::InsertMany(database, all_entries); CharacterParcelsContainersRepository::InsertMany(database, all_entries);