Commit Graph

376 Commits

Author SHA1 Message Date
simoleo89 9d98fbf9ee feat(wheel): support adding & removing fortune-wheel prizes from the editor
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.
2026-05-31 10:49:10 +02:00
medievalshell 02ab30180c 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-<id> style. No need to enumerate each one.
2026-05-31 03:39:23 +02:00
medievalshell da63439d53 fix(bans): persist client machine fingerprint so machine/super bans work
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.
2026-05-31 00:04:00 +02:00
github-actions[bot] bf1a29a6e8 🆙 Bump version to 4.2.26 [skip ci] 2026-05-30 05:53:48 +00:00
DuckieTM a7f207bb76 Merge pull request #134 from medievalshell/Dev
feat: persist `scale` for room ads / branding furni
2026-05-30 07:13:59 +02:00
duckietm b7915884b6 🆙 Update Rare-Value page 2026-05-29 08:28:01 +02:00
medievalshell 478f7bdba0 feat/fix: RCON wheel+soundboard reload, robust SSO reconnect behind Cloudflare
- 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).
2026-05-29 04:45:34 +02:00
medievalshell c255f1e1b4 fix: guard RoomBundleLayout against null RoomManager during catalog init
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.
2026-05-29 00:45:02 +02:00
Medievalshell 08d1ae97a7 Merge branch 'duckietm:main' into Dev 2026-05-28 22:16:17 +02:00
github-actions[bot] f8fe1e3e22 🆙 Bump version to 4.2.25 [skip ci] 2026-05-28 14:37:58 +00:00
duckietm 1ba2e43d4d 🆙 Wheel updates 2026-05-28 16:36:22 +02:00
medievalshell 8dd5155562 feat: persist scale for room ads / branding furni
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.
2026-05-28 15:30:33 +02:00
DuckieTM 4f4f581371 Merge pull request #129 from medievalshell/Dev
feat: rare values + fortune wheel + in-client prize editor + feat: soundboard (room-scoped custom audio pads) + feat: version string tied to project version + "Extended" title
2026-05-28 13:50:52 +02:00
duckietm 9705b3e42a 🆕 Added the option turn in menu for BOT 2026-05-28 13:00:02 +02:00
medievalshell e626a7fc50 feat: version string tied to project version + "Extended" title
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).
2026-05-28 12:33:50 +02:00
Medievalshell d6ebb632e6 Merge branch 'duckietm:main' into Dev 2026-05-28 12:11:27 +02:00
github-actions[bot] 014ca9ca48 🆙 Bump version to 4.2.24 [skip ci] 2026-05-28 09:50:45 +00:00
github-actions[bot] 1d6e05ee57 🆙 Bump version to 4.2.23 [skip ci] 2026-05-28 09:35:48 +00:00
github-actions[bot] e772686c4b 🆙 Bump version to 4.2.22 [skip ci] 2026-05-28 09:05:33 +00:00
DuckieTM a00f7b01f5 Merge pull request #130 from duckietm/dev
Dev
2026-05-28 11:04:35 +02:00
duckietm 9ea7acf05c 🆙 Update for Frank 2026-05-28 10:53:50 +02:00
duckietm bab43af41e 🆕 Frank the BOT
-- 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
2026-05-28 10:41:25 +02:00
medievalshell 10a2b2b872 feat: soundboard (room-scoped custom audio pads)
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
2026-05-28 09:03:27 +02:00
medievalshell 458b37dbed feat: rare values + fortune wheel + in-client prize editor
Catalog-derived rare value map (diamond-priced), fortune wheel (WheelManager,
weighted RNG, lazy daily reset, rewards, recent wins) + admin prize editor
gated on acc_supporttool. Packets 9300-9305 / 9400-9404. Migration 020.
2026-05-28 02:39:01 +02:00
github-actions[bot] 55b38e7b85 🆙 Bump version to 4.2.21 [skip ci] 2026-05-27 13:39:01 +00:00
DuckieTM 4a96c5baaf Merge pull request #128 from duckietm/dev
Dev
2026-05-27 15:37:57 +02:00
duckietm 539c5b5b96 🆙 Fix BOTS in catalog and inventory 2026-05-27 13:46:17 +02:00
duckietm 7b7154e68f 🆙 Fix search and buy #1 2026-05-27 11:34:55 +02:00
github-actions[bot] 691dc42627 🆙 Bump version to 4.2.20 [skip ci] 2026-05-27 07:43:14 +00:00
DuckieTM 226873c1fb Merge pull request #127 from duckietm/dev
Dev
2026-05-27 09:42:21 +02:00
duckietm e213609609 🆕 Added Pickup furni to the floorplan 2026-05-27 09:37:49 +02:00
github-actions[bot] ccadb81970 🆙 Bump version to 4.2.19 [skip ci] 2026-05-26 15:16:04 +00:00
DuckieTM 0a3a940946 Merge pull request #125 from duckietm/dev
🆙 Small fix floorplan
2026-05-26 17:15:07 +02:00
duckietm 4613fbe80c 🆙 Small fix floorplan 2026-05-26 17:14:49 +02:00
github-actions[bot] 9328f4a355 🆙 Bump version to 4.2.18 [skip ci] 2026-05-26 14:37:17 +00:00
DuckieTM da8b947ddf Merge pull request #124 from duckietm/dev
🆕 Brand new Floorplan
2026-05-26 16:36:17 +02:00
duckietm b9658d0407 🆕 Brand new Floorplan 2026-05-26 16:35:58 +02:00
DuckieTM 68d3731393 Merge pull request #123 from duckietm/dev
Dev
2026-05-26 12:53:07 +02:00
DuckieTM c20e273a2c Merge pull request #120 from simoleo89/feat/housekeeping-packets
feat(housekeeping): in-client admin packet handlers
2026-05-26 10:44:13 +02:00
github-actions[bot] 83d418e712 🆙 Bump version to 4.2.17 [skip ci] 2026-05-26 08:04:04 +00:00
DuckieTM 38d05e8a06 Merge pull request #122 from duckietm/dev
Dev
2026-05-26 10:03:05 +02:00
duckietm c83a3bad8e 🆙 Gift Updates 2026-05-26 09:58:22 +02:00
DuckieTM 0bd23ad244 Merge pull request #121 from Lorenzune/merge-duckie-main-2026-05-06
Improve emulator monitoring and wired stability safeguards
2026-05-25 18:41:50 +02:00
Lorenzune d8f74b2477 Improve emulator monitoring and wired stability safeguards 2026-05-25 10:10:57 +02:00
simoleo89 dac09e92d1 fix(housekeeping): hash reset password with BCrypt, not SHA-256
`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.
2026-05-24 22:25:16 +02:00
simoleo89 fbf979419e feat(housekeeping): hotel alert + dashboard + audit log
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.
2026-05-24 16:31:01 +02:00
simoleo89 6126c35779 feat(housekeeping): economy domain — credits/currency/items/hc
* 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.
2026-05-24 16:29:55 +02:00
simoleo89 a1749c9eda feat(housekeeping): rooms domain — find/search + open/close/mute/kick-all/transfer/delete
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.
2026-05-24 16:29:55 +02:00
simoleo89 525c124fa5 feat(housekeeping): set-rank + trade-lock + reset-password
Closes out the users-domain HK actions.

* Incoming 9107 HousekeepingSetUserRankEvent — (userId, rankId).
  Validates the rank exists in `permission_ranks`, UPDATEs users.rank,
  and if the target is online rebinds their HabboInfo to the fresh
  Rank object and ships a UserPermissionsComposer so server-side
  hasPermission() and the client's useHasPermission(key) consumers
  re-render against the new permissions without a relog.

* Incoming 9108 HousekeepingTradeLockUserEvent — (userId, hours,
  reason). Writes `users_settings.trade_locked_until = now + hours*3600`
  so the lock survives logout/login. Online targets also get their
  in-memory HabboStats.allowTrade cleared and an optional alert.

* Incoming 9109 HousekeepingResetUserPasswordEvent — (userId).
  Generates a 12-char alphanumeric (SecureRandom over a curated
  ambiguity-free alphabet), writes its SHA-256 hex to users.password
  (the column is varchar(64) — already sized for SHA-256 hex) and
  blanks auth_ticket so any live SSO ticket can't bypass the reset.
  Plaintext is returned to the operator in the action-result
  message — they relay it out-of-band. If your CMS uses a hash other
  than SHA-256, swap the MessageDigest.getInstance constant.

`mvn compile` clean.
2026-05-24 16:29:55 +02:00
simoleo89 57087a31f2 fix(housekeeping): emit localizable error keys instead of bare slugs
Every HK action handler returned bare error slugs (\"invalid_input\",
\"user_offline\", \"no_active_ban\", \"target_unkickable\", \"ban_failed\",
\"user_not_found\") in HousekeepingActionResultComposer.message. The
client's `localizeOrPassthrough` only treats a value as a translation
key when it contains a dot, so those bare slugs were rendered raw in
the status banner and the toast — ugly and untranslatable.

Re-prefix all error messages with `housekeeping.error.` so the EN +
IT dictionaries can resolve them. Success path is unchanged (server
sends empty string, client falls back to `housekeeping.action.success`).

Companion dictionary entries land on the client side.
2026-05-24 16:29:55 +02:00