Compare commits
35 Commits
9c1be72db0
...
b6e05cb0b9
| Author | SHA1 | Date | |
|---|---|---|---|
| b6e05cb0b9 | |||
| 1507b91463 | |||
| bd2bb6325e | |||
| 3f0a85d794 | |||
| 74d05211d6 | |||
| 7ad06614fe | |||
| 163a85a468 | |||
| a51711e4bb | |||
| ae312339a3 | |||
| a429bb53ce | |||
| a549880df1 | |||
| b06af3718c | |||
| a20a896582 | |||
| e1a3421212 | |||
| 19e51a2b12 | |||
| b89a5c5ce9 | |||
| 65d3277319 | |||
| a22bfa10f9 | |||
| d9d1319a3a | |||
| 15ecbf4345 | |||
| 5e3bbcd427 | |||
| a6c79db07b | |||
| 6e33bc6c17 | |||
| 01208bb359 | |||
| fa88aaae52 | |||
| 2da400a267 | |||
| 8103135dfb | |||
| cfabff7288 | |||
| 2f5a27a708 | |||
| fdfe8bcc4b | |||
| a19fd8db74 | |||
| e63d71423d | |||
| a7afe35fab | |||
| 56d6339313 | |||
| 2475572573 |
@ -11,7 +11,6 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
@ -21,8 +20,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
*/
|
||||
public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
||||
|
||||
private static final Pattern VALID_IDENTIFIER_REGEX = Pattern.compile("[a-z0-9/\\-_]*");
|
||||
|
||||
private final String namespace;
|
||||
private final String name;
|
||||
|
||||
@ -39,7 +36,7 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
||||
* @return a new channel identifier
|
||||
*/
|
||||
public static MinecraftChannelIdentifier forDefaultNamespace(String name) {
|
||||
return new MinecraftChannelIdentifier("minecraft", name);
|
||||
return new MinecraftChannelIdentifier(Key.MINECRAFT_NAMESPACE, name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,14 +49,10 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
||||
public static MinecraftChannelIdentifier create(String namespace, String name) {
|
||||
checkArgument(!Strings.isNullOrEmpty(namespace), "namespace is null or empty");
|
||||
checkArgument(name != null, "namespace is null or empty");
|
||||
checkArgument(VALID_IDENTIFIER_REGEX.matcher(namespace).matches(),
|
||||
"namespace is not valid, must match: %s got %s",
|
||||
VALID_IDENTIFIER_REGEX.toString(),
|
||||
namespace);
|
||||
checkArgument(VALID_IDENTIFIER_REGEX.matcher(name).matches(),
|
||||
"name is not valid, must match: %s got %s",
|
||||
VALID_IDENTIFIER_REGEX.toString(),
|
||||
name);
|
||||
checkArgument(Key.parseableNamespace(namespace),
|
||||
"namespace is not valid, must match: [a-z0-9_.-] got %s", namespace);
|
||||
checkArgument(Key.parseableValue(name),
|
||||
"name is not valid, must match: [a-z0-9/._-] got %s", name);
|
||||
return new MinecraftChannelIdentifier(namespace, name);
|
||||
}
|
||||
|
||||
@ -72,10 +65,9 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
||||
public static MinecraftChannelIdentifier from(String identifier) {
|
||||
int colonPos = identifier.indexOf(':');
|
||||
if (colonPos == -1) {
|
||||
throw new IllegalArgumentException("Identifier does not contain a colon.");
|
||||
}
|
||||
if (colonPos + 1 == identifier.length()) {
|
||||
throw new IllegalArgumentException("Identifier is empty.");
|
||||
return create(Key.MINECRAFT_NAMESPACE, identifier);
|
||||
} else if (colonPos == 0) {
|
||||
return create(Key.MINECRAFT_NAMESPACE, identifier.substring(1));
|
||||
}
|
||||
String namespace = identifier.substring(0, colonPos);
|
||||
String name = identifier.substring(colonPos + 1);
|
||||
|
||||
@ -31,6 +31,7 @@ public final class ServerPing {
|
||||
private final net.kyori.adventure.text.Component description;
|
||||
private final @Nullable Favicon favicon;
|
||||
private final @Nullable ModInfo modinfo;
|
||||
private final boolean preventsChatReports = true;
|
||||
|
||||
public ServerPing(Version version, @Nullable Players players,
|
||||
net.kyori.adventure.text.Component description, @Nullable Favicon favicon) {
|
||||
|
||||
@ -47,17 +47,25 @@ class MinecraftChannelIdentifierTest {
|
||||
create("velocity", "test/test2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromIdentifierDefaultNamespace() {
|
||||
assertEquals("minecraft", from("test").getNamespace());
|
||||
assertEquals("minecraft", from(":test").getNamespace());
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromIdentifierAllowsEmptyName() {
|
||||
from("minecraft:");
|
||||
from(":");
|
||||
from("");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fromIdentifierThrowsOnBadValues() {
|
||||
assertAll(
|
||||
() -> assertThrows(IllegalArgumentException.class, () -> from("")),
|
||||
() -> assertThrows(IllegalArgumentException.class, () -> from(":")),
|
||||
() -> assertThrows(IllegalArgumentException.class, () -> from(":a")),
|
||||
() -> assertThrows(IllegalArgumentException.class, () -> from("a:")),
|
||||
() -> assertThrows(IllegalArgumentException.class, () -> from("hello:$$$$$$")),
|
||||
() -> assertThrows(IllegalArgumentException.class, () -> from("he/llo:wor/ld")),
|
||||
() -> assertThrows(IllegalArgumentException.class, () -> from("hello::"))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -3,7 +3,7 @@ configurate3 = "3.7.3"
|
||||
configurate4 = "4.1.2"
|
||||
flare = "2.0.1"
|
||||
log4j = "2.24.1"
|
||||
netty = "4.1.119.Final"
|
||||
netty = "4.2.0.Final"
|
||||
|
||||
[plugins]
|
||||
indra-publishing = "net.kyori.indra.publishing:2.0.6"
|
||||
@ -54,6 +54,7 @@ netty-codec-http = { module = "io.netty:netty-codec-http", version.ref = "netty"
|
||||
netty-handler = { module = "io.netty:netty-handler", version.ref = "netty" }
|
||||
netty-transport-native-epoll = { module = "io.netty:netty-transport-native-epoll", version.ref = "netty" }
|
||||
netty-transport-native-kqueue = { module = "io.netty:netty-transport-native-kqueue", version.ref = "netty" }
|
||||
netty-transport-native-iouring = { module = "io.netty:netty-transport-native-io_uring", version.ref = "netty" }
|
||||
nightconfig = "com.electronwill.night-config:toml:3.6.7"
|
||||
slf4j = "org.slf4j:slf4j-api:2.0.12"
|
||||
snakeyaml = "org.yaml:snakeyaml:1.33"
|
||||
|
||||
@ -121,6 +121,9 @@ dependencies {
|
||||
implementation(libs.netty.transport.native.epoll)
|
||||
implementation(variantOf(libs.netty.transport.native.epoll) { classifier("linux-x86_64") })
|
||||
implementation(variantOf(libs.netty.transport.native.epoll) { classifier("linux-aarch_64") })
|
||||
implementation(libs.netty.transport.native.iouring)
|
||||
implementation(variantOf(libs.netty.transport.native.iouring) { classifier("linux-x86_64") })
|
||||
implementation(variantOf(libs.netty.transport.native.iouring) { classifier("linux-aarch_64") })
|
||||
implementation(libs.netty.transport.native.kqueue)
|
||||
implementation(variantOf(libs.netty.transport.native.kqueue) { classifier("osx-x86_64") })
|
||||
implementation(variantOf(libs.netty.transport.native.kqueue) { classifier("osx-aarch_64") })
|
||||
|
||||
@ -84,6 +84,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
||||
private static final Logger logger = LogManager.getLogger(MinecraftConnection.class);
|
||||
|
||||
private final Channel channel;
|
||||
public boolean pendingConfigurationSwitch = false;
|
||||
private SocketAddress remoteAddress;
|
||||
private StateRegistry state;
|
||||
private Map<StateRegistry, MinecraftSessionHandler> sessionHandlers;
|
||||
|
||||
@ -34,6 +34,8 @@ import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.command.CommandGraphInjector;
|
||||
@ -288,31 +290,14 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Register and unregister packets are simply forwarded to the server as-is.
|
||||
if (PluginMessageUtil.isRegister(packet) || PluginMessageUtil.isUnregister(packet)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PluginMessageUtil.isMcBrand(packet)) {
|
||||
PluginMessagePacket rewritten = PluginMessageUtil
|
||||
.rewriteMinecraftBrand(packet,
|
||||
server.getVersion(), playerConnection.getProtocolVersion());
|
||||
playerConnection.write(rewritten);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (serverConn.getPhase().handle(serverConn, serverConn.getPlayer(), packet)) {
|
||||
// Handled.
|
||||
return true;
|
||||
}
|
||||
|
||||
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
|
||||
if (id == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] copy = ByteBufUtil.getBytes(packet.content());
|
||||
PluginMessageEvent event = new PluginMessageEvent(serverConn, serverConn.getPlayer(), id, copy);
|
||||
String channel = packet.getChannel();
|
||||
PluginMessageEvent event = new PluginMessageEvent(serverConn, serverConn.getPlayer(), channel.indexOf(':') == -1 ? new LegacyChannelIdentifier(channel) : MinecraftChannelIdentifier.from(channel), copy);
|
||||
server.getEventManager().fire(event).thenAcceptAsync(pme -> {
|
||||
if (pme.getResult().isAllowed() && !playerConnection.isClosed()) {
|
||||
PluginMessagePacket copied = new PluginMessagePacket(
|
||||
|
||||
@ -30,6 +30,8 @@ import com.velocitypowered.api.event.player.TabCompleteEvent;
|
||||
import com.velocitypowered.api.event.player.configuration.PlayerEnteredConfigurationEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.ConnectionTypes;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
@ -72,6 +74,7 @@ import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.title.GenericTitlePacket;
|
||||
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||
import com.velocitypowered.proxy.util.CharacterUtil;
|
||||
import com.velocitypowered.proxy.util.except.QuietRuntimeException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@ -308,14 +311,17 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
logger.warn("A plugin message was received while the backend server was not "
|
||||
+ "ready. Channel: {}. Packet discarded.", packet.getChannel());
|
||||
} else if (PluginMessageUtil.isRegister(packet)) {
|
||||
List<ChannelIdentifier> channels = PluginMessageUtil.getChannels(packet, this.player.getProtocolVersion());
|
||||
List<ChannelIdentifier> channels =
|
||||
PluginMessageUtil.getChannels(this.player.getClientsideChannels().size(), packet,
|
||||
this.player.getProtocolVersion());
|
||||
player.getClientsideChannels().addAll(channels);
|
||||
server.getEventManager()
|
||||
.fireAndForget(
|
||||
new PlayerChannelRegisterEvent(player, ImmutableList.copyOf(channels)));
|
||||
backendConn.write(packet.retain());
|
||||
} else if (PluginMessageUtil.isUnregister(packet)) {
|
||||
player.getClientsideChannels().removeAll(PluginMessageUtil.getChannels(packet, this.player.getProtocolVersion()));
|
||||
player.getClientsideChannels()
|
||||
.removeAll(PluginMessageUtil.getChannels(0, packet, this.player.getProtocolVersion()));
|
||||
backendConn.write(packet.retain());
|
||||
} else if (PluginMessageUtil.isMcBrand(packet)) {
|
||||
String brand = PluginMessageUtil.readBrandMessage(packet.content());
|
||||
@ -335,43 +341,25 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
}
|
||||
|
||||
if (!player.getPhase().handle(player, packet, serverConn)) {
|
||||
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
|
||||
if (id == null) {
|
||||
// We don't have any plugins listening on this channel, process the packet now.
|
||||
if (!player.getPhase().consideredComplete() || !serverConn.getPhase()
|
||||
.consideredComplete()) {
|
||||
// The client is trying to send messages too early. This is primarily caused by mods,
|
||||
// but further aggravated by Velocity. To work around these issues, we will queue any
|
||||
// non-FML handshake messages to be sent once the FML handshake has completed or the
|
||||
// JoinGame packet has been received by the proxy, whichever comes first.
|
||||
//
|
||||
// We also need to make sure to retain these packets, so they can be flushed
|
||||
// appropriately.
|
||||
loginPluginMessages.add(packet.retain());
|
||||
} else {
|
||||
// The connection is ready, send the packet now.
|
||||
backendConn.write(packet.retain());
|
||||
}
|
||||
} else {
|
||||
byte[] copy = ByteBufUtil.getBytes(packet.content());
|
||||
PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id, copy);
|
||||
server.getEventManager().fire(event).thenAcceptAsync(pme -> {
|
||||
if (pme.getResult().isAllowed()) {
|
||||
PluginMessagePacket message = new PluginMessagePacket(packet.getChannel(),
|
||||
Unpooled.wrappedBuffer(copy));
|
||||
if (!player.getPhase().consideredComplete() || !serverConn.getPhase()
|
||||
.consideredComplete()) {
|
||||
// We're still processing the connection (see above), enqueue the packet for now.
|
||||
loginPluginMessages.add(message.retain());
|
||||
} else {
|
||||
backendConn.write(message);
|
||||
}
|
||||
byte[] copy = ByteBufUtil.getBytes(packet.content());
|
||||
String channel = packet.getChannel();
|
||||
PluginMessageEvent event = new PluginMessageEvent(player, serverConn, channel.indexOf(':') == -1 ? new LegacyChannelIdentifier(channel) : MinecraftChannelIdentifier.from(channel), copy);
|
||||
server.getEventManager().fire(event).thenAcceptAsync(pme -> {
|
||||
if (pme.getResult().isAllowed()) {
|
||||
PluginMessagePacket message = new PluginMessagePacket(packet.getChannel(),
|
||||
Unpooled.wrappedBuffer(copy));
|
||||
if (!player.getPhase().consideredComplete() || !serverConn.getPhase()
|
||||
.consideredComplete()) {
|
||||
// We're still processing the connection (see above), enqueue the packet for now.
|
||||
loginPluginMessages.add(message.retain());
|
||||
} else {
|
||||
backendConn.write(message);
|
||||
}
|
||||
}, backendConn.eventLoop()).exceptionally((ex) -> {
|
||||
logger.error("Exception while handling plugin message packet for {}", player, ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}, backendConn.eventLoop()).exceptionally((ex) -> {
|
||||
logger.error("Exception while handling plugin message packet for {}", player, ex);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -389,10 +377,14 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(FinishedUpdatePacket packet) {
|
||||
if (!player.getConnection().pendingConfigurationSwitch) {
|
||||
throw new QuietRuntimeException("Not expecting reconfiguration");
|
||||
}
|
||||
// Complete client switch
|
||||
player.getConnection().setActiveSessionHandler(StateRegistry.CONFIG);
|
||||
VelocityServerConnection serverConnection = player.getConnectedServer();
|
||||
server.getEventManager().fireAndForget(new PlayerEnteredConfigurationEvent(player, serverConnection));
|
||||
server.getEventManager()
|
||||
.fireAndForget(new PlayerEnteredConfigurationEvent(player, serverConnection));
|
||||
if (serverConnection != null) {
|
||||
MinecraftConnection smc = serverConnection.ensureConnected();
|
||||
CompletableFuture.runAsync(() -> {
|
||||
|
||||
@ -145,7 +145,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, KeyIdentifiable,
|
||||
VelocityInboundConnection {
|
||||
|
||||
private static final int MAX_CLIENTSIDE_PLUGIN_CHANNELS = 1024;
|
||||
public static final int MAX_CLIENTSIDE_PLUGIN_CHANNELS = 1024;
|
||||
private static final PlainTextComponentSerializer PASS_THRU_TRANSLATE =
|
||||
PlainTextComponentSerializer.builder().flattener(TranslatableMapper.FLATTENER).build();
|
||||
static final PermissionProvider DEFAULT_PERMISSIONS = s -> PermissionFunction.ALWAYS_UNDEFINED;
|
||||
@ -1318,6 +1318,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
||||
connection.write(BundleDelimiterPacket.INSTANCE);
|
||||
}
|
||||
connection.write(StartUpdatePacket.INSTANCE);
|
||||
connection.pendingConfigurationSwitch = true;
|
||||
connection.getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.CONFIG);
|
||||
// Make sure we don't send any play packets to the player after update start
|
||||
connection.addPlayPacketQueueHandler();
|
||||
|
||||
@ -20,25 +20,32 @@ package com.velocitypowered.proxy.network;
|
||||
import com.velocitypowered.proxy.util.concurrent.VelocityNettyThreadFactory;
|
||||
import io.netty.channel.ChannelFactory;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.IoHandlerFactory;
|
||||
import io.netty.channel.MultiThreadIoEventLoopGroup;
|
||||
import io.netty.channel.epoll.Epoll;
|
||||
import io.netty.channel.epoll.EpollDatagramChannel;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollIoHandler;
|
||||
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||
import io.netty.channel.epoll.EpollSocketChannel;
|
||||
import io.netty.channel.kqueue.KQueue;
|
||||
import io.netty.channel.kqueue.KQueueDatagramChannel;
|
||||
import io.netty.channel.kqueue.KQueueEventLoopGroup;
|
||||
import io.netty.channel.kqueue.KQueueIoHandler;
|
||||
import io.netty.channel.kqueue.KQueueServerSocketChannel;
|
||||
import io.netty.channel.kqueue.KQueueSocketChannel;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.nio.NioIoHandler;
|
||||
import io.netty.channel.socket.DatagramChannel;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.channel.uring.IoUring;
|
||||
import io.netty.channel.uring.IoUringDatagramChannel;
|
||||
import io.netty.channel.uring.IoUringIoHandler;
|
||||
import io.netty.channel.uring.IoUringServerSocketChannel;
|
||||
import io.netty.channel.uring.IoUringSocketChannel;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Enumerates the supported transports for Velocity.
|
||||
@ -47,32 +54,36 @@ public enum TransportType {
|
||||
NIO("NIO", NioServerSocketChannel::new,
|
||||
NioSocketChannel::new,
|
||||
NioDatagramChannel::new,
|
||||
(name, type) -> new NioEventLoopGroup(0, createThreadFactory(name, type))),
|
||||
NioIoHandler::newFactory),
|
||||
EPOLL("epoll", EpollServerSocketChannel::new,
|
||||
EpollSocketChannel::new,
|
||||
EpollDatagramChannel::new,
|
||||
(name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type))),
|
||||
EpollIoHandler::newFactory),
|
||||
KQUEUE("kqueue", KQueueServerSocketChannel::new,
|
||||
KQueueSocketChannel::new,
|
||||
KQueueDatagramChannel::new,
|
||||
(name, type) -> new KQueueEventLoopGroup(0, createThreadFactory(name, type)));
|
||||
KQueueIoHandler::newFactory),
|
||||
IO_URING("io_uring", IoUringServerSocketChannel::new,
|
||||
IoUringSocketChannel::new,
|
||||
IoUringDatagramChannel::new,
|
||||
IoUringIoHandler::newFactory);
|
||||
|
||||
final String name;
|
||||
final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory;
|
||||
final ChannelFactory<? extends SocketChannel> socketChannelFactory;
|
||||
final ChannelFactory<? extends DatagramChannel> datagramChannelFactory;
|
||||
final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory;
|
||||
final Supplier<IoHandlerFactory> ioHandlerFactorySupplier;
|
||||
|
||||
TransportType(final String name,
|
||||
final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory,
|
||||
final ChannelFactory<? extends SocketChannel> socketChannelFactory,
|
||||
final ChannelFactory<? extends DatagramChannel> datagramChannelFactory,
|
||||
final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory) {
|
||||
final Supplier<IoHandlerFactory> ioHandlerFactorySupplier) {
|
||||
this.name = name;
|
||||
this.serverSocketChannelFactory = serverSocketChannelFactory;
|
||||
this.socketChannelFactory = socketChannelFactory;
|
||||
this.datagramChannelFactory = datagramChannelFactory;
|
||||
this.eventLoopGroupFactory = eventLoopGroupFactory;
|
||||
this.ioHandlerFactorySupplier = ioHandlerFactorySupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -80,8 +91,15 @@ public enum TransportType {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new event loop group for the given type.
|
||||
*
|
||||
* @param type the type of event loop group to create
|
||||
* @return the event loop group
|
||||
*/
|
||||
public EventLoopGroup createEventLoopGroup(final Type type) {
|
||||
return this.eventLoopGroupFactory.apply(this.name, type);
|
||||
return new MultiThreadIoEventLoopGroup(
|
||||
0, createThreadFactory(this.name, type), this.ioHandlerFactorySupplier.get());
|
||||
}
|
||||
|
||||
private static ThreadFactory createThreadFactory(final String name, final Type type) {
|
||||
@ -98,6 +116,10 @@ public enum TransportType {
|
||||
return NIO;
|
||||
}
|
||||
|
||||
if (IoUring.isAvailable() && Boolean.getBoolean("velocity.enable-iouring-transport")) {
|
||||
return IO_URING;
|
||||
}
|
||||
|
||||
if (Epoll.isAvailable()) {
|
||||
return EPOLL;
|
||||
}
|
||||
|
||||
@ -87,6 +87,7 @@ import com.velocitypowered.proxy.protocol.packet.StatusResponsePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.TabCompleteRequestPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponsePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.TransferPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.UpdateTeamsPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.ChatAcknowledgementPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletionPacket;
|
||||
@ -731,6 +732,22 @@ public enum StateRegistry {
|
||||
ClientboundServerLinksPacket::new,
|
||||
map(0x7B, MINECRAFT_1_21, false),
|
||||
map(0x82, MINECRAFT_1_21_2, false));
|
||||
clientbound.register(UpdateTeamsPacket.class, UpdateTeamsPacket::new,
|
||||
map(0x41, ProtocolVersion.MINECRAFT_1_9, true),
|
||||
map(0x43, ProtocolVersion.MINECRAFT_1_12, true),
|
||||
map(0x44, ProtocolVersion.MINECRAFT_1_12_1, true),
|
||||
map(0x47, ProtocolVersion.MINECRAFT_1_13, true),
|
||||
map(0x4B, ProtocolVersion.MINECRAFT_1_14, true),
|
||||
map(0x4C, ProtocolVersion.MINECRAFT_1_15, true),
|
||||
map(0x55, ProtocolVersion.MINECRAFT_1_17, true),
|
||||
map(0x58, ProtocolVersion.MINECRAFT_1_19_1, true),
|
||||
map(0x56, ProtocolVersion.MINECRAFT_1_19_3, true),
|
||||
map(0x5A, ProtocolVersion.MINECRAFT_1_19_4, true),
|
||||
map(0x5C, ProtocolVersion.MINECRAFT_1_20_2, true),
|
||||
map(0x5E, ProtocolVersion.MINECRAFT_1_20_3, true),
|
||||
map(0x60, ProtocolVersion.MINECRAFT_1_20_5, true),
|
||||
map(0x67, ProtocolVersion.MINECRAFT_1_21_2, true)
|
||||
);
|
||||
}
|
||||
},
|
||||
LOGIN {
|
||||
|
||||
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.protocol.packet;
|
||||
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class UpdateTeamsPacket implements MinecraftPacket {
|
||||
private String name;
|
||||
private Mode mode;
|
||||
|
||||
private Component displayName;
|
||||
private Component prefix;
|
||||
private Component suffix;
|
||||
private NameTagVisibility nameTagVisibility;
|
||||
private CollisionRule collisionRule;
|
||||
private int color;
|
||||
private byte friendlyFlags;
|
||||
|
||||
private List<String> players;
|
||||
|
||||
public UpdateTeamsPacket(String name, Mode mode, Component displayName, Component prefix, Component suffix, NameTagVisibility nameTagVisibility, CollisionRule collisionRule, int color, byte friendlyFlags, List<String> players) {
|
||||
this.name = name;
|
||||
this.mode = mode;
|
||||
this.displayName = displayName;
|
||||
this.prefix = prefix;
|
||||
this.suffix = suffix;
|
||||
this.nameTagVisibility = nameTagVisibility;
|
||||
this.collisionRule = collisionRule;
|
||||
this.color = color;
|
||||
this.friendlyFlags = friendlyFlags;
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
public UpdateTeamsPacket() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException("Packet is not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(MinecraftSessionHandler minecraftSessionHandler) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||
ProtocolUtils.writeString(byteBuf, name);
|
||||
byteBuf.writeByte(mode.ordinal());
|
||||
|
||||
switch (mode) {
|
||||
case CREATE, UPDATE:
|
||||
new ComponentHolder(protocolVersion, displayName).write(byteBuf);
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_13)) {
|
||||
new ComponentHolder(protocolVersion, prefix).write(byteBuf);
|
||||
new ComponentHolder(protocolVersion, suffix).write(byteBuf);
|
||||
}
|
||||
byteBuf.writeByte(friendlyFlags);
|
||||
ProtocolUtils.writeString(byteBuf, nameTagVisibility.getValue());
|
||||
ProtocolUtils.writeString(byteBuf, collisionRule.getValue());
|
||||
if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_12_2)) {
|
||||
ProtocolUtils.writeVarInt(byteBuf, color);
|
||||
new ComponentHolder(protocolVersion, prefix).write(byteBuf);
|
||||
new ComponentHolder(protocolVersion, suffix).write(byteBuf);
|
||||
} else {
|
||||
byteBuf.writeByte((byte) color);
|
||||
}
|
||||
|
||||
ProtocolUtils.writeVarInt(byteBuf, players.size());
|
||||
for (String player : players) {
|
||||
ProtocolUtils.writeString(byteBuf, player);
|
||||
}
|
||||
break;
|
||||
case ADD_PLAYER, REMOVE_PLAYER:
|
||||
ProtocolUtils.writeVarInt(byteBuf, players.size());
|
||||
for (String player : players) {
|
||||
ProtocolUtils.writeString(byteBuf, player);
|
||||
}
|
||||
break;
|
||||
case REMOVE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
CREATE,
|
||||
REMOVE,
|
||||
UPDATE,
|
||||
ADD_PLAYER,
|
||||
REMOVE_PLAYER,
|
||||
}
|
||||
|
||||
public enum NameTagVisibility {
|
||||
ALWAYS("always"),
|
||||
NEVER("never"),
|
||||
HIDE_FOR_OTHER_TEAMS("hideForOtherTeams"),
|
||||
HIDE_FOR_OWN_TEAM("hideForOwnTeam");
|
||||
|
||||
private final String value;
|
||||
|
||||
NameTagVisibility(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CollisionRule {
|
||||
ALWAYS("always"),
|
||||
NEVER("never"),
|
||||
PUSH_OTHER_TEAMS("pushOtherTeams"),
|
||||
PUSH_OWN_TEAM("pushOwnTeam");
|
||||
|
||||
private final String value;
|
||||
|
||||
CollisionRule(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public Component getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public Component getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public Component getSuffix() {
|
||||
return suffix;
|
||||
}
|
||||
|
||||
public NameTagVisibility getNameTagVisibility() {
|
||||
return nameTagVisibility;
|
||||
}
|
||||
|
||||
public CollisionRule getCollisionRule() {
|
||||
return collisionRule;
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public byte getFriendlyFlags() {
|
||||
return friendlyFlags;
|
||||
}
|
||||
|
||||
public List<String> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public void setDisplayName(Component displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public void setPrefix(Component prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public void setSuffix(Component suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public void setNameTagVisibility(NameTagVisibility nameTagVisibility) {
|
||||
this.nameTagVisibility = nameTagVisibility;
|
||||
}
|
||||
|
||||
public void setCollisionRule(CollisionRule collisionRule) {
|
||||
this.collisionRule = collisionRule;
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public void setFriendlyFlags(byte friendlyFlags) {
|
||||
this.friendlyFlags = friendlyFlags;
|
||||
}
|
||||
|
||||
public void setPlayers(List<String> players) {
|
||||
this.players = players;
|
||||
}
|
||||
}
|
||||
@ -116,6 +116,8 @@ public class KeyedPlayerChatPacket implements MinecraftPacket {
|
||||
ProtocolUtils.readByteArray(buf));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -132,6 +132,7 @@ public class KeyedPlayerCommandPacket implements MinecraftPacket {
|
||||
unsigned = true;
|
||||
}
|
||||
|
||||
unsigned = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -69,6 +69,7 @@ public class SessionPlayerChatPacket implements MinecraftPacket {
|
||||
this.salt = buf.readLong();
|
||||
this.signed = buf.readBoolean();
|
||||
if (this.signed) {
|
||||
this.signed = false;
|
||||
this.signature = readMessageSignature(buf);
|
||||
} else {
|
||||
this.signature = new byte[0];
|
||||
|
||||
@ -46,6 +46,8 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
|
||||
this.salt = buf.readLong();
|
||||
this.argumentSignatures = new ArgumentSignatures(buf);
|
||||
this.lastSeenMessages = new LastSeenMessages(buf, protocolVersion);
|
||||
|
||||
this.argumentSignatures = new ArgumentSignatures();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -26,6 +26,7 @@ import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import com.velocitypowered.api.util.ProxyVersion;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
||||
import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket;
|
||||
@ -33,7 +34,6 @@ import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -97,10 +97,12 @@ public final class PluginMessageUtil {
|
||||
/**
|
||||
* Fetches all the channels in a register or unregister plugin message.
|
||||
*
|
||||
* @param existingChannels the number of channels already registered
|
||||
* @param message the message to get the channels from
|
||||
* @return the channels, as an immutable list
|
||||
*/
|
||||
public static List<ChannelIdentifier> getChannels(PluginMessagePacket message,
|
||||
public static List<ChannelIdentifier> getChannels(int existingChannels,
|
||||
PluginMessagePacket message,
|
||||
ProtocolVersion protocolVersion) {
|
||||
checkNotNull(message, "message");
|
||||
checkArgument(isRegister(message) || isUnregister(message), "Unknown channel type %s",
|
||||
@ -111,8 +113,11 @@ public final class PluginMessageUtil {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
String payload = message.content().toString(StandardCharsets.UTF_8);
|
||||
checkArgument(payload.length() <= Short.MAX_VALUE, "payload too long: %s", payload.length());
|
||||
String[] channels = payload.split("\0");
|
||||
List<ChannelIdentifier> channelIdentifiers = new ArrayList<>();
|
||||
checkArgument(existingChannels + channels.length <= ConnectedPlayer.MAX_CLIENTSIDE_PLUGIN_CHANNELS,
|
||||
"too many channels: %s + %s > %s", existingChannels, channels.length, ConnectedPlayer.MAX_CLIENTSIDE_PLUGIN_CHANNELS);
|
||||
ImmutableList.Builder<ChannelIdentifier> channelIdentifiers = ImmutableList.builderWithExpectedSize(channels.length);
|
||||
try {
|
||||
for (String channel : channels) {
|
||||
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_13)) {
|
||||
@ -128,7 +133,7 @@ public final class PluginMessageUtil {
|
||||
throw ILLEGAL_CHANNEL;
|
||||
}
|
||||
}
|
||||
return ImmutableList.copyOf(channelIdentifiers);
|
||||
return channelIdentifiers.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
9
steamwarci.yml
Normal file
9
steamwarci.yml
Normal file
@ -0,0 +1,9 @@
|
||||
build:
|
||||
- "./gradlew build -x check -x javadoc --no-daemon"
|
||||
|
||||
|
||||
artifacts:
|
||||
"/jars/Velocity.jar": "proxy/build/libs/velocity-proxy-3.4.0-SNAPSHOT-all.jar"
|
||||
|
||||
release:
|
||||
- "mvn deploy:deploy-file -DgroupId=de.steamwar -DartifactId=velocity -Dversion=RELEASE -Dpackaging=jar -Dfile=/jars/Velocity.jar -Durl=file:///var/www/maven/"
|
||||
Reference in New Issue
Block a user