Compare commits
26 Commits
08a42b3723
...
19e51a2b12
| Author | SHA1 | Date | |
|---|---|---|---|
| 19e51a2b12 | |||
| 965db127a9 | |||
| be5f0ace26 | |||
| b89a5c5ce9 | |||
| a33f2d1af5 | |||
| 65d3277319 | |||
| 9cfcfcf2ed | |||
| a22bfa10f9 | |||
| d9d1319a3a | |||
| cefa3b272e | |||
| 15ecbf4345 | |||
| 5e3bbcd427 | |||
| a6c79db07b | |||
| 6e33bc6c17 | |||
| 01208bb359 | |||
| fa88aaae52 | |||
| 2da400a267 | |||
| 8103135dfb | |||
| cfabff7288 | |||
| 2f5a27a708 | |||
| fdfe8bcc4b | |||
| a19fd8db74 | |||
| e63d71423d | |||
| a7afe35fab | |||
| 56d6339313 | |||
| 2475572573 |
@ -26,6 +26,7 @@ public final class CommandExecuteEvent implements ResultedEvent<CommandResult> {
|
||||
private final CommandSource commandSource;
|
||||
private final String command;
|
||||
private CommandResult result;
|
||||
private InvocationInfo invocationInfo;
|
||||
|
||||
/**
|
||||
* Constructs a CommandExecuteEvent.
|
||||
@ -34,9 +35,21 @@ public final class CommandExecuteEvent implements ResultedEvent<CommandResult> {
|
||||
* @param command the command being executed without first slash
|
||||
*/
|
||||
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.command = Preconditions.checkNotNull(command, "command");
|
||||
this.result = CommandResult.allowed();
|
||||
this.invocationInfo = invocationInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,6 +74,16 @@ public final class CommandExecuteEvent implements ResultedEvent<CommandResult> {
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the info of the command invocation.
|
||||
*
|
||||
* @since 3.4.0
|
||||
* @return invocation info
|
||||
*/
|
||||
public InvocationInfo getInvocationInfo() {
|
||||
return this.invocationInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult getResult() {
|
||||
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}.
|
||||
*/
|
||||
|
||||
@ -88,7 +88,8 @@ public enum ProtocolVersion implements Ordered<ProtocolVersion> {
|
||||
MINECRAFT_1_20_3(765, "1.20.3", "1.20.4"),
|
||||
MINECRAFT_1_20_5(766, "1.20.5", "1.20.6"),
|
||||
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;
|
||||
|
||||
|
||||
@ -168,6 +168,25 @@ public interface TabList {
|
||||
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
|
||||
*/
|
||||
@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,
|
||||
int gameMode, @Nullable ChatSession chatSession, boolean listed);
|
||||
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder);
|
||||
}
|
||||
|
||||
@ -139,6 +139,27 @@ public interface TabListEntry extends KeyIdentifiable {
|
||||
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}.
|
||||
*
|
||||
@ -161,6 +182,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
||||
private int latency = 0;
|
||||
private int gameMode = 0;
|
||||
private boolean listed = true;
|
||||
private int listOrder = 0;
|
||||
|
||||
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
|
||||
* @return ${code this}, for chaining
|
||||
@ -254,6 +276,19 @@ public interface TabListEntry extends KeyIdentifiable {
|
||||
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}.
|
||||
*
|
||||
@ -266,7 +301,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
||||
if (profile == null) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +30,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) {
|
||||
|
||||
@ -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-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-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" }
|
||||
nightconfig = "com.electronwill.night-config:toml:3.6.7"
|
||||
slf4j = "org.slf4j:slf4j-api:2.0.12"
|
||||
|
||||
@ -117,6 +117,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") })
|
||||
|
||||
@ -253,7 +253,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
||||
commandManager.metaBuilder(callbackCommand)
|
||||
.plugin(VelocityVirtualPlugin.INSTANCE)
|
||||
.build(),
|
||||
velocityParentCommand
|
||||
callbackCommand
|
||||
);
|
||||
final BrigadierCommand serverCommand = ServerCommand.create(this);
|
||||
commandManager.register(
|
||||
|
||||
@ -218,13 +218,14 @@ public class VelocityCommandManager implements CommandManager {
|
||||
*
|
||||
* @param source the source to execute the command for
|
||||
* @param cmdLine the command to execute
|
||||
* @param invocationInfo the invocation info
|
||||
* @return the {@link CompletableFuture} of the event
|
||||
*/
|
||||
public CompletableFuture<CommandExecuteEvent> callCommandEvent(final CommandSource source,
|
||||
final String cmdLine) {
|
||||
final String cmdLine, final CommandExecuteEvent.InvocationInfo invocationInfo) {
|
||||
Preconditions.checkNotNull(source, "source");
|
||||
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) {
|
||||
@ -266,7 +267,12 @@ public class VelocityCommandManager implements CommandManager {
|
||||
Preconditions.checkNotNull(source, "source");
|
||||
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();
|
||||
if (commandResult.isForwardToServer() || !commandResult.isAllowed()) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -340,43 +340,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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ public final class ConnectionManager {
|
||||
.childOption(ChannelOption.IP_TOS, 0x18)
|
||||
.localAddress(address);
|
||||
|
||||
if (server.getConfiguration().useTcpFastOpen()) {
|
||||
if (transportType.supportsTcpFastOpenServer() && server.getConfiguration().useTcpFastOpen()) {
|
||||
bootstrap.option(ChannelOption.TCP_FASTOPEN, 3);
|
||||
}
|
||||
|
||||
@ -169,7 +169,7 @@ public final class ConnectionManager {
|
||||
this.server.getConfiguration().getConnectTimeout())
|
||||
.group(group == null ? this.workerGroup : group)
|
||||
.resolver(this.resolver.asGroup());
|
||||
if (server.getConfiguration().useTcpFastOpen()) {
|
||||
if (transportType.supportsTcpFastOpenClient() && server.getConfiguration().useTcpFastOpen()) {
|
||||
bootstrap.option(ChannelOption.TCP_FASTOPEN_CONNECT, true);
|
||||
}
|
||||
return bootstrap;
|
||||
|
||||
@ -37,6 +37,8 @@ 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.incubator.channel.uring.*;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@ -47,32 +49,50 @@ public enum TransportType {
|
||||
NIO("NIO", NioServerSocketChannel::new,
|
||||
NioSocketChannel::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,
|
||||
EpollSocketChannel::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,
|
||||
KQueueSocketChannel::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 ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory;
|
||||
final ChannelFactory<? extends SocketChannel> socketChannelFactory;
|
||||
final ChannelFactory<? extends DatagramChannel> datagramChannelFactory;
|
||||
final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory;
|
||||
final boolean supportsTcpFastOpenServer;
|
||||
final boolean supportsTcpFastOpenClient;
|
||||
|
||||
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 BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory,
|
||||
final boolean supportsTcpFastOpenServer,
|
||||
final boolean supportsTcpFastOpenClient) {
|
||||
this.name = name;
|
||||
this.serverSocketChannelFactory = serverSocketChannelFactory;
|
||||
this.socketChannelFactory = socketChannelFactory;
|
||||
this.datagramChannelFactory = datagramChannelFactory;
|
||||
this.eventLoopGroupFactory = eventLoopGroupFactory;
|
||||
this.supportsTcpFastOpenServer = supportsTcpFastOpenServer;
|
||||
this.supportsTcpFastOpenClient = supportsTcpFastOpenClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -84,6 +104,14 @@ public enum TransportType {
|
||||
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) {
|
||||
return new VelocityNettyThreadFactory("Netty " + name + ' ' + type.toString() + " #%d");
|
||||
}
|
||||
@ -98,6 +126,10 @@ public enum TransportType {
|
||||
return NIO;
|
||||
}
|
||||
|
||||
if(IOUring.isAvailable()) {
|
||||
return IO_URING;
|
||||
}
|
||||
|
||||
if (Epoll.isAvailable()) {
|
||||
return EPOLL;
|
||||
}
|
||||
|
||||
@ -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_21;
|
||||
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_8;
|
||||
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.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;
|
||||
@ -358,7 +360,8 @@ public enum StateRegistry {
|
||||
map(0x27, MINECRAFT_1_20_2, false),
|
||||
map(0x28, MINECRAFT_1_20_3, 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(
|
||||
FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE,
|
||||
map(0x0B, MINECRAFT_1_20_2, false),
|
||||
@ -704,6 +707,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;
|
||||
}
|
||||
}
|
||||
@ -193,6 +193,11 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
|
||||
info.listOrder = ProtocolUtils.readVarInt(buf);
|
||||
}, (version, buf, info) -> { // write
|
||||
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;
|
||||
@ -223,6 +228,7 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
|
||||
private int gameMode;
|
||||
@Nullable
|
||||
private ComponentHolder displayName;
|
||||
private boolean showHat;
|
||||
private int listOrder;
|
||||
@Nullable
|
||||
private RemoteChatSession chatSession;
|
||||
@ -256,6 +262,10 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public boolean isShowHat() {
|
||||
return showHat;
|
||||
}
|
||||
|
||||
public int getListOrder() {
|
||||
return listOrder;
|
||||
}
|
||||
@ -285,6 +295,10 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public void setShowHat(boolean showHat) {
|
||||
this.showHat = showHat;
|
||||
}
|
||||
|
||||
public void setListOrder(int listOrder) {
|
||||
this.listOrder = listOrder;
|
||||
}
|
||||
|
||||
@ -56,8 +56,10 @@ public interface CommandHandler<T extends MinecraftPacket> {
|
||||
|
||||
default void queueCommandResult(VelocityServer server, ConnectedPlayer player,
|
||||
BiFunction<CommandExecuteEvent, LastSeenMessages, CompletableFuture<MinecraftPacket>> futurePacketCreator,
|
||||
String message, Instant timestamp, @Nullable LastSeenMessages lastSeenMessages) {
|
||||
CompletableFuture<CommandExecuteEvent> eventFuture = server.getCommandManager().callCommandEvent(player, message);
|
||||
String message, Instant timestamp, @Nullable LastSeenMessages lastSeenMessages,
|
||||
CommandExecuteEvent.InvocationInfo invocationInfo) {
|
||||
CompletableFuture<CommandExecuteEvent> eventFuture = server.getCommandManager().callCommandEvent(player, message,
|
||||
invocationInfo);
|
||||
player.getChatQueue().queuePacket(
|
||||
newLastSeenMessages -> eventFuture
|
||||
.thenComposeAsync(event -> futurePacketCreator.apply(event, newLastSeenMessages))
|
||||
|
||||
@ -47,8 +47,11 @@ public class SystemChatPacket implements MinecraftPacket {
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
||||
component = ComponentHolder.read(buf, version);
|
||||
// System chat is never decoded so this doesn't matter for now
|
||||
type = ChatType.values()[ProtocolUtils.readVarInt(buf)];
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19_1)){
|
||||
type = buf.readBoolean() ? ChatType.GAME_INFO : ChatType.SYSTEM;
|
||||
} else {
|
||||
type = ChatType.values()[ProtocolUtils.readVarInt(buf)];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -111,6 +111,6 @@ public class KeyedCommandHandler implements CommandHandler<KeyedPlayerCommandPac
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}, packet.getCommand(), packet.getTimestamp(), null);
|
||||
}, packet.getCommand(), packet.getTimestamp(), null, new CommandExecuteEvent.InvocationInfo(CommandExecuteEvent.SignedState.UNSUPPORTED, CommandExecuteEvent.Source.PLAYER));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -62,6 +62,6 @@ public class LegacyCommandHandler implements CommandHandler<LegacyChatPacket> {
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}, command, Instant.now(), null);
|
||||
}, command, Instant.now(), null, new CommandExecuteEvent.InvocationInfo(CommandExecuteEvent.SignedState.UNSUPPORTED, CommandExecuteEvent.Source.PLAYER));
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,6 +117,7 @@ public class SessionCommandHandler implements CommandHandler<SessionPlayerComman
|
||||
}
|
||||
return forwardCommand(fixedPacket, commandToRun);
|
||||
});
|
||||
}, packet.command, packet.timeStamp, packet.lastSeenMessages);
|
||||
}, packet.command, packet.timeStamp, packet.lastSeenMessages,
|
||||
new CommandExecuteEvent.InvocationInfo(packet.getEventSignedState(), CommandExecuteEvent.Source.PLAYER));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
package com.velocitypowered.proxy.protocol.packet.chat.session;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
@ -45,6 +46,8 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
|
||||
this.salt = buf.readLong();
|
||||
this.argumentSignatures = new ArgumentSignatures(buf);
|
||||
this.lastSeenMessages = new LastSeenMessages(buf);
|
||||
|
||||
this.argumentSignatures = new ArgumentSignatures();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -68,6 +71,10 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
|
||||
return !argumentSignatures.isEmpty();
|
||||
}
|
||||
|
||||
public CommandExecuteEvent.SignedState getEventSignedState() {
|
||||
return !this.argumentSignatures.isEmpty() ? CommandExecuteEvent.SignedState.SIGNED_WITH_ARGS : CommandExecuteEvent.SignedState.SIGNED_WITHOUT_ARGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(MinecraftSessionHandler handler) {
|
||||
return handler.handle(this);
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
package com.velocitypowered.proxy.protocol.packet.chat.session;
|
||||
|
||||
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.LastSeenMessages;
|
||||
@ -44,6 +45,11 @@ public class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecuteEvent.SignedState getEventSignedState() {
|
||||
return CommandExecuteEvent.SignedState.UNSIGNED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UnsignedPlayerCommandPacket{" +
|
||||
|
||||
@ -159,12 +159,17 @@ public class KeyedVelocityTabList implements InternalTabList {
|
||||
|
||||
@Override
|
||||
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||
int gameMode,
|
||||
@Nullable ChatSession chatSession, boolean listed) {
|
||||
int gameMode, @Nullable ChatSession chatSession, boolean listed) {
|
||||
return new KeyedVelocityTabListEntry(this, profile, displayName, latency, gameMode,
|
||||
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
|
||||
public void processLegacy(LegacyPlayerListItemPacket packet) {
|
||||
// Packets are already forwarded on, so no need to do that here
|
||||
|
||||
@ -19,6 +19,7 @@ package com.velocitypowered.proxy.tablist;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.player.ChatSession;
|
||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||
@ -89,7 +90,7 @@ public class VelocityTabList implements InternalTabList {
|
||||
} else {
|
||||
entry = new VelocityTabListEntry(this, entry1.getProfile(),
|
||||
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
|
||||
@ -128,6 +129,11 @@ public class VelocityTabList implements InternalTabList {
|
||||
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LISTED);
|
||||
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())) {
|
||||
ChatSession from = entry.getChatSession();
|
||||
if (from != null) {
|
||||
@ -162,6 +168,11 @@ public class VelocityTabList implements InternalTabList {
|
||||
}
|
||||
playerInfoEntry.setLatency(entry.getLatency());
|
||||
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;
|
||||
});
|
||||
@ -207,9 +218,9 @@ public class VelocityTabList implements InternalTabList {
|
||||
@Override
|
||||
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||
int gameMode,
|
||||
@Nullable ChatSession chatSession, boolean listed) {
|
||||
@Nullable ChatSession chatSession, boolean listed, int listOrder) {
|
||||
return new VelocityTabListEntry(this, profile, displayName, latency, gameMode, chatSession,
|
||||
listed);
|
||||
listed, listOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -246,7 +257,8 @@ public class VelocityTabList implements InternalTabList {
|
||||
0,
|
||||
-1,
|
||||
null,
|
||||
false
|
||||
false,
|
||||
0
|
||||
)
|
||||
);
|
||||
} else {
|
||||
@ -274,6 +286,9 @@ public class VelocityTabList implements InternalTabList {
|
||||
if (actions.contains(UpsertPlayerInfoPacket.Action.UPDATE_LISTED)) {
|
||||
currentEntry.setListedWithoutUpdate(entry.isListed());
|
||||
}
|
||||
if (actions.contains(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER)) {
|
||||
currentEntry.setListOrderWithoutUpdate(entry.getListOrder());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
package com.velocitypowered.proxy.tablist;
|
||||
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.player.ChatSession;
|
||||
import com.velocitypowered.api.proxy.player.TabList;
|
||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||
@ -38,6 +39,7 @@ public class VelocityTabListEntry implements TabListEntry {
|
||||
private int latency;
|
||||
private int gameMode;
|
||||
private boolean listed;
|
||||
private int listOrder;
|
||||
private @Nullable ChatSession session;
|
||||
|
||||
/**
|
||||
@ -45,7 +47,7 @@ public class VelocityTabListEntry implements TabListEntry {
|
||||
*/
|
||||
public VelocityTabListEntry(VelocityTabList tabList, GameProfile profile, Component displayName,
|
||||
int latency,
|
||||
int gameMode, @Nullable ChatSession session, boolean listed) {
|
||||
int gameMode, @Nullable ChatSession session, boolean listed, int listOrder) {
|
||||
this.tabList = tabList;
|
||||
this.profile = profile;
|
||||
this.displayName = displayName;
|
||||
@ -53,6 +55,7 @@ public class VelocityTabListEntry implements TabListEntry {
|
||||
this.gameMode = gameMode;
|
||||
this.session = session;
|
||||
this.listed = listed;
|
||||
this.listOrder = listOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -150,4 +153,24 @@ public class VelocityTabListEntry implements TabListEntry {
|
||||
void setListedWithoutUpdate(boolean 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
10
steamwarci.yml
Normal 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/"
|
||||
Reference in New Issue
Block a user