Compare commits

..

26 Commits

Author SHA1 Message Date
19e51a2b12 Merge remote-tracking branch 'upstream/dev/3.0.0' 2024-12-06 11:14:27 +01:00
965db127a9 InvocationInfo API (#1467)
* Invocation Source API

Allows proxies to detect if a command is executed from an unsigned/signed/api source.

This is useful because it allows commands executed from the player manually or by clicking on a chat message to be controlled.

* Update api significantly to improve api coverage

* javadoc

* javadoc

* Update api/src/main/java/com/velocitypowered/api/event/command/CommandExecuteEvent.java

Co-authored-by: powercas_gamer <cas@mizule.dev>

* Update api/src/main/java/com/velocitypowered/api/event/command/CommandExecuteEvent.java

Co-authored-by: powercas_gamer <cas@mizule.dev>

* Fix rename

---------

Co-authored-by: powercas_gamer <cas@mizule.dev>
2024-12-04 09:36:45 -08:00
be5f0ace26 Minecraft 1.21.4 (#1457) 2024-12-03 15:07:26 +01:00
b89a5c5ce9 Fix CI 2024-12-02 12:45:04 +01:00
a33f2d1af5 Fix callback command not working (#1464)
Broken in 2016d1482f (diff-1217e64751ab6522195a858620cceeb4f2d2b6506c36dd4e396726ad3e7ef0cb)
2024-12-01 10:31:44 +00:00
65d3277319 Merge remote-tracking branch 'upstream/dev/3.0.0' 2024-11-30 09:25:25 +01:00
9cfcfcf2ed Fix SystemChatPacket: Support reading packet properly in newer MC versions (#1461) 2024-11-19 17:49:17 +00: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
cefa3b272e feat: expose list order in TabListEntry (#1451)
* feat: expose list order in TabListEntry

* fix: address comment (from github)

* fix: address another comment (from github)
2024-11-09 18:03:34 -05: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
30 changed files with 583 additions and 84 deletions

View File

@ -26,6 +26,7 @@ public final class CommandExecuteEvent implements ResultedEvent<CommandResult> {
private final CommandSource commandSource; private final CommandSource commandSource;
private final String command; private final String command;
private CommandResult result; private CommandResult result;
private InvocationInfo invocationInfo;
/** /**
* Constructs a CommandExecuteEvent. * Constructs a CommandExecuteEvent.
@ -34,9 +35,21 @@ public final class CommandExecuteEvent implements ResultedEvent<CommandResult> {
* @param command the command being executed without first slash * @param command the command being executed without first slash
*/ */
public CommandExecuteEvent(CommandSource commandSource, String command) { public CommandExecuteEvent(CommandSource commandSource, String command) {
this(commandSource, command, new InvocationInfo(SignedState.UNSUPPORTED, Source.API));
}
/**
* Constructs a CommandExecuteEvent.
*
* @param commandSource the source executing the command
* @param command the command being executed without first slash
* @param invocationInfo the invocation info of this command
*/
public CommandExecuteEvent(CommandSource commandSource, String command, InvocationInfo invocationInfo) {
this.commandSource = Preconditions.checkNotNull(commandSource, "commandSource"); this.commandSource = Preconditions.checkNotNull(commandSource, "commandSource");
this.command = Preconditions.checkNotNull(command, "command"); this.command = Preconditions.checkNotNull(command, "command");
this.result = CommandResult.allowed(); this.result = CommandResult.allowed();
this.invocationInfo = invocationInfo;
} }
/** /**
@ -61,6 +74,16 @@ public final class CommandExecuteEvent implements ResultedEvent<CommandResult> {
return command; return command;
} }
/**
* Returns the info of the command invocation.
*
* @since 3.4.0
* @return invocation info
*/
public InvocationInfo getInvocationInfo() {
return this.invocationInfo;
}
@Override @Override
public CommandResult getResult() { public CommandResult getResult() {
return result; return result;
@ -80,6 +103,75 @@ public final class CommandExecuteEvent implements ResultedEvent<CommandResult> {
+ '}'; + '}';
} }
/**
* Represents information about a command invocation, including its signed state and source.
*
* @since 3.4.0
*/
public record InvocationInfo(SignedState signedState, Source source) {
}
/**
* Represents the signed state of a command invocation.
*
* @since 3.4.0
*/
public enum SignedState {
/**
* Indicates that the command was executed from a signed source with signed message arguments,
* This is currently only possible by typing a command in chat with signed arguments.
*
* <p><b>Note:</b> Cancelling the {@link CommandExecuteEvent} in this state will result in the player being kicked.</p>
*
* @since 3.4.0
*/
SIGNED_WITH_ARGS,
/**
* Indicates that the command was executed from an signed source with no signed message arguments,
* This is currently only possible by typing a command in chat.
*
* @since 3.4.0
*/
SIGNED_WITHOUT_ARGS,
/**
* Indicates that the command was executed from an unsigned source,
* such as clicking on a component with a {@link net.kyori.adventure.text.event.ClickEvent.Action#RUN_COMMAND}.
*
* <p>Clients running version 1.20.5 or later will send this state.</p>
*
* @since 3.4.0
*/
UNSIGNED,
/**
* Indicates that the command invocation does not support signing.
*
* <p>This state is sent by clients running versions prior to 1.19.3.</p>
*
* @since 3.4.0
*/
UNSUPPORTED
}
/**
* Represents the source of a command invocation.
*
* @since 3.4.0
*/
public enum Source {
/**
* Indicates that the command was invoked by a player.
*
* @since 3.4.0
*/
PLAYER,
/**
* Indicates that the command was invoked programmatically through an API call.
*
* @since 3.4.0
*/
API
}
/** /**
* Represents the result of the {@link CommandExecuteEvent}. * Represents the result of the {@link CommandExecuteEvent}.
*/ */

View File

@ -88,7 +88,8 @@ public enum ProtocolVersion implements Ordered<ProtocolVersion> {
MINECRAFT_1_20_3(765, "1.20.3", "1.20.4"), MINECRAFT_1_20_3(765, "1.20.3", "1.20.4"),
MINECRAFT_1_20_5(766, "1.20.5", "1.20.6"), MINECRAFT_1_20_5(766, "1.20.5", "1.20.6"),
MINECRAFT_1_21(767, "1.21", "1.21.1"), MINECRAFT_1_21(767, "1.21", "1.21.1"),
MINECRAFT_1_21_2(768, "1.21.2", "1.21.3"); MINECRAFT_1_21_2(768, "1.21.2", "1.21.3"),
MINECRAFT_1_21_4(769, "1.21.4");
private static final int SNAPSHOT_BIT = 30; private static final int SNAPSHOT_BIT = 30;

View File

@ -168,6 +168,25 @@ public interface TabList {
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead. * @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
*/ */
@Deprecated @Deprecated
default TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
int gameMode, @Nullable ChatSession chatSession, boolean listed) {
return buildEntry(profile, displayName, latency, gameMode, chatSession, listed, 0);
}
/**
* Represents an entry in a {@link Player}'s tab list.
*
* @param profile the profile
* @param displayName the display name
* @param latency the latency
* @param gameMode the game mode
* @param chatSession the chat session
* @param listed the visible status of entry
* @param listOrder the order/priority of entry in the tab list
* @return the entry
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
*/
@Deprecated
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
int gameMode, @Nullable ChatSession chatSession, boolean listed); int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder);
} }

View File

@ -139,6 +139,27 @@ public interface TabListEntry extends KeyIdentifiable {
return this; return this;
} }
/**
* Returns the order/priority of this entry in the tab list.
*
* @return order of this entry
* @sinceMinecraft 1.21.2
*/
default int getListOrder() {
return 0;
}
/**
* Sets the order/priority of this entry in the tab list.
*
* @param order order of this entry
* @return {@code this}, for chaining
* @sinceMinecraft 1.21.2
*/
default TabListEntry setListOrder(int order) {
return this;
}
/** /**
* Returns a {@link Builder} to create a {@link TabListEntry}. * Returns a {@link Builder} to create a {@link TabListEntry}.
* *
@ -161,6 +182,7 @@ public interface TabListEntry extends KeyIdentifiable {
private int latency = 0; private int latency = 0;
private int gameMode = 0; private int gameMode = 0;
private boolean listed = true; private boolean listed = true;
private int listOrder = 0;
private @Nullable ChatSession chatSession; private @Nullable ChatSession chatSession;
@ -243,7 +265,7 @@ public interface TabListEntry extends KeyIdentifiable {
} }
/** /**
* Sets wether this entry should be visible. * Sets whether this entry should be visible.
* *
* @param listed to set * @param listed to set
* @return ${code this}, for chaining * @return ${code this}, for chaining
@ -254,6 +276,19 @@ public interface TabListEntry extends KeyIdentifiable {
return this; return this;
} }
/**
* Sets the order/priority of this entry in the tab list.
*
* @param order to set
* @return ${code this}, for chaining
* @sinceMinecraft 1.21.2
* @see TabListEntry#getListOrder()
*/
public Builder listOrder(int order) {
this.listOrder = order;
return this;
}
/** /**
* Constructs the {@link TabListEntry} specified by {@code this} {@link Builder}. * Constructs the {@link TabListEntry} specified by {@code this} {@link Builder}.
* *
@ -266,7 +301,7 @@ public interface TabListEntry extends KeyIdentifiable {
if (profile == null) { if (profile == null) {
throw new IllegalStateException("The GameProfile must be set when building a TabListEntry"); throw new IllegalStateException("The GameProfile must be set when building a TabListEntry");
} }
return tabList.buildEntry(profile, displayName, latency, gameMode, chatSession, listed); return tabList.buildEntry(profile, displayName, latency, gameMode, chatSession, listed, listOrder);
} }
} }
} }

View File

@ -30,6 +30,7 @@ public final class ServerPing {
private final net.kyori.adventure.text.Component description; private final net.kyori.adventure.text.Component description;
private final @Nullable Favicon favicon; private final @Nullable Favicon favicon;
private final @Nullable ModInfo modinfo; private final @Nullable ModInfo modinfo;
private final boolean preventsChatReports = true;
public ServerPing(Version version, @Nullable Players players, public ServerPing(Version version, @Nullable Players players,
net.kyori.adventure.text.Component description, @Nullable Favicon favicon) { net.kyori.adventure.text.Component description, @Nullable Favicon favicon) {

View File

@ -53,6 +53,7 @@ netty-codec-haproxy = { module = "io.netty:netty-codec-haproxy", version.ref = "
netty-codec-http = { module = "io.netty:netty-codec-http", version.ref = "netty" } netty-codec-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
netty-handler = { module = "io.netty:netty-handler", 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-epoll = { module = "io.netty:netty-transport-native-epoll", version.ref = "netty" }
netty-transport-native-iouring = { module = "io.netty.incubator:netty-incubator-transport-native-io_uring", version = "0.0.25.Final" }
netty-transport-native-kqueue = { module = "io.netty:netty-transport-native-kqueue", version.ref = "netty" } netty-transport-native-kqueue = { module = "io.netty:netty-transport-native-kqueue", version.ref = "netty" }
nightconfig = "com.electronwill.night-config:toml:3.6.7" nightconfig = "com.electronwill.night-config:toml:3.6.7"
slf4j = "org.slf4j:slf4j-api:2.0.12" slf4j = "org.slf4j:slf4j-api:2.0.12"

View File

@ -117,6 +117,9 @@ dependencies {
implementation(libs.netty.transport.native.epoll) 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-x86_64") })
implementation(variantOf(libs.netty.transport.native.epoll) { classifier("linux-aarch_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(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-x86_64") })
implementation(variantOf(libs.netty.transport.native.kqueue) { classifier("osx-aarch_64") }) implementation(variantOf(libs.netty.transport.native.kqueue) { classifier("osx-aarch_64") })

View File

@ -253,7 +253,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
commandManager.metaBuilder(callbackCommand) commandManager.metaBuilder(callbackCommand)
.plugin(VelocityVirtualPlugin.INSTANCE) .plugin(VelocityVirtualPlugin.INSTANCE)
.build(), .build(),
velocityParentCommand callbackCommand
); );
final BrigadierCommand serverCommand = ServerCommand.create(this); final BrigadierCommand serverCommand = ServerCommand.create(this);
commandManager.register( commandManager.register(

View File

@ -218,13 +218,14 @@ public class VelocityCommandManager implements CommandManager {
* *
* @param source the source to execute the command for * @param source the source to execute the command for
* @param cmdLine the command to execute * @param cmdLine the command to execute
* @param invocationInfo the invocation info
* @return the {@link CompletableFuture} of the event * @return the {@link CompletableFuture} of the event
*/ */
public CompletableFuture<CommandExecuteEvent> callCommandEvent(final CommandSource source, public CompletableFuture<CommandExecuteEvent> callCommandEvent(final CommandSource source,
final String cmdLine) { final String cmdLine, final CommandExecuteEvent.InvocationInfo invocationInfo) {
Preconditions.checkNotNull(source, "source"); Preconditions.checkNotNull(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine"); Preconditions.checkNotNull(cmdLine, "cmdLine");
return eventManager.fire(new CommandExecuteEvent(source, cmdLine)); return eventManager.fire(new CommandExecuteEvent(source, cmdLine, invocationInfo));
} }
private boolean executeImmediately0(final CommandSource source, final ParseResults<CommandSource> parsed) { private boolean executeImmediately0(final CommandSource source, final ParseResults<CommandSource> parsed) {
@ -266,7 +267,12 @@ public class VelocityCommandManager implements CommandManager {
Preconditions.checkNotNull(source, "source"); Preconditions.checkNotNull(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine"); Preconditions.checkNotNull(cmdLine, "cmdLine");
return callCommandEvent(source, cmdLine).thenComposeAsync(event -> { CommandExecuteEvent.InvocationInfo invocationInfo = new CommandExecuteEvent.InvocationInfo(
CommandExecuteEvent.SignedState.UNSUPPORTED,
CommandExecuteEvent.Source.API
);
return callCommandEvent(source, cmdLine, invocationInfo).thenComposeAsync(event -> {
CommandExecuteEvent.CommandResult commandResult = event.getResult(); CommandExecuteEvent.CommandResult commandResult = event.getResult();
if (commandResult.isForwardToServer() || !commandResult.isAllowed()) { if (commandResult.isForwardToServer() || !commandResult.isAllowed()) {
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);

View File

@ -34,6 +34,8 @@ import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
import com.velocitypowered.api.event.proxy.ProxyPingEvent; import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; 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.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.command.CommandGraphInjector; import com.velocitypowered.proxy.command.CommandGraphInjector;
@ -288,31 +290,14 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
return true; 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)) { if (serverConn.getPhase().handle(serverConn, serverConn.getPlayer(), packet)) {
// Handled. // Handled.
return true; return true;
} }
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
if (id == null) {
return false;
}
byte[] copy = ByteBufUtil.getBytes(packet.content()); 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 -> { server.getEventManager().fire(event).thenAcceptAsync(pme -> {
if (pme.getResult().isAllowed() && !playerConnection.isClosed()) { if (pme.getResult().isAllowed() && !playerConnection.isClosed()) {
PluginMessagePacket copied = new PluginMessagePacket( PluginMessagePacket copied = new PluginMessagePacket(

View File

@ -340,43 +340,25 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
} }
if (!player.getPhase().handle(player, packet, serverConn)) { if (!player.getPhase().handle(player, packet, serverConn)) {
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel()); byte[] copy = ByteBufUtil.getBytes(packet.content());
if (id == null) { String channel = packet.getChannel();
// We don't have any plugins listening on this channel, process the packet now. PluginMessageEvent event = new PluginMessageEvent(player, serverConn, channel.indexOf(':') == -1 ? new LegacyChannelIdentifier(channel) : MinecraftChannelIdentifier.from(channel), copy);
if (!player.getPhase().consideredComplete() || !serverConn.getPhase() server.getEventManager().fire(event).thenAcceptAsync(pme -> {
.consideredComplete()) { if (pme.getResult().isAllowed()) {
// The client is trying to send messages too early. This is primarily caused by mods, PluginMessagePacket message = new PluginMessagePacket(packet.getChannel(),
// but further aggravated by Velocity. To work around these issues, we will queue any Unpooled.wrappedBuffer(copy));
// non-FML handshake messages to be sent once the FML handshake has completed or the if (!player.getPhase().consideredComplete() || !serverConn.getPhase()
// JoinGame packet has been received by the proxy, whichever comes first. .consideredComplete()) {
// // We're still processing the connection (see above), enqueue the packet for now.
// We also need to make sure to retain these packets, so they can be flushed loginPluginMessages.add(message.retain());
// appropriately. } else {
loginPluginMessages.add(packet.retain()); backendConn.write(message);
} 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);
}
} }
}, backendConn.eventLoop()).exceptionally((ex) -> { }
logger.error("Exception while handling plugin message packet for {}", player, ex); }, backendConn.eventLoop()).exceptionally((ex) -> {
return null; logger.error("Exception while handling plugin message packet for {}", player, ex);
}); return null;
} });
} }
} }
} }

View File

@ -100,7 +100,7 @@ public final class ConnectionManager {
.childOption(ChannelOption.IP_TOS, 0x18) .childOption(ChannelOption.IP_TOS, 0x18)
.localAddress(address); .localAddress(address);
if (server.getConfiguration().useTcpFastOpen()) { if (transportType.supportsTcpFastOpenServer() && server.getConfiguration().useTcpFastOpen()) {
bootstrap.option(ChannelOption.TCP_FASTOPEN, 3); bootstrap.option(ChannelOption.TCP_FASTOPEN, 3);
} }
@ -169,7 +169,7 @@ public final class ConnectionManager {
this.server.getConfiguration().getConnectTimeout()) this.server.getConfiguration().getConnectTimeout())
.group(group == null ? this.workerGroup : group) .group(group == null ? this.workerGroup : group)
.resolver(this.resolver.asGroup()); .resolver(this.resolver.asGroup());
if (server.getConfiguration().useTcpFastOpen()) { if (transportType.supportsTcpFastOpenClient() && server.getConfiguration().useTcpFastOpen()) {
bootstrap.option(ChannelOption.TCP_FASTOPEN_CONNECT, true); bootstrap.option(ChannelOption.TCP_FASTOPEN_CONNECT, true);
} }
return bootstrap; return bootstrap;

View File

@ -37,6 +37,8 @@ import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.incubator.channel.uring.*;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -47,32 +49,50 @@ public enum TransportType {
NIO("NIO", NioServerSocketChannel::new, NIO("NIO", NioServerSocketChannel::new,
NioSocketChannel::new, NioSocketChannel::new,
NioDatagramChannel::new, NioDatagramChannel::new,
(name, type) -> new NioEventLoopGroup(0, createThreadFactory(name, type))), (name, type) -> new NioEventLoopGroup(0, createThreadFactory(name, type)),
false,
false),
EPOLL("epoll", EpollServerSocketChannel::new, EPOLL("epoll", EpollServerSocketChannel::new,
EpollSocketChannel::new, EpollSocketChannel::new,
EpollDatagramChannel::new, EpollDatagramChannel::new,
(name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type))), (name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type)),
Epoll.isTcpFastOpenServerSideAvailable(),
Epoll.isTcpFastOpenClientSideAvailable()),
IO_URING("io_uring", IOUringServerSocketChannel::new,
IOUringSocketChannel::new,
IOUringDatagramChannel::new,
(name, type) -> new IOUringEventLoopGroup(0, createThreadFactory(name, type)),
IOUring.isTcpFastOpenServerSideAvailable(),
IOUring.isTcpFastOpenClientSideAvailable()),
KQUEUE("kqueue", KQueueServerSocketChannel::new, KQUEUE("kqueue", KQueueServerSocketChannel::new,
KQueueSocketChannel::new, KQueueSocketChannel::new,
KQueueDatagramChannel::new, KQueueDatagramChannel::new,
(name, type) -> new KQueueEventLoopGroup(0, createThreadFactory(name, type))); (name, type) -> new KQueueEventLoopGroup(0, createThreadFactory(name, type)),
KQueue.isTcpFastOpenServerSideAvailable(),
KQueue.isTcpFastOpenClientSideAvailable());
final String name; final String name;
final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory; final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory;
final ChannelFactory<? extends SocketChannel> socketChannelFactory; final ChannelFactory<? extends SocketChannel> socketChannelFactory;
final ChannelFactory<? extends DatagramChannel> datagramChannelFactory; final ChannelFactory<? extends DatagramChannel> datagramChannelFactory;
final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory; final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory;
final boolean supportsTcpFastOpenServer;
final boolean supportsTcpFastOpenClient;
TransportType(final String name, TransportType(final String name,
final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory, final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory,
final ChannelFactory<? extends SocketChannel> socketChannelFactory, final ChannelFactory<? extends SocketChannel> socketChannelFactory,
final ChannelFactory<? extends DatagramChannel> datagramChannelFactory, final ChannelFactory<? extends DatagramChannel> datagramChannelFactory,
final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory) { final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory,
final boolean supportsTcpFastOpenServer,
final boolean supportsTcpFastOpenClient) {
this.name = name; this.name = name;
this.serverSocketChannelFactory = serverSocketChannelFactory; this.serverSocketChannelFactory = serverSocketChannelFactory;
this.socketChannelFactory = socketChannelFactory; this.socketChannelFactory = socketChannelFactory;
this.datagramChannelFactory = datagramChannelFactory; this.datagramChannelFactory = datagramChannelFactory;
this.eventLoopGroupFactory = eventLoopGroupFactory; this.eventLoopGroupFactory = eventLoopGroupFactory;
this.supportsTcpFastOpenServer = supportsTcpFastOpenServer;
this.supportsTcpFastOpenClient = supportsTcpFastOpenClient;
} }
@Override @Override
@ -84,6 +104,14 @@ public enum TransportType {
return this.eventLoopGroupFactory.apply(this.name, type); return this.eventLoopGroupFactory.apply(this.name, type);
} }
public boolean supportsTcpFastOpenServer() {
return supportsTcpFastOpenServer;
}
public boolean supportsTcpFastOpenClient() {
return supportsTcpFastOpenClient;
}
private static ThreadFactory createThreadFactory(final String name, final Type type) { private static ThreadFactory createThreadFactory(final String name, final Type type) {
return new VelocityNettyThreadFactory("Netty " + name + ' ' + type.toString() + " #%d"); return new VelocityNettyThreadFactory("Netty " + name + ' ' + type.toString() + " #%d");
} }
@ -98,6 +126,10 @@ public enum TransportType {
return NIO; return NIO;
} }
if(IOUring.isAvailable()) {
return IO_URING;
}
if (Epoll.isAvailable()) { if (Epoll.isAvailable()) {
return EPOLL; return EPOLL;
} }

View File

@ -38,6 +38,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_3;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_5; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_5;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_4;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
@ -85,6 +86,7 @@ import com.velocitypowered.proxy.protocol.packet.StatusResponsePacket;
import com.velocitypowered.proxy.protocol.packet.TabCompleteRequestPacket; import com.velocitypowered.proxy.protocol.packet.TabCompleteRequestPacket;
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponsePacket; import com.velocitypowered.proxy.protocol.packet.TabCompleteResponsePacket;
import com.velocitypowered.proxy.protocol.packet.TransferPacket; 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.UpsertPlayerInfoPacket;
import com.velocitypowered.proxy.protocol.packet.chat.ChatAcknowledgementPacket; import com.velocitypowered.proxy.protocol.packet.chat.ChatAcknowledgementPacket;
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletionPacket; import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletionPacket;
@ -358,7 +360,8 @@ public enum StateRegistry {
map(0x27, MINECRAFT_1_20_2, false), map(0x27, MINECRAFT_1_20_2, false),
map(0x28, MINECRAFT_1_20_3, false), map(0x28, MINECRAFT_1_20_3, false),
map(0x2B, MINECRAFT_1_20_5, false), map(0x2B, MINECRAFT_1_20_5, false),
map(0x2D, MINECRAFT_1_21_2, false)); map(0x2D, MINECRAFT_1_21_2, false),
map(0x2F, MINECRAFT_1_21_4, false));
serverbound.register( serverbound.register(
FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE, FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE,
map(0x0B, MINECRAFT_1_20_2, false), map(0x0B, MINECRAFT_1_20_2, false),
@ -704,6 +707,22 @@ public enum StateRegistry {
ClientboundServerLinksPacket::new, ClientboundServerLinksPacket::new,
map(0x7B, MINECRAFT_1_21, false), map(0x7B, MINECRAFT_1_21, false),
map(0x82, MINECRAFT_1_21_2, 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 { 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

@ -193,6 +193,11 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
info.listOrder = ProtocolUtils.readVarInt(buf); info.listOrder = ProtocolUtils.readVarInt(buf);
}, (version, buf, info) -> { // write }, (version, buf, info) -> { // write
ProtocolUtils.writeVarInt(buf, info.listOrder); ProtocolUtils.writeVarInt(buf, info.listOrder);
}),
UPDATE_HAT((version, buf, info) -> { // read
info.showHat = buf.readBoolean();
}, (version, buf, info) -> { // write
buf.writeBoolean(info.showHat);
}); });
private final Read read; private final Read read;
@ -223,6 +228,7 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
private int gameMode; private int gameMode;
@Nullable @Nullable
private ComponentHolder displayName; private ComponentHolder displayName;
private boolean showHat;
private int listOrder; private int listOrder;
@Nullable @Nullable
private RemoteChatSession chatSession; private RemoteChatSession chatSession;
@ -256,6 +262,10 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
return displayName; return displayName;
} }
public boolean isShowHat() {
return showHat;
}
public int getListOrder() { public int getListOrder() {
return listOrder; return listOrder;
} }
@ -285,6 +295,10 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
this.displayName = displayName; this.displayName = displayName;
} }
public void setShowHat(boolean showHat) {
this.showHat = showHat;
}
public void setListOrder(int listOrder) { public void setListOrder(int listOrder) {
this.listOrder = listOrder; this.listOrder = listOrder;
} }

View File

@ -56,8 +56,10 @@ public interface CommandHandler<T extends MinecraftPacket> {
default void queueCommandResult(VelocityServer server, ConnectedPlayer player, default void queueCommandResult(VelocityServer server, ConnectedPlayer player,
BiFunction<CommandExecuteEvent, LastSeenMessages, CompletableFuture<MinecraftPacket>> futurePacketCreator, BiFunction<CommandExecuteEvent, LastSeenMessages, CompletableFuture<MinecraftPacket>> futurePacketCreator,
String message, Instant timestamp, @Nullable LastSeenMessages lastSeenMessages) { String message, Instant timestamp, @Nullable LastSeenMessages lastSeenMessages,
CompletableFuture<CommandExecuteEvent> eventFuture = server.getCommandManager().callCommandEvent(player, message); CommandExecuteEvent.InvocationInfo invocationInfo) {
CompletableFuture<CommandExecuteEvent> eventFuture = server.getCommandManager().callCommandEvent(player, message,
invocationInfo);
player.getChatQueue().queuePacket( player.getChatQueue().queuePacket(
newLastSeenMessages -> eventFuture newLastSeenMessages -> eventFuture
.thenComposeAsync(event -> futurePacketCreator.apply(event, newLastSeenMessages)) .thenComposeAsync(event -> futurePacketCreator.apply(event, newLastSeenMessages))

View File

@ -47,8 +47,11 @@ public class SystemChatPacket implements MinecraftPacket {
@Override @Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
component = ComponentHolder.read(buf, version); component = ComponentHolder.read(buf, version);
// System chat is never decoded so this doesn't matter for now if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)){
type = ChatType.values()[ProtocolUtils.readVarInt(buf)]; type = buf.readBoolean() ? ChatType.GAME_INFO : ChatType.SYSTEM;
} else {
type = ChatType.values()[ProtocolUtils.readVarInt(buf)];
}
} }
@Override @Override

View File

@ -111,6 +111,6 @@ public class KeyedCommandHandler implements CommandHandler<KeyedPlayerCommandPac
} }
return null; return null;
}); });
}, packet.getCommand(), packet.getTimestamp(), null); }, packet.getCommand(), packet.getTimestamp(), null, new CommandExecuteEvent.InvocationInfo(CommandExecuteEvent.SignedState.UNSUPPORTED, CommandExecuteEvent.Source.PLAYER));
} }
} }

