🆕 Brand new Floorplan

This commit is contained in:
duckietm
2026-05-26 16:35:58 +02:00
parent 4ef4ed1a96
commit b9658d0407
2 changed files with 167 additions and 128 deletions
@@ -89,6 +89,7 @@ public class HabboStats implements Runnable {
public long lastTradeTimestamp = Emulator.getIntUnixTimestamp();
public long lastGiftTimestamp = Emulator.getIntUnixTimestamp();
public long lastPurchaseTimestamp = Emulator.getIntUnixTimestamp();
public long lastFloorplanSaveTimestamp = 0;
public int uiFlags;
public boolean hasGottenDefaultSavedSearches;
private HabboInfo habboInfo;
@@ -11,16 +11,24 @@ import com.eu.habbo.messages.outgoing.generic.alerts.BubbleAlertKeys;
import com.eu.habbo.messages.outgoing.generic.alerts.GenericAlertComposer;
import com.eu.habbo.messages.outgoing.rooms.ForwardToRoomComposer;
import gnu.trove.set.hash.THashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.StringJoiner;
import java.util.regex.Pattern;
public class FloorPlanEditorSaveEvent extends MessageHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(FloorPlanEditorSaveEvent.class);
public static int MAXIMUM_FLOORPLAN_WIDTH_LENGTH = 64;
public static int MAXIMUM_FLOORPLAN_SIZE = 64 * 64;
private static final int SAVE_COOLDOWN_SECONDS = 3;
private static final Pattern ALLOWED_MAP_CHARS = Pattern.compile("[a-qA-Q0-9xX\r]+");
@Override
public int getRatelimit() {
return 500;
@@ -38,48 +46,63 @@ public class FloorPlanEditorSaveEvent extends MessageHandler {
if (room == null)
return;
if (room.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || this.client.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER)) {
if (!(room.getOwnerId() == this.client.getHabbo().getHabboInfo().getId() || this.client.getHabbo().hasPermission(Permission.ACC_ANYROOMOWNER))) {
return;
}
long now = Emulator.getIntUnixTimestamp();
if (now - this.client.getHabbo().getHabboStats().lastFloorplanSaveTimestamp < SAVE_COOLDOWN_SECONDS) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FLOORPLAN_EDITOR_ERROR.key, "Please wait a few seconds before saving again."));
return;
}
StringJoiner errors = new StringJoiner("<br />");
String map = this.packet.readString();
if (map == null || map.length() > MAXIMUM_FLOORPLAN_SIZE) {
LOGGER.warn("Floorplan save rejected (oversize): user={} room={} mapLen={}",
this.client.getHabbo().getHabboInfo().getId(), room.getId(), map == null ? 0 : map.length());
return;
}
if (!ALLOWED_MAP_CHARS.matcher(map).matches()) {
LOGGER.warn("Floorplan save rejected (illegal chars): user={} room={}",
this.client.getHabbo().getHabboInfo().getId(), room.getId());
return;
}
map = map.replace("X", "x");
String[] mapRows = map.split("\r");
if (mapRows.length == 0 || mapRows.length > MAXIMUM_FLOORPLAN_WIDTH_LENGTH) {
return;
}
int firstRowSize = mapRows[0].length();
if (Emulator.getConfig().getBoolean("hotel.room.floorplan.check.enabled")) {
if (!map.matches("[a-zA-Z0-9\r]+")) errors.add("${notification.floorplan_editor.error.title}");
Arrays.stream(mapRows)
.filter(line -> line.length() != firstRowSize)
.findAny()
.ifPresent(s -> errors.add("(General): Line " + (Arrays.asList(mapRows).indexOf(s) + 1) + " is of different length than line 1"));
if (map.isEmpty() || map.replace("x", "").replace("\r", "").isEmpty()) {
errors.add("${notification.floorplan_editor.error.message.effective_height_is_0}");
}
if (map.length() > MAXIMUM_FLOORPLAN_SIZE) {
errors.add("${notification.floorplan_editor.error.message.too_large_area}");
}
if (mapRows.length > MAXIMUM_FLOORPLAN_WIDTH_LENGTH) errors.add("${notification.floorplan_editor.error.message.too_large_height}");
else if (Arrays.stream(mapRows).anyMatch(l -> l.length() > MAXIMUM_FLOORPLAN_WIDTH_LENGTH || l.isEmpty())) errors.add("${notification.floorplan_editor.error.message.too_large_width}");
if (errors.length() > 0) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FLOORPLAN_EDITOR_ERROR.key, errors.toString()));
if (firstRowSize == 0 || firstRowSize > MAXIMUM_FLOORPLAN_WIDTH_LENGTH) {
return;
}
for (String row : mapRows) {
if (row.length() != firstRowSize) {
return;
}
}
if (Emulator.getConfig().getBoolean("hotel.room.floorplan.check.enabled")) {
if (map.replace("x", "").replace("\r", "").isEmpty()) {
errors.add("${notification.floorplan_editor.error.message.effective_height_is_0}");
}
}
int doorX = this.packet.readInt();
int doorY = this.packet.readInt();
if (doorX < 0 || doorX > firstRowSize || doorY < 0 || doorY >= mapRows.length) {
if (doorX < 0 || doorX >= firstRowSize || doorY < 0 || doorY >= mapRows.length) {
errors.add("${notification.floorplan_editor.error.message.entry_tile_outside_map}");
}
if (doorY < mapRows.length && doorX < mapRows[doorY].length() && mapRows[doorY].charAt(doorX) == 'x') {
} else if (mapRows[doorY].charAt(doorX) == 'x') {
errors.add("${notification.floorplan_editor.error.message.entry_not_on_tile}");
}
@@ -105,6 +128,11 @@ public class FloorPlanEditorSaveEvent extends MessageHandler {
errors.add("${notification.floorplan_editor.error.message.invalid_walls_fixed_height}");
}
if (errors.length() > 0) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FLOORPLAN_EDITOR_ERROR.key, errors.toString()));
return;
}
THashSet<RoomTile> locked_tileList = room.getLockedTiles();
THashSet<RoomTile> new_tileList = new THashSet<>();
blockingRoomItemScan:
@@ -119,14 +147,22 @@ public class FloorPlanEditorSaveEvent extends MessageHandler {
if (square.equalsIgnoreCase("x") && room.getTopItemAt(x, y) != null) {
errors.add("${notification.floorplan_editor.error.message.change_blocked_by_room_item}");
break blockingRoomItemScan;
} else {
}
try {
if (square.isEmpty()) {
height = 0;
} else if (Emulator.isNumeric(square)) {
height = Short.parseShort(square);
} else {
height = (short) (10 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(square.toUpperCase()));
int idx = "abcdefghijklmnopq".indexOf(square.toLowerCase());
if (idx < 0) {
return;
}
height = (short) (10 + idx);
}
} catch (NumberFormatException e) {
return;
}
if (tile != null && tile.state != RoomTileState.INVALID && height != tile.z && room.getTopItemAt(x, y) != null) {
@@ -141,8 +177,6 @@ public class FloorPlanEditorSaveEvent extends MessageHandler {
errors.add("${notification.floorplan_editor.error.message.change_blocked_by_room_item}");
}
if (errors.length() > 0) {
this.client.sendResponse(new BubbleAlertComposer(BubbleAlertKeys.FLOORPLAN_EDITOR_ERROR.key, errors.toString()));
return;
@@ -177,6 +211,11 @@ public class FloorPlanEditorSaveEvent extends MessageHandler {
room.setFloorSize(floorSize);
room.setWallHeight(wallHeight);
room.save();
this.client.getHabbo().getHabboStats().lastFloorplanSaveTimestamp = now;
LOGGER.info("Floorplan saved: user={} room={} mapLen={} rows={} cols={}",
this.client.getHabbo().getHabboInfo().getId(), room.getId(), map.length(), mapRows.length, firstRowSize);
Collection<Habbo> habbos = new ArrayList<>(room.getUserCount());
habbos.addAll(room.getHabbos());
Emulator.getGameEnvironment().getRoomManager().unloadRoom(room);
@@ -188,4 +227,3 @@ public class FloorPlanEditorSaveEvent extends MessageHandler {
}
}
}
}