mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-19 14:59:27 +00:00
Resolve "not in event loop" issue on Geyser-Velocity with use-direct-connection enabled (#5720)
This commit is contained in:
@@ -8,6 +8,10 @@ dependencies {
|
|||||||
annotationProcessor(libs.velocity.api)
|
annotationProcessor(libs.velocity.api)
|
||||||
api(projects.core)
|
api(projects.core)
|
||||||
|
|
||||||
|
compileOnly(libs.velocity.proxy)
|
||||||
|
compileOnly(libs.netty.transport.native.io.uring)
|
||||||
|
compileOnly(libs.netty.transport.native.kqueue)
|
||||||
|
|
||||||
compileOnlyApi(libs.velocity.api)
|
compileOnlyApi(libs.velocity.api)
|
||||||
api(libs.cloud.velocity)
|
api(libs.cloud.velocity)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
package org.geysermc.geyser.platform.velocity;
|
package org.geysermc.geyser.platform.velocity;
|
||||||
|
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
|
import com.velocitypowered.proxy.network.TransportType;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
@@ -33,18 +34,25 @@ import io.netty.channel.ChannelInitializer;
|
|||||||
import io.netty.channel.ChannelOption;
|
import io.netty.channel.ChannelOption;
|
||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
import io.netty.channel.IoEventLoop;
|
import io.netty.channel.IoEventLoop;
|
||||||
|
import io.netty.channel.IoEventLoopGroup;
|
||||||
|
import io.netty.channel.IoHandler;
|
||||||
import io.netty.channel.IoHandlerFactory;
|
import io.netty.channel.IoHandlerFactory;
|
||||||
import io.netty.channel.MultiThreadIoEventLoopGroup;
|
import io.netty.channel.MultiThreadIoEventLoopGroup;
|
||||||
|
import io.netty.channel.SingleThreadIoEventLoop;
|
||||||
import io.netty.channel.WriteBufferWaterMark;
|
import io.netty.channel.WriteBufferWaterMark;
|
||||||
|
import io.netty.channel.epoll.EpollIoHandler;
|
||||||
|
import io.netty.channel.kqueue.KQueueIoHandler;
|
||||||
import io.netty.channel.local.LocalAddress;
|
import io.netty.channel.local.LocalAddress;
|
||||||
import io.netty.channel.local.LocalIoHandler;
|
import io.netty.channel.local.LocalIoHandler;
|
||||||
|
import io.netty.channel.nio.NioIoHandler;
|
||||||
|
import io.netty.channel.uring.IoUringIoHandler;
|
||||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||||
|
import io.netty.util.concurrent.ThreadAwareExecutor;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
|
||||||
import org.geysermc.geyser.network.netty.GeyserInjector;
|
import org.geysermc.geyser.network.netty.GeyserInjector;
|
||||||
|
import org.geysermc.geyser.network.netty.IoHandlerWrapper;
|
||||||
import org.geysermc.geyser.network.netty.LocalServerChannelWrapper;
|
import org.geysermc.geyser.network.netty.LocalServerChannelWrapper;
|
||||||
import org.geysermc.geyser.network.netty.WatchedSingleThreadIoEventLoop;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -86,9 +94,18 @@ public class GeyserVelocityInjector extends GeyserInjector {
|
|||||||
|
|
||||||
Field workerGroupField = connectionManagerClass.getDeclaredField("workerGroup");
|
Field workerGroupField = connectionManagerClass.getDeclaredField("workerGroup");
|
||||||
workerGroupField.setAccessible(true);
|
workerGroupField.setAccessible(true);
|
||||||
EventLoopGroup workerGroup = (EventLoopGroup) workerGroupField.get(connectionManager);
|
IoEventLoopGroup workerGroup = (IoEventLoopGroup) workerGroupField.get(connectionManager);
|
||||||
|
|
||||||
EventLoopGroup wrapperGroup = new MultiThreadIoEventLoopGroup(LocalIoHandler.newFactory()) {
|
var factory = LocalIoHandler.newFactory();
|
||||||
|
var nativeFactory = getNativeHandlerFactory();
|
||||||
|
var wrapperFactory = new IoHandlerFactory() {
|
||||||
|
@Override
|
||||||
|
public IoHandler newHandler(ThreadAwareExecutor ioExecutor) {
|
||||||
|
return new IoHandlerWrapper(factory.newHandler(ioExecutor), nativeFactory.newHandler(ioExecutor));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EventLoopGroup wrapperGroup = new MultiThreadIoEventLoopGroup(factory) {
|
||||||
@Override
|
@Override
|
||||||
protected ThreadFactory newDefaultThreadFactory() {
|
protected ThreadFactory newDefaultThreadFactory() {
|
||||||
return new DefaultThreadFactory("Geyser Backend Worker Group", Thread.MAX_PRIORITY);
|
return new DefaultThreadFactory("Geyser Backend Worker Group", Thread.MAX_PRIORITY);
|
||||||
@@ -96,7 +113,7 @@ public class GeyserVelocityInjector extends GeyserInjector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IoEventLoop newChild(Executor executor, IoHandlerFactory ioHandlerFactory, Object... args) {
|
protected IoEventLoop newChild(Executor executor, IoHandlerFactory ioHandlerFactory, Object... args) {
|
||||||
return new WatchedSingleThreadIoEventLoop(workerGroup, this, executor, ioHandlerFactory);
|
return new SingleThreadIoEventLoop(workerGroup, executor, wrapperFactory);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,4 +143,13 @@ public class GeyserVelocityInjector extends GeyserInjector {
|
|||||||
this.localChannel = channelFuture;
|
this.localChannel = channelFuture;
|
||||||
this.serverSocketAddress = channelFuture.channel().localAddress();
|
this.serverSocketAddress = channelFuture.channel().localAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IoHandlerFactory getNativeHandlerFactory() {
|
||||||
|
return switch (TransportType.bestType()) {
|
||||||
|
case NIO -> NioIoHandler.newFactory();
|
||||||
|
case EPOLL -> EpollIoHandler.newFactory();
|
||||||
|
case KQUEUE -> KQueueIoHandler.newFactory();
|
||||||
|
case IO_URING -> IoUringIoHandler.newFactory();
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
* @author GeyserMC
|
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.geysermc.geyser.network.netty;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.EventLoopGroup;
|
|
||||||
import io.netty.channel.IoEventLoopGroup;
|
|
||||||
import io.netty.channel.IoHandlerFactory;
|
|
||||||
import io.netty.channel.SingleThreadIoEventLoop;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
public class WatchedSingleThreadIoEventLoop extends SingleThreadIoEventLoop {
|
|
||||||
private final EventLoopGroup trueWorkerGroup;
|
|
||||||
|
|
||||||
public WatchedSingleThreadIoEventLoop(EventLoopGroup trueWorkerGroup, IoEventLoopGroup parent,
|
|
||||||
Executor executor, IoHandlerFactory ioHandlerFactory) {
|
|
||||||
super(parent, executor, ioHandlerFactory);
|
|
||||||
this.trueWorkerGroup = trueWorkerGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public ChannelFuture register(Channel channel) {
|
|
||||||
if (channel.getClass().getName().startsWith("org.geysermc.geyser")) {
|
|
||||||
return super.register(channel);
|
|
||||||
}
|
|
||||||
// Starting with Netty 4.2, channels/event loops are very picky with what can be accepted for each.
|
|
||||||
// For example, IoUringIoHandler (on a Linux machine, what Velocity's worker group will be)
|
|
||||||
// will not accept LocalChannels on bootstrap creation in GeyserVelocityInjector.
|
|
||||||
// And using a MultiThreadEventLoopGroup with LocalIoHandler will throw an error when trying to
|
|
||||||
// connect to the backend server.
|
|
||||||
// Inserting ourselves here allows our local channels to use the event loop made for LocalChannels,
|
|
||||||
// while re-using the settings and style of Velocity.
|
|
||||||
return this.trueWorkerGroup.register(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -130,6 +130,7 @@ minecraftauth = { group = "net.raphimc", name = "MinecraftAuth", version.ref = "
|
|||||||
mcprotocollib = { group = "org.geysermc.mcprotocollib", name = "protocol", version.ref = "mcprotocollib" }
|
mcprotocollib = { group = "org.geysermc.mcprotocollib", name = "protocol", version.ref = "mcprotocollib" }
|
||||||
raknet = { group = "org.cloudburstmc.netty", name = "netty-transport-raknet", version.ref = "raknet" }
|
raknet = { group = "org.cloudburstmc.netty", name = "netty-transport-raknet", version.ref = "raknet" }
|
||||||
terminalconsoleappender = { group = "net.minecrell", name = "terminalconsoleappender", version.ref = "terminalconsoleappender" }
|
terminalconsoleappender = { group = "net.minecrell", name = "terminalconsoleappender", version.ref = "terminalconsoleappender" }
|
||||||
|
velocity-proxy = { group = "com.velocitypowered", name = "velocity-proxy", version.ref = "velocity" }
|
||||||
velocity-api = { group = "com.velocitypowered", name = "velocity-api", version.ref = "velocity" }
|
velocity-api = { group = "com.velocitypowered", name = "velocity-api", version.ref = "velocity" }
|
||||||
viaproxy = { group = "net.raphimc", name = "ViaProxy", version.ref = "viaproxy" }
|
viaproxy = { group = "net.raphimc", name = "ViaProxy", version.ref = "viaproxy" }
|
||||||
viaversion = { group = "com.viaversion", name = "viaversion", version.ref = "viaversion" }
|
viaversion = { group = "com.viaversion", name = "viaversion", version.ref = "viaversion" }
|
||||||
|
|||||||
Reference in New Issue
Block a user