Merge pull request #267 from simoleo89/fix/commands-redeem-values

fix(commands): bound redeem values
This commit is contained in:
DuckieTM
2026-06-18 12:54:09 +02:00
committed by GitHub
2 changed files with 103 additions and 18 deletions
@@ -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;
}
}
@@ -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));
}
}