Extend wired runtime metadata and placeholder support

- add wf_xtra_text_output_furni_name and expand text placeholders for furni, users, bots and pets

- include room, entry, teleport and item metadata needed by the new :wired tools flow

- animate furniture position updates through the wired movement path

- fix teleport pair persistence/lookups for items_teleports with explicit column inserts
This commit is contained in:
Lorenzune
2026-03-27 09:37:14 +01:00
parent f22e4c30a8
commit dfd9cd1357
27 changed files with 625 additions and 62 deletions
@@ -47,6 +47,8 @@ public class RoomUserPetComposer extends MessageComposer {
this.response.appendBoolean(true); //Can breed
this.response.appendInt(0);
this.response.appendString("");
this.response.appendString("unknown");
this.response.appendInt(0);
return this.response;
}
@@ -60,6 +60,7 @@ import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMovePhys
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraMoveNoAnimation;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraOrEval;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraRandom;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputFurniName;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputUsername;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraUnseen;
import com.eu.habbo.habbohotel.items.interactions.wired.selector.*;
@@ -355,6 +356,7 @@ public class ItemManager {
this.interactionsList.add(new ItemInteraction("wf_xtra_exec_in_order", WiredExtraExecuteInOrder.class));
this.interactionsList.add(new ItemInteraction("wf_xtra_execution_limit", WiredExtraExecutionLimit.class));
this.interactionsList.add(new ItemInteraction("wf_xtra_text_output_username", WiredExtraTextOutputUsername.class));
this.interactionsList.add(new ItemInteraction("wf_xtra_text_output_furni_name", WiredExtraTextOutputFurniName.class));
this.interactionsList.add(new ItemInteraction("wf_highscore", InteractionWiredHighscore.class));
@@ -697,7 +699,7 @@ public class ItemManager {
}
public void insertTeleportPair(int itemOneId, int itemTwoId) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO items_teleports VALUES (?, ?)")) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO items_teleports (teleport_one_id, teleport_two_id) VALUES (?, ?)")) {
statement.setInt(1, itemOneId);
statement.setInt(2, itemTwoId);
statement.execute();
@@ -717,20 +719,28 @@ public class ItemManager {
}
public int[] getTargetTeleportRoomId(HabboItem item) {
int[] a = new int[]{};
int[] target = new int[]{};
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT items.id, items.room_id FROM items_teleports INNER JOIN items ON items_teleports.teleport_one_id = items.id OR items_teleports.teleport_two_id = items.id WHERE items.id != ? AND items.room_id > 0 LIMIT 1")) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT items_teleports.*, A.room_id as a_room_id, A.id as a_id, B.room_id as b_room_id, B.id as b_id FROM items_teleports INNER JOIN items AS A ON items_teleports.teleport_one_id = A.id INNER JOIN items AS B ON items_teleports.teleport_two_id = B.id WHERE (teleport_one_id = ? OR teleport_two_id = ?) LIMIT 1")) {
statement.setInt(1, item.getId());
statement.setInt(2, item.getId());
try (ResultSet set = statement.executeQuery()) {
if (set.next()) {
a = new int[]{set.getInt("room_id"), set.getInt("id")};
final boolean useA = (set.getInt("a_id") != item.getId());
final int targetRoomId = useA ? set.getInt("a_room_id") : set.getInt("b_room_id");
final int targetItemId = useA ? set.getInt("a_id") : set.getInt("b_id");
if (targetRoomId > 0 && targetItemId > 0) {
target = new int[]{targetRoomId, targetItemId};
}
}
}
} catch (SQLException e) {
LOGGER.error("Caught SQL exception", e);
}
return a;
return target;
}
public HabboItem loadHabboItem(int itemId) {
@@ -224,13 +224,19 @@ public abstract class InteractionWiredEffect extends InteractionWired implements
}
protected LinkedHashSet<HabboItem> getSelectableFloorItems(Room room) {
return this.getSelectableFloorItems(room, null);
}
protected LinkedHashSet<HabboItem> getSelectableFloorItems(Room room, WiredContext ctx) {
LinkedHashSet<HabboItem> result = new LinkedHashSet<>();
if (room == null) {
return result;
}
boolean includeWiredItems = this.includeWiredTargets(ctx);
room.getFloorItems().forEach(item -> {
if (item != null && !(item instanceof InteractionWired)) {
if (item != null && (includeWiredItems || !(item instanceof InteractionWired))) {
result.add(item);
}
});
@@ -238,6 +244,10 @@ public abstract class InteractionWiredEffect extends InteractionWired implements
return result;
}
protected boolean includeWiredTargets(WiredContext ctx) {
return ctx != null && ctx.includeWiredSelectorItems();
}
protected <T> LinkedHashSet<T> toLinkedHashSet(Iterable<T> values) {
LinkedHashSet<T> result = new LinkedHashSet<>();
if (values == null) {
@@ -0,0 +1,294 @@
package com.eu.habbo.habbohotel.items.interactions.wired.extra;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.gameclients.GameClient;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.core.WiredSourceUtil;
import com.eu.habbo.messages.ServerMessage;
import gnu.trove.set.hash.THashSet;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class WiredExtraTextOutputFurniName extends InteractionWiredExtra {
public static final int CODE = 68;
public static final int TYPE_SINGLE = 1;
public static final int TYPE_MULTIPLE = 2;
public static final String DEFAULT_PLACEHOLDER_NAME = "";
public static final String DEFAULT_DELIMITER = ", ";
public static final int MAX_PLACEHOLDER_NAME_LENGTH = 32;
public static final int MAX_DELIMITER_LENGTH = 16;
private static final Pattern WRAPPED_PLACEHOLDER_PATTERN = Pattern.compile("^\\$\\((.*)\\)$");
private final THashSet<HabboItem> items;
private String placeholderName = DEFAULT_PLACEHOLDER_NAME;
private int placeholderType = TYPE_SINGLE;
private String delimiter = DEFAULT_DELIMITER;
private int furniSource = WiredSourceUtil.SOURCE_TRIGGER;
public WiredExtraTextOutputFurniName(ResultSet set, Item baseItem) throws SQLException {
super(set, baseItem);
this.items = new THashSet<>();
}
public WiredExtraTextOutputFurniName(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) {
super(id, userId, item, extradata, limitedStack, limitedSells);
this.items = new THashSet<>();
}
@Override
public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) {
return false;
}
@Override
public boolean saveData(WiredSettings settings, GameClient gameClient) {
int[] intParams = settings.getIntParams();
String[] stringData = splitStringData(settings.getStringParam());
this.placeholderType = normalizePlaceholderType((intParams.length > 0) ? intParams[0] : TYPE_SINGLE);
this.furniSource = normalizeFurniSource((intParams.length > 1) ? intParams[1] : WiredSourceUtil.SOURCE_TRIGGER);
this.placeholderName = normalizePlaceholderName(stringData[0]);
this.delimiter = normalizeDelimiter(stringData[1]);
this.items.clear();
if (this.furniSource != WiredSourceUtil.SOURCE_SELECTED) {
return true;
}
Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId());
if (room == null) {
return false;
}
if (settings.getFurniIds().length > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) {
return false;
}
for (int itemId : settings.getFurniIds()) {
HabboItem item = room.getHabboItem(itemId);
if (item != null) {
this.items.add(item);
}
}
return true;
}
@Override
public String getWiredData() {
return WiredManager.getGson().toJson(new JsonData(
this.placeholderName,
this.placeholderType,
this.delimiter,
this.furniSource,
this.items.stream().map(HabboItem::getId).collect(Collectors.toList())
));
}
@Override
public void serializeWiredData(ServerMessage message, Room room) {
this.refresh(room);
message.appendBoolean(false);
message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION);
message.appendInt(this.items.size());
for (HabboItem item : this.items) {
message.appendInt(item.getId());
}
message.appendInt(this.getBaseItem().getSpriteId());
message.appendInt(this.getId());
message.appendString(this.placeholderName + "\t" + this.delimiter);
message.appendInt(2);
message.appendInt(this.placeholderType);
message.appendInt(this.furniSource);
message.appendInt(0);
message.appendInt(CODE);
message.appendInt(0);
message.appendInt(0);
}
@Override
public void loadWiredData(ResultSet set, Room room) throws SQLException {
this.onPickUp();
String wiredData = set.getString("wired_data");
if (wiredData == null || wiredData.isEmpty()) {
return;
}
if (wiredData.startsWith("{")) {
JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class);
if (data != null) {
this.placeholderName = normalizePlaceholderName(data.placeholderName);
this.placeholderType = normalizePlaceholderType(data.placeholderType);
this.delimiter = normalizeDelimiter(data.delimiter);
this.furniSource = normalizeFurniSource(data.furniSource);
if (data.itemIds != null) {
for (Integer itemId : data.itemIds) {
HabboItem item = room.getHabboItem(itemId);
if (item != null) {
this.items.add(item);
}
}
}
}
return;
}
String[] legacyData = splitStringData(wiredData);
this.placeholderName = normalizePlaceholderName(legacyData[0]);
this.delimiter = normalizeDelimiter(legacyData[1]);
}
@Override
public void onPickUp() {
this.items.clear();
this.placeholderName = DEFAULT_PLACEHOLDER_NAME;
this.placeholderType = TYPE_SINGLE;
this.delimiter = DEFAULT_DELIMITER;
this.furniSource = WiredSourceUtil.SOURCE_TRIGGER;
}
@Override
public void onWalk(RoomUnit roomUnit, Room room, Object[] objects) throws Exception {
}
@Override
public boolean hasConfiguration() {
return true;
}
public String getPlaceholderName() {
return this.placeholderName;
}
public String getPlaceholderToken() {
return this.placeholderName.isEmpty() ? "" : "$(" + this.placeholderName + ")";
}
public int getPlaceholderType() {
return this.placeholderType;
}
public String getDelimiter() {
return this.delimiter;
}
public int getFurniSource() {
return this.furniSource;
}
public THashSet<HabboItem> getItems() {
return this.items;
}
private void refresh(Room room) {
THashSet<HabboItem> remove = new THashSet<>();
for (HabboItem item : this.items) {
if (room.getHabboItem(item.getId()) == null) {
remove.add(item);
}
}
for (HabboItem item : remove) {
this.items.remove(item);
}
}
private static String[] splitStringData(String value) {
if (value == null) {
return new String[] { DEFAULT_PLACEHOLDER_NAME, DEFAULT_DELIMITER };
}
String[] parts = value.split("\t", -1);
if (parts.length <= 1) {
return new String[] { value, DEFAULT_DELIMITER };
}
return new String[] { parts[0], parts[1] };
}
private static int normalizePlaceholderType(int value) {
return (value == TYPE_MULTIPLE) ? TYPE_MULTIPLE : TYPE_SINGLE;
}
private static String normalizePlaceholderName(String value) {
if (value == null) {
return DEFAULT_PLACEHOLDER_NAME;
}
String normalized = value.trim().replace("\t", "").replace("\r", "").replace("\n", "");
if (WRAPPED_PLACEHOLDER_PATTERN.matcher(normalized).matches()) {
normalized = normalized.substring(2, normalized.length() - 1).trim();
}
if (normalized.length() > MAX_PLACEHOLDER_NAME_LENGTH) {
normalized = normalized.substring(0, MAX_PLACEHOLDER_NAME_LENGTH);
}
return normalized;
}
private static String normalizeDelimiter(String value) {
if (value == null) {
return DEFAULT_DELIMITER;
}
String normalized = value.replace("\t", "").replace("\r", "").replace("\n", "");
if (normalized.length() > MAX_DELIMITER_LENGTH) {
normalized = normalized.substring(0, MAX_DELIMITER_LENGTH);
}
return normalized;
}
private static int normalizeFurniSource(int value) {
switch (value) {
case WiredSourceUtil.SOURCE_SELECTED:
case WiredSourceUtil.SOURCE_SELECTOR:
case WiredSourceUtil.SOURCE_SIGNAL:
case WiredSourceUtil.SOURCE_TRIGGER:
return value;
default:
return WiredSourceUtil.SOURCE_TRIGGER;
}
}
static class JsonData {
String placeholderName;
int placeholderType;
String delimiter;
int furniSource;
List<Integer> itemIds;
JsonData(String placeholderName, int placeholderType, String delimiter, int furniSource, List<Integer> itemIds) {
this.placeholderName = placeholderName;
this.placeholderType = placeholderType;
this.delimiter = delimiter;
this.furniSource = furniSource;
this.itemIds = itemIds;
}
}
}
@@ -48,10 +48,12 @@ public class WiredEffectFurniAltitude extends InteractionWiredEffect {
return;
}
boolean includeWiredItems = this.includeWiredTargets(ctx);
Set<HabboItem> matchingItems = new LinkedHashSet<>();
room.getFloorItems().forEach(item -> {
if (item == null || item instanceof InteractionWired) {
if (item == null || (!includeWiredItems && item instanceof InteractionWired)) {
return;
}
@@ -62,7 +64,7 @@ public class WiredEffectFurniAltitude extends InteractionWiredEffect {
Set<HabboItem> result = new LinkedHashSet<>(matchingItems);
result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert);
result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room, ctx), ctx.targets().items(), this.filterExisting, this.invert);
ctx.targets().setItems(result);
}
@@ -44,16 +44,16 @@ public class WiredEffectFurniArea extends InteractionWiredEffect {
Room room = ctx.room();
if (room == null || areaWidth <= 0 || areaHeight <= 0) return;
List<HabboItem> furniInArea = getFurniInArea(room);
List<HabboItem> furniInArea = getFurniInArea(room, this.includeWiredTargets(ctx));
ctx.targets().setItems(this.applySelectorModifiers(
furniInArea,
this.getSelectableFloorItems(room),
this.getSelectableFloorItems(room, ctx),
ctx.targets().items(),
this.filterExisting,
this.invert));
}
private List<HabboItem> getFurniInArea(Room room) {
private List<HabboItem> getFurniInArea(Room room, boolean includeWiredItems) {
List<HabboItem> result = new ArrayList<>();
int maxX = rootX + areaWidth - 1;
@@ -62,7 +62,7 @@ public class WiredEffectFurniArea extends InteractionWiredEffect {
for (int x = rootX; x <= maxX; x++) {
for (int y = rootY; y <= maxY; y++) {
for (HabboItem item : room.getItemsAt(x, y)) {
if (item != null && !(item instanceof InteractionWired) && !result.contains(item)) {
if (item != null && (includeWiredItems || !(item instanceof InteractionWired)) && !result.contains(item)) {
result.add(item);
}
}
@@ -161,6 +161,22 @@ public class WiredEffectFurniArea extends InteractionWiredEffect {
return false;
}
public int getRootX() {
return this.rootX;
}
public int getRootY() {
return this.rootY;
}
public int getAreaWidth() {
return this.areaWidth;
}
public int getAreaHeight() {
return this.areaHeight;
}
static class JsonData {
int rootX;
int rootY;
@@ -48,6 +48,8 @@ public class WiredEffectFurniByType extends InteractionWiredEffect {
Room room = ctx.room();
if (room == null) return;
boolean includeWiredItems = this.includeWiredTargets(ctx);
List<HabboItem> sourceFurni = resolveSourceFurni(ctx, room);
if (sourceFurni.isEmpty()) return;
@@ -61,7 +63,7 @@ public class WiredEffectFurniByType extends InteractionWiredEffect {
Set<HabboItem> matched = new LinkedHashSet<>();
room.getFloorItems().forEach(item -> {
if (item instanceof InteractionWired) return;
if (!includeWiredItems && item instanceof InteractionWired) return;
String key = matchState
? item.getBaseItem().getId() + ":" + item.getExtradata()
: String.valueOf(item.getBaseItem().getId());
@@ -70,7 +72,7 @@ public class WiredEffectFurniByType extends InteractionWiredEffect {
}
});
Set<HabboItem> result = this.applySelectorModifiers(matched, this.getSelectableFloorItems(room), ctx.targets().items(), filterExisting, invert);
Set<HabboItem> result = this.applySelectorModifiers(matched, this.getSelectableFloorItems(room, ctx), ctx.targets().items(), filterExisting, invert);
ctx.targets().setItems(result);
}
@@ -61,6 +61,8 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect {
Room room = ctx.room();
if (room == null || tileOffsets.isEmpty()) return;
boolean includeWiredItems = this.includeWiredTargets(ctx);
List<int[]> sourcePositions = resolveSourcePositions(ctx, room);
if (sourcePositions.isEmpty()) return;
@@ -75,7 +77,7 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect {
for (HabboItem item : room.getItemsAt(tx, ty)) {
if (item == null) continue;
totalRaw++;
if (item instanceof InteractionWired) {
if (!includeWiredItems && item instanceof InteractionWired) {
wiredSkipped++;
LOGGER.info("[FurniNeighborhood] SKIP wired item {} ({}) at ({},{})",
item.getId(), item.getClass().getSimpleName(), tx, ty);
@@ -89,7 +91,7 @@ public class WiredEffectFurniNeighborhood extends InteractionWiredEffect {
}
LOGGER.info("[FurniNeighborhood] Raw={}, wiredSkipped={}, kept={}", totalRaw, wiredSkipped, result.size());
result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), filterExisting, invert);
result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room, ctx), ctx.targets().items(), filterExisting, invert);
// Always set the selector result — even if empty.
// An empty result means no items matched the neighborhood, so downstream
@@ -67,12 +67,13 @@ public class WiredEffectFurniOnFurni extends InteractionWiredEffect {
}
Set<HabboItem> result = new LinkedHashSet<>();
boolean includeWiredItems = this.includeWiredTargets(ctx);
for (HabboItem sourceItem : sourceItems) {
result.addAll(this.resolveRelatedItems(room, sourceItem));
result.addAll(this.resolveRelatedItems(room, sourceItem, includeWiredItems));
}
result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert);
result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room, ctx), ctx.targets().items(), this.filterExisting, this.invert);
ctx.targets().setItems(result);
}
@@ -211,7 +212,7 @@ public class WiredEffectFurniOnFurni extends InteractionWiredEffect {
return false;
}
private Set<HabboItem> resolveRelatedItems(Room room, HabboItem sourceItem) {
private Set<HabboItem> resolveRelatedItems(Room room, HabboItem sourceItem, boolean includeWiredItems) {
Set<HabboItem> result = new LinkedHashSet<>();
if (sourceItem == null) {
@@ -237,7 +238,7 @@ public class WiredEffectFurniOnFurni extends InteractionWiredEffect {
}
for (HabboItem matchedItem : room.getItemsAt(tile)) {
if (matchedItem == null || matchedItem instanceof InteractionWired) {
if (matchedItem == null || (!includeWiredItems && matchedItem instanceof InteractionWired)) {
continue;
}
@@ -45,12 +45,14 @@ public class WiredEffectFurniPicks extends InteractionWiredEffect {
return;
}
boolean includeWiredItems = this.includeWiredTargets(ctx);
Set<HabboItem> result = this.pickedFurniIds.stream()
.map(room::getHabboItem)
.filter(item -> item != null && !(item instanceof InteractionWired))
.filter(item -> item != null && (includeWiredItems || !(item instanceof InteractionWired)))
.collect(Collectors.toCollection(LinkedHashSet::new));
result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert);
result = this.applySelectorModifiers(result, this.getSelectableFloorItems(room, ctx), ctx.targets().items(), this.filterExisting, this.invert);
ctx.targets().setItems(result);
}
@@ -42,15 +42,17 @@ public class WiredEffectFurniSignal extends InteractionWiredEffect {
return;
}
boolean includeWiredItems = this.includeWiredTargets(ctx);
Set<HabboItem> result = new LinkedHashSet<>();
if (ctx.eventType() == WiredEvent.Type.SIGNAL_RECEIVED) {
List<HabboItem> signalItems = WiredSourceUtil.resolveItems(ctx, WiredSourceUtil.SOURCE_SIGNAL, null);
Set<HabboItem> matched = signalItems.stream()
.filter(item -> item != null && !(item instanceof InteractionWired))
.filter(item -> item != null && (includeWiredItems || !(item instanceof InteractionWired)))
.collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new));
result = this.applySelectorModifiers(matched, this.getSelectableFloorItems(room), ctx.targets().items(), this.filterExisting, this.invert);
result = this.applySelectorModifiers(matched, this.getSelectableFloorItems(room, ctx), ctx.targets().items(), this.filterExisting, this.invert);
}
ctx.targets().setItems(result);
@@ -147,6 +147,22 @@ public class WiredEffectUsersArea extends InteractionWiredEffect {
return false;
}
public int getRootX() {
return this.rootX;
}
public int getRootY() {
return this.rootY;
}
public int getAreaWidth() {
return this.areaWidth;
}
public int getAreaHeight() {
return this.areaHeight;
}
static class JsonData {
int rootX;
int rootY;
@@ -28,6 +28,7 @@ import com.eu.habbo.messages.outgoing.inventory.InventoryRefreshComposer;
import com.eu.habbo.messages.outgoing.rooms.HideDoorbellComposer;
import com.eu.habbo.messages.outgoing.rooms.UpdateStackHeightComposer;
import com.eu.habbo.messages.outgoing.rooms.items.*;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserIgnoredComposer;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
import com.eu.habbo.plugin.Event;
import com.eu.habbo.plugin.events.furniture.FurniturePickedUpEvent;
@@ -1890,10 +1891,12 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
public void muteHabbo(Habbo habbo, int minutes) {
this.chatManager.muteHabbo(habbo, minutes);
this.sendComposer(new RoomUserIgnoredComposer(habbo, RoomUserIgnoredComposer.MUTED).compose());
}
public void unmuteHabbo(Habbo habbo) {
this.chatManager.unmuteHabbo(habbo);
this.sendComposer(new RoomUserIgnoredComposer(habbo, RoomUserIgnoredComposer.UNIGNORED).compose());
}
public boolean isMuted(Habbo habbo) {
@@ -654,6 +654,8 @@ public class RoomManager {
}
habbo.setRoomUnit(new RoomUnit());
habbo.getHabboInfo().setRoomEntryMethod("door");
habbo.getHabboInfo().setRoomEntryTeleportId(0);
habbo.getRoomUnit().clearStatus();
if (habbo.getRoomUnit().getCurrentLocation() == null) {
@@ -672,8 +674,10 @@ public class RoomManager {
// Furniture teleport spawn
habbo.getRoomUnit().setCanLeaveRoomByDoor(false);
habbo.getRoomUnit().isTeleporting = true;
habbo.getHabboInfo().setRoomEntryMethod("teleport");
HabboItem topItem = room.getTopItemAt(doorLocation.x, doorLocation.y);
if (topItem != null) {
habbo.getHabboInfo().setRoomEntryTeleportId(topItem.getId());
habbo.getRoomUnit().setRotation(RoomUserRotation.values()[topItem.getRotation()]);
}
}
@@ -47,6 +47,8 @@ public class HabboInfo implements Runnable {
private int InfostandOverlay;
private int loadingRoom;
private Room currentRoom;
private String roomEntryMethod = "door";
private int roomEntryTeleportId = 0;
private int roomQueueId;
private RideablePet riding;
private Class<? extends Game> currentGame;
@@ -435,6 +437,22 @@ public class HabboInfo implements Runnable {
this.currentRoom = room;
}
public String getRoomEntryMethod() {
return this.roomEntryMethod;
}
public void setRoomEntryMethod(String roomEntryMethod) {
this.roomEntryMethod = roomEntryMethod;
}
public int getRoomEntryTeleportId() {
return this.roomEntryTeleportId;
}
public void setRoomEntryTeleportId(int roomEntryTeleportId) {
this.roomEntryTeleportId = roomEntryTeleportId;
}
public int getRoomQueueId() {
return this.roomQueueId;
}
@@ -133,6 +133,24 @@ public abstract class HabboItem implements Runnable, IEventTriggers {
serverMessage.appendInt(-1);
serverMessage.appendInt(this.isUsable());
serverMessage.appendInt(this.getUserId());
serverMessage.appendInt(this.getBaseItem().allowStack() ? 1 : 0);
serverMessage.appendInt(this.getBaseItem().allowSit() ? 1 : 0);
serverMessage.appendInt(this.getBaseItem().allowLay() ? 1 : 0);
serverMessage.appendInt(this.getBaseItem().allowWalk() ? 1 : 0);
serverMessage.appendInt(this.getBaseItem().getWidth());
serverMessage.appendInt(this.getBaseItem().getLength());
serverMessage.appendInt(this.getTeleportTargetId());
}
public int getTeleportTargetId() {
if (!(InteractionTeleport.class.isAssignableFrom(this.getBaseItem().getInteractionType().getType())
|| InteractionTeleportTile.class.isAssignableFrom(this.getBaseItem().getInteractionType().getType()))) {
return 0;
}
int[] target = Emulator.getGameEnvironment().getItemManager().getTargetTeleportRoomId(this);
return (target.length >= 2) ? target[1] : 0;
}
public int getId() {
@@ -59,6 +59,9 @@ public final class WiredContext {
/** Extra settings from the trigger item (for legacy compatibility) */
private final Object[] legacySettings;
/** Whether selector item resolution should include wired furniture too. */
private boolean includeWiredSelectorItems = false;
/**
* Create a new wired context.
*
@@ -256,6 +259,14 @@ public final class WiredContext {
return legacySettings != null ? legacySettings : new Object[0];
}
public boolean includeWiredSelectorItems() {
return this.includeWiredSelectorItems;
}
public void setIncludeWiredSelectorItems(boolean includeWiredSelectorItems) {
this.includeWiredSelectorItems = includeWiredSelectorItems;
}
// ========== Utility Methods ==========
/**
@@ -93,6 +93,24 @@ public final class WiredSourceUtil {
return value == SOURCE_SELECTED || isDefaultUserSource(value);
}
public static List<HabboItem> resolveSelectorItems(WiredContext ctx, boolean includeWiredItems) {
if (ctx == null) {
return Collections.emptyList();
}
if (!includeWiredItems) {
return resolveItems(ctx, SOURCE_SELECTOR, null);
}
WiredContext selectorContext = executeSelectors(cloneSelectorContext(ctx, true));
if (selectorContext == null || !selectorContext.targets().isItemsModifiedBySelector()) {
return Collections.emptyList();
}
return new ArrayList<>(selectorContext.targets().items());
}
private static WiredTargets getSelectorTargets(WiredContext ctx) {
if (ctx == null) {
return new WiredTargets();
@@ -139,6 +157,7 @@ public final class WiredSourceUtil {
new WiredState(100),
originalCtx.legacySettings()
);
selectorCtx.setIncludeWiredSelectorItems(originalCtx.includeWiredSelectorItems());
List<InteractionWiredEffect> selectorEffects = getOrderedSelectorEffects(originalCtx, room, triggerItem);
@@ -159,6 +178,23 @@ public final class WiredSourceUtil {
return selectorCtx;
}
private static WiredContext cloneSelectorContext(WiredContext originalCtx, boolean includeWiredItems) {
if (originalCtx == null) {
return null;
}
WiredContext selectorCtx = new WiredContext(
originalCtx.event(),
originalCtx.triggerItem(),
originalCtx.stack(),
originalCtx.services(),
new WiredState(100),
originalCtx.legacySettings()
);
selectorCtx.setIncludeWiredSelectorItems(includeWiredItems);
return selectorCtx;
}
private static List<InteractionWiredEffect> getOrderedSelectorEffects(WiredContext originalCtx, Room room, HabboItem triggerItem) {
List<InteractionWiredEffect> selectorEffects = new ArrayList<>();
@@ -1,9 +1,13 @@
package com.eu.habbo.habbohotel.wired.core;
import com.eu.habbo.habbohotel.bots.Bot;
import com.eu.habbo.habbohotel.items.interactions.InteractionWiredExtra;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputFurniName;
import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredExtraTextOutputUsername;
import com.eu.habbo.habbohotel.pets.Pet;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit;
import com.eu.habbo.habbohotel.rooms.RoomUnitType;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboItem;
import gnu.trove.set.hash.THashSet;
@@ -36,18 +40,25 @@ public final class WiredTextPlaceholderUtil {
String resolvedText = text;
for (InteractionWiredExtra extra : WiredExecutionOrderUtil.sort(extras)) {
if (!(extra instanceof WiredExtraTextOutputUsername)) {
if (extra instanceof WiredExtraTextOutputUsername) {
WiredExtraTextOutputUsername usernameExtra = (WiredExtraTextOutputUsername) extra;
String placeholderToken = usernameExtra.getPlaceholderToken();
if (!placeholderToken.isEmpty() && resolvedText.contains(placeholderToken)) {
resolvedText = resolvedText.replace(placeholderToken, buildUsernameReplacement(ctx, usernameExtra));
}
continue;
}
WiredExtraTextOutputUsername usernameExtra = (WiredExtraTextOutputUsername) extra;
String placeholderToken = usernameExtra.getPlaceholderToken();
if (extra instanceof WiredExtraTextOutputFurniName) {
WiredExtraTextOutputFurniName furniExtra = (WiredExtraTextOutputFurniName) extra;
String placeholderToken = furniExtra.getPlaceholderToken();
if (placeholderToken.isEmpty() || !resolvedText.contains(placeholderToken)) {
continue;
if (!placeholderToken.isEmpty() && resolvedText.contains(placeholderToken)) {
resolvedText = resolvedText.replace(placeholderToken, buildFurniNameReplacement(ctx, furniExtra));
}
}
resolvedText = resolvedText.replace(placeholderToken, buildUsernameReplacement(ctx, usernameExtra));
}
return resolvedText;
@@ -83,7 +94,6 @@ public final class WiredTextPlaceholderUtil {
return "";
}
Room room = ctx.room();
LinkedHashSet<Integer> seenUserIds = new LinkedHashSet<>();
List<String> usernames = new ArrayList<>();
@@ -92,12 +102,12 @@ public final class WiredTextPlaceholderUtil {
continue;
}
Habbo habbo = room.getHabbo(unit);
if ((habbo == null) || (habbo.getHabboInfo() == null)) {
String roomUnitName = getRoomUnitName(ctx.room(), unit);
if (roomUnitName == null || roomUnitName.trim().isEmpty()) {
continue;
}
usernames.add(habbo.getHabboInfo().getUsername());
usernames.add(roomUnitName);
}
if (usernames.isEmpty()) {
@@ -110,4 +120,70 @@ public final class WiredTextPlaceholderUtil {
return usernames.get(0);
}
private static String buildFurniNameReplacement(WiredContext ctx, WiredExtraTextOutputFurniName extra) {
List<HabboItem> items;
if (extra.getFurniSource() == WiredSourceUtil.SOURCE_SELECTOR) {
items = WiredSourceUtil.resolveSelectorItems(ctx, true);
} else {
items = WiredSourceUtil.resolveItems(ctx, extra.getFurniSource(), extra.getItems());
}
if (items.isEmpty()) {
return "";
}
LinkedHashSet<Integer> seenItemIds = new LinkedHashSet<>();
List<String> furniNames = new ArrayList<>();
for (HabboItem item : items) {
if ((item == null) || (item.getBaseItem() == null) || !seenItemIds.add(item.getId())) {
continue;
}
String furniName = item.getBaseItem().getFullName();
if (furniName == null || furniName.trim().isEmpty()) {
furniName = item.getBaseItem().getName();
}
if (furniName == null || furniName.trim().isEmpty()) {
continue;
}
furniNames.add(furniName);
}
if (furniNames.isEmpty()) {
return "";
}
if (extra.getPlaceholderType() == WiredExtraTextOutputFurniName.TYPE_MULTIPLE) {
return String.join(extra.getDelimiter(), furniNames);
}
return furniNames.get(0);
}
private static String getRoomUnitName(Room room, RoomUnit roomUnit) {
if (room == null || roomUnit == null) {
return "";
}
if (roomUnit.getRoomUnitType() == RoomUnitType.USER) {
Habbo habbo = room.getHabbo(roomUnit);
return (habbo != null && habbo.getHabboInfo() != null) ? habbo.getHabboInfo().getUsername() : "";
}
if (roomUnit.getRoomUnitType() == RoomUnitType.BOT) {
Bot bot = room.getBot(roomUnit);
return (bot != null) ? bot.getName() : "";
}
if (roomUnit.getRoomUnitType() == RoomUnitType.PET) {
Pet pet = room.getPet(roomUnit);
return (pet != null) ? pet.getName() : "";
}
return "";
}
}
@@ -7,8 +7,12 @@ import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertComposer;
import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertKeys;
import com.eu.habbo.messages.outgoing.rooms.WiredMovementsComposer;
import com.eu.habbo.messages.outgoing.rooms.items.FloorItemUpdateComposer;
import java.util.ArrayList;
import java.util.List;
public class UpdateFurniturePositionEvent extends MessageHandler {
@Override
public void handle() throws Exception {
@@ -34,10 +38,30 @@ public class UpdateFurniturePositionEvent extends MessageHandler {
return;
}
error = room.moveFurniTo(item, tile, rotation, z, this.client.getHabbo(), true, true);
RoomTile oldTile = room.getLayout().getTile(item.getX(), item.getY());
double oldZ = item.getZ();
error = room.moveFurniTo(item, tile, rotation, z, this.client.getHabbo(), false, true);
if (error != FurnitureMovementError.NONE) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FURNITURE_PLACEMENT_ERROR.key, error.errorCode));
this.client.sendResponse(new FloorItemUpdateComposer(item));
return;
}
if (oldTile != null) {
List<WiredMovementsComposer.MovementData> movements = new ArrayList<>(1);
movements.add(WiredMovementsComposer.furniMovement(
item.getId(),
oldTile.x,
oldTile.y,
tile.x,
tile.y,
oldZ,
item.getZ(),
item.getRotation(),
WiredMovementsComposer.DEFAULT_DURATION));
room.sendComposer(new WiredMovementsComposer(movements).compose());
}
}
}
@@ -7,6 +7,7 @@ import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.outgoing.MessageComposer;
import com.eu.habbo.messages.outgoing.Outgoing;
import com.eu.habbo.util.HotelDateTimeUtil;
public class RoomDataComposer extends MessageComposer {
private final Room room;
@@ -108,6 +109,9 @@ public class RoomDataComposer extends MessageComposer {
this.response.appendInt(this.room.getChatSpeed());
this.response.appendInt(this.room.getChatDistance());
this.response.appendInt(this.room.getChatProtection());
this.response.appendString(HotelDateTimeUtil.getTimezoneId());
this.response.appendString(String.valueOf(HotelDateTimeUtil.now().toInstant().toEpochMilli()));
this.response.appendInt(Room.MAXIMUM_FURNI);
return this.response;
@@ -128,4 +132,4 @@ public class RoomDataComposer extends MessageComposer {
public boolean isEnterRoom() {
return enterRoom;
}
}
}
@@ -24,6 +24,13 @@ public class AddFloorItemComposer extends MessageComposer {
this.response.appendInt(-1);
this.response.appendInt(this.item instanceof InteractionTeleport || this.item instanceof InteractionSwitch || this.item instanceof InteractionSwitchRemoteControl || this.item instanceof InteractionVendingMachine || this.item instanceof InteractionInformationTerminal || this.item instanceof InteractionPostIt|| this.item instanceof InteractionPuzzleBox ? 2 : this.item.isUsable() ? 1 : 0);
this.response.appendInt(this.item.getUserId());
this.response.appendInt(this.item.getBaseItem().allowStack() ? 1 : 0);
this.response.appendInt(this.item.getBaseItem().allowSit() ? 1 : 0);
this.response.appendInt(this.item.getBaseItem().allowLay() ? 1 : 0);
this.response.appendInt(this.item.getBaseItem().allowWalk() ? 1 : 0);
this.response.appendInt(this.item.getBaseItem().getWidth());
this.response.appendInt(this.item.getBaseItem().getLength());
this.response.appendInt(this.item.getTeleportTargetId());
this.response.appendString(this.itemOwnerName);
return this.response;
}
@@ -23,6 +23,13 @@ public class FloorItemUpdateComposer extends MessageComposer {
this.response.appendInt(-1);
this.response.appendInt(0);
this.response.appendInt(this.item.getUserId());
this.response.appendInt(this.item.getBaseItem().allowStack() ? 1 : 0);
this.response.appendInt(this.item.getBaseItem().allowSit() ? 1 : 0);
this.response.appendInt(this.item.getBaseItem().allowLay() ? 1 : 0);
this.response.appendInt(this.item.getBaseItem().allowWalk() ? 1 : 0);
this.response.appendInt(this.item.getBaseItem().getWidth());
this.response.appendInt(this.item.getBaseItem().getLength());
this.response.appendInt(this.item.getTeleportTargetId());
return this.response;
}
@@ -46,6 +46,13 @@ public class RoomFloorItemsComposer extends MessageComposer {
this.response.appendInt(-1);
this.response.appendInt(item instanceof InteractionTeleport || item instanceof InteractionSwitch || item instanceof InteractionSwitchRemoteControl || item instanceof InteractionVendingMachine || item instanceof InteractionInformationTerminal || item instanceof InteractionPostIt || item instanceof InteractionPuzzleBox ? 2 : item.isUsable() ? 1 : 0);
this.response.appendInt(item.getUserId());
this.response.appendInt(item.getBaseItem().allowStack() ? 1 : 0);
this.response.appendInt(item.getBaseItem().allowSit() ? 1 : 0);
this.response.appendInt(item.getBaseItem().allowLay() ? 1 : 0);
this.response.appendInt(item.getBaseItem().allowWalk() ? 1 : 0);
this.response.appendInt(item.getBaseItem().getWidth());
this.response.appendInt(item.getBaseItem().getLength());
this.response.appendInt(item.getTeleportTargetId());
}
return this.response;
}
@@ -59,6 +59,8 @@ public class RoomPetComposer extends MessageComposer implements TIntObjectProced
this.response.appendBoolean(pet instanceof MonsterplantPet && ((MonsterplantPet) pet).isPubliclyBreedable()); //Breedable checkbox //Toggle breeding permission
this.response.appendInt(pet instanceof MonsterplantPet ? ((MonsterplantPet) pet).getGrowthStage() : pet.getLevel());
this.response.appendString("");
this.response.appendString("unknown");
this.response.appendInt(0);
return true;
}
@@ -66,6 +66,8 @@ public class RoomUsersComposer extends MessageComposer {
this.response.appendString("");
this.response.appendInt(this.habbo.getHabboStats().getAchievementScore());
this.response.appendBoolean(true);
this.response.appendString(this.habbo.getHabboInfo().getRoomEntryMethod());
this.response.appendInt(this.habbo.getHabboInfo().getRoomEntryTeleportId());
} else if (this.habbos != null) {
this.response.appendInt(this.habbos.size());
for (Habbo habbo : this.habbos) {
@@ -97,6 +99,8 @@ public class RoomUsersComposer extends MessageComposer {
this.response.appendString("");
this.response.appendInt(habbo.getHabboStats().getAchievementScore());
this.response.appendBoolean(true);
this.response.appendString(habbo.getHabboInfo().getRoomEntryMethod());
this.response.appendInt(habbo.getHabboInfo().getRoomEntryTeleportId());
}
}
} else if (this.bot != null) {
@@ -128,6 +132,8 @@ public class RoomUsersComposer extends MessageComposer {
this.response.appendShort(7);
this.response.appendShort(8);
this.response.appendShort(9);
this.response.appendString("unknown");
this.response.appendInt(0);
} else if (this.bots != null) {
this.response.appendInt(this.bots.size());
for (Bot bot : this.bots) {
@@ -158,6 +164,8 @@ public class RoomUsersComposer extends MessageComposer {
this.response.appendShort(7);
this.response.appendShort(8);
this.response.appendShort(9);
this.response.appendString("unknown");
this.response.appendInt(0);
}
}
return this.response;
@@ -10,17 +10,8 @@ import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.habbohotel.wired.core.WiredFreezeUtil;
import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer;
import com.eu.habbo.threading.runnables.HabboItemNewState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
class TeleportActionTwo implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(TeleportActionTwo.class);
private final HabboItem currentTeleport;
private final Room room;
private final GameClient client;
@@ -65,23 +56,11 @@ class TeleportActionTwo implements Runnable {
((InteractionTeleport) this.currentTeleport).setTargetId(0);
}
if (((InteractionTeleport) this.currentTeleport).getTargetId() == 0) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT items_teleports.*, A.room_id as a_room_id, A.id as a_id, B.room_id as b_room_id, B.id as b_id FROM items_teleports INNER JOIN items AS A ON items_teleports.teleport_one_id = A.id INNER JOIN items AS B ON items_teleports.teleport_two_id = B.id WHERE (teleport_one_id = ? OR teleport_two_id = ?)")) {
statement.setInt(1, this.currentTeleport.getId());
statement.setInt(2, this.currentTeleport.getId());
int[] targetTeleport = Emulator.getGameEnvironment().getItemManager().getTargetTeleportRoomId(this.currentTeleport);
try (ResultSet set = statement.executeQuery()) {
if (set.next()) {
if (set.getInt("a_id") != this.currentTeleport.getId()) {
((InteractionTeleport) this.currentTeleport).setTargetId(set.getInt("a_id"));
((InteractionTeleport) this.currentTeleport).setTargetRoomId(set.getInt("a_room_id"));
} else {
((InteractionTeleport) this.currentTeleport).setTargetId(set.getInt("b_id"));
((InteractionTeleport) this.currentTeleport).setTargetRoomId(set.getInt("b_room_id"));
}
}
}
} catch (SQLException e) {
LOGGER.error("Caught SQL exception", e);
if (targetTeleport.length == 2) {
((InteractionTeleport) this.currentTeleport).setTargetRoomId(targetTeleport[0]);
((InteractionTeleport) this.currentTeleport).setTargetId(targetTeleport[1]);
}
}