Compare commits
21 Commits
updatev2
...
19e51a2b12
| Author | SHA1 | Date | |
|---|---|---|---|
| 19e51a2b12 | |||
| b89a5c5ce9 | |||
| 65d3277319 | |||
| a22bfa10f9 | |||
| d9d1319a3a | |||
| 15ecbf4345 | |||
| 5e3bbcd427 | |||
| a6c79db07b | |||
| 6e33bc6c17 | |||
| 01208bb359 | |||
| fa88aaae52 | |||
| 2da400a267 | |||
| 8103135dfb | |||
| cfabff7288 | |||
| 2f5a27a708 | |||
| fdfe8bcc4b | |||
| a19fd8db74 | |||
| e63d71423d | |||
| a7afe35fab | |||
| 56d6339313 | |||
| 2475572573 |
7
.github/workflows/gradle.yml
vendored
7
.github/workflows/gradle.yml
vendored
@ -10,14 +10,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
- name: Validate Gradle Wrapper
|
||||||
persist-credentials: false
|
uses: gradle/actions/wrapper-validation@v3
|
||||||
- name: Set up Gradle
|
|
||||||
uses: gradle/actions/setup-gradle@v4
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
cache: 'gradle'
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
|||||||
@ -61,7 +61,6 @@ tasks {
|
|||||||
o.encoding = "UTF-8"
|
o.encoding = "UTF-8"
|
||||||
o.source = "17"
|
o.source = "17"
|
||||||
|
|
||||||
o.use()
|
|
||||||
o.links(
|
o.links(
|
||||||
"https://www.slf4j.org/apidocs/",
|
"https://www.slf4j.org/apidocs/",
|
||||||
"https://guava.dev/releases/${libs.guava.get().version}/api/docs/",
|
"https://guava.dev/releases/${libs.guava.get().version}/api/docs/",
|
||||||
|
|||||||
@ -7,10 +7,8 @@
|
|||||||
|
|
||||||
package com.velocitypowered.api.command;
|
package com.velocitypowered.api.command;
|
||||||
|
|
||||||
import com.mojang.brigadier.suggestion.Suggestions;
|
|
||||||
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
@ -118,27 +116,6 @@ public interface CommandManager {
|
|||||||
*/
|
*/
|
||||||
CompletableFuture<Boolean> executeImmediatelyAsync(CommandSource source, String cmdLine);
|
CompletableFuture<Boolean> executeImmediatelyAsync(CommandSource source, String cmdLine);
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously collects suggestions to fill in the given command {@code cmdLine}.
|
|
||||||
* Returns only the raw completion suggestions without tooltips.
|
|
||||||
*
|
|
||||||
* @param source the source to execute the command for
|
|
||||||
* @param cmdLine the partially completed command
|
|
||||||
* @return a {@link CompletableFuture} eventually completed with a {@link List}, possibly empty
|
|
||||||
*/
|
|
||||||
CompletableFuture<List<String>> offerSuggestions(CommandSource source, String cmdLine);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously collects suggestions to fill in the given command {@code cmdLine}.
|
|
||||||
* Returns the brigadier {@link Suggestions} with tooltips for each result.
|
|
||||||
*
|
|
||||||
* @param source the source to execute the command for
|
|
||||||
* @param cmdLine the partially completed command
|
|
||||||
* @return a {@link CompletableFuture} eventually completed with {@link Suggestions}, possibly
|
|
||||||
* empty
|
|
||||||
*/
|
|
||||||
CompletableFuture<Suggestions> offerBrigadierSuggestions(CommandSource source, String cmdLine);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an immutable collection of the case-insensitive aliases registered
|
* Returns an immutable collection of the case-insensitive aliases registered
|
||||||
* on this manager.
|
* on this manager.
|
||||||
|
|||||||
@ -27,7 +27,7 @@ public interface CommandSource extends Audience, PermissionSubject {
|
|||||||
* for more information on the format.
|
* for more information on the format.
|
||||||
**/
|
**/
|
||||||
default void sendRichMessage(final @NotNull String message) {
|
default void sendRichMessage(final @NotNull String message) {
|
||||||
this.sendMessage(MiniMessage.miniMessage().deserialize(message, this));
|
this.sendMessage(MiniMessage.miniMessage().deserialize(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,7 +43,7 @@ public interface CommandSource extends Audience, PermissionSubject {
|
|||||||
final @NotNull String message,
|
final @NotNull String message,
|
||||||
final @NotNull TagResolver @NotNull... resolvers
|
final @NotNull TagResolver @NotNull... resolvers
|
||||||
) {
|
) {
|
||||||
this.sendMessage(MiniMessage.miniMessage().deserialize(message, this, resolvers));
|
this.sendMessage(MiniMessage.miniMessage().deserialize(message, resolvers));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -12,14 +12,6 @@ package com.velocitypowered.api.event;
|
|||||||
*/
|
*/
|
||||||
public enum PostOrder {
|
public enum PostOrder {
|
||||||
|
|
||||||
FIRST, EARLY, NORMAL, LATE, LAST,
|
FIRST, EARLY, NORMAL, LATE, LAST, CUSTOM
|
||||||
|
|
||||||
/**
|
|
||||||
* Previously used to specify that {@link Subscribe#priority()} should be used.
|
|
||||||
*
|
|
||||||
* @deprecated No longer required, you only need to specify {@link Subscribe#priority()}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
CUSTOM
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,9 +32,12 @@ public @interface Subscribe {
|
|||||||
* The priority of this event handler. Priorities are used to determine the order in which event
|
* The priority of this event handler. Priorities are used to determine the order in which event
|
||||||
* handlers are called. The higher the priority, the earlier the event handler will be called.
|
* handlers are called. The higher the priority, the earlier the event handler will be called.
|
||||||
*
|
*
|
||||||
|
* <p>Note that due to compatibility constraints, you must specify {@link PostOrder#CUSTOM}
|
||||||
|
* in order to use this field.</p>
|
||||||
|
*
|
||||||
* @return the priority
|
* @return the priority
|
||||||
*/
|
*/
|
||||||
short priority() default 0;
|
short priority() default Short.MIN_VALUE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the handler must be called asynchronously. By default, all event handlers are called
|
* Whether the handler must be called asynchronously. By default, all event handlers are called
|
||||||
|
|||||||
@ -9,6 +9,7 @@ package com.velocitypowered.api.event.command;
|
|||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
import com.mojang.brigadier.tree.RootCommandNode;
|
import com.mojang.brigadier.tree.RootCommandNode;
|
||||||
import com.velocitypowered.api.event.annotation.AwaitingEvent;
|
import com.velocitypowered.api.event.annotation.AwaitingEvent;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
@ -20,6 +21,7 @@ import com.velocitypowered.api.proxy.Player;
|
|||||||
* client.
|
* client.
|
||||||
*/
|
*/
|
||||||
@AwaitingEvent
|
@AwaitingEvent
|
||||||
|
@Beta
|
||||||
public class PlayerAvailableCommandsEvent {
|
public class PlayerAvailableCommandsEvent {
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package com.velocitypowered.api.event.player;
|
package com.velocitypowered.api.event.player;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
@ -17,6 +18,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||||||
* available in {@link Player#getCurrentServer()}. Velocity will not wait on this event to finish
|
* available in {@link Player#getCurrentServer()}. Velocity will not wait on this event to finish
|
||||||
* firing.
|
* firing.
|
||||||
*/
|
*/
|
||||||
|
@Beta
|
||||||
public class ServerPostConnectEvent {
|
public class ServerPostConnectEvent {
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final RegisteredServer previousServer;
|
private final RegisteredServer previousServer;
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package com.velocitypowered.api.event.proxy.server;
|
package com.velocitypowered.api.event.proxy.server;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||||
@ -22,6 +23,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* @param registeredServer A {@link RegisteredServer} that has been registered.
|
* @param registeredServer A {@link RegisteredServer} that has been registered.
|
||||||
* @since 3.3.0
|
* @since 3.3.0
|
||||||
*/
|
*/
|
||||||
|
@Beta
|
||||||
public record ServerRegisteredEvent(@NotNull RegisteredServer registeredServer) {
|
public record ServerRegisteredEvent(@NotNull RegisteredServer registeredServer) {
|
||||||
public ServerRegisteredEvent {
|
public ServerRegisteredEvent {
|
||||||
Preconditions.checkNotNull(registeredServer, "registeredServer");
|
Preconditions.checkNotNull(registeredServer, "registeredServer");
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package com.velocitypowered.api.event.proxy.server;
|
package com.velocitypowered.api.event.proxy.server;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||||
@ -22,6 +23,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* @param unregisteredServer A {@link RegisteredServer} that has been unregistered.
|
* @param unregisteredServer A {@link RegisteredServer} that has been unregistered.
|
||||||
* @since 3.3.0
|
* @since 3.3.0
|
||||||
*/
|
*/
|
||||||
|
@Beta
|
||||||
public record ServerUnregisteredEvent(@NotNull RegisteredServer unregisteredServer) {
|
public record ServerUnregisteredEvent(@NotNull RegisteredServer unregisteredServer) {
|
||||||
public ServerUnregisteredEvent {
|
public ServerUnregisteredEvent {
|
||||||
Preconditions.checkNotNull(unregisteredServer, "unregisteredServer");
|
Preconditions.checkNotNull(unregisteredServer, "unregisteredServer");
|
||||||
|
|||||||
@ -89,9 +89,7 @@ public enum ProtocolVersion implements Ordered<ProtocolVersion> {
|
|||||||
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"),
|
MINECRAFT_1_21_4(769, "1.21.4");
|
||||||
MINECRAFT_1_21_5(770, "1.21.5"),
|
|
||||||
MINECRAFT_1_21_6(771, "1.21.6");
|
|
||||||
|
|
||||||
private static final int SNAPSHOT_BIT = 30;
|
private static final int SNAPSHOT_BIT = 30;
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.velocitypowered.api.proxy;
|
package com.velocitypowered.api.proxy;
|
||||||
|
|
||||||
import com.velocitypowered.api.network.HandshakeIntent;
|
|
||||||
import com.velocitypowered.api.network.ProtocolState;
|
import com.velocitypowered.api.network.ProtocolState;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
@ -61,11 +60,4 @@ public interface InboundConnection {
|
|||||||
* @return the protocol state of the connection
|
* @return the protocol state of the connection
|
||||||
*/
|
*/
|
||||||
ProtocolState getProtocolState();
|
ProtocolState getProtocolState();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the initial intent for the connection.
|
|
||||||
*
|
|
||||||
* @return the intent of the connection
|
|
||||||
*/
|
|
||||||
HandshakeIntent getHandshakeIntent();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,13 +41,6 @@ public interface ProxyServer extends Audience {
|
|||||||
*/
|
*/
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the proxy is currently shutting down.
|
|
||||||
*
|
|
||||||
* @return {@code true} if the proxy is shutting down, {@code false} otherwise
|
|
||||||
*/
|
|
||||||
boolean isShuttingDown();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes all listening endpoints for this server.
|
* Closes all listening endpoints for this server.
|
||||||
* This includes the main minecraft listener and query channel.
|
* This includes the main minecraft listener and query channel.
|
||||||
|
|||||||
@ -148,59 +148,4 @@ public interface ProxyConfig {
|
|||||||
* @return read timeout (in milliseconds)
|
* @return read timeout (in milliseconds)
|
||||||
*/
|
*/
|
||||||
int getReadTimeout();
|
int getReadTimeout();
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the rate limit for how fast a player can execute commands.
|
|
||||||
*
|
|
||||||
* @return the command rate limit (in milliseconds)
|
|
||||||
*/
|
|
||||||
int getCommandRatelimit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get whether we should forward commands to the backend if the player is rate limited.
|
|
||||||
*
|
|
||||||
* @return whether to forward commands if rate limited
|
|
||||||
*/
|
|
||||||
boolean isForwardCommandsIfRateLimited();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the kick limit for commands that are rate limited.
|
|
||||||
* If this limit is 0 or less, the player will be not be kicked.
|
|
||||||
*
|
|
||||||
* @return the rate limited command rate limit
|
|
||||||
*/
|
|
||||||
int getKickAfterRateLimitedCommands();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get whether the proxy should kick players who are command rate limited.
|
|
||||||
*
|
|
||||||
* @return whether to kick players who are rate limited
|
|
||||||
*/
|
|
||||||
default boolean isKickOnCommandRateLimit() {
|
|
||||||
return getKickAfterRateLimitedCommands() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the rate limit for how fast a player can tab complete.
|
|
||||||
*
|
|
||||||
* @return the tab complete rate limit (in milliseconds)
|
|
||||||
*/
|
|
||||||
int getTabCompleteRatelimit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the kick limit for tab completes that are rate limited.
|
|
||||||
* If this limit is 0 or less, the player will be not be kicked.
|
|
||||||
*
|
|
||||||
* @return the rate limited command rate limit
|
|
||||||
*/
|
|
||||||
int getKickAfterRateLimitedTabCompletes();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get whether the proxy should kick players who are tab complete rate limited.
|
|
||||||
*
|
|
||||||
* @return whether to kick players who are rate limited
|
|
||||||
*/
|
|
||||||
default boolean isKickOnTabCompleteRateLimit() {
|
|
||||||
return getKickAfterRateLimitedTabCompletes() > 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import net.kyori.adventure.key.Key;
|
import net.kyori.adventure.key.Key;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
@ -20,6 +21,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||||||
*/
|
*/
|
||||||
public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
||||||
|
|
||||||
|
private static final Pattern VALID_IDENTIFIER_REGEX = Pattern.compile("[a-z0-9/\\-_]*");
|
||||||
|
|
||||||
private final String namespace;
|
private final String namespace;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
@ -36,7 +39,7 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
|||||||
* @return a new channel identifier
|
* @return a new channel identifier
|
||||||
*/
|
*/
|
||||||
public static MinecraftChannelIdentifier forDefaultNamespace(String name) {
|
public static MinecraftChannelIdentifier forDefaultNamespace(String name) {
|
||||||
return new MinecraftChannelIdentifier(Key.MINECRAFT_NAMESPACE, name);
|
return new MinecraftChannelIdentifier("minecraft", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,10 +52,14 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
|||||||
public static MinecraftChannelIdentifier create(String namespace, String name) {
|
public static MinecraftChannelIdentifier create(String namespace, String name) {
|
||||||
checkArgument(!Strings.isNullOrEmpty(namespace), "namespace is null or empty");
|
checkArgument(!Strings.isNullOrEmpty(namespace), "namespace is null or empty");
|
||||||
checkArgument(name != null, "namespace is null or empty");
|
checkArgument(name != null, "namespace is null or empty");
|
||||||
checkArgument(Key.parseableNamespace(namespace),
|
checkArgument(VALID_IDENTIFIER_REGEX.matcher(namespace).matches(),
|
||||||
"namespace is not valid, must match: [a-z0-9_.-] got %s", namespace);
|
"namespace is not valid, must match: %s got %s",
|
||||||
checkArgument(Key.parseableValue(name),
|
VALID_IDENTIFIER_REGEX.toString(),
|
||||||
"name is not valid, must match: [a-z0-9/._-] got %s", name);
|
namespace);
|
||||||
|
checkArgument(VALID_IDENTIFIER_REGEX.matcher(name).matches(),
|
||||||
|
"name is not valid, must match: %s got %s",
|
||||||
|
VALID_IDENTIFIER_REGEX.toString(),
|
||||||
|
name);
|
||||||
return new MinecraftChannelIdentifier(namespace, name);
|
return new MinecraftChannelIdentifier(namespace, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,9 +72,10 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
|||||||
public static MinecraftChannelIdentifier from(String identifier) {
|
public static MinecraftChannelIdentifier from(String identifier) {
|
||||||
int colonPos = identifier.indexOf(':');
|
int colonPos = identifier.indexOf(':');
|
||||||
if (colonPos == -1) {
|
if (colonPos == -1) {
|
||||||
return create(Key.MINECRAFT_NAMESPACE, identifier);
|
throw new IllegalArgumentException("Identifier does not contain a colon.");
|
||||||
} else if (colonPos == 0) {
|
}
|
||||||
return create(Key.MINECRAFT_NAMESPACE, identifier.substring(1));
|
if (colonPos + 1 == identifier.length()) {
|
||||||
|
throw new IllegalArgumentException("Identifier is empty.");
|
||||||
}
|
}
|
||||||
String namespace = identifier.substring(0, colonPos);
|
String namespace = identifier.substring(0, colonPos);
|
||||||
String name = identifier.substring(colonPos + 1);
|
String name = identifier.substring(colonPos + 1);
|
||||||
|
|||||||
@ -40,7 +40,6 @@ public interface TabList {
|
|||||||
* Adds a {@link TabListEntry} to the {@link Player}'s tab list.
|
* Adds a {@link TabListEntry} to the {@link Player}'s tab list.
|
||||||
*
|
*
|
||||||
* @param entry to add to the tab list
|
* @param entry to add to the tab list
|
||||||
* @throws IllegalArgumentException on versions below 1.19.3, if an entry with the same UUID already exists
|
|
||||||
*/
|
*/
|
||||||
void addEntry(TabListEntry entry);
|
void addEntry(TabListEntry entry);
|
||||||
|
|
||||||
@ -48,7 +47,6 @@ public interface TabList {
|
|||||||
* Adds a {@link Iterable} of {@link TabListEntry}'s to the {@link Player}'s tab list.
|
* Adds a {@link Iterable} of {@link TabListEntry}'s to the {@link Player}'s tab list.
|
||||||
*
|
*
|
||||||
* @param entries to add to the tab list
|
* @param entries to add to the tab list
|
||||||
* @throws IllegalArgumentException on versions below 1.19.3, if an entry with the same UUID already exists
|
|
||||||
*/
|
*/
|
||||||
default void addEntries(Iterable<TabListEntry> entries) {
|
default void addEntries(Iterable<TabListEntry> entries) {
|
||||||
for (TabListEntry entry : entries) {
|
for (TabListEntry entry : entries) {
|
||||||
@ -60,7 +58,6 @@ public interface TabList {
|
|||||||
* Adds an array of {@link TabListEntry}'s to the {@link Player}'s tab list.
|
* Adds an array of {@link TabListEntry}'s to the {@link Player}'s tab list.
|
||||||
*
|
*
|
||||||
* @param entries to add to the tab list
|
* @param entries to add to the tab list
|
||||||
* @throws IllegalArgumentException on versions below 1.19.3, if an entry with the same UUID already exists
|
|
||||||
*/
|
*/
|
||||||
default void addEntries(TabListEntry... entries) {
|
default void addEntries(TabListEntry... entries) {
|
||||||
for (TabListEntry entry : entries) {
|
for (TabListEntry entry : entries) {
|
||||||
@ -190,26 +187,6 @@ 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, int listOrder) {
|
|
||||||
return buildEntry(profile, displayName, latency, gameMode, chatSession, listed, listOrder, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* @param showHat the visibility of this entry's hat layer
|
|
||||||
* @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 listOrder, boolean showHat);
|
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,7 +80,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
* <li>150-300 will display 4 bars</li>
|
* <li>150-300 will display 4 bars</li>
|
||||||
* <li>300-600 will display 3 bars</li>
|
* <li>300-600 will display 3 bars</li>
|
||||||
* <li>600-1000 will display 2 bars</li>
|
* <li>600-1000 will display 2 bars</li>
|
||||||
* <li>A latency greater than 1 second will display 1 bar</li>
|
* <li>A latency move than 1 second will display 1 bar</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @return latency set for {@code this} entry
|
* @return latency set for {@code this} entry
|
||||||
@ -160,27 +160,6 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether this entry's hat layer is shown in the tab list.
|
|
||||||
*
|
|
||||||
* @return whether to show this entry's hat layer
|
|
||||||
* @sinceMinecraft 1.21.4
|
|
||||||
*/
|
|
||||||
default boolean isShowHat() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether to show this entry's hat layer in the tab list.
|
|
||||||
*
|
|
||||||
* @param showHat whether to show this entry's hat layer
|
|
||||||
* @return {@code this}, for chaining
|
|
||||||
* @sinceMinecraft 1.21.4
|
|
||||||
*/
|
|
||||||
default TabListEntry setShowHat(boolean showHat) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link Builder} to create a {@link TabListEntry}.
|
* Returns a {@link Builder} to create a {@link TabListEntry}.
|
||||||
*
|
*
|
||||||
@ -204,7 +183,6 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
private int gameMode = 0;
|
private int gameMode = 0;
|
||||||
private boolean listed = true;
|
private boolean listed = true;
|
||||||
private int listOrder = 0;
|
private int listOrder = 0;
|
||||||
private boolean showHat;
|
|
||||||
|
|
||||||
private @Nullable ChatSession chatSession;
|
private @Nullable ChatSession chatSession;
|
||||||
|
|
||||||
@ -290,7 +268,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
* Sets whether 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
|
||||||
* @see TabListEntry#isListed()
|
* @see TabListEntry#isListed()
|
||||||
*/
|
*/
|
||||||
public Builder listed(boolean listed) {
|
public Builder listed(boolean listed) {
|
||||||
@ -302,7 +280,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
* Sets the order/priority of this entry in the tab list.
|
* Sets the order/priority of this entry in the tab list.
|
||||||
*
|
*
|
||||||
* @param order to set
|
* @param order to set
|
||||||
* @return {@code this}, for chaining
|
* @return ${code this}, for chaining
|
||||||
* @sinceMinecraft 1.21.2
|
* @sinceMinecraft 1.21.2
|
||||||
* @see TabListEntry#getListOrder()
|
* @see TabListEntry#getListOrder()
|
||||||
*/
|
*/
|
||||||
@ -311,18 +289,6 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether this entry's hat layer should be shown in the tab list.
|
|
||||||
*
|
|
||||||
* @param showHat to set
|
|
||||||
* @return {@code this}, for chaining
|
|
||||||
* @see TabListEntry#isShowHat()
|
|
||||||
*/
|
|
||||||
public Builder showHat(boolean showHat) {
|
|
||||||
this.showHat = showHat;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the {@link TabListEntry} specified by {@code this} {@link Builder}.
|
* Constructs the {@link TabListEntry} specified by {@code this} {@link Builder}.
|
||||||
*
|
*
|
||||||
@ -335,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, listOrder, showHat);
|
return tabList.buildEntry(profile, displayName, latency, gameMode, chatSession, listed, listOrder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import java.util.Objects;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import net.kyori.adventure.builder.AbstractBuilder;
|
import net.kyori.adventure.builder.AbstractBuilder;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the parameters used to ping a {@link RegisteredServer}.
|
* Contains the parameters used to ping a {@link RegisteredServer}.
|
||||||
@ -31,12 +30,10 @@ public final class PingOptions {
|
|||||||
public static final PingOptions DEFAULT = PingOptions.builder().build();
|
public static final PingOptions DEFAULT = PingOptions.builder().build();
|
||||||
private final ProtocolVersion protocolVersion;
|
private final ProtocolVersion protocolVersion;
|
||||||
private final long timeout;
|
private final long timeout;
|
||||||
private final String virtualHost;
|
|
||||||
|
|
||||||
private PingOptions(final Builder builder) {
|
private PingOptions(final Builder builder) {
|
||||||
this.protocolVersion = builder.protocolVersion;
|
this.protocolVersion = builder.protocolVersion;
|
||||||
this.timeout = builder.timeout;
|
this.timeout = builder.timeout;
|
||||||
this.virtualHost = builder.virtualHost;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,16 +54,6 @@ public final class PingOptions {
|
|||||||
return this.timeout;
|
return this.timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The virtual host to pass to the server for the ping.
|
|
||||||
*
|
|
||||||
* @return the virtual hostname to pass to the server for the ping
|
|
||||||
* @since 3.4.0
|
|
||||||
*/
|
|
||||||
public @Nullable String getVirtualHost() {
|
|
||||||
return this.virtualHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new builder to assign values to a new PingOptions.
|
* Create a new builder to assign values to a new PingOptions.
|
||||||
*
|
*
|
||||||
@ -81,9 +68,10 @@ public final class PingOptions {
|
|||||||
if (o == null) {
|
if (o == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!(o instanceof final PingOptions other)) {
|
if (!(o instanceof PingOptions)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
final PingOptions other = (PingOptions) o;
|
||||||
return Objects.equals(this.protocolVersion, other.protocolVersion)
|
return Objects.equals(this.protocolVersion, other.protocolVersion)
|
||||||
&& Objects.equals(this.timeout, other.timeout);
|
&& Objects.equals(this.timeout, other.timeout);
|
||||||
}
|
}
|
||||||
@ -109,7 +97,6 @@ public final class PingOptions {
|
|||||||
public static final class Builder implements AbstractBuilder<PingOptions> {
|
public static final class Builder implements AbstractBuilder<PingOptions> {
|
||||||
private ProtocolVersion protocolVersion = ProtocolVersion.UNKNOWN;
|
private ProtocolVersion protocolVersion = ProtocolVersion.UNKNOWN;
|
||||||
private long timeout = 0;
|
private long timeout = 0;
|
||||||
private String virtualHost = null;
|
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
}
|
}
|
||||||
@ -159,18 +146,6 @@ public final class PingOptions {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the virtual host to pass to the server for the ping.
|
|
||||||
*
|
|
||||||
* @param virtualHost the virtual hostname to pass to the server for the ping
|
|
||||||
* @return this builder
|
|
||||||
* @since 3.4.0
|
|
||||||
*/
|
|
||||||
public Builder virtualHost(final @Nullable String virtualHost) {
|
|
||||||
this.virtualHost = virtualHost;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link PingOptions} with the values of this Builder.
|
* Create a new {@link PingOptions} with the values of this Builder.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import com.velocitypowered.api.util.Favicon;
|
|||||||
import com.velocitypowered.api.util.ModInfo;
|
import com.velocitypowered.api.util.ModInfo;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -31,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) {
|
||||||
@ -160,79 +160,31 @@ public final class ServerPing {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the modified {@code version} info in the response.
|
|
||||||
*
|
|
||||||
* @param version version info to set
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder version(Version version) {
|
public Builder version(Version version) {
|
||||||
this.version = Preconditions.checkNotNull(version, "version");
|
this.version = Preconditions.checkNotNull(version, "version");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the modified {@code onlinePlayers} number in the response.
|
|
||||||
*
|
|
||||||
* @param onlinePlayers number for online players to set
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder onlinePlayers(int onlinePlayers) {
|
public Builder onlinePlayers(int onlinePlayers) {
|
||||||
this.onlinePlayers = onlinePlayers;
|
this.onlinePlayers = onlinePlayers;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the modified {@code maximumPlayers} number in the response.
|
|
||||||
* <b>This will not modify the actual maximum players that can join the server.</b>
|
|
||||||
*
|
|
||||||
* @param maximumPlayers number for maximum players to set
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder maximumPlayers(int maximumPlayers) {
|
public Builder maximumPlayers(int maximumPlayers) {
|
||||||
this.maximumPlayers = maximumPlayers;
|
this.maximumPlayers = maximumPlayers;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the modified {@code players} array in the response.
|
|
||||||
*
|
|
||||||
* @param players array of SamplePlayers to add
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder samplePlayers(SamplePlayer... players) {
|
public Builder samplePlayers(SamplePlayer... players) {
|
||||||
this.samplePlayers.addAll(Arrays.asList(players));
|
this.samplePlayers.addAll(Arrays.asList(players));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the modified {@code players} collection in the response.
|
|
||||||
*
|
|
||||||
* @param players collection of SamplePlayers to add
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder samplePlayers(Collection<SamplePlayer> players) {
|
|
||||||
this.samplePlayers.addAll(players);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the modified {@code modType} in the response.
|
|
||||||
*
|
|
||||||
* @param modType the mod type to set
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder modType(String modType) {
|
public Builder modType(String modType) {
|
||||||
this.modType = Preconditions.checkNotNull(modType, "modType");
|
this.modType = Preconditions.checkNotNull(modType, "modType");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the modified {@code mods} array in the response.
|
|
||||||
*
|
|
||||||
* @param mods array of mods to use
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder mods(ModInfo.Mod... mods) {
|
public Builder mods(ModInfo.Mod... mods) {
|
||||||
this.mods.addAll(Arrays.asList(mods));
|
this.mods.addAll(Arrays.asList(mods));
|
||||||
return this;
|
return this;
|
||||||
@ -242,7 +194,7 @@ public final class ServerPing {
|
|||||||
* Uses the modified {@code mods} list in the response.
|
* Uses the modified {@code mods} list in the response.
|
||||||
*
|
*
|
||||||
* @param mods the mods list to use
|
* @param mods the mods list to use
|
||||||
* @return this builder, for chaining
|
* @return this build, for chaining
|
||||||
*/
|
*/
|
||||||
public Builder mods(ModInfo mods) {
|
public Builder mods(ModInfo mods) {
|
||||||
Preconditions.checkNotNull(mods, "mods");
|
Preconditions.checkNotNull(mods, "mods");
|
||||||
@ -252,74 +204,36 @@ public final class ServerPing {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the current list of mods to use in the response.
|
|
||||||
*
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder clearMods() {
|
public Builder clearMods() {
|
||||||
this.mods.clear();
|
this.mods.clear();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the current list of PlayerSamples to use in the response.
|
|
||||||
*
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder clearSamplePlayers() {
|
public Builder clearSamplePlayers() {
|
||||||
this.samplePlayers.clear();
|
this.samplePlayers.clear();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the server as mod incompatible in the response.
|
|
||||||
*
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder notModCompatible() {
|
public Builder notModCompatible() {
|
||||||
this.nullOutModinfo = true;
|
this.nullOutModinfo = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables nulling Players in the response.
|
|
||||||
* This will display the player count as {@code ???}.
|
|
||||||
*
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder nullPlayers() {
|
public Builder nullPlayers() {
|
||||||
this.nullOutPlayers = true;
|
this.nullOutPlayers = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the {@code description} Component in the response.
|
|
||||||
*
|
|
||||||
* @param description Component to use as the description.
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder description(net.kyori.adventure.text.Component description) {
|
public Builder description(net.kyori.adventure.text.Component description) {
|
||||||
this.description = Preconditions.checkNotNull(description, "description");
|
this.description = Preconditions.checkNotNull(description, "description");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the {@code favicon} in the response.
|
|
||||||
*
|
|
||||||
* @param favicon Favicon instance to use.
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder favicon(Favicon favicon) {
|
public Builder favicon(Favicon favicon) {
|
||||||
this.favicon = Preconditions.checkNotNull(favicon, "favicon");
|
this.favicon = Preconditions.checkNotNull(favicon, "favicon");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the current favicon used in the response.
|
|
||||||
*
|
|
||||||
* @return this builder, for chaining
|
|
||||||
*/
|
|
||||||
public Builder clearFavicon() {
|
public Builder clearFavicon() {
|
||||||
this.favicon = null;
|
this.favicon = null;
|
||||||
return this;
|
return this;
|
||||||
@ -516,10 +430,6 @@ public final class ServerPing {
|
|||||||
*/
|
*/
|
||||||
public static final class SamplePlayer {
|
public static final class SamplePlayer {
|
||||||
|
|
||||||
public static final SamplePlayer ANONYMOUS = new SamplePlayer(
|
|
||||||
"Anonymous Player",
|
|
||||||
new UUID(0L, 0L)
|
|
||||||
);
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final UUID id;
|
private final UUID id;
|
||||||
|
|
||||||
|
|||||||
@ -76,17 +76,9 @@ public final class ModInfo {
|
|||||||
private final String id;
|
private final String id;
|
||||||
private final String version;
|
private final String version;
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new mod info.
|
|
||||||
*
|
|
||||||
* @param id the mod identifier
|
|
||||||
* @param version the mod version
|
|
||||||
*/
|
|
||||||
public Mod(String id, String version) {
|
public Mod(String id, String version) {
|
||||||
this.id = Preconditions.checkNotNull(id, "id");
|
this.id = Preconditions.checkNotNull(id, "id");
|
||||||
this.version = Preconditions.checkNotNull(version, "version");
|
this.version = Preconditions.checkNotNull(version, "version");
|
||||||
Preconditions.checkArgument(id.length() < 128, "mod id is too long");
|
|
||||||
Preconditions.checkArgument(version.length() < 128, "mod version is too long");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
|||||||
@ -47,25 +47,17 @@ class MinecraftChannelIdentifierTest {
|
|||||||
create("velocity", "test/test2");
|
create("velocity", "test/test2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void fromIdentifierDefaultNamespace() {
|
|
||||||
assertEquals("minecraft", from("test").getNamespace());
|
|
||||||
assertEquals("minecraft", from(":test").getNamespace());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void fromIdentifierAllowsEmptyName() {
|
|
||||||
from("minecraft:");
|
|
||||||
from(":");
|
|
||||||
from("");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void fromIdentifierThrowsOnBadValues() {
|
void fromIdentifierThrowsOnBadValues() {
|
||||||
assertAll(
|
assertAll(
|
||||||
|
() -> assertThrows(IllegalArgumentException.class, () -> from("")),
|
||||||
|
() -> assertThrows(IllegalArgumentException.class, () -> from(":")),
|
||||||
|
() -> assertThrows(IllegalArgumentException.class, () -> from(":a")),
|
||||||
|
() -> assertThrows(IllegalArgumentException.class, () -> from("a:")),
|
||||||
() -> assertThrows(IllegalArgumentException.class, () -> from("hello:$$$$$$")),
|
() -> assertThrows(IllegalArgumentException.class, () -> from("hello:$$$$$$")),
|
||||||
() -> assertThrows(IllegalArgumentException.class, () -> from("he/llo:wor/ld")),
|
|
||||||
() -> assertThrows(IllegalArgumentException.class, () -> from("hello::"))
|
() -> assertThrows(IllegalArgumentException.class, () -> from("hello::"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -2,8 +2,8 @@
|
|||||||
configurate3 = "3.7.3"
|
configurate3 = "3.7.3"
|
||||||
configurate4 = "4.1.2"
|
configurate4 = "4.1.2"
|
||||||
flare = "2.0.1"
|
flare = "2.0.1"
|
||||||
log4j = "2.24.3"
|
log4j = "2.24.1"
|
||||||
netty = "4.2.1.Final"
|
netty = "4.1.114.Final"
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
indra-publishing = "net.kyori.indra.publishing:2.0.6"
|
indra-publishing = "net.kyori.indra.publishing:2.0.6"
|
||||||
@ -11,14 +11,14 @@ shadow = "io.github.goooler.shadow:8.1.5"
|
|||||||
spotless = "com.diffplug.spotless:6.25.0"
|
spotless = "com.diffplug.spotless:6.25.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
adventure-bom = "net.kyori:adventure-bom:4.21.0"
|
adventure-bom = "net.kyori:adventure-bom:4.17.0"
|
||||||
adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.21.0"
|
adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.17.0"
|
||||||
adventure-facet = "net.kyori:adventure-platform-facet:4.3.4"
|
adventure-facet = "net.kyori:adventure-platform-facet:4.3.4"
|
||||||
asm = "org.ow2.asm:asm:9.8"
|
asm = "org.ow2.asm:asm:9.6"
|
||||||
auto-service = "com.google.auto.service:auto-service:1.0.1"
|
auto-service = "com.google.auto.service:auto-service:1.0.1"
|
||||||
auto-service-annotations = "com.google.auto.service:auto-service-annotations:1.0.1"
|
auto-service-annotations = "com.google.auto.service:auto-service-annotations:1.0.1"
|
||||||
brigadier = "com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT"
|
brigadier = "com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT"
|
||||||
bstats = "org.bstats:bstats-base:3.0.3"
|
bstats = "org.bstats:bstats-base:3.0.2"
|
||||||
caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.8"
|
caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.8"
|
||||||
checker-qual = "org.checkerframework:checker-qual:3.42.0"
|
checker-qual = "org.checkerframework:checker-qual:3.42.0"
|
||||||
checkstyle = "com.puppycrawl.tools:checkstyle:10.9.3"
|
checkstyle = "com.puppycrawl.tools:checkstyle:10.9.3"
|
||||||
@ -33,11 +33,11 @@ disruptor = "com.lmax:disruptor:4.0.0"
|
|||||||
fastutil = "it.unimi.dsi:fastutil:8.5.15"
|
fastutil = "it.unimi.dsi:fastutil:8.5.15"
|
||||||
flare-core = { module = "space.vectrix.flare:flare", version.ref = "flare" }
|
flare-core = { module = "space.vectrix.flare:flare", version.ref = "flare" }
|
||||||
flare-fastutil = { module = "space.vectrix.flare:flare-fastutil", version.ref = "flare" }
|
flare-fastutil = { module = "space.vectrix.flare:flare-fastutil", version.ref = "flare" }
|
||||||
jline = "org.jline:jline-terminal-jansi:3.30.2"
|
jline = "org.jline:jline-terminal-jansi:3.27.1"
|
||||||
jopt = "net.sf.jopt-simple:jopt-simple:5.0.4"
|
jopt = "net.sf.jopt-simple:jopt-simple:5.0.4"
|
||||||
junit = "org.junit.jupiter:junit-jupiter:5.10.2"
|
junit = "org.junit.jupiter:junit-jupiter:5.10.2"
|
||||||
jspecify = "org.jspecify:jspecify:0.3.0"
|
jspecify = "org.jspecify:jspecify:0.3.0"
|
||||||
kyori-ansi = "net.kyori:ansi:1.1.1"
|
kyori-ansi = "net.kyori:ansi:1.1.0"
|
||||||
guava = "com.google.guava:guava:25.1-jre"
|
guava = "com.google.guava:guava:25.1-jre"
|
||||||
gson = "com.google.code.gson:gson:2.10.1"
|
gson = "com.google.code.gson:gson:2.10.1"
|
||||||
guice = "com.google.inject:guice:6.0.0"
|
guice = "com.google.inject:guice:6.0.0"
|
||||||
@ -53,10 +53,10 @@ 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" }
|
||||||
netty-transport-native-iouring = { module = "io.netty:netty-transport-native-io_uring", 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.17"
|
slf4j = "org.slf4j:slf4j-api:2.0.12"
|
||||||
snakeyaml = "org.yaml:snakeyaml:1.33"
|
snakeyaml = "org.yaml:snakeyaml:1.33"
|
||||||
spotbugs-annotations = "com.github.spotbugs:spotbugs-annotations:4.7.3"
|
spotbugs-annotations = "com.github.spotbugs:spotbugs-annotations:4.7.3"
|
||||||
terminalconsoleappender = "net.minecrell:terminalconsoleappender:1.3.0"
|
terminalconsoleappender = "net.minecrell:terminalconsoleappender:1.3.0"
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
11
gradlew
vendored
11
gradlew
vendored
@ -15,8 +15,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@ -57,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@ -86,8 +84,7 @@ done
|
|||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
' "$PWD" ) || exit
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@ -203,7 +200,7 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command:
|
# Collect all arguments for the java command:
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
@ -249,4 +246,4 @@ eval "set -- $(
|
|||||||
tr '\n' ' '
|
tr '\n' ' '
|
||||||
)" '"$@"'
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
6
gradlew.bat
vendored
6
gradlew.bat
vendored
@ -13,8 +13,6 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
@rem SPDX-License-Identifier: Apache-2.0
|
|
||||||
@rem
|
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@ -36,7 +34,7 @@ set APP_HOME=%DIRNAME%
|
|||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS=-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
@ -91,4 +89,4 @@ exit /b %EXIT_CODE%
|
|||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
:omega
|
:omega
|
||||||
|
|||||||
@ -26,15 +26,6 @@ Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// But, you're saying, *why* are we using the key as the IV? After all, reusing the key as
|
|
||||||
// the IV defeats the entire point - we might as well just initialize it to all zeroes.
|
|
||||||
//
|
|
||||||
// You can blame Mojang. For the record, we also don't consider the Minecraft protocol
|
|
||||||
// encryption scheme to be secure, and it has reached the point where any serious cryptographic
|
|
||||||
// protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
|
|
||||||
// most serious.
|
|
||||||
//
|
|
||||||
// If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
|
|
||||||
CCCryptorRef cryptor = NULL;
|
CCCryptorRef cryptor = NULL;
|
||||||
CCCryptorStatus result = CCCryptorCreateWithMode(encrypt ? kCCEncrypt : kCCDecrypt,
|
CCCryptorStatus result = CCCryptorCreateWithMode(encrypt ? kCCEncrypt : kCCDecrypt,
|
||||||
kCCModeCFB8,
|
kCCModeCFB8,
|
||||||
|
|||||||
@ -32,15 +32,6 @@ Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// But, you're saying, *why* are we using the key as the IV? After all, reusing the key as
|
|
||||||
// the IV defeats the entire point - we might as well just initialize it to all zeroes.
|
|
||||||
//
|
|
||||||
// You can blame Mojang. For the record, we also don't consider the Minecraft protocol
|
|
||||||
// encryption scheme to be secure, and it has reached the point where any serious cryptographic
|
|
||||||
// protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
|
|
||||||
// most serious.
|
|
||||||
//
|
|
||||||
// If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
|
|
||||||
int result = EVP_CipherInit(ctx, EVP_aes_128_cfb8(), (byte*) keyBytes, (byte*) keyBytes,
|
int result = EVP_CipherInit(ctx, EVP_aes_128_cfb8(), (byte*) keyBytes, (byte*) keyBytes,
|
||||||
encrypt);
|
encrypt);
|
||||||
if (result != 1) {
|
if (result != 1) {
|
||||||
|
|||||||
@ -57,8 +57,7 @@ public class JavaVelocityCompressor implements VelocityCompressor {
|
|||||||
inflater.setInput(source.nioBuffer());
|
inflater.setInput(source.nioBuffer());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final int readable = source.readableBytes();
|
while (!inflater.finished() && inflater.getBytesWritten() < uncompressedSize) {
|
||||||
while (!inflater.finished() && inflater.getBytesRead() < readable) {
|
|
||||||
if (!destination.isWritable()) {
|
if (!destination.isWritable()) {
|
||||||
destination.ensureWritable(ZLIB_BUFFER_SIZE);
|
destination.ensureWritable(ZLIB_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,15 +48,6 @@ public class JavaVelocityCipher implements VelocityCipher {
|
|||||||
|
|
||||||
private JavaVelocityCipher(boolean encrypt, SecretKey key) throws GeneralSecurityException {
|
private JavaVelocityCipher(boolean encrypt, SecretKey key) throws GeneralSecurityException {
|
||||||
this.cipher = Cipher.getInstance("AES/CFB8/NoPadding");
|
this.cipher = Cipher.getInstance("AES/CFB8/NoPadding");
|
||||||
// But, you're saying, *why* are we using the key as the IV? After all, reusing the key as
|
|
||||||
// the IV defeats the entire point - we might as well just initialize it to all zeroes.
|
|
||||||
//
|
|
||||||
// You can blame Mojang. For the record, we also don't consider the Minecraft protocol
|
|
||||||
// encryption scheme to be secure, and it has reached the point where any serious cryptographic
|
|
||||||
// protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
|
|
||||||
// most serious.
|
|
||||||
//
|
|
||||||
// If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
|
|
||||||
this.cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key,
|
this.cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key,
|
||||||
new IvParameterSpec(key.getEncoded()));
|
new IvParameterSpec(key.getEncoded()));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,11 +51,7 @@ tasks {
|
|||||||
exclude("it/unimi/dsi/fastutil/ints/*Int2Short*")
|
exclude("it/unimi/dsi/fastutil/ints/*Int2Short*")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/*Int2Reference*")
|
exclude("it/unimi/dsi/fastutil/ints/*Int2Reference*")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/IntAVL*")
|
exclude("it/unimi/dsi/fastutil/ints/IntAVL*")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/IntArrayF*")
|
exclude("it/unimi/dsi/fastutil/ints/IntArray*")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/IntArrayI*")
|
|
||||||
exclude("it/unimi/dsi/fastutil/ints/IntArrayL*")
|
|
||||||
exclude("it/unimi/dsi/fastutil/ints/IntArrayP*")
|
|
||||||
exclude("it/unimi/dsi/fastutil/ints/IntArraySet*")
|
|
||||||
exclude("it/unimi/dsi/fastutil/ints/*IntBi*")
|
exclude("it/unimi/dsi/fastutil/ints/*IntBi*")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/Int*Pair")
|
exclude("it/unimi/dsi/fastutil/ints/Int*Pair")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/IntLinked*")
|
exclude("it/unimi/dsi/fastutil/ints/IntLinked*")
|
||||||
@ -100,7 +96,6 @@ tasks {
|
|||||||
runShadow {
|
runShadow {
|
||||||
workingDir = file("run").also(File::mkdirs)
|
workingDir = file("run").also(File::mkdirs)
|
||||||
standardInput = System.`in`
|
standardInput = System.`in`
|
||||||
jvmArgs("-Dvelocity.packet-decode-logging=true")
|
|
||||||
}
|
}
|
||||||
named<JavaExec>("run") {
|
named<JavaExec>("run") {
|
||||||
workingDir = file("run").also(File::mkdirs)
|
workingDir = file("run").also(File::mkdirs)
|
||||||
|
|||||||
@ -65,8 +65,7 @@ public class Metrics {
|
|||||||
logger::info,
|
logger::info,
|
||||||
config.isLogErrorsEnabled(),
|
config.isLogErrorsEnabled(),
|
||||||
config.isLogSentDataEnabled(),
|
config.isLogSentDataEnabled(),
|
||||||
config.isLogResponseStatusTextEnabled(),
|
config.isLogResponseStatusTextEnabled()
|
||||||
false
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!config.didExistBefore()) {
|
if (!config.didExistBefore()) {
|
||||||
|
|||||||
@ -47,11 +47,6 @@ public class Velocity {
|
|||||||
System.setProperty("io.netty.native.workdir", System.getProperty("velocity.natives-tmpdir"));
|
System.setProperty("io.netty.native.workdir", System.getProperty("velocity.natives-tmpdir"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore allocator used before Netty 4.2 due to oom issues with the adaptive allocator
|
|
||||||
if (System.getProperty("io.netty.allocator.type") == null) {
|
|
||||||
System.setProperty("io.netty.allocator.type", "pooled");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable the resource leak detector by default as it reduces performance. Allow the user to
|
// Disable the resource leak detector by default as it reduces performance. Allow the user to
|
||||||
// override this if desired.
|
// override this if desired.
|
||||||
if (!VelocityProperties.hasProperty("io.netty.leakDetection.level")) {
|
if (!VelocityProperties.hasProperty("io.netty.leakDetection.level")) {
|
||||||
|
|||||||
@ -75,13 +75,11 @@ import io.netty.channel.ChannelInitializer;
|
|||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.text.MessageFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -105,7 +103,7 @@ import net.kyori.adventure.audience.ForwardingAudience;
|
|||||||
import net.kyori.adventure.key.Key;
|
import net.kyori.adventure.key.Key;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.translation.GlobalTranslator;
|
import net.kyori.adventure.translation.GlobalTranslator;
|
||||||
import net.kyori.adventure.translation.TranslationStore;
|
import net.kyori.adventure.translation.TranslationRegistry;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bstats.MetricsBase;
|
import org.bstats.MetricsBase;
|
||||||
@ -164,9 +162,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
private final Map<UUID, ConnectedPlayer> connectionsByUuid = new ConcurrentHashMap<>();
|
private final Map<UUID, ConnectedPlayer> connectionsByUuid = new ConcurrentHashMap<>();
|
||||||
private final Map<String, ConnectedPlayer> connectionsByName = new ConcurrentHashMap<>();
|
private final Map<String, ConnectedPlayer> connectionsByName = new ConcurrentHashMap<>();
|
||||||
private final VelocityConsole console;
|
private final VelocityConsole console;
|
||||||
private @MonotonicNonNull Ratelimiter<InetAddress> ipAttemptLimiter;
|
private @MonotonicNonNull Ratelimiter ipAttemptLimiter;
|
||||||
private @MonotonicNonNull Ratelimiter<UUID> commandRateLimiter;
|
|
||||||
private @MonotonicNonNull Ratelimiter<UUID> tabCompleteRateLimiter;
|
|
||||||
private final VelocityEventManager eventManager;
|
private final VelocityEventManager eventManager;
|
||||||
private final VelocityScheduler scheduler;
|
private final VelocityScheduler scheduler;
|
||||||
private final VelocityChannelRegistrar channelRegistrar = new VelocityChannelRegistrar();
|
private final VelocityChannelRegistrar channelRegistrar = new VelocityChannelRegistrar();
|
||||||
@ -240,15 +236,6 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
|
|
||||||
registerTranslations();
|
registerTranslations();
|
||||||
|
|
||||||
// Yes, you're reading that correctly. We're generating a 1024-bit RSA keypair. Sounds
|
|
||||||
// dangerous, right? We're well within the realm of factoring such a key...
|
|
||||||
//
|
|
||||||
// You can blame Mojang. For the record, we also don't consider the Minecraft protocol
|
|
||||||
// encryption scheme to be secure, and it has reached the point where any serious cryptographic
|
|
||||||
// protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
|
|
||||||
// most serious.
|
|
||||||
//
|
|
||||||
// If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
|
|
||||||
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
|
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
|
||||||
|
|
||||||
cm.logChannelInformation();
|
cm.logChannelInformation();
|
||||||
@ -299,8 +286,6 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ipAttemptLimiter = Ratelimiters.createWithMilliseconds(configuration.getLoginRatelimit());
|
ipAttemptLimiter = Ratelimiters.createWithMilliseconds(configuration.getLoginRatelimit());
|
||||||
commandRateLimiter = Ratelimiters.createWithMilliseconds(configuration.getCommandRatelimit());
|
|
||||||
tabCompleteRateLimiter = Ratelimiters.createWithMilliseconds(configuration.getTabCompleteRatelimit());
|
|
||||||
loadPlugins();
|
loadPlugins();
|
||||||
|
|
||||||
// Go ahead and fire the proxy initialization event. We block since plugins should have a chance
|
// Go ahead and fire the proxy initialization event. We block since plugins should have a chance
|
||||||
@ -338,8 +323,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void registerTranslations() {
|
private void registerTranslations() {
|
||||||
final TranslationStore.StringBased<MessageFormat> translationRegistry =
|
final TranslationRegistry translationRegistry = TranslationRegistry
|
||||||
TranslationStore.messageFormat(Key.key("velocity", "translations"));
|
.create(Key.key("velocity", "translations"));
|
||||||
translationRegistry.defaultLocale(Locale.US);
|
translationRegistry.defaultLocale(Locale.US);
|
||||||
try {
|
try {
|
||||||
ResourceUtils.visitResources(VelocityServer.class, path -> {
|
ResourceUtils.visitResources(VelocityServer.class, path -> {
|
||||||
@ -660,18 +645,10 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
return cm.createHttpClient();
|
return cm.createHttpClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
public @MonotonicNonNull Ratelimiter<InetAddress> getIpAttemptLimiter() {
|
public Ratelimiter getIpAttemptLimiter() {
|
||||||
return ipAttemptLimiter;
|
return ipAttemptLimiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @MonotonicNonNull Ratelimiter<UUID> getCommandRateLimiter() {
|
|
||||||
return commandRateLimiter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @MonotonicNonNull Ratelimiter<UUID> getTabCompleteRateLimiter() {
|
|
||||||
return tabCompleteRateLimiter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the {@code connection} can be registered with the proxy.
|
* Checks if the {@code connection} can be registered with the proxy.
|
||||||
*
|
*
|
||||||
@ -817,11 +794,6 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
public VelocityChannelRegistrar getChannelRegistrar() {
|
public VelocityChannelRegistrar getChannelRegistrar() {
|
||||||
return channelRegistrar;
|
return channelRegistrar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isShuttingDown() {
|
|
||||||
return shutdownInProgress.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InetSocketAddress getBoundAddress() {
|
public InetSocketAddress getBoundAddress() {
|
||||||
|
|||||||
@ -300,14 +300,27 @@ public class VelocityCommandManager implements CommandManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Returns suggestions to fill in the given command.
|
||||||
|
*
|
||||||
|
* @param source the source to execute the command for
|
||||||
|
* @param cmdLine the partially completed command
|
||||||
|
* @return a {@link CompletableFuture} eventually completed with a {@link List}, possibly empty
|
||||||
|
*/
|
||||||
public CompletableFuture<List<String>> offerSuggestions(final CommandSource source,
|
public CompletableFuture<List<String>> offerSuggestions(final CommandSource source,
|
||||||
final String cmdLine) {
|
final String cmdLine) {
|
||||||
return offerBrigadierSuggestions(source, cmdLine)
|
return offerBrigadierSuggestions(source, cmdLine)
|
||||||
.thenApply(suggestions -> Lists.transform(suggestions.getList(), Suggestion::getText));
|
.thenApply(suggestions -> Lists.transform(suggestions.getList(), Suggestion::getText));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Returns suggestions to fill in the given command.
|
||||||
|
*
|
||||||
|
* @param source the source to execute the command for
|
||||||
|
* @param cmdLine the partially completed command
|
||||||
|
* @return a {@link CompletableFuture} eventually completed with {@link Suggestions}, possibly
|
||||||
|
* empty
|
||||||
|
*/
|
||||||
public CompletableFuture<Suggestions> offerBrigadierSuggestions(
|
public CompletableFuture<Suggestions> offerBrigadierSuggestions(
|
||||||
final CommandSource source, final String cmdLine) {
|
final CommandSource source, final String cmdLine) {
|
||||||
Preconditions.checkNotNull(source, "source");
|
Preconditions.checkNotNull(source, "source");
|
||||||
|
|||||||
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.command.builtin;
|
package com.velocitypowered.proxy.command.builtin;
|
||||||
|
|
||||||
import com.google.gson.JsonSyntaxException;
|
|
||||||
import com.mojang.brigadier.Command;
|
import com.mojang.brigadier.Command;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
@ -26,9 +25,8 @@ import com.velocitypowered.api.command.BrigadierCommand;
|
|||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuts down the proxy.
|
* Shuts down the proxy.
|
||||||
@ -55,22 +53,11 @@ public final class ShutdownCommand {
|
|||||||
StringArgumentType.greedyString())
|
StringArgumentType.greedyString())
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
String reason = context.getArgument("reason", String.class);
|
String reason = context.getArgument("reason", String.class);
|
||||||
Component reasonComponent = null;
|
server.shutdown(true, MiniMessage.miniMessage().deserialize(
|
||||||
|
MiniMessage.miniMessage().serialize(
|
||||||
if (reason.startsWith("{") || reason.startsWith("[") || reason.startsWith("\"")) {
|
LegacyComponentSerializer.legacy('&').deserialize(reason)
|
||||||
try {
|
)
|
||||||
reasonComponent = GsonComponentSerializer.gson()
|
));
|
||||||
.deserializeOrNull(reason);
|
|
||||||
} catch (JsonSyntaxException expected) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reasonComponent == null) {
|
|
||||||
reasonComponent = MiniMessage.miniMessage().deserialize(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
server.shutdown(true, reasonComponent);
|
|
||||||
return Command.SINGLE_SUCCESS;
|
return Command.SINGLE_SUCCESS;
|
||||||
})
|
})
|
||||||
).build());
|
).build());
|
||||||
|
|||||||
@ -82,7 +82,7 @@ public final class VelocityCommand {
|
|||||||
.executes(new Heap())
|
.executes(new Heap())
|
||||||
.build();
|
.build();
|
||||||
final LiteralCommandNode<CommandSource> info = BrigadierCommand.literalArgumentBuilder("info")
|
final LiteralCommandNode<CommandSource> info = BrigadierCommand.literalArgumentBuilder("info")
|
||||||
.requires(source -> source.getPermissionValue("velocity.command.info") == Tristate.TRUE)
|
.requires(source -> source.getPermissionValue("velocity.command.info") != Tristate.FALSE)
|
||||||
.executes(new Info(server))
|
.executes(new Info(server))
|
||||||
.build();
|
.build();
|
||||||
final LiteralCommandNode<CommandSource> plugins = BrigadierCommand
|
final LiteralCommandNode<CommandSource> plugins = BrigadierCommand
|
||||||
|
|||||||
@ -78,8 +78,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
private boolean onlineModeKickExistingPlayers = false;
|
private boolean onlineModeKickExistingPlayers = false;
|
||||||
@Expose
|
@Expose
|
||||||
private PingPassthroughMode pingPassthrough = PingPassthroughMode.DISABLED;
|
private PingPassthroughMode pingPassthrough = PingPassthroughMode.DISABLED;
|
||||||
@Expose
|
|
||||||
private boolean samplePlayersInPing = false;
|
|
||||||
private final Servers servers;
|
private final Servers servers;
|
||||||
private final ForcedHosts forcedHosts;
|
private final ForcedHosts forcedHosts;
|
||||||
@Expose
|
@Expose
|
||||||
@ -107,9 +105,8 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
boolean preventClientProxyConnections, boolean announceForge,
|
boolean preventClientProxyConnections, boolean announceForge,
|
||||||
PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret,
|
PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret,
|
||||||
boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough,
|
boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough,
|
||||||
boolean samplePlayersInPing, boolean enablePlayerAddressLogging, Servers servers,
|
boolean enablePlayerAddressLogging, Servers servers, ForcedHosts forcedHosts,
|
||||||
ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics,
|
Advanced advanced, Query query, Metrics metrics, boolean forceKeyAuthentication) {
|
||||||
boolean forceKeyAuthentication) {
|
|
||||||
this.bind = bind;
|
this.bind = bind;
|
||||||
this.motd = motd;
|
this.motd = motd;
|
||||||
this.showMaxPlayers = showMaxPlayers;
|
this.showMaxPlayers = showMaxPlayers;
|
||||||
@ -120,7 +117,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
this.forwardingSecret = forwardingSecret;
|
this.forwardingSecret = forwardingSecret;
|
||||||
this.onlineModeKickExistingPlayers = onlineModeKickExistingPlayers;
|
this.onlineModeKickExistingPlayers = onlineModeKickExistingPlayers;
|
||||||
this.pingPassthrough = pingPassthrough;
|
this.pingPassthrough = pingPassthrough;
|
||||||
this.samplePlayersInPing = samplePlayersInPing;
|
|
||||||
this.enablePlayerAddressLogging = enablePlayerAddressLogging;
|
this.enablePlayerAddressLogging = enablePlayerAddressLogging;
|
||||||
this.servers = servers;
|
this.servers = servers;
|
||||||
this.forcedHosts = forcedHosts;
|
this.forcedHosts = forcedHosts;
|
||||||
@ -234,11 +230,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (advanced.commandRateLimit < 0) {
|
|
||||||
logger.error("Invalid command rate limit {}", advanced.commandRateLimit);
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadFavicon();
|
loadFavicon();
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
@ -360,31 +351,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
return advanced.getReadTimeout();
|
return advanced.getReadTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCommandRatelimit() {
|
|
||||||
return advanced.getCommandRateLimit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTabCompleteRatelimit() {
|
|
||||||
return advanced.getTabCompleteRateLimit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getKickAfterRateLimitedTabCompletes() {
|
|
||||||
return advanced.getKickAfterRateLimitedTabCompletes();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isForwardCommandsIfRateLimited() {
|
|
||||||
return advanced.isForwardCommandsIfRateLimited();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getKickAfterRateLimitedCommands() {
|
|
||||||
return advanced.getKickAfterRateLimitedCommands();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isProxyProtocol() {
|
public boolean isProxyProtocol() {
|
||||||
return advanced.isProxyProtocol();
|
return advanced.isProxyProtocol();
|
||||||
}
|
}
|
||||||
@ -405,10 +371,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
return pingPassthrough;
|
return pingPassthrough;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getSamplePlayersInPing() {
|
|
||||||
return samplePlayersInPing;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isPlayerAddressLoggingEnabled() {
|
public boolean isPlayerAddressLoggingEnabled() {
|
||||||
return enablePlayerAddressLogging;
|
return enablePlayerAddressLogging;
|
||||||
}
|
}
|
||||||
@ -445,10 +407,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
return forceKeyAuthentication;
|
return forceKeyAuthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnableReusePort() {
|
|
||||||
return advanced.isEnableReusePort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(this)
|
return MoreObjects.toStringHelper(this)
|
||||||
@ -545,8 +503,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
final PingPassthroughMode pingPassthroughMode = config.getEnumOrElse("ping-passthrough",
|
final PingPassthroughMode pingPassthroughMode = config.getEnumOrElse("ping-passthrough",
|
||||||
PingPassthroughMode.DISABLED);
|
PingPassthroughMode.DISABLED);
|
||||||
|
|
||||||
final boolean samplePlayersInPing = config.getOrElse("sample-players-in-ping", false);
|
|
||||||
|
|
||||||
final String bind = config.getOrElse("bind", "0.0.0.0:25565");
|
final String bind = config.getOrElse("bind", "0.0.0.0:25565");
|
||||||
final int maxPlayers = config.getIntOrElse("show-max-players", 500);
|
final int maxPlayers = config.getIntOrElse("show-max-players", 500);
|
||||||
final boolean onlineMode = config.getOrElse("online-mode", true);
|
final boolean onlineMode = config.getOrElse("online-mode", true);
|
||||||
@ -577,7 +533,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
forwardingSecret,
|
forwardingSecret,
|
||||||
kickExisting,
|
kickExisting,
|
||||||
pingPassthroughMode,
|
pingPassthroughMode,
|
||||||
samplePlayersInPing,
|
|
||||||
enablePlayerAddressLogging,
|
enablePlayerAddressLogging,
|
||||||
new Servers(serversConfig),
|
new Servers(serversConfig),
|
||||||
new ForcedHosts(forcedHostsConfig),
|
new ForcedHosts(forcedHostsConfig),
|
||||||
@ -761,18 +716,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
private boolean logPlayerConnections = true;
|
private boolean logPlayerConnections = true;
|
||||||
@Expose
|
@Expose
|
||||||
private boolean acceptTransfers = false;
|
private boolean acceptTransfers = false;
|
||||||
@Expose
|
|
||||||
private boolean enableReusePort = false;
|
|
||||||
@Expose
|
|
||||||
private int commandRateLimit = 50;
|
|
||||||
@Expose
|
|
||||||
private boolean forwardCommandsIfRateLimited = true;
|
|
||||||
@Expose
|
|
||||||
private int kickAfterRateLimitedCommands = 5;
|
|
||||||
@Expose
|
|
||||||
private int tabCompleteRateLimit = 50;
|
|
||||||
@Expose
|
|
||||||
private int kickAfterRateLimitedTabCompletes = 10;
|
|
||||||
|
|
||||||
private Advanced() {
|
private Advanced() {
|
||||||
}
|
}
|
||||||
@ -798,12 +741,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
this.logCommandExecutions = config.getOrElse("log-command-executions", false);
|
this.logCommandExecutions = config.getOrElse("log-command-executions", false);
|
||||||
this.logPlayerConnections = config.getOrElse("log-player-connections", true);
|
this.logPlayerConnections = config.getOrElse("log-player-connections", true);
|
||||||
this.acceptTransfers = config.getOrElse("accepts-transfers", false);
|
this.acceptTransfers = config.getOrElse("accepts-transfers", false);
|
||||||
this.enableReusePort = config.getOrElse("enable-reuse-port", false);
|
|
||||||
this.commandRateLimit = config.getIntOrElse("command-rate-limit", 25);
|
|
||||||
this.forwardCommandsIfRateLimited = config.getOrElse("forward-commands-if-rate-limited", true);
|
|
||||||
this.kickAfterRateLimitedCommands = config.getIntOrElse("kick-after-rate-limited-commands", 0);
|
|
||||||
this.tabCompleteRateLimit = config.getIntOrElse("tab-complete-rate-limit", 10); // very lenient
|
|
||||||
this.kickAfterRateLimitedTabCompletes = config.getIntOrElse("kick-after-rate-limited-tab-completes", 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,30 +804,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
return this.acceptTransfers;
|
return this.acceptTransfers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnableReusePort() {
|
|
||||||
return enableReusePort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCommandRateLimit() {
|
|
||||||
return commandRateLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isForwardCommandsIfRateLimited() {
|
|
||||||
return forwardCommandsIfRateLimited;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getKickAfterRateLimitedCommands() {
|
|
||||||
return kickAfterRateLimitedCommands;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTabCompleteRateLimit() {
|
|
||||||
return tabCompleteRateLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getKickAfterRateLimitedTabCompletes() {
|
|
||||||
return kickAfterRateLimitedTabCompletes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Advanced{"
|
return "Advanced{"
|
||||||
@ -908,7 +821,6 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
+ ", logCommandExecutions=" + logCommandExecutions
|
+ ", logCommandExecutions=" + logCommandExecutions
|
||||||
+ ", logPlayerConnections=" + logPlayerConnections
|
+ ", logPlayerConnections=" + logPlayerConnections
|
||||||
+ ", acceptTransfers=" + acceptTransfers
|
+ ", acceptTransfers=" + acceptTransfers
|
||||||
+ ", enableReusePort=" + enableReusePort
|
|
||||||
+ '}';
|
+ '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,6 @@ import com.velocitypowered.proxy.protocol.netty.MinecraftCompressDecoder;
|
|||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftCompressorAndLengthEncoder;
|
import com.velocitypowered.proxy.protocol.netty.MinecraftCompressorAndLengthEncoder;
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder;
|
import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder;
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder;
|
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder;
|
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder;
|
||||||
import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueInboundHandler;
|
import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueInboundHandler;
|
||||||
import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueOutboundHandler;
|
import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueOutboundHandler;
|
||||||
@ -85,7 +84,6 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
|||||||
private static final Logger logger = LogManager.getLogger(MinecraftConnection.class);
|
private static final Logger logger = LogManager.getLogger(MinecraftConnection.class);
|
||||||
|
|
||||||
private final Channel channel;
|
private final Channel channel;
|
||||||
public boolean pendingConfigurationSwitch = false;
|
|
||||||
private SocketAddress remoteAddress;
|
private SocketAddress remoteAddress;
|
||||||
private StateRegistry state;
|
private StateRegistry state;
|
||||||
private Map<StateRegistry, MinecraftSessionHandler> sessionHandlers;
|
private Map<StateRegistry, MinecraftSessionHandler> sessionHandlers;
|
||||||
@ -369,11 +367,6 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
|||||||
ensureInEventLoop();
|
ensureInEventLoop();
|
||||||
|
|
||||||
this.state = state;
|
this.state = state;
|
||||||
final MinecraftVarintFrameDecoder frameDecoder = this.channel.pipeline()
|
|
||||||
.get(MinecraftVarintFrameDecoder.class);
|
|
||||||
if (frameDecoder != null) {
|
|
||||||
frameDecoder.setState(state);
|
|
||||||
}
|
|
||||||
// If the connection is LEGACY (<1.6), the decoder and encoder are not set.
|
// If the connection is LEGACY (<1.6), the decoder and encoder are not set.
|
||||||
final MinecraftEncoder minecraftEncoder = this.channel.pipeline()
|
final MinecraftEncoder minecraftEncoder = this.channel.pipeline()
|
||||||
.get(MinecraftEncoder.class);
|
.get(MinecraftEncoder.class);
|
||||||
|
|||||||
@ -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;
|
||||||
@ -47,7 +49,6 @@ import com.velocitypowered.proxy.connection.util.ConnectionMessages;
|
|||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder;
|
|
||||||
import com.velocitypowered.proxy.protocol.packet.AvailableCommandsPacket;
|
import com.velocitypowered.proxy.protocol.packet.AvailableCommandsPacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.BossBarPacket;
|
import com.velocitypowered.proxy.protocol.packet.BossBarPacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket;
|
import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket;
|
||||||
@ -150,7 +151,6 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
MinecraftConnection smc = serverConn.ensureConnected();
|
MinecraftConnection smc = serverConn.ensureConnected();
|
||||||
smc.setAutoReading(false);
|
smc.setAutoReading(false);
|
||||||
// Even when not auto reading messages are still decoded. Decode them with the correct state
|
// Even when not auto reading messages are still decoded. Decode them with the correct state
|
||||||
smc.getChannel().pipeline().get(MinecraftVarintFrameDecoder.class).setState(StateRegistry.CONFIG);
|
|
||||||
smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.CONFIG);
|
smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.CONFIG);
|
||||||
serverConn.getPlayer().switchToConfigState();
|
serverConn.getPlayer().switchToConfigState();
|
||||||
return true;
|
return true;
|
||||||
@ -290,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(
|
||||||
|
|||||||
@ -20,7 +20,6 @@ package com.velocitypowered.proxy.connection.backend;
|
|||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.ServerConnection;
|
import com.velocitypowered.api.proxy.ServerConnection;
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
|
||||||
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
||||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
@ -302,24 +301,9 @@ public class BungeeCordMessageResponder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processGetPlayerServer(ByteBufDataInput in) {
|
static String getBungeeCordChannel(ProtocolVersion version) {
|
||||||
proxy.getPlayer(in.readUTF()).ifPresent(player -> {
|
return version.noLessThan(ProtocolVersion.MINECRAFT_1_13) ? MODERN_CHANNEL.getId()
|
||||||
player.getCurrentServer().ifPresent(server -> {
|
: LEGACY_CHANNEL.getId();
|
||||||
ByteBuf buf = Unpooled.buffer();
|
|
||||||
ByteBufDataOutput out = new ByteBufDataOutput(buf);
|
|
||||||
|
|
||||||
out.writeUTF("GetPlayerServer");
|
|
||||||
out.writeUTF(player.getUsername());
|
|
||||||
out.writeUTF(server.getServerInfo().getName());
|
|
||||||
|
|
||||||
sendResponseOnConnection(buf);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static ChannelIdentifier getBungeeCordChannel(ProtocolVersion version) {
|
|
||||||
return version.noLessThan(ProtocolVersion.MINECRAFT_1_13) ? MODERN_CHANNEL
|
|
||||||
: LEGACY_CHANNEL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this method will always release the buffer!
|
// Note: this method will always release the buffer!
|
||||||
@ -330,8 +314,8 @@ public class BungeeCordMessageResponder {
|
|||||||
// Note: this method will always release the buffer!
|
// Note: this method will always release the buffer!
|
||||||
private static void sendServerResponse(ConnectedPlayer player, ByteBuf buf) {
|
private static void sendServerResponse(ConnectedPlayer player, ByteBuf buf) {
|
||||||
MinecraftConnection serverConnection = player.ensureAndGetCurrentServer().ensureConnected();
|
MinecraftConnection serverConnection = player.ensureAndGetCurrentServer().ensureConnected();
|
||||||
ChannelIdentifier chan = getBungeeCordChannel(serverConnection.getProtocolVersion());
|
String chan = getBungeeCordChannel(serverConnection.getProtocolVersion());
|
||||||
PluginMessagePacket msg = new PluginMessagePacket(chan.getId(), buf);
|
PluginMessagePacket msg = new PluginMessagePacket(chan, buf);
|
||||||
serverConnection.write(msg);
|
serverConnection.write(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,9 +331,6 @@ public class BungeeCordMessageResponder {
|
|||||||
ByteBufDataInput in = new ByteBufDataInput(message.content());
|
ByteBufDataInput in = new ByteBufDataInput(message.content());
|
||||||
String subChannel = in.readUTF();
|
String subChannel = in.readUTF();
|
||||||
switch (subChannel) {
|
switch (subChannel) {
|
||||||
case "GetPlayerServer":
|
|
||||||
this.processGetPlayerServer(in);
|
|
||||||
break;
|
|
||||||
case "ForwardToPlayer":
|
case "ForwardToPlayer":
|
||||||
this.processForwardToPlayer(in);
|
this.processForwardToPlayer(in);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.connection.backend;
|
package com.velocitypowered.proxy.connection.backend;
|
||||||
|
|
||||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
|
||||||
import com.velocitypowered.api.event.connection.PreTransferEvent;
|
import com.velocitypowered.api.event.connection.PreTransferEvent;
|
||||||
import com.velocitypowered.api.event.player.CookieRequestEvent;
|
import com.velocitypowered.api.event.player.CookieRequestEvent;
|
||||||
import com.velocitypowered.api.event.player.CookieStoreEvent;
|
import com.velocitypowered.api.event.player.CookieStoreEvent;
|
||||||
@ -25,7 +24,6 @@ import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
|||||||
import com.velocitypowered.api.event.player.ServerResourcePackRemoveEvent;
|
import com.velocitypowered.api.event.player.ServerResourcePackRemoveEvent;
|
||||||
import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
|
import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
|
||||||
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.connection.MinecraftConnection;
|
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||||
@ -40,7 +38,6 @@ import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
|
|||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder;
|
|
||||||
import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket;
|
import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket;
|
import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.DisconnectPacket;
|
import com.velocitypowered.proxy.protocol.packet.DisconnectPacket;
|
||||||
@ -57,8 +54,6 @@ import com.velocitypowered.proxy.protocol.packet.config.RegistrySyncPacket;
|
|||||||
import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket;
|
import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.config.TagsUpdatePacket;
|
import com.velocitypowered.proxy.protocol.packet.config.TagsUpdatePacket;
|
||||||
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@ -233,7 +228,6 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
final ConnectedPlayer player = serverConn.getPlayer();
|
final ConnectedPlayer player = serverConn.getPlayer();
|
||||||
final ClientConfigSessionHandler configHandler = (ClientConfigSessionHandler) player.getConnection().getActiveSessionHandler();
|
final ClientConfigSessionHandler configHandler = (ClientConfigSessionHandler) player.getConnection().getActiveSessionHandler();
|
||||||
|
|
||||||
smc.getChannel().pipeline().get(MinecraftVarintFrameDecoder.class).setState(StateRegistry.PLAY);
|
|
||||||
smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.PLAY);
|
smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.PLAY);
|
||||||
//noinspection DataFlowIssue
|
//noinspection DataFlowIssue
|
||||||
configHandler.handleBackendFinishUpdate(serverConn).thenRunAsync(() -> {
|
configHandler.handleBackendFinishUpdate(serverConn).thenRunAsync(() -> {
|
||||||
@ -267,29 +261,7 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion(),
|
PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion(),
|
||||||
serverConn.getPlayer().getProtocolVersion()));
|
serverConn.getPlayer().getProtocolVersion()));
|
||||||
} else {
|
} else {
|
||||||
byte[] bytes = ByteBufUtil.getBytes(packet.content());
|
serverConn.getPlayer().getConnection().write(packet.retain());
|
||||||
ChannelIdentifier id = this.server.getChannelRegistrar().getFromId(packet.getChannel());
|
|
||||||
|
|
||||||
if (id == null) {
|
|
||||||
serverConn.getPlayer().getConnection().write(packet.retain());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling this stuff async means that we should probably pause
|
|
||||||
// the connection while we toss this off into another pool
|
|
||||||
this.serverConn.getConnection().setAutoReading(false);
|
|
||||||
this.server.getEventManager()
|
|
||||||
.fire(new PluginMessageEvent(serverConn, serverConn.getPlayer(), id, bytes))
|
|
||||||
.thenAcceptAsync(pme -> {
|
|
||||||
if (pme.getResult().isAllowed() && !serverConn.getPlayer().getConnection().isClosed()) {
|
|
||||||
serverConn.getPlayer().getConnection().write(new PluginMessagePacket(
|
|
||||||
pme.getIdentifier().getId(), Unpooled.wrappedBuffer(bytes)));
|
|
||||||
}
|
|
||||||
this.serverConn.getConnection().setAutoReading(true);
|
|
||||||
}, serverConn.ensureConnected().eventLoop()).exceptionally((ex) -> {
|
|
||||||
logger.error("Exception while handling plugin message {}", packet, ex);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -97,7 +97,7 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
|
|||||||
// Initiate a regular connection and move over to it.
|
// Initiate a regular connection and move over to it.
|
||||||
ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(),
|
ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(),
|
||||||
mcConnection, inbound.getVirtualHost().orElse(null), inbound.getRawVirtualHost().orElse(null), onlineMode,
|
mcConnection, inbound.getVirtualHost().orElse(null), inbound.getRawVirtualHost().orElse(null), onlineMode,
|
||||||
inbound.getHandshakeIntent(), inbound.getIdentifiedKey());
|
inbound.getIdentifiedKey());
|
||||||
this.connectedPlayer = player;
|
this.connectedPlayer = player;
|
||||||
if (!server.canRegisterConnection(player)) {
|
if (!server.canRegisterConnection(player)) {
|
||||||
player.disconnect0(
|
player.disconnect0(
|
||||||
@ -106,9 +106,7 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
|
|||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server.getConfiguration().isLogPlayerConnections()) {
|
logger.info("{} has connected", player);
|
||||||
logger.info("{} has connected", player);
|
|
||||||
}
|
|
||||||
|
|
||||||
return server.getEventManager()
|
return server.getEventManager()
|
||||||
.fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
|
.fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
|
||||||
|
|||||||
@ -17,17 +17,14 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.connection.client;
|
package com.velocitypowered.proxy.connection.client;
|
||||||
|
|
||||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
|
||||||
import com.velocitypowered.api.event.player.CookieReceiveEvent;
|
import com.velocitypowered.api.event.player.CookieReceiveEvent;
|
||||||
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
|
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerConfigurationEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerFinishConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerFinishConfigurationEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerFinishedConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerFinishedConfigurationEvent;
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||||
import com.velocitypowered.proxy.connection.backend.BungeeCordMessageResponder;
|
|
||||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||||
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
|
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
@ -44,7 +41,6 @@ import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket;
|
|||||||
import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket;
|
import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket;
|
||||||
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -127,32 +123,8 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
brandChannel = packet.getChannel();
|
brandChannel = packet.getChannel();
|
||||||
// Client sends `minecraft:brand` packet immediately after Login,
|
// Client sends `minecraft:brand` packet immediately after Login,
|
||||||
// but at this time the backend server may not be ready
|
// but at this time the backend server may not be ready
|
||||||
} else if (BungeeCordMessageResponder.isBungeeCordMessage(packet)) {
|
|
||||||
return true;
|
|
||||||
} else if (serverConn != null) {
|
} else if (serverConn != null) {
|
||||||
byte[] bytes = ByteBufUtil.getBytes(packet.content());
|
serverConn.ensureConnected().write(packet.retain());
|
||||||
ChannelIdentifier id = this.server.getChannelRegistrar().getFromId(packet.getChannel());
|
|
||||||
|
|
||||||
if (id == null) {
|
|
||||||
serverConn.ensureConnected().write(packet.retain());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling this stuff async means that we should probably pause
|
|
||||||
// the connection while we toss this off into another pool
|
|
||||||
serverConn.getPlayer().getConnection().setAutoReading(false);
|
|
||||||
this.server.getEventManager()
|
|
||||||
.fire(new PluginMessageEvent(serverConn.getPlayer(), serverConn, id, bytes))
|
|
||||||
.thenAcceptAsync(pme -> {
|
|
||||||
if (pme.getResult().isAllowed() && serverConn.getConnection() != null) {
|
|
||||||
serverConn.ensureConnected().write(new PluginMessagePacket(
|
|
||||||
pme.getIdentifier().getId(), Unpooled.wrappedBuffer(bytes)));
|
|
||||||
}
|
|
||||||
serverConn.getPlayer().getConnection().setAutoReading(true);
|
|
||||||
}, player.getConnection().eventLoop()).exceptionally((ex) -> {
|
|
||||||
logger.error("Exception while handling plugin message packet for {}", player, ex);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -170,11 +142,7 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
@Override
|
@Override
|
||||||
public boolean handle(KnownPacksPacket packet) {
|
public boolean handle(KnownPacksPacket packet) {
|
||||||
callConfigurationEvent().thenRun(() -> {
|
callConfigurationEvent().thenRun(() -> {
|
||||||
VelocityServerConnection targetServer =
|
player.getConnectionInFlightOrConnectedServer().ensureConnected().write(packet);
|
||||||
player.getConnectionInFlightOrConnectedServer();
|
|
||||||
if (targetServer != null) {
|
|
||||||
targetServer.ensureConnected().write(packet);
|
|
||||||
}
|
|
||||||
}).exceptionally(ex -> {
|
}).exceptionally(ex -> {
|
||||||
logger.error("Error forwarding known packs response to backend:", ex);
|
logger.error("Error forwarding known packs response to backend:", ex);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -30,6 +30,8 @@ import com.velocitypowered.api.event.player.TabCompleteEvent;
|
|||||||
import com.velocitypowered.api.event.player.configuration.PlayerEnteredConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerEnteredConfigurationEvent;
|
||||||
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.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.connection.ConnectionTypes;
|
import com.velocitypowered.proxy.connection.ConnectionTypes;
|
||||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||||
@ -72,7 +74,6 @@ import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket;
|
|||||||
import com.velocitypowered.proxy.protocol.packet.title.GenericTitlePacket;
|
import com.velocitypowered.proxy.protocol.packet.title.GenericTitlePacket;
|
||||||
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||||
import com.velocitypowered.proxy.util.CharacterUtil;
|
import com.velocitypowered.proxy.util.CharacterUtil;
|
||||||
import com.velocitypowered.proxy.util.except.QuietRuntimeException;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
@ -112,8 +113,6 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
private CompletableFuture<Void> configSwitchFuture;
|
private CompletableFuture<Void> configSwitchFuture;
|
||||||
|
|
||||||
private int failedTabCompleteAttempts;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a client play session handler.
|
* Constructs a client play session handler.
|
||||||
*
|
*
|
||||||
@ -161,7 +160,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void activated() {
|
public void activated() {
|
||||||
configSwitchFuture = new CompletableFuture<>();
|
configSwitchFuture = new CompletableFuture<>();
|
||||||
Collection<ChannelIdentifier> channels =
|
Collection<String> channels =
|
||||||
server.getChannelRegistrar().getChannelsForProtocol(player.getProtocolVersion());
|
server.getChannelRegistrar().getChannelsForProtocol(player.getProtocolVersion());
|
||||||
if (!channels.isEmpty()) {
|
if (!channels.isEmpty()) {
|
||||||
PluginMessagePacket register = constructChannelsPacket(player.getProtocolVersion(), channels);
|
PluginMessagePacket register = constructChannelsPacket(player.getProtocolVersion(), channels);
|
||||||
@ -171,7 +170,6 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deactivated() {
|
public void deactivated() {
|
||||||
player.discardChatQueue();
|
|
||||||
for (PluginMessagePacket message : loginPluginMessages) {
|
for (PluginMessagePacket message : loginPluginMessages) {
|
||||||
ReferenceCountUtil.release(message);
|
ReferenceCountUtil.release(message);
|
||||||
}
|
}
|
||||||
@ -309,17 +307,20 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
logger.warn("A plugin message was received while the backend server was not "
|
logger.warn("A plugin message was received while the backend server was not "
|
||||||
+ "ready. Channel: {}. Packet discarded.", packet.getChannel());
|
+ "ready. Channel: {}. Packet discarded.", packet.getChannel());
|
||||||
} else if (PluginMessageUtil.isRegister(packet)) {
|
} else if (PluginMessageUtil.isRegister(packet)) {
|
||||||
List<ChannelIdentifier> channels =
|
List<String> channels = PluginMessageUtil.getChannels(packet);
|
||||||
PluginMessageUtil.getChannels(this.player.getClientsideChannels().size(), packet,
|
List<ChannelIdentifier> channelIdentifiers = new ArrayList<>();
|
||||||
this.player.getProtocolVersion());
|
for (String channel : channels) {
|
||||||
player.getClientsideChannels().addAll(channels);
|
try {
|
||||||
|
channelIdentifiers.add(MinecraftChannelIdentifier.from(channel));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
channelIdentifiers.add(new LegacyChannelIdentifier(channel));
|
||||||
|
}
|
||||||
|
}
|
||||||
server.getEventManager()
|
server.getEventManager()
|
||||||
.fireAndForget(
|
.fireAndForget(
|
||||||
new PlayerChannelRegisterEvent(player, ImmutableList.copyOf(channels)));
|
new PlayerChannelRegisterEvent(player, ImmutableList.copyOf(channelIdentifiers)));
|
||||||
backendConn.write(packet.retain());
|
backendConn.write(packet.retain());
|
||||||
} else if (PluginMessageUtil.isUnregister(packet)) {
|
} else if (PluginMessageUtil.isUnregister(packet)) {
|
||||||
player.getClientsideChannels()
|
|
||||||
.removeAll(PluginMessageUtil.getChannels(0, packet, this.player.getProtocolVersion()));
|
|
||||||
backendConn.write(packet.retain());
|
backendConn.write(packet.retain());
|
||||||
} else if (PluginMessageUtil.isMcBrand(packet)) {
|
} else if (PluginMessageUtil.isMcBrand(packet)) {
|
||||||
String brand = PluginMessageUtil.readBrandMessage(packet.content());
|
String brand = PluginMessageUtil.readBrandMessage(packet.content());
|
||||||
@ -339,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;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,14 +376,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(FinishedUpdatePacket packet) {
|
public boolean handle(FinishedUpdatePacket packet) {
|
||||||
if (!player.getConnection().pendingConfigurationSwitch) {
|
|
||||||
throw new QuietRuntimeException("Not expecting reconfiguration");
|
|
||||||
}
|
|
||||||
// Complete client switch
|
// Complete client switch
|
||||||
player.getConnection().setActiveSessionHandler(StateRegistry.CONFIG);
|
player.getConnection().setActiveSessionHandler(StateRegistry.CONFIG);
|
||||||
VelocityServerConnection serverConnection = player.getConnectedServer();
|
VelocityServerConnection serverConnection = player.getConnectedServer();
|
||||||
server.getEventManager()
|
server.getEventManager().fireAndForget(new PlayerEnteredConfigurationEvent(player, serverConnection));
|
||||||
.fireAndForget(new PlayerEnteredConfigurationEvent(player, serverConnection));
|
|
||||||
if (serverConnection != null) {
|
if (serverConnection != null) {
|
||||||
MinecraftConnection smc = serverConnection.ensureConnected();
|
MinecraftConnection smc = serverConnection.ensureConnected();
|
||||||
CompletableFuture.runAsync(() -> {
|
CompletableFuture.runAsync(() -> {
|
||||||
@ -447,13 +426,6 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handle(JoinGamePacket packet) {
|
|
||||||
// Forward the packet as normal, but discard any chat state we have queued - the client will do this too
|
|
||||||
player.discardChatQueue();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleGeneric(MinecraftPacket packet) {
|
public void handleGeneric(MinecraftPacket packet) {
|
||||||
VelocityServerConnection serverConnection = player.getConnectedServer();
|
VelocityServerConnection serverConnection = player.getConnectedServer();
|
||||||
@ -587,15 +559,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
// Tell the server about the proxy's plugin message channels.
|
// Tell the server about the proxy's plugin message channels.
|
||||||
ProtocolVersion serverVersion = serverMc.getProtocolVersion();
|
ProtocolVersion serverVersion = serverMc.getProtocolVersion();
|
||||||
final Collection<ChannelIdentifier> channels = server.getChannelRegistrar()
|
final Collection<String> channels = server.getChannelRegistrar()
|
||||||
.getChannelsForProtocol(serverMc.getProtocolVersion());
|
.getChannelsForProtocol(serverMc.getProtocolVersion());
|
||||||
if (!channels.isEmpty()) {
|
if (!channels.isEmpty()) {
|
||||||
serverMc.delayedWrite(constructChannelsPacket(serverVersion, channels));
|
serverMc.delayedWrite(constructChannelsPacket(serverVersion, channels));
|
||||||
}
|
}
|
||||||
// Tell the server about this client's plugin message channels.
|
|
||||||
if (!player.getClientsideChannels().isEmpty()) {
|
|
||||||
serverMc.delayedWrite(constructChannelsPacket(serverVersion, player.getClientsideChannels()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we had plugin messages queued during login/FML handshake, send them now.
|
// If we had plugin messages queued during login/FML handshake, send them now.
|
||||||
PluginMessagePacket pm;
|
PluginMessagePacket pm;
|
||||||
@ -677,17 +645,6 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!server.getTabCompleteRateLimiter().attempt(player.getUniqueId())) {
|
|
||||||
if (server.getConfiguration().isKickOnTabCompleteRateLimit()
|
|
||||||
&& failedTabCompleteAttempts++ >= server.getConfiguration().getKickAfterRateLimitedTabCompletes()) {
|
|
||||||
player.disconnect(Component.translatable("velocity.kick.tab-complete-rate-limit"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
failedTabCompleteAttempts = 0;
|
|
||||||
|
|
||||||
server.getCommandManager().offerBrigadierSuggestions(player, command)
|
server.getCommandManager().offerBrigadierSuggestions(player, command)
|
||||||
.thenAcceptAsync(suggestions -> {
|
.thenAcceptAsync(suggestions -> {
|
||||||
if (suggestions.isEmpty()) {
|
if (suggestions.isEmpty()) {
|
||||||
|
|||||||
@ -38,7 +38,6 @@ import com.velocitypowered.api.event.player.PlayerModInfoEvent;
|
|||||||
import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent;
|
import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent;
|
||||||
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
|
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerEnterConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerEnterConfigurationEvent;
|
||||||
import com.velocitypowered.api.network.HandshakeIntent;
|
|
||||||
import com.velocitypowered.api.network.ProtocolState;
|
import com.velocitypowered.api.network.ProtocolState;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.permission.PermissionFunction;
|
import com.velocitypowered.api.permission.PermissionFunction;
|
||||||
@ -99,7 +98,6 @@ import com.velocitypowered.proxy.tablist.VelocityTabListLegacy;
|
|||||||
import com.velocitypowered.proxy.util.ClosestLocaleMatcher;
|
import com.velocitypowered.proxy.util.ClosestLocaleMatcher;
|
||||||
import com.velocitypowered.proxy.util.DurationUtils;
|
import com.velocitypowered.proxy.util.DurationUtils;
|
||||||
import com.velocitypowered.proxy.util.TranslatableMapper;
|
import com.velocitypowered.proxy.util.TranslatableMapper;
|
||||||
import com.velocitypowered.proxy.util.collect.CappedSet;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
@ -123,7 +121,6 @@ import net.kyori.adventure.permission.PermissionChecker;
|
|||||||
import net.kyori.adventure.platform.facet.FacetPointers;
|
import net.kyori.adventure.platform.facet.FacetPointers;
|
||||||
import net.kyori.adventure.platform.facet.FacetPointers.Type;
|
import net.kyori.adventure.platform.facet.FacetPointers.Type;
|
||||||
import net.kyori.adventure.pointer.Pointers;
|
import net.kyori.adventure.pointer.Pointers;
|
||||||
import net.kyori.adventure.pointer.PointersSupplier;
|
|
||||||
import net.kyori.adventure.resource.ResourcePackInfoLike;
|
import net.kyori.adventure.resource.ResourcePackInfoLike;
|
||||||
import net.kyori.adventure.resource.ResourcePackRequest;
|
import net.kyori.adventure.resource.ResourcePackRequest;
|
||||||
import net.kyori.adventure.resource.ResourcePackRequestLike;
|
import net.kyori.adventure.resource.ResourcePackRequestLike;
|
||||||
@ -146,30 +143,19 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, KeyIdentifiable,
|
public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, KeyIdentifiable,
|
||||||
VelocityInboundConnection {
|
VelocityInboundConnection {
|
||||||
|
|
||||||
public static final int MAX_CLIENTSIDE_PLUGIN_CHANNELS = Integer.getInteger("velocity.max-clientside-plugin-channels", 1024);
|
|
||||||
private static final PlainTextComponentSerializer PASS_THRU_TRANSLATE =
|
private static final PlainTextComponentSerializer PASS_THRU_TRANSLATE =
|
||||||
PlainTextComponentSerializer.builder().flattener(TranslatableMapper.FLATTENER).build();
|
PlainTextComponentSerializer.builder().flattener(TranslatableMapper.FLATTENER).build();
|
||||||
static final PermissionProvider DEFAULT_PERMISSIONS = s -> PermissionFunction.ALWAYS_UNDEFINED;
|
static final PermissionProvider DEFAULT_PERMISSIONS = s -> PermissionFunction.ALWAYS_UNDEFINED;
|
||||||
|
|
||||||
private static final ComponentLogger logger = ComponentLogger.logger(ConnectedPlayer.class);
|
private static final ComponentLogger logger = ComponentLogger.logger(ConnectedPlayer.class);
|
||||||
|
|
||||||
private static final @NotNull PointersSupplier<ConnectedPlayer> POINTERS_SUPPLIER =
|
private final Identity identity = new IdentityImpl();
|
||||||
PointersSupplier.<ConnectedPlayer>builder()
|
|
||||||
.resolving(Identity.UUID, Player::getUniqueId)
|
|
||||||
.resolving(Identity.NAME, Player::getUsername)
|
|
||||||
.resolving(Identity.DISPLAY_NAME, player -> Component.text(player.getUsername()))
|
|
||||||
.resolving(Identity.LOCALE, Player::getEffectiveLocale)
|
|
||||||
.resolving(PermissionChecker.POINTER, Player::getPermissionChecker)
|
|
||||||
.resolving(FacetPointers.TYPE, player -> Type.PLAYER)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The actual Minecraft connection. This is actually a wrapper object around the Netty channel.
|
* The actual Minecraft connection. This is actually a wrapper object around the Netty channel.
|
||||||
*/
|
*/
|
||||||
private final MinecraftConnection connection;
|
private final MinecraftConnection connection;
|
||||||
private final @Nullable InetSocketAddress virtualHost;
|
private final @Nullable InetSocketAddress virtualHost;
|
||||||
private final @Nullable String rawVirtualHost;
|
private final @Nullable String rawVirtualHost;
|
||||||
private final HandshakeIntent handshakeIntent;
|
|
||||||
private GameProfile profile;
|
private GameProfile profile;
|
||||||
private PermissionFunction permissionFunction;
|
private PermissionFunction permissionFunction;
|
||||||
private int tryIndex = 0;
|
private int tryIndex = 0;
|
||||||
@ -185,32 +171,37 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
private final InternalTabList tabList;
|
private final InternalTabList tabList;
|
||||||
private final VelocityServer server;
|
private final VelocityServer server;
|
||||||
private ClientConnectionPhase connectionPhase;
|
private ClientConnectionPhase connectionPhase;
|
||||||
private final Collection<ChannelIdentifier> clientsideChannels;
|
|
||||||
private final CompletableFuture<Void> teardownFuture = new CompletableFuture<>();
|
private final CompletableFuture<Void> teardownFuture = new CompletableFuture<>();
|
||||||
private @MonotonicNonNull List<String> serversToTry = null;
|
private @MonotonicNonNull List<String> serversToTry = null;
|
||||||
private final ResourcePackHandler resourcePackHandler;
|
private final ResourcePackHandler resourcePackHandler;
|
||||||
private final BundleDelimiterHandler bundleHandler = new BundleDelimiterHandler(this);
|
private final BundleDelimiterHandler bundleHandler = new BundleDelimiterHandler(this);
|
||||||
|
|
||||||
|
private final @NotNull Pointers pointers =
|
||||||
|
Player.super.pointers().toBuilder()
|
||||||
|
.withDynamic(Identity.UUID, this::getUniqueId)
|
||||||
|
.withDynamic(Identity.NAME, this::getUsername)
|
||||||
|
.withDynamic(Identity.DISPLAY_NAME, () -> Component.text(this.getUsername()))
|
||||||
|
.withDynamic(Identity.LOCALE, this::getEffectiveLocale)
|
||||||
|
.withStatic(PermissionChecker.POINTER, getPermissionChecker())
|
||||||
|
.withStatic(FacetPointers.TYPE, Type.PLAYER).build();
|
||||||
private @Nullable String clientBrand;
|
private @Nullable String clientBrand;
|
||||||
private @Nullable Locale effectiveLocale;
|
private @Nullable Locale effectiveLocale;
|
||||||
private final @Nullable IdentifiedKey playerKey;
|
private final @Nullable IdentifiedKey playerKey;
|
||||||
private @Nullable ClientSettingsPacket clientSettingsPacket;
|
private @Nullable ClientSettingsPacket clientSettingsPacket;
|
||||||
private volatile ChatQueue chatQueue;
|
private final ChatQueue chatQueue;
|
||||||
private final ChatBuilderFactory chatBuilderFactory;
|
private final ChatBuilderFactory chatBuilderFactory;
|
||||||
|
|
||||||
ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection,
|
ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection,
|
||||||
@Nullable InetSocketAddress virtualHost, @Nullable String rawVirtualHost, boolean onlineMode,
|
@Nullable InetSocketAddress virtualHost, @Nullable String rawVirtualHost, boolean onlineMode,
|
||||||
HandshakeIntent handshakeIntent, @Nullable IdentifiedKey playerKey) {
|
@Nullable IdentifiedKey playerKey) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.virtualHost = virtualHost;
|
this.virtualHost = virtualHost;
|
||||||
this.rawVirtualHost = rawVirtualHost;
|
this.rawVirtualHost = rawVirtualHost;
|
||||||
this.handshakeIntent = handshakeIntent;
|
|
||||||
this.permissionFunction = PermissionFunction.ALWAYS_UNDEFINED;
|
this.permissionFunction = PermissionFunction.ALWAYS_UNDEFINED;
|
||||||
this.connectionPhase = connection.getType().getInitialClientPhase();
|
this.connectionPhase = connection.getType().getInitialClientPhase();
|
||||||
this.onlineMode = onlineMode;
|
this.onlineMode = onlineMode;
|
||||||
this.clientsideChannels = CappedSet.create(MAX_CLIENTSIDE_PLUGIN_CHANNELS);
|
|
||||||
|
|
||||||
if (connection.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
|
if (connection.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
|
||||||
this.tabList = new VelocityTabList(this);
|
this.tabList = new VelocityTabList(this);
|
||||||
@ -242,24 +233,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
return chatQueue;
|
return chatQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Discards any messages still being processed by the {@link ChatQueue}, and creates a fresh state for future packets.
|
|
||||||
* This should be used on server switches, or whenever the client resets its own 'last seen' state.
|
|
||||||
*/
|
|
||||||
public void discardChatQueue() {
|
|
||||||
// No need for atomic swap, should only be called from event loop
|
|
||||||
final ChatQueue oldChatQueue = chatQueue;
|
|
||||||
chatQueue = new ChatQueue(this);
|
|
||||||
oldChatQueue.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BundleDelimiterHandler getBundleHandler() {
|
public BundleDelimiterHandler getBundleHandler() {
|
||||||
return this.bundleHandler;
|
return this.bundleHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull Identity identity() {
|
public @NonNull Identity identity() {
|
||||||
return Identity.identity(this.getUniqueId());
|
return this.identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -365,7 +345,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Pointers pointers() {
|
public @NotNull Pointers pointers() {
|
||||||
return POINTERS_SUPPLIER.view(this);
|
return this.pointers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -398,20 +378,14 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates the message in the user's locale, falling back to the default locale if not set.
|
* Translates the message in the user's locale.
|
||||||
*
|
*
|
||||||
* @param message the message to translate
|
* @param message the message to translate
|
||||||
* @return the translated message
|
* @return the translated message
|
||||||
*/
|
*/
|
||||||
public Component translateMessage(Component message) {
|
public Component translateMessage(Component message) {
|
||||||
Locale locale = this.getEffectiveLocale();
|
Locale locale = ClosestLocaleMatcher.INSTANCE
|
||||||
if (locale == null && settings != null) {
|
.lookupClosest(getEffectiveLocale() == null ? Locale.getDefault() : getEffectiveLocale());
|
||||||
locale = settings.getLocale();
|
|
||||||
}
|
|
||||||
if (locale == null) {
|
|
||||||
locale = Locale.getDefault();
|
|
||||||
}
|
|
||||||
locale = ClosestLocaleMatcher.INSTANCE.lookupClosest(locale);
|
|
||||||
return GlobalTranslator.render(message, locale);
|
return GlobalTranslator.render(message, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1113,14 +1087,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
throw new IllegalStateException("Can only send server links in CONFIGURATION or PLAY protocol");
|
throw new IllegalStateException("Can only send server links in CONFIGURATION or PLAY protocol");
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.write(new ClientboundServerLinksPacket(links.stream()
|
connection.write(new ClientboundServerLinksPacket(List.copyOf(links).stream()
|
||||||
.map(l -> new ClientboundServerLinksPacket.ServerLink(
|
.map(l -> new ClientboundServerLinksPacket.ServerLink(l, getProtocolVersion())).toList()));
|
||||||
l.getBuiltInType().map(Enum::ordinal).orElse(-1),
|
|
||||||
l.getCustomLabel()
|
|
||||||
.map(c -> new ComponentHolder(getProtocolVersion(), translateMessage(c)))
|
|
||||||
.orElse(null),
|
|
||||||
l.getUrl().toString()))
|
|
||||||
.toList()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1316,17 +1284,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
public void switchToConfigState() {
|
public void switchToConfigState() {
|
||||||
server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer()))
|
server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer()))
|
||||||
.completeOnTimeout(null, 5, TimeUnit.SECONDS).thenRunAsync(() -> {
|
.completeOnTimeout(null, 5, TimeUnit.SECONDS).thenRunAsync(() -> {
|
||||||
// if the connection was closed earlier, there is a risk that the player is no longer connected
|
|
||||||
if (!connection.getChannel().isActive()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bundleHandler.isInBundleSession()) {
|
if (bundleHandler.isInBundleSession()) {
|
||||||
bundleHandler.toggleBundleSession();
|
bundleHandler.toggleBundleSession();
|
||||||
connection.write(BundleDelimiterPacket.INSTANCE);
|
connection.write(BundleDelimiterPacket.INSTANCE);
|
||||||
}
|
}
|
||||||
connection.write(StartUpdatePacket.INSTANCE);
|
connection.write(StartUpdatePacket.INSTANCE);
|
||||||
connection.pendingConfigurationSwitch = true;
|
|
||||||
connection.getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.CONFIG);
|
connection.getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.CONFIG);
|
||||||
// Make sure we don't send any play packets to the player after update start
|
// Make sure we don't send any play packets to the player after update start
|
||||||
connection.addPlayPacketQueueHandler();
|
connection.addPlayPacketQueueHandler();
|
||||||
@ -1355,28 +1317,22 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
this.connectionPhase = connectionPhase;
|
this.connectionPhase = connectionPhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all the plugin message channels that registered by client.
|
|
||||||
*
|
|
||||||
* @return the channels
|
|
||||||
*/
|
|
||||||
public Collection<ChannelIdentifier> getClientsideChannels() {
|
|
||||||
return clientsideChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable IdentifiedKey getIdentifiedKey() {
|
public @Nullable IdentifiedKey getIdentifiedKey() {
|
||||||
return playerKey;
|
return playerKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class IdentityImpl implements Identity {
|
||||||
public ProtocolState getProtocolState() {
|
|
||||||
return connection.getState().toProtocolState();
|
@Override
|
||||||
|
public @NonNull UUID uuid() {
|
||||||
|
return ConnectedPlayer.this.getUniqueId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HandshakeIntent getHandshakeIntent() {
|
public ProtocolState getProtocolState() {
|
||||||
return handshakeIntent;
|
return connection.getState().toProtocolState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder {
|
private final class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder {
|
||||||
|
|||||||
@ -277,10 +277,5 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
|
|||||||
public ProtocolState getProtocolState() {
|
public ProtocolState getProtocolState() {
|
||||||
return connection.getState().toProtocolState();
|
return connection.getState().toProtocolState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandshakeIntent getHandshakeIntent() {
|
|
||||||
return HandshakeIntent.STATUS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.connection.client;
|
package com.velocitypowered.proxy.connection.client;
|
||||||
|
|
||||||
import com.velocitypowered.api.network.HandshakeIntent;
|
|
||||||
import com.velocitypowered.api.network.ProtocolState;
|
import com.velocitypowered.api.network.ProtocolState;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.proxy.InboundConnection;
|
import com.velocitypowered.api.proxy.InboundConnection;
|
||||||
@ -99,11 +98,6 @@ public final class InitialInboundConnection implements VelocityInboundConnection
|
|||||||
return connection.getState().toProtocolState();
|
return connection.getState().toProtocolState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandshakeIntent getHandshakeIntent() {
|
|
||||||
return handshake.getIntent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects the connection from the server.
|
* Disconnects the connection from the server.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.connection.client;
|
package com.velocitypowered.proxy.connection.client;
|
||||||
|
|
||||||
import com.velocitypowered.api.network.HandshakeIntent;
|
|
||||||
import com.velocitypowered.api.network.ProtocolState;
|
import com.velocitypowered.api.network.ProtocolState;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.proxy.LoginPhaseConnection;
|
import com.velocitypowered.api.proxy.LoginPhaseConnection;
|
||||||
@ -178,9 +177,4 @@ public class LoginInboundConnection implements LoginPhaseConnection, KeyIdentifi
|
|||||||
public ProtocolState getProtocolState() {
|
public ProtocolState getProtocolState() {
|
||||||
return delegate.getProtocolState();
|
return delegate.getProtocolState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandshakeIntent getHandshakeIntent() {
|
|
||||||
return delegate.getHandshakeIntent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,7 +64,6 @@ class LegacyForgeUtil {
|
|||||||
if (discriminator == MOD_LIST_DISCRIMINATOR) {
|
if (discriminator == MOD_LIST_DISCRIMINATOR) {
|
||||||
ImmutableList.Builder<ModInfo.Mod> mods = ImmutableList.builder();
|
ImmutableList.Builder<ModInfo.Mod> mods = ImmutableList.builder();
|
||||||
int modCount = ProtocolUtils.readVarInt(contents);
|
int modCount = ProtocolUtils.readVarInt(contents);
|
||||||
Preconditions.checkArgument(modCount < 1024, "Oversized mods list");
|
|
||||||
|
|
||||||
for (int index = 0; index < modCount; index++) {
|
for (int index = 0; index < modCount; index++) {
|
||||||
String id = ProtocolUtils.readString(contents);
|
String id = ProtocolUtils.readString(contents);
|
||||||
|
|||||||
@ -30,12 +30,10 @@ import com.velocitypowered.proxy.config.VelocityConfiguration;
|
|||||||
import com.velocitypowered.proxy.server.VelocityRegisteredServer;
|
import com.velocitypowered.proxy.server.VelocityRegisteredServer;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common utilities for handling server list ping results.
|
* Common utilities for handling server list ping results.
|
||||||
@ -53,27 +51,11 @@ public class ServerListPingHandler {
|
|||||||
version = ProtocolVersion.MAXIMUM_VERSION;
|
version = ProtocolVersion.MAXIMUM_VERSION;
|
||||||
}
|
}
|
||||||
VelocityConfiguration configuration = server.getConfiguration();
|
VelocityConfiguration configuration = server.getConfiguration();
|
||||||
List<ServerPing.SamplePlayer> samplePlayers;
|
|
||||||
if (configuration.getSamplePlayersInPing()) {
|
|
||||||
List<ServerPing.SamplePlayer> unshuffledPlayers = server.getAllPlayers().stream()
|
|
||||||
.map(p -> {
|
|
||||||
if (p.getPlayerSettings().isClientListingAllowed()) {
|
|
||||||
return new ServerPing.SamplePlayer(p.getUsername(), p.getUniqueId());
|
|
||||||
} else {
|
|
||||||
return ServerPing.SamplePlayer.ANONYMOUS;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
Collections.shuffle(unshuffledPlayers);
|
|
||||||
samplePlayers = unshuffledPlayers.subList(0, Math.min(12, server.getPlayerCount()));
|
|
||||||
} else {
|
|
||||||
samplePlayers = ImmutableList.of();
|
|
||||||
}
|
|
||||||
return new ServerPing(
|
return new ServerPing(
|
||||||
new ServerPing.Version(version.getProtocol(),
|
new ServerPing.Version(version.getProtocol(),
|
||||||
"Velocity " + ProtocolVersion.SUPPORTED_VERSION_STRING),
|
"Velocity " + ProtocolVersion.SUPPORTED_VERSION_STRING),
|
||||||
new ServerPing.Players(server.getPlayerCount(), configuration.getShowMaxPlayers(),
|
new ServerPing.Players(server.getPlayerCount(), configuration.getShowMaxPlayers(),
|
||||||
samplePlayers),
|
ImmutableList.of()),
|
||||||
configuration.getMotd(),
|
configuration.getMotd(),
|
||||||
configuration.getFavicon().orElse(null),
|
configuration.getFavicon().orElse(null),
|
||||||
configuration.isAnnounceForge() ? ModInfo.DEFAULT : null
|
configuration.isAnnounceForge() ? ModInfo.DEFAULT : null
|
||||||
@ -81,7 +63,7 @@ public class ServerListPingHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<ServerPing> attemptPingPassthrough(VelocityInboundConnection connection,
|
private CompletableFuture<ServerPing> attemptPingPassthrough(VelocityInboundConnection connection,
|
||||||
PingPassthroughMode mode, List<String> servers, ProtocolVersion responseProtocolVersion, String virtualHostStr) {
|
PingPassthroughMode mode, List<String> servers, ProtocolVersion responseProtocolVersion) {
|
||||||
ServerPing fallback = constructLocalPing(connection.getProtocolVersion());
|
ServerPing fallback = constructLocalPing(connection.getProtocolVersion());
|
||||||
List<CompletableFuture<ServerPing>> pings = new ArrayList<>();
|
List<CompletableFuture<ServerPing>> pings = new ArrayList<>();
|
||||||
for (String s : servers) {
|
for (String s : servers) {
|
||||||
@ -91,7 +73,7 @@ public class ServerListPingHandler {
|
|||||||
}
|
}
|
||||||
VelocityRegisteredServer vrs = (VelocityRegisteredServer) rs.get();
|
VelocityRegisteredServer vrs = (VelocityRegisteredServer) rs.get();
|
||||||
pings.add(vrs.ping(connection.getConnection().eventLoop(), PingOptions.builder()
|
pings.add(vrs.ping(connection.getConnection().eventLoop(), PingOptions.builder()
|
||||||
.version(responseProtocolVersion).virtualHost(virtualHostStr).build()));
|
.version(responseProtocolVersion).build()));
|
||||||
}
|
}
|
||||||
if (pings.isEmpty()) {
|
if (pings.isEmpty()) {
|
||||||
return CompletableFuture.completedFuture(fallback);
|
return CompletableFuture.completedFuture(fallback);
|
||||||
@ -173,7 +155,7 @@ public class ServerListPingHandler {
|
|||||||
.orElse("");
|
.orElse("");
|
||||||
List<String> serversToTry = server.getConfiguration().getForcedHosts().getOrDefault(
|
List<String> serversToTry = server.getConfiguration().getForcedHosts().getOrDefault(
|
||||||
virtualHostStr, server.getConfiguration().getAttemptConnectionOrder());
|
virtualHostStr, server.getConfiguration().getAttemptConnectionOrder());
|
||||||
return attemptPingPassthrough(connection, passthroughMode, serversToTry, shownVersion, virtualHostStr);
|
return attemptPingPassthrough(connection, passthroughMode, serversToTry, shownVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,8 @@ import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
|||||||
import it.unimi.dsi.fastutil.Pair;
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
@ -109,6 +111,42 @@ public enum EncryptionUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a signature for input data.
|
||||||
|
*
|
||||||
|
* @param algorithm the signature algorithm
|
||||||
|
* @param base the private key to sign with
|
||||||
|
* @param toSign the byte array(s) of data to sign
|
||||||
|
* @return the generated signature
|
||||||
|
*/
|
||||||
|
public static byte[] generateSignature(String algorithm, PrivateKey base, byte[]... toSign) {
|
||||||
|
Preconditions.checkArgument(toSign.length > 0);
|
||||||
|
try {
|
||||||
|
Signature construct = Signature.getInstance(algorithm);
|
||||||
|
construct.initSign(base);
|
||||||
|
for (byte[] bytes : toSign) {
|
||||||
|
construct.update(bytes);
|
||||||
|
}
|
||||||
|
return construct.sign();
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid signature parameters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a long array as Big-endian byte array.
|
||||||
|
*
|
||||||
|
* @param bits the long (array) of numbers to encode
|
||||||
|
* @return the encoded bytes
|
||||||
|
*/
|
||||||
|
public static byte[] longToBigEndianByteArray(long... bits) {
|
||||||
|
ByteBuffer ret = ByteBuffer.allocate(8 * bits.length).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
for (long put : bits) {
|
||||||
|
ret.putLong(put);
|
||||||
|
}
|
||||||
|
return ret.array();
|
||||||
|
}
|
||||||
|
|
||||||
public static String encodeUrlEncoded(byte[] data) {
|
public static String encodeUrlEncoded(byte[] data) {
|
||||||
return MIME_SPECIAL_ENCODER.encodeToString(data);
|
return MIME_SPECIAL_ENCODER.encodeToString(data);
|
||||||
}
|
}
|
||||||
@ -117,6 +155,22 @@ public enum EncryptionUtils {
|
|||||||
return Base64.getMimeDecoder().decode(toParse);
|
return Base64.getMimeDecoder().decode(toParse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a cer-encoded RSA key into its key bytes.
|
||||||
|
*
|
||||||
|
* @param toParse the cer-encoded key String
|
||||||
|
* @param descriptors the type of key
|
||||||
|
* @return the parsed key bytes
|
||||||
|
*/
|
||||||
|
public static byte[] parsePemEncoded(String toParse, Pair<String, String> descriptors) {
|
||||||
|
int startIdx = toParse.indexOf(descriptors.first());
|
||||||
|
Preconditions.checkArgument(startIdx >= 0);
|
||||||
|
int firstLen = descriptors.first().length();
|
||||||
|
int endIdx = toParse.indexOf(descriptors.second(), firstLen + startIdx) + 1;
|
||||||
|
Preconditions.checkArgument(endIdx > 0);
|
||||||
|
return decodeUrlEncoded(toParse.substring(startIdx + firstLen, endIdx));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes an RSA key as String cer format.
|
* Encodes an RSA key as String cer format.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -350,9 +350,8 @@ public class VelocityEventManager implements EventManager {
|
|||||||
asyncType = AsyncType.ALWAYS;
|
asyncType = AsyncType.ALWAYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default value of 0 will fall back to PostOrder, the default PostOrder (NORMAL) is also 0
|
|
||||||
final short order;
|
final short order;
|
||||||
if (subscribe.priority() != 0) {
|
if (subscribe.order() == PostOrder.CUSTOM) {
|
||||||
order = subscribe.priority();
|
order = subscribe.priority();
|
||||||
} else {
|
} else {
|
||||||
order = (short) POST_ORDER_MAP.get(subscribe.order());
|
order = (short) POST_ORDER_MAP.get(subscribe.order());
|
||||||
|
|||||||
@ -51,7 +51,7 @@ public class BackendChannelInitializer extends ChannelInitializer<Channel> {
|
|||||||
@Override
|
@Override
|
||||||
protected void initChannel(Channel ch) {
|
protected void initChannel(Channel ch) {
|
||||||
ch.pipeline()
|
ch.pipeline()
|
||||||
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder(ProtocolUtils.Direction.CLIENTBOUND))
|
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
|
||||||
.addLast(READ_TIMEOUT,
|
.addLast(READ_TIMEOUT,
|
||||||
new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(),
|
new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(),
|
||||||
TimeUnit.MILLISECONDS))
|
TimeUnit.MILLISECONDS))
|
||||||
|
|||||||
@ -18,8 +18,6 @@
|
|||||||
package com.velocitypowered.proxy.network;
|
package com.velocitypowered.proxy.network;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.HashMultimap;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
import com.velocitypowered.api.event.proxy.ListenerBoundEvent;
|
import com.velocitypowered.api.event.proxy.ListenerBoundEvent;
|
||||||
import com.velocitypowered.api.event.proxy.ListenerCloseEvent;
|
import com.velocitypowered.api.event.proxy.ListenerCloseEvent;
|
||||||
import com.velocitypowered.api.network.ListenerType;
|
import com.velocitypowered.api.network.ListenerType;
|
||||||
@ -30,17 +28,14 @@ import com.velocitypowered.proxy.protocol.netty.GameSpyQueryHandler;
|
|||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelOption;
|
import io.netty.channel.ChannelOption;
|
||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
import io.netty.channel.WriteBufferWaterMark;
|
import io.netty.channel.WriteBufferWaterMark;
|
||||||
import io.netty.channel.unix.UnixChannelOption;
|
|
||||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||||
import io.netty.util.concurrent.MultithreadEventExecutorGroup;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.util.Collection;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
@ -54,7 +49,7 @@ public final class ConnectionManager {
|
|||||||
private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20,
|
private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20,
|
||||||
1 << 21);
|
1 << 21);
|
||||||
private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class);
|
private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class);
|
||||||
private final Multimap<InetSocketAddress, Endpoint> endpoints = HashMultimap.create();
|
private final Map<InetSocketAddress, Endpoint> endpoints = new HashMap<>();
|
||||||
private final TransportType transportType;
|
private final TransportType transportType;
|
||||||
private final EventLoopGroup bossGroup;
|
private final EventLoopGroup bossGroup;
|
||||||
private final EventLoopGroup workerGroup;
|
private final EventLoopGroup workerGroup;
|
||||||
@ -98,60 +93,37 @@ public final class ConnectionManager {
|
|||||||
public void bind(final InetSocketAddress address) {
|
public void bind(final InetSocketAddress address) {
|
||||||
final ServerBootstrap bootstrap = new ServerBootstrap()
|
final ServerBootstrap bootstrap = new ServerBootstrap()
|
||||||
.channelFactory(this.transportType.serverSocketChannelFactory)
|
.channelFactory(this.transportType.serverSocketChannelFactory)
|
||||||
|
.group(this.bossGroup, this.workerGroup)
|
||||||
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK)
|
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK)
|
||||||
.childHandler(this.serverChannelInitializer.get())
|
.childHandler(this.serverChannelInitializer.get())
|
||||||
.childOption(ChannelOption.TCP_NODELAY, true)
|
.childOption(ChannelOption.TCP_NODELAY, true)
|
||||||
.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);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server.getConfiguration().isEnableReusePort()) {
|
bootstrap.bind()
|
||||||
// We don't need a boss group, since each worker will bind to the socket
|
.addListener((ChannelFutureListener) future -> {
|
||||||
bootstrap.option(UnixChannelOption.SO_REUSEPORT, true)
|
final Channel channel = future.channel();
|
||||||
.group(this.workerGroup);
|
if (future.isSuccess()) {
|
||||||
} else {
|
this.endpoints.put(address, new Endpoint(channel, ListenerType.MINECRAFT));
|
||||||
bootstrap.group(this.bossGroup, this.workerGroup);
|
|
||||||
}
|
// Warn people with console access that HAProxy is in use, see PR: #1436
|
||||||
|
if (this.server.getConfiguration().isProxyProtocol()) {
|
||||||
final int binds = server.getConfiguration().isEnableReusePort()
|
LOGGER.warn("Using HAProxy and listening on {}, please ensure this listener is adequately firewalled.", channel.localAddress());
|
||||||
? ((MultithreadEventExecutorGroup) this.workerGroup).executorCount() : 1;
|
|
||||||
|
|
||||||
for (int bind = 0; bind < binds; bind++) {
|
|
||||||
// Wait for each bind to open. If we encounter any errors, don't try to bind again.
|
|
||||||
int finalBind = bind;
|
|
||||||
ChannelFuture f = bootstrap.bind()
|
|
||||||
.addListener((ChannelFutureListener) future -> {
|
|
||||||
final Channel channel = future.channel();
|
|
||||||
if (future.isSuccess()) {
|
|
||||||
this.endpoints.put(address, new Endpoint(channel, ListenerType.MINECRAFT));
|
|
||||||
|
|
||||||
LOGGER.info("Listening on {}", channel.localAddress());
|
|
||||||
|
|
||||||
if (finalBind == 0) {
|
|
||||||
// Warn people with console access that HAProxy is in use, see PR: #1436
|
|
||||||
if (this.server.getConfiguration().isProxyProtocol()) {
|
|
||||||
LOGGER.warn(
|
|
||||||
"Using HAProxy and listening on {}, please ensure this listener is adequately firewalled.",
|
|
||||||
channel.localAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fire the proxy bound event after the socket is bound
|
|
||||||
server.getEventManager().fireAndForget(
|
|
||||||
new ListenerBoundEvent(address, ListenerType.MINECRAFT));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOGGER.error("Can't bind to {}", address, future.cause());
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
f.syncUninterruptibly();
|
|
||||||
|
|
||||||
if (!f.isSuccess()) {
|
LOGGER.info("Listening on {}", channel.localAddress());
|
||||||
break;
|
|
||||||
}
|
// Fire the proxy bound event after the socket is bound
|
||||||
}
|
server.getEventManager().fireAndForget(
|
||||||
|
new ListenerBoundEvent(address, ListenerType.MINECRAFT));
|
||||||
|
} else {
|
||||||
|
LOGGER.error("Can't bind to {}", address, future.cause());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,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;
|
||||||
@ -209,20 +181,17 @@ public final class ConnectionManager {
|
|||||||
* @param oldBind the endpoint to close
|
* @param oldBind the endpoint to close
|
||||||
*/
|
*/
|
||||||
public void close(InetSocketAddress oldBind) {
|
public void close(InetSocketAddress oldBind) {
|
||||||
Collection<Endpoint> endpoints = this.endpoints.removeAll(oldBind);
|
Endpoint endpoint = endpoints.remove(oldBind);
|
||||||
Preconditions.checkState(!endpoints.isEmpty(), "Endpoint was not registered");
|
|
||||||
|
|
||||||
ListenerType type = endpoints.iterator().next().getType();
|
|
||||||
|
|
||||||
// Fire proxy close event to notify plugins of socket close. We block since plugins
|
// Fire proxy close event to notify plugins of socket close. We block since plugins
|
||||||
// should have a chance to be notified before the server stops accepting connections.
|
// should have a chance to be notified before the server stops accepting connections.
|
||||||
server.getEventManager().fire(new ListenerCloseEvent(oldBind, type)).join();
|
server.getEventManager().fire(new ListenerCloseEvent(oldBind, endpoint.getType())).join();
|
||||||
|
|
||||||
for (Endpoint endpoint : endpoints) {
|
Channel serverChannel = endpoint.getChannel();
|
||||||
Channel serverChannel = endpoint.getChannel();
|
|
||||||
LOGGER.info("Closing endpoint {}", serverChannel.localAddress());
|
Preconditions.checkState(serverChannel != null, "Endpoint %s not registered", oldBind);
|
||||||
serverChannel.close().syncUninterruptibly();
|
LOGGER.info("Closing endpoint {}", serverChannel.localAddress());
|
||||||
}
|
serverChannel.close().syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -231,28 +200,24 @@ public final class ConnectionManager {
|
|||||||
* @param interrupt should closing forward interruptions
|
* @param interrupt should closing forward interruptions
|
||||||
*/
|
*/
|
||||||
public void closeEndpoints(boolean interrupt) {
|
public void closeEndpoints(boolean interrupt) {
|
||||||
for (final Map.Entry<InetSocketAddress, Collection<Endpoint>> entry : this.endpoints.asMap()
|
for (final Map.Entry<InetSocketAddress, Endpoint> entry : this.endpoints.entrySet()) {
|
||||||
.entrySet()) {
|
|
||||||
final InetSocketAddress address = entry.getKey();
|
final InetSocketAddress address = entry.getKey();
|
||||||
final Collection<Endpoint> endpoints = entry.getValue();
|
final Endpoint endpoint = entry.getValue();
|
||||||
ListenerType type = endpoints.iterator().next().getType();
|
|
||||||
|
|
||||||
// Fire proxy close event to notify plugins of socket close. We block since plugins
|
// Fire proxy close event to notify plugins of socket close. We block since plugins
|
||||||
// should have a chance to be notified before the server stops accepting connections.
|
// should have a chance to be notified before the server stops accepting connections.
|
||||||
server.getEventManager().fire(new ListenerCloseEvent(address, type)).join();
|
server.getEventManager().fire(new ListenerCloseEvent(address, endpoint.getType())).join();
|
||||||
|
|
||||||
for (Endpoint endpoint : endpoints) {
|
LOGGER.info("Closing endpoint {}", address);
|
||||||
LOGGER.info("Closing endpoint {}", address);
|
if (interrupt) {
|
||||||
if (interrupt) {
|
try {
|
||||||
try {
|
endpoint.getChannel().close().sync();
|
||||||
endpoint.getChannel().close().sync();
|
} catch (final InterruptedException e) {
|
||||||
} catch (final InterruptedException e) {
|
LOGGER.info("Interrupted whilst closing endpoint", e);
|
||||||
LOGGER.info("Interrupted whilst closing endpoint", e);
|
Thread.currentThread().interrupt();
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
endpoint.getChannel().close().syncUninterruptibly();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
endpoint.getChannel().close().syncUninterruptibly();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.endpoints.clear();
|
this.endpoints.clear();
|
||||||
|
|||||||
@ -58,7 +58,7 @@ public class ServerChannelInitializer extends ChannelInitializer<Channel> {
|
|||||||
protected void initChannel(final Channel ch) {
|
protected void initChannel(final Channel ch) {
|
||||||
ch.pipeline()
|
ch.pipeline()
|
||||||
.addLast(LEGACY_PING_DECODER, new LegacyPingDecoder())
|
.addLast(LEGACY_PING_DECODER, new LegacyPingDecoder())
|
||||||
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder(ProtocolUtils.Direction.SERVERBOUND))
|
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
|
||||||
.addLast(READ_TIMEOUT,
|
.addLast(READ_TIMEOUT,
|
||||||
new ReadTimeoutHandler(this.server.getConfiguration().getReadTimeout(),
|
new ReadTimeoutHandler(this.server.getConfiguration().getReadTimeout(),
|
||||||
TimeUnit.MILLISECONDS))
|
TimeUnit.MILLISECONDS))
|
||||||
|
|||||||
@ -20,32 +20,27 @@ package com.velocitypowered.proxy.network;
|
|||||||
import com.velocitypowered.proxy.util.concurrent.VelocityNettyThreadFactory;
|
import com.velocitypowered.proxy.util.concurrent.VelocityNettyThreadFactory;
|
||||||
import io.netty.channel.ChannelFactory;
|
import io.netty.channel.ChannelFactory;
|
||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
import io.netty.channel.IoHandlerFactory;
|
|
||||||
import io.netty.channel.MultiThreadIoEventLoopGroup;
|
|
||||||
import io.netty.channel.epoll.Epoll;
|
import io.netty.channel.epoll.Epoll;
|
||||||
import io.netty.channel.epoll.EpollDatagramChannel;
|
import io.netty.channel.epoll.EpollDatagramChannel;
|
||||||
import io.netty.channel.epoll.EpollIoHandler;
|
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||||
import io.netty.channel.epoll.EpollServerSocketChannel;
|
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||||
import io.netty.channel.epoll.EpollSocketChannel;
|
import io.netty.channel.epoll.EpollSocketChannel;
|
||||||
import io.netty.channel.kqueue.KQueue;
|
import io.netty.channel.kqueue.KQueue;
|
||||||
import io.netty.channel.kqueue.KQueueDatagramChannel;
|
import io.netty.channel.kqueue.KQueueDatagramChannel;
|
||||||
import io.netty.channel.kqueue.KQueueIoHandler;
|
import io.netty.channel.kqueue.KQueueEventLoopGroup;
|
||||||
import io.netty.channel.kqueue.KQueueServerSocketChannel;
|
import io.netty.channel.kqueue.KQueueServerSocketChannel;
|
||||||
import io.netty.channel.kqueue.KQueueSocketChannel;
|
import io.netty.channel.kqueue.KQueueSocketChannel;
|
||||||
import io.netty.channel.nio.NioIoHandler;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.DatagramChannel;
|
import io.netty.channel.socket.DatagramChannel;
|
||||||
import io.netty.channel.socket.ServerSocketChannel;
|
import io.netty.channel.socket.ServerSocketChannel;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
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.channel.uring.IoUring;
|
import io.netty.incubator.channel.uring.*;
|
||||||
import io.netty.channel.uring.IoUringDatagramChannel;
|
|
||||||
import io.netty.channel.uring.IoUringIoHandler;
|
|
||||||
import io.netty.channel.uring.IoUringServerSocketChannel;
|
|
||||||
import io.netty.channel.uring.IoUringSocketChannel;
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumerates the supported transports for Velocity.
|
* Enumerates the supported transports for Velocity.
|
||||||
@ -54,36 +49,50 @@ public enum TransportType {
|
|||||||
NIO("NIO", NioServerSocketChannel::new,
|
NIO("NIO", NioServerSocketChannel::new,
|
||||||
NioSocketChannel::new,
|
NioSocketChannel::new,
|
||||||
NioDatagramChannel::new,
|
NioDatagramChannel::new,
|
||||||
NioIoHandler::newFactory),
|
(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,
|
||||||
EpollIoHandler::newFactory),
|
(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,
|
||||||
KQueueIoHandler::newFactory),
|
(name, type) -> new KQueueEventLoopGroup(0, createThreadFactory(name, type)),
|
||||||
IO_URING("io_uring", IoUringServerSocketChannel::new,
|
KQueue.isTcpFastOpenServerSideAvailable(),
|
||||||
IoUringSocketChannel::new,
|
KQueue.isTcpFastOpenClientSideAvailable());
|
||||||
IoUringDatagramChannel::new,
|
|
||||||
IoUringIoHandler::newFactory);
|
|
||||||
|
|
||||||
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 Supplier<IoHandlerFactory> ioHandlerFactorySupplier;
|
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 Supplier<IoHandlerFactory> ioHandlerFactorySupplier) {
|
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.ioHandlerFactorySupplier = ioHandlerFactorySupplier;
|
this.eventLoopGroupFactory = eventLoopGroupFactory;
|
||||||
|
this.supportsTcpFastOpenServer = supportsTcpFastOpenServer;
|
||||||
|
this.supportsTcpFastOpenClient = supportsTcpFastOpenClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -91,15 +100,16 @@ public enum TransportType {
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new event loop group for the given type.
|
|
||||||
*
|
|
||||||
* @param type the type of event loop group to create
|
|
||||||
* @return the event loop group
|
|
||||||
*/
|
|
||||||
public EventLoopGroup createEventLoopGroup(final Type type) {
|
public EventLoopGroup createEventLoopGroup(final Type type) {
|
||||||
return new MultiThreadIoEventLoopGroup(
|
return this.eventLoopGroupFactory.apply(this.name, type);
|
||||||
0, createThreadFactory(this.name, type), this.ioHandlerFactorySupplier.get());
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
@ -116,7 +126,7 @@ public enum TransportType {
|
|||||||
return NIO;
|
return NIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IoUring.isAvailable() && !Boolean.getBoolean("velocity.disable-iouring-transport")) {
|
if(IOUring.isAvailable()) {
|
||||||
return IO_URING;
|
return IO_URING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
|||||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||||
import net.kyori.adventure.text.serializer.json.JSONOptions;
|
import net.kyori.adventure.text.serializer.json.JSONOptions;
|
||||||
import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer;
|
import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer;
|
||||||
import net.kyori.option.OptionSchema;
|
import net.kyori.option.OptionState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utilities for writing and reading data in the Minecraft protocol.
|
* Utilities for writing and reading data in the Minecraft protocol.
|
||||||
@ -60,11 +60,10 @@ public enum ProtocolUtils {
|
|||||||
.downsampleColors()
|
.downsampleColors()
|
||||||
.legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
|
.legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
|
||||||
.options(
|
.options(
|
||||||
OptionSchema.globalSchema().stateBuilder()
|
OptionState.optionState()
|
||||||
// before 1.16
|
// before 1.16
|
||||||
.value(JSONOptions.EMIT_RGB, Boolean.FALSE)
|
.value(JSONOptions.EMIT_RGB, Boolean.FALSE)
|
||||||
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.VALUE_FIELD)
|
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.LEGACY_ONLY)
|
||||||
.value(JSONOptions.EMIT_CLICK_EVENT_TYPE, JSONOptions.ClickEventValueMode.CAMEL_CASE)
|
|
||||||
// before 1.20.3
|
// before 1.20.3
|
||||||
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.FALSE)
|
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.FALSE)
|
||||||
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.FALSE)
|
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.FALSE)
|
||||||
@ -76,12 +75,10 @@ public enum ProtocolUtils {
|
|||||||
GsonComponentSerializer.builder()
|
GsonComponentSerializer.builder()
|
||||||
.legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
|
.legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
|
||||||
.options(
|
.options(
|
||||||
OptionSchema.globalSchema().stateBuilder()
|
OptionState.optionState()
|
||||||
// after 1.16
|
// after 1.16
|
||||||
.value(JSONOptions.EMIT_RGB, Boolean.TRUE)
|
.value(JSONOptions.EMIT_RGB, Boolean.TRUE)
|
||||||
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.CAMEL_CASE)
|
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.MODERN_ONLY)
|
||||||
.value(JSONOptions.EMIT_CLICK_EVENT_TYPE, JSONOptions.ClickEventValueMode.CAMEL_CASE)
|
|
||||||
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_KEY_AS_TYPE_AND_UUID_AS_ID, true)
|
|
||||||
// before 1.20.3
|
// before 1.20.3
|
||||||
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.FALSE)
|
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.FALSE)
|
||||||
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.FALSE)
|
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.FALSE)
|
||||||
@ -89,37 +86,17 @@ public enum ProtocolUtils {
|
|||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
private static final GsonComponentSerializer PRE_1_21_5_SERIALIZER =
|
|
||||||
GsonComponentSerializer.builder()
|
|
||||||
.legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
|
|
||||||
.options(
|
|
||||||
OptionSchema.globalSchema().stateBuilder()
|
|
||||||
// after 1.16
|
|
||||||
.value(JSONOptions.EMIT_RGB, Boolean.TRUE)
|
|
||||||
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.CAMEL_CASE)
|
|
||||||
.value(JSONOptions.EMIT_CLICK_EVENT_TYPE, JSONOptions.ClickEventValueMode.CAMEL_CASE)
|
|
||||||
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_KEY_AS_TYPE_AND_UUID_AS_ID, true)
|
|
||||||
// after 1.20.3
|
|
||||||
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.TRUE)
|
|
||||||
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.TRUE)
|
|
||||||
.value(JSONOptions.VALIDATE_STRICT_EVENTS, Boolean.TRUE)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
private static final GsonComponentSerializer MODERN_SERIALIZER =
|
private static final GsonComponentSerializer MODERN_SERIALIZER =
|
||||||
GsonComponentSerializer.builder()
|
GsonComponentSerializer.builder()
|
||||||
.legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
|
.legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get())
|
||||||
.options(
|
.options(
|
||||||
OptionSchema.globalSchema().stateBuilder()
|
OptionState.optionState()
|
||||||
// after 1.16
|
// after 1.16
|
||||||
.value(JSONOptions.EMIT_RGB, Boolean.TRUE)
|
.value(JSONOptions.EMIT_RGB, Boolean.TRUE)
|
||||||
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.SNAKE_CASE)
|
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.MODERN_ONLY)
|
||||||
.value(JSONOptions.EMIT_CLICK_EVENT_TYPE, JSONOptions.ClickEventValueMode.SNAKE_CASE)
|
|
||||||
// after 1.20.3
|
// after 1.20.3
|
||||||
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.TRUE)
|
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.TRUE)
|
||||||
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.TRUE)
|
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.TRUE)
|
||||||
// after 1.21.5
|
|
||||||
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_KEY_AS_TYPE_AND_UUID_AS_ID, Boolean.FALSE)
|
|
||||||
.value(JSONOptions.VALIDATE_STRICT_EVENTS, Boolean.TRUE)
|
.value(JSONOptions.VALIDATE_STRICT_EVENTS, Boolean.TRUE)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
@ -736,11 +713,8 @@ public enum ProtocolUtils {
|
|||||||
* @return the appropriate {@link GsonComponentSerializer}
|
* @return the appropriate {@link GsonComponentSerializer}
|
||||||
*/
|
*/
|
||||||
public static GsonComponentSerializer getJsonChatSerializer(ProtocolVersion version) {
|
public static GsonComponentSerializer getJsonChatSerializer(ProtocolVersion version) {
|
||||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_5)) {
|
|
||||||
return MODERN_SERIALIZER;
|
|
||||||
}
|
|
||||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) {
|
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) {
|
||||||
return PRE_1_21_5_SERIALIZER;
|
return MODERN_SERIALIZER;
|
||||||
}
|
}
|
||||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) {
|
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) {
|
||||||
return PRE_1_20_3_SERIALIZER;
|
return PRE_1_20_3_SERIALIZER;
|
||||||
|
|||||||
@ -39,8 +39,6 @@ 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_21_4;
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_5;
|
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_6;
|
|
||||||
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;
|
||||||
@ -88,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;
|
||||||
@ -257,8 +256,7 @@ public enum StateRegistry {
|
|||||||
map(0x09, MINECRAFT_1_19_4, false),
|
map(0x09, MINECRAFT_1_19_4, false),
|
||||||
map(0x0A, MINECRAFT_1_20_2, false),
|
map(0x0A, MINECRAFT_1_20_2, false),
|
||||||
map(0x0B, MINECRAFT_1_20_5, false),
|
map(0x0B, MINECRAFT_1_20_5, false),
|
||||||
map(0x0D, MINECRAFT_1_21_2, false),
|
map(0x0D, MINECRAFT_1_21_2, false));
|
||||||
map(0x0E, MINECRAFT_1_21_6, false));
|
|
||||||
serverbound.register(
|
serverbound.register(
|
||||||
LegacyChatPacket.class,
|
LegacyChatPacket.class,
|
||||||
LegacyChatPacket::new,
|
LegacyChatPacket::new,
|
||||||
@ -271,8 +269,7 @@ public enum StateRegistry {
|
|||||||
ChatAcknowledgementPacket.class,
|
ChatAcknowledgementPacket.class,
|
||||||
ChatAcknowledgementPacket::new,
|
ChatAcknowledgementPacket::new,
|
||||||
map(0x03, MINECRAFT_1_19_3, false),
|
map(0x03, MINECRAFT_1_19_3, false),
|
||||||
map(0x04, MINECRAFT_1_21_2, false),
|
map(0x04, MINECRAFT_1_21_2, false));
|
||||||
map(0x05, MINECRAFT_1_21_6, false));
|
|
||||||
serverbound.register(KeyedPlayerCommandPacket.class, KeyedPlayerCommandPacket::new,
|
serverbound.register(KeyedPlayerCommandPacket.class, KeyedPlayerCommandPacket::new,
|
||||||
map(0x03, MINECRAFT_1_19, false),
|
map(0x03, MINECRAFT_1_19, false),
|
||||||
map(0x04, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false));
|
map(0x04, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false));
|
||||||
@ -282,19 +279,16 @@ public enum StateRegistry {
|
|||||||
serverbound.register(SessionPlayerCommandPacket.class, SessionPlayerCommandPacket::new,
|
serverbound.register(SessionPlayerCommandPacket.class, SessionPlayerCommandPacket::new,
|
||||||
map(0x04, MINECRAFT_1_19_3, false),
|
map(0x04, MINECRAFT_1_19_3, false),
|
||||||
map(0x05, MINECRAFT_1_20_5, false),
|
map(0x05, MINECRAFT_1_20_5, false),
|
||||||
map(0x06, MINECRAFT_1_21_2, false),
|
map(0x06, MINECRAFT_1_21_2, false));
|
||||||
map(0x07, MINECRAFT_1_21_6, false));
|
|
||||||
serverbound.register(UnsignedPlayerCommandPacket.class, UnsignedPlayerCommandPacket::new,
|
serverbound.register(UnsignedPlayerCommandPacket.class, UnsignedPlayerCommandPacket::new,
|
||||||
map(0x04, MINECRAFT_1_20_5, false),
|
map(0x04, MINECRAFT_1_20_5, false),
|
||||||
map(0x05, MINECRAFT_1_21_2, false),
|
map(0x05, MINECRAFT_1_21_2, false));
|
||||||
map(0x06, MINECRAFT_1_21_6, false));
|
|
||||||
serverbound.register(
|
serverbound.register(
|
||||||
SessionPlayerChatPacket.class,
|
SessionPlayerChatPacket.class,
|
||||||
SessionPlayerChatPacket::new,
|
SessionPlayerChatPacket::new,
|
||||||
map(0x05, MINECRAFT_1_19_3, false),
|
map(0x05, MINECRAFT_1_19_3, false),
|
||||||
map(0x06, MINECRAFT_1_20_5, false),
|
map(0x06, MINECRAFT_1_20_5, false),
|
||||||
map(0x07, MINECRAFT_1_21_2, false),
|
map(0x07, MINECRAFT_1_21_2, false));
|
||||||
map(0x08, MINECRAFT_1_21_6, false));
|
|
||||||
serverbound.register(
|
serverbound.register(
|
||||||
ClientSettingsPacket.class,
|
ClientSettingsPacket.class,
|
||||||
ClientSettingsPacket::new,
|
ClientSettingsPacket::new,
|
||||||
@ -309,13 +303,11 @@ public enum StateRegistry {
|
|||||||
map(0x08, MINECRAFT_1_19_4, false),
|
map(0x08, MINECRAFT_1_19_4, false),
|
||||||
map(0x09, MINECRAFT_1_20_2, false),
|
map(0x09, MINECRAFT_1_20_2, false),
|
||||||
map(0x0A, MINECRAFT_1_20_5, false),
|
map(0x0A, MINECRAFT_1_20_5, false),
|
||||||
map(0x0C, MINECRAFT_1_21_2, false),
|
map(0x0C, MINECRAFT_1_21_2, false));
|
||||||
map(0x0D, MINECRAFT_1_21_6, false));
|
|
||||||
serverbound.register(
|
serverbound.register(
|
||||||
ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new,
|
ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new,
|
||||||
map(0x11, MINECRAFT_1_20_5, false),
|
map(0x11, MINECRAFT_1_20_5, false),
|
||||||
map(0x13, MINECRAFT_1_21_2, false),
|
map(0x13, MINECRAFT_1_21_2, false));
|
||||||
map(0x14, MINECRAFT_1_21_6, false));
|
|
||||||
serverbound.register(
|
serverbound.register(
|
||||||
PluginMessagePacket.class,
|
PluginMessagePacket.class,
|
||||||
PluginMessagePacket::new,
|
PluginMessagePacket::new,
|
||||||
@ -333,8 +325,7 @@ public enum StateRegistry {
|
|||||||
map(0x0F, MINECRAFT_1_20_2, false),
|
map(0x0F, MINECRAFT_1_20_2, false),
|
||||||
map(0x10, MINECRAFT_1_20_3, false),
|
map(0x10, MINECRAFT_1_20_3, false),
|
||||||
map(0x12, MINECRAFT_1_20_5, false),
|
map(0x12, MINECRAFT_1_20_5, false),
|
||||||
map(0x14, MINECRAFT_1_21_2, false),
|
map(0x14, MINECRAFT_1_21_2, false));
|
||||||
map(0x15, MINECRAFT_1_21_6, false));
|
|
||||||
serverbound.register(
|
serverbound.register(
|
||||||
KeepAlivePacket.class,
|
KeepAlivePacket.class,
|
||||||
KeepAlivePacket::new,
|
KeepAlivePacket::new,
|
||||||
@ -353,8 +344,7 @@ public enum StateRegistry {
|
|||||||
map(0x14, MINECRAFT_1_20_2, false),
|
map(0x14, MINECRAFT_1_20_2, false),
|
||||||
map(0x15, MINECRAFT_1_20_3, false),
|
map(0x15, MINECRAFT_1_20_3, false),
|
||||||
map(0x18, MINECRAFT_1_20_5, false),
|
map(0x18, MINECRAFT_1_20_5, false),
|
||||||
map(0x1A, MINECRAFT_1_21_2, false),
|
map(0x1A, MINECRAFT_1_21_2, false));
|
||||||
map(0x1B, MINECRAFT_1_21_6, false));
|
|
||||||
serverbound.register(
|
serverbound.register(
|
||||||
ResourcePackResponsePacket.class,
|
ResourcePackResponsePacket.class,
|
||||||
ResourcePackResponsePacket::new,
|
ResourcePackResponsePacket::new,
|
||||||
@ -371,14 +361,12 @@ public enum StateRegistry {
|
|||||||
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),
|
map(0x2F, MINECRAFT_1_21_4, false));
|
||||||
map(0x30, MINECRAFT_1_21_6, 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),
|
||||||
map(0x0C, MINECRAFT_1_20_5, false),
|
map(0x0C, MINECRAFT_1_20_5, false),
|
||||||
map(0x0E, MINECRAFT_1_21_2, false),
|
map(0x0E, MINECRAFT_1_21_2, false));
|
||||||
map(0x0F, MINECRAFT_1_21_6, false));
|
|
||||||
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
BossBarPacket.class,
|
BossBarPacket.class,
|
||||||
@ -389,8 +377,7 @@ public enum StateRegistry {
|
|||||||
map(0x0D, MINECRAFT_1_17, false),
|
map(0x0D, MINECRAFT_1_17, false),
|
||||||
map(0x0A, MINECRAFT_1_19, false),
|
map(0x0A, MINECRAFT_1_19, false),
|
||||||
map(0x0B, MINECRAFT_1_19_4, false),
|
map(0x0B, MINECRAFT_1_19_4, false),
|
||||||
map(0x0A, MINECRAFT_1_20_2, false),
|
map(0x0A, MINECRAFT_1_20_2, false));
|
||||||
map(0x09, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
LegacyChatPacket.class,
|
LegacyChatPacket.class,
|
||||||
LegacyChatPacket::new,
|
LegacyChatPacket::new,
|
||||||
@ -411,8 +398,7 @@ public enum StateRegistry {
|
|||||||
map(0x0E, MINECRAFT_1_19, false),
|
map(0x0E, MINECRAFT_1_19, false),
|
||||||
map(0x0D, MINECRAFT_1_19_3, false),
|
map(0x0D, MINECRAFT_1_19_3, false),
|
||||||
map(0x0F, MINECRAFT_1_19_4, false),
|
map(0x0F, MINECRAFT_1_19_4, false),
|
||||||
map(0x10, MINECRAFT_1_20_2, false),
|
map(0x10, MINECRAFT_1_20_2, false));
|
||||||
map(0x0F, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
AvailableCommandsPacket.class,
|
AvailableCommandsPacket.class,
|
||||||
AvailableCommandsPacket::new,
|
AvailableCommandsPacket::new,
|
||||||
@ -424,12 +410,10 @@ public enum StateRegistry {
|
|||||||
map(0x0F, MINECRAFT_1_19, false),
|
map(0x0F, MINECRAFT_1_19, false),
|
||||||
map(0x0E, MINECRAFT_1_19_3, false),
|
map(0x0E, MINECRAFT_1_19_3, false),
|
||||||
map(0x10, MINECRAFT_1_19_4, false),
|
map(0x10, MINECRAFT_1_19_4, false),
|
||||||
map(0x11, MINECRAFT_1_20_2, false),
|
map(0x11, MINECRAFT_1_20_2, false));
|
||||||
map(0x10, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new,
|
ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new,
|
||||||
map(0x16, MINECRAFT_1_20_5, false),
|
map(0x16, MINECRAFT_1_20_5, false));
|
||||||
map(0x15, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
PluginMessagePacket.class,
|
PluginMessagePacket.class,
|
||||||
PluginMessagePacket::new,
|
PluginMessagePacket::new,
|
||||||
@ -446,8 +430,7 @@ public enum StateRegistry {
|
|||||||
map(0x15, MINECRAFT_1_19_3, false),
|
map(0x15, MINECRAFT_1_19_3, false),
|
||||||
map(0x17, MINECRAFT_1_19_4, false),
|
map(0x17, MINECRAFT_1_19_4, false),
|
||||||
map(0x18, MINECRAFT_1_20_2, false),
|
map(0x18, MINECRAFT_1_20_2, false),
|
||||||
map(0x19, MINECRAFT_1_20_5, false),
|
map(0x19, MINECRAFT_1_20_5, false));
|
||||||
map(0x18, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
DisconnectPacket.class,
|
DisconnectPacket.class,
|
||||||
() -> new DisconnectPacket(this),
|
() -> new DisconnectPacket(this),
|
||||||
@ -464,8 +447,7 @@ public enum StateRegistry {
|
|||||||
map(0x17, MINECRAFT_1_19_3, false),
|
map(0x17, MINECRAFT_1_19_3, false),
|
||||||
map(0x1A, MINECRAFT_1_19_4, false),
|
map(0x1A, MINECRAFT_1_19_4, false),
|
||||||
map(0x1B, MINECRAFT_1_20_2, false),
|
map(0x1B, MINECRAFT_1_20_2, false),
|
||||||
map(0x1D, MINECRAFT_1_20_5, false),
|
map(0x1D, MINECRAFT_1_20_5, false));
|
||||||
map(0x1C, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
KeepAlivePacket.class,
|
KeepAlivePacket.class,
|
||||||
KeepAlivePacket::new,
|
KeepAlivePacket::new,
|
||||||
@ -483,8 +465,7 @@ public enum StateRegistry {
|
|||||||
map(0x23, MINECRAFT_1_19_4, false),
|
map(0x23, MINECRAFT_1_19_4, false),
|
||||||
map(0x24, MINECRAFT_1_20_2, false),
|
map(0x24, MINECRAFT_1_20_2, false),
|
||||||
map(0x26, MINECRAFT_1_20_5, false),
|
map(0x26, MINECRAFT_1_20_5, false),
|
||||||
map(0x27, MINECRAFT_1_21_2, false),
|
map(0x27, MINECRAFT_1_21_2, false));
|
||||||
map(0x26, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
JoinGamePacket.class,
|
JoinGamePacket.class,
|
||||||
JoinGamePacket::new,
|
JoinGamePacket::new,
|
||||||
@ -502,8 +483,7 @@ public enum StateRegistry {
|
|||||||
map(0x28, MINECRAFT_1_19_4, false),
|
map(0x28, MINECRAFT_1_19_4, false),
|
||||||
map(0x29, MINECRAFT_1_20_2, false),
|
map(0x29, MINECRAFT_1_20_2, false),
|
||||||
map(0x2B, MINECRAFT_1_20_5, false),
|
map(0x2B, MINECRAFT_1_20_5, false),
|
||||||
map(0x2C, MINECRAFT_1_21_2, false),
|
map(0x2C, MINECRAFT_1_21_2, false));
|
||||||
map(0x2B, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
RespawnPacket.class,
|
RespawnPacket.class,
|
||||||
RespawnPacket::new,
|
RespawnPacket::new,
|
||||||
@ -524,15 +504,13 @@ public enum StateRegistry {
|
|||||||
map(0x43, MINECRAFT_1_20_2, true),
|
map(0x43, MINECRAFT_1_20_2, true),
|
||||||
map(0x45, MINECRAFT_1_20_3, true),
|
map(0x45, MINECRAFT_1_20_3, true),
|
||||||
map(0x47, MINECRAFT_1_20_5, true),
|
map(0x47, MINECRAFT_1_20_5, true),
|
||||||
map(0x4C, MINECRAFT_1_21_2, true),
|
map(0x4C, MINECRAFT_1_21_2, true));
|
||||||
map(0x4B, MINECRAFT_1_21_5, true));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
RemoveResourcePackPacket.class,
|
RemoveResourcePackPacket.class,
|
||||||
RemoveResourcePackPacket::new,
|
RemoveResourcePackPacket::new,
|
||||||
map(0x43, MINECRAFT_1_20_3, false),
|
map(0x43, MINECRAFT_1_20_3, false),
|
||||||
map(0x45, MINECRAFT_1_20_5, false),
|
map(0x45, MINECRAFT_1_20_5, false),
|
||||||
map(0x4A, MINECRAFT_1_21_2, false),
|
map(0x4A, MINECRAFT_1_21_2, false));
|
||||||
map(0x49, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
ResourcePackRequestPacket.class,
|
ResourcePackRequestPacket.class,
|
||||||
ResourcePackRequestPacket::new,
|
ResourcePackRequestPacket::new,
|
||||||
@ -553,8 +531,7 @@ public enum StateRegistry {
|
|||||||
map(0x42, MINECRAFT_1_20_2, false),
|
map(0x42, MINECRAFT_1_20_2, false),
|
||||||
map(0x44, MINECRAFT_1_20_3, false),
|
map(0x44, MINECRAFT_1_20_3, false),
|
||||||
map(0x46, MINECRAFT_1_20_5, false),
|
map(0x46, MINECRAFT_1_20_5, false),
|
||||||
map(0x4B, MINECRAFT_1_21_2, false),
|
map(0x4B, MINECRAFT_1_21_2, false));
|
||||||
map(0x4A, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
HeaderAndFooterPacket.class,
|
HeaderAndFooterPacket.class,
|
||||||
HeaderAndFooterPacket::new,
|
HeaderAndFooterPacket::new,
|
||||||
@ -576,8 +553,7 @@ public enum StateRegistry {
|
|||||||
map(0x68, MINECRAFT_1_20_2, true),
|
map(0x68, MINECRAFT_1_20_2, true),
|
||||||
map(0x6A, MINECRAFT_1_20_3, true),
|
map(0x6A, MINECRAFT_1_20_3, true),
|
||||||
map(0x6D, MINECRAFT_1_20_5, true),
|
map(0x6D, MINECRAFT_1_20_5, true),
|
||||||
map(0x74, MINECRAFT_1_21_2, true),
|
map(0x74, MINECRAFT_1_21_2, true));
|
||||||
map(0x73, MINECRAFT_1_21_5, true));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
LegacyTitlePacket.class,
|
LegacyTitlePacket.class,
|
||||||
LegacyTitlePacket::new,
|
LegacyTitlePacket::new,
|
||||||
@ -598,8 +574,7 @@ public enum StateRegistry {
|
|||||||
map(0x5F, MINECRAFT_1_20_2, true),
|
map(0x5F, MINECRAFT_1_20_2, true),
|
||||||
map(0x61, MINECRAFT_1_20_3, true),
|
map(0x61, MINECRAFT_1_20_3, true),
|
||||||
map(0x63, MINECRAFT_1_20_5, true),
|
map(0x63, MINECRAFT_1_20_5, true),
|
||||||
map(0x6A, MINECRAFT_1_21_2, true),
|
map(0x6A, MINECRAFT_1_21_2, true));
|
||||||
map(0x69, MINECRAFT_1_21_5, true));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
TitleTextPacket.class,
|
TitleTextPacket.class,
|
||||||
TitleTextPacket::new,
|
TitleTextPacket::new,
|
||||||
@ -611,8 +586,7 @@ public enum StateRegistry {
|
|||||||
map(0x61, MINECRAFT_1_20_2, true),
|
map(0x61, MINECRAFT_1_20_2, true),
|
||||||
map(0x63, MINECRAFT_1_20_3, true),
|
map(0x63, MINECRAFT_1_20_3, true),
|
||||||
map(0x65, MINECRAFT_1_20_5, true),
|
map(0x65, MINECRAFT_1_20_5, true),
|
||||||
map(0x6C, MINECRAFT_1_21_2, true),
|
map(0x6C, MINECRAFT_1_21_2, true));
|
||||||
map(0x6B, MINECRAFT_1_21_5, true));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
TitleActionbarPacket.class,
|
TitleActionbarPacket.class,
|
||||||
TitleActionbarPacket::new,
|
TitleActionbarPacket::new,
|
||||||
@ -624,8 +598,7 @@ public enum StateRegistry {
|
|||||||
map(0x48, MINECRAFT_1_20_2, true),
|
map(0x48, MINECRAFT_1_20_2, true),
|
||||||
map(0x4A, MINECRAFT_1_20_3, true),
|
map(0x4A, MINECRAFT_1_20_3, true),
|
||||||
map(0x4C, MINECRAFT_1_20_5, true),
|
map(0x4C, MINECRAFT_1_20_5, true),
|
||||||
map(0x51, MINECRAFT_1_21_2, true),
|
map(0x51, MINECRAFT_1_21_2, true));
|
||||||
map(0x50, MINECRAFT_1_21_5, true));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
TitleTimesPacket.class,
|
TitleTimesPacket.class,
|
||||||
TitleTimesPacket::new,
|
TitleTimesPacket::new,
|
||||||
@ -637,8 +610,7 @@ public enum StateRegistry {
|
|||||||
map(0x62, MINECRAFT_1_20_2, true),
|
map(0x62, MINECRAFT_1_20_2, true),
|
||||||
map(0x64, MINECRAFT_1_20_3, true),
|
map(0x64, MINECRAFT_1_20_3, true),
|
||||||
map(0x66, MINECRAFT_1_20_5, true),
|
map(0x66, MINECRAFT_1_20_5, true),
|
||||||
map(0x6D, MINECRAFT_1_21_2, true),
|
map(0x6D, MINECRAFT_1_21_2, true));
|
||||||
map(0x6C, MINECRAFT_1_21_5, true));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
TitleClearPacket.class,
|
TitleClearPacket.class,
|
||||||
TitleClearPacket::new,
|
TitleClearPacket::new,
|
||||||
@ -646,8 +618,7 @@ public enum StateRegistry {
|
|||||||
map(0x0D, MINECRAFT_1_19, true),
|
map(0x0D, MINECRAFT_1_19, true),
|
||||||
map(0x0C, MINECRAFT_1_19_3, true),
|
map(0x0C, MINECRAFT_1_19_3, true),
|
||||||
map(0x0E, MINECRAFT_1_19_4, true),
|
map(0x0E, MINECRAFT_1_19_4, true),
|
||||||
map(0x0F, MINECRAFT_1_20_2, true),
|
map(0x0F, MINECRAFT_1_20_2, true));
|
||||||
map(0x0E, MINECRAFT_1_21_5, true));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
LegacyPlayerListItemPacket.class,
|
LegacyPlayerListItemPacket.class,
|
||||||
LegacyPlayerListItemPacket::new,
|
LegacyPlayerListItemPacket::new,
|
||||||
@ -667,8 +638,7 @@ public enum StateRegistry {
|
|||||||
map(0x39, MINECRAFT_1_19_4, false),
|
map(0x39, MINECRAFT_1_19_4, false),
|
||||||
map(0x3B, MINECRAFT_1_20_2, false),
|
map(0x3B, MINECRAFT_1_20_2, false),
|
||||||
map(0x3D, MINECRAFT_1_20_5, false),
|
map(0x3D, MINECRAFT_1_20_5, false),
|
||||||
map(0x3F, MINECRAFT_1_21_2, false),
|
map(0x3F, MINECRAFT_1_21_2, false));
|
||||||
map(0x3E, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
UpsertPlayerInfoPacket.class,
|
UpsertPlayerInfoPacket.class,
|
||||||
UpsertPlayerInfoPacket::new,
|
UpsertPlayerInfoPacket::new,
|
||||||
@ -676,13 +646,11 @@ public enum StateRegistry {
|
|||||||
map(0x3A, MINECRAFT_1_19_4, false),
|
map(0x3A, MINECRAFT_1_19_4, false),
|
||||||
map(0x3C, MINECRAFT_1_20_2, false),
|
map(0x3C, MINECRAFT_1_20_2, false),
|
||||||
map(0x3E, MINECRAFT_1_20_5, false),
|
map(0x3E, MINECRAFT_1_20_5, false),
|
||||||
map(0x40, MINECRAFT_1_21_2, false),
|
map(0x40, MINECRAFT_1_21_2, false));
|
||||||
map(0x3F, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new,
|
ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new,
|
||||||
map(0x6B, MINECRAFT_1_20_5, false),
|
map(0x6B, MINECRAFT_1_20_5, false),
|
||||||
map(0x72, MINECRAFT_1_21_2, false),
|
map(0x72, MINECRAFT_1_21_2, false));
|
||||||
map(0x71, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
SystemChatPacket.class,
|
SystemChatPacket.class,
|
||||||
SystemChatPacket::new,
|
SystemChatPacket::new,
|
||||||
@ -693,8 +661,7 @@ public enum StateRegistry {
|
|||||||
map(0x67, MINECRAFT_1_20_2, true),
|
map(0x67, MINECRAFT_1_20_2, true),
|
||||||
map(0x69, MINECRAFT_1_20_3, true),
|
map(0x69, MINECRAFT_1_20_3, true),
|
||||||
map(0x6C, MINECRAFT_1_20_5, true),
|
map(0x6C, MINECRAFT_1_20_5, true),
|
||||||
map(0x73, MINECRAFT_1_21_2, true),
|
map(0x73, MINECRAFT_1_21_2, true));
|
||||||
map(0x72, MINECRAFT_1_21_5, true));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
PlayerChatCompletionPacket.class,
|
PlayerChatCompletionPacket.class,
|
||||||
PlayerChatCompletionPacket::new,
|
PlayerChatCompletionPacket::new,
|
||||||
@ -702,8 +669,7 @@ public enum StateRegistry {
|
|||||||
map(0x14, MINECRAFT_1_19_3, true),
|
map(0x14, MINECRAFT_1_19_3, true),
|
||||||
map(0x16, MINECRAFT_1_19_4, true),
|
map(0x16, MINECRAFT_1_19_4, true),
|
||||||
map(0x17, MINECRAFT_1_20_2, true),
|
map(0x17, MINECRAFT_1_20_2, true),
|
||||||
map(0x18, MINECRAFT_1_20_5, true),
|
map(0x18, MINECRAFT_1_20_5, true));
|
||||||
map(0x17, MINECRAFT_1_21_5, true));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
ServerDataPacket.class,
|
ServerDataPacket.class,
|
||||||
ServerDataPacket::new,
|
ServerDataPacket::new,
|
||||||
@ -714,16 +680,14 @@ public enum StateRegistry {
|
|||||||
map(0x47, MINECRAFT_1_20_2, false),
|
map(0x47, MINECRAFT_1_20_2, false),
|
||||||
map(0x49, MINECRAFT_1_20_3, false),
|
map(0x49, MINECRAFT_1_20_3, false),
|
||||||
map(0x4B, MINECRAFT_1_20_5, false),
|
map(0x4B, MINECRAFT_1_20_5, false),
|
||||||
map(0x50, MINECRAFT_1_21_2, false),
|
map(0x50, MINECRAFT_1_21_2, false));
|
||||||
map(0x4F, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
StartUpdatePacket.class,
|
StartUpdatePacket.class,
|
||||||
() -> StartUpdatePacket.INSTANCE,
|
() -> StartUpdatePacket.INSTANCE,
|
||||||
map(0x65, MINECRAFT_1_20_2, false),
|
map(0x65, MINECRAFT_1_20_2, false),
|
||||||
map(0x67, MINECRAFT_1_20_3, false),
|
map(0x67, MINECRAFT_1_20_3, false),
|
||||||
map(0x69, MINECRAFT_1_20_5, false),
|
map(0x69, MINECRAFT_1_20_5, false),
|
||||||
map(0x70, MINECRAFT_1_21_2, false),
|
map(0x70, MINECRAFT_1_21_2, false));
|
||||||
map(0x6F, MINECRAFT_1_21_5, false));
|
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
BundleDelimiterPacket.class,
|
BundleDelimiterPacket.class,
|
||||||
() -> BundleDelimiterPacket.INSTANCE,
|
() -> BundleDelimiterPacket.INSTANCE,
|
||||||
@ -743,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 {
|
||||||
|
|||||||
@ -39,7 +39,6 @@ public class MinecraftCompressDecoder extends MessageToMessageDecoder<ByteBuf> {
|
|||||||
private static final int UNCOMPRESSED_CAP =
|
private static final int UNCOMPRESSED_CAP =
|
||||||
Boolean.getBoolean("velocity.increased-compression-cap")
|
Boolean.getBoolean("velocity.increased-compression-cap")
|
||||||
? HARD_MAXIMUM_UNCOMPRESSED_SIZE : VANILLA_MAXIMUM_UNCOMPRESSED_SIZE;
|
? HARD_MAXIMUM_UNCOMPRESSED_SIZE : VANILLA_MAXIMUM_UNCOMPRESSED_SIZE;
|
||||||
private static final boolean SKIP_COMPRESSION_VALIDATION = Boolean.getBoolean("velocity.skip-uncompressed-packet-size-validation");
|
|
||||||
|
|
||||||
private int threshold;
|
private int threshold;
|
||||||
private final VelocityCompressor compressor;
|
private final VelocityCompressor compressor;
|
||||||
@ -53,11 +52,6 @@ public class MinecraftCompressDecoder extends MessageToMessageDecoder<ByteBuf> {
|
|||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||||
int claimedUncompressedSize = ProtocolUtils.readVarInt(in);
|
int claimedUncompressedSize = ProtocolUtils.readVarInt(in);
|
||||||
if (claimedUncompressedSize == 0) {
|
if (claimedUncompressedSize == 0) {
|
||||||
if (!SKIP_COMPRESSION_VALIDATION) {
|
|
||||||
int actualUncompressedSize = in.readableBytes();
|
|
||||||
checkFrame(actualUncompressedSize < threshold, "Actual uncompressed size %s is greater than"
|
|
||||||
+ " threshold %s", actualUncompressedSize, threshold);
|
|
||||||
}
|
|
||||||
// This message is not compressed.
|
// This message is not compressed.
|
||||||
out.add(in.retain());
|
out.add(in.retain());
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -135,7 +135,7 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
|
|||||||
|
|
||||||
private String getExtraConnectionDetail(int packetId) {
|
private String getExtraConnectionDetail(int packetId) {
|
||||||
return "Direction " + direction + " Protocol " + registry.version + " State " + state
|
return "Direction " + direction + " Protocol " + registry.version + " State " + state
|
||||||
+ " ID 0x" + Integer.toHexString(packetId);
|
+ " ID " + Integer.toHexString(packetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProtocolVersion(ProtocolVersion protocolVersion) {
|
public void setProtocolVersion(ProtocolVersion protocolVersion) {
|
||||||
|
|||||||
@ -19,51 +19,21 @@ package com.velocitypowered.proxy.protocol.netty;
|
|||||||
|
|
||||||
import static io.netty.util.ByteProcessor.FIND_NON_NUL;
|
import static io.netty.util.ByteProcessor.FIND_NON_NUL;
|
||||||
|
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
|
||||||
import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
||||||
import com.velocitypowered.proxy.util.except.QuietRuntimeException;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frames Minecraft server packets which are prefixed by a 21-bit VarInt encoding.
|
* Frames Minecraft server packets which are prefixed by a 21-bit VarInt encoding.
|
||||||
*/
|
*/
|
||||||
public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
|
public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(MinecraftVarintFrameDecoder.class);
|
|
||||||
private static final QuietRuntimeException FRAME_DECODER_FAILED =
|
|
||||||
new QuietRuntimeException("A packet frame decoder failed. For more information, launch "
|
|
||||||
+ "Velocity with -Dvelocity.packet-decode-logging=true to see more.");
|
|
||||||
private static final QuietDecoderException BAD_PACKET_LENGTH =
|
private static final QuietDecoderException BAD_PACKET_LENGTH =
|
||||||
new QuietDecoderException("Bad packet length");
|
new QuietDecoderException("Bad packet length");
|
||||||
private static final QuietDecoderException VARINT_TOO_BIG =
|
private static final QuietDecoderException VARINT_TOO_BIG =
|
||||||
new QuietDecoderException("VarInt too big");
|
new QuietDecoderException("VarInt too big");
|
||||||
private static final QuietDecoderException UNKNOWN_PACKET =
|
|
||||||
new QuietDecoderException("Unknown packet");
|
|
||||||
|
|
||||||
private final ProtocolUtils.Direction direction;
|
|
||||||
private final StateRegistry.PacketRegistry.ProtocolRegistry registry;
|
|
||||||
private StateRegistry state;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@code MinecraftVarintFrameDecoder} decoding packets from the specified {@code Direction}.
|
|
||||||
*
|
|
||||||
* @param direction the direction from which we decode from
|
|
||||||
*/
|
|
||||||
public MinecraftVarintFrameDecoder(ProtocolUtils.Direction direction) {
|
|
||||||
this.direction = direction;
|
|
||||||
this.registry = StateRegistry.HANDSHAKE.getProtocolRegistry(
|
|
||||||
direction, ProtocolVersion.MINIMUM_VERSION);
|
|
||||||
this.state = StateRegistry.HANDSHAKE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
|
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
|
||||||
@ -92,43 +62,6 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
|
|||||||
throw BAD_PACKET_LENGTH;
|
throw BAD_PACKET_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length > 0) {
|
|
||||||
if (state == StateRegistry.HANDSHAKE && direction == ProtocolUtils.Direction.SERVERBOUND) {
|
|
||||||
StateRegistry.PacketRegistry.ProtocolRegistry registry =
|
|
||||||
state.getProtocolRegistry(direction, ProtocolVersion.MINIMUM_VERSION);
|
|
||||||
|
|
||||||
final int index = in.readerIndex();
|
|
||||||
final int packetId = readRawVarInt21(in);
|
|
||||||
// Index hasn't changed, we've read nothing
|
|
||||||
if (index == in.readerIndex()) {
|
|
||||||
in.resetReaderIndex();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final int payloadLength = length - ProtocolUtils.varIntBytes(packetId);
|
|
||||||
|
|
||||||
MinecraftPacket packet = registry.createPacket(packetId);
|
|
||||||
|
|
||||||
// We handle every packet in this phase, if you said something we don't know, something is really wrong
|
|
||||||
if (packet == null) {
|
|
||||||
throw UNKNOWN_PACKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We 'technically' have the incoming bytes of a payload here, and so, these can actually parse
|
|
||||||
// the packet if needed, so, we'll take advantage of the existing methods
|
|
||||||
int expectedMinLen = packet.expectedMinLength(in, direction, registry.version);
|
|
||||||
int expectedMaxLen = packet.expectedMaxLength(in, direction, registry.version);
|
|
||||||
if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) {
|
|
||||||
throw handleOverflow(packet, expectedMaxLen, in.readableBytes());
|
|
||||||
}
|
|
||||||
if (payloadLength < expectedMinLen) {
|
|
||||||
throw handleUnderflow(packet, expectedMaxLen, in.readableBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
in.readerIndex(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// note that zero-length packets are ignored
|
// note that zero-length packets are ignored
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
if (in.readableBytes() < length) {
|
if (in.readableBytes() < length) {
|
||||||
@ -139,16 +72,6 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
||||||
if (MinecraftDecoder.DEBUG) {
|
|
||||||
LOGGER.atWarn()
|
|
||||||
.withThrowable(cause)
|
|
||||||
.log("Exception caught while decoding frame for {}", ctx.channel().remoteAddress());
|
|
||||||
}
|
|
||||||
super.exceptionCaught(ctx, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a VarInt from the buffer of up to 21 bits in size.
|
* Reads a VarInt from the buffer of up to 21 bits in size.
|
||||||
*
|
*
|
||||||
@ -218,26 +141,4 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
|
|||||||
}
|
}
|
||||||
return result | (tmp & 0x7F) << 14;
|
return result | (tmp & 0x7F) << 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Exception handleOverflow(MinecraftPacket packet, int expected, int actual) {
|
|
||||||
if (MinecraftDecoder.DEBUG) {
|
|
||||||
return new CorruptedFrameException("Packet sent for " + packet.getClass() + " was too "
|
|
||||||
+ "big (expected " + expected + " bytes, got " + actual + " bytes)");
|
|
||||||
} else {
|
|
||||||
return FRAME_DECODER_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Exception handleUnderflow(MinecraftPacket packet, int expected, int actual) {
|
|
||||||
if (MinecraftDecoder.DEBUG) {
|
|
||||||
return new CorruptedFrameException("Packet sent for " + packet.getClass() + " was too "
|
|
||||||
+ "small (expected " + expected + " bytes, got " + actual + " bytes)");
|
|
||||||
} else {
|
|
||||||
return FRAME_DECODER_FAILED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setState(StateRegistry stateRegistry) {
|
|
||||||
this.state = stateRegistry;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,14 +50,12 @@ import java.util.Deque;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Predicate;
|
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
public class AvailableCommandsPacket implements MinecraftPacket {
|
public class AvailableCommandsPacket implements MinecraftPacket {
|
||||||
|
|
||||||
private static final Command<CommandSource> PLACEHOLDER_COMMAND = source -> 0;
|
private static final Command<CommandSource> PLACEHOLDER_COMMAND = source -> 0;
|
||||||
private static final Predicate<CommandSource> PLACEHOLDER_REQUIREMENT = source -> true;
|
|
||||||
|
|
||||||
private static final byte NODE_TYPE_ROOT = 0x00;
|
private static final byte NODE_TYPE_ROOT = 0x00;
|
||||||
private static final byte NODE_TYPE_LITERAL = 0x01;
|
private static final byte NODE_TYPE_LITERAL = 0x01;
|
||||||
@ -67,7 +65,6 @@ public class AvailableCommandsPacket implements MinecraftPacket {
|
|||||||
private static final byte FLAG_EXECUTABLE = 0x04;
|
private static final byte FLAG_EXECUTABLE = 0x04;
|
||||||
private static final byte FLAG_IS_REDIRECT = 0x08;
|
private static final byte FLAG_IS_REDIRECT = 0x08;
|
||||||
private static final byte FLAG_HAS_SUGGESTIONS = 0x10;
|
private static final byte FLAG_HAS_SUGGESTIONS = 0x10;
|
||||||
private static final byte FLAG_IS_RESTRICTED = 0x20;
|
|
||||||
|
|
||||||
private @MonotonicNonNull RootCommandNode<CommandSource> rootNode;
|
private @MonotonicNonNull RootCommandNode<CommandSource> rootNode;
|
||||||
|
|
||||||
@ -149,9 +146,6 @@ public class AvailableCommandsPacket implements MinecraftPacket {
|
|||||||
if (node.getCommand() != null) {
|
if (node.getCommand() != null) {
|
||||||
flags |= FLAG_EXECUTABLE;
|
flags |= FLAG_EXECUTABLE;
|
||||||
}
|
}
|
||||||
if (node.getRequirement() == PLACEHOLDER_REQUIREMENT) {
|
|
||||||
flags |= FLAG_IS_RESTRICTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node instanceof LiteralCommandNode<?>) {
|
if (node instanceof LiteralCommandNode<?>) {
|
||||||
flags |= NODE_TYPE_LITERAL;
|
flags |= NODE_TYPE_LITERAL;
|
||||||
@ -295,11 +289,6 @@ public class AvailableCommandsPacket implements MinecraftPacket {
|
|||||||
args.executes(PLACEHOLDER_COMMAND);
|
args.executes(PLACEHOLDER_COMMAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If restricted, add empty requirement
|
|
||||||
if ((flags & FLAG_IS_RESTRICTED) != 0) {
|
|
||||||
args.requires(PLACEHOLDER_REQUIREMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.built = args.build();
|
this.built = args.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,16 +106,4 @@ public class HandshakePacket implements MinecraftPacket {
|
|||||||
public boolean handle(MinecraftSessionHandler handler) {
|
public boolean handle(MinecraftSessionHandler handler) {
|
||||||
return handler.handle(this);
|
return handler.handle(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int expectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction,
|
|
||||||
ProtocolVersion version) {
|
|
||||||
return 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int expectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction,
|
|
||||||
ProtocolVersion version) {
|
|
||||||
return 9 + (MAXIMUM_HOSTNAME_LENGTH * 3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -58,13 +58,6 @@ public class ArgumentIdentifier {
|
|||||||
this.versionById = ImmutableMap.copyOf(temp);
|
this.versionById = ImmutableMap.copyOf(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ArgumentIdentifier{" +
|
|
||||||
"identifier='" + identifier + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIdentifier() {
|
public String getIdentifier() {
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,8 +22,6 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_3;
|
|||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_4;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_4;
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_3;
|
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_5;
|
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_6;
|
|
||||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.id;
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.id;
|
||||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.mapSet;
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.mapSet;
|
||||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.DoubleArgumentPropertySerializer.DOUBLE;
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.DoubleArgumentPropertySerializer.DOUBLE;
|
||||||
@ -165,7 +163,6 @@ public class ArgumentPropertyRegistry {
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Argument type identifier " + id + " unknown.");
|
|
||||||
} else {
|
} else {
|
||||||
String identifier = ProtocolUtils.readString(buf);
|
String identifier = ProtocolUtils.readString(buf);
|
||||||
for (ArgumentIdentifier i : byIdentifier.keySet()) {
|
for (ArgumentIdentifier i : byIdentifier.keySet()) {
|
||||||
@ -209,79 +206,65 @@ public class ArgumentPropertyRegistry {
|
|||||||
empty(id("minecraft:item_stack", mapSet(MINECRAFT_1_19, 14)));
|
empty(id("minecraft:item_stack", mapSet(MINECRAFT_1_19, 14)));
|
||||||
empty(id("minecraft:item_predicate", mapSet(MINECRAFT_1_19, 15)));
|
empty(id("minecraft:item_predicate", mapSet(MINECRAFT_1_19, 15)));
|
||||||
empty(id("minecraft:color", mapSet(MINECRAFT_1_19, 16)));
|
empty(id("minecraft:color", mapSet(MINECRAFT_1_19, 16)));
|
||||||
empty(id("minecraft:component", mapSet(MINECRAFT_1_21_6, 18), mapSet(MINECRAFT_1_19, 17)));
|
empty(id("minecraft:component", mapSet(MINECRAFT_1_19, 17)));
|
||||||
empty(id("minecraft:style", mapSet(MINECRAFT_1_21_6, 19), mapSet(MINECRAFT_1_20_3, 18))); // added 1.20.3
|
empty(id("minecraft:style", mapSet(MINECRAFT_1_20_3, 18))); // added 1.20.3
|
||||||
empty(id("minecraft:message", mapSet(MINECRAFT_1_21_6, 20), mapSet(MINECRAFT_1_20_3, 19), mapSet(MINECRAFT_1_19, 18)));
|
empty(id("minecraft:message", mapSet(MINECRAFT_1_20_3, 19), mapSet(MINECRAFT_1_19, 18)));
|
||||||
empty(id("minecraft:nbt_compound_tag", mapSet(MINECRAFT_1_21_6, 21), mapSet(MINECRAFT_1_20_3, 20), mapSet(MINECRAFT_1_19, 19))); // added in 1.14
|
empty(id("minecraft:nbt_compound_tag", mapSet(MINECRAFT_1_20_3, 20), mapSet(MINECRAFT_1_19, 19))); // added in 1.14
|
||||||
empty(id("minecraft:nbt_tag", mapSet(MINECRAFT_1_21_6, 22), mapSet(MINECRAFT_1_20_3, 21), mapSet(MINECRAFT_1_19, 20))); // added in 1.14
|
empty(id("minecraft:nbt_tag", mapSet(MINECRAFT_1_20_3, 21), mapSet(MINECRAFT_1_19, 20))); // added in 1.14
|
||||||
empty(id("minecraft:nbt_path", mapSet(MINECRAFT_1_21_6, 23), mapSet(MINECRAFT_1_20_3, 22), mapSet(MINECRAFT_1_19, 21)));
|
empty(id("minecraft:nbt_path", mapSet(MINECRAFT_1_20_3, 22), mapSet(MINECRAFT_1_19, 21)));
|
||||||
empty(id("minecraft:objective", mapSet(MINECRAFT_1_21_6, 24), mapSet(MINECRAFT_1_20_3, 23), mapSet(MINECRAFT_1_19, 22)));
|
empty(id("minecraft:objective", mapSet(MINECRAFT_1_20_3, 23), mapSet(MINECRAFT_1_19, 22)));
|
||||||
empty(id("minecraft:objective_criteria", mapSet(MINECRAFT_1_21_6, 25), mapSet(MINECRAFT_1_20_3, 24), mapSet(MINECRAFT_1_19, 23)));
|
empty(id("minecraft:objective_criteria", mapSet(MINECRAFT_1_20_3, 24), mapSet(MINECRAFT_1_19, 23)));
|
||||||
empty(id("minecraft:operation", mapSet(MINECRAFT_1_21_6, 26), mapSet(MINECRAFT_1_20_3, 25), mapSet(MINECRAFT_1_19, 24)));
|
empty(id("minecraft:operation", mapSet(MINECRAFT_1_20_3, 25), mapSet(MINECRAFT_1_19, 24)));
|
||||||
empty(id("minecraft:particle", mapSet(MINECRAFT_1_21_6, 27), mapSet(MINECRAFT_1_20_3, 26), mapSet(MINECRAFT_1_19, 25)));
|
empty(id("minecraft:particle", mapSet(MINECRAFT_1_20_3, 26), mapSet(MINECRAFT_1_19, 25)));
|
||||||
empty(id("minecraft:angle", mapSet(MINECRAFT_1_21_6, 28), mapSet(MINECRAFT_1_20_3, 27), mapSet(MINECRAFT_1_19, 26))); // added in 1.16.2
|
empty(id("minecraft:angle", mapSet(MINECRAFT_1_20_3, 27), mapSet(MINECRAFT_1_19, 26))); // added in 1.16.2
|
||||||
empty(id("minecraft:rotation", mapSet(MINECRAFT_1_21_6, 29), mapSet(MINECRAFT_1_20_3, 28), mapSet(MINECRAFT_1_19, 27)));
|
empty(id("minecraft:rotation", mapSet(MINECRAFT_1_20_3, 28), mapSet(MINECRAFT_1_19, 27)));
|
||||||
empty(id("minecraft:scoreboard_slot", mapSet(MINECRAFT_1_21_6, 30), mapSet(MINECRAFT_1_20_3, 29), mapSet(MINECRAFT_1_19, 28)));
|
empty(id("minecraft:scoreboard_slot", mapSet(MINECRAFT_1_20_3, 29), mapSet(MINECRAFT_1_19, 28)));
|
||||||
empty(id("minecraft:score_holder", mapSet(MINECRAFT_1_21_6, 31), mapSet(MINECRAFT_1_20_3, 30), mapSet(MINECRAFT_1_19, 29)),
|
empty(id("minecraft:score_holder", mapSet(MINECRAFT_1_20_3, 30), mapSet(MINECRAFT_1_19, 29)), ByteArgumentPropertySerializer.BYTE);
|
||||||
ByteArgumentPropertySerializer.BYTE);
|
empty(id("minecraft:swizzle", mapSet(MINECRAFT_1_20_3, 31), mapSet(MINECRAFT_1_19, 30)));
|
||||||
empty(id("minecraft:swizzle", mapSet(MINECRAFT_1_21_6, 32), mapSet(MINECRAFT_1_20_3, 31), mapSet(MINECRAFT_1_19, 30)));
|
empty(id("minecraft:team", mapSet(MINECRAFT_1_20_3, 32), mapSet(MINECRAFT_1_19, 31)));
|
||||||
empty(id("minecraft:team", mapSet(MINECRAFT_1_21_6, 33), mapSet(MINECRAFT_1_20_3, 32), mapSet(MINECRAFT_1_19, 31)));
|
empty(id("minecraft:item_slot", mapSet(MINECRAFT_1_20_3, 33), mapSet(MINECRAFT_1_19, 32)));
|
||||||
empty(id("minecraft:item_slot", mapSet(MINECRAFT_1_21_6, 34), mapSet(MINECRAFT_1_20_3, 33), mapSet(MINECRAFT_1_19, 32)));
|
empty(id("minecraft:item_slots", mapSet(MINECRAFT_1_20_5, 34))); // added 1.20.5
|
||||||
empty(id("minecraft:item_slots", mapSet(MINECRAFT_1_21_6, 35), mapSet(MINECRAFT_1_20_5, 34))); // added 1.20.5
|
empty(id("minecraft:resource_location", mapSet(MINECRAFT_1_20_5, 35), mapSet(MINECRAFT_1_20_3, 34), mapSet(MINECRAFT_1_19, 33)));
|
||||||
empty(id("minecraft:resource_location", mapSet(MINECRAFT_1_21_6, 36), mapSet(MINECRAFT_1_20_5, 35), mapSet(MINECRAFT_1_20_3, 34),
|
|
||||||
mapSet(MINECRAFT_1_19, 33)));
|
|
||||||
empty(id("minecraft:mob_effect", mapSet(MINECRAFT_1_19_3, -1), mapSet(MINECRAFT_1_19, 34)));
|
empty(id("minecraft:mob_effect", mapSet(MINECRAFT_1_19_3, -1), mapSet(MINECRAFT_1_19, 34)));
|
||||||
empty(id("minecraft:function", mapSet(MINECRAFT_1_21_6, 37), mapSet(MINECRAFT_1_20_5, 36), mapSet(MINECRAFT_1_20_3, 35),
|
empty(id("minecraft:function", mapSet(MINECRAFT_1_20_5, 36), mapSet(MINECRAFT_1_20_3, 35), mapSet(MINECRAFT_1_19_3, 34),
|
||||||
mapSet(MINECRAFT_1_19_3, 34), mapSet(MINECRAFT_1_19, 35)));
|
mapSet(MINECRAFT_1_19, 35)));
|
||||||
empty(id("minecraft:entity_anchor", mapSet(MINECRAFT_1_21_6, 38), mapSet(MINECRAFT_1_20_5, 37), mapSet(MINECRAFT_1_20_3, 36),
|
empty(id("minecraft:entity_anchor", mapSet(MINECRAFT_1_20_5, 37), mapSet(MINECRAFT_1_20_3, 36), mapSet(MINECRAFT_1_19_3, 35),
|
||||||
mapSet(MINECRAFT_1_19_3, 35), mapSet(MINECRAFT_1_19, 36)));
|
mapSet(MINECRAFT_1_19, 36)));
|
||||||
empty(id("minecraft:int_range", mapSet(MINECRAFT_1_21_6, 39), mapSet(MINECRAFT_1_20_5, 38), mapSet(MINECRAFT_1_20_3, 37),
|
empty(id("minecraft:int_range", mapSet(MINECRAFT_1_20_5, 38), mapSet(MINECRAFT_1_20_3, 37), mapSet(MINECRAFT_1_19_3, 36),
|
||||||
mapSet(MINECRAFT_1_19_3, 36), mapSet(MINECRAFT_1_19, 37)));
|
mapSet(MINECRAFT_1_19, 37)));
|
||||||
empty(id("minecraft:float_range", mapSet(MINECRAFT_1_21_6, 40), mapSet(MINECRAFT_1_20_5, 39), mapSet(MINECRAFT_1_20_3, 38),
|
empty(id("minecraft:float_range", mapSet(MINECRAFT_1_20_5, 39), mapSet(MINECRAFT_1_20_3, 38), mapSet(MINECRAFT_1_19_3, 37),
|
||||||
mapSet(MINECRAFT_1_19_3, 37), mapSet(MINECRAFT_1_19, 38)));
|
mapSet(MINECRAFT_1_19, 38)));
|
||||||
empty(id("minecraft:item_enchantment", mapSet(MINECRAFT_1_19_3, -1), mapSet(MINECRAFT_1_19, 39)));
|
empty(id("minecraft:item_enchantment", mapSet(MINECRAFT_1_19_3, -1), mapSet(MINECRAFT_1_19, 39)));
|
||||||
empty(id("minecraft:entity_summon", mapSet(MINECRAFT_1_19_3, -1), mapSet(MINECRAFT_1_19, 40)));
|
empty(id("minecraft:entity_summon", mapSet(MINECRAFT_1_19_3, -1), mapSet(MINECRAFT_1_19, 40)));
|
||||||
empty(id("minecraft:dimension", mapSet(MINECRAFT_1_21_6, 41), mapSet(MINECRAFT_1_20_5, 40), mapSet(MINECRAFT_1_20_3, 39),
|
empty(id("minecraft:dimension", mapSet(MINECRAFT_1_20_5, 40), mapSet(MINECRAFT_1_20_3, 39), mapSet(MINECRAFT_1_19_3, 38),
|
||||||
mapSet(MINECRAFT_1_19_3, 38), mapSet(MINECRAFT_1_19, 41)));
|
mapSet(MINECRAFT_1_19, 41)));
|
||||||
empty(id("minecraft:gamemode", mapSet(MINECRAFT_1_21_6, 42), mapSet(MINECRAFT_1_20_5, 41), mapSet(MINECRAFT_1_20_3, 40),
|
empty(id("minecraft:gamemode", mapSet(MINECRAFT_1_20_5, 41), mapSet(MINECRAFT_1_20_3, 40), mapSet(MINECRAFT_1_19_3, 39))); // 1.19.3
|
||||||
mapSet(MINECRAFT_1_19_3, 39))); // 1.19.3
|
|
||||||
|
|
||||||
empty(id("minecraft:time", mapSet(MINECRAFT_1_21_6, 43), mapSet(MINECRAFT_1_20_5, 42), mapSet(MINECRAFT_1_20_3, 41),
|
empty(id("minecraft:time", mapSet(MINECRAFT_1_20_5, 42), mapSet(MINECRAFT_1_20_3, 41), mapSet(MINECRAFT_1_19_3, 40),
|
||||||
mapSet(MINECRAFT_1_19_3, 40), mapSet(MINECRAFT_1_19, 42)), TimeArgumentSerializer.TIME); // added in 1.14
|
mapSet(MINECRAFT_1_19, 42)), TimeArgumentSerializer.TIME); // added in 1.14
|
||||||
|
|
||||||
register(id("minecraft:resource_or_tag", mapSet(MINECRAFT_1_21_6, 44), mapSet(MINECRAFT_1_20_5, 43), mapSet(MINECRAFT_1_20_3, 42),
|
register(id("minecraft:resource_or_tag", mapSet(MINECRAFT_1_20_5, 43), mapSet(MINECRAFT_1_20_3, 42), mapSet(MINECRAFT_1_19_3, 41),
|
||||||
mapSet(MINECRAFT_1_19_3, 41), mapSet(MINECRAFT_1_19, 43)), RegistryKeyArgument.class, RegistryKeyArgumentSerializer.REGISTRY);
|
mapSet(MINECRAFT_1_19, 43)), RegistryKeyArgument.class, RegistryKeyArgumentSerializer.REGISTRY);
|
||||||
register(id("minecraft:resource_or_tag_key", mapSet(MINECRAFT_1_21_6, 45), mapSet(MINECRAFT_1_20_5, 44), mapSet(MINECRAFT_1_20_3, 43),
|
register(id("minecraft:resource_or_tag_key", mapSet(MINECRAFT_1_20_5, 44), mapSet(MINECRAFT_1_20_3, 43), mapSet(MINECRAFT_1_19_3, 42)),
|
||||||
mapSet(MINECRAFT_1_19_3, 42)),
|
|
||||||
RegistryKeyArgumentList.ResourceOrTagKey.class,
|
RegistryKeyArgumentList.ResourceOrTagKey.class,
|
||||||
RegistryKeyArgumentList.ResourceOrTagKey.Serializer.REGISTRY);
|
RegistryKeyArgumentList.ResourceOrTagKey.Serializer.REGISTRY);
|
||||||
register(id("minecraft:resource", mapSet(MINECRAFT_1_21_6, 46), mapSet(MINECRAFT_1_20_5, 45), mapSet(MINECRAFT_1_20_3, 44),
|
register(id("minecraft:resource", mapSet(MINECRAFT_1_20_5, 45), mapSet(MINECRAFT_1_20_3, 44), mapSet(MINECRAFT_1_19_3, 43),
|
||||||
mapSet(MINECRAFT_1_19_3, 43), mapSet(MINECRAFT_1_19, 44)),
|
mapSet(MINECRAFT_1_19, 44)),
|
||||||
RegistryKeyArgument.class, RegistryKeyArgumentSerializer.REGISTRY);
|
RegistryKeyArgument.class, RegistryKeyArgumentSerializer.REGISTRY);
|
||||||
register(id("minecraft:resource_key", mapSet(MINECRAFT_1_21_6, 47), mapSet(MINECRAFT_1_20_5, 46), mapSet(MINECRAFT_1_20_3, 45),
|
register(id("minecraft:resource_key", mapSet(MINECRAFT_1_20_5, 46), mapSet(MINECRAFT_1_20_3, 45), mapSet(MINECRAFT_1_19_3, 44)),
|
||||||
mapSet(MINECRAFT_1_19_3, 44)),
|
|
||||||
RegistryKeyArgumentList.ResourceKey.class,
|
RegistryKeyArgumentList.ResourceKey.class,
|
||||||
RegistryKeyArgumentList.ResourceKey.Serializer.REGISTRY);
|
RegistryKeyArgumentList.ResourceKey.Serializer.REGISTRY);
|
||||||
register(id("minecraft:resource_selector", mapSet(MINECRAFT_1_21_6, 48), mapSet(MINECRAFT_1_21_5, 47)),
|
|
||||||
RegistryKeyArgumentList.ResourceSelector.class,
|
|
||||||
RegistryKeyArgumentList.ResourceSelector.Serializer.REGISTRY);
|
|
||||||
|
|
||||||
empty(id("minecraft:template_mirror", mapSet(MINECRAFT_1_21_6, 49), mapSet(MINECRAFT_1_21_5, 48), mapSet(MINECRAFT_1_20_5, 47),
|
empty(id("minecraft:template_mirror", mapSet(MINECRAFT_1_20_5, 47), mapSet(MINECRAFT_1_20_3, 46), mapSet(MINECRAFT_1_19, 45))); // 1.19
|
||||||
mapSet(MINECRAFT_1_20_3, 46), mapSet(MINECRAFT_1_19, 45))); // 1.19
|
empty(id("minecraft:template_rotation", mapSet(MINECRAFT_1_20_5, 48), mapSet(MINECRAFT_1_20_3, 47), mapSet(MINECRAFT_1_19, 46))); // 1.19
|
||||||
empty(id("minecraft:template_rotation", mapSet(MINECRAFT_1_21_6, 50), mapSet(MINECRAFT_1_21_5, 49), mapSet(MINECRAFT_1_20_5, 48),
|
empty(id("minecraft:heightmap", mapSet(MINECRAFT_1_20_3, 49), mapSet(MINECRAFT_1_19_4, 47))); // 1.19.4
|
||||||
mapSet(MINECRAFT_1_20_3, 47), mapSet(MINECRAFT_1_19, 46))); // 1.19
|
|
||||||
empty(id("minecraft:heightmap", mapSet(MINECRAFT_1_21_6, 51), mapSet(MINECRAFT_1_21_5, 50), mapSet(MINECRAFT_1_20_3, 49),
|
|
||||||
mapSet(MINECRAFT_1_19_4, 47))); // 1.19.4
|
|
||||||
|
|
||||||
empty(id("minecraft:uuid", mapSet(MINECRAFT_1_21_6, 56), mapSet(MINECRAFT_1_21_5, 54),mapSet(MINECRAFT_1_20_5, 53), mapSet(MINECRAFT_1_20_3, 48),
|
empty(id("minecraft:uuid", mapSet(MINECRAFT_1_20_5, 53), mapSet(MINECRAFT_1_20_3, 48), mapSet(MINECRAFT_1_19_4, 48),
|
||||||
mapSet(MINECRAFT_1_19_4, 48), mapSet(MINECRAFT_1_19, 47))); // added in 1.16
|
mapSet(MINECRAFT_1_19, 47))); // added in 1.16
|
||||||
|
|
||||||
empty(id("minecraft:loot_table", mapSet(MINECRAFT_1_21_6, 52), mapSet(MINECRAFT_1_21_5, 51), mapSet(MINECRAFT_1_20_5, 50)));
|
empty(id("minecraft:loot_table", mapSet(MINECRAFT_1_20_5, 50)));
|
||||||
empty(id("minecraft:loot_predicate", mapSet(MINECRAFT_1_21_6, 53), mapSet(MINECRAFT_1_21_5, 52), mapSet(MINECRAFT_1_20_5, 51)));
|
empty(id("minecraft:loot_predicate", mapSet(MINECRAFT_1_20_5, 51)));
|
||||||
empty(id("minecraft:loot_modifier", mapSet(MINECRAFT_1_21_6, 54), mapSet(MINECRAFT_1_21_5, 53), mapSet(MINECRAFT_1_20_5, 52)));
|
empty(id("minecraft:loot_modifier", mapSet(MINECRAFT_1_20_5, 52)));
|
||||||
|
|
||||||
empty(id("minecraft:hex_color", mapSet(MINECRAFT_1_21_6, 17))); // added in 1.21.6
|
|
||||||
empty(id("minecraft:dialog", mapSet(MINECRAFT_1_21_6, 55))); // added in 1.21.6
|
|
||||||
|
|
||||||
// Crossstitch support
|
// Crossstitch support
|
||||||
register(id("crossstitch:mod_argument", mapSet(MINECRAFT_1_19, -256)), ModArgumentProperty.class, MOD);
|
register(id("crossstitch:mod_argument", mapSet(MINECRAFT_1_19, -256)), ModArgumentProperty.class, MOD);
|
||||||
|
|||||||
@ -67,23 +67,23 @@ public final class RegistryKeyArgumentList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ResourceSelector extends RegistryKeyArgument {
|
public static class Resource extends RegistryKeyArgument {
|
||||||
|
|
||||||
public ResourceSelector(String identifier) {
|
public Resource(String identifier) {
|
||||||
super(identifier);
|
super(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Serializer implements ArgumentPropertySerializer<ResourceSelector> {
|
public static class Serializer implements ArgumentPropertySerializer<Resource> {
|
||||||
|
|
||||||
static final ResourceSelector.Serializer REGISTRY = new ResourceSelector.Serializer();
|
static final Resource.Serializer REGISTRY = new Resource.Serializer();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResourceSelector deserialize(ByteBuf buf, ProtocolVersion protocolVersion) {
|
public Resource deserialize(ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||||
return new ResourceSelector(ProtocolUtils.readString(buf));
|
return new Resource(ProtocolUtils.readString(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(ResourceSelector object, ByteBuf buf, ProtocolVersion protocolVersion) {
|
public void serialize(Resource object, ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||||
ProtocolUtils.writeString(buf, object.getIdentifier());
|
ProtocolUtils.writeString(buf, object.getIdentifier());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,15 +32,13 @@ import java.util.function.Function;
|
|||||||
* A precisely ordered queue which allows for outside entries into the ordered queue through
|
* A precisely ordered queue which allows for outside entries into the ordered queue through
|
||||||
* piggybacking timestamps.
|
* piggybacking timestamps.
|
||||||
*/
|
*/
|
||||||
public class ChatQueue implements AutoCloseable {
|
public class ChatQueue {
|
||||||
|
|
||||||
private final Object internalLock = new Object();
|
private final Object internalLock = new Object();
|
||||||
private final ConnectedPlayer player;
|
private final ConnectedPlayer player;
|
||||||
private final ChatState chatState = new ChatState();
|
private final ChatState chatState = new ChatState();
|
||||||
private CompletableFuture<Void> head = CompletableFuture.completedFuture(null);
|
private CompletableFuture<Void> head = CompletableFuture.completedFuture(null);
|
||||||
|
|
||||||
private volatile boolean closed;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a {@link ChatQueue} for a specific {@link ConnectedPlayer}.
|
* Instantiates a {@link ChatQueue} for a specific {@link ConnectedPlayer}.
|
||||||
*
|
*
|
||||||
@ -52,14 +50,8 @@ public class ChatQueue implements AutoCloseable {
|
|||||||
|
|
||||||
private void queueTask(Task task) {
|
private void queueTask(Task task) {
|
||||||
synchronized (internalLock) {
|
synchronized (internalLock) {
|
||||||
if (closed) {
|
|
||||||
throw new IllegalStateException("ChatQueue has already been closed");
|
|
||||||
}
|
|
||||||
MinecraftConnection smc = player.ensureAndGetCurrentServer().ensureConnected();
|
MinecraftConnection smc = player.ensureAndGetCurrentServer().ensureConnected();
|
||||||
head = head.thenCompose(v -> {
|
head = head.thenCompose(v -> {
|
||||||
if (closed) {
|
|
||||||
return CompletableFuture.completedFuture(null);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
return task.update(chatState, smc).exceptionally(ignored -> null);
|
return task.update(chatState, smc).exceptionally(ignored -> null);
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
@ -110,9 +102,9 @@ public class ChatQueue implements AutoCloseable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends MinecraftPacket> CompletableFuture<Void> writePacket(T packet, MinecraftConnection smc) {
|
private static <T extends MinecraftPacket> CompletableFuture<Void> writePacket(T packet, MinecraftConnection smc) {
|
||||||
return CompletableFuture.runAsync(() -> {
|
return CompletableFuture.runAsync(() -> {
|
||||||
if (!closed && !smc.isClosed()) {
|
if (!smc.isClosed()) {
|
||||||
ChannelFuture future = smc.write(packet);
|
ChannelFuture future = smc.write(packet);
|
||||||
if (future != null) {
|
if (future != null) {
|
||||||
future.awaitUninterruptibly();
|
future.awaitUninterruptibly();
|
||||||
@ -121,11 +113,6 @@ public class ChatQueue implements AutoCloseable {
|
|||||||
}, smc.eventLoop());
|
}, smc.eventLoop());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
closed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface Task {
|
private interface Task {
|
||||||
CompletableFuture<Void> update(ChatState chatState, MinecraftConnection smc);
|
CompletableFuture<Void> update(ChatState chatState, MinecraftConnection smc);
|
||||||
}
|
}
|
||||||
@ -187,7 +174,7 @@ public class ChatQueue implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LastSeenMessages createLastSeen() {
|
public LastSeenMessages createLastSeen() {
|
||||||
return new LastSeenMessages(0, lastSeenMessages, (byte) 0);
|
return new LastSeenMessages(0, lastSeenMessages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,7 @@ import net.kyori.adventure.nbt.LongBinaryTag;
|
|||||||
import net.kyori.adventure.nbt.ShortBinaryTag;
|
import net.kyori.adventure.nbt.ShortBinaryTag;
|
||||||
import net.kyori.adventure.nbt.StringBinaryTag;
|
import net.kyori.adventure.nbt.StringBinaryTag;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
@ -105,14 +106,16 @@ public class ComponentHolder {
|
|||||||
public BinaryTag getBinaryTag() {
|
public BinaryTag getBinaryTag() {
|
||||||
if (binaryTag == null) {
|
if (binaryTag == null) {
|
||||||
// TODO: replace this with adventure-text-serializer-nbt
|
// TODO: replace this with adventure-text-serializer-nbt
|
||||||
binaryTag = serialize(ProtocolUtils.getJsonChatSerializer(version).serializeToTree(getComponent()));
|
binaryTag = serialize(GsonComponentSerializer.gson().serializeToTree(getComponent()));
|
||||||
}
|
}
|
||||||
return binaryTag;
|
return binaryTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BinaryTag serialize(JsonElement json) {
|
public static BinaryTag serialize(JsonElement json) {
|
||||||
if (json instanceof JsonPrimitive jsonPrimitive) {
|
if (json instanceof JsonPrimitive) {
|
||||||
if (jsonPrimitive.isNumber()) {
|
JsonPrimitive jsonPrimitive = (JsonPrimitive) json;
|
||||||
|
|
||||||
|
if (jsonPrimitive.isNumber()) {
|
||||||
Number number = json.getAsNumber();
|
Number number = json.getAsNumber();
|
||||||
|
|
||||||
if (number instanceof Byte) {
|
if (number instanceof Byte) {
|
||||||
|
|||||||
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.protocol.packet.chat;
|
package com.velocitypowered.proxy.protocol.packet.chat;
|
||||||
|
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -27,38 +26,30 @@ public class LastSeenMessages {
|
|||||||
|
|
||||||
public static final int WINDOW_SIZE = 20;
|
public static final int WINDOW_SIZE = 20;
|
||||||
private static final int DIV_FLOOR = -Math.floorDiv(-WINDOW_SIZE, 8);
|
private static final int DIV_FLOOR = -Math.floorDiv(-WINDOW_SIZE, 8);
|
||||||
private final int offset;
|
private int offset;
|
||||||
private final BitSet acknowledged;
|
private BitSet acknowledged;
|
||||||
private byte checksum;
|
|
||||||
|
|
||||||
public LastSeenMessages() {
|
public LastSeenMessages() {
|
||||||
this(0, new BitSet(), (byte) 0);
|
this.offset = 0;
|
||||||
|
this.acknowledged = new BitSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LastSeenMessages(int offset, BitSet acknowledged, byte checksum) {
|
public LastSeenMessages(int offset, BitSet acknowledged) {
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
this.acknowledged = acknowledged;
|
this.acknowledged = acknowledged;
|
||||||
this.checksum = checksum;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LastSeenMessages(ByteBuf buf, ProtocolVersion protocolVersion) {
|
public LastSeenMessages(ByteBuf buf) {
|
||||||
this.offset = ProtocolUtils.readVarInt(buf);
|
this.offset = ProtocolUtils.readVarInt(buf);
|
||||||
|
|
||||||
byte[] bytes = new byte[DIV_FLOOR];
|
byte[] bytes = new byte[DIV_FLOOR];
|
||||||
buf.readBytes(bytes);
|
buf.readBytes(bytes);
|
||||||
this.acknowledged = BitSet.valueOf(bytes);
|
this.acknowledged = BitSet.valueOf(bytes);
|
||||||
|
|
||||||
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_21_5)) {
|
|
||||||
this.checksum = buf.readByte();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void encode(ByteBuf buf, ProtocolVersion protocolVersion) {
|
public void encode(ByteBuf buf) {
|
||||||
ProtocolUtils.writeVarInt(buf, offset);
|
ProtocolUtils.writeVarInt(buf, offset);
|
||||||
buf.writeBytes(Arrays.copyOf(acknowledged.toByteArray(), DIV_FLOOR));
|
buf.writeBytes(Arrays.copyOf(acknowledged.toByteArray(), DIV_FLOOR));
|
||||||
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_21_5)) {
|
|
||||||
buf.writeByte(this.checksum);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOffset() {
|
public int getOffset() {
|
||||||
@ -70,15 +61,14 @@ public class LastSeenMessages {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LastSeenMessages offset(final int offset) {
|
public LastSeenMessages offset(final int offset) {
|
||||||
return new LastSeenMessages(this.offset + offset, acknowledged, checksum);
|
return new LastSeenMessages(this.offset + offset, acknowledged);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "LastSeenMessages{" +
|
return "LastSeenMessages{" +
|
||||||
"offset=" + offset +
|
"offset=" + offset +
|
||||||
", acknowledged=" + acknowledged +
|
", acknowledged=" + acknowledged +
|
||||||
", checksum=" + checksum +
|
'}';
|
||||||
'}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2025 Velocity Contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.velocitypowered.proxy.protocol.packet.chat;
|
|
||||||
|
|
||||||
import com.velocitypowered.api.proxy.Player;
|
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
|
|
||||||
public abstract class RateLimitedCommandHandler<T extends MinecraftPacket> implements CommandHandler<T> {
|
|
||||||
|
|
||||||
private final Player player;
|
|
||||||
private final VelocityServer velocityServer;
|
|
||||||
|
|
||||||
private int failedAttempts;
|
|
||||||
|
|
||||||
protected RateLimitedCommandHandler(Player player, VelocityServer velocityServer) {
|
|
||||||
this.player = player;
|
|
||||||
this.velocityServer = velocityServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handlePlayerCommand(MinecraftPacket packet) {
|
|
||||||
if (packetClass().isInstance(packet)) {
|
|
||||||
if (!velocityServer.getCommandRateLimiter().attempt(player.getUniqueId())) {
|
|
||||||
if (velocityServer.getConfiguration().isKickOnCommandRateLimit() && failedAttempts++ >= velocityServer.getConfiguration().getKickAfterRateLimitedCommands()) {
|
|
||||||
player.disconnect(Component.translatable("velocity.kick.command-rate-limit"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (velocityServer.getConfiguration().isForwardCommandsIfRateLimited()) {
|
|
||||||
return false; // Send the packet to the server
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
failedAttempts = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePlayerCommandInternal(packetClass().cast(packet));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -21,18 +21,17 @@ import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
|||||||
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
import com.velocitypowered.proxy.protocol.packet.chat.RateLimitedCommandHandler;
|
import com.velocitypowered.proxy.protocol.packet.chat.CommandHandler;
|
||||||
import com.velocitypowered.proxy.protocol.packet.chat.builder.ChatBuilderV2;
|
import com.velocitypowered.proxy.protocol.packet.chat.builder.ChatBuilderV2;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
|
||||||
public class KeyedCommandHandler extends RateLimitedCommandHandler<KeyedPlayerCommandPacket> {
|
public class KeyedCommandHandler implements CommandHandler<KeyedPlayerCommandPacket> {
|
||||||
|
|
||||||
private final ConnectedPlayer player;
|
private final ConnectedPlayer player;
|
||||||
private final VelocityServer server;
|
private final VelocityServer server;
|
||||||
|
|
||||||
public KeyedCommandHandler(ConnectedPlayer player, VelocityServer server) {
|
public KeyedCommandHandler(ConnectedPlayer player, VelocityServer server) {
|
||||||
super(player, server);
|
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -116,6 +116,8 @@ public class KeyedPlayerChatPacket implements MinecraftPacket {
|
|||||||
ProtocolUtils.readByteArray(buf));
|
ProtocolUtils.readByteArray(buf));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -132,6 +132,7 @@ public class KeyedPlayerCommandPacket implements MinecraftPacket {
|
|||||||
unsigned = true;
|
unsigned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -91,8 +91,7 @@ public class LegacyChatPacket implements MinecraftPacket {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
||||||
message = ProtocolUtils.readString(buf, direction == ProtocolUtils.Direction.CLIENTBOUND
|
message = ProtocolUtils.readString(buf);
|
||||||
? 262144 : version.noLessThan(ProtocolVersion.MINECRAFT_1_11) ? 256 : 100);
|
|
||||||
if (direction == ProtocolUtils.Direction.CLIENTBOUND
|
if (direction == ProtocolUtils.Direction.CLIENTBOUND
|
||||||
&& version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
&& version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||||
type = buf.readByte();
|
type = buf.readByte();
|
||||||
|
|||||||
@ -20,18 +20,16 @@ package com.velocitypowered.proxy.protocol.packet.chat.legacy;
|
|||||||
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
import com.velocitypowered.proxy.protocol.packet.chat.RateLimitedCommandHandler;
|
import com.velocitypowered.proxy.protocol.packet.chat.CommandHandler;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class LegacyCommandHandler extends RateLimitedCommandHandler<LegacyChatPacket> {
|
public class LegacyCommandHandler implements CommandHandler<LegacyChatPacket> {
|
||||||
|
|
||||||
private final ConnectedPlayer player;
|
private final ConnectedPlayer player;
|
||||||
private final VelocityServer server;
|
private final VelocityServer server;
|
||||||
|
|
||||||
public LegacyCommandHandler(ConnectedPlayer player, VelocityServer server) {
|
public LegacyCommandHandler(ConnectedPlayer player, VelocityServer server) {
|
||||||
super(player, server);
|
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,19 +22,17 @@ import com.velocitypowered.proxy.VelocityServer;
|
|||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.chat.ChatAcknowledgementPacket;
|
import com.velocitypowered.proxy.protocol.packet.chat.ChatAcknowledgementPacket;
|
||||||
|
import com.velocitypowered.proxy.protocol.packet.chat.CommandHandler;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import com.velocitypowered.proxy.protocol.packet.chat.RateLimitedCommandHandler;
|
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
public class SessionCommandHandler extends RateLimitedCommandHandler<SessionPlayerCommandPacket> {
|
public class SessionCommandHandler implements CommandHandler<SessionPlayerCommandPacket> {
|
||||||
|
|
||||||
private final ConnectedPlayer player;
|
private final ConnectedPlayer player;
|
||||||
private final VelocityServer server;
|
private final VelocityServer server;
|
||||||
|
|
||||||
public SessionCommandHandler(ConnectedPlayer player, VelocityServer server) {
|
public SessionCommandHandler(ConnectedPlayer player, VelocityServer server) {
|
||||||
super(player, server);
|
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,11 +69,12 @@ 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];
|
||||||
}
|
}
|
||||||
this.lastSeenMessages = new LastSeenMessages(buf, protocolVersion);
|
this.lastSeenMessages = new LastSeenMessages(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -86,7 +87,7 @@ public class SessionPlayerChatPacket implements MinecraftPacket {
|
|||||||
if (this.signed) {
|
if (this.signed) {
|
||||||
buf.writeBytes(this.signature);
|
buf.writeBytes(this.signature);
|
||||||
}
|
}
|
||||||
this.lastSeenMessages.encode(buf, protocolVersion);
|
this.lastSeenMessages.encode(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -45,7 +45,9 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
|
|||||||
this.timeStamp = Instant.ofEpochMilli(buf.readLong());
|
this.timeStamp = Instant.ofEpochMilli(buf.readLong());
|
||||||
this.salt = buf.readLong();
|
this.salt = buf.readLong();
|
||||||
this.argumentSignatures = new ArgumentSignatures(buf);
|
this.argumentSignatures = new ArgumentSignatures(buf);
|
||||||
this.lastSeenMessages = new LastSeenMessages(buf, protocolVersion);
|
this.lastSeenMessages = new LastSeenMessages(buf);
|
||||||
|
|
||||||
|
this.argumentSignatures = new ArgumentSignatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -54,7 +56,7 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
|
|||||||
buf.writeLong(this.timeStamp.toEpochMilli());
|
buf.writeLong(this.timeStamp.toEpochMilli());
|
||||||
buf.writeLong(this.salt);
|
buf.writeLong(this.salt);
|
||||||
this.argumentSignatures.encode(buf);
|
this.argumentSignatures.encode(buf);
|
||||||
this.lastSeenMessages.encode(buf, protocolVersion);
|
this.lastSeenMessages.encode(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCommand() {
|
public String getCommand() {
|
||||||
|
|||||||
@ -68,6 +68,12 @@ public class ClientboundServerLinksPacket implements MinecraftPacket {
|
|||||||
|
|
||||||
public record ServerLink(int id, ComponentHolder displayName, String url) {
|
public record ServerLink(int id, ComponentHolder displayName, String url) {
|
||||||
|
|
||||||
|
public ServerLink(com.velocitypowered.api.util.ServerLink link, ProtocolVersion protocolVersion) {
|
||||||
|
this(link.getBuiltInType().map(Enum::ordinal).orElse(-1),
|
||||||
|
link.getCustomLabel().map(c -> new ComponentHolder(protocolVersion, c)).orElse(null),
|
||||||
|
link.getUrl().toString());
|
||||||
|
}
|
||||||
|
|
||||||
private static ServerLink read(ByteBuf buf, ProtocolVersion version) {
|
private static ServerLink read(ByteBuf buf, ProtocolVersion version) {
|
||||||
if (buf.readBoolean()) {
|
if (buf.readBoolean()) {
|
||||||
return new ServerLink(ProtocolUtils.readVarInt(buf), null, ProtocolUtils.readString(buf));
|
return new ServerLink(ProtocolUtils.readVarInt(buf), null, ProtocolUtils.readString(buf));
|
||||||
|
|||||||
@ -36,7 +36,7 @@ public class KnownPacksPacket implements MinecraftPacket {
|
|||||||
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
|
public void decode(ByteBuf buf, ProtocolUtils.Direction direction,
|
||||||
ProtocolVersion protocolVersion) {
|
ProtocolVersion protocolVersion) {
|
||||||
final int packCount = ProtocolUtils.readVarInt(buf);
|
final int packCount = ProtocolUtils.readVarInt(buf);
|
||||||
if (direction == ProtocolUtils.Direction.SERVERBOUND && packCount > MAX_LENGTH_PACKS) {
|
if (packCount > MAX_LENGTH_PACKS) {
|
||||||
throw TOO_MANY_PACKS;
|
throw TOO_MANY_PACKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,20 +22,13 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
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.LegacyChannelIdentifier;
|
|
||||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
|
||||||
import com.velocitypowered.api.util.ProxyVersion;
|
import com.velocitypowered.api.util.ProxyVersion;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
|
||||||
import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket;
|
import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket;
|
||||||
import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -92,18 +85,13 @@ public final class PluginMessageUtil {
|
|||||||
.equals(UNREGISTER_CHANNEL);
|
.equals(UNREGISTER_CHANNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final QuietDecoderException ILLEGAL_CHANNEL = new QuietDecoderException("Illegal channel");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all the channels in a register or unregister plugin message.
|
* Fetches all the channels in a register or unregister plugin message.
|
||||||
*
|
*
|
||||||
* @param existingChannels the number of channels already registered
|
|
||||||
* @param message the message to get the channels from
|
* @param message the message to get the channels from
|
||||||
* @return the channels, as an immutable list
|
* @return the channels, as an immutable list
|
||||||
*/
|
*/
|
||||||
public static List<ChannelIdentifier> getChannels(int existingChannels,
|
public static List<String> getChannels(PluginMessagePacket message) {
|
||||||
PluginMessagePacket message,
|
|
||||||
ProtocolVersion protocolVersion) {
|
|
||||||
checkNotNull(message, "message");
|
checkNotNull(message, "message");
|
||||||
checkArgument(isRegister(message) || isUnregister(message), "Unknown channel type %s",
|
checkArgument(isRegister(message) || isUnregister(message), "Unknown channel type %s",
|
||||||
message.getChannel());
|
message.getChannel());
|
||||||
@ -112,28 +100,8 @@ public final class PluginMessageUtil {
|
|||||||
// has caused issues with 1.13+ compatibility. Just return an empty list.
|
// has caused issues with 1.13+ compatibility. Just return an empty list.
|
||||||
return ImmutableList.of();
|
return ImmutableList.of();
|
||||||
}
|
}
|
||||||
String payload = message.content().toString(StandardCharsets.UTF_8);
|
String channels = message.content().toString(StandardCharsets.UTF_8);
|
||||||
checkArgument(payload.length() <= Short.MAX_VALUE, "payload too long: %s", payload.length());
|
return ImmutableList.copyOf(channels.split("\0"));
|
||||||
String[] channels = payload.split("\0");
|
|
||||||
checkArgument(existingChannels + channels.length <= ConnectedPlayer.MAX_CLIENTSIDE_PLUGIN_CHANNELS,
|
|
||||||
"too many channels: %s + %s > %s", existingChannels, channels.length, ConnectedPlayer.MAX_CLIENTSIDE_PLUGIN_CHANNELS);
|
|
||||||
ImmutableList.Builder<ChannelIdentifier> channelIdentifiers = ImmutableList.builderWithExpectedSize(channels.length);
|
|
||||||
try {
|
|
||||||
for (String channel : channels) {
|
|
||||||
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_13)) {
|
|
||||||
channelIdentifiers.add(MinecraftChannelIdentifier.from(channel));
|
|
||||||
} else {
|
|
||||||
channelIdentifiers.add(new LegacyChannelIdentifier(channel));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
if (MinecraftDecoder.DEBUG) {
|
|
||||||
throw e;
|
|
||||||
} else {
|
|
||||||
throw ILLEGAL_CHANNEL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return channelIdentifiers.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,31 +112,16 @@ public final class PluginMessageUtil {
|
|||||||
* @return the plugin message to send
|
* @return the plugin message to send
|
||||||
*/
|
*/
|
||||||
public static PluginMessagePacket constructChannelsPacket(ProtocolVersion protocolVersion,
|
public static PluginMessagePacket constructChannelsPacket(ProtocolVersion protocolVersion,
|
||||||
Collection<ChannelIdentifier> channels) {
|
Collection<String> channels) {
|
||||||
checkNotNull(channels, "channels");
|
checkNotNull(channels, "channels");
|
||||||
checkArgument(!channels.isEmpty(), "no channels specified");
|
checkArgument(!channels.isEmpty(), "no channels specified");
|
||||||
String channelName = protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_13)
|
String channelName = protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_13)
|
||||||
? REGISTER_CHANNEL : REGISTER_CHANNEL_LEGACY;
|
? REGISTER_CHANNEL : REGISTER_CHANNEL_LEGACY;
|
||||||
ByteBuf contents = Unpooled.buffer();
|
ByteBuf contents = Unpooled.buffer();
|
||||||
contents.writeCharSequence(joinChannels(channels), StandardCharsets.UTF_8);
|
contents.writeCharSequence(String.join("\0", channels), StandardCharsets.UTF_8);
|
||||||
return new PluginMessagePacket(channelName, contents);
|
return new PluginMessagePacket(channelName, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String joinChannels(Collection<ChannelIdentifier> channels) {
|
|
||||||
checkNotNull(channels, "channels");
|
|
||||||
checkArgument(!channels.isEmpty(), "no channels specified");
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
Iterator<ChannelIdentifier> iterator = channels.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
ChannelIdentifier channel = iterator.next();
|
|
||||||
sb.append(channel.getId());
|
|
||||||
if (iterator.hasNext()) {
|
|
||||||
sb.append('\0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rewrites the brand message to indicate the presence of Velocity.
|
* Rewrites the brand message to indicate the presence of Velocity.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -43,23 +43,20 @@ public class PingSessionHandler implements MinecraftSessionHandler {
|
|||||||
private final MinecraftConnection connection;
|
private final MinecraftConnection connection;
|
||||||
private final ProtocolVersion version;
|
private final ProtocolVersion version;
|
||||||
private boolean completed = false;
|
private boolean completed = false;
|
||||||
private final String virtualHostString;
|
|
||||||
|
|
||||||
PingSessionHandler(CompletableFuture<ServerPing> result, RegisteredServer server,
|
PingSessionHandler(CompletableFuture<ServerPing> result, RegisteredServer server,
|
||||||
MinecraftConnection connection, ProtocolVersion version, String virtualHostString) {
|
MinecraftConnection connection, ProtocolVersion version) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.virtualHostString = virtualHostString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activated() {
|
public void activated() {
|
||||||
HandshakePacket handshake = new HandshakePacket();
|
HandshakePacket handshake = new HandshakePacket();
|
||||||
handshake.setIntent(HandshakeIntent.STATUS);
|
handshake.setIntent(HandshakeIntent.STATUS);
|
||||||
handshake.setServerAddress(this.virtualHostString == null || this.virtualHostString.isEmpty()
|
handshake.setServerAddress(server.getServerInfo().getAddress().getHostString());
|
||||||
? server.getServerInfo().getAddress().getHostString() : this.virtualHostString);
|
|
||||||
handshake.setPort(server.getServerInfo().getAddress().getPort());
|
handshake.setPort(server.getServerInfo().getAddress().getPort());
|
||||||
handshake.setProtocolVersion(version);
|
handshake.setProtocolVersion(version);
|
||||||
connection.delayedWrite(handshake);
|
connection.delayedWrite(handshake);
|
||||||
|
|||||||
@ -114,7 +114,7 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud
|
|||||||
server.createBootstrap(loop).handler(new ChannelInitializer<>() {
|
server.createBootstrap(loop).handler(new ChannelInitializer<>() {
|
||||||
@Override
|
@Override
|
||||||
protected void initChannel(Channel ch) {
|
protected void initChannel(Channel ch) {
|
||||||
ch.pipeline().addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder(ProtocolUtils.Direction.CLIENTBOUND))
|
ch.pipeline().addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
|
||||||
.addLast(READ_TIMEOUT, new ReadTimeoutHandler(
|
.addLast(READ_TIMEOUT, new ReadTimeoutHandler(
|
||||||
pingOptions.getTimeout() == 0
|
pingOptions.getTimeout() == 0
|
||||||
? server.getConfiguration().getReadTimeout()
|
? server.getConfiguration().getReadTimeout()
|
||||||
@ -129,7 +129,7 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud
|
|||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
MinecraftConnection conn = future.channel().pipeline().get(MinecraftConnection.class);
|
MinecraftConnection conn = future.channel().pipeline().get(MinecraftConnection.class);
|
||||||
PingSessionHandler handler = new PingSessionHandler(pingFuture,
|
PingSessionHandler handler = new PingSessionHandler(pingFuture,
|
||||||
VelocityRegisteredServer.this, conn, pingOptions.getProtocolVersion(), pingOptions.getVirtualHost());
|
VelocityRegisteredServer.this, conn, pingOptions.getProtocolVersion());
|
||||||
conn.setActiveSessionHandler(StateRegistry.HANDSHAKE, handler);
|
conn.setActiveSessionHandler(StateRegistry.HANDSHAKE, handler);
|
||||||
} else {
|
} else {
|
||||||
pingFuture.completeExceptionally(future.cause());
|
pingFuture.completeExceptionally(future.cause());
|
||||||
|
|||||||
@ -166,7 +166,7 @@ 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, @Nullable ChatSession chatSession, boolean listed, int listOrder, boolean showHat) {
|
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder) {
|
||||||
return buildEntry(profile, displayName, latency, gameMode, chatSession, listed);
|
return buildEntry(profile, displayName, latency, gameMode, chatSession, listed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -90,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.getListOrder(), entry1.isShowHat());
|
entry1.getLatency(), entry1.getGameMode(), entry1.getChatSession(), entry1.isListed(), entry1.getListOrder());
|
||||||
}
|
}
|
||||||
|
|
||||||
EnumSet<UpsertPlayerInfoPacket.Action> actions = EnumSet
|
EnumSet<UpsertPlayerInfoPacket.Action> actions = EnumSet
|
||||||
@ -134,11 +134,6 @@ public class VelocityTabList implements InternalTabList {
|
|||||||
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER);
|
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER);
|
||||||
playerInfoEntry.setListOrder(entry.getListOrder());
|
playerInfoEntry.setListOrder(entry.getListOrder());
|
||||||
}
|
}
|
||||||
if (!Objects.equals(previousEntry.isShowHat(), entry.isShowHat())
|
|
||||||
&& player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_4)) {
|
|
||||||
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_HAT);
|
|
||||||
playerInfoEntry.setShowHat(entry.isShowHat());
|
|
||||||
}
|
|
||||||
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) {
|
||||||
@ -178,10 +173,6 @@ public class VelocityTabList implements InternalTabList {
|
|||||||
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER);
|
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER);
|
||||||
playerInfoEntry.setListOrder(entry.getListOrder());
|
playerInfoEntry.setListOrder(entry.getListOrder());
|
||||||
}
|
}
|
||||||
if (!entry.isShowHat() && player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_4)) {
|
|
||||||
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_HAT);
|
|
||||||
playerInfoEntry.setShowHat(entry.isShowHat());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
});
|
});
|
||||||
@ -227,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, int listOrder, boolean showHat) {
|
@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, listOrder, showHat);
|
listed, listOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -267,8 +258,7 @@ public class VelocityTabList implements InternalTabList {
|
|||||||
-1,
|
-1,
|
||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
0,
|
0
|
||||||
true
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -40,7 +40,6 @@ public class VelocityTabListEntry implements TabListEntry {
|
|||||||
private int gameMode;
|
private int gameMode;
|
||||||
private boolean listed;
|
private boolean listed;
|
||||||
private int listOrder;
|
private int listOrder;
|
||||||
private boolean showHat;
|
|
||||||
private @Nullable ChatSession session;
|
private @Nullable ChatSession session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,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 listOrder, boolean showHat) {
|
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;
|
||||||
@ -57,7 +56,6 @@ public class VelocityTabListEntry implements TabListEntry {
|
|||||||
this.session = session;
|
this.session = session;
|
||||||
this.listed = listed;
|
this.listed = listed;
|
||||||
this.listOrder = listOrder;
|
this.listOrder = listOrder;
|
||||||
this.showHat = showHat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -175,24 +173,4 @@ public class VelocityTabListEntry implements TabListEntry {
|
|||||||
void setListOrderWithoutUpdate(int listOrder) {
|
void setListOrderWithoutUpdate(int listOrder) {
|
||||||
this.listOrder = listOrder;
|
this.listOrder = listOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isShowHat() {
|
|
||||||
return showHat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VelocityTabListEntry setShowHat(boolean showHat) {
|
|
||||||
this.showHat = showHat;
|
|
||||||
if (tabList.getPlayer().getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_4)) {
|
|
||||||
UpsertPlayerInfoPacket.Entry upsertEntry = this.tabList.createRawEntry(this);
|
|
||||||
upsertEntry.setShowHat(showHat);
|
|
||||||
tabList.emitActionRaw(UpsertPlayerInfoPacket.Action.UPDATE_HAT, upsertEntry);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setShowHatWithoutUpdate(boolean showHat) {
|
|
||||||
this.showHat = showHat;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -35,8 +35,6 @@ public class VelocityTabListEntryLegacy extends KeyedVelocityTabListEntry {
|
|||||||
@Override
|
@Override
|
||||||
public TabListEntry setDisplayName(@Nullable Component displayName) {
|
public TabListEntry setDisplayName(@Nullable Component displayName) {
|
||||||
getTabList().removeEntry(getProfile().getId()); // We have to remove first if updating
|
getTabList().removeEntry(getProfile().getId()); // We have to remove first if updating
|
||||||
setDisplayNameInternal(displayName);
|
return super.setDisplayName(displayName);
|
||||||
getTabList().addEntry(this);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,8 +19,6 @@ package com.velocitypowered.proxy.tablist;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
|
||||||
import com.velocitypowered.api.proxy.player.ChatSession;
|
|
||||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||||
import com.velocitypowered.api.util.GameProfile;
|
import com.velocitypowered.api.util.GameProfile;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
@ -135,22 +133,9 @@ public class VelocityTabListLegacy extends KeyedVelocityTabList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public TabListEntry buildEntry(GameProfile profile,
|
|
||||||
net.kyori.adventure.text.@Nullable Component displayName,
|
|
||||||
int latency, int gameMode, @Nullable IdentifiedKey key) {
|
|
||||||
return new VelocityTabListEntryLegacy(this, profile, displayName, latency, gameMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||||
int gameMode, @Nullable ChatSession chatSession, boolean listed) {
|
int gameMode) {
|
||||||
return new VelocityTabListEntryLegacy(this, profile, displayName, latency, gameMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
|
||||||
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder, boolean showHat) {
|
|
||||||
return new VelocityTabListEntryLegacy(this, profile, displayName, latency, gameMode);
|
return new VelocityTabListEntryLegacy(this, profile, displayName, latency, gameMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,9 @@ import net.kyori.adventure.text.Component;
|
|||||||
import net.kyori.adventure.text.TranslatableComponent;
|
import net.kyori.adventure.text.TranslatableComponent;
|
||||||
import net.kyori.adventure.text.flattener.ComponentFlattener;
|
import net.kyori.adventure.text.flattener.ComponentFlattener;
|
||||||
import net.kyori.adventure.translation.GlobalTranslator;
|
import net.kyori.adventure.translation.GlobalTranslator;
|
||||||
|
import net.kyori.adventure.translation.TranslationRegistry;
|
||||||
|
import net.kyori.adventure.translation.Translator;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Velocity Translation Mapper.
|
* Velocity Translation Mapper.
|
||||||
@ -40,9 +43,25 @@ public enum TranslatableMapper implements BiConsumer<TranslatableComponent, Cons
|
|||||||
final TranslatableComponent translatableComponent,
|
final TranslatableComponent translatableComponent,
|
||||||
final Consumer<Component> componentConsumer
|
final Consumer<Component> componentConsumer
|
||||||
) {
|
) {
|
||||||
final Locale locale = ClosestLocaleMatcher.INSTANCE.lookupClosest(Locale.getDefault());
|
for (final Translator source : GlobalTranslator.translator().sources()) {
|
||||||
if (GlobalTranslator.translator().canTranslate(translatableComponent.key(), locale)) {
|
if (source instanceof TranslationRegistry registry
|
||||||
componentConsumer.accept(GlobalTranslator.render(translatableComponent, locale));
|
&& registry.contains(translatableComponent.key())) {
|
||||||
|
componentConsumer.accept(GlobalTranslator.render(translatableComponent,
|
||||||
|
ClosestLocaleMatcher.INSTANCE.lookupClosest(Locale.getDefault())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final @Nullable String fallback = translatableComponent.fallback();
|
||||||
|
if (fallback == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (final Translator source : GlobalTranslator.translator().sources()) {
|
||||||
|
if (source instanceof TranslationRegistry registry && registry.contains(fallback)) {
|
||||||
|
componentConsumer.accept(
|
||||||
|
GlobalTranslator.render(Component.translatable(fallback),
|
||||||
|
ClosestLocaleMatcher.INSTANCE.lookupClosest(Locale.getDefault())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,10 +79,10 @@ public class VelocityChannelRegistrar implements ChannelRegistrar {
|
|||||||
*
|
*
|
||||||
* @return all legacy channel IDs
|
* @return all legacy channel IDs
|
||||||
*/
|
*/
|
||||||
public Collection<ChannelIdentifier> getLegacyChannelIds() {
|
public Collection<String> getLegacyChannelIds() {
|
||||||
Collection<ChannelIdentifier> ids = new HashSet<>();
|
Collection<String> ids = new HashSet<>();
|
||||||
for (ChannelIdentifier value : identifierMap.values()) {
|
for (ChannelIdentifier value : identifierMap.values()) {
|
||||||
ids.add(new LegacyChannelIdentifier(value.getId()));
|
ids.add(value.getId());
|
||||||
}
|
}
|
||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
@ -92,13 +92,13 @@ public class VelocityChannelRegistrar implements ChannelRegistrar {
|
|||||||
*
|
*
|
||||||
* @return the channel IDs for Minecraft 1.13 and above
|
* @return the channel IDs for Minecraft 1.13 and above
|
||||||
*/
|
*/
|
||||||
public Collection<ChannelIdentifier> getModernChannelIds() {
|
public Collection<String> getModernChannelIds() {
|
||||||
Collection<ChannelIdentifier> ids = new HashSet<>();
|
Collection<String> ids = new HashSet<>();
|
||||||
for (ChannelIdentifier value : identifierMap.values()) {
|
for (ChannelIdentifier value : identifierMap.values()) {
|
||||||
if (value instanceof MinecraftChannelIdentifier) {
|
if (value instanceof MinecraftChannelIdentifier) {
|
||||||
ids.add(value);
|
ids.add(value.getId());
|
||||||
} else {
|
} else {
|
||||||
ids.add(MinecraftChannelIdentifier.from(PluginMessageUtil.transformLegacyToModernChannel(value.getId())));
|
ids.add(PluginMessageUtil.transformLegacyToModernChannel(value.getId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ids;
|
return ids;
|
||||||
@ -114,7 +114,7 @@ public class VelocityChannelRegistrar implements ChannelRegistrar {
|
|||||||
* @param protocolVersion the protocol version in use
|
* @param protocolVersion the protocol version in use
|
||||||
* @return the list of channels to register
|
* @return the list of channels to register
|
||||||
*/
|
*/
|
||||||
public Collection<ChannelIdentifier> getChannelsForProtocol(ProtocolVersion protocolVersion) {
|
public Collection<String> getChannelsForProtocol(ProtocolVersion protocolVersion) {
|
||||||
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_13)) {
|
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_13)) {
|
||||||
return getModernChannelIds();
|
return getModernChannelIds();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2019-2023 Velocity Contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.velocitypowered.proxy.util.collect;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.google.common.collect.ForwardingSet;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An unsynchronized collection that puts an upper bound on the size of the collection.
|
|
||||||
*/
|
|
||||||
public final class CappedSet<T> extends ForwardingSet<T> {
|
|
||||||
|
|
||||||
private final Set<T> delegate;
|
|
||||||
private final int upperSize;
|
|
||||||
|
|
||||||
private CappedSet(Set<T> delegate, int upperSize) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
this.upperSize = upperSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a capped collection backed by a {@link HashSet}.
|
|
||||||
*
|
|
||||||
* @param maxSize the maximum size of the collection
|
|
||||||
* @param <T> the type of elements in the collection
|
|
||||||
* @return the new collection
|
|
||||||
*/
|
|
||||||
public static <T> Set<T> create(int maxSize) {
|
|
||||||
return new CappedSet<>(new HashSet<>(), maxSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Set<T> delegate() {
|
|
||||||
return delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean add(T element) {
|
|
||||||
if (this.delegate.size() >= upperSize) {
|
|
||||||
Preconditions.checkState(this.delegate.contains(element),
|
|
||||||
"collection is too large (%s >= %s)",
|
|
||||||
this.delegate.size(), this.upperSize);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.delegate.add(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean addAll(Collection<? extends T> collection) {
|
|
||||||
return this.standardAddAll(collection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -22,15 +22,15 @@ import com.github.benmanes.caffeine.cache.Caffeine;
|
|||||||
import com.github.benmanes.caffeine.cache.Ticker;
|
import com.github.benmanes.caffeine.cache.Ticker;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple rate-limiter based on a Caffeine {@link Cache}.
|
* A simple rate-limiter based on a Caffeine {@link Cache}.
|
||||||
*/
|
*/
|
||||||
public class CaffeineCacheRatelimiter<T> implements Ratelimiter<T> {
|
public class CaffeineCacheRatelimiter implements Ratelimiter {
|
||||||
|
|
||||||
private final Cache<T, Long> expiringCache;
|
private final Cache<InetAddress, Long> expiringCache;
|
||||||
private final long timeoutNanos;
|
private final long timeoutNanos;
|
||||||
|
|
||||||
CaffeineCacheRatelimiter(long time, TimeUnit unit) {
|
CaffeineCacheRatelimiter(long time, TimeUnit unit) {
|
||||||
@ -49,15 +49,16 @@ public class CaffeineCacheRatelimiter<T> implements Ratelimiter<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to rate-limit the object.
|
* Attempts to rate-limit the client.
|
||||||
*
|
*
|
||||||
* @param key the object to rate limit
|
* @param address the address to rate limit
|
||||||
* @return true if we should allow the object, false if we should rate-limit
|
* @return true if we should allow the client, false if we should rate-limit
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean attempt(@NotNull T key) {
|
public boolean attempt(InetAddress address) {
|
||||||
|
Preconditions.checkNotNull(address, "address");
|
||||||
long expectedNewValue = System.nanoTime() + timeoutNanos;
|
long expectedNewValue = System.nanoTime() + timeoutNanos;
|
||||||
long last = expiringCache.get(key, (key1) -> expectedNewValue);
|
long last = expiringCache.get(address, (address1) -> expectedNewValue);
|
||||||
return expectedNewValue == last;
|
return expectedNewValue == last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,16 +17,16 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.util.ratelimit;
|
package com.velocitypowered.proxy.util.ratelimit;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import java.net.InetAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Ratelimiter} that does no rate-limiting.
|
* A {@link Ratelimiter} that does no rate-limiting.
|
||||||
*/
|
*/
|
||||||
enum NoopCacheRatelimiter implements Ratelimiter<Object> {
|
enum NoopCacheRatelimiter implements Ratelimiter {
|
||||||
INSTANCE;
|
INSTANCE;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean attempt(@NotNull Object key) {
|
public boolean attempt(InetAddress address) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,18 +17,18 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.util.ratelimit;
|
package com.velocitypowered.proxy.util.ratelimit;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import java.net.InetAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows rate limiting of objects.
|
* Allows rate limiting of clients.
|
||||||
*/
|
*/
|
||||||
public interface Ratelimiter<T> {
|
public interface Ratelimiter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to rate-limit the object.
|
* Determines whether or not to allow the connection.
|
||||||
*
|
*
|
||||||
* @param key the object to rate limit
|
* @param address the address to rate limit
|
||||||
* @return true if we should allow the object, false if we should rate-limit
|
* @return true if allowed, false if not
|
||||||
*/
|
*/
|
||||||
boolean attempt(@NotNull T key);
|
boolean attempt(InetAddress address);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,9 +28,8 @@ public final class Ratelimiters {
|
|||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
public static Ratelimiter createWithMilliseconds(long ms) {
|
||||||
public static <T> Ratelimiter<T> createWithMilliseconds(long ms) {
|
return ms <= 0 ? NoopCacheRatelimiter.INSTANCE : new CaffeineCacheRatelimiter(ms,
|
||||||
return ms <= 0 ? (Ratelimiter<T>) NoopCacheRatelimiter.INSTANCE : new CaffeineCacheRatelimiter(ms,
|
|
||||||
TimeUnit.MILLISECONDS);
|
TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,6 +62,4 @@ velocity.command.dump-server-error=An error occurred on the Velocity servers and
|
|||||||
velocity.command.dump-offline=Likely cause: Invalid system DNS settings or no internet connection
|
velocity.command.dump-offline=Likely cause: Invalid system DNS settings or no internet connection
|
||||||
velocity.command.send-usage=/send <player> <server>
|
velocity.command.send-usage=/send <player> <server>
|
||||||
# Kick
|
# Kick
|
||||||
velocity.kick.shutdown=Proxy shutting down.
|
velocity.kick.shutdown=Proxy shutting down.
|
||||||
velocity.kick.command-rate-limit=You are sending too many commands too quickly.
|
|
||||||
velocity.kick.tab-complete-rate-limit=You are sending too many tab complete requests too quickly.
|
|
||||||
@ -66,11 +66,6 @@ kick-existing-players = false
|
|||||||
# configuration is used if no servers could be contacted.
|
# configuration is used if no servers could be contacted.
|
||||||
ping-passthrough = "DISABLED"
|
ping-passthrough = "DISABLED"
|
||||||
|
|
||||||
# If enabled (default is false), then a sample of the online players on the proxy will be visible
|
|
||||||
# when hovering over the player count in the server list.
|
|
||||||
# This doesn't have any effect when ping passthrough is set to either "description" or "all".
|
|
||||||
sample-players-in-ping = false
|
|
||||||
|
|
||||||
# If not enabled (default is true) player IP addresses will be replaced by <ip address withheld> in logs
|
# If not enabled (default is true) player IP addresses will be replaced by <ip address withheld> in logs
|
||||||
enable-player-address-logging = true
|
enable-player-address-logging = true
|
||||||
|
|
||||||
@ -150,33 +145,6 @@ log-player-connections = true
|
|||||||
# Transfer packet (Minecraft 1.20.5) to be received.
|
# Transfer packet (Minecraft 1.20.5) to be received.
|
||||||
accepts-transfers = false
|
accepts-transfers = false
|
||||||
|
|
||||||
# Enables support for SO_REUSEPORT. This may help the proxy scale better on multicore systems
|
|
||||||
# with a lot of incoming connections, and provide better CPU utilization than the existing
|
|
||||||
# strategy of having a single thread accepting connections and distributing them to worker
|
|
||||||
# threads. Disabled by default. Requires Linux or macOS.
|
|
||||||
enable-reuse-port = false
|
|
||||||
|
|
||||||
# How fast (in milliseconds) are clients allowed to send commands after the last command
|
|
||||||
# By default this is 50ms (20 commands per second)
|
|
||||||
command-rate-limit = 50
|
|
||||||
|
|
||||||
# Should we forward commands to the backend upon being rate limited?
|
|
||||||
# This will forward the command to the server instead of processing it on the proxy.
|
|
||||||
# Since most server implementations have a rate limit, this will prevent the player
|
|
||||||
# from being able to send excessive commands to the server.
|
|
||||||
forward-commands-if-rate-limited = true
|
|
||||||
|
|
||||||
# How many commands are allowed to be sent after the rate limit is hit before the player is kicked?
|
|
||||||
# Setting this to 0 or lower will disable this feature.
|
|
||||||
kick-after-rate-limited-commands = 0
|
|
||||||
|
|
||||||
# How fast (in milliseconds) are clients allowed to send tab completions after the last tab completion
|
|
||||||
tab-complete-rate-limit = 10
|
|
||||||
|
|
||||||
# How many tab completions are allowed to be sent after the rate limit is hit before the player is kicked?
|
|
||||||
# Setting this to 0 or lower will disable this feature.
|
|
||||||
kick-after-rate-limited-tab-completes = 0
|
|
||||||
|
|
||||||
[query]
|
[query]
|
||||||
# Whether to enable responding to GameSpy 4 query responses or not.
|
# Whether to enable responding to GameSpy 4 query responses or not.
|
||||||
enabled = false
|
enabled = false
|
||||||
|
|||||||
@ -20,10 +20,8 @@ package com.velocitypowered.proxy.util;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
|
||||||
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
||||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class VelocityChannelRegistrarTest {
|
class VelocityChannelRegistrarTest {
|
||||||
@ -48,9 +46,9 @@ class VelocityChannelRegistrarTest {
|
|||||||
// Two channels cover the modern channel (velocity:test) and the legacy-mapped channel
|
// Two channels cover the modern channel (velocity:test) and the legacy-mapped channel
|
||||||
// (legacy:velocitytest). Make sure they're what we expect.
|
// (legacy:velocitytest). Make sure they're what we expect.
|
||||||
assertEquals(ImmutableSet.of(MODERN.getId(), SIMPLE_LEGACY_REMAPPED), registrar
|
assertEquals(ImmutableSet.of(MODERN.getId(), SIMPLE_LEGACY_REMAPPED), registrar
|
||||||
.getModernChannelIds().stream().map(ChannelIdentifier::getId).collect(Collectors.toSet()));
|
.getModernChannelIds());
|
||||||
assertEquals(ImmutableSet.of(SIMPLE_LEGACY.getId(), MODERN.getId()), registrar
|
assertEquals(ImmutableSet.of(SIMPLE_LEGACY.getId(), MODERN.getId()), registrar
|
||||||
.getLegacyChannelIds().stream().map(ChannelIdentifier::getId).collect(Collectors.toSet()));
|
.getLegacyChannelIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -59,10 +57,9 @@ class VelocityChannelRegistrarTest {
|
|||||||
registrar.register(SPECIAL_REMAP_LEGACY, MODERN_SPECIAL_REMAP);
|
registrar.register(SPECIAL_REMAP_LEGACY, MODERN_SPECIAL_REMAP);
|
||||||
|
|
||||||
// This one, just one channel for the modern case.
|
// This one, just one channel for the modern case.
|
||||||
assertEquals(ImmutableSet.of(MODERN_SPECIAL_REMAP.getId()),
|
assertEquals(ImmutableSet.of(MODERN_SPECIAL_REMAP.getId()), registrar.getModernChannelIds());
|
||||||
registrar.getModernChannelIds().stream().map(ChannelIdentifier::getId).collect(Collectors.toSet()));
|
|
||||||
assertEquals(ImmutableSet.of(MODERN_SPECIAL_REMAP.getId(), SPECIAL_REMAP_LEGACY.getId()),
|
assertEquals(ImmutableSet.of(MODERN_SPECIAL_REMAP.getId(), SPECIAL_REMAP_LEGACY.getId()),
|
||||||
registrar.getLegacyChannelIds().stream().map(ChannelIdentifier::getId).collect(Collectors.toSet()));
|
registrar.getLegacyChannelIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -71,9 +68,7 @@ class VelocityChannelRegistrarTest {
|
|||||||
registrar.register(MODERN, SIMPLE_LEGACY);
|
registrar.register(MODERN, SIMPLE_LEGACY);
|
||||||
registrar.unregister(SIMPLE_LEGACY);
|
registrar.unregister(SIMPLE_LEGACY);
|
||||||
|
|
||||||
assertEquals(ImmutableSet.of(MODERN.getId()),
|
assertEquals(ImmutableSet.of(MODERN.getId()), registrar.getModernChannelIds());
|
||||||
registrar.getModernChannelIds().stream().map(ChannelIdentifier::getId).collect(Collectors.toSet()));;
|
assertEquals(ImmutableSet.of(MODERN.getId()), registrar.getLegacyChannelIds());
|
||||||
assertEquals(ImmutableSet.of(MODERN.getId()),
|
|
||||||
registrar.getLegacyChannelIds().stream().map(ChannelIdentifier::getId).collect(Collectors.toSet()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user