diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/pathfinding/impl/PathfinderImpl.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/pathfinding/impl/PathfinderImpl.java index b6bd7df5..c3105f54 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/pathfinding/impl/PathfinderImpl.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/rooms/pathfinding/impl/PathfinderImpl.java @@ -24,8 +24,11 @@ public class PathfinderImpl implements Pathfinder { private static final int CACHED_TIMEOUT_MS = Emulator.getConfig() .getInt(CONFIG_EXECUTION_TIME, 25); + // Default ON: bound A* to CACHED_TIMEOUT_MS (25ms) so a pathological search + // can't run unbounded and stall the thread. On timeout findPath returns an + // empty path (the unit simply doesn't move there) — graceful degradation. private static final boolean CACHED_TIMEOUT_ENABLED = Emulator.getConfig() - .getBoolean(CONFIG_TIMEOUT_ENABLED, false); + .getBoolean(CONFIG_TIMEOUT_ENABLED, true); private static final long CACHED_TIMEOUT_NANOS = CACHED_TIMEOUT_MS * 1_000_000L; private final Room room; diff --git a/Emulator/src/main/java/com/eu/habbo/networking/gameserver/WebSocketChannelInitializer.java b/Emulator/src/main/java/com/eu/habbo/networking/gameserver/WebSocketChannelInitializer.java index 6418992d..44a1b6be 100644 --- a/Emulator/src/main/java/com/eu/habbo/networking/gameserver/WebSocketChannelInitializer.java +++ b/Emulator/src/main/java/com/eu/habbo/networking/gameserver/WebSocketChannelInitializer.java @@ -26,12 +26,26 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; +import io.netty.util.concurrent.DefaultEventExecutorGroup; +import io.netty.util.concurrent.DefaultThreadFactory; +import io.netty.util.concurrent.EventExecutorGroup; import javax.net.ssl.SSLEngine; public class WebSocketChannelInitializer extends ChannelInitializer { private static final int MAX_FRAME_SIZE = 500000; + // Runs the game packet handler OFF the Netty I/O event loop, so a blocking + // handler (login/friends/catalog/guild JDBC, A* pathfinding, etc.) can no + // longer stall socket I/O for every other client sharing that I/O thread. + // A DefaultEventExecutorGroup pins each channel to one executor, so a single + // client's packets stay strictly ordered (no new intra-client races); the + // cross-client concurrency degree is the same the multi-threaded I/O group + // already had. Daemon threads so they don't block JVM shutdown. + private static final EventExecutorGroup PACKET_HANDLER_GROUP = new DefaultEventExecutorGroup( + Math.max(16, Runtime.getRuntime().availableProcessors() * 2), + new DefaultThreadFactory("GamePacketHandler", true)); + private final SslContext sslContext; private final boolean sslEnabled; private final WebSocketServerProtocolConfig wsConfig; @@ -82,7 +96,7 @@ public class WebSocketChannelInitializer extends ChannelInitializer