From 46306c820511168372d21132e63b9aa72971a927 Mon Sep 17 00:00:00 2001 From: medievalshell <150498573+medievalshell@users.noreply.github.com> Date: Thu, 4 Jun 2026 01:17:32 +0200 Subject: [PATCH 1/2] feat(mentions): hotel-wide @nick delivery + sender figure + disable-mention persistence - resolveHabbo() falls back to a hotel-wide online lookup so a direct @nick mention reaches the target even when they are in a different room (was resolved only within the sender's room). - HabboMention now carries the sender figure (live from the sender Habbo, history from a users.look JOIN); MentionReceived/MentionsList composers append it so the client can render the sender avatar in the notification. - 009: add users_settings.mentions_enabled / mass_mentions_enabled columns so :disablementions / :disablemassmentions actually persist. --- Database Updates/009_mentions_wordfilter.sql | 15 ++++++++++++++- .../habbo/habbohotel/mentions/HabboMention.java | 16 ++++++++++++++++ .../habbohotel/mentions/MentionManager.java | 15 +++++++++++++-- .../mentions/MentionReceivedComposer.java | 1 + .../outgoing/mentions/MentionsListComposer.java | 1 + 5 files changed, 45 insertions(+), 3 deletions(-) diff --git a/Database Updates/009_mentions_wordfilter.sql b/Database Updates/009_mentions_wordfilter.sql index 31383093..2d4935b6 100644 --- a/Database Updates/009_mentions_wordfilter.sql +++ b/Database Updates/009_mentions_wordfilter.sql @@ -73,4 +73,17 @@ INSERT IGNORE INTO `emulator_settings` (`key`, `value`, `comment`) VALUES ALTER TABLE `wordfilter` ADD COLUMN `prefix_only` ENUM('0','1') NOT NULL DEFAULT '0' -COMMENT 'When 1, this word only applies to custom prefixes, not to chat/motto/guild.' AFTER `mute`; \ No newline at end of file +COMMENT 'When 1, this word only applies to custom prefixes, not to chat/motto/guild.' AFTER `mute`; + +-- ---------------------------------------------------------------------------- +-- 5. Per-user mention preferences (:disablementions / :disablemassmentions) +-- +-- Read by HabboStats (default '1' = enabled), toggled by the commands. +-- Without these columns the toggle commands cannot persist. +-- ---------------------------------------------------------------------------- + +ALTER TABLE `users_settings` + ADD COLUMN IF NOT EXISTS `mentions_enabled` ENUM('0','1') NOT NULL DEFAULT '1' + COMMENT 'Receive @nick mention notifications.', + ADD COLUMN IF NOT EXISTS `mass_mentions_enabled` ENUM('0','1') NOT NULL DEFAULT '1' + COMMENT 'Receive broadcast (@all / @friends / @room) mentions.'; diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/HabboMention.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/HabboMention.java index 96ff7a4d..e075955f 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/HabboMention.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/HabboMention.java @@ -21,6 +21,7 @@ public class HabboMention { private final int mentionType; private final int timestamp; private final boolean read; + private final String senderFigure; public HabboMention(ResultSet set) throws SQLException { this.id = set.getInt("id"); @@ -33,6 +34,16 @@ public class HabboMention { this.mentionType = set.getInt("mention_type"); this.timestamp = set.getInt("timestamp"); this.read = set.getInt("read") == 1; + this.senderFigure = hasSenderFigure(set) ? set.getString("sender_figure") : ""; + } + + private static boolean hasSenderFigure(ResultSet set) { + try { + set.findColumn("sender_figure"); + return true; + } catch (SQLException e) { + return false; + } } public HabboMention(int targetUserId, int id, Habbo sender, Room room, String roomName, String message, int mentionType, int timestamp) { @@ -46,6 +57,7 @@ public class HabboMention { this.mentionType = mentionType; this.timestamp = timestamp; this.read = false; + this.senderFigure = sender.getHabboInfo().getLook(); } public int getId() { @@ -87,4 +99,8 @@ public class HabboMention { public boolean isRead() { return this.read; } + + public String getSenderFigure() { + return this.senderFigure == null ? "" : this.senderFigure; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/MentionManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/MentionManager.java index 0326da3e..7ffc61ab 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/MentionManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/MentionManager.java @@ -297,7 +297,7 @@ public class MentionManager { if (limit > 200) limit = 200; try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement( - "SELECT * FROM habbo_mentions WHERE target_user_id = ? ORDER BY id DESC LIMIT ?")) { + "SELECT habbo_mentions.*, users.look AS sender_figure FROM habbo_mentions LEFT JOIN users ON users.id = habbo_mentions.sender_user_id WHERE target_user_id = ? ORDER BY id DESC LIMIT ?")) { statement.setInt(1, userId); statement.setInt(2, limit); try (ResultSet set = statement.executeQuery()) { @@ -428,9 +428,20 @@ public class MentionManager { if (habbo != null) { return habbo; } + // Hotel-wide fallback: a @nick mention must reach the target even when + // they are online in a different room (not just the sender's room). + HabboManager habboManager = Emulator.getGameEnvironment().getHabboManager(); + habbo = habboManager.getHabbo(rawToken); + if (habbo != null) { + return habbo; + } String trimmed = trimTrailingPunctuation(rawToken); if (!trimmed.isEmpty() && !trimmed.equals(rawToken)) { - return room.getHabbo(trimmed); + habbo = room.getHabbo(trimmed); + if (habbo != null) { + return habbo; + } + return habboManager.getHabbo(trimmed); } return null; } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/mentions/MentionReceivedComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/mentions/MentionReceivedComposer.java index b7a446c7..f07ebf00 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/mentions/MentionReceivedComposer.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/mentions/MentionReceivedComposer.java @@ -18,6 +18,7 @@ public class MentionReceivedComposer extends MessageComposer { this.response.appendInt(this.mention.getId()); this.response.appendInt(this.mention.getSenderUserId()); this.response.appendString(this.mention.getSenderUsername()); + this.response.appendString(this.mention.getSenderFigure()); this.response.appendInt(this.mention.getRoomId()); this.response.appendString(this.mention.getRoomName()); this.response.appendString(this.mention.getMessage()); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/mentions/MentionsListComposer.java b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/mentions/MentionsListComposer.java index e261fe3c..4b6632e2 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/outgoing/mentions/MentionsListComposer.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/outgoing/mentions/MentionsListComposer.java @@ -23,6 +23,7 @@ public class MentionsListComposer extends MessageComposer { this.response.appendInt(mention.getId()); this.response.appendInt(mention.getSenderUserId()); this.response.appendString(mention.getSenderUsername()); + this.response.appendString(mention.getSenderFigure()); this.response.appendInt(mention.getRoomId()); this.response.appendString(mention.getRoomName()); this.response.appendString(mention.getMessage()); From 85758b53fa6567bf95ccce1832c2b09cb983407c Mon Sep 17 00:00:00 2001 From: duckietm Date: Thu, 4 Jun 2026 10:43:05 +0200 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=86=99=20Updates=20Mention?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../habbohotel/mentions/MentionManager.java | 47 ++++++++++++------- README.md | 2 +- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/MentionManager.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/MentionManager.java index 7ffc61ab..5cf08b53 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/MentionManager.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/mentions/MentionManager.java @@ -9,18 +9,8 @@ import com.eu.habbo.habbohotel.users.HabboManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.sql.*; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class MentionManager { @@ -43,7 +33,6 @@ public class MentionManager { return Emulator.getConfig().getInt("mentions.enabled", 1) == 1; } - /** Broadcast category resolved from a mention alias. */ public enum BroadcastScope { NONE, ROOM, @@ -249,7 +238,9 @@ public class MentionManager { } private boolean acceptsMention(Habbo recipient, boolean isBroadcast) { - if (recipient == null || recipient.getHabboStats() == null) return true; + if (recipient == null) return false; + if (recipient.getClient() == null) return false; + if (recipient.getHabboStats() == null) return false; if (!recipient.getHabboStats().mentionsEnabled()) return false; if (isBroadcast && !recipient.getHabboStats().massMentionsEnabled()) return false; return true; @@ -423,13 +414,37 @@ public class MentionManager { return value.substring(0, max); } + private boolean isBotOrPetName(Room room, String token) { + if (room == null || token == null || token.isEmpty()) return false; + + List bots = room.getBots(token); + if (bots != null && !bots.isEmpty()) return true; + + if (room.getUnitManager() != null && room.getUnitManager().getPets() != null) { + for (com.eu.habbo.habbohotel.pets.Pet pet : room.getUnitManager().getPets()) { + if (pet != null && pet.getName() != null && pet.getName().equalsIgnoreCase(token)) { + return true; + } + } + } + + return false; + } + private Habbo resolveHabbo(Room room, String rawToken) { + if (isBotOrPetName(room, rawToken)) { + return null; + } + String trimmedForBotCheck = trimTrailingPunctuation(rawToken); + if (!trimmedForBotCheck.equals(rawToken) && isBotOrPetName(room, trimmedForBotCheck)) { + return null; + } + Habbo habbo = room.getHabbo(rawToken); if (habbo != null) { return habbo; } - // Hotel-wide fallback: a @nick mention must reach the target even when - // they are online in a different room (not just the sender's room). + HabboManager habboManager = Emulator.getGameEnvironment().getHabboManager(); habbo = habboManager.getHabbo(rawToken); if (habbo != null) { diff --git a/README.md b/README.md index 4b44ac8b..a08df0a7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ and is developed for free by talented developers and is compatible with the foll [Latest compiled version](https://github.com/duckietm/Arcturus-Morningstar-Extended/tree/main/Latest_Compiled_Version) ## Connection ## -Use the Websocket plugin! +Use the BUILD-IN Websocket so do NOT load any websocket plugin! ### How do I connect to my emulator using Secure Websockets (wss)? You have several options to add WSS support to your websocket server.