Merge pull request #238 from simoleo89/fix/wired-trigger-timers

fix(wired): clamp trigger timers
This commit is contained in:
DuckieTM
2026-06-18 12:33:01 +02:00
committed by GitHub
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.InteractionWiredTrigger;
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.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -29,6 +30,9 @@ import java.util.List;
*/
public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
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 */
public int executeTime;
@@ -68,18 +72,19 @@ public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements Wi
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
Integer storedExecuteTime = null;
try {
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.executeTime = data.executeTime;
} else {
if (wiredData.length() >= 1) {
this.executeTime = (Integer.parseInt(wiredData));
storedExecuteTime = data != null ? data.executeTime : null;
} else if (wiredData != null && wiredData.length() >= 1) {
storedExecuteTime = Integer.parseInt(wiredData);
}
} catch (RuntimeException ignored) {
storedExecuteTime = null;
}
if (this.executeTime < 500) {
this.executeTime = 20 * 500;
}
this.executeTime = WiredTimerInputGuard.normalizeStoredMillis(storedExecuteTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
// Initialize for tick system - will be registered by RoomItemManager
this.accumulatedTime = 0;
@@ -134,7 +139,7 @@ public class WiredTriggerAtSetTime extends InteractionWiredTrigger implements Wi
@Override
public boolean saveData(WiredSettings settings) {
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();
@@ -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.InteractionWiredTrigger;
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.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -29,6 +30,9 @@ import java.util.List;
*/
public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
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 */
private int executeTime;
@@ -68,18 +72,19 @@ public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements W
public void loadWiredData(ResultSet set, Room room) throws SQLException {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
Integer storedExecuteTime = null;
try {
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.executeTime = data.executeTime;
} else {
if (wiredData.length() >= 1) {
this.executeTime = (Integer.parseInt(wiredData));
storedExecuteTime = data != null ? data.executeTime : null;
} else if (wiredData != null && wiredData.length() >= 1) {
storedExecuteTime = Integer.parseInt(wiredData);
}
} catch (RuntimeException ignored) {
storedExecuteTime = null;
}
if (this.executeTime < 500) {
this.executeTime = 20 * 500;
}
this.executeTime = WiredTimerInputGuard.normalizeStoredMillis(storedExecuteTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
// Initialize for tick system
this.accumulatedTime = 0;
@@ -134,7 +139,7 @@ public class WiredTriggerAtTimeLong extends InteractionWiredTrigger implements W
@Override
public boolean saveData(WiredSettings settings) {
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();
@@ -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.InteractionWiredTrigger;
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.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -30,6 +31,9 @@ import java.util.List;
public class WiredTriggerRepeater extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
public static final WiredTriggerType type = WiredTriggerType.PERIODICALLY;
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 */
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 {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
Integer storedRepeatTime = null;
try {
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.repeatTime = data.repeatTime;
} else {
if (wiredData.length() >= 1) {
this.repeatTime = (Integer.parseInt(wiredData));
storedRepeatTime = data != null ? data.repeatTime : null;
} else if (wiredData != null && wiredData.length() >= 1) {
storedRepeatTime = Integer.parseInt(wiredData);
}
} catch (RuntimeException ignored) {
storedRepeatTime = null;
}
if (this.repeatTime < 500) {
this.repeatTime = 20 * 500;
}
this.repeatTime = WiredTimerInputGuard.normalizeStoredMillis(storedRepeatTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
}
@Override
@@ -123,13 +128,7 @@ public class WiredTriggerRepeater extends InteractionWiredTrigger implements Wir
@Override
public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false;
int newRepeatTime = settings.getIntParams()[0] * 500;
if (newRepeatTime < 500) {
newRepeatTime = 500;
}
this.repeatTime = newRepeatTime;
this.repeatTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY);
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.InteractionWiredTrigger;
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.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -29,6 +30,9 @@ import java.util.List;
*/
public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements WiredTickable, WiredTriggerReset {
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;
/** 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 {
String wiredData = set.getString("wired_data");
if (wiredData.startsWith("{")) {
Integer storedRepeatTime = null;
try {
if (wiredData != null && wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
this.repeatTime = data.repeatTime;
} else {
if (wiredData.length() >= 1) {
this.repeatTime = (Integer.parseInt(wiredData));
storedRepeatTime = data != null ? data.repeatTime : null;
} else if (wiredData != null && wiredData.length() >= 1) {
storedRepeatTime = Integer.parseInt(wiredData);
}
} catch (RuntimeException ignored) {
storedRepeatTime = null;
}
if (this.repeatTime < 5000) {
this.repeatTime = 20 * 5000;
}
this.repeatTime = WiredTimerInputGuard.normalizeStoredMillis(storedRepeatTime, MIN_DELAY, LEGACY_FALLBACK_DELAY);
}
@Override
@@ -123,11 +128,7 @@ public class WiredTriggerRepeaterLong extends InteractionWiredTrigger implements
@Override
public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false;
int interval = settings.getIntParams()[0];
if (interval < 1) {
interval = 1;
}
this.repeatTime = interval * 5000;
this.repeatTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY);
// No accumulated time reset needed - using global tick count
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.interactions.InteractionWiredEffect;
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.wired.WiredTriggerType;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
@@ -94,8 +95,7 @@ public class WiredTriggerRepeaterShort extends WiredTriggerRepeater {
public boolean saveData(WiredSettings settings) {
if (settings.getIntParams().length < 1) return false;
int newRepeatTime = settings.getIntParams()[0] * STEP_MS;
this.repeatTime = clampRepeatTime(newRepeatTime);
this.repeatTime = WiredTimerInputGuard.fromClientUnits(settings.getIntParams()[0], STEP_MS, MIN_DELAY, MAX_DELAY);
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));
}
}