View File

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

View File

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

View File

@ -62,6 +62,6 @@ public class LegacyCommandHandler implements CommandHandler<LegacyChatPacket> {
} }
return null; return null;
}); });
}, command, Instant.now(), null); }, command, Instant.now(), null, new CommandExecuteEvent.InvocationInfo(CommandExecuteEvent.SignedState.UNSUPPORTED, CommandExecuteEvent.Source.PLAYER));
} }
} }

View File

@ -117,6 +117,7 @@ public class SessionCommandHandler implements CommandHandler<SessionPlayerComman
} }
return forwardCommand(fixedPacket, commandToRun); return forwardCommand(fixedPacket, commandToRun);
}); });
}, packet.command, packet.timeStamp, packet.lastSeenMessages); }, packet.command, packet.timeStamp, packet.lastSeenMessages,
new CommandExecuteEvent.InvocationInfo(packet.getEventSignedState(), CommandExecuteEvent.Source.PLAYER));
} }
} }

View File

@ -69,6 +69,7 @@ public class SessionPlayerChatPacket implements MinecraftPacket {
this.salt = buf.readLong(); this.salt = buf.readLong();
this.signed = buf.readBoolean(); this.signed = buf.readBoolean();
if (this.signed) { if (this.signed) {
this.signed = false;
this.signature = readMessageSignature(buf); this.signature = readMessageSignature(buf);
} else { } else {
this.signature = new byte[0]; this.signature = new byte[0];

View File

@ -18,6 +18,7 @@
package com.velocitypowered.proxy.protocol.packet.chat.session; package com.velocitypowered.proxy.protocol.packet.chat.session;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.velocitypowered.api.event.command.CommandExecuteEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
@ -45,6 +46,8 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
this.salt = buf.readLong(); this.salt = buf.readLong();
this.argumentSignatures = new ArgumentSignatures(buf); this.argumentSignatures = new ArgumentSignatures(buf);
this.lastSeenMessages = new LastSeenMessages(buf); this.lastSeenMessages = new LastSeenMessages(buf);
this.argumentSignatures = new ArgumentSignatures();
} }
@Override @Override
@ -68,6 +71,10 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
return !argumentSignatures.isEmpty(); return !argumentSignatures.isEmpty();
} }
public CommandExecuteEvent.SignedState getEventSignedState() {
return !this.argumentSignatures.isEmpty() ? CommandExecuteEvent.SignedState.SIGNED_WITH_ARGS : CommandExecuteEvent.SignedState.SIGNED_WITHOUT_ARGS;
}
@Override @Override
public boolean handle(MinecraftSessionHandler handler) { public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this); return handler.handle(this);

