You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
Merge pull request #134 from medievalshell/Dev
feat: persist `scale` for room ads / branding furni
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
-- Fortune Wheel — admin permission grant (`acc_wheeladmin`)
|
||||
--
|
||||
-- Both the client (FortuneWheelView "Settings" button) and the server handlers
|
||||
-- (WheelAdminGetPrizesEvent / WheelAdminSavePrizesEvent, PERMISSION_KEY =
|
||||
-- "acc_wheeladmin") gate the prize editor on `acc_wheeladmin`.
|
||||
--
|
||||
-- The key itself is registered upstream in
|
||||
-- `Database Updates/008_soundboard_fortune_wheel.sql`, but that file only grants
|
||||
-- it to `rank_7` (its author's 7-rank hotel) and its ON DUPLICATE clause touches
|
||||
-- the comment only. So on a hotel with a different rank layout the key would end
|
||||
-- up granted to nobody useful. This file supplies the portable grant.
|
||||
--
|
||||
-- Idempotent + portable: registers the key as a no-op safety (in case 008 hasn't
|
||||
-- run yet) and grants it to the same ranks that hold acc_ads_background — the
|
||||
-- adjacent "manage room-ad furni" permission — with dynamic SQL over the per-rank
|
||||
-- columns, so no rank ids are hardcoded. If acc_ads_background is absent the JOIN
|
||||
-- matches nothing and the key stays ungranted (safe; the button just stays
|
||||
-- hidden until granted by hand).
|
||||
--
|
||||
-- After applying, reload at runtime with `:update_permissions` (rebinds online
|
||||
-- users to the fresh rank and rebroadcasts the permission map — no relog) or
|
||||
-- restart the emulator.
|
||||
|
||||
-- 1) Safety no-op registration (008 is the canonical registrar). rank_* default 0.
|
||||
INSERT IGNORE INTO `permission_definitions` (`permission_key`, `max_value`, `comment`)
|
||||
VALUES (
|
||||
'acc_wheeladmin',
|
||||
1,
|
||||
'Allows opening the fortune wheel prize editor (FortuneWheelSettingsView) to add/edit prize slices. Gated server-side by the same key.'
|
||||
);
|
||||
|
||||
-- 2) Grant to the same ranks as acc_ads_background, portably.
|
||||
SET @cols := NULL;
|
||||
|
||||
SELECT GROUP_CONCAT(CONCAT('dst.`', `column_name`, '` = src.`', `column_name`, '`') SEPARATOR ', ')
|
||||
INTO @cols
|
||||
FROM `information_schema`.`columns`
|
||||
WHERE `table_schema` = DATABASE()
|
||||
AND `table_name` = 'permission_definitions'
|
||||
AND `column_name` REGEXP '^rank_[0-9]+$';
|
||||
|
||||
SET @sql := CONCAT(
|
||||
'UPDATE `permission_definitions` dst ',
|
||||
'JOIN `permission_definitions` src ON src.`permission_key` = ''acc_ads_background'' ',
|
||||
'SET ', @cols, ' ',
|
||||
'WHERE dst.`permission_key` = ''acc_wheeladmin'''
|
||||
);
|
||||
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.eu.habbo</groupId>
|
||||
<artifactId>Habbo</artifactId>
|
||||
<version>4.2.24</version>
|
||||
<version>4.2.25</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
+7
-3
@@ -42,14 +42,18 @@ public class RoomBundleLayout extends SingleBundle {
|
||||
}
|
||||
|
||||
if (this.room == null) {
|
||||
if (this.roomId > 0) {
|
||||
this.room = Emulator.getGameEnvironment().getRoomManager().loadRoom(this.roomId);
|
||||
RoomManager roomManager = Emulator.getGameEnvironment().getRoomManager();
|
||||
if (this.roomId > 0 && roomManager != null) {
|
||||
this.room = roomManager.loadRoom(this.roomId);
|
||||
|
||||
if (this.room != null)
|
||||
this.room.preventUnloading = true;
|
||||
} else {
|
||||
} else if (this.roomId <= 0) {
|
||||
LOGGER.error("No room id specified for room bundle {}({})", this.getPageName(), this.getId());
|
||||
}
|
||||
// roomManager can be null when CatalogManager.loadFurnitureValues() runs
|
||||
// during GameEnvironment.load() before RoomManager is constructed; in that
|
||||
// case skip eager room loading — the bundle resolves lazily at runtime.
|
||||
}
|
||||
|
||||
if (this.room == null) {
|
||||
|
||||
@@ -153,7 +153,13 @@ public class GameClient {
|
||||
this.channel.close();
|
||||
|
||||
if (this.habbo != null) {
|
||||
if (this.habbo.isOnline()) {
|
||||
// Agisci sull'Habbo SOLO se è ancora attaccato a QUESTO client. Su un
|
||||
// reconnect veloce (drop Cloudflare → il client riconnette) l'Habbo può
|
||||
// essere già stato riassegnato alla NUOVA connessione (session resume):
|
||||
// in quel caso questo dispose della vecchia connessione NON deve
|
||||
// parcheggiarlo né disconnetterlo, altrimenti ucciderebbe la sessione
|
||||
// appena ripristinata (era la causa del "Bye"/kick al 2° reconnect).
|
||||
if (this.habbo.getClient() == this && this.habbo.isOnline()) {
|
||||
// Try to park the habbo in the grace period instead of immediate disconnect
|
||||
boolean parked = SessionResumeManager.getInstance().parkHabbo(this.habbo, this.ssoTicket);
|
||||
|
||||
|
||||
+20
-4
@@ -118,16 +118,32 @@ public class SessionResumeManager {
|
||||
LOGGER.error("[SessionResume] Error during deferred disconnect", e);
|
||||
}
|
||||
|
||||
clearSsoTicket(habbo.getHabboInfo().getId());
|
||||
// NON svuotare il ticket SSO qui. Dietro Cloudflare la pagina si ricarica
|
||||
// lentamente (~15s) e la grace (5s) scade prima che la nuova connessione
|
||||
// arrivi: svuotando il ticket si cancellava quello NUOVO appena scritto dal
|
||||
// CMS per il refresh → "non-existing SSO token" → bisognava refreshare 2 volte.
|
||||
// Il ticket vive col suo TTL (auth_ticket_expires_at) e viene sovrascritto dal
|
||||
// CMS al prossimo /client o azzerato al logout.
|
||||
}
|
||||
|
||||
private void restoreSsoTicket(int userId, String ssoTicket) {
|
||||
// Restore the old ticket ONLY if no fresh ticket has been written in the
|
||||
// meantime. On a hard-refresh the CMS writes a NEW auth_ticket for the same
|
||||
// user before this parking restore runs; without the guard we'd clobber it
|
||||
// with the old ticket, so the new connection's SSO wouldn't be found and the
|
||||
// client would get "session expired" on the first attempt. The guard means:
|
||||
// normal reconnect (ticket cleared to '' after login) -> restore; hard-refresh
|
||||
// (CMS already wrote a new ticket) -> leave the new ticket untouched.
|
||||
try (var connection = Emulator.getDatabase().getDataSource().getConnection();
|
||||
var statement = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) {
|
||||
var statement = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? AND (auth_ticket = '' OR auth_ticket IS NULL) LIMIT 1")) {
|
||||
statement.setString(1, ssoTicket);
|
||||
statement.setInt(2, userId);
|
||||
statement.execute();
|
||||
LOGGER.info("[SessionResume] Restored SSO ticket for user {} during grace period", userId);
|
||||
int updated = statement.executeUpdate();
|
||||
if (updated > 0) {
|
||||
LOGGER.info("[SessionResume] Restored SSO ticket for user {} during grace period", userId);
|
||||
} else {
|
||||
LOGGER.info("[SessionResume] Skipped SSO restore for user {} — a newer ticket is already present (likely a fresh login/hard-refresh)", userId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("[SessionResume] Failed to restore SSO ticket for user " + userId, e);
|
||||
}
|
||||
|
||||
+4
@@ -29,6 +29,10 @@ public class InteractionRoomAds extends InteractionCustomValues {
|
||||
{
|
||||
this.put("offsetZ", "0");
|
||||
}
|
||||
|
||||
{
|
||||
this.put("scale", "100");
|
||||
}
|
||||
};
|
||||
|
||||
public InteractionRoomAds(ResultSet set, Item baseItem) throws SQLException {
|
||||
|
||||
@@ -132,15 +132,12 @@ public class HabboManager {
|
||||
Emulator.getPluginManager().fireEvent(new UserRegisteredEvent(habbo));
|
||||
}
|
||||
|
||||
if (!Emulator.debugging) {
|
||||
try (PreparedStatement stmt = connection.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) {
|
||||
stmt.setString(1, "");
|
||||
stmt.setInt(2, habbo.getHabboInfo().getId());
|
||||
stmt.execute();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Caught SQL exception", e);
|
||||
}
|
||||
}
|
||||
// NB: il ticket SSO NON viene svuotato qui di proposito. Dietro
|
||||
// Cloudflare il WebSocket viene droppato e il client ritenta più
|
||||
// volte con lo STESSO ticket: se lo consumassimo al primo uso, i
|
||||
// retry (e l'hard-refresh) fallirebbero con "non-existing SSO token".
|
||||
// Il ticket resta valido fino alla scadenza (auth_ticket_expires_at,
|
||||
// TTL gestito dal CMS) o finché il CMS non ne scrive uno nuovo / logout.
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
|
||||
+4
-11
@@ -133,17 +133,10 @@ public class SecureLoginEvent extends MessageHandler {
|
||||
this.client.setHabbo(habbo);
|
||||
this.client.setMachineId(habbo.getHabboInfo().getMachineID());
|
||||
|
||||
// Clear the SSO ticket now that session is resumed (prevent reuse)
|
||||
if (!Emulator.debugging) {
|
||||
try (java.sql.Connection conn = Emulator.getDatabase().getDataSource().getConnection();
|
||||
java.sql.PreparedStatement stmt = conn.prepareStatement("UPDATE users SET auth_ticket = ? WHERE id = ? LIMIT 1")) {
|
||||
stmt.setString(1, "");
|
||||
stmt.setInt(2, habbo.getHabboInfo().getId());
|
||||
stmt.execute();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to clear SSO ticket after session resume", e);
|
||||
}
|
||||
}
|
||||
// NB: NON svuotiamo il ticket SSO qui (vedi HabboManager.loadHabbo):
|
||||
// dietro Cloudflare il client ritenta la connessione con lo stesso
|
||||
// ticket, quindi deve restare valido fino alla scadenza TTL. Consumarlo
|
||||
// farebbe fallire i retry / l'hard-refresh con "non-existing SSO token".
|
||||
} else {
|
||||
// Normal login — load from database
|
||||
habbo = Emulator.getGameEnvironment().getHabboManager().loadHabbo(sso);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.eu.habbo.messages.rcon;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
// Ricarica i suoni della Soundboard dal DB (live), così i suoni aggiunti/caricati
|
||||
// dal CMS (/admin/soundboard) si applicano senza riavviare l'emulatore.
|
||||
public class UpdateSoundboard extends RCONMessage<UpdateSoundboard.SoundboardJSON> {
|
||||
|
||||
public UpdateSoundboard() {
|
||||
super(SoundboardJSON.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Gson gson, SoundboardJSON object) {
|
||||
Emulator.getGameEnvironment().getSoundboardManager().reload();
|
||||
}
|
||||
|
||||
static class SoundboardJSON {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.eu.habbo.messages.rcon;
|
||||
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
// Ricarica i premi/settings della Ruota della Fortuna dal DB (live), così le
|
||||
// modifiche fatte dal CMS (/admin/wheel) si applicano senza riavviare l'emulatore.
|
||||
public class UpdateWheel extends RCONMessage<UpdateWheel.WheelJSON> {
|
||||
|
||||
public UpdateWheel() {
|
||||
super(WheelJSON.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Gson gson, WheelJSON object) {
|
||||
Emulator.getGameEnvironment().getWheelManager().reload();
|
||||
}
|
||||
|
||||
static class WheelJSON {
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,8 @@ public class RCONServer extends Server {
|
||||
this.addRCONMessage("sendroombundle", SendRoomBundle.class);
|
||||
this.addRCONMessage("setrank", SetRank.class);
|
||||
this.addRCONMessage("updatewordfilter", UpdateWordfilter.class);
|
||||
this.addRCONMessage("updatewheel", UpdateWheel.class);
|
||||
this.addRCONMessage("updatesoundboard", UpdateSoundboard.class);
|
||||
this.addRCONMessage("updatecatalog", UpdateCatalog.class);
|
||||
this.addRCONMessage("executecommand", ExecuteCommand.class);
|
||||
this.addRCONMessage("progressachievement", ProgressAchievement.class);
|
||||
|
||||
Reference in New Issue
Block a user