Merge pull request #1862 from DarkflameUniverse/fix-item-exploits

fix: item exploits
This commit is contained in:
Gie "Max" Vanommeslaeghe
2025-09-01 22:33:07 +02:00
committed by GitHub
25 changed files with 44 additions and 38 deletions

View File

@@ -496,7 +496,7 @@ void Character::OnZoneLoad() {
// Remove all GM items // Remove all GM items
for (const auto lot : Inventory::GetAllGMItems()) { for (const auto lot : Inventory::GetAllGMItems()) {
inventoryComponent->RemoveItem(lot, inventoryComponent->GetLotCount(lot)); inventoryComponent->RemoveItem(lot, inventoryComponent->GetLotCount(lot), eInventoryType::ALL);
} }
} }

View File

@@ -12,7 +12,7 @@ void ConsumeItemBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bi
auto inventoryComponent = caster->GetComponent<InventoryComponent>(); auto inventoryComponent = caster->GetComponent<InventoryComponent>();
if (!inventoryComponent) return; if (!inventoryComponent) return;
if (inventoryComponent->RemoveItem(this->m_ConsumeLOT, this->m_NumToConsume, eInventoryType::INVALID, false, true)){ if (inventoryComponent->RemoveItem(this->m_ConsumeLOT, this->m_NumToConsume, eInventoryType::ALL, false, true)){
action_to_cast = m_ActionConsumed; action_to_cast = m_ActionConsumed;
} }
} }

View File

@@ -64,12 +64,11 @@ void AchievementVendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
} }
const uint32_t altCurrencyCost = itemComp.commendationCost * count; const uint32_t altCurrencyCost = itemComp.commendationCost * count;
if (inventoryComponent->GetLotCount(costLOT) < altCurrencyCost) { if (inventoryComponent->GetLotCount(costLOT) < altCurrencyCost || !inventoryComponent->RemoveItem(costLOT, altCurrencyCost, eInventoryType::ALL)) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL); GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return; return;
} }
inventoryComponent->RemoveItem(costLOT, altCurrencyCost);
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR); inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS); GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);

View File

@@ -354,10 +354,7 @@ bool ActivityComponent::CheckCost(Entity* player) const {
bool ActivityComponent::TakeCost(Entity* player) const { bool ActivityComponent::TakeCost(Entity* player) const {
auto* inventoryComponent = player->GetComponent<InventoryComponent>(); auto* inventoryComponent = player->GetComponent<InventoryComponent>();
if (CheckCost(player)) { return CheckCost(player) && inventoryComponent->RemoveItem(m_ActivityInfo.optionalCostLOT, m_ActivityInfo.optionalCostCount, eInventoryType::ALL);
inventoryComponent->RemoveItem(m_ActivityInfo.optionalCostLOT, m_ActivityInfo.optionalCostCount);
return true;
} else return false;
} }
void ActivityComponent::PlayerReady(Entity* player, bool bReady) { void ActivityComponent::PlayerReady(Entity* player, bool bReady) {

View File

@@ -180,7 +180,6 @@ void InventoryComponent::AddItem(
const int32_t sourceType, const int32_t sourceType,
const bool bound, const bool bound,
int32_t preferredSlot) { int32_t preferredSlot) {
LOG("AddItem %i %i %s %s", lot, count, StringifiedEnum::ToString(lootSourceType).data(), StringifiedEnum::ToString(inventoryType).data());
if (count == 0) { if (count == 0) {
LOG("Attempted to add 0 of item (%i) to the inventory!", lot); LOG("Attempted to add 0 of item (%i) to the inventory!", lot);

View File

@@ -164,10 +164,17 @@ void VendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
return; return;
} }
} }
bool success = true;
for (const auto& [crafintCurrencyLOT, crafintCurrencyCount]: craftingCurrencies) { for (const auto& [crafintCurrencyLOT, crafintCurrencyCount]: craftingCurrencies) {
inventoryComponent->RemoveItem(crafintCurrencyLOT, crafintCurrencyCount * count); success = inventoryComponent->RemoveItem(crafintCurrencyLOT, crafintCurrencyCount * count, eInventoryType::ALL);
if (!success) break;
} }
if (!success) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return;
}
float buyScalar = GetBuyScalar(); float buyScalar = GetBuyScalar();
const auto coinCost = static_cast<uint32_t>(std::floor((itemComp.baseValue * buyScalar) * count)); const auto coinCost = static_cast<uint32_t>(std::floor((itemComp.baseValue * buyScalar) * count));
@@ -184,7 +191,7 @@ void VendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL); GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return; return;
} }
inventoryComponent->RemoveItem(itemComp.currencyLOT, altCurrencyCost); inventoryComponent->RemoveItem(itemComp.currencyLOT, altCurrencyCost, eInventoryType::ALL);
} }
character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR); character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR);