View File

@ -17,6 +17,7 @@
package com.velocitypowered.proxy.protocol.packet.chat.session; package com.velocitypowered.proxy.protocol.packet.chat.session;
import com.velocitypowered.api.event.command.CommandExecuteEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages; import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages;
@ -44,6 +45,11 @@ public class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket {
return false; return false;
} }
@Override
public CommandExecuteEvent.SignedState getEventSignedState() {
return CommandExecuteEvent.SignedState.UNSIGNED;
}
@Override @Override
public String toString() { public String toString() {
return "UnsignedPlayerCommandPacket{" + return "UnsignedPlayerCommandPacket{" +

View File

@ -159,12 +159,17 @@ public class KeyedVelocityTabList implements InternalTabList {
@Override @Override
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
int gameMode, int gameMode, @Nullable ChatSession chatSession, boolean listed) {
@Nullable ChatSession chatSession, boolean listed) {
return new KeyedVelocityTabListEntry(this, profile, displayName, latency, gameMode, return new KeyedVelocityTabListEntry(this, profile, displayName, latency, gameMode,
chatSession == null ? null : chatSession.getIdentifiedKey()); chatSession == null ? null : chatSession.getIdentifiedKey());
} }
@Override
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder) {
return buildEntry(profile, displayName, latency, gameMode, chatSession, listed);
}
@Override @Override
public void processLegacy(LegacyPlayerListItemPacket packet) { public void processLegacy(LegacyPlayerListItemPacket packet) {
// Packets are already forwarded on, so no need to do that here // Packets are already forwarded on, so no need to do that here

View File

@ -19,6 +19,7 @@ package com.velocitypowered.proxy.tablist;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.player.ChatSession; import com.velocitypowered.api.proxy.player.ChatSession;
import com.velocitypowered.api.proxy.player.TabListEntry; import com.velocitypowered.api.proxy.player.TabListEntry;
@ -89,7 +90,7 @@ public class VelocityTabList implements InternalTabList {
} else { } else {
entry = new VelocityTabListEntry(this, entry1.getProfile(), entry = new VelocityTabListEntry(this, entry1.getProfile(),
entry1.getDisplayNameComponent().orElse(null), entry1.getDisplayNameComponent().orElse(null),
entry1.getLatency(), entry1.getGameMode(), entry1.getChatSession(), entry1.isListed()); entry1.getLatency(), entry1.getGameMode(), entry1.getChatSession(), entry1.isListed(), entry1.getListOrder());
} }
EnumSet<UpsertPlayerInfoPacket.Action> actions = EnumSet EnumSet<UpsertPlayerInfoPacket.Action> actions = EnumSet
@ -128,6 +129,11 @@ public class VelocityTabList implements InternalTabList {
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LISTED); actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LISTED);
playerInfoEntry.setListed(entry.isListed()); playerInfoEntry.setListed(entry.isListed());
} }
if (!Objects.equals(previousEntry.getListOrder(), entry.getListOrder())
&& player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER);
playerInfoEntry.setListOrder(entry.getListOrder());
}
if (!Objects.equals(previousEntry.getChatSession(), entry.getChatSession())) { if (!Objects.equals(previousEntry.getChatSession(), entry.getChatSession())) {
ChatSession from = entry.getChatSession(); ChatSession from = entry.getChatSession();
if (from != null) { if (from != null) {
@ -162,6 +168,11 @@ public class VelocityTabList implements InternalTabList {
} }
playerInfoEntry.setLatency(entry.getLatency()); playerInfoEntry.setLatency(entry.getLatency());
playerInfoEntry.setListed(entry.isListed()); playerInfoEntry.setListed(entry.isListed());
if (entry.getListOrder() != 0
&& player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER);
playerInfoEntry.setListOrder(entry.getListOrder());
}
} }
return entry; return entry;
}); });
@ -207,9 +218,9 @@ public class VelocityTabList implements InternalTabList {
@Override @Override
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
int gameMode, int gameMode,
@Nullable ChatSession chatSession, boolean listed) { @Nullable ChatSession chatSession, boolean listed, int listOrder) {
return new VelocityTabListEntry(this, profile, displayName, latency, gameMode, chatSession, return new VelocityTabListEntry(this, profile, displayName, latency, gameMode, chatSession,
listed); listed, listOrder);
} }
@Override @Override
@ -246,7 +257,8 @@ public class VelocityTabList implements InternalTabList {
0, 0,
-1, -1,
null, null,
false false,
0
) )
); );
} else { } else {
@ -274,6 +286,9 @@ public class VelocityTabList implements InternalTabList {
if (actions.contains(UpsertPlayerInfoPacket.Action.UPDATE_LISTED)) { if (actions.contains(UpsertPlayerInfoPacket.Action.UPDATE_LISTED)) {
currentEntry.setListedWithoutUpdate(entry.isListed()); currentEntry.setListedWithoutUpdate(entry.isListed());
} }
if (actions.contains(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER)) {
currentEntry.setListOrderWithoutUpdate(entry.getListOrder());
}
} }
@Override @Override

