fix(wired): bound match position inputs

This commit is contained in:
simoleo89
2026-06-17 18:37:25 +02:00
parent 416d0bb088
commit a5dabd924e
3 changed files with 158 additions and 13 deletions
@@ -87,7 +87,7 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
this.direction = params[1] == 1; this.direction = params[1] == 1;
this.position = params[2] == 1; this.position = params[2] == 1;
this.altitude = (params.length > 3) && (params[3] == 1); this.altitude = (params.length > 3) && (params[3] == 1);
this.furniSource = (params.length > 4) ? params[4] : ((params.length > 3 && params[3] > 1) ? params[3] : WiredSourceUtil.SOURCE_TRIGGER); this.furniSource = (params.length > 4) ? WiredMatchPositionInputGuard.normalizeFurniSource(params[4], false) : ((params.length > 3 && params[3] > 1) ? WiredMatchPositionInputGuard.normalizeFurniSource(params[3], false) : WiredSourceUtil.SOURCE_TRIGGER);
this.quantifier = (params.length > 5) ? this.normalizeQuantifier(params[5]) : QUANTIFIER_ALL; this.quantifier = (params.length > 5) ? this.normalizeQuantifier(params[5]) : QUANTIFIER_ALL;
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
@@ -108,6 +108,8 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY(), item.getZ())); this.settings.add(new WiredMatchFurniSetting(item.getId(), item.getExtradata(), item.getRotation(), item.getX(), item.getY(), item.getZ()));
} }
this.furniSource = WiredMatchPositionInputGuard.normalizeFurniSource(this.furniSource, !this.settings.isEmpty());
return true; return true;
} }
@@ -255,27 +257,23 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
this.position = data.position; this.position = data.position;
this.direction = data.direction; this.direction = data.direction;
this.altitude = data.altitude; this.altitude = data.altitude;
if (data.settings != null) { this.settings.addAll(WiredMatchPositionInputGuard.sanitizeSettings(data.settings, room));
this.settings.addAll(data.settings); this.furniSource = WiredMatchPositionInputGuard.normalizeFurniSource(data.furniSource, !this.settings.isEmpty());
}
this.furniSource = data.furniSource;
this.quantifier = this.normalizeQuantifier(data.quantifier); this.quantifier = this.normalizeQuantifier(data.quantifier);
} else { } else {
String[] data = wiredData.split(":"); String[] data = wiredData.split(":");
if (data.length >= 5) { if (data.length >= 5) {
try { try {
int itemCount = Integer.parseInt(data[0]); int itemCount = Math.min(Integer.parseInt(data[0]), WiredManager.MAXIMUM_FURNI_SELECTION);
String[] items = data[1].split(";"); String[] items = data[1].split(";");
for (int i = 0; i < itemCount && i < items.length; i++) { for (int i = 0; i < itemCount && i < items.length; i++) {
String[] stuff = items[i].split("-"); WiredMatchFurniSetting setting = this.parseLegacySetting(items[i], room);
if (setting != null) {
if (stuff.length >= 6) this.settings.add(setting);
this.settings.add(new WiredMatchFurniSetting(Integer.parseInt(stuff[0]), stuff[1], Integer.parseInt(stuff[2]), Integer.parseInt(stuff[3]), Integer.parseInt(stuff[4]), Double.parseDouble(stuff[5]))); }
else if (stuff.length >= 5)
this.settings.add(new WiredMatchFurniSetting(Integer.parseInt(stuff[0]), stuff[1], Integer.parseInt(stuff[2]), Integer.parseInt(stuff[3]), Integer.parseInt(stuff[4])));
} }
this.state = data[2].equals("1"); this.state = data[2].equals("1");
@@ -287,11 +285,33 @@ public class WiredConditionMatchStatePosition extends InteractionWiredCondition
} }
this.altitude = false; this.altitude = false;
this.furniSource = this.settings.isEmpty() ? WiredSourceUtil.SOURCE_TRIGGER : WiredSourceUtil.SOURCE_SELECTED; this.furniSource = WiredMatchPositionInputGuard.normalizeFurniSource(WiredSourceUtil.SOURCE_TRIGGER, !this.settings.isEmpty());
this.quantifier = QUANTIFIER_ALL; this.quantifier = QUANTIFIER_ALL;
} }
} }
private WiredMatchFurniSetting parseLegacySetting(String value, Room room) {
String[] parts = value.split("-", 6);
if (parts.length < 5) {
return null;
}
try {
double z = (parts.length >= 6) ? Double.parseDouble(parts[5]) : 0.0D;
return WiredMatchPositionInputGuard.sanitizeParts(
Integer.parseInt(parts[0]),
parts[1],
Integer.parseInt(parts[2]),
Integer.parseInt(parts[3]),
Integer.parseInt(parts[4]),
z,
room
);
} catch (NumberFormatException ignored) {
return null;
}
}
@Override @Override
public void onPickUp() { public void onPickUp() {
this.settings.clear(); this.settings.clear();
@@ -0,0 +1,92 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.WiredMatchFurniSetting;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public final class WiredMatchPositionInputGuard {
public static final int MAX_STATE_LENGTH = 512;
private WiredMatchPositionInputGuard() {
}
public static int normalizeFurniSource(int value, boolean hasSelectedSettings) {
int source = switch (value) {
case WiredSourceUtil.SOURCE_SELECTED, WiredSourceUtil.SOURCE_SELECTOR,
WiredSourceUtil.SOURCE_SIGNAL, WiredSourceUtil.SOURCE_TRIGGER -> value;
default -> WiredSourceUtil.SOURCE_TRIGGER;
};
return (hasSelectedSettings && source == WiredSourceUtil.SOURCE_TRIGGER)
? WiredSourceUtil.SOURCE_SELECTED
: source;
}
public static List<WiredMatchFurniSetting> sanitizeSettings(Collection<WiredMatchFurniSetting> settings, Room room) {
List<WiredMatchFurniSetting> result = new ArrayList<>();
if (settings == null || room == null) {
return result;
}
for (WiredMatchFurniSetting setting : settings) {
WiredMatchFurniSetting normalized = sanitizeSetting(setting, room);
if (normalized != null) {
result.add(normalized);
}
if (result.size() >= WiredManager.MAXIMUM_FURNI_SELECTION) {
break;
}
}
return result;
}
public static WiredMatchFurniSetting sanitizeSetting(WiredMatchFurniSetting setting, Room room) {
if (setting == null || room == null) {
return null;
}
return sanitizeParts(setting.item_id, setting.state, setting.rotation, setting.x, setting.y, setting.z, room);
}
public static WiredMatchFurniSetting sanitizeParts(int itemId, String state, int rotation, int x, int y, double z, Room room) {
if (itemId < 1 || room == null) {
return null;
}
HabboItem item = room.getHabboItem(itemId);
if (item == null || rotation < 0 || rotation > 7 || !Double.isFinite(z)) {
return null;
}
if (x < Short.MIN_VALUE || x > Short.MAX_VALUE || y < Short.MIN_VALUE || y > Short.MAX_VALUE) {
return null;
}
if (room.getLayout() != null && room.getLayout().getTile((short) x, (short) y) == null) {
return null;
}
return new WiredMatchFurniSetting(itemId, normalizeState(state), rotation, x, y, z);
}
public static String normalizeState(String state) {
if (state == null) {
return "";
}
String normalized = state.replace('\t', ' ').replace('\r', ' ').replace('\n', ' ');
if (normalized.length() > MAX_STATE_LENGTH) {
return normalized.substring(0, MAX_STATE_LENGTH);
}
return normalized;
}
}
@@ -0,0 +1,33 @@
package com.eu.habbo.habbohotel.items.interactions.wired.conditions;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
class WiredMatchPositionInputGuardTest {
@Test
void furniSourcesFallBackToTriggerWhenUnknown() {
assertEquals(WiredSourceUtil.SOURCE_TRIGGER, WiredMatchPositionInputGuard.normalizeFurniSource(-1, false));
assertEquals(WiredSourceUtil.SOURCE_SELECTOR, WiredMatchPositionInputGuard.normalizeFurniSource(WiredSourceUtil.SOURCE_SELECTOR, false));
assertEquals(WiredSourceUtil.SOURCE_SIGNAL, WiredMatchPositionInputGuard.normalizeFurniSource(WiredSourceUtil.SOURCE_SIGNAL, false));
}
@Test
void selectedSettingsPromoteTriggerSourceToSelected() {
assertEquals(WiredSourceUtil.SOURCE_SELECTED,
WiredMatchPositionInputGuard.normalizeFurniSource(WiredSourceUtil.SOURCE_TRIGGER, true));
}
@Test
void stateIsNullSafeSingleLineAndBounded() {
assertEquals("", WiredMatchPositionInputGuard.normalizeState(null));
assertEquals("a b c", WiredMatchPositionInputGuard.normalizeState("a\tb\nc"));
String longState = "x".repeat(WiredMatchPositionInputGuard.MAX_STATE_LENGTH + 10);
String normalized = WiredMatchPositionInputGuard.normalizeState(longState);
assertEquals(WiredMatchPositionInputGuard.MAX_STATE_LENGTH, normalized.length());
assertFalse(normalized.contains("\n"));
}
}