View File

@@ -4823,11 +4823,10 @@ void GameMessages::HandleBuybackFromVendor(RakNet::BitStream& inStream, Entity*
if (Inventory::IsValidItem(itemComp.currencyLOT)) { if (Inventory::IsValidItem(itemComp.currencyLOT)) {
const uint32_t altCurrencyCost = std::floor(itemComp.altCurrencyCost * sellScalar) * count; const uint32_t altCurrencyCost = std::floor(itemComp.altCurrencyCost * sellScalar) * count;
if (inv->GetLotCount(itemComp.currencyLOT) < altCurrencyCost) { if (inv->GetLotCount(itemComp.currencyLOT) < altCurrencyCost || !inv->RemoveItem(itemComp.currencyLOT, altCurrencyCost, eInventoryType::ALL)) {
GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::PURCHASE_FAIL); GameMessages::SendVendorTransactionResult(entity, sysAddr, eVendorTransactionResult::PURCHASE_FAIL);
return; return;
} }
inv->RemoveItem(itemComp.currencyLOT, altCurrencyCost);
} }
//inv->RemoveItem(count, -1, iObjID); //inv->RemoveItem(count, -1, iObjID);
@@ -5508,10 +5507,18 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream& inStream, Entity*
modules += u"1:" + (modToStr); modules += u"1:" + (modToStr);
if (k + 1 != count) modules += u"+"; if (k + 1 != count) modules += u"+";
bool hasItem = false;
if (temp->GetLotCount(mod) > 0) { if (temp->GetLotCount(mod) > 0) {
inv->RemoveItem(mod, 1, TEMP_MODELS); hasItem = inv->RemoveItem(mod, 1, TEMP_MODELS);
} else { } else {
inv->RemoveItem(mod, 1); hasItem = inv->RemoveItem(mod, 1, eInventoryType::ALL);
}
if (!hasItem) {
LOG("Player (%llu) attempted to finish a modular build without having all the required parts.", character->GetObjectID());
GameMessages::SendFinishArrangingWithItem(character, entity->GetObjectID()); // kick them from modular build
GameMessages::SendModularBuildEnd(character); // i dont know if this does anything but DLUv2 did it
return;
} }
// Doing this check for 1 singular mission that needs to know when you've swapped every part out during a car modular build. // Doing this check for 1 singular mission that needs to know when you've swapped every part out during a car modular build.

View File

