Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-16 21:10:30 +01:00
Cleaning up some stuff in the proxy implementation.
Dieser Commit ist enthalten in:
Ursprung
2d0c826ec9
Commit
7b84da2fa7
@ -139,14 +139,13 @@ public class VelocityServer implements ProxyServer {
|
|||||||
scheduler = new VelocityScheduler(pluginManager, Sleeper.SYSTEM);
|
scheduler = new VelocityScheduler(pluginManager, Sleeper.SYSTEM);
|
||||||
loadPlugins();
|
loadPlugins();
|
||||||
|
|
||||||
// Post the first event
|
|
||||||
pluginManager.getPlugins().forEach(container -> {
|
|
||||||
container.getInstance().ifPresent(plugin -> eventManager.register(plugin, plugin));
|
|
||||||
});
|
|
||||||
try {
|
try {
|
||||||
|
// Go ahead and fire the proxy initialization event. We block since plugins should have a chance
|
||||||
|
// to fully initialize before we accept any connections to the server.
|
||||||
eventManager.fire(new ProxyInitializeEvent()).get();
|
eventManager.fire(new ProxyInitializeEvent()).get();
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
// Ignore, we don't care.
|
// Ignore, we don't care. InterruptedException is unlikely to happen (and if it does, you've got bigger
|
||||||
|
// issues) and there is almost no chance ExecutionException will be thrown.
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cm.bind(configuration.getBind());
|
this.cm.bind(configuration.getBind());
|
||||||
@ -176,6 +175,11 @@ public class VelocityServer implements ProxyServer {
|
|||||||
logger.error("Couldn't load plugins", e);
|
logger.error("Couldn't load plugins", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register the plugin main classes so that we may proceed with firing the proxy initialize event
|
||||||
|
pluginManager.getPlugins().forEach(container -> {
|
||||||
|
container.getInstance().ifPresent(plugin -> eventManager.register(plugin, plugin));
|
||||||
|
});
|
||||||
|
|
||||||
logger.info("Loaded {} plugins", pluginManager.getPlugins().size());
|
logger.info("Loaded {} plugins", pluginManager.getPlugins().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,9 +207,11 @@ public class VelocityServer implements ProxyServer {
|
|||||||
|
|
||||||
eventManager.fire(new ProxyShutdownEvent());
|
eventManager.fire(new ProxyShutdownEvent());
|
||||||
try {
|
try {
|
||||||
eventManager.shutdown();
|
if (!eventManager.shutdown() || scheduler.shutdown()) {
|
||||||
|
logger.error("Your plugins took over 10 seconds to shut down.");
|
||||||
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
logger.error("Your plugins took over 10 seconds to shut down.");
|
// Not much we can do about this...
|
||||||
}
|
}
|
||||||
|
|
||||||
shutdown = true;
|
shutdown = true;
|
||||||
|
@ -25,7 +25,6 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(MinecraftPacket packet) {
|
public void handle(MinecraftPacket packet) {
|
||||||
//Not handleable packets: Chat, TabCompleteResponse, Respawn, Scoreboard*
|
|
||||||
if (!connection.getPlayer().isActive()) {
|
if (!connection.getPlayer().isActive()) {
|
||||||
// Connection was left open accidentally. Close it so as to avoid "You logged in from another location"
|
// Connection was left open accidentally. Close it so as to avoid "You logged in from another location"
|
||||||
// errors.
|
// errors.
|
||||||
|
@ -21,6 +21,10 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles communication with the connected Minecraft client. This is effectively the primary nerve center that
|
||||||
|
* joins backend servers with players.
|
||||||
|
*/
|
||||||
public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||||
private static final Logger logger = LogManager.getLogger(ClientPlaySessionHandler.class);
|
private static final Logger logger = LogManager.getLogger(ClientPlaySessionHandler.class);
|
||||||
private static final int MAX_PLUGIN_CHANNELS = 128;
|
private static final int MAX_PLUGIN_CHANNELS = 128;
|
||||||
@ -53,6 +57,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (packet instanceof Chat) {
|
if (packet instanceof Chat) {
|
||||||
|
// Try to handle any commands on the proxy. If that fails, send it onto the client.
|
||||||
Chat chat = (Chat) packet;
|
Chat chat = (Chat) packet;
|
||||||
String msg = ((Chat) packet).getMessage();
|
String msg = ((Chat) packet).getMessage();
|
||||||
if (msg.startsWith("/")) {
|
if (msg.startsWith("/")) {
|
||||||
@ -137,14 +142,15 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
player.getConnection().delayedWrite(joinGame);
|
player.getConnection().delayedWrite(joinGame);
|
||||||
idRemapper = EntityIdRemapper.getMapper(joinGame.getEntityId(), player.getConnection().getProtocolVersion());
|
idRemapper = EntityIdRemapper.getMapper(joinGame.getEntityId(), player.getConnection().getProtocolVersion());
|
||||||
} else {
|
} else {
|
||||||
// In order to handle switching to another server we will need send three packets:
|
// Ah, this is the meat and potatoes of the whole venture!
|
||||||
|
//
|
||||||
|
// In order to handle switching to another server, you will need to send three packets:
|
||||||
//
|
//
|
||||||
// - The join game packet from the backend server
|
// - The join game packet from the backend server
|
||||||
// - A respawn packet with a different dimension
|
// - A respawn packet with a different dimension
|
||||||
// - Another respawn with the correct dimension
|
// - Another respawn with the correct dimension
|
||||||
//
|
//
|
||||||
// We can't simply ignore the packet with the different dimension. If you try to be smart about it it doesn't
|
// The two respawns with different dimensions are required, otherwise the client gets confused.
|
||||||
// work.
|
|
||||||
//
|
//
|
||||||
// Most notably, by having the client accept the join game packet, we can work around the need to perform
|
// Most notably, by having the client accept the join game packet, we can work around the need to perform
|
||||||
// entity ID rewrites, eliminating potential issues from rewriting packets and improving compatibility with
|
// entity ID rewrites, eliminating potential issues from rewriting packets and improving compatibility with
|
||||||
@ -197,7 +203,6 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (actuallyRegistered.size() > 0) {
|
if (actuallyRegistered.size() > 0) {
|
||||||
logger.info("Rewritten register packet: {}", actuallyRegistered);
|
|
||||||
PluginMessage newRegisterPacket = PluginMessageUtil.constructChannelsPacket(packet.getChannel(), actuallyRegistered);
|
PluginMessage newRegisterPacket = PluginMessageUtil.constructChannelsPacket(packet.getChannel(), actuallyRegistered);
|
||||||
player.getConnectedServer().getMinecraftConnection().write(newRegisterPacket);
|
player.getConnectedServer().getMinecraftConnection().write(newRegisterPacket);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
|||||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
import com.velocitypowered.proxy.protocol.packet.*;
|
import com.velocitypowered.proxy.protocol.packet.*;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import net.kyori.text.TextComponent;
|
import net.kyori.text.TextComponent;
|
||||||
import net.kyori.text.TranslatableComponent;
|
import net.kyori.text.TranslatableComponent;
|
||||||
import net.kyori.text.format.TextColor;
|
import net.kyori.text.format.TextColor;
|
||||||
@ -71,6 +73,11 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleUnknown(ByteBuf buf) {
|
||||||
|
throw new IllegalStateException("Unknown data " + ByteBufUtil.hexDump(buf));
|
||||||
|
}
|
||||||
|
|
||||||
private void handleLegacy(MinecraftPacket packet) {
|
private void handleLegacy(MinecraftPacket packet) {
|
||||||
if (packet instanceof LegacyPing) {
|
if (packet instanceof LegacyPing) {
|
||||||
VelocityConfiguration configuration = VelocityServer.getServer().getConfiguration();
|
VelocityConfiguration configuration = VelocityServer.getServer().getConfiguration();
|
||||||
|
@ -16,6 +16,8 @@ import com.velocitypowered.proxy.connection.MinecraftConnection;
|
|||||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.util.EncryptionUtils;
|
import com.velocitypowered.proxy.util.EncryptionUtils;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import net.kyori.text.TextComponent;
|
import net.kyori.text.TextComponent;
|
||||||
import net.kyori.text.format.TextColor;
|
import net.kyori.text.format.TextColor;
|
||||||
@ -204,4 +206,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
inbound.setSessionHandler(new InitialConnectSessionHandler(player));
|
inbound.setSessionHandler(new InitialConnectSessionHandler(player));
|
||||||
player.createConnectionRequest(toTry.get()).fireAndForget();
|
player.createConnectionRequest(toTry.get()).fireAndForget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleUnknown(ByteBuf buf) {
|
||||||
|
throw new IllegalStateException("Unknown data " + ByteBufUtil.hexDump(buf));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(MinecraftPacket packet) {
|
public void handle(MinecraftPacket packet) {
|
||||||
Preconditions.checkArgument(packet instanceof StatusPing|| packet instanceof StatusRequest,
|
Preconditions.checkArgument(packet instanceof StatusPing || packet instanceof StatusRequest,
|
||||||
"Unrecognized packet type " + packet.getClass().getName());
|
"Unrecognized packet type " + packet.getClass().getName());
|
||||||
|
|
||||||
if (packet instanceof StatusPing) {
|
if (packet instanceof StatusPing) {
|
||||||
|
@ -9,7 +9,7 @@ import com.velocitypowered.api.event.EventManager;
|
|||||||
import com.velocitypowered.api.event.PostOrder;
|
import com.velocitypowered.api.event.PostOrder;
|
||||||
import com.velocitypowered.api.event.Subscribe;
|
import com.velocitypowered.api.event.Subscribe;
|
||||||
import com.velocitypowered.api.plugin.PluginManager;
|
import com.velocitypowered.api.plugin.PluginManager;
|
||||||
import com.velocitypowered.proxy.util.concurrency.ThreadRecorderThreadFactory;
|
import com.velocitypowered.proxy.util.concurrency.RecordingThreadFactory;
|
||||||
import net.kyori.event.EventSubscriber;
|
import net.kyori.event.EventSubscriber;
|
||||||
import net.kyori.event.PostResult;
|
import net.kyori.event.PostResult;
|
||||||
import net.kyori.event.SimpleEventBus;
|
import net.kyori.event.SimpleEventBus;
|
||||||
@ -35,12 +35,12 @@ public class VelocityEventManager implements EventManager {
|
|||||||
new ASMEventExecutorFactory<>(new PluginClassLoader(new URL[0])),
|
new ASMEventExecutorFactory<>(new PluginClassLoader(new URL[0])),
|
||||||
new VelocityMethodScanner());
|
new VelocityMethodScanner());
|
||||||
private final ExecutorService service;
|
private final ExecutorService service;
|
||||||
private final ThreadRecorderThreadFactory recordingThreadFactory;
|
private final RecordingThreadFactory recordingThreadFactory;
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
|
|
||||||
public VelocityEventManager(PluginManager pluginManager) {
|
public VelocityEventManager(PluginManager pluginManager) {
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
this.recordingThreadFactory = new ThreadRecorderThreadFactory(new ThreadFactoryBuilder()
|
this.recordingThreadFactory = new RecordingThreadFactory(new ThreadFactoryBuilder()
|
||||||
.setNameFormat("Velocity Event Executor - #%d").setDaemon(true).build());
|
.setNameFormat("Velocity Event Executor - #%d").setDaemon(true).build());
|
||||||
this.service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), recordingThreadFactory);
|
this.service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), recordingThreadFactory);
|
||||||
}
|
}
|
||||||
@ -123,9 +123,9 @@ public class VelocityEventManager implements EventManager {
|
|||||||
bus.unregister(handler);
|
bus.unregister(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() throws InterruptedException {
|
public boolean shutdown() throws InterruptedException {
|
||||||
service.shutdown();
|
service.shutdown();
|
||||||
service.awaitTermination(10, TimeUnit.SECONDS);
|
return service.awaitTermination(10, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class VelocityEventBus extends SimpleEventBus<Object> {
|
private static class VelocityEventBus extends SimpleEventBus<Object> {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.velocitypowered.proxy.plugin;
|
package com.velocitypowered.proxy.plugin;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.plugin.PluginManager;
|
import com.velocitypowered.api.plugin.PluginManager;
|
||||||
|
@ -5,7 +5,7 @@ import io.netty.buffer.ByteBuf;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a protocol-specific entity ID remapper for certain Minecraft packets. This is mostly required to support
|
* Represents a protocol-specific entity ID remapper for certain Minecraft packets. This is mostly required to support
|
||||||
* old versions of Minecraft. For Minecraft 1.9 clients and above, Velocity can use a more efficient method based on
|
* old versions of Minecraft. For Minecraft 1.8 clients and above, Velocity can use a more efficient method based on
|
||||||
* sending JoinGame packets multiple times.
|
* sending JoinGame packets multiple times.
|
||||||
*/
|
*/
|
||||||
public interface EntityIdRemapper {
|
public interface EntityIdRemapper {
|
||||||
|
@ -40,11 +40,12 @@ public class VelocityScheduler implements Scheduler {
|
|||||||
return new TaskBuilderImpl(plugin, runnable);
|
return new TaskBuilderImpl(plugin, runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public boolean shutdown() throws InterruptedException {
|
||||||
for (ScheduledTask task : ImmutableList.copyOf(tasksByPlugin.values())) {
|
for (ScheduledTask task : ImmutableList.copyOf(tasksByPlugin.values())) {
|
||||||
task.cancel();
|
task.cancel();
|
||||||
}
|
}
|
||||||
taskService.shutdown();
|
taskService.shutdown();
|
||||||
|
return taskService.awaitTermination(10, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TaskBuilderImpl implements TaskBuilder {
|
private class TaskBuilderImpl implements TaskBuilder {
|
||||||
|
@ -2,19 +2,22 @@ package com.velocitypowered.proxy.util.concurrency;
|
|||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.MapMaker;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a {@link ThreadFactory} that records the threads it has spawned.
|
* A {@link ThreadFactory} that records the threads it has created. Once a thread terminates, it is automatically removed
|
||||||
|
* from the recorder.
|
||||||
*/
|
*/
|
||||||
public class ThreadRecorderThreadFactory implements ThreadFactory {
|
public class RecordingThreadFactory implements ThreadFactory {
|
||||||
private final ThreadFactory backing;
|
private final ThreadFactory backing;
|
||||||
private final Set<Thread> threads = ConcurrentHashMap.newKeySet();
|
private final Set<Thread> threads = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
|
||||||
|
|
||||||
public ThreadRecorderThreadFactory(ThreadFactory backing) {
|
public RecordingThreadFactory(ThreadFactory backing) {
|
||||||
this.backing = Preconditions.checkNotNull(backing, "backing");
|
this.backing = Preconditions.checkNotNull(backing, "backing");
|
||||||
}
|
}
|
||||||
|
|
@ -7,11 +7,11 @@ import java.util.concurrent.Executors;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
class ThreadRecorderThreadFactoryTest {
|
class RecordingThreadFactoryTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void newThread() throws Exception {
|
void newThread() throws Exception {
|
||||||
ThreadRecorderThreadFactory factory = new ThreadRecorderThreadFactory(Executors.defaultThreadFactory());
|
RecordingThreadFactory factory = new RecordingThreadFactory(Executors.defaultThreadFactory());
|
||||||
CountDownLatch started = new CountDownLatch(1);
|
CountDownLatch started = new CountDownLatch(1);
|
||||||
CountDownLatch endThread = new CountDownLatch(1);
|
CountDownLatch endThread = new CountDownLatch(1);
|
||||||
factory.newThread(() -> {
|
factory.newThread(() -> {
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren