Compare commits

..

35 Commits

Author SHA1 Message Date
b6e05cb0b9 Refactor TCP Fast Open checks and update message identifiers.
All checks were successful
SteamWarCI Build successful
Removed transport type conditions for TCP Fast Open to streamline configuration usage. Added imports for new message identifiers in `ClientPlaySessionHandler`. Cleaned up Netty library definitions in `libs.versions.toml`.
2025-04-27 20:09:05 +02:00
1507b91463 Merge remote-tracking branch 'upstream/dev/3.0.0' into update
# Conflicts:
#	proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java
2025-04-27 19:53:15 +02:00
bd2bb6325e Validate state transition 2025-04-17 18:59:49 +01:00
3f0a85d794 Fix MinecraftChannelIdentifier parsing to align with vanilla (#1552) 2025-04-14 13:52:02 -04:00
74d05211d6 Also validate length before caring to invest time into processing 2025-04-12 16:52:31 +01:00
7ad06614fe Appease checkstyle gods 2025-04-12 16:22:06 +01:00
163a85a468 Merge branch 'cleanup/plugin-message-channel-handling' into dev/3.0.0 2025-04-12 16:20:21 +01:00
a51711e4bb Use an ImmutableList Builder 2025-04-12 16:20:07 +01:00
ae312339a3 Disable io_uring transport by default 2025-04-11 00:35:49 -04:00
a429bb53ce Merge remote-tracking branch 'origin/dev/3.0.0' into cleanup/plugin-message-channel-handling 2025-04-09 10:06:40 +01:00
a549880df1 Bump to Netty 4.2.0 (#1380) 2025-04-09 01:21:08 -04:00
b06af3718c Merge remote-tracking branch 'github/dev/3.0.0'
All checks were successful
SteamWarCI Build successful
# Conflicts:
#	proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java
2025-04-01 07:06:14 +02:00
a20a896582 Skip javadoc generation
All checks were successful
SteamWarCI Build successful
2025-01-22 09:37:30 +01:00
e1a3421212 Adapt to new server
Some checks failed
SteamWarCI Build failed
2025-01-22 09:33:59 +01:00
19e51a2b12 Merge remote-tracking branch 'upstream/dev/3.0.0' 2024-12-06 11:14:27 +01:00
b89a5c5ce9 Fix CI 2024-12-02 12:45:04 +01:00
65d3277319 Merge remote-tracking branch 'upstream/dev/3.0.0' 2024-11-30 09:25:25 +01:00
a22bfa10f9 Merge pull request 'Update 1.21.2 client support' (#5) from upstream into master
Reviewed-on: https://steamwar.de/devlabs/SteamWar/Velocity/pulls/5
2024-11-11 08:15:32 +01:00
d9d1319a3a Merge remote-tracking branch 'upstream/dev/3.0.0' into upstream
# Conflicts:
#	proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java
2024-11-10 18:32:48 +01:00
15ecbf4345 Merge pull request 'Update Velocity (might fix Command problems) and fix PluginMessages...' (#4) from fix-pluginmessages into master
Reviewed-on: https://steamwar.de/devlabs/SteamWar/Velocity/pulls/4
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2024-08-20 08:14:11 +02:00
5e3bbcd427 Fix command signature issues. 2024-08-20 08:03:18 +02:00
a6c79db07b Remove filter checks to receive PluginMessages unfiltered. 2024-08-18 15:32:08 +02:00
6e33bc6c17 Merge remote-tracking branch 'refs/remotes/upstream/dev/3.0.0' 2024-08-18 15:28:10 +02:00
01208bb359 Indicate NoChatReports support in ServerPing 2024-06-24 18:36:32 +02:00
fa88aaae52 Always unsign chat. 2024-06-21 12:48:58 +02:00
2da400a267 Merge pull request 'Implement Velocity PRs #998 #1246 and #1309 (io_uring, tcp_fastopen and PluginMessage race condition fix)' (#3) from io_uring into master
Reviewed-on: https://steamwar.de/devlabs/SteamWar/Velocity/pulls/3
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2024-06-19 12:06:10 +02:00
8103135dfb Fix type 2024-06-19 10:21:36 +02:00
cfabff7288 Implement Velocity PRs #998 #1246 and #1309 (io_uring, tcp_fastopen and PluginMessage race condition fix) 2024-06-19 10:11:32 +02:00
2f5a27a708 Fix CI 2024-06-19 09:47:18 +02:00
fdfe8bcc4b Fix CI 2024-06-19 09:40:58 +02:00
a19fd8db74 Add UpdateTeamsPacket 2024-06-16 21:27:13 +02:00
e63d71423d Add UpdateTeamsPacket 2024-06-16 21:24:06 +02:00
a7afe35fab Rebuild 2024-06-16 13:25:07 +02:00
56d6339313 Fix JVM 2024-06-16 13:18:29 +02:00
2475572573 Add steamwarci.yml 2024-06-16 12:52:43 +02:00
18 changed files with 374 additions and 98 deletions

View File

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

View File

@ -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) {

View File

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

View File

@ -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"

View File

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

View File

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

View File

@ -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(

View File

@ -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,26 +341,9 @@ 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);
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(),
@ -374,7 +363,6 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
}
}
}
}
return true;
}
@ -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(() -> {

View File

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

View File

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

View File

@ -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 {

View File

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

View File

@ -116,6 +116,8 @@ public class KeyedPlayerChatPacket implements MinecraftPacket {
ProtocolUtils.readByteArray(buf));
}
}
unsigned = true;
}
@Override

View File

@ -132,6 +132,7 @@ public class KeyedPlayerCommandPacket implements MinecraftPacket {
unsigned = true;
}
unsigned = true;
}
@Override

View File

@ -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];

View File

@ -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

View File

@ -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
View 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/"