1
0
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:
chris
2025-08-15 23:38:42 +03:00
committed by GitHub
parent 28dcffa21e
commit e2ae101419
4 changed files with 36 additions and 66 deletions

View File

@@ -8,6 +8,10 @@ dependencies {
annotationProcessor(libs.velocity.api)
api(projects.core)
compileOnly(libs.velocity.proxy)
compileOnly(libs.netty.transport.native.io.uring)
compileOnly(libs.netty.transport.native.kqueue)
compileOnlyApi(libs.velocity.api)
api(libs.cloud.velocity)
}

View File

@@ -26,6 +26,7 @@
package org.geysermc.geyser.platform.velocity;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.proxy.network.TransportType;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
@@ -33,18 +34,25 @@ import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.IoEventLoop;
import io.netty.channel.IoEventLoopGroup;
import io.netty.channel.IoHandler;
import io.netty.channel.IoHandlerFactory;
import io.netty.channel.MultiThreadIoEventLoopGroup;
import io.netty.channel.SingleThreadIoEventLoop;
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.LocalIoHandler;
import io.netty.channel.nio.NioIoHandler;
import io.netty.channel.uring.IoUringIoHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.ThreadAwareExecutor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
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.WatchedSingleThreadIoEventLoop;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -86,9 +94,18 @@ public class GeyserVelocityInjector extends GeyserInjector {
Field workerGroupField = connectionManagerClass.getDeclaredField("workerGroup");
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
protected ThreadFactory newDefaultThreadFactory() {
return new DefaultThreadFactory("Geyser Backend Worker Group", Thread.MAX_PRIORITY);
@@ -96,7 +113,7 @@ public class GeyserVelocityInjector extends GeyserInjector {
@Override
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.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();
};
}
}

View File

@@ -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);
}
}

View File

@@ -130,6 +130,7 @@ minecraftauth = { group = "net.raphimc", name = "MinecraftAuth", version.ref = "
mcprotocollib = { group = "org.geysermc.mcprotocollib", name = "protocol", version.ref = "mcprotocollib" }
raknet = { group = "org.cloudburstmc.netty", name = "netty-transport-raknet", version.ref = "raknet" }
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" }
viaproxy = { group = "net.raphimc", name = "ViaProxy", version.ref = "viaproxy" }
viaversion = { group = "com.viaversion", name = "viaversion", version.ref = "viaversion" }