From 1416cd746483157aff940bda2f3ff57bedd688e9 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 6 Jun 2026 02:21:21 +0200 Subject: [PATCH] =?UTF-8?q?feat(furnieditor):=20FurniEditorRevertFurnidata?= =?UTF-8?q?Event=20=E2=80=94=20restore=20last=20furnidata=20backup=20+=20r?= =?UTF-8?q?ebroadcast?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/eu/habbo/messages/PacketManager.java | 1 + .../FurniEditorRevertFurnidataEvent.java | 118 ++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 Emulator/src/main/java/com/eu/habbo/messages/incoming/furnieditor/FurniEditorRevertFurnidataEvent.java 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 c9bbfc09..ac1455ce 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/PacketManager.java @@ -286,6 +286,7 @@ public class PacketManager { this.registerHandler(Incoming.FurniEditorUpdateEvent, FurniEditorUpdateEvent.class); this.registerHandler(Incoming.FurniEditorDeleteEvent, FurniEditorDeleteEvent.class); this.registerHandler(Incoming.FurniEditorUpdateFurnidataEvent, FurniEditorUpdateFurnidataEvent.class); + this.registerHandler(Incoming.FurniEditorRevertFurnidataEvent, FurniEditorRevertFurnidataEvent.class); // Catalog Admin this.registerHandler(Incoming.CatalogAdminSavePageEvent, CatalogAdminSavePageEvent.class); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/furnieditor/FurniEditorRevertFurnidataEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/furnieditor/FurniEditorRevertFurnidataEvent.java new file mode 100644 index 00000000..02e535be --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/furnieditor/FurniEditorRevertFurnidataEvent.java @@ -0,0 +1,118 @@ +package com.eu.habbo.messages.incoming.furnieditor; + +import com.eu.habbo.Emulator; +import com.eu.habbo.habbohotel.items.FurnidataEntry; +import com.eu.habbo.habbohotel.items.FurnidataLock; +import com.eu.habbo.habbohotel.items.FurnidataWriter; +import com.eu.habbo.habbohotel.items.FurnitureTextProvider; +import com.eu.habbo.habbohotel.permissions.Permission; +import com.eu.habbo.habbohotel.users.Habbo; +import com.eu.habbo.messages.incoming.MessageHandler; +import com.eu.habbo.messages.outgoing.furniture.FurnitureDataReloadComposer; +import com.eu.habbo.messages.outgoing.furnieditor.FurniEditorResultComposer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Incoming handler 10048 — admin reverts a furni's furnidata to the last rotating backup. + * + * Flow: permission check → read item_id → resolve classname → under FurnidataLock: + * FurnidataWriter.revertLastBackup → FurnitureTextProvider.reindexFromSource → + * broadcast FurnitureDataReloadComposer (10047) → audit log → respond. + */ +public class FurniEditorRevertFurnidataEvent extends MessageHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(FurniEditorRevertFurnidataEvent.class); + + @Override + public void handle() throws Exception { + Habbo habbo = this.client.getHabbo(); + + // 1. Permission check + if (!habbo.hasPermission(Permission.ACC_CATALOGFURNI)) { + this.client.sendResponse(new FurniEditorResultComposer(false, "No permission")); + return; + } + + // 2. Read packet + int itemId = this.packet.readInt(); + + if (itemId <= 0) { + this.client.sendResponse(new FurniEditorResultComposer(false, "Invalid item ID")); + return; + } + + // 3. Resolve classname from item_id (reuse static helper from update handler) + String classname = FurniEditorUpdateFurnidataEvent.classnameForItem(itemId); + String classnameForLog = (classname != null) ? classname : "?"; + + // 4. Verify provider is configured + FurnitureTextProvider provider = + Emulator.getGameEnvironment().getFurnitureTextProvider(); + + if (provider == null || provider.getSource() == null) { + this.client.sendResponse(new FurniEditorResultComposer(false, "Furnidata source not configured")); + return; + } + + int adminId = habbo.getHabboInfo().getId(); + + // 5. Revert + reindex + broadcast under the shared lock + boolean reverted; + List delta; + + FurnidataLock.LOCK.lock(); + try { + FurnidataWriter writer = new FurnidataWriter( + provider.getSource(), + provider.isSourceDirectory(), + provider.getMaxBytes(), + 3 /* backupKeep */ + ); + reverted = writer.revertLastBackup(); + if (!reverted) { + this.client.sendResponse(new FurniEditorResultComposer(false, "No backup found to revert")); + return; + } + + delta = provider.reindexFromSource(); + + if (!delta.isEmpty()) { + int deltaCap = Integer.parseInt( + Emulator.getConfig().getValue("items.furnidata.delta.cap", "500")); + FurnitureDataReloadComposer composer = (delta.size() > deltaCap) + ? new FurnitureDataReloadComposer(FurnitureDataReloadComposer.MODE_RELOAD_HINT, List.of()) + : new FurnitureDataReloadComposer(FurnitureDataReloadComposer.MODE_DELTA, delta); + broadcastToAll(composer); + } + } finally { + FurnidataLock.LOCK.unlock(); + } + + // 6. Audit log (outside lock — DB write, not latency-sensitive) + FurnidataAuditLog.record( + adminId, + classnameForLog, + "REVERT_FURNIDATA", + "", // previous state unknown at this point + "", + "", + "" + ); + + // 7. Respond success + this.client.sendResponse(new FurniEditorResultComposer(true, "Furnidata reverted", itemId)); + LOGGER.info("FurniEditorRevertFurnidataEvent: admin {} reverted furnidata for classname '{}' (item {})", + adminId, classnameForLog, itemId); + } + + private static void broadcastToAll(FurnitureDataReloadComposer composer) { + for (Habbo habbo : Emulator.getGameEnvironment().getHabboManager().getOnlineHabbos().values()) { + if (habbo.getClient() != null) { + habbo.getClient().sendResponse(composer); + } + } + } +}