View File

@ -17,6 +17,7 @@
package com.velocitypowered.proxy.tablist; package com.velocitypowered.proxy.tablist;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.player.ChatSession; import com.velocitypowered.api.proxy.player.ChatSession;
import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.player.TabList;
import com.velocitypowered.api.proxy.player.TabListEntry; import com.velocitypowered.api.proxy.player.TabListEntry;
@ -38,6 +39,7 @@ public class VelocityTabListEntry implements TabListEntry {
private int latency; private int latency;
private int gameMode; private int gameMode;
private boolean listed; private boolean listed;
private int listOrder;
private @Nullable ChatSession session; private @Nullable ChatSession session;
/** /**
@ -45,7 +47,7 @@ public class VelocityTabListEntry implements TabListEntry {
*/ */
public VelocityTabListEntry(VelocityTabList tabList, GameProfile profile, Component displayName, public VelocityTabListEntry(VelocityTabList tabList, GameProfile profile, Component displayName,
int latency, int latency,
int gameMode, @Nullable ChatSession session, boolean listed) { int gameMode, @Nullable ChatSession session, boolean listed, int listOrder) {
this.tabList = tabList; this.tabList = tabList;
this.profile = profile; this.profile = profile;
this.displayName = displayName; this.displayName = displayName;
@ -53,6 +55,7 @@ public class VelocityTabListEntry implements TabListEntry {
this.gameMode = gameMode; this.gameMode = gameMode;
this.session = session; this.session = session;
this.listed = listed; this.listed = listed;
this.listOrder = listOrder;
} }
@Override @Override
@ -150,4 +153,24 @@ public class VelocityTabListEntry implements TabListEntry {
void setListedWithoutUpdate(boolean listed) { void setListedWithoutUpdate(boolean listed) {
this.listed = listed; this.listed = listed;
} }
@Override
public int getListOrder() {
return listOrder;
}
@Override
public VelocityTabListEntry setListOrder(int listOrder) {
this.listOrder = listOrder;
if (tabList.getPlayer().getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
UpsertPlayerInfoPacket.Entry upsertEntry = this.tabList.createRawEntry(this);
upsertEntry.setListOrder(listOrder);
tabList.emitActionRaw(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER, upsertEntry);
}
return this;
}
void setListOrderWithoutUpdate(int listOrder) {
this.listOrder = listOrder;
}
} }

10
steamwarci.yml Normal file
View File

@ -0,0 +1,10 @@
build:
- "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ./gradlew build -x check"
- "./gradlew --stop"
artifacts:
"/binarys/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=proxy/build/libs/velocity-proxy-3.4.0-SNAPSHOT-all.jar -Durl=file:///var/www/html/maven/"