From 8463a9cea0467788506f99f148503295dec3e7ea Mon Sep 17 00:00:00 2001 From: duckietm Date: Wed, 11 Mar 2026 12:37:05 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=95=20wf=5Fact=5Fsend=5Fsignal=20&=20w?= =?UTF-8?q?f=5Ftrg=5Frecv=5Fsignal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the signal sender can only be a max of 25 in a room the signal receiver can only be a max of 5 in a room one signal receiver can only accept a max of 5 senders (you can select the receivers with the senders) Why : otherwise you can flood the rooms and let you CPU go 🍌 --- .../habbo/habbohotel/items/ItemManager.java | 2 + .../wired/effects/WiredEffectSendSignal.java | 350 ++++++++++++++++++ .../triggers/WiredTriggerReceiveSignal.java | 118 ++++++ .../rooms/FurnitureMovementError.java | 4 +- .../habbohotel/rooms/RoomItemManager.java | 12 + .../habbohotel/rooms/RoomSpecialTypes.java | 40 +- .../habbohotel/wired/WiredEffectType.java | 3 +- .../habbohotel/wired/WiredTriggerType.java | 3 +- .../habbohotel/wired/core/WiredEvent.java | 15 + 9 files changed, 542 insertions(+), 5 deletions(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java create mode 100644 Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java index e4b8feb7..b33f1c10 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/ItemManager.java @@ -221,6 +221,7 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_trg_score_achieved", WiredTriggerScoreAchieved.class)); this.interactionsList.add(new ItemInteraction("wf_trg_game_team_win", WiredTriggerTeamWins.class)); this.interactionsList.add(new ItemInteraction("wf_trg_game_team_lose", WiredTriggerTeamLoses.class)); + this.interactionsList.add(new ItemInteraction("wf_trg_recv_signal", WiredTriggerReceiveSignal.class)); this.interactionsList.add(new ItemInteraction("wf_act_toggle_state", WiredEffectToggleFurni.class)); @@ -258,6 +259,7 @@ public class ItemManager { this.interactionsList.add(new ItemInteraction("wf_slc_furni_bytype", WiredEffectFurniByType.class)); this.interactionsList.add(new ItemInteraction("wf_slc_users_area", WiredEffectUsersArea.class)); this.interactionsList.add(new ItemInteraction("wf_slc_users_neighborhood", WiredEffectUsersNeighborhood.class)); + this.interactionsList.add(new ItemInteraction("wf_act_send_signal", WiredEffectSendSignal.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_has_furni_on", WiredConditionFurniHaveFurni.class)); this.interactionsList.add(new ItemInteraction("wf_cnd_furnis_hv_avtrs", WiredConditionFurniHaveHabbo.class)); diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java new file mode 100644 index 00000000..66a4429d --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/effects/WiredEffectSendSignal.java @@ -0,0 +1,350 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.effects; + +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.InteractionWiredEffect; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomSpecialTypes; +import com.eu.habbo.habbohotel.rooms.RoomTile; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredEffectType; +import com.eu.habbo.habbohotel.wired.core.*; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.messages.ServerMessage; +import com.eu.habbo.messages.incoming.wired.WiredSaveException; +import gnu.trove.procedure.TObjectProcedure; +import gnu.trove.set.hash.THashSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + + +public class WiredEffectSendSignal extends InteractionWiredEffect { + private static final Logger LOGGER = LoggerFactory.getLogger(WiredEffectSendSignal.class); + + public static final WiredEffectType type = WiredEffectType.SEND_SIGNAL; + + private static final int MAX_SIGNAL_DEPTH = 10; + + private static final int ANTENNA_PICKED = 0; + private static final int ANTENNA_TRIGGER = 1; + + private static final int FORWARD_NONE = 0; + private static final int FORWARD_TRIGGER = 1; + + private THashSet items; + private int antennaSource = ANTENNA_PICKED; + private int furniForward = FORWARD_NONE; + private int userForward = FORWARD_NONE; + private boolean signalPerFurni = false; + private boolean signalPerUser = false; + private int channel = 0; + + public WiredEffectSendSignal(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + this.items = new THashSet<>(); + } + + public WiredEffectSendSignal(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 void execute(WiredContext ctx) { + Room room = ctx.room(); + if (room == null) return; + + LOGGER.debug("[SendSignal] execute() called, itemId={}, antennaSource={}, pickedItems={}", this.getId(), antennaSource, this.items.size()); + + int currentDepth = ctx.event().getCallStackDepth(); + if (currentDepth >= MAX_SIGNAL_DEPTH) { + LOGGER.debug("[SendSignal] Max signal depth reached ({}), aborting", currentDepth); + return; + } + + Collection antennas; + if (antennaSource == ANTENNA_TRIGGER) { + antennas = ctx.sourceItem() + .map(Collections::singleton) + .orElse(Collections.emptySet()); + } else { + antennas = ctx.targets().isItemsModifiedBySelector() + ? new ArrayList<>(ctx.targets().items()) + : new ArrayList<>(this.items); + } + + if (antennas.isEmpty()) { + LOGGER.debug("[SendSignal] No antennas resolved, aborting. antennaSource={}, selectorModified={}", antennaSource, ctx.targets().isItemsModifiedBySelector()); + return; + } + LOGGER.debug("[SendSignal] Resolved {} antenna(s), firing signals", antennas.size()); + + RoomUnit forwardedUser = null; + if (userForward == FORWARD_TRIGGER) { + forwardedUser = ctx.actor().orElse(null); + } + + HabboItem forwardedFurni = null; + if (furniForward == FORWARD_TRIGGER) { + forwardedFurni = ctx.sourceItem().orElse(null); + } + + Set visitedTiles = new HashSet<>(); + List antennaTiles = new ArrayList<>(); + for (HabboItem antenna : antennas) { + if (antenna == null) continue; + String key = antenna.getX() + "," + antenna.getY(); + if (visitedTiles.add(key)) { + RoomTile tile = room.getLayout().getTile(antenna.getX(), antenna.getY()); + if (tile != null) { + antennaTiles.add(tile); + } + } + } + + int nextDepth = currentDepth + 1; + + if (signalPerFurni || signalPerUser) { + if (signalPerFurni) { + for (RoomTile tile : antennaTiles) { + fireSignalAtTile(room, tile, forwardedUser, forwardedFurni, nextDepth); + } + } + if (signalPerUser && ctx.targets().hasUsers()) { + for (RoomUnit user : ctx.targets().users()) { + for (RoomTile tile : antennaTiles) { + fireSignalAtTile(room, tile, user, forwardedFurni, nextDepth); + } + } + } else if (!signalPerFurni) { + for (RoomTile tile : antennaTiles) { + fireSignalAtTile(room, tile, forwardedUser, forwardedFurni, nextDepth); + } + } + } else { + for (RoomTile tile : antennaTiles) { + fireSignalAtTile(room, tile, forwardedUser, forwardedFurni, nextDepth); + } + } + } + + private void fireSignalAtTile(Room room, RoomTile tile, RoomUnit actor, HabboItem sourceItem, int depth) { + LOGGER.debug("[SendSignal] fireSignalAtTile: tile={},{} depth={} channel={} actor={} sourceItem={}", tile.x, tile.y, depth, channel, actor != null ? actor.getId() : "null", sourceItem != null ? sourceItem.getId() : "null"); + + WiredEvent.Builder builder = WiredEvent.builder(WiredEvent.Type.SIGNAL_RECEIVED, room) + .tile(tile) + .callStackDepth(depth) + .signalChannel(this.channel) + .triggeredByEffect(true); + + if (actor != null) builder.actor(actor); + if (sourceItem != null) builder.sourceItem(sourceItem); + + boolean result = WiredManager.handleEvent(builder.build()); + LOGGER.debug("[SendSignal] handleEvent returned: {}", result); + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + List itemsSnapshot = new ArrayList<>(this.items); + + itemsSnapshot.removeIf(item -> + item.getRoomId() != this.getRoomId() || room.getHabboItem(item.getId()) == null); + this.items.retainAll(itemsSnapshot); + + message.appendBoolean(false); + message.appendInt(WiredManager.MAXIMUM_FURNI_SELECTION); + message.appendInt(itemsSnapshot.size()); + for (HabboItem item : itemsSnapshot) { + message.appendInt(item.getId()); + } + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + + message.appendInt(6); + message.appendInt(antennaSource); + message.appendInt(furniForward); + message.appendInt(userForward); + message.appendInt(signalPerFurni ? 1 : 0); + message.appendInt(signalPerUser ? 1 : 0); + message.appendInt(channel); + + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(this.getDelay()); + + if (this.requiresTriggeringUser()) { + List invalidTriggers = new ArrayList<>(); + room.getRoomSpecialTypes().getTriggers(this.getX(), this.getY()).forEach(new TObjectProcedure() { + @Override + public boolean execute(InteractionWiredTrigger object) { + if (!object.isTriggeredByRoomUnit()) { + invalidTriggers.add(object.getBaseItem().getSpriteId()); + } + return true; + } + }); + message.appendInt(invalidTriggers.size()); + for (Integer i : invalidTriggers) { + message.appendInt(i); + } + } else { + message.appendInt(0); + } + } + + @Override + public boolean saveData(WiredSettings settings, GameClient gameClient) throws WiredSaveException { + int itemsCount = settings.getFurniIds().length; + if (itemsCount > Emulator.getConfig().getInt("hotel.wired.furni.selection.count")) { + throw new WiredSaveException("Too many furni selected"); + } + + List newItems = new ArrayList<>(); + Room room = Emulator.getGameEnvironment().getRoomManager().getRoom(this.getRoomId()); + for (int i = 0; i < itemsCount; i++) { + int itemId = settings.getFurniIds()[i]; + HabboItem it = room.getHabboItem(itemId); + if (it == null) throw new WiredSaveException(String.format("Item %s not found", itemId)); + newItems.add(it); + } + + if (room != null && room.getRoomSpecialTypes() != null) { + for (HabboItem receiver : newItems) { + int count = room.getRoomSpecialTypes().countSendersTargetingReceiver(receiver.getId(), this); + if (count >= RoomSpecialTypes.MAX_SENDERS_PER_RECEIVER) { + throw new WiredSaveException("Maximum of " + RoomSpecialTypes.MAX_SENDERS_PER_RECEIVER + " senders per receiver reached"); + } + } + } + + int delay = settings.getDelay(); + if (delay > Emulator.getConfig().getInt("hotel.wired.max_delay", 20)) { + throw new WiredSaveException("Delay too long"); + } + + int[] params = settings.getIntParams(); + this.antennaSource = params.length > 0 ? params[0] : ANTENNA_PICKED; + this.furniForward = params.length > 1 ? params[1] : FORWARD_NONE; + this.userForward = params.length > 2 ? params[2] : FORWARD_NONE; + this.signalPerFurni = params.length > 3 && params[3] == 1; + this.signalPerUser = params.length > 4 && params[4] == 1; + this.channel = params.length > 5 ? params[5] : 0; + this.items.clear(); + this.items.addAll(newItems); + this.setDelay(delay); + + LOGGER.debug("[SendSignal] saveData: antennaSource={}, furniForward={}, userForward={}, signalPerFurni={}, signalPerUser={}, channel={}, items={}", + antennaSource, furniForward, userForward, signalPerFurni, signalPerUser, channel, items.size()); + + return true; + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public String getWiredData() { + List itemsSnapshot = new ArrayList<>(this.items); + return WiredManager.getGson().toJson(new JsonData( + this.getDelay(), + itemsSnapshot.stream().map(HabboItem::getId).collect(Collectors.toList()), + antennaSource, furniForward, userForward, signalPerFurni, signalPerUser, channel + )); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + this.items = new THashSet<>(); + String wiredData = set.getString("wired_data"); + + if (wiredData != null && wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.setDelay(data.delay); + this.antennaSource = data.antennaSource; + this.furniForward = data.furniForward; + this.userForward = data.userForward; + this.signalPerFurni = data.signalPerFurni; + this.signalPerUser = data.signalPerUser; + this.channel = data.channel; + if (data.itemIds != null) { + for (Integer id : data.itemIds) { + HabboItem item = room.getHabboItem(id); + if (item != null) this.items.add(item); + } + } + } + } + + @Override + public void onPickUp() { + this.items.clear(); + this.antennaSource = ANTENNA_PICKED; + this.furniForward = FORWARD_NONE; + this.userForward = FORWARD_NONE; + this.signalPerFurni = false; + this.signalPerUser = false; + this.channel = 0; + this.setDelay(0); + } + + @Override + public WiredEffectType getType() { + return type; + } + + public int getChannel() { + return channel; + } + + public boolean hasPickedItem(int itemId) { + try { + for (HabboItem item : new ArrayList<>(this.items)) { + if (item != null && item.getId() == itemId) return true; + } + } catch (Exception e) { + } + return false; + } + + @Override + protected long requiredCooldown() { + return COOLDOWN_TRIGGER_STACKS; + } + + static class JsonData { + int delay; + List itemIds; + int antennaSource; + int furniForward; + int userForward; + boolean signalPerFurni; + boolean signalPerUser; + int channel; + + public JsonData(int delay, List itemIds, int antennaSource, int furniForward, + int userForward, boolean signalPerFurni, boolean signalPerUser, int channel) { + this.delay = delay; + this.itemIds = itemIds; + this.antennaSource = antennaSource; + this.furniForward = furniForward; + this.userForward = userForward; + this.signalPerFurni = signalPerFurni; + this.signalPerUser = signalPerUser; + this.channel = channel; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java new file mode 100644 index 00000000..c2e96e93 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/interactions/wired/triggers/WiredTriggerReceiveSignal.java @@ -0,0 +1,118 @@ +package com.eu.habbo.habbohotel.items.interactions.wired.triggers; + +import com.eu.habbo.habbohotel.items.Item; +import com.eu.habbo.habbohotel.items.interactions.InteractionWiredTrigger; +import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; +import com.eu.habbo.habbohotel.rooms.Room; +import com.eu.habbo.habbohotel.rooms.RoomSpecialTypes; +import com.eu.habbo.habbohotel.rooms.RoomUnit; +import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.wired.WiredTriggerType; +import com.eu.habbo.habbohotel.wired.core.WiredEvent; +import com.eu.habbo.habbohotel.wired.core.WiredManager; +import com.eu.habbo.messages.ServerMessage; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class WiredTriggerReceiveSignal extends InteractionWiredTrigger { + public static final WiredTriggerType type = WiredTriggerType.RECEIVE_SIGNAL; + + private int channel = 0; // signal channel (0-based) + + public WiredTriggerReceiveSignal(ResultSet set, Item baseItem) throws SQLException { + super(set, baseItem); + } + + public WiredTriggerReceiveSignal(int id, int userId, Item item, String extradata, int limitedStack, int limitedSells) { + super(id, userId, item, extradata, limitedStack, limitedSells); + } + + @Override + public boolean matches(HabboItem triggerItem, WiredEvent event) { + return event.getType() == WiredEvent.Type.SIGNAL_RECEIVED + && event.getSignalChannel() == this.channel; + } + + public int getChannel() { + return channel; + } + + @Override + public boolean isTriggeredByRoomUnit() { + return false; + } + + @Deprecated + @Override + public boolean execute(RoomUnit roomUnit, Room room, Object[] stuff) { + return false; + } + + @Override + public WiredTriggerType getType() { + return type; + } + + @Override + public void serializeWiredData(ServerMessage message, Room room) { + int senderCount = 0; + try { + if (room != null && room.getRoomSpecialTypes() != null) { + senderCount = room.getRoomSpecialTypes().countSendersTargetingReceiver(this.getId()); + } + } catch (Exception e) { + } + + message.appendBoolean(false); + message.appendInt(0); + message.appendInt(0); + message.appendInt(this.getBaseItem().getSpriteId()); + message.appendInt(this.getId()); + message.appendString(""); + message.appendInt(3); + message.appendInt(channel); + message.appendInt(senderCount); + message.appendInt(RoomSpecialTypes.MAX_SENDERS_PER_RECEIVER); + message.appendInt(0); + message.appendInt(this.getType().code); + message.appendInt(0); + message.appendInt(0); + } + + @Override + public boolean saveData(WiredSettings settings) { + int[] params = settings.getIntParams(); + this.channel = params.length > 0 ? params[0] : 0; + return true; + } + + @Override + public String getWiredData() { + return WiredManager.getGson().toJson(new JsonData(channel)); + } + + @Override + public void loadWiredData(ResultSet set, Room room) throws SQLException { + String wiredData = set.getString("wired_data"); + if (wiredData != null && wiredData.startsWith("{")) { + JsonData data = WiredManager.getGson().fromJson(wiredData, JsonData.class); + this.channel = data.channel; + } + } + + @Override + public void onPickUp() { + this.channel = 0; + } + + static class JsonData { + int channel; + + public JsonData() {} + + public JsonData(int channel) { + this.channel = channel; + } + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/FurnitureMovementError.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/FurnitureMovementError.java index 9c1ddb76..c5638efb 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/FurnitureMovementError.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/FurnitureMovementError.java @@ -14,7 +14,9 @@ public enum FurnitureMovementError { MAX_DIMMERS("${room.error.max_dimmers}"), MAX_SOUNDFURNI("${room.errors.max_soundfurni}"), MAX_ITEMS("${room.error.max_furniture}"), - MAX_STICKIES("${room.error.max_stickies}"); + MAX_STICKIES("${room.error.max_stickies}"), + MAX_SIGNAL_SENDERS("${room.error.max_signal_senders}"), + MAX_SIGNAL_RECEIVERS("${room.error.max_signal_receivers}"); public final String errorCode; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java index d6d36d6f..466f8abf 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomItemManager.java @@ -14,7 +14,9 @@ import com.eu.habbo.habbohotel.items.interactions.games.freeze.InteractionFreeze import com.eu.habbo.habbohotel.items.interactions.games.tag.InteractionTagField; import com.eu.habbo.habbohotel.items.interactions.games.tag.InteractionTagPole; import com.eu.habbo.habbohotel.items.interactions.pets.*; +import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectSendSignal; import com.eu.habbo.habbohotel.items.interactions.wired.extra.WiredBlob; +import com.eu.habbo.habbohotel.items.interactions.wired.triggers.WiredTriggerReceiveSignal; import com.eu.habbo.habbohotel.permissions.Permission; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboInfo; @@ -1261,6 +1263,16 @@ public class RoomItemManager { * Places a floor furniture item at a position. */ public FurnitureMovementError placeFloorFurniAt(HabboItem item, RoomTile tile, int rotation, Habbo owner) { + RoomSpecialTypes specialTypes = this.room.getRoomSpecialTypes(); + if (specialTypes != null) { + if (item instanceof WiredEffectSendSignal && specialTypes.isSignalSenderLimitReached()) { + return FurnitureMovementError.MAX_SIGNAL_SENDERS; + } + if (item instanceof WiredTriggerReceiveSignal && specialTypes.isSignalReceiverLimitReached()) { + return FurnitureMovementError.MAX_SIGNAL_RECEIVERS; + } + } + boolean pluginHelper = false; if (Emulator.getPluginManager().isRegistered(FurniturePlacedEvent.class, true)) { FurniturePlacedEvent event = Emulator.getPluginManager() diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java index d6eabd1f..6d00aafe 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomSpecialTypes.java @@ -21,6 +21,7 @@ import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetFood; import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetToy; import com.eu.habbo.habbohotel.items.interactions.pets.InteractionPetTree; import com.eu.habbo.habbohotel.users.HabboItem; +import com.eu.habbo.habbohotel.items.interactions.wired.effects.WiredEffectSendSignal; import com.eu.habbo.habbohotel.wired.WiredConditionType; import com.eu.habbo.habbohotel.wired.WiredEffectType; import com.eu.habbo.habbohotel.wired.WiredTriggerType; @@ -340,11 +341,46 @@ public class RoomSpecialTypes { * Adds a wired trigger to the room. * @param trigger The trigger to add */ + public static final int MAX_SIGNAL_SENDERS_PER_ROOM = 25; + public static final int MAX_SIGNAL_RECEIVERS_PER_ROOM = 5; + public static final int MAX_SENDERS_PER_RECEIVER = 5; + + public boolean isSignalSenderLimitReached() { + Set existing = this.wiredEffects.get(WiredEffectType.SEND_SIGNAL); + return existing != null && existing.size() >= MAX_SIGNAL_SENDERS_PER_ROOM; + } + + public boolean isSignalReceiverLimitReached() { + Set existing = this.wiredTriggers.get(WiredTriggerType.RECEIVE_SIGNAL); + return existing != null && existing.size() >= MAX_SIGNAL_RECEIVERS_PER_ROOM; + } + + public int countSendersTargetingReceiver(int receiverItemId, InteractionWiredEffect excludeSender) { + Set senders = this.wiredEffects.get(WiredEffectType.SEND_SIGNAL); + if (senders == null) return 0; + + int count = 0; + for (InteractionWiredEffect effect : senders) { + if (excludeSender != null && effect.getId() == excludeSender.getId()) continue; + if (effect instanceof WiredEffectSendSignal) { + WiredEffectSendSignal sender = (WiredEffectSendSignal) effect; + if (sender.hasPickedItem(receiverItemId)) { + count++; + } + } + } + return count; + } + + public int countSendersTargetingReceiver(int receiverItemId) { + return countSendersTargetingReceiver(receiverItemId, null); + } + public void addTrigger(InteractionWiredTrigger trigger) { // Add to type-based index this.wiredTriggers.computeIfAbsent(trigger.getType(), k -> ConcurrentHashMap.newKeySet()) .add(trigger); - + // Add to spatial index long key = coordinateKey(trigger.getX(), trigger.getY()); this.wiredTriggersByLocation.computeIfAbsent(key, k -> ConcurrentHashMap.newKeySet()) @@ -464,7 +500,7 @@ public class RoomSpecialTypes { // Add to type-based index this.wiredEffects.computeIfAbsent(effect.getType(), k -> ConcurrentHashMap.newKeySet()) .add(effect); - + // Add to spatial index long key = coordinateKey(effect.getX(), effect.getY()); this.wiredEffectsByLocation.computeIfAbsent(key, k -> ConcurrentHashMap.newKeySet()) diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java index ef342a58..0d5e74d2 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredEffectType.java @@ -31,7 +31,8 @@ public enum WiredEffectType { FURNI_NEIGHBORHOOD_SELECTOR(29), FURNI_BYTYPE_SELECTOR(30), USERS_AREA_SELECTOR(31), - USERS_NEIGHBORHOOD_SELECTOR(32); + USERS_NEIGHBORHOOD_SELECTOR(32), + SEND_SIGNAL(33); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java index e1286fa1..e1767c1b 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/WiredTriggerType.java @@ -20,7 +20,8 @@ public enum WiredTriggerType { UNIDLES(11), CUSTOM(13), STARTS_DANCING(11), - STOPS_DANCING(11); + STOPS_DANCING(11), + RECEIVE_SIGNAL(15); public final int code; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java index 8ddc2215..ca3c543b 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/wired/core/WiredEvent.java @@ -94,6 +94,9 @@ public final class WiredEvent { /** Team loses a game */ TEAM_LOSES(WiredTriggerType.CUSTOM), + /** Signal received from a Send Signal effect */ + SIGNAL_RECEIVED(WiredTriggerType.RECEIVE_SIGNAL), + /** Custom trigger type for plugins */ CUSTOM(WiredTriggerType.CUSTOM); @@ -137,6 +140,7 @@ public final class WiredEvent { private final int scoreAdded; // amount added for score achieved events private final boolean triggeredByEffect; // true if triggered by a wired effect (to prevent loops) private final int callStackDepth; // recursion depth for trigger stacks effect + private final int signalChannel; // channel for signal routing (0-based) private final long createdAtMs; private WiredEvent(Builder builder) { @@ -151,6 +155,7 @@ public final class WiredEvent { this.scoreAdded = builder.scoreAdded; this.triggeredByEffect = builder.triggeredByEffect; this.callStackDepth = builder.callStackDepth; + this.signalChannel = builder.signalChannel; this.createdAtMs = builder.createdAtMs; } @@ -249,6 +254,10 @@ public final class WiredEvent { return callStackDepth; } + public int getSignalChannel() { + return signalChannel; + } + /** * Get the timestamp when this event was created. * @return milliseconds since epoch @@ -303,6 +312,7 @@ public final class WiredEvent { private int scoreAdded; private boolean triggeredByEffect; private int callStackDepth; + private int signalChannel; private long createdAtMs = System.currentTimeMillis(); private Builder(Type type, Room room) { @@ -402,6 +412,11 @@ public final class WiredEvent { return this; } + public Builder signalChannel(int signalChannel) { + this.signalChannel = signalChannel; + return this; + } + /** * Set a custom creation timestamp. * @param createdAtMs milliseconds since epoch