diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RedeemCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RedeemCommand.java index 9f2632f3..dacc0a1d 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RedeemCommand.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/RedeemCommand.java @@ -32,35 +32,44 @@ public class RedeemCommand extends Command { for (HabboItem item : gameClient.getHabbo().getInventory().getItemsComponent().getItemsAsValueCollection()) { if (item.getBaseItem().getName().startsWith("CF_") || item.getBaseItem().getName().startsWith("CFC_") || item.getBaseItem().getName().startsWith("DF_") || item.getBaseItem().getName().startsWith("PF_")) { if (item.getUserId() == gameClient.getHabbo().getHabboInfo().getId()) { - items.add(item); + boolean redeemable = false; if ((item.getBaseItem().getName().startsWith("CF_") || item.getBaseItem().getName().startsWith("CFC_")) && !item.getBaseItem().getName().contains("_diamond_")) { - try { - credits += Integer.parseInt(item.getBaseItem().getName().split("_")[1]); - } catch (Exception e) { + Integer amount = parsePositiveRedeemValue(item.getBaseItem().getName(), 1); + if (amount != null) { + Integer total = addRedeemValue(credits, amount); + if (total != null) { + credits = total; + redeemable = true; + } } } else if (item.getBaseItem().getName().startsWith("PF_")) { - try { - pixels += Integer.parseInt(item.getBaseItem().getName().split("_")[1]); - } catch (Exception e) { + Integer amount = parsePositiveRedeemValue(item.getBaseItem().getName(), 1); + if (amount != null) { + Integer total = addRedeemValue(pixels, amount); + if (total != null) { + pixels = total; + redeemable = true; + } } } else if (item.getBaseItem().getName().startsWith("DF_")) { - int pointsType; - int pointsAmount; + Integer pointsType = parsePositiveRedeemValue(item.getBaseItem().getName(), 1); + Integer pointsAmount = parsePositiveRedeemValue(item.getBaseItem().getName(), 2); - pointsType = Integer.parseInt(item.getBaseItem().getName().split("_")[1]); - pointsAmount = Integer.parseInt(item.getBaseItem().getName().split("_")[2]); - - points.adjustOrPutValue(pointsType, pointsAmount, pointsAmount); + if (pointsType != null && pointsAmount != null && addRedeemPoints(points, pointsType, pointsAmount)) { + redeemable = true; + } } else if (item.getBaseItem().getName().startsWith("CF_diamond_")) { - int pointsType; - int pointsAmount; + Integer pointsAmount = parsePositiveRedeemValue(item.getBaseItem().getName(), 2); - pointsType = 5; - pointsAmount = Integer.parseInt(item.getBaseItem().getName().split("_")[2]); + if (pointsAmount != null && addRedeemPoints(points, 5, pointsAmount)) { + redeemable = true; + } + } - points.adjustOrPutValue(pointsType, pointsAmount, pointsAmount); + if (redeemable) { + items.add(item); } } } @@ -103,4 +112,41 @@ public class RedeemCommand extends Command { return true; } + + static Integer parsePositiveRedeemValue(String itemName, int index) { + if (itemName == null) { + return null; + } + + String[] parts = itemName.split("_"); + if (index < 0 || index >= parts.length) { + return null; + } + + try { + int value = Integer.parseInt(parts[index]); + return value > 0 ? value : null; + } catch (NumberFormatException e) { + return null; + } + } + + static Integer addRedeemValue(int current, int amount) { + try { + return Math.addExact(current, amount); + } catch (ArithmeticException e) { + return null; + } + } + + static boolean addRedeemPoints(TIntIntMap points, int pointsType, int amount) { + int current = points.get(pointsType); + Integer total = addRedeemValue(current, amount); + if (total == null) { + return false; + } + + points.put(pointsType, total); + return true; + } } diff --git a/Emulator/src/test/java/com/eu/habbo/habbohotel/commands/RedeemCommandValueGuardTest.java b/Emulator/src/test/java/com/eu/habbo/habbohotel/commands/RedeemCommandValueGuardTest.java new file mode 100644 index 00000000..0a3091fe --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/habbohotel/commands/RedeemCommandValueGuardTest.java @@ -0,0 +1,39 @@ +package com.eu.habbo.habbohotel.commands; + +import gnu.trove.map.hash.TIntIntHashMap; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class RedeemCommandValueGuardTest { + @Test + void parsesOnlyPositiveRedeemValues() { + assertEquals(50, RedeemCommand.parsePositiveRedeemValue("CF_50", 1)); + assertEquals(7, RedeemCommand.parsePositiveRedeemValue("DF_5_7", 2)); + assertNull(RedeemCommand.parsePositiveRedeemValue("CF_0", 1)); + assertNull(RedeemCommand.parsePositiveRedeemValue("CF_-10", 1)); + assertNull(RedeemCommand.parsePositiveRedeemValue("CF_nope", 1)); + assertNull(RedeemCommand.parsePositiveRedeemValue("CF_10", 2)); + } + + @Test + void rejectsOverflowingCurrencyTotals() { + assertEquals(15, RedeemCommand.addRedeemValue(10, 5)); + assertNull(RedeemCommand.addRedeemValue(Integer.MAX_VALUE, 1)); + } + + @Test + void rejectsOverflowingPointTotalsWithoutChangingTheMap() { + TIntIntHashMap points = new TIntIntHashMap(); + points.put(5, Integer.MAX_VALUE); + + assertFalse(RedeemCommand.addRedeemPoints(points, 5, 1)); + assertEquals(Integer.MAX_VALUE, points.get(5)); + + assertTrue(RedeemCommand.addRedeemPoints(points, 6, 10)); + assertEquals(10, points.get(6)); + } +}