@@ -289,11 +289,10 @@ bool Item::Consume() {
GameMessages::SendUseItemResult(inventory->GetComponent()->GetParent(), lot, success); GameMessages::SendUseItemResult(inventory->GetComponent()->GetParent(), lot, success);
if (success) { const auto myLot = this->lot;
if (success && inventory->GetComponent()->RemoveItem(lot, 1, eInventoryType::ALL)) {
// Save this because if this is the last item in the inventory // Save this because if this is the last item in the inventory
// we may delete ourself (lol) // we may delete ourself (lol)
const auto myLot = this->lot;
inventory->GetComponent()->RemoveItem(lot, 1);
auto* missionComponent = inventory->GetComponent()->GetParent()->GetComponent<MissionComponent>(); auto* missionComponent = inventory->GetComponent()->GetParent()->GetComponent<MissionComponent>();
if (missionComponent) missionComponent->Progress(eMissionTaskType::GATHER, myLot, LWOOBJID_EMPTY, "", -1); if (missionComponent) missionComponent->Progress(eMissionTaskType::GATHER, myLot, LWOOBJID_EMPTY, "", -1);
} }

View File

@@ -106,7 +106,7 @@ namespace Mail {
// Remove coins and items from the sender // Remove coins and items from the sender
player->GetCharacter()->SetCoins(player->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL); player->GetCharacter()->SetCoins(player->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL);
if (inventoryComponent && hasAttachment && item) { if (inventoryComponent && hasAttachment && item) {
removeSuccess = inventoryComponent->RemoveItem(mailInfo.itemLOT, mailInfo.itemCount, INVALID, true); removeSuccess = inventoryComponent->RemoveItem(mailInfo.itemLOT, mailInfo.itemCount, ALL, true);
auto* missionComponent = player->GetComponent<MissionComponent>(); auto* missionComponent = player->GetComponent<MissionComponent>();
if (missionComponent && removeSuccess) missionComponent->Progress(eMissionTaskType::GATHER, mailInfo.itemLOT, LWOOBJID_EMPTY, "", -mailInfo.itemCount); if (missionComponent && removeSuccess) missionComponent->Progress(eMissionTaskType::GATHER, mailInfo.itemLOT, LWOOBJID_EMPTY, "", -mailInfo.itemCount);
} }

View File

@@ -130,9 +130,7 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat
case PreconditionType::HasItem: case PreconditionType::HasItem:
if (evaluateCosts) // As far as I know this is only used for quickbuilds, and removal shouldn't actually be handled here. if (evaluateCosts) // As far as I know this is only used for quickbuilds, and removal shouldn't actually be handled here.
{ {
inventoryComponent->RemoveItem(value, count); return inventoryComponent->RemoveItem(value, count, eInventoryType::ALL);
return true;
} }
return inventoryComponent->GetLotCount(value) >= count; return inventoryComponent->GetLotCount(value) >= count;

View File

@@ -24,6 +24,6 @@ void AgCagedBricksServer::OnUse(Entity* self, Entity* user) {
auto inv = static_cast<InventoryComponent*>(user->GetComponent(eReplicaComponentType::INVENTORY)); auto inv = static_cast<InventoryComponent*>(user->GetComponent(eReplicaComponentType::INVENTORY));
if (inv) { if (inv) {
inv->RemoveItem(14553, 1); inv->RemoveItem(14553, 1, eInventoryType::ALL);
} }
} }

View File

@@ -22,7 +22,7 @@ void NpcCowboyServer::OnMissionDialogueOK(Entity* self, Entity* target, int miss
inventoryComponent->AddItem(14378, 1, eLootSourceType::NONE); inventoryComponent->AddItem(14378, 1, eLootSourceType::NONE);
} }
} else if (missionState == eMissionState::READY_TO_COMPLETE || missionState == eMissionState::COMPLETE_READY_TO_COMPLETE) { } else if (missionState == eMissionState::READY_TO_COMPLETE || missionState == eMissionState::COMPLETE_READY_TO_COMPLETE) {
inventoryComponent->RemoveItem(14378, 1); inventoryComponent->RemoveItem(14378, 1, eInventoryType::ALL);
} }
// Next up hide or show the samples based on the mission state // Next up hide or show the samples based on the mission state

View File

@@ -22,7 +22,7 @@ void NpcWispServer::OnMissionDialogueOK(Entity* self, Entity* target, int missio
&& maelstromVacuum == nullptr) { && maelstromVacuum == nullptr) {
inventory->AddItem(maelstromVacuumLot, 1, eLootSourceType::NONE); inventory->AddItem(maelstromVacuumLot, 1, eLootSourceType::NONE);
} else if (missionState == eMissionState::READY_TO_COMPLETE || missionState == eMissionState::COMPLETE_READY_TO_COMPLETE) { } else if (missionState == eMissionState::READY_TO_COMPLETE || missionState == eMissionState::COMPLETE_READY_TO_COMPLETE) {
inventory->RemoveItem(maelstromVacuumLot, 1); inventory->RemoveItem(maelstromVacuumLot, 1, eInventoryType::ALL);
} }
// Next up hide or show the samples based on the mission state // Next up hide or show the samples based on the mission state

View File

@@ -31,7 +31,7 @@ void RemoveRentalGear::OnMissionDialogueOK(Entity* self, Entity* target, int mis
auto* id = inv->FindItemByLot(item); auto* id = inv->FindItemByLot(item);
if (id) { if (id) {
inv->UnEquipItem(id); inv->UnEquipItem(id);
inv->RemoveItem(id->GetLot(), id->GetCount()); inv->RemoveItem(id->GetLot(), id->GetCount(), eInventoryType::ALL);
} }
} }

