The prize editor could only update existing rows; savePrize was UPDATE-only,
so the admin panel had no way to add a new slice or remove an old one.
- WheelManager.savePrize now takes a sortOrder and inserts when id <= 0
(returning the generated id) or updates + re-enables when id > 0, so a
previously removed prize can be brought back. sort_order is persisted to
match the editor's display order.
- New WheelManager.disablePrizesNotIn(keptIds) soft-deletes (enabled = 0)
any prize absent from the saved authoritative list. Non-destructive: rows
stay in the table and loadPrizes already filters enabled = 1.
- WheelAdminSavePrizesEvent collects the saved ids and disables the rest
before reloading.
No schema change (wheel_prizes already has enabled + sort_order) and no
packet change (id = 0 / omission express insert / delete on the existing
wire). Pairs with the Nitro-V3 client editor add/remove buttons.
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-<id> style. No need to enumerate each one.
The Nitro client already sends a strong machine fingerprint (Thumbmark,
"IID-<hash>") 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.
- RCON: add updatewheel/updatesoundboard (reload WheelManager/SoundboardManager live) so the CMS admin pages apply changes without an emulator restart.
- SSO ticket is no longer single-use: loadHabbo, session-resume and performFullDisconnect no longer clear auth_ticket. Behind Cloudflare the WS is dropped and the client retries with the same ticket; clearing it caused 'non-existing SSO token' and the 'refresh twice' / kicked-on-reconnect symptoms. The ticket now lives until its TTL (auth_ticket_expires_at), is overwritten by the CMS on the next /client load, or cleared on logout.
- SessionResume: restoreSsoTicket only restores when auth_ticket is empty (don't clobber a fresh CMS ticket); GameClient.dispose only parks/disconnects when the habbo is still attached to this client (a fast reconnect may have re-attached it to the new connection).
CatalogManager.loadFurnitureValues() (rare-values feature) iterates every catalog page during GameEnvironment.load(); for a RoomBundleLayout this calls getRoomManager().loadRoom(), but RoomManager is constructed after CatalogManager so getRoomManager() returns null -> NullPointerException -> boot aborts. Null-guard the room load so the bundle resolves lazily at runtime instead.
InteractionRoomAds now carries a `scale` default value (100) alongside
imageUrl/clickUrl/offsetX/Y/Z, so the image zoom set in the client's
position editor is stored and broadcast like the other branding fields.
The :about / :info hotel-info title was hardcoded ("Arcturus Morningstar
4.1.0") and drifted from the real build. Now Emulator.version reads the
jar manifest's Implementation-Version (= ${project.version}, added via the
assembly plugin) and falls back to MAJOR.MINOR.BUILD only outside a jar.
Title becomes "Arcturus Morningstar Extended <version>" (e.g. 4.2.24).
-- VERY IMPORTANT !!!!
-- First check if the items_base ID and catalog_items ID is not in use !
-- After the SQL please go to the catalog_items table and change the page_id to where your BOTS are located
Server side of the soundboard feature:
- rooms.soundboard_enabled flag + soundboard_sounds table (self-bootstraps
at boot via SoundboardManager; migration 021 seeds up-front)
- SoundboardManager loads enabled sounds and persists the per-room flag
- SoundboardPlayEvent broadcasts the pressed pad to everyone in the room
- SoundboardSetEnabledEvent (owner/staff) toggles the room flag and
pushes refreshed settings
- settings (flag + sound list) sent on room enter, alongside YouTube
`HousekeepingResetUserPasswordEvent` was writing a SHA-256 hex digest
into `users.password`, but the Nitro auth path
(`SessionEndpoints` / `AccountChangeEndpoints` → `AuthHttpUtil.checkPassword`)
only does `BCrypt.checkpw`. A SHA-256 hex string doesn't start with
`$2…$`, so jbcrypt throws `IllegalArgumentException`, `checkPassword`
returns false, and operators saw "credenziali invalide" on every
account whose password had been reset from the in-client panel.
Switch to `BCrypt.hashpw(plain, BCrypt.gensalt(10))` — same idiom
already used by `SessionEndpoints.java:351` and
`AccountChangeEndpoints.java:98`. Cost 10 (vs 12 there) is fine for a
server-generated 12-char random password: gensalt(10) keeps the
operator-facing reset snappy and the output is identical-shape
(`$2a$…`) to what jbcrypt 0.4 already accepts.
Side-effects:
- drops the `MessageDigest` / `NoSuchAlgorithmException` /
`StandardCharsets` imports and the local `sha256Hex` helper
- repurposes the existing `housekeeping.error.hash_failed` key for
`BCrypt.gensalt`'s only failure mode (invalid cost / log_rounds out
of range) so the client error surface is unchanged
- updates the file javadoc to stop telling future readers to "swap the
MessageDigest constant" — Arcturus itself only verifies BCrypt
Companion of duckietm/Nitro-V3#157 (`feat/housekeeping-panel`). The
client/UI is untouched — packet 9200, the action-result reveal card,
the copy button, and the plaintext flow through `message` are all
unchanged.
Closes out the HK panel server-side surface.
* Incoming 9127 HousekeepingSendHotelAlertEvent — broadcast a
StaffAlertWithLinkComposer to every online user that hasn't
set blockStaffAlerts. Composed once, fanned out by reference;
empty-message guard returns `housekeeping.error.alert_empty`.
* Outgoing 9206 HousekeepingDashboardComposer + Incoming 9128
HousekeepingGetDashboardEvent — single round trip with the
aggregated counters: online / total users + active / total
rooms + pending support tickets + sanctions in the last 24h +
approximate emulator uptime + a version string. Active-rooms
is derived from RoomManager.getActiveRooms().getUserCount()>0
to avoid counting idle preloaded rooms. Peak online today /
all-time aren't tracked yet, so they currently echo the live
online count as a best-effort placeholder.
* Outgoing 9207 HousekeepingActionLogComposer + Incoming 9129
HousekeepingListActionLogEvent — read the optional
housekeeping_log table. If the table isn't there the SQL
exception is swallowed and an empty list goes back, so the
panel renders a no-entries view rather than crashing. Schema
is documented in the handler's javadoc; operators who want
audit run a single CREATE TABLE then the HK panel populates
from new writes (writes are a follow-up — every HK handler
will eventually append a row).
`mvn package` clean — the final fat jar lands in
Latest_Compiled_Version/ after the build finishes.
* Incoming 9117 HousekeepingGiveCreditsEvent — Habbo.giveCredits for
online (ships UserCreditsComposer) or UPDATE users.credits for offline.
* Incoming 9118 HousekeepingGiveCurrencyEvent — generic across the
non-credits currencies. currencyType 0 => duckets/pixels (givePixels),
5 => diamonds (givePoints(5,n)), anything else routes through
givePoints(type,n). Offline path INSERT ... ON DUPLICATE KEY UPDATE
users_currency.
* Incoming 9119 HousekeepingGrantItemEvent — batch-INSERT N rows into
the items table with item_id = base furni id. Capped at 100 per call
so a typo can't bury the DB. Online inventory refresh deferred — the
user picks the new items up on next hand-inventory open or relog.
* Incoming 9120 HousekeepingSetHcSubscriptionEvent — extends
users_settings.club_expire_timestamp by `days*86400`. Stacks on top
of the existing expiry if it's still in the future, otherwise starts
from now. days==0 clamps to now (effective cancel).
All four reuse HousekeepingActionResultComposer (no new outgoing
composer this slice).
`mvn compile` clean.
Eight new incoming handlers + two new outgoing composers cover the
full rooms-domain HK panel.
* Outgoing 9202 HousekeepingRoomDetailComposer — single room with a
leading `found` boolean. Writes the IHousekeepingRoom shape via a
static `appendRoomFields` that HousekeepingRoomListComposer shares.
* Outgoing 9203 HousekeepingRoomListComposer — `count` then N rooms.
Used for both find-by-name (exact match, up to 50) and the prefix
autocomplete dropdown (up to 8).
* Incoming 9110 HousekeepingFindRoomByIdEvent — loadRoom(id, false)
covers both the in-memory cache and the offline `SELECT * FROM rooms`
path. No `loadData` so HK doesn't pull furni/bots/pets just to
render a summary.
* Incoming 9111 HousekeepingSearchRoomsEvent — (query, exactMatch,
limit). Branches between `name = ?` and `name LIKE ?` so the same
wire packet serves both the autocomplete and the exact-find flows.
Hard-capped to 50.
* Incoming 9112 HousekeepingRoomStateEvent — (roomId, open). Toggles
Room.setState(OPEN | LOCKED) and persists via Room.save(). One
packet covers both the open and close API endpoints.
* Incoming 9113 HousekeepingMuteRoomEvent — (roomId, minutes). Room.
setMuted is a boolean, so minutes==0 unmutes and minutes>0 mutes.
A scheduled auto-unmute is left for a future slice; the wire field
is reserved.
* Incoming 9114 HousekeepingKickAllFromRoomEvent — Room.ejectAll().
* Incoming 9115 HousekeepingTransferRoomOwnershipEvent — UPDATEs both
rooms.owner_id and rooms.owner_name so the navigator cached name
doesn't go stale. Validates the new owner exists via
HabboManager.getHabboInfo before touching the row.
* Incoming 9116 HousekeepingDeleteRoomEvent — ejectAll + dispose +
uncacheRoom + DELETE FROM rooms, mirroring the minimum-viable
subset of RequestDeleteRoomEvent. Pets/guild/custom-layout cleanup
is skipped on this slice (orphans don't crash the emulator).
`mvn compile` clean.