From bf1a29a6e8bc7b9fcc3b1948e60b1f64b0bf1bd2 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Sat, 30 May 2026 05:53:48 +0000
Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=86=99=20Bump=20version=20to=204.2.26?=
=?UTF-8?q?=20[skip=20ci]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Emulator/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Emulator/pom.xml b/Emulator/pom.xml
index 88b0e6a3..17628061 100644
--- a/Emulator/pom.xml
+++ b/Emulator/pom.xml
@@ -6,7 +6,7 @@
com.eu.habbo
Habbo
- 4.2.25
+ 4.2.26
UTF-8
From da63439d53706f5902793519a754501d3d1dad78 Mon Sep 17 00:00:00 2001
From: medievalshell
Date: Sun, 31 May 2026 00:03:39 +0200
Subject: [PATCH 2/3] fix(bans): persist client machine fingerprint so
machine/super bans work
The Nitro client already sends a strong machine fingerprint (Thumbmark,
"IID-") via the UniqueID packet (header 2490 -> MachineIDEvent), but
the emulator only stored it on the GameClient and never copied it onto the
Habbo's HabboInfo, so it was never written to users.machine_id. As a result
machine/super bans (which read users.machine_id) matched nobody.
- MachineIDEvent: when the fingerprint arrives and the Habbo is already
loaded, copy it onto HabboInfo and persist (run the Habbo save).
- SecureLoginEvent: if the fingerprint arrived before login, copy it onto
HabboInfo right before the login save.
This makes machine/super bans effective without changing the client.
---
.../messages/incoming/handshake/MachineIDEvent.java | 10 ++++++++++
.../messages/incoming/handshake/SecureLoginEvent.java | 6 ++++++
2 files changed, 16 insertions(+)
diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/MachineIDEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/MachineIDEvent.java
index 477fc3c6..64696bf7 100644
--- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/MachineIDEvent.java
+++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/MachineIDEvent.java
@@ -1,5 +1,6 @@
package com.eu.habbo.messages.incoming.handshake;
+import com.eu.habbo.Emulator;
import com.eu.habbo.messages.NoAuthMessage;
import com.eu.habbo.messages.incoming.MessageHandler;
import org.slf4j.Logger;
@@ -24,6 +25,15 @@ public class MachineIDEvent extends MessageHandler {
this.client.setMachineId(storedMachineId);
+ // Persist the machine fingerprint onto the user so machine/super bans can
+ // target it (createOfflineUserBan copies users.machine_id). The Nitro client
+ // sends this UniqueID packet right after the SSO ticket, so the Habbo is
+ // normally already loaded by the time we get here.
+ if (!storedMachineId.isEmpty() && this.client.getHabbo() != null && this.client.getHabbo().getHabboInfo() != null) {
+ this.client.getHabbo().getHabboInfo().setMachineID(storedMachineId);
+ Emulator.getThreading().run(this.client.getHabbo());
+ }
+
LOGGER.debug("Setting client MachineId to {}", storedMachineId);
}
}
\ No newline at end of file
diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java
index 7c7dcf3c..cdebe27d 100644
--- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java
+++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/handshake/SecureLoginEvent.java
@@ -161,6 +161,12 @@ public class SecureLoginEvent extends MessageHandler {
throw new NullPointerException(habbo.getHabboInfo().getUsername() + " has a NON EXISTING RANK!");
}
+ // If the machine fingerprint already arrived (UniqueID before login),
+ // persist it so machine/super bans can target this user.
+ if (this.client.getMachineId() != null && !this.client.getMachineId().isEmpty()) {
+ this.client.getHabbo().getHabboInfo().setMachineID(this.client.getMachineId());
+ }
+
Emulator.getThreading().run(habbo);
Emulator.getGameEnvironment().getHabboManager().addHabbo(habbo);
} catch (Exception e) {
From 02ab30180c49d05eefabb469c567ea733b247369 Mon Sep 17 00:00:00 2001
From: medievalshell
Date: Sun, 31 May 2026 03:39:23 +0200
Subject: [PATCH 3/3] fix(chat): relay unknown chat bubble ids instead of
resetting to default
getBubble() fell back to NORMAL (bubble 0) for any id not in the registered
BUBBLES map, so custom client-side chat bubbles (e.g. ids 253+) rendered as
the default bubble for everyone. Now unknown positive ids (<=1000) pass
through as a transient bubble carrying that id, so the server relays it and
clients render their own .bubble- style. No need to enumerate each one.
---
.../habbohotel/rooms/RoomChatMessageBubbles.java | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomChatMessageBubbles.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomChatMessageBubbles.java
index d38133e4..fef00f33 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomChatMessageBubbles.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/RoomChatMessageBubbles.java
@@ -134,7 +134,18 @@ public class RoomChatMessageBubbles {
}
public static RoomChatMessageBubbles getBubble(int id) {
- return BUBBLES.getOrDefault(id, NORMAL);
+ RoomChatMessageBubbles bubble = BUBBLES.get(id);
+ if (bubble != null) return bubble;
+
+ // Custom chat bubbles (client-side only, e.g. ids 253+) are not registered
+ // above. Instead of falling back to NORMAL (which made them render as the
+ // default bubble), pass the id through so the server relays it as-is and
+ // the client renders its own .bubble- style. Capped to avoid abuse.
+ if (id > 0 && id <= 1000) {
+ return new RoomChatMessageBubbles(id, "CUSTOM_" + id, "", true, false);
+ }
+
+ return NORMAL;
}
private static void registerBubble(RoomChatMessageBubbles bubble) {