fix(wired): clamp trigger timers

This commit is contained in:
simoleo89
2026-06-17 18:20:55 +02:00
parent 416d0bb088
commit 916b9df7a3
7 changed files with 142 additions and 52 deletions
@@ -0,0 +1,45 @@
package com.eu.habbo.habbohotel.items.interactions.wired;
public final class WiredTimerInputGuard {
public static final int MAX_TIMER_MS = 24 * 60 * 60 * 1000;
private WiredTimerInputGuard() {
}
public static int fromClientUnits(int units, int stepMs, int minMs) {
return fromClientUnits(units, stepMs, minMs, MAX_TIMER_MS);
}
public static int fromClientUnits(int units, int stepMs, int minMs, int maxMs) {
if (units < 1 || stepMs < 1) {
return minMs;
}
long value = (long) units * stepMs;
return clamp(value, minMs, maxMs);
}
public static int normalizeStoredMillis(Integer millis, int minMs, int fallbackMs) {
return normalizeStoredMillis(millis, minMs, fallbackMs, MAX_TIMER_MS);
}
public static int normalizeStoredMillis(Integer millis, int minMs, int fallbackMs, int maxMs) {
if (millis == null || millis < minMs) {
return fallbackMs;
}
return clamp(millis.longValue(), minMs, maxMs);
}
private static int clamp(long value, int minMs, int maxMs) {
if (value < minMs) {
return minMs;
}
if (value > maxMs) {
return maxMs;
}
return (int) value;
}
}
@@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTimerInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset; import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset;
import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -29,6 +30,9 @@ import java.util.List;
*/ */
public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset { public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
public static final WiredTriggerType type = WiredTriggerType.AT_GIVEN_TIME; public static final WiredTriggerType type = WiredTriggerType.AT_GIVEN_TIME;
private static final int STEP_MS = 500;
private static final int MIN_DELAY = STEP_MS;
private static final int LEGACY_FALLBACK_DELAY = 20 * STEP_MS;
/** The time in milliseconds until the trigger fires */ /** The time in milliseconds until the trigger fires */
public int executeTime; public int executeTime;
@@ -68,18 +72,19 @@ public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements Wi
public void loadWiredData(ResultSet set, Room room) throws SQLException { public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data"); String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) { Integer storedExecuteTime = null;
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); try {
this.executeTime = data.executeTime; if (wiredData != null && wiredData.startsWith("{")) {
} else { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (wiredData.length() >= 1) { storedExecuteTime = data != null ? data.executeTime : null;
this.executeTime = (Integer.parseInt(wiredData)); } else if (wiredData != null && wiredData.length() >= 1) {
storedExecuteTime = Integer.parseInt(wiredData);
} }
} catch (RuntimeException ignored) {
storedExecuteTime = null;
} }
if (this.executeTime < 500) { this.executeTime = WiredTimerInputGuard.normalizeStoredMillis(storedExecuteTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
this.executeTime = 20 * 500;
}
// Initialize for tick system - will be registered by RoomItemManager // Initialize for tick system - will be registered by RoomItemManager
this.accumulatedTime = 0; this.accumulatedTime = 0;
@@ -134,7 +139,7 @@ public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements Wi
@Override @Override
public boolean saveData(WiredSettings settings) { public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false; if (settings.getIntParams().length < 1) return false;
this.executeTime = settings.getIntParams()[0] * 500; this.executeTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY);
this.resetTimer(); this.resetTimer();
@@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTimerInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset; import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset;
import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -29,6 +30,9 @@ import java.util.List;
*/ */
public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset { public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
private static final WiredTriggerType type = WiredTriggerType.AT_GIVEN_TIME; private static final WiredTriggerType type = WiredTriggerType.AT_GIVEN_TIME;
private static final int STEP_MS = 500;
private static final int MIN_DELAY = STEP_MS;
private static final int LEGACY_FALLBACK_DELAY = 20 * STEP_MS;
/** The time in milliseconds until the trigger fires */ /** The time in milliseconds until the trigger fires */
private int executeTime; private int executeTime;
@@ -68,18 +72,19 @@ public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements W
public void loadWiredData(ResultSet set, Room room) throws SQLException { public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data"); String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) { Integer storedExecuteTime = null;
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); try {
this.executeTime = data.executeTime; if (wiredData != null && wiredData.startsWith("{")) {
} else { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (wiredData.length() >= 1) { storedExecuteTime = data != null ? data.executeTime : null;
this.executeTime = (Integer.parseInt(wiredData)); } else if (wiredData != null && wiredData.length() >= 1) {
storedExecuteTime = Integer.parseInt(wiredData);
} }
} catch (RuntimeException ignored) {
storedExecuteTime = null;
} }
if (this.executeTime < 500) { this.executeTime = WiredTimerInputGuard.normalizeStoredMillis(storedExecuteTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
this.executeTime = 20 * 500;
}
// Initialize for tick system // Initialize for tick system
this.accumulatedTime = 0; this.accumulatedTime = 0;
@@ -134,7 +139,7 @@ public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements W
@Override @Override
public boolean saveData(WiredSettings settings) { public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false; if (settings.getIntParams().length < 1) return false;
this.executeTime = settings.getIntParams()[0] * 500; this.executeTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY);
this.resetTimer(); this.resetTimer();
@@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTimerInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset; import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset;
import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -30,6 +31,9 @@ import java.util.List;
public class WiredTriggerRepeater extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset { public class WiredTriggerRepeater extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
public static final WiredTriggerType type = WiredTriggerType.PERIODICALLY; public static final WiredTriggerType type = WiredTriggerType.PERIODICALLY;
public static final int DEFAULT_DELAY = 10 * 500; // 5 seconds default public static final int DEFAULT_DELAY = 10 * 500; // 5 seconds default
private static final int STEP_MS = 500;
private static final int MIN_DELAY = STEP_MS;
private static final int LEGACY_FALLBACK_DELAY = 20 * STEP_MS;
/** The interval in milliseconds between triggers */ /** The interval in milliseconds between triggers */
protected int repeatTime = DEFAULT_DELAY; protected int repeatTime = DEFAULT_DELAY;
@@ -63,18 +67,19 @@ public class WiredTriggerRepeater extends InteractionWiredTrigger implements Wir
public void loadWiredData(ResultSet set, Room room) throws SQLException { public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data"); String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) { Integer storedRepeatTime = null;
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); try {
this.repeatTime = data.repeatTime; if (wiredData != null && wiredData.startsWith("{")) {
} else { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (wiredData.length() >= 1) { storedRepeatTime = data != null ? data.repeatTime : null;
this.repeatTime = (Integer.parseInt(wiredData)); } else if (wiredData != null && wiredData.length() >= 1) {
storedRepeatTime = Integer.parseInt(wiredData);
} }
} catch (RuntimeException ignored) {
storedRepeatTime = null;
} }
if (this.repeatTime < 500) { this.repeatTime = WiredTimerInputGuard.normalizeStoredMillis(storedRepeatTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
this.repeatTime = 20 * 500;
}
} }
@Override @Override
@@ -123,13 +128,7 @@ public class WiredTriggerRepeater extends InteractionWiredTrigger implements Wir
@Override @Override
public boolean saveData(WiredSettings settings) { public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false; if (settings.getIntParams().length < 1) return false;
int newRepeatTime = settings.getIntParams()[0] * 500; this.repeatTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY);
if (newRepeatTime < 500) {
newRepeatTime = 500;
}
this.repeatTime = newRepeatTime;
return true; return true;
} }
@@ -4,6 +4,7 @@ import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTimerInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset; import com.eu.habbo.habbohotel.items.interactions.wired.WiredTriggerReset;
import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -29,6 +30,9 @@ import java.util.List;
*/ */
public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset { public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
public static final int DEFAULT_DELAY = 10 * 5000; // 50 seconds default public static final int DEFAULT_DELAY = 10 * 5000; // 50 seconds default
private static final int STEP_MS = 5000;
private static final int MIN_DELAY = STEP_MS;
private static final int LEGACY_FALLBACK_DELAY = 20 * STEP_MS;
private static final WiredTriggerType type = WiredTriggerType.PERIODICALLY_LONG; private static final WiredTriggerType type = WiredTriggerType.PERIODICALLY_LONG;
/** The interval in milliseconds between triggers */ /** The interval in milliseconds between triggers */
@@ -63,18 +67,19 @@ public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements
public void loadWiredData(ResultSet set, Room room) throws SQLException { public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data"); String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) { Integer storedRepeatTime = null;
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); try {
this.repeatTime = data.repeatTime; if (wiredData != null && wiredData.startsWith("{")) {
} else { JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (wiredData.length() >= 1) { storedRepeatTime = data != null ? data.repeatTime : null;
this.repeatTime = (Integer.parseInt(wiredData)); } else if (wiredData != null && wiredData.length() >= 1) {
storedRepeatTime = Integer.parseInt(wiredData);
} }
} catch (RuntimeException ignored) {
storedRepeatTime = null;
} }
if (this.repeatTime < 5000) { this.repeatTime = WiredTimerInputGuard.normalizeStoredMillis(storedRepeatTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
this.repeatTime = 20 * 5000;
}
} }
@Override @Override
@@ -123,11 +128,7 @@ public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements
@Override @Override
public boolean saveData(WiredSettings settings) { public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false; if (settings.getIntParams().length < 1) return false;
int interval = settings.getIntParams()[0]; this.repeatTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY);
if (interval < 1) {
interval = 1;
}
this.repeatTime = interval * 5000;
// No accumulated time reset needed - using global tick count // No accumulated time reset needed - using global tick count
return true; return true;
} }
@@ -3,6 +3,7 @@ package com.eu.habbo.habbohotel.items.interactions.wired.triggers;
import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect; import com.eu.habbo.habbohotel.items.interactions.InteractionWiredEffect;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredTimerInputGuard;
import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.wired.WiredTriggerType; import com.eu.habbo.habbohotel.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredManager;
@@ -94,8 +95,7 @@ public class WiredTriggerRepeaterShort extends WiredTriggerRepeater {
public boolean saveData(WiredSettings settings) { public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false; if (settings.getIntParams().length < 1) return false;
int newRepeatTime = settings.getIntParams()[0] * STEP_MS; this.repeatTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY, MAX_DELAY);
this.repeatTime = clampRepeatTime(newRepeatTime);
return true; return true;
} }
@@ -0,0 +1,35 @@
package com.eu.habbo.habbohotel.items.interactions.wired;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class WiredTimerInputGuardTest {
@Test
void clientTimerUnitsAreMultipliedWithoutOverflow() {
assertEquals(500, WiredTimerInputGuard.fromClientUnits(1, 500, 500));
assertEquals(WiredTimerInputGuard.MAX_TIMER_MS,
WiredTimerInputGuard.fromClientUnits(Integer.MAX_VALUE, 5000, 5000));
}
@Test
void invalidClientTimerUnitsUseMinimumDelay() {
assertEquals(500, WiredTimerInputGuard.fromClientUnits(0, 500, 500));
assertEquals(5000, WiredTimerInputGuard.fromClientUnits(-10, 5000, 5000));
}
@Test
void storedTimerValuesFallbackOrClamp() {
assertEquals(10000, WiredTimerInputGuard.normalizeStoredMillis(null, 500, 10000));
assertEquals(10000, WiredTimerInputGuard.normalizeStoredMillis(-1, 500, 10000));
assertEquals(500, WiredTimerInputGuard.normalizeStoredMillis(500, 500, 10000));
assertEquals(WiredTimerInputGuard.MAX_TIMER_MS,
WiredTimerInputGuard.normalizeStoredMillis(Integer.MAX_VALUE, 500, 10000));
}
@Test
void shortRepeaterKeepsItsLegacyMaximum() {
assertEquals(500, WiredTimerInputGuard.fromClientUnits(Integer.MAX_VALUE, 50, 50, 500));
}
}