diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/catalog/PurchaseTargetOfferEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/catalog/PurchaseTargetOfferEvent.java index 13fc307f..0eaf2a0f 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/catalog/PurchaseTargetOfferEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/catalog/PurchaseTargetOfferEvent.java @@ -31,18 +31,21 @@ public class PurchaseTargetOfferEvent extends MessageHandler { HabboOfferPurchase purchase = HabboOfferPurchase.getOrCreate(this.client.getHabbo(), offerId); if (purchase != null) { - amount = Math.min(offer.getPurchaseLimit() - purchase.getAmount(), amount); + amount = TargetOfferPurchaseGuard.purchasableAmount(amount, offer.getPurchaseLimit(), purchase.getAmount()); + if (amount <= 0) return; + int now = Emulator.getIntUnixTimestamp(); if (offer.getExpirationTime() > now) { - purchase.update(amount, now); CatalogItem item = Emulator.getGameEnvironment().getCatalogManager().getCatalogItem(offer.getCatalogItem()); + if (item == null) return; if (item.isLimited()) { amount = 1; } + purchase.update(amount, now); Emulator.getGameEnvironment().getCatalogManager().purchaseItem(null, item, this.client.getHabbo(), amount, "", false); } } } } -} \ No newline at end of file +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/catalog/TargetOfferPurchaseGuard.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/catalog/TargetOfferPurchaseGuard.java new file mode 100644 index 00000000..61000bf3 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/catalog/TargetOfferPurchaseGuard.java @@ -0,0 +1,19 @@ +package com.eu.habbo.messages.incoming.catalog; + +final class TargetOfferPurchaseGuard { + private TargetOfferPurchaseGuard() { + } + + static int purchasableAmount(int requestedAmount, int purchaseLimit, int alreadyPurchased) { + if (requestedAmount <= 0 || purchaseLimit <= 0) { + return 0; + } + + int remaining = purchaseLimit - Math.max(alreadyPurchased, 0); + if (remaining <= 0) { + return 0; + } + + return Math.min(requestedAmount, remaining); + } +} diff --git a/Emulator/src/test/java/com/eu/habbo/messages/incoming/catalog/TargetOfferPurchaseGuardTest.java b/Emulator/src/test/java/com/eu/habbo/messages/incoming/catalog/TargetOfferPurchaseGuardTest.java new file mode 100644 index 00000000..e09903e1 --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/messages/incoming/catalog/TargetOfferPurchaseGuardTest.java @@ -0,0 +1,35 @@ +package com.eu.habbo.messages.incoming.catalog; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TargetOfferPurchaseGuardTest { + @Test + void rejectsInvalidRequestedAmounts() { + assertEquals(0, TargetOfferPurchaseGuard.purchasableAmount(0, 10, 0)); + assertEquals(0, TargetOfferPurchaseGuard.purchasableAmount(-5, 10, 0)); + } + + @Test + void rejectsOffersWithoutPositiveLimits() { + assertEquals(0, TargetOfferPurchaseGuard.purchasableAmount(1, 0, 0)); + assertEquals(0, TargetOfferPurchaseGuard.purchasableAmount(1, -10, 0)); + } + + @Test + void capsAmountToRemainingLimit() { + assertEquals(3, TargetOfferPurchaseGuard.purchasableAmount(10, 5, 2)); + } + + @Test + void rejectsWhenLimitIsAlreadyConsumed() { + assertEquals(0, TargetOfferPurchaseGuard.purchasableAmount(1, 5, 5)); + assertEquals(0, TargetOfferPurchaseGuard.purchasableAmount(1, 5, 8)); + } + + @Test + void doesNotLetNegativePurchaseHistoryIncreaseRemainingLimit() { + assertEquals(5, TargetOfferPurchaseGuard.purchasableAmount(10, 5, -4)); + } +}