Merge pull request #14 from duckietm/Dev

Dev
This commit is contained in:
DuckieTM
2026-03-18 07:35:22 +01:00
committed by GitHub
34 changed files with 567 additions and 870 deletions
@@ -0,0 +1 @@
ALTER TABLE `rooms` ADD COLUMN `allow_underpass` ENUM('0','1') NOT NULL DEFAULT '0' AFTER `move_diagonally`;
@@ -7,7 +7,6 @@ import com.eu.habbo.core.*;
import com.eu.habbo.core.consolecommands.ConsoleCommand; import com.eu.habbo.core.consolecommands.ConsoleCommand;
import com.eu.habbo.database.Database; import com.eu.habbo.database.Database;
import com.eu.habbo.habbohotel.GameEnvironment; import com.eu.habbo.habbohotel.GameEnvironment;
import com.eu.habbo.networking.camera.CameraClient;
import com.eu.habbo.networking.gameserver.GameServer; import com.eu.habbo.networking.gameserver.GameServer;
import com.eu.habbo.networking.rconserver.RCONServer; import com.eu.habbo.networking.rconserver.RCONServer;
import com.eu.habbo.plugin.PluginManager; import com.eu.habbo.plugin.PluginManager;
@@ -64,7 +63,6 @@ public final class Emulator {
private static TextsManager texts; private static TextsManager texts;
private static GameServer gameServer; private static GameServer gameServer;
private static RCONServer rconServer; private static RCONServer rconServer;
private static CameraClient cameraClient;
private static Logging logging; private static Logging logging;
private static Database database; private static Database database;
private static DatabaseLogger databaseLogger; private static DatabaseLogger databaseLogger;
@@ -132,6 +130,26 @@ public final class Emulator {
Emulator.pluginManager.reload(); Emulator.pluginManager.reload();
Emulator.getPluginManager().fireEvent(new EmulatorConfigUpdatedEvent()); Emulator.getPluginManager().fireEvent(new EmulatorConfigUpdatedEvent());
Emulator.texts = new TextsManager(); Emulator.texts = new TextsManager();
Emulator.config.register("camera.url", "http://localhost/camera/");
Emulator.config.register("imager.location.output.camera", "/public/camera/");
Emulator.config.register("imager.location.output.thumbnail", "/public/camera/thumbnails/");
Emulator.config.register("camera.price.points.publish", "1");
Emulator.config.register("camera.price.points.publish.type", "5");
Emulator.config.register("camera.publish.delay", "180");
Emulator.config.register("camera.price.credits", "2");
Emulator.config.register("camera.price.points", "0");
Emulator.config.register("camera.price.points.type", "5");
Emulator.config.register("camera.render.delay", "5");
Emulator.texts.register("camera.permission", "You don't have permission to use the camera!");
Emulator.texts.register("camera.wait", "Please wait %seconds% seconds before making another picture.");
Emulator.texts.register("camera.error.creation", "Failed to create your picture. *sadpanda*");
File thumbnailDir = new File(Emulator.config.getValue("imager.location.output.thumbnail"));
if (!thumbnailDir.exists()) {
thumbnailDir.mkdirs();
}
new CleanerThread(); new CleanerThread();
Emulator.gameServer = new GameServer(getConfig().getValue("game.host", "127.0.0.1"), getConfig().getInt("game.port", 30000)); Emulator.gameServer = new GameServer(getConfig().getValue("game.host", "127.0.0.1"), getConfig().getInt("game.port", 30000));
Emulator.rconServer = new RCONServer(getConfig().getValue("rcon.host", "127.0.0.1"), getConfig().getInt("rcon.port", 30001)); Emulator.rconServer = new RCONServer(getConfig().getValue("rcon.host", "127.0.0.1"), getConfig().getInt("rcon.port", 30001));
@@ -231,7 +249,6 @@ public final class Emulator {
if (Emulator.pluginManager != null) if (Emulator.pluginManager != null)
tryShutdown(() -> Emulator.pluginManager.fireEvent(new EmulatorStartShutdownEvent())); tryShutdown(() -> Emulator.pluginManager.fireEvent(new EmulatorStartShutdownEvent()));
if (Emulator.cameraClient != null) tryShutdown(() -> Emulator.cameraClient.disconnect());
if (Emulator.rconServer != null) tryShutdown(() -> Emulator.rconServer.stop()); if (Emulator.rconServer != null) tryShutdown(() -> Emulator.rconServer.stop());
if (Emulator.gameEnvironment != null) tryShutdown(() -> Emulator.gameEnvironment.dispose()); if (Emulator.gameEnvironment != null) tryShutdown(() -> Emulator.gameEnvironment.dispose());
if (Emulator.pluginManager != null) if (Emulator.pluginManager != null)
@@ -318,14 +335,6 @@ public final class Emulator {
return badgeImager; return badgeImager;
} }
public static CameraClient getCameraClient() {
return cameraClient;
}
public static synchronized void setCameraClient(CameraClient client) {
cameraClient = client;
}
public static int getTimeStarted() { public static int getTimeStarted() {
return timeStarted; return timeStarted;
} }
@@ -20,7 +20,6 @@ public abstract class ConsoleCommand {
addCommand(new ConsoleShutdownCommand()); addCommand(new ConsoleShutdownCommand());
addCommand(new ConsoleInfoCommand()); addCommand(new ConsoleInfoCommand());
addCommand(new ConsoleTestCommand()); addCommand(new ConsoleTestCommand());
addCommand(new ConsoleReconnectCameraCommand());
addCommand(new ShowInteractionsCommand()); addCommand(new ShowInteractionsCommand());
addCommand(new ShowRCONCommands()); addCommand(new ShowRCONCommands());
addCommand(new ThankyouArcturusCommand()); addCommand(new ThankyouArcturusCommand());
@@ -1,19 +0,0 @@
package com.eu.habbo.core.consolecommands;
import com.eu.habbo.networking.camera.CameraClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConsoleReconnectCameraCommand extends ConsoleCommand {
private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleReconnectCameraCommand.class);
public ConsoleReconnectCameraCommand() {
super("camera", "Attempt to reconnect to the camera server.");
}
@Override
public void handle(String[] args) throws Exception {
LOGGER.info("Connecting to the camera...");
CameraClient.attemptReconnect = true;
}
}
@@ -187,7 +187,6 @@ public class CommandHandler {
addCommand(new ChangeNameCommand()); addCommand(new ChangeNameCommand());
addCommand(new ChatTypeCommand()); addCommand(new ChatTypeCommand());
addCommand(new CommandsCommand()); addCommand(new CommandsCommand());
addCommand(new ConnectCameraCommand());
addCommand(new ControlCommand()); addCommand(new ControlCommand());
addCommand(new CoordsCommand()); addCommand(new CoordsCommand());
addCommand(new CreditsCommand()); addCommand(new CreditsCommand());
@@ -1,15 +0,0 @@
package com.eu.habbo.habbohotel.commands;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.gameclients.GameClient;
public class ConnectCameraCommand extends Command {
public ConnectCameraCommand() {
super("cmd_connect_camera", Emulator.getTexts().getValue("commands.keys.cmd_connect_camera").split(";"));
}
@Override
public boolean handle(GameClient gameClient, String[] params) throws Exception {
return false;
}
}
@@ -156,6 +156,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
private volatile boolean promoted; private volatile boolean promoted;
private volatile int tradeMode; private volatile int tradeMode;
private volatile boolean moveDiagonally; private volatile boolean moveDiagonally;
private volatile boolean allowUnderpass;
private volatile boolean jukeboxActive; private volatile boolean jukeboxActive;
private volatile boolean hideWired; private volatile boolean hideWired;
private RoomPromotion promotion; private RoomPromotion promotion;
@@ -239,6 +240,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
this.tradeMode = set.getInt("trade_mode"); this.tradeMode = set.getInt("trade_mode");
this.moveDiagonally = set.getString("move_diagonally").equals("1"); this.moveDiagonally = set.getString("move_diagonally").equals("1");
this.allowUnderpass = set.getString("allow_underpass").equals("1");
this.preLoaded = true; this.preLoaded = true;
this.allowBotsWalk = true; this.allowBotsWalk = true;
@@ -1077,7 +1079,7 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
if (this.needsUpdate) { if (this.needsUpdate) {
try (Connection connection = Emulator.getDatabase().getDataSource() try (Connection connection = Emulator.getDatabase().getDataSource()
.getConnection(); PreparedStatement statement = connection.prepareStatement( .getConnection(); PreparedStatement statement = connection.prepareStatement(
"UPDATE rooms SET name = ?, description = ?, password = ?, state = ?, users_max = ?, category = ?, score = ?, paper_floor = ?, paper_wall = ?, paper_landscape = ?, thickness_wall = ?, wall_height = ?, thickness_floor = ?, moodlight_data = ?, tags = ?, allow_other_pets = ?, allow_other_pets_eat = ?, allow_walkthrough = ?, allow_hidewall = ?, chat_mode = ?, chat_weight = ?, chat_speed = ?, chat_hearing_distance = ?, chat_protection =?, who_can_mute = ?, who_can_kick = ?, who_can_ban = ?, poll_id = ?, guild_id = ?, roller_speed = ?, override_model = ?, is_staff_picked = ?, promoted = ?, trade_mode = ?, move_diagonally = ?, owner_id = ?, owner_name = ?, jukebox_active = ?, hidewired = ? WHERE id = ?")) { "UPDATE rooms SET name = ?, description = ?, password = ?, state = ?, users_max = ?, category = ?, score = ?, paper_floor = ?, paper_wall = ?, paper_landscape = ?, thickness_wall = ?, wall_height = ?, thickness_floor = ?, moodlight_data = ?, tags = ?, allow_other_pets = ?, allow_other_pets_eat = ?, allow_walkthrough = ?, allow_hidewall = ?, chat_mode = ?, chat_weight = ?, chat_speed = ?, chat_hearing_distance = ?, chat_protection =?, who_can_mute = ?, who_can_kick = ?, who_can_ban = ?, poll_id = ?, guild_id = ?, roller_speed = ?, override_model = ?, is_staff_picked = ?, promoted = ?, trade_mode = ?, move_diagonally = ?, owner_id = ?, owner_name = ?, jukebox_active = ?, hidewired = ?, allow_underpass = ? WHERE id = ?")) {
statement.setString(1, this.name); statement.setString(1, this.name);
statement.setString(2, this.description); statement.setString(2, this.description);
statement.setString(3, this.password); statement.setString(3, this.password);
@@ -1126,7 +1128,8 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
statement.setString(37, this.ownerName); statement.setString(37, this.ownerName);
statement.setString(38, this.jukeboxActive ? "1" : "0"); statement.setString(38, this.jukeboxActive ? "1" : "0");
statement.setString(39, this.hideWired ? "1" : "0"); statement.setString(39, this.hideWired ? "1" : "0");
statement.setInt(40, this.id); statement.setString(40, this.allowUnderpass ? "1" : "0");
statement.setInt(41, this.id);
statement.executeUpdate(); statement.executeUpdate();
this.needsUpdate = false; this.needsUpdate = false;
} catch (SQLException e) { } catch (SQLException e) {
@@ -1408,6 +1411,14 @@ public class Room implements Comparable<Room>, ISerialize, Runnable {
this.allowWalkthrough = allowWalkthrough; this.allowWalkthrough = allowWalkthrough;
} }
public boolean isAllowUnderpass() {
return this.allowUnderpass;
}
public void setAllowUnderpass(boolean allowUnderpass) {
this.allowUnderpass = allowUnderpass;
}
public boolean isAllowBotsWalk() { public boolean isAllowBotsWalk() {
return this.allowBotsWalk; return this.allowBotsWalk;
} }
@@ -448,6 +448,48 @@ public class RoomItemManager {
return highestItem; return highestItem;
} }
/**
* Gets the top walkable item at a position, considering underpass.
* If the topmost item is elevated enough to walk under, returns the highest item at walk surface level instead.
*/
public HabboItem getWalkableItemAt(int x, int y) {
HabboItem topItem = this.getTopItemAt(x, y);
if (topItem == null) {
return null;
}
// If underpass is disabled for this room, just return the top item
if (!this.room.isAllowUnderpass()) {
return topItem;
}
// If the top item is walkable, just return it
if (topItem.isWalkable() || topItem.getBaseItem().allowWalk() || topItem.getBaseItem().allowSit() || topItem.getBaseItem().allowLay()) {
return topItem;
}
// Check for underpass: get the walk surface height
double walkSurface = this.room.getLayout() != null ? this.room.getLayout().getHeightAtSquare(x, y) : 0;
HabboItem walkSurfaceItem = null;
for (HabboItem item : this.getItemsAt(x, y)) {
if (item.isWalkable() || item.getBaseItem().allowWalk() || item.getBaseItem().allowSit() || item.getBaseItem().allowLay()) {
double itemTop = item.getZ() + Item.getCurrentHeight(item);
if (itemTop > walkSurface) {
walkSurface = itemTop;
walkSurfaceItem = item;
}
}
}
// If there's enough clearance under the top blocking item, return the walk surface item
if (topItem.getZ() - walkSurface >= RoomLayout.UNDERPASS_HEIGHT) {
return walkSurfaceItem;
}
return topItem;
}
/** /**
* Gets the top item from a set of tiles. * Gets the top item from a set of tiles.
*/ */
@@ -19,6 +19,7 @@ public class RoomLayout {
protected static final int DIAGONALMOVEMENTCOST = 14; protected static final int DIAGONALMOVEMENTCOST = 14;
public static double MAXIMUM_STEP_HEIGHT = 1.5; public static double MAXIMUM_STEP_HEIGHT = 1.5;
public static boolean ALLOW_FALLING = true; public static boolean ALLOW_FALLING = true;
public static double UNDERPASS_HEIGHT = 1.5;
public boolean CANMOVEDIAGONALY = true; public boolean CANMOVEDIAGONALY = true;
private String name; private String name;
private short doorX; private short doorX;
@@ -91,9 +91,41 @@ public class RoomTileManager {
tallestItem = item; tallestItem = item;
} }
if (this.room.isAllowUnderpass() && result == RoomTileState.BLOCKED && tallestItem != null) {
double walkSurface = this.getUnderpassWalkHeight(tile, items, exclude);
if (tallestItem.getZ() - walkSurface >= RoomLayout.UNDERPASS_HEIGHT) {
result = RoomTileState.OPEN;
}
}
return result; return result;
} }
/**
* Calculates the walk surface height for underpass checks.
* Returns the floor height or the top of the highest walkable item below any blocking items.
*/
private double getUnderpassWalkHeight(RoomTile tile, THashSet<HabboItem> items, HabboItem exclude) {
RoomLayout layout = this.room.getLayout();
double walkHeight = layout != null ? layout.getHeightAtSquare(tile.x, tile.y) : 0;
if (items != null) {
for (HabboItem item : items) {
if (exclude != null && item == exclude) {
continue;
}
if (item.isWalkable() || item.getBaseItem().allowWalk() || item.getBaseItem().allowSit() || item.getBaseItem().allowLay()) {
double itemTop = item.getZ() + Item.getCurrentHeight(item);
if (itemTop > walkHeight) {
walkHeight = itemTop;
}
}
}
}
return walkHeight;
}
/** /**
* Determines the tile state based on a specific item. * Determines the tile state based on a specific item.
*/ */
@@ -193,7 +225,22 @@ public class RoomTileManager {
HabboItem item = this.room.getItemManager().getTopItemAt(x, y, exclude); HabboItem item = this.room.getItemManager().getTopItemAt(x, y, exclude);
if (item != null) { if (item != null) {
canStack = item.getBaseItem().allowStack(); canStack = item.getBaseItem().allowStack();
height = item.getZ() + (item.getBaseItem().allowSit() ? 0 : Item.getCurrentHeight(item)); double itemTop = item.getZ() + (item.getBaseItem().allowSit() ? 0 : Item.getCurrentHeight(item));
// Underpass: if the top item is blocking but high enough to walk under, use floor height
if (this.room.isAllowUnderpass() && !item.isWalkable() && !item.getBaseItem().allowWalk() && !item.getBaseItem().allowSit() && !item.getBaseItem().allowLay()) {
RoomLayout layout2 = this.room.getLayout();
RoomTile tile = layout2 != null ? layout2.getTile(x, y) : null;
THashSet<HabboItem> allItems = tile != null ? this.room.getItemManager().getItemsAt(tile) : null;
double walkSurface = this.getUnderpassWalkHeight(tile, allItems, exclude);
if (item.getZ() - walkSurface >= RoomLayout.UNDERPASS_HEIGHT) {
height = walkSurface;
} else {
height = itemTop;
}
} else {
height = itemTop;
}
} }
if (calculateHeightmap) { if (calculateHeightmap) {
@@ -396,6 +443,14 @@ public class RoomTileManager {
} }
} }
// Underpass: if top item blocks but is high enough, allow walking under
if (this.room.isAllowUnderpass() && !canWalk && topItem != null) {
double walkSurface = this.getUnderpassWalkHeight(roomTile, items, null);
if (topItem.getZ() - walkSurface >= RoomLayout.UNDERPASS_HEIGHT) {
canWalk = true;
}
}
return canWalk; return canWalk;
} }
@@ -235,7 +235,7 @@ public class RoomUnit {
} }
} }
HabboItem item = room.getTopItemAt(next.x, next.y); HabboItem item = room.getItemManager().getWalkableItemAt(next.x, next.y);
boolean canSitNextTile = room.canSitAt(next.x, next.y); boolean canSitNextTile = room.canSitAt(next.x, next.y);
boolean canLayNextTile = room.canLayAt(next.x, next.y); boolean canLayNextTile = room.canLayAt(next.x, next.y);
@@ -2,6 +2,7 @@ package com.eu.habbo.messages.incoming.camera;
import com.eu.habbo.Emulator; import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboInfo;
import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.camera.CameraPublishWaitMessageComposer; import com.eu.habbo.messages.outgoing.camera.CameraPublishWaitMessageComposer;
import com.eu.habbo.messages.outgoing.catalog.NotEnoughPointsTypeComposer; import com.eu.habbo.messages.outgoing.catalog.NotEnoughPointsTypeComposer;
@@ -16,49 +17,54 @@ import java.sql.SQLException;
public class CameraPublishToWebEvent extends MessageHandler { public class CameraPublishToWebEvent extends MessageHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CameraPublishToWebEvent.class); private static final Logger LOGGER = LoggerFactory.getLogger(CameraPublishToWebEvent.class);
public static int CAMERA_PUBLISH_POINTS = 5; public static int CAMERA_PUBLISH_POINTS = 1;
public static int CAMERA_PUBLISH_POINTS_TYPE = 0; public static int CAMERA_PUBLISH_POINTS_TYPE = 5;
public static int CAMERA_PUBLISH_DELAY = 180;
@Override @Override
public void handle() throws Exception { public void handle() {
Habbo habbo = this.client.getHabbo(); Habbo habbo = this.client.getHabbo();
if (habbo == null) return; if (habbo == null) return;
if (habbo.getHabboInfo().getPhotoTimestamp() == 0) return;
if (habbo.getHabboInfo().getPhotoJSON().isEmpty()) return;
if (!habbo.getHabboInfo().getPhotoJSON().contains(habbo.getHabboInfo().getPhotoTimestamp() + "")) return;
if (habbo.getHabboInfo().getCurrencyAmount(CameraPublishToWebEvent.CAMERA_PUBLISH_POINTS_TYPE) < CameraPublishToWebEvent.CAMERA_PUBLISH_POINTS) { HabboInfo habboInfo = habbo.getHabboInfo();
this.client.sendResponse(new NotEnoughPointsTypeComposer(false, true, CameraPublishToWebEvent.CAMERA_PUBLISH_POINTS));
int points = habboInfo.getCurrencyAmount(CAMERA_PUBLISH_POINTS_TYPE);
if (points < CAMERA_PUBLISH_POINTS) {
String currencyName = Emulator.getTexts().getValue("seasonal.name." + CAMERA_PUBLISH_POINTS_TYPE, "currency");
habbo.alert("You don't have enough " + currencyName + "!");
this.client.sendResponse(new NotEnoughPointsTypeComposer(false, true, CAMERA_PUBLISH_POINTS_TYPE));
return; return;
} }
int timestamp = Emulator.getIntUnixTimestamp(); int photoTimestamp = habboInfo.getPhotoTimestamp();
String photoJSON = habboInfo.getPhotoJSON();
if (photoTimestamp == 0 || photoJSON.isEmpty() || !photoJSON.contains(Integer.toString(photoTimestamp)))
return;
boolean isOk = false; int currentTimestamp = Emulator.getIntUnixTimestamp();
int cooldownLeft = Math.max(0, Emulator.getConfig().getInt("camera.publish.delay") - (timestamp - this.client.getHabbo().getHabboInfo().getWebPublishTimestamp())); int timeSinceLastPublish = currentTimestamp - habboInfo.getWebPublishTimestamp();
if (cooldownLeft == 0) { if (timeSinceLastPublish < CAMERA_PUBLISH_DELAY) {
UserPublishPictureEvent publishPictureEvent = new UserPublishPictureEvent(this.client.getHabbo(), this.client.getHabbo().getHabboInfo().getPhotoURL(), timestamp, this.client.getHabbo().getHabboInfo().getPhotoRoomId()); int wait = CAMERA_PUBLISH_DELAY - timeSinceLastPublish;
this.client.sendResponse(new CameraPublishWaitMessageComposer(false, wait, habboInfo.getPhotoURL()));
} else {
UserPublishPictureEvent publishPictureEvent = new UserPublishPictureEvent(habbo, habboInfo.getPhotoURL(), currentTimestamp, habboInfo.getPhotoRoomId());
if (!Emulator.getPluginManager().fireEvent(publishPictureEvent).isCancelled()) { if (!Emulator.getPluginManager().fireEvent(publishPictureEvent).isCancelled()) {
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO camera_web (user_id, room_id, timestamp, url) VALUES (?, ?, ?, ?)")) { try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
statement.setInt(1, this.client.getHabbo().getHabboInfo().getId()); PreparedStatement statement = connection.prepareStatement("INSERT INTO camera_web (user_id, room_id, timestamp, url) VALUES (?, ?, ?, ?)")) {
statement.setInt(1, habboInfo.getId());
statement.setInt(2, publishPictureEvent.roomId); statement.setInt(2, publishPictureEvent.roomId);
statement.setInt(3, publishPictureEvent.timestamp); statement.setInt(3, publishPictureEvent.timestamp);
statement.setString(4, publishPictureEvent.URL); statement.setString(4, publishPictureEvent.URL);
statement.execute(); statement.execute();
habboInfo.setWebPublishTimestamp(currentTimestamp);
this.client.getHabbo().getHabboInfo().setWebPublishTimestamp(timestamp); habbo.givePoints(CAMERA_PUBLISH_POINTS_TYPE, -CAMERA_PUBLISH_POINTS);
this.client.getHabbo().givePoints(CameraPublishToWebEvent.CAMERA_PUBLISH_POINTS_TYPE, -CameraPublishToWebEvent.CAMERA_PUBLISH_POINTS);
isOk = true;
} catch (SQLException e) { } catch (SQLException e) {
LOGGER.error("Caught SQL exception", e); LOGGER.error("Caught SQL exception", e);
} }
} }
this.client.sendResponse(new CameraPublishWaitMessageComposer(true, 0, ""));
} }
this.client.sendResponse(new CameraPublishWaitMessageComposer(isOk, cooldownLeft, isOk ? this.client.getHabbo().getHabboInfo().getPhotoURL() : ""));
} }
} }
@@ -2,6 +2,9 @@ package com.eu.habbo.messages.incoming.camera;
import com.eu.habbo.Emulator; import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.achievements.AchievementManager; import com.eu.habbo.habbohotel.achievements.AchievementManager;
import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboInfo;
import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.habbohotel.users.HabboItem;
import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.camera.CameraPurchaseSuccesfullComposer; import com.eu.habbo.messages.outgoing.camera.CameraPurchaseSuccesfullComposer;
@@ -11,47 +14,53 @@ import com.eu.habbo.messages.outgoing.inventory.InventoryRefreshComposer;
import com.eu.habbo.plugin.events.users.UserPurchasePictureEvent; import com.eu.habbo.plugin.events.users.UserPurchasePictureEvent;
public class CameraPurchaseEvent extends MessageHandler { public class CameraPurchaseEvent extends MessageHandler {
public static int CAMERA_PURCHASE_CREDITS = 5; public static int CAMERA_PURCHASE_CREDITS = 2;
public static int CAMERA_PURCHASE_POINTS = 5; public static int CAMERA_PURCHASE_POINTS = 0;
public static int CAMERA_PURCHASE_POINTS_TYPE = 0; public static int CAMERA_PURCHASE_POINTS_TYPE = 5;
@Override @Override
public void handle() throws Exception { public void handle() {
if (this.client.getHabbo().getHabboInfo().getCredits() < CameraPurchaseEvent.CAMERA_PURCHASE_CREDITS) { Habbo habbo = this.client.getHabbo();
HabboInfo habboInfo = habbo.getHabboInfo();
if (habboInfo.getCredits() < CAMERA_PURCHASE_CREDITS) {
habbo.alert("You don't have enough credits!");
this.client.sendResponse(new NotEnoughPointsTypeComposer(true, false, 0)); this.client.sendResponse(new NotEnoughPointsTypeComposer(true, false, 0));
return; return;
} }
if (this.client.getHabbo().getHabboInfo().getCurrencyAmount(CameraPurchaseEvent.CAMERA_PURCHASE_POINTS_TYPE) < CameraPurchaseEvent.CAMERA_PURCHASE_POINTS) { if (habboInfo.getCurrencyAmount(CAMERA_PURCHASE_POINTS_TYPE) < CAMERA_PURCHASE_POINTS) {
this.client.sendResponse(new NotEnoughPointsTypeComposer(false, true, CameraPurchaseEvent.CAMERA_PURCHASE_POINTS_TYPE)); String alertMessage = "You don't have enough " + Emulator.getTexts().getValue("seasonal.name." + CAMERA_PURCHASE_POINTS_TYPE, "currency") + "!";
habbo.alert(alertMessage);
this.client.sendResponse(new NotEnoughPointsTypeComposer(false, true, CAMERA_PURCHASE_POINTS_TYPE));
return; return;
} }
if (this.client.getHabbo().getHabboInfo().getPhotoTimestamp() == 0) return; if (habboInfo.getPhotoTimestamp() == 0 || habboInfo.getPhotoJSON().isEmpty()
if (this.client.getHabbo().getHabboInfo().getPhotoJSON().isEmpty()) return; || !habboInfo.getPhotoJSON().contains(Integer.toString(habboInfo.getPhotoTimestamp())))
if (!this.client.getHabbo().getHabboInfo().getPhotoJSON().contains(this.client.getHabbo().getHabboInfo().getPhotoTimestamp() + ""))
return; return;
if (Emulator.getPluginManager().fireEvent(new UserPurchasePictureEvent(this.client.getHabbo(), this.client.getHabbo().getHabboInfo().getPhotoURL(), this.client.getHabbo().getHabboInfo().getCurrentRoom().getId(), this.client.getHabbo().getHabboInfo().getPhotoTimestamp())).isCancelled()) { if (Emulator.getPluginManager().fireEvent(new UserPurchasePictureEvent(habbo, habboInfo.getPhotoURL(), habboInfo.getCurrentRoom().getId(), habboInfo.getPhotoTimestamp())).isCancelled())
return; return;
}
HabboItem photoItem = Emulator.getGameEnvironment().getItemManager().createItem(this.client.getHabbo().getHabboInfo().getId(), Emulator.getGameEnvironment().getItemManager().getItem(Emulator.getConfig().getInt("camera.item_id")), 0, 0, this.client.getHabbo().getHabboInfo().getPhotoJSON()); Item item = Emulator.getGameEnvironment().getItemManager().getItem(Emulator.getConfig().getInt("camera.item_id"));
if (item == null || !item.getInteractionType().getName().equals("external_image"))
return;
HabboItem photoItem = Emulator.getGameEnvironment().getItemManager().createItem(habboInfo.getId(), item, 0, 0, habboInfo.getPhotoJSON());
if (photoItem != null) { if (photoItem != null) {
photoItem.setExtradata(photoItem.getExtradata().replace("%id%", photoItem.getId() + "")); photoItem.setExtradata(photoItem.getExtradata().replace("%id%", Integer.toString(photoItem.getId())));
photoItem.needsUpdate(true); photoItem.needsUpdate(true);
habbo.getInventory().getItemsComponent().addItem(photoItem);
this.client.getHabbo().getInventory().getItemsComponent().addItem(photoItem);
this.client.sendResponse(new CameraPurchaseSuccesfullComposer()); this.client.sendResponse(new CameraPurchaseSuccesfullComposer());
this.client.sendResponse(new AddHabboItemComposer(photoItem)); this.client.sendResponse(new AddHabboItemComposer(photoItem));
this.client.sendResponse(new InventoryRefreshComposer()); this.client.sendResponse(new InventoryRefreshComposer());
this.client.getHabbo().giveCredits(-CameraPurchaseEvent.CAMERA_PURCHASE_CREDITS); habbo.giveCredits(-CAMERA_PURCHASE_CREDITS);
this.client.getHabbo().givePoints(CameraPurchaseEvent.CAMERA_PURCHASE_POINTS_TYPE, -CameraPurchaseEvent.CAMERA_PURCHASE_POINTS); habbo.givePoints(CAMERA_PURCHASE_POINTS_TYPE, -CAMERA_PURCHASE_POINTS);
AchievementManager.progressAchievement(this.client.getHabbo(), Emulator.getGameEnvironment().getAchievementManager().getAchievement("CameraPhotoCount")); AchievementManager.progressAchievement(habbo, Emulator.getGameEnvironment().getAchievementManager().getAchievement("CameraPhotoCount"));
} }
} }
} }
@@ -1,37 +1,227 @@
package com.eu.habbo.messages.incoming.camera; package com.eu.habbo.messages.incoming.camera;
import com.eu.habbo.Emulator; import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboInfo;
import com.eu.habbo.habbohotel.users.HabboStats;
import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.networking.camera.CameraClient; import com.eu.habbo.messages.outgoing.camera.CameraURLComposer;
import com.eu.habbo.networking.camera.messages.outgoing.CameraRenderImageComposer; import io.netty.buffer.ByteBuf;
import com.eu.habbo.util.crypto.ZIP; import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
public class CameraRoomPictureEvent extends MessageHandler { public class CameraRoomPictureEvent extends MessageHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CameraRoomPictureEvent.class);
public static int CAMERA_RENDER_DELAY = 15;
public static int MAX_IMAGE_BYTES = 2 * 1024 * 1024; // 2 MB max upload
public static int MAX_IMAGE_WIDTH = 1024;
public static int MAX_IMAGE_HEIGHT = 1024;
public static int MAX_DAILY_RENDERS = 50;
private ByteBuf image = null;
@Override @Override
public void handle() throws Exception { public void handle() {
if (!this.client.getHabbo().hasPermission("acc_camera")) { try {
this.client.getHabbo().alert(Emulator.getTexts().getValue("camera.permission")); this.make();
} finally {
if (this.image != null) {
this.image.release();
}
}
}
private void make() {
Habbo habbo = this.client.getHabbo();
if (!habbo.hasPermission("acc_camera")) {
habbo.alert(Emulator.getTexts().getValue("camera.permission"));
return; return;
} }
if (CameraClient.isLoggedIn) { HabboInfo habboInfo = habbo.getHabboInfo();
this.packet.getBuffer().readFloat(); HabboStats habboStats = habbo.getHabboStats();
int timestamp = Emulator.getIntUnixTimestamp();
byte[] data = this.packet.getBuffer().readBytes(this.packet.getBuffer().readableBytes()).array(); if (habboStats.cache.containsKey("camera_render_cooldown")) {
int cameraTimestamp = (Integer) habboStats.cache.get("camera_render_cooldown");
String content = new String(ZIP.inflate(data)); if (timestamp - cameraTimestamp < CAMERA_RENDER_DELAY) {
CameraRenderImageComposer composer = new CameraRenderImageComposer(this.client.getHabbo().getHabboInfo().getId(), this.client.getHabbo().getHabboInfo().getCurrentRoom().getBackgroundTonerColor().getRGB(), 320, 320, content); String alertMessage = Emulator.getTexts().getValue("camera.wait").replace("%seconds%", Integer.toString(CAMERA_RENDER_DELAY - (timestamp - cameraTimestamp)));
this.client.getHabbo().getHabboInfo().setPhotoJSON(Emulator.getConfig().getValue("camera.extradata").replace("%timestamp%", composer.timestamp + "")); habbo.alert(alertMessage);
this.client.getHabbo().getHabboInfo().setPhotoTimestamp(composer.timestamp); if (habboInfo.getPhotoURL() != null) {
String[] splittedPhotoURL = habboInfo.getPhotoURL().split("/");
if (this.client.getHabbo().getHabboInfo().getCurrentRoom() != null) { if (splittedPhotoURL.length > 0) {
this.client.getHabbo().getHabboInfo().setPhotoRoomId(this.client.getHabbo().getHabboInfo().getCurrentRoom().getId()); this.client.sendResponse(new CameraURLComposer(splittedPhotoURL[splittedPhotoURL.length - 1]));
}
}
return;
} }
Emulator.getCameraClient().sendMessage(composer);
} else {
this.client.getHabbo().alert(Emulator.getTexts().getValue("camera.disabled"));
} }
// Daily render quota check
int dailyRenderCount = getDailyRenderCount(habboStats, timestamp);
if (dailyRenderCount >= MAX_DAILY_RENDERS) {
habbo.alert(Emulator.getTexts().getValue("camera.daily.limit", "You have reached the daily photo limit. Try again tomorrow."));
return;
}
incrementDailyRenderCount(habboStats, timestamp, dailyRenderCount);
habboStats.cache.put("camera_render_cooldown", timestamp);
Room room = habboInfo.getCurrentRoom();
if (room == null) return;
int count = this.packet.readInt();
// Reject oversized payloads before reading
if (count <= 0 || count > MAX_IMAGE_BYTES) {
LOGGER.warn("User {} attempted camera upload with invalid size: {} bytes", habboInfo.getUsername(), count);
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
return;
}
this.image = this.packet.getBuffer().readBytes(count);
if (this.image == null) return;
byte[] imageBytes = ByteBufUtil.getBytes(this.image, 0, 4, true);
if (imageBytes == null || imageBytes.length < 4 || !isPNG(imageBytes)) {
LOGGER.warn("User {} attempted camera upload with non-PNG data", habboInfo.getUsername());
return;
}
// Validate image dimensions before fully decoding
int[] dimensions;
try {
dimensions = readPNGDimensions(this.image);
} catch (IOException e) {
LOGGER.warn("User {} uploaded image with unreadable dimensions", habboInfo.getUsername());
handleImageProcessingError(habbo);
return;
}
if (dimensions == null || dimensions[0] <= 0 || dimensions[1] <= 0
|| dimensions[0] > MAX_IMAGE_WIDTH || dimensions[1] > MAX_IMAGE_HEIGHT) {
LOGGER.warn("User {} attempted camera upload with invalid dimensions: {}x{}",
habboInfo.getUsername(),
dimensions != null ? dimensions[0] : "null",
dimensions != null ? dimensions[1] : "null");
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
return;
}
BufferedImage theImage;
try (ByteBufInputStream in = new ByteBufInputStream(this.image)) {
theImage = ImageIO.read(in);
} catch (IOException e) {
handleImageProcessingError(habbo);
return;
}
if (theImage == null) {
LOGGER.warn("User {} uploaded image that could not be decoded", habboInfo.getUsername());
handleImageProcessingError(habbo);
return;
}
// Double-check decoded dimensions match expectations
if (theImage.getWidth() > MAX_IMAGE_WIDTH || theImage.getHeight() > MAX_IMAGE_HEIGHT) {
LOGGER.warn("User {} decoded image exceeds dimension limits: {}x{}", habboInfo.getUsername(), theImage.getWidth(), theImage.getHeight());
handleImageProcessingError(habbo);
return;
}
String fileName = habboInfo.getId() + "_" + timestamp;
String URL = fileName + ".png";
String URLsmall = fileName + "_small.png";
String base = Emulator.getConfig().getValue("camera.url");
String json = Emulator.getConfig().getValue("camera.extradata")
.replace("%timestamp%", Integer.toString(timestamp))
.replace("%room_id%", Integer.toString(room.getId()))
.replace("%url%", base + URL);
habboInfo.setPhotoURL(base + URL);
habboInfo.setPhotoTimestamp(timestamp);
habboInfo.setPhotoRoomId(room.getId());
habboInfo.setPhotoJSON(json);
File imageFile = new File(Emulator.getConfig().getValue("imager.location.output.camera") + URL);
File smallImageFile = new File(Emulator.getConfig().getValue("imager.location.output.camera") + URLsmall);
try {
ImageIO.write(theImage, "png", imageFile);
int smallWidth = theImage.getWidth(null) / 2;
int smallHeight = theImage.getHeight(null) / 2;
BufferedImage bi = new BufferedImage(smallWidth, smallHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics2D = bi.createGraphics();
graphics2D.setRenderingHint(java.awt.RenderingHints.KEY_INTERPOLATION, java.awt.RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
graphics2D.drawImage(theImage, 0, 0, smallWidth, smallHeight, null);
graphics2D.dispose();
ImageIO.write(bi, "png", smallImageFile);
} catch (IOException e) {
handleImageProcessingError(habbo);
return;
}
this.client.sendResponse(new CameraURLComposer(URL));
}
private boolean isPNG(byte[] bytes) {
return bytes[0] == (byte) 0x89 && bytes[1] == 0x50 && bytes[2] == 0x4E && bytes[3] == 0x47;
}
/**
* Read PNG dimensions from the IHDR chunk without fully decoding the image.
* This prevents decompression bomb attacks by checking dimensions before allocation.
*/
private int[] readPNGDimensions(ByteBuf buf) throws IOException {
try (ByteBufInputStream in = new ByteBufInputStream(buf.duplicate())) {
try (ImageInputStream iis = ImageIO.createImageInputStream(in)) {
Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
if (!readers.hasNext()) return null;
ImageReader reader = readers.next();
try {
reader.setInput(iis);
int width = reader.getWidth(0);
int height = reader.getHeight(0);
return new int[]{width, height};
} finally {
reader.dispose();
}
}
}
}
private int getDailyRenderCount(HabboStats stats, int currentTimestamp) {
if (!stats.cache.containsKey("camera_daily_count") || !stats.cache.containsKey("camera_daily_reset")) {
return 0;
}
int resetTimestamp = (Integer) stats.cache.get("camera_daily_reset");
// Reset counter if more than 24 hours have passed
if (currentTimestamp - resetTimestamp >= 86400) {
return 0;
}
return (Integer) stats.cache.get("camera_daily_count");
}
private void incrementDailyRenderCount(HabboStats stats, int currentTimestamp, int currentCount) {
if (currentCount == 0) {
stats.cache.put("camera_daily_reset", currentTimestamp);
}
stats.cache.put("camera_daily_count", currentCount + 1);
}
private void handleImageProcessingError(Habbo habbo) {
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
} }
} }
@@ -1,37 +1,167 @@
package com.eu.habbo.messages.incoming.camera; package com.eu.habbo.messages.incoming.camera;
import com.eu.habbo.Emulator; import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboInfo;
import com.eu.habbo.habbohotel.users.HabboStats;
import com.eu.habbo.messages.incoming.MessageHandler; import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.camera.CameraRoomThumbnailSavedComposer; import com.eu.habbo.messages.outgoing.camera.CameraRoomThumbnailSavedComposer;
import com.eu.habbo.networking.camera.CameraClient; import io.netty.buffer.ByteBuf;
import com.eu.habbo.networking.camera.messages.outgoing.CameraRenderImageComposer; import io.netty.buffer.ByteBufInputStream;
import com.eu.habbo.util.crypto.ZIP; import io.netty.buffer.ByteBufUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
public class CameraRoomThumbnailEvent extends MessageHandler { public class CameraRoomThumbnailEvent extends MessageHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CameraRoomThumbnailEvent.class);
public static int CAMERA_RENDER_DELAY = 15;
public static int MAX_THUMBNAIL_BYTES = 1024 * 1024; // 1 MB max for thumbnails
public static int MAX_THUMBNAIL_WIDTH = 640;
public static int MAX_THUMBNAIL_HEIGHT = 640;
private ByteBuf image = null;
@Override @Override
public void handle() throws Exception { public void handle() {
if (!this.client.getHabbo().hasPermission("acc_camera")) { try {
this.client.getHabbo().alert(Emulator.getTexts().getValue("camera.permission")); this.make();
} finally {
if (this.image != null) {
this.image.release();
}
}
}
private void make() {
Habbo habbo = this.client.getHabbo();
if (!habbo.hasPermission("acc_camera")) {
habbo.alert(Emulator.getTexts().getValue("camera.permission"));
return; return;
} }
if (!this.client.getHabbo().getHabboInfo().getCurrentRoom().isOwner(this.client.getHabbo())) HabboStats habboStats = habbo.getHabboStats();
int timestamp = Emulator.getIntUnixTimestamp();
if (habboStats.cache.containsKey("camera_render_cooldown")) {
int cameraTimestamp = (Integer) habboStats.cache.get("camera_render_cooldown");
if (timestamp - cameraTimestamp < CAMERA_RENDER_DELAY) {
String alertMessage = Emulator.getTexts().getValue("camera.wait").replace("%seconds%", Integer.toString(CAMERA_RENDER_DELAY - (timestamp - cameraTimestamp)));
habbo.alert(alertMessage);
return;
}
}
habboStats.cache.put("camera_render_cooldown", timestamp);
HabboInfo habboInfo = habbo.getHabboInfo();
Room room = habboInfo.getCurrentRoom();
if (room == null || !room.isOwner(habbo)) return;
int count = this.packet.readInt();
// Reject oversized payloads before reading
if (count <= 0 || count > MAX_THUMBNAIL_BYTES) {
LOGGER.warn("User {} attempted thumbnail upload with invalid size: {} bytes", habboInfo.getUsername(), count);
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
return; return;
}
if (CameraClient.isLoggedIn) { this.image = this.packet.getBuffer().readBytes(count);
this.packet.getBuffer().readFloat(); if (this.image == null || !isValidImage(this.image)) {
byte[] data = this.packet.getBuffer().readBytes(this.packet.getBuffer().readableBytes()).array(); LOGGER.warn("User {} attempted thumbnail upload with non-PNG data", habboInfo.getUsername());
String content = new String(ZIP.inflate(data)); return;
}
CameraRenderImageComposer composer = new CameraRenderImageComposer(this.client.getHabbo().getHabboInfo().getId(), this.client.getHabbo().getHabboInfo().getCurrentRoom().getBackgroundTonerColor().getRGB(), 110, 110, content); // Validate dimensions before fully decoding (prevents decompression bombs)
int[] dimensions;
try {
dimensions = readPNGDimensions(this.image);
} catch (IOException e) {
LOGGER.warn("User {} uploaded thumbnail with unreadable dimensions", habboInfo.getUsername());
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
return;
}
this.client.getHabbo().getHabboInfo().setPhotoJSON(Emulator.getConfig().getValue("camera.extradata").replace("%timestamp%", composer.timestamp + "")); if (dimensions == null || dimensions[0] <= 0 || dimensions[1] <= 0
this.client.getHabbo().getHabboInfo().setPhotoTimestamp(composer.timestamp); || dimensions[0] > MAX_THUMBNAIL_WIDTH || dimensions[1] > MAX_THUMBNAIL_HEIGHT) {
LOGGER.warn("User {} attempted thumbnail upload with invalid dimensions: {}x{}",
habboInfo.getUsername(),
dimensions != null ? dimensions[0] : "null",
dimensions != null ? dimensions[1] : "null");
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
return;
}
Emulator.getCameraClient().sendMessage(composer); BufferedImage theImage;
} else { try (ByteBufInputStream in = new ByteBufInputStream(this.image)) {
this.client.sendResponse(new CameraRoomThumbnailSavedComposer()); theImage = ImageIO.read(in);
this.client.getHabbo().alert(Emulator.getTexts().getValue("camera.disabled")); } catch (IOException e) {
LOGGER.error("Failed to decode thumbnail from user {}", habboInfo.getUsername(), e);
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
return;
}
if (theImage == null) {
LOGGER.warn("User {} uploaded thumbnail that could not be decoded", habboInfo.getUsername());
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
return;
}
// Double-check decoded dimensions
if (theImage.getWidth() > MAX_THUMBNAIL_WIDTH || theImage.getHeight() > MAX_THUMBNAIL_HEIGHT) {
LOGGER.warn("User {} decoded thumbnail exceeds dimension limits: {}x{}", habboInfo.getUsername(), theImage.getWidth(), theImage.getHeight());
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
return;
}
File imageFile = new File(Emulator.getConfig().getValue("imager.location.output.thumbnail") + room.getId() + ".png");
try {
ImageIO.write(theImage, "png", imageFile);
} catch (IOException e) {
LOGGER.error("Failed to write thumbnail for room {}", room.getId(), e);
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
return;
}
this.client.sendResponse(new CameraRoomThumbnailSavedComposer());
}
private boolean isValidImage(ByteBuf imageBuffer) {
byte[] imageBytes = ByteBufUtil.getBytes(imageBuffer, 0, 4, true);
return imageBytes != null && imageBytes.length >= 4
&& imageBytes[0] == (byte) 0x89 && imageBytes[1] == 0x50
&& imageBytes[2] == 0x4E && imageBytes[3] == 0x47;
}
/**
* Read PNG dimensions from the IHDR chunk without fully decoding the image.
* This prevents decompression bomb attacks by checking dimensions before allocation.
*/
private int[] readPNGDimensions(ByteBuf buf) throws IOException {
try (ByteBufInputStream in = new ByteBufInputStream(buf.duplicate())) {
try (ImageInputStream iis = ImageIO.createImageInputStream(in)) {
Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
if (!readers.hasNext()) return null;
ImageReader reader = readers.next();
try {
reader.setInput(iis);
int width = reader.getWidth(0);
int height = reader.getHeight(0);
return new int[]{width, height};
} finally {
reader.dispose();
}
}
} }
} }
} }
@@ -129,6 +129,11 @@ public class RoomSettingsSaveEvent extends MessageHandler {
room.setChatSpeed(this.packet.readInt()); room.setChatSpeed(this.packet.readInt());
room.setChatDistance(Math.abs(this.packet.readInt())); room.setChatDistance(Math.abs(this.packet.readInt()));
room.setChatProtection(this.packet.readInt()); room.setChatProtection(this.packet.readInt());
if (this.packet.bytesAvailable() > 0) {
room.setAllowUnderpass(this.packet.readBoolean());
}
room.setNeedsUpdate(true); room.setNeedsUpdate(true);
room.sendComposer(new RoomThicknessComposer(room).compose()); room.sendComposer(new RoomThicknessComposer(room).compose());
@@ -52,6 +52,7 @@ public class RoomSettingsComposer extends MessageComposer {
this.response.appendInt(this.room.getMuteOption()); this.response.appendInt(this.room.getMuteOption());
this.response.appendInt(this.room.getKickOption()); this.response.appendInt(this.room.getKickOption());
this.response.appendInt(this.room.getBanOption()); this.response.appendInt(this.room.getBanOption());
this.response.appendInt(this.room.isAllowUnderpass() ? 1 : 0);
return this.response; return this.response;
} }
@@ -1,92 +0,0 @@
package com.eu.habbo.networking.camera;
import com.eu.habbo.networking.camera.messages.outgoing.CameraLoginComposer;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CameraClient {
private static final Logger LOGGER = LoggerFactory.getLogger(CameraClient.class);
private static final String host = "google.com";
private static final int port = 1232;
public static ChannelFuture channelFuture;
public static boolean isLoggedIn = false;
public static boolean attemptReconnect = true;
private static Channel channel;
private final Bootstrap bootstrap = new Bootstrap();
public CameraClient() {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
this.bootstrap.group(eventLoopGroup);
this.bootstrap.channel(NioSocketChannel.class);
this.bootstrap.option(ChannelOption.TCP_NODELAY, true);
this.bootstrap.option(ChannelOption.SO_KEEPALIVE, false);
this.bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new CameraDecoder());
ch.pipeline().addLast(new CameraHandler());
}
});
this.bootstrap.option(ChannelOption.SO_RCVBUF, 5120);
this.bootstrap.option(ChannelOption.SO_REUSEADDR, true);
this.bootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(5120));
this.bootstrap.option(ChannelOption.ALLOCATOR, new UnpooledByteBufAllocator(false));
}
public void connect() {
CameraClient.channelFuture = this.bootstrap.connect(host, port);
while (!CameraClient.channelFuture.isDone()) {
}
if (CameraClient.channelFuture.isSuccess()) {
CameraClient.attemptReconnect = false;
CameraClient.channel = channelFuture.channel();
LOGGER.info("Connected to the Camera Server. Attempting to login.");
this.sendMessage(new CameraLoginComposer());
} else {
LOGGER.error("Failed to connect to the Camera Server. Server unreachable.");
CameraClient.channel = null;
CameraClient.channelFuture.channel().close();
CameraClient.channelFuture = null;
CameraClient.attemptReconnect = true;
}
}
public void disconnect() {
if (channelFuture != null) {
try {
channelFuture.channel().close().sync();
channelFuture = null;
} catch (Exception e) {
e.printStackTrace();
}
}
channel = null;
isLoggedIn = false;
LOGGER.info("Disconnected from the camera server.");
}
public void sendMessage(CameraOutgoingMessage outgoingMessage) {
try {
if (isLoggedIn || outgoingMessage instanceof CameraLoginComposer) {
outgoingMessage.compose(channel);
channel.write(outgoingMessage.get().copy(), channel.voidPromise());
channel.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -1,29 +0,0 @@
package com.eu.habbo.networking.camera;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
class CameraDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> objects) {
int readerIndex = byteBuf.readerIndex();
if (byteBuf.readableBytes() < 6) {
byteBuf.readerIndex(readerIndex);
return;
}
int length = byteBuf.readInt();
byteBuf.readerIndex(readerIndex);
if (byteBuf.readableBytes() < (length)) {
byteBuf.readerIndex(readerIndex);
return;
}
byteBuf.readerIndex(readerIndex);
objects.add(byteBuf.readBytes(length + 4));
}
}
@@ -1,50 +0,0 @@
package com.eu.habbo.networking.camera;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class CameraHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
ByteBuf message = (ByteBuf) msg;
((ByteBuf) msg).readerIndex(0);
int length = message.readInt();
ByteBuf b = Unpooled.wrappedBuffer(message.readBytes(length));
short header = b.readShort();
try {
CameraPacketHandler.instance().handle(ctx.channel(), header, b);
} catch (Exception e) {
} finally {
try {
b.release();
} catch (Exception e) {
}
try {
((ByteBuf) msg).release();
} catch (Exception e) {
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
CameraClient.attemptReconnect = true;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
}
}
@@ -1,63 +0,0 @@
package com.eu.habbo.networking.camera;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import java.nio.charset.Charset;
public abstract class CameraIncomingMessage extends CameraMessage {
public CameraIncomingMessage(Short header, ByteBuf body) {
super(header);
this.buffer.writerIndex(0).writeBytes(body);
}
public int readShort() {
return this.buffer.readShort();
}
public Integer readInt() {
try {
return this.buffer.readInt();
} catch (Exception e) {
}
return 0;
}
public boolean readBoolean() {
try {
return this.buffer.readByte() == 1;
} catch (Exception e) {
}
return false;
}
public String readString() {
try {
int length = this.readInt();
byte[] data = new byte[length];
this.buffer.readBytes(data);
return new String(data);
} catch (Exception e) {
return "";
}
}
public String getMessageBody() {
String consoleText = this.buffer.toString(Charset.defaultCharset());
for (int i = -1; i < 31; i++) {
consoleText = consoleText.replace(Character.toString((char) i), "[" + i + "]");
}
return consoleText;
}
public int bytesAvailable() {
return this.buffer.readableBytes();
}
public abstract void handle(Channel client) throws Exception;
}
@@ -1,18 +0,0 @@
package com.eu.habbo.networking.camera;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
public class CameraMessage {
protected final short header;
protected final ByteBuf buffer;
public CameraMessage(short header) {
this.header = header;
this.buffer = Unpooled.buffer();
}
public short getHeader() {
return this.header;
}
}
@@ -1,128 +0,0 @@
package com.eu.habbo.networking.camera;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.channel.Channel;
import java.io.IOException;
import java.nio.charset.Charset;
public abstract class CameraOutgoingMessage extends CameraMessage {
private final ByteBufOutputStream stream;
public CameraOutgoingMessage(short header) {
super(header);
this.stream = new ByteBufOutputStream(this.buffer);
try {
this.stream.writeInt(0);
this.stream.writeShort(header);
} catch (Exception e) {
}
}
public void appendRawBytes(byte[] bytes) {
try {
this.stream.write(bytes);
} catch (IOException e) {
}
}
public void appendString(String obj) {
try {
byte[] data = obj.getBytes();
this.stream.writeInt(data.length);
this.stream.write(data);
} catch (IOException e) {
}
}
public void appendChar(int obj) {
try {
this.stream.writeChar(obj);
} catch (IOException e) {
}
}
public void appendChars(Object obj) {
try {
this.stream.writeChars(obj.toString());
} catch (IOException e) {
}
}
public void appendInt32(Integer obj) {
try {
this.stream.writeInt(obj);
} catch (IOException e) {
}
}
public void appendInt32(Byte obj) {
try {
this.stream.writeInt((int) obj);
} catch (IOException e) {
}
}
public void appendInt32(Boolean obj) {
try {
this.stream.writeInt(obj ? 1 : 0);
} catch (IOException e) {
}
}
public void appendShort(int obj) {
try {
this.stream.writeShort((short) obj);
} catch (IOException e) {
}
}
public void appendByte(Integer b) {
try {
this.stream.writeByte(b);
} catch (IOException e) {
}
}
public void appendBoolean(Boolean obj) {
try {
this.stream.writeBoolean(obj);
} catch (IOException e) {
}
}
public CameraOutgoingMessage appendResponse(CameraOutgoingMessage obj) {
try {
this.stream.write(obj.get().array());
} catch (IOException e) {
}
return this;
}
public String getBodyString() {
ByteBuf buffer = this.stream.buffer().duplicate();
buffer.setInt(0, buffer.writerIndex() - 4);
String consoleText = buffer.toString(Charset.forName("UTF-8"));
for (int i = 0; i < 14; i++) {
consoleText = consoleText.replace(Character.toString((char) i), "[" + i + "]");
}
buffer.discardSomeReadBytes();
return consoleText;
}
public ByteBuf get() {
this.buffer.setInt(0, this.buffer.writerIndex() - 4);
return this.buffer.copy();
}
public abstract void compose(Channel channel);
}
@@ -1,48 +0,0 @@
package com.eu.habbo.networking.camera;
import com.eu.habbo.networking.camera.messages.incoming.*;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
public class CameraPacketHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CameraPacketHandler.class);
private static CameraPacketHandler INSTANCE;
private final HashMap<Short, Class<? extends CameraIncomingMessage>> packetDefinitions;
public CameraPacketHandler() {
this.packetDefinitions = new HashMap<>();
this.packetDefinitions.put((short) 1, CameraLoginStatusEvent.class);
this.packetDefinitions.put((short) 2, CameraResultURLEvent.class);
this.packetDefinitions.put((short) 3, CameraRoomThumbnailGeneratedEvent.class);
this.packetDefinitions.put((short) 4, CameraUpdateNotification.class);
this.packetDefinitions.put((short) 5, CameraAuthenticationTicketEvent.class);
}
public static CameraPacketHandler instance() {
if (INSTANCE == null) {
INSTANCE = new CameraPacketHandler();
}
return INSTANCE;
}
public void handle(Channel channel, short i, ByteBuf ii) {
Class<? extends CameraIncomingMessage> declaredClass = this.packetDefinitions.get(i);
if (declaredClass != null) {
try {
CameraIncomingMessage message = declaredClass.getDeclaredConstructor(new Class[]{Short.class, ByteBuf.class}).newInstance(i, ii);
message.handle(channel);
message.buffer.release();
} catch (Exception e) {
LOGGER.error("Caught exception", e);
}
}
}
}
@@ -1,6 +0,0 @@
package com.eu.habbo.networking.camera.messages;
public class CameraOutgoingHeaders {
public final static short LoginComposer = 1;
public final static short RenderImageComposer = 2;
}
@@ -1,21 +0,0 @@
package com.eu.habbo.networking.camera.messages.incoming;
import com.eu.habbo.messages.outgoing.gamecenter.basejump.BaseJumpLoadGameComposer;
import com.eu.habbo.networking.camera.CameraIncomingMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
public class CameraAuthenticationTicketEvent extends CameraIncomingMessage {
public CameraAuthenticationTicketEvent(Short header, ByteBuf body) {
super(header, body);
}
@Override
public void handle(Channel client) throws Exception {
String ticket = this.readString();
if (ticket.startsWith("FASTFOOD")) {
BaseJumpLoadGameComposer.FASTFOOD_KEY = ticket;
}
}
}
@@ -1,56 +0,0 @@
package com.eu.habbo.networking.camera.messages.incoming;
import com.eu.habbo.networking.camera.CameraClient;
import com.eu.habbo.networking.camera.CameraIncomingMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CameraLoginStatusEvent extends CameraIncomingMessage {
private static final Logger LOGGER = LoggerFactory.getLogger(CameraLoginStatusEvent.class);
public final static int LOGIN_OK = 0;
public final static int LOGIN_ERROR = 1;
public final static int NO_ACCOUNT = 2;
public final static int ALREADY_LOGGED_IN = 3;
public final static int BANNED = 4;
public final static int OLD_BUILD = 5;
public final static int NO_CAMERA_SUBSCRIPTION = 6;
public CameraLoginStatusEvent(Short header, ByteBuf body) {
super(header, body);
}
@Override
public void handle(Channel client) throws Exception {
int status = this.readInt();
if (status == LOGIN_ERROR) {
LOGGER.error("Failed to login to Camera Server: Incorrect Details");
} else if (status == NO_ACCOUNT) {
LOGGER.error("Failed to login to Camera Server: No Account Found. Register for free on the Arcturus Forums! Visit http://arcturus.pw/");
} else if (status == BANNED) {
LOGGER.error("Sorry but you seem to be banned from the Arcturus forums and therefor cant use the Camera Server :'(");
} else if (status == ALREADY_LOGGED_IN) {
LOGGER.error("You seem to be already connected to the Camera Server");
} else if (status == OLD_BUILD) {
LOGGER.error("This version of Arcturus Emulator is no longer supported by the Camera Server. Upgrade your emulator.");
} else if (status == NO_CAMERA_SUBSCRIPTION) {
LOGGER.error("You don't have a Camera Subscription and therefor cannot use the camera!");
LOGGER.error("Please consider making a donation to keep this project going. The emulator can be used free of charge!");
LOGGER.error("A trial version is available for $2.5. A year subscription is only $10 and a permanent subscription is $25.");
LOGGER.error("By donating this subscription you support the development of the emulator you are using :)");
LOGGER.error("Visit http://arcturus.pw/mysubscriptions.php to buy your subscription!");
LOGGER.error("Please Consider getting a subscription. Regards: The General");
}
if (status == LOGIN_OK) {
CameraClient.isLoggedIn = true;
LOGGER.info("Succesfully connected to the Arcturus Camera Server!");
} else {
CameraClient.attemptReconnect = false;
}
}
}
@@ -1,56 +0,0 @@
package com.eu.habbo.networking.camera.messages.incoming;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.achievements.AchievementManager;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.messages.outgoing.camera.CameraURLComposer;
import com.eu.habbo.networking.camera.CameraIncomingMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
public class CameraResultURLEvent extends CameraIncomingMessage {
public final static int STATUS_OK = 0;
public final static int STATUS_ERROR = 1;
public CameraResultURLEvent(Short header, ByteBuf body) {
super(header, body);
}
@Override
public void handle(Channel client) throws Exception {
int userId = this.readInt();
int status = this.readInt();
String URL = this.readString();
if (!Emulator.getConfig().getBoolean("camera.use.https", true)) {
URL = URL.replace("https://", "http://");
}
int roomId = this.readInt();
int timestamp = this.readInt();
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(userId);
if (status == STATUS_ERROR) {
if (habbo != null) {
habbo.getHabboInfo().setPhotoTimestamp(0);
habbo.getHabboInfo().setPhotoJSON("");
habbo.getHabboInfo().setPhotoURL("");
habbo.alert(Emulator.getTexts().getValue("camera.error.creation"));
return;
}
}
if (status == STATUS_OK) {
if (habbo != null) {
if (timestamp == habbo.getHabboInfo().getPhotoTimestamp()) {
AchievementManager.progressAchievement(habbo, Emulator.getGameEnvironment().getAchievementManager().getAchievement("CameraPhotoCount"), 1);
habbo.getClient().sendResponse(new CameraURLComposer(URL));
habbo.getHabboInfo().setPhotoJSON(habbo.getHabboInfo().getPhotoJSON().replace("%room_id%", roomId + "").replace("%url%", URL));
habbo.getHabboInfo().setPhotoURL(URL);
}
}
}
}
}
@@ -1,25 +0,0 @@
package com.eu.habbo.networking.camera.messages.incoming;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.messages.outgoing.camera.CameraRoomThumbnailSavedComposer;
import com.eu.habbo.networking.camera.CameraIncomingMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
public class CameraRoomThumbnailGeneratedEvent extends CameraIncomingMessage {
public CameraRoomThumbnailGeneratedEvent(Short header, ByteBuf body) {
super(header, body);
}
@Override
public void handle(Channel client) throws Exception {
int userId = this.readInt();
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(userId);
if (habbo != null) {
habbo.getClient().sendResponse(new CameraRoomThumbnailSavedComposer());
}
}
}
@@ -1,37 +0,0 @@
package com.eu.habbo.networking.camera.messages.incoming;
import com.eu.habbo.Emulator;
import com.eu.habbo.messages.outgoing.generic.alerts.GenericAlertComposer;
import com.eu.habbo.networking.camera.CameraIncomingMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CameraUpdateNotification extends CameraIncomingMessage {
private static final Logger LOGGER = LoggerFactory.getLogger(CameraUpdateNotification.class);
public CameraUpdateNotification(Short header, ByteBuf body) {
super(header, body);
}
@Override
public void handle(Channel client) throws Exception {
boolean alert = this.readBoolean();
String message = this.readString();
int type = this.readInt();
if (type == 0) {
LOGGER.info("Camera update: {}", message);
} else if (type == 1) {
LOGGER.warn("Camera update: {}", message);
} else if (type == 2) {
LOGGER.error("Camera update: {}", message);
}
if (alert) {
Emulator.getGameServer().getGameClientManager().sendBroadcastResponse(new GenericAlertComposer(message).compose());
}
}
}
@@ -1,19 +0,0 @@
package com.eu.habbo.networking.camera.messages.outgoing;
import com.eu.habbo.Emulator;
import com.eu.habbo.networking.camera.CameraOutgoingMessage;
import com.eu.habbo.networking.camera.messages.CameraOutgoingHeaders;
import io.netty.channel.Channel;
public class CameraLoginComposer extends CameraOutgoingMessage {
public CameraLoginComposer() {
super(CameraOutgoingHeaders.LoginComposer);
}
@Override
public void compose(Channel channel) {
this.appendString(Emulator.getConfig().getValue("username").trim());
this.appendString(Emulator.getConfig().getValue("password").trim());
this.appendString(Emulator.version);
}
}
@@ -1,36 +0,0 @@
package com.eu.habbo.networking.camera.messages.outgoing;
import com.eu.habbo.Emulator;
import com.eu.habbo.networking.camera.CameraOutgoingMessage;
import com.eu.habbo.networking.camera.messages.CameraOutgoingHeaders;
import io.netty.channel.Channel;
public class CameraRenderImageComposer extends CameraOutgoingMessage {
public final int timestamp;
final int userId;
final int backgroundColor;
final int width;
final int height;
final String JSON;
public CameraRenderImageComposer(int userId, int backgroundColor, int width, int height, String json) {
super(CameraOutgoingHeaders.RenderImageComposer);
this.userId = userId;
this.timestamp = Emulator.getIntUnixTimestamp();
this.backgroundColor = backgroundColor;
this.width = width;
this.height = height;
this.JSON = json;
}
@Override
public void compose(Channel channel) {
this.appendInt32(this.userId);
this.appendInt32(this.timestamp);
this.appendInt32(this.backgroundColor);
this.appendInt32(this.width);
this.appendInt32(this.height);
this.appendString(this.JSON);
}
}
@@ -28,8 +28,6 @@ import com.eu.habbo.habbohotel.wired.core.WiredEngine;
import com.eu.habbo.habbohotel.wired.core.WiredManager; import com.eu.habbo.habbohotel.wired.core.WiredManager;
import com.eu.habbo.habbohotel.wired.highscores.WiredHighscoreManager; import com.eu.habbo.habbohotel.wired.highscores.WiredHighscoreManager;
import com.eu.habbo.messages.PacketManager; import com.eu.habbo.messages.PacketManager;
import com.eu.habbo.messages.incoming.camera.CameraPublishToWebEvent;
import com.eu.habbo.messages.incoming.camera.CameraPurchaseEvent;
import com.eu.habbo.messages.incoming.catalog.CheckPetNameEvent; import com.eu.habbo.messages.incoming.catalog.CheckPetNameEvent;
import com.eu.habbo.messages.incoming.floorplaneditor.FloorPlanEditorSaveEvent; import com.eu.habbo.messages.incoming.floorplaneditor.FloorPlanEditorSaveEvent;
import com.eu.habbo.messages.incoming.hotelview.HotelViewRequestLTDAvailabilityEvent; import com.eu.habbo.messages.incoming.hotelview.HotelViewRequestLTDAvailabilityEvent;
@@ -161,11 +159,6 @@ public class PluginManager {
ChangeNameCheckUsernameEvent.VALID_CHARACTERS = Emulator.getConfig().getValue("allowed.username.characters", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-=!?@:,."); ChangeNameCheckUsernameEvent.VALID_CHARACTERS = Emulator.getConfig().getValue("allowed.username.characters", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-=!?@:,.");
CameraPublishToWebEvent.CAMERA_PUBLISH_POINTS = Emulator.getConfig().getInt("camera.price.points.publish", 5);
CameraPublishToWebEvent.CAMERA_PUBLISH_POINTS_TYPE = Emulator.getConfig().getInt("camera.price.points.publish.type", 0);
CameraPurchaseEvent.CAMERA_PURCHASE_CREDITS = Emulator.getConfig().getInt("camera.price.credits", 5);
CameraPurchaseEvent.CAMERA_PURCHASE_POINTS = Emulator.getConfig().getInt("camera.price.points", 5);
CameraPurchaseEvent.CAMERA_PURCHASE_POINTS_TYPE = Emulator.getConfig().getInt("camera.price.points.type", 0);
BuyRoomPromotionEvent.ROOM_PROMOTION_BADGE = Emulator.getConfig().getValue("room.promotion.badge", "RADZZ"); BuyRoomPromotionEvent.ROOM_PROMOTION_BADGE = Emulator.getConfig().getValue("room.promotion.badge", "RADZZ");
BotManager.MAXIMUM_BOT_INVENTORY_SIZE = Emulator.getConfig().getInt("hotel.bots.max.inventory"); BotManager.MAXIMUM_BOT_INVENTORY_SIZE = Emulator.getConfig().getInt("hotel.bots.max.inventory");
@@ -1,36 +0,0 @@
package com.eu.habbo.threading.runnables;
import com.eu.habbo.Emulator;
import com.eu.habbo.networking.camera.CameraClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CameraClientAutoReconnect implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(CameraClientAutoReconnect.class);
@Override
public void run() {
if (CameraClient.attemptReconnect && !Emulator.isShuttingDown) {
if (!(CameraClient.channelFuture != null && CameraClient.channelFuture.channel().isRegistered())) {
LOGGER.info("Attempting to connect to the Camera server.");
if (Emulator.getCameraClient() != null) {
Emulator.getCameraClient().disconnect();
} else {
Emulator.setCameraClient(new CameraClient());
}
try {
Emulator.getCameraClient().connect();
} catch (Exception e) {
LOGGER.error("Failed to start the camera client.", e);
}
} else {
CameraClient.attemptReconnect = false;
LOGGER.info("Already connected to the camera. Reconnecting not needed!");
}
}
Emulator.getThreading().run(this, 5000);
}
}