View File

@@ -74,13 +74,13 @@ void ImgBrickConsoleQB::OnUse(Entity* self, Entity* user) {
if (missionComponent != nullptr && inventoryComponent != nullptr) { if (missionComponent != nullptr && inventoryComponent != nullptr) {
if (missionComponent->GetMissionState(1302) == eMissionState::ACTIVE) { if (missionComponent->GetMissionState(1302) == eMissionState::ACTIVE) {
inventoryComponent->RemoveItem(13074, 1); inventoryComponent->RemoveItem(13074, 1, eInventoryType::ALL);
missionComponent->ForceProgressTaskType(1302, 1, 1); missionComponent->ForceProgressTaskType(1302, 1, 1);
} }
if (missionComponent->GetMissionState(1926) == eMissionState::ACTIVE) { if (missionComponent->GetMissionState(1926) == eMissionState::ACTIVE) {
inventoryComponent->RemoveItem(14472, 1); inventoryComponent->RemoveItem(14472, 1, eInventoryType::ALL);
missionComponent->ForceProgressTaskType(1926, 1, 1); missionComponent->ForceProgressTaskType(1926, 1, 1);
} }

View File

@@ -14,7 +14,7 @@ void TokenConsoleServer::OnUse(Entity* self, Entity* user) {
//make sure the user has the required amount of infected bricks //make sure the user has the required amount of infected bricks
if (inv && inv->GetLotCount(6194) >= bricksToTake) { if (inv && inv->GetLotCount(6194) >= bricksToTake) {
//yeet the bricks //yeet the bricks
inv->RemoveItem(6194, bricksToTake); inv->RemoveItem(6194, bricksToTake, eInventoryType::ALL);
//play sound //play sound
if (self->HasVar(u"sound1")) { if (self->HasVar(u"sound1")) {

View File

@@ -34,7 +34,7 @@ void NsTokenConsoleServer::OnUse(Entity* self, Entity* user) {
return; return;
} }
inventoryComponent->RemoveItem(6194, 25); inventoryComponent->RemoveItem(6194, 25, eInventoryType::ALL);
const auto useSound = self->GetVar<std::string>(u"sound1"); const auto useSound = self->GetVar<std::string>(u"sound1");

View File

@@ -51,7 +51,7 @@ void NtCombatChallengeServer::OnMessageBoxResponse(Entity* self, Entity* sender,
auto* inventoryComponent = sender->GetComponent<InventoryComponent>(); auto* inventoryComponent = sender->GetComponent<InventoryComponent>();
if (inventoryComponent != nullptr) { if (inventoryComponent != nullptr) {
inventoryComponent->RemoveItem(3039, 1); inventoryComponent->RemoveItem(3039, 1, eInventoryType::ALL);
} }
GameMessages::SendPlayNDAudioEmitter(self, sender->GetSystemAddress(), startSound); GameMessages::SendPlayNDAudioEmitter(self, sender->GetSystemAddress(), startSound);

View File

@@ -34,7 +34,7 @@ void NtDukeServer::OnMissionDialogueOK(Entity* self, Entity* target, int mission
if ((state == eMissionState::AVAILABLE || state == eMissionState::ACTIVE) && lotCount < 1) { if ((state == eMissionState::AVAILABLE || state == eMissionState::ACTIVE) && lotCount < 1) {
inventoryComponent->AddItem(m_SwordLot, 1, eLootSourceType::NONE); inventoryComponent->AddItem(m_SwordLot, 1, eLootSourceType::NONE);
} else if (state == eMissionState::READY_TO_COMPLETE) { } else if (state == eMissionState::READY_TO_COMPLETE) {
inventoryComponent->RemoveItem(m_SwordLot, lotCount); inventoryComponent->RemoveItem(m_SwordLot, lotCount, eInventoryType::ALL);
} }
} }
NtBcSubmitServer::OnMissionDialogueOK(self, target, missionID, missionState); NtBcSubmitServer::OnMissionDialogueOK(self, target, missionID, missionState);

View File

@@ -8,7 +8,7 @@ void NtVandaServer::OnMissionDialogueOK(Entity* self, Entity* target, int missio
if (missionID == m_AlienPartMissionID && missionState == eMissionState::READY_TO_COMPLETE) { if (missionID == m_AlienPartMissionID && missionState == eMissionState::READY_TO_COMPLETE) {
auto* inventoryComponent = target->GetComponent<InventoryComponent>(); auto* inventoryComponent = target->GetComponent<InventoryComponent>();
for (const auto& alienPartLot : m_AlienPartLots) { for (const auto& alienPartLot : m_AlienPartLots) {
inventoryComponent->RemoveItem(alienPartLot, 1); inventoryComponent->RemoveItem(alienPartLot, 1, eInventoryType::ALL);
} }
} }
NtBcSubmitServer::OnMissionDialogueOK(self, target, missionID, missionState); NtBcSubmitServer::OnMissionDialogueOK(self, target, missionID, missionState);

View File

@@ -20,7 +20,7 @@ void SpawnGryphonServer::OnUse(Entity* self, Entity* user) {
// Little extra for handling the case of the egg being placed the first time // Little extra for handling the case of the egg being placed the first time
if (missionComponent != nullptr && inventoryComponent != nullptr if (missionComponent != nullptr && inventoryComponent != nullptr
&& missionComponent->GetMissionState(1391) == eMissionState::ACTIVE) { && missionComponent->GetMissionState(1391) == eMissionState::ACTIVE) {
inventoryComponent->RemoveItem(12483, inventoryComponent->GetLotCount(12483)); inventoryComponent->RemoveItem(12483, inventoryComponent->GetLotCount(12483), eInventoryType::ALL);
GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID()); GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, self->GetObjectID());
return; return;
} }

View File

@@ -39,7 +39,7 @@ void NjColeNPC::OnMissionDialogueOK(Entity* self, Entity* target, int missionID,
} }
if (inventoryComponent->GetLotCount(14499) > 0) { if (inventoryComponent->GetLotCount(14499) > 0) {
inventoryComponent->RemoveItem(14499, 1); inventoryComponent->RemoveItem(14499, 1, eInventoryType::ALL);
} else { } else {
return; return;
} }

View File

@@ -9,7 +9,7 @@ void NjScrollChestServer::OnUse(Entity* self, Entity* user) {
if (playerInventory != nullptr && playerInventory->GetLotCount(keyLOT) == 1) { if (playerInventory != nullptr && playerInventory->GetLotCount(keyLOT) == 1) {
// Check for the key and remove // Check for the key and remove
playerInventory->RemoveItem(keyLOT, 1); playerInventory->RemoveItem(keyLOT, 1, eInventoryType::ALL);
// Reward the player with the item set // Reward the player with the item set
playerInventory->AddItem(rewardItemLOT, 1, eLootSourceType::NONE); playerInventory->AddItem(rewardItemLOT, 1, eLootSourceType::NONE);

View File

@@ -14,7 +14,7 @@ void NPCAddRemoveItem::OnMissionDialogueOK(Entity* self, Entity* target, int mis
if (itemSetting.add && (missionState == eMissionState::AVAILABLE || missionState == eMissionState::COMPLETE_AVAILABLE)) { if (itemSetting.add && (missionState == eMissionState::AVAILABLE || missionState == eMissionState::COMPLETE_AVAILABLE)) {
inventory->AddItem(lot, 1, eLootSourceType::NONE); inventory->AddItem(lot, 1, eLootSourceType::NONE);
} else if (itemSetting.remove && (missionState == eMissionState::READY_TO_COMPLETE || missionState == eMissionState::COMPLETE_READY_TO_COMPLETE)) { } else if (itemSetting.remove && (missionState == eMissionState::READY_TO_COMPLETE || missionState == eMissionState::COMPLETE_READY_TO_COMPLETE)) {
inventory->RemoveItem(lot, 1); inventory->RemoveItem(lot, 1, eInventoryType::ALL);
} }
} }
} }

View File

@@ -24,7 +24,7 @@ void AgPropGuard::OnMissionDialogueOK(Entity* self, Entity* target, int missionI
if (id) { if (id) {
inventoryComponent->UnEquipItem(id); inventoryComponent->UnEquipItem(id);
inventoryComponent->RemoveItem(id->GetLot(), id->GetCount()); inventoryComponent->RemoveItem(id->GetLot(), id->GetCount(), eInventoryType::ALL);
} }
} }
} else if ( } else if (