From 7624d3fbc3bd8b3bee3b4f3869d40260952ef52e Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Tue, 2 Jun 2026 14:44:08 +0200 Subject: [PATCH] feat(mentions): server-side delete packet + robust direct-nick resolution --- .../habbohotel/mentions/MentionManager.java | 60 +++++++++++++++---- .../com/eu/habbo/messages/PacketManager.java | 1 + .../eu/habbo/messages/incoming/Incoming.java | 1 + .../incoming/mentions/DeleteMentionEvent.java | 14 +++++ 4 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/messages/incoming/mentions/DeleteMentionEvent.java 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 904474b5..b95c7be4 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 @@ -65,26 +65,24 @@ public class MentionManager { Set aliases = this.roomAliases(); boolean roomBroadcast = false; - LinkedHashSet directNicks = new LinkedHashSet<>(); + LinkedHashSet directTokens = new LinkedHashSet<>(); for (String token : message.split("\\s+")) { if (token.length() < 2 || token.charAt(0) != '@') { continue; } - String nick = token.substring(1).replaceAll("[^A-Za-z0-9_]", "").toLowerCase(); - if (nick.isEmpty()) { - continue; - } + String raw = token.substring(1); + String aliasCandidate = trimTrailingPunctuation(raw).toLowerCase(); - if (aliases.contains(nick)) { + if (!aliasCandidate.isEmpty() && aliases.contains(aliasCandidate)) { roomBroadcast = true; - } else { - directNicks.add(nick); + } else if (!raw.isEmpty()) { + directTokens.add(raw); } } - if (!roomBroadcast && directNicks.isEmpty()) { + if (!roomBroadcast && directTokens.isEmpty()) { return; } @@ -106,8 +104,8 @@ public class MentionManager { } } } else { - for (String nick : directNicks) { - Habbo habbo = room.getHabbo(nick); + for (String token : directTokens) { + Habbo habbo = this.resolveHabbo(room, token); if (habbo == null || habbo.getHabboInfo().getId() == senderId) { continue; } @@ -208,4 +206,44 @@ public class MentionManager { LOGGER.error("Failed to mark mentions as read.", e); } } + + public void delete(int userId, int mentionId) { + try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); + PreparedStatement statement = connection.prepareStatement( + "DELETE FROM habbo_mentions WHERE target_user_id = ? AND id = ?")) { + statement.setInt(1, userId); + statement.setInt(2, mentionId); + statement.executeUpdate(); + } catch (SQLException e) { + LOGGER.error("Failed to delete mention.", e); + } + } + + private static final String TRAILING_PUNCTUATION = ".,!?;:)]}\"'"; + + private static String trimTrailingPunctuation(String value) { + int end = value.length(); + while (end > 0 && TRAILING_PUNCTUATION.indexOf(value.charAt(end - 1)) >= 0) { + end--; + } + return value.substring(0, end); + } + + /** + * Resolve a present room occupant from a raw mention token. Tries the token + * verbatim first (so usernames containing allowed punctuation such as '-', + * '.', '!' still match), then falls back to a trailing-punctuation-trimmed + * form so a mention written as "@Bob!" still resolves the user "Bob". + */ + private Habbo resolveHabbo(Room room, String rawToken) { + Habbo habbo = room.getHabbo(rawToken); + if (habbo != null) { + return habbo; + } + String trimmed = trimTrailingPunctuation(rawToken); + if (!trimmed.isEmpty() && !trimmed.equals(rawToken)) { + return room.getHabbo(trimmed); + } + return null; + } } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java b/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java index 599c1ad2..1a09d3a5 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java @@ -429,6 +429,7 @@ public class PacketManager { void registerRooms() throws Exception { this.registerHandler(Incoming.RequestMentionsEvent, RequestMentionsEvent.class); this.registerHandler(Incoming.MarkMentionsReadEvent, MarkMentionsReadEvent.class); + this.registerHandler(Incoming.DeleteMentionEvent, DeleteMentionEvent.class); this.registerHandler(Incoming.RequestRoomLoadEvent, RequestRoomLoadEvent.class); this.registerHandler(Incoming.RequestHeightmapEvent, RequestRoomHeightmapEvent.class); this.registerHandler(Incoming.RequestRoomHeightmapEvent, RequestRoomHeightmapEvent.class); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java index b088492a..77f72fe3 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/Incoming.java @@ -498,4 +498,5 @@ public class Incoming { public static final int SoundboardSetEnabledEvent = 9307; public static final int RequestMentionsEvent = 4803; public static final int MarkMentionsReadEvent = 4804; + public static final int DeleteMentionEvent = 4805; } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/mentions/DeleteMentionEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/mentions/DeleteMentionEvent.java new file mode 100644 index 00000000..6be3622b --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/mentions/DeleteMentionEvent.java @@ -0,0 +1,14 @@ +package com.eu.habbo.messages.incoming.mentions; + +import com.eu.habbo.Emulator; +import com.eu.habbo.messages.incoming.MessageHandler; + +public class DeleteMentionEvent extends MessageHandler { + @Override + public void handle() throws Exception { + int userId = this.client.getHabbo().getHabboInfo().getId(); + int mentionId = this.packet.readInt(); + + Emulator.getGameEnvironment().getMentionManager().delete(userId, mentionId); + } +}