Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-12-25 15:50:19 +01:00
Merge branch 'dev/1.1.0' into feature/bungeeguard
Dieser Commit ist enthalten in:
Commit
2cd9f081c3
@ -26,6 +26,7 @@ dependencies {
|
|||||||
compile "org.slf4j:slf4j-api:${slf4jVersion}"
|
compile "org.slf4j:slf4j-api:${slf4jVersion}"
|
||||||
compile 'com.google.inject:guice:4.2.2'
|
compile 'com.google.inject:guice:4.2.2'
|
||||||
compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
|
compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
|
||||||
|
compile 'com.mojang:brigadier:1.0.17'
|
||||||
|
|
||||||
compile "org.spongepowered:configurate-hocon:${configurateVersion}"
|
compile "org.spongepowered:configurate-hocon:${configurateVersion}"
|
||||||
compile "org.spongepowered:configurate-yaml:${configurateVersion}"
|
compile "org.spongepowered:configurate-yaml:${configurateVersion}"
|
||||||
@ -63,6 +64,7 @@ artifacts {
|
|||||||
javadoc {
|
javadoc {
|
||||||
options.encoding = 'UTF-8'
|
options.encoding = 'UTF-8'
|
||||||
options.charSet = 'UTF-8'
|
options.charSet = 'UTF-8'
|
||||||
|
options.source = '8'
|
||||||
options.links(
|
options.links(
|
||||||
'http://www.slf4j.org/apidocs/',
|
'http://www.slf4j.org/apidocs/',
|
||||||
'https://google.github.io/guava/releases/25.1-jre/api/docs/',
|
'https://google.github.io/guava/releases/25.1-jre/api/docs/',
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.velocitypowered.api.command;
|
package com.velocitypowered.api.command;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an interface to register a command executor with the proxy.
|
* Represents an interface to register a command executor with the proxy.
|
||||||
*/
|
*/
|
||||||
@ -34,11 +36,52 @@ public interface CommandManager {
|
|||||||
void unregister(String alias);
|
void unregister(String alias);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to execute a command from the specified {@code cmdLine}.
|
* Calls CommandExecuteEvent and attempts to execute a command using the specified {@code cmdLine}
|
||||||
|
* in a blocking fashion.
|
||||||
*
|
*
|
||||||
* @param source the command's source
|
* @param source the command's source
|
||||||
* @param cmdLine the command to run
|
* @param cmdLine the command to run
|
||||||
* @return true if the command was found and executed, false if it was not
|
* @return true if the command was found and executed, false if it was not
|
||||||
|
*
|
||||||
|
* @deprecated This method will block current thread during event call and command execution.
|
||||||
|
* Prefer {@link #executeAsync(CommandSource, String)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
boolean execute(CommandSource source, String cmdLine);
|
boolean execute(CommandSource source, String cmdLine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to execute a command using the specified {@code cmdLine} in a blocking fashion without
|
||||||
|
* calling CommandExecuteEvent.
|
||||||
|
*
|
||||||
|
* @param source the command's source
|
||||||
|
* @param cmdLine the command to run
|
||||||
|
* @return true if the command was found and executed, false if it was not
|
||||||
|
*
|
||||||
|
* @deprecated This method will block current thread during event and command execution.
|
||||||
|
* Prefer {@link #executeImmediatelyAsync(CommandSource, String)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
boolean executeImmediately(CommandSource source, String cmdLine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls CommandExecuteEvent and attempts to execute a command from the specified {@code cmdLine}
|
||||||
|
* async.
|
||||||
|
*
|
||||||
|
* @param source the command's source
|
||||||
|
* @param cmdLine the command to run
|
||||||
|
* @return A future that will be completed with the result of the command execution.
|
||||||
|
* Can be completed exceptionally if exception was thrown during execution.
|
||||||
|
*/
|
||||||
|
CompletableFuture<Boolean> executeAsync(CommandSource source, String cmdLine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to execute a command from the specified {@code cmdLine} async
|
||||||
|
* without calling CommandExecuteEvent.
|
||||||
|
*
|
||||||
|
* @param source the command's source
|
||||||
|
* @param cmdLine the command to run
|
||||||
|
* @return A future that will be completed with the result of the command execution.
|
||||||
|
* Can be completed exceptionally if exception was thrown during execution.
|
||||||
|
*/
|
||||||
|
CompletableFuture<Boolean> executeImmediatelyAsync(CommandSource source, String cmdLine);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
package com.velocitypowered.api.event.command;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
|
import com.velocitypowered.api.event.ResultedEvent;
|
||||||
|
import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is fired when someone executing command.
|
||||||
|
*/
|
||||||
|
public final class CommandExecuteEvent implements ResultedEvent<CommandResult> {
|
||||||
|
|
||||||
|
private final CommandSource commandSource;
|
||||||
|
private final String command;
|
||||||
|
private CommandResult result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a CommandExecuteEvent.
|
||||||
|
* @param commandSource the source executing the command
|
||||||
|
* @param command the command being executed without first slash
|
||||||
|
*/
|
||||||
|
public CommandExecuteEvent(CommandSource commandSource, String command) {
|
||||||
|
this.commandSource = Preconditions.checkNotNull(commandSource, "commandSource");
|
||||||
|
this.command = Preconditions.checkNotNull(command, "command");
|
||||||
|
this.result = CommandResult.allowed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandSource getCommandSource() {
|
||||||
|
return commandSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the original command being executed without first slash.
|
||||||
|
* @return the original command being executed
|
||||||
|
*/
|
||||||
|
public String getCommand() {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandResult getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResult(CommandResult result) {
|
||||||
|
this.result = Preconditions.checkNotNull(result, "result");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "CommandExecuteEvent{"
|
||||||
|
+ "commandSource=" + commandSource
|
||||||
|
+ ", command=" + command
|
||||||
|
+ ", result=" + result
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the result of the {@link CommandExecuteEvent}.
|
||||||
|
*/
|
||||||
|
public static final class CommandResult implements Result {
|
||||||
|
|
||||||
|
private static final CommandResult ALLOWED = new CommandResult(true, false,null);
|
||||||
|
private static final CommandResult DENIED = new CommandResult(false, false,null);
|
||||||
|
private static final CommandResult FORWARD_TO_SERVER = new CommandResult(false, true, null);
|
||||||
|
|
||||||
|
private @Nullable String command;
|
||||||
|
private final boolean status;
|
||||||
|
private final boolean forward;
|
||||||
|
|
||||||
|
private CommandResult(boolean status, boolean forward, @Nullable String command) {
|
||||||
|
this.status = status;
|
||||||
|
this.forward = forward;
|
||||||
|
this.command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getCommand() {
|
||||||
|
return Optional.ofNullable(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForwardToServer() {
|
||||||
|
return forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return status ? "allowed" : "denied";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the command to be sent, without modification.
|
||||||
|
* @return the allowed result
|
||||||
|
*/
|
||||||
|
public static CommandResult allowed() {
|
||||||
|
return ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents the command from being executed.
|
||||||
|
* @return the denied result
|
||||||
|
*/
|
||||||
|
public static CommandResult denied() {
|
||||||
|
return DENIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents the command from being executed, but forward command to server.
|
||||||
|
* @return the forward result
|
||||||
|
*/
|
||||||
|
public static CommandResult forwardToServer() {
|
||||||
|
return FORWARD_TO_SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents the command from being executed on proxy, but forward command to server.
|
||||||
|
* @param newCommand the command without first slash to use instead
|
||||||
|
* @return a result with a new command being forwarded to server
|
||||||
|
*/
|
||||||
|
public static CommandResult forwardToServer(@NonNull String newCommand) {
|
||||||
|
Preconditions.checkNotNull(newCommand, "newCommand");
|
||||||
|
return new CommandResult(false, true, newCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the command to be executed, but silently replaced old command with another.
|
||||||
|
* @param newCommand the command to use instead without first slash
|
||||||
|
* @return a result with a new command
|
||||||
|
*/
|
||||||
|
public static CommandResult command(@NonNull String newCommand) {
|
||||||
|
Preconditions.checkNotNull(newCommand, "newCommand");
|
||||||
|
return new CommandResult(true, false, newCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.velocitypowered.api.event.command;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
|
import com.mojang.brigadier.tree.RootCommandNode;
|
||||||
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows plugins to modify the packet indicating commands available on the server to a
|
||||||
|
* Minecraft 1.13+ client.
|
||||||
|
*/
|
||||||
|
@Beta
|
||||||
|
public class PlayerAvailableCommandsEvent {
|
||||||
|
|
||||||
|
private final Player player;
|
||||||
|
private final RootCommandNode<?> rootNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an available commands event.
|
||||||
|
* @param player the targeted player
|
||||||
|
* @param rootNode the Brigadier root node
|
||||||
|
*/
|
||||||
|
public PlayerAvailableCommandsEvent(Player player,
|
||||||
|
RootCommandNode<?> rootNode) {
|
||||||
|
this.player = checkNotNull(player, "player");
|
||||||
|
this.rootNode = checkNotNull(rootNode, "rootNode");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RootCommandNode<?> getRootNode() {
|
||||||
|
return rootNode;
|
||||||
|
}
|
||||||
|
}
|
@ -60,13 +60,11 @@ public final class GameProfileRequestEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the game profile to use for this connection. It is invalid to use this method on an
|
* Sets the game profile to use for this connection.
|
||||||
* online-mode connection.
|
|
||||||
*
|
*
|
||||||
* @param gameProfile the profile for this connection, {@code null} uses the original profile
|
* @param gameProfile the profile for this connection, {@code null} uses the original profile
|
||||||
*/
|
*/
|
||||||
public void setGameProfile(@Nullable GameProfile gameProfile) {
|
public void setGameProfile(@Nullable GameProfile gameProfile) {
|
||||||
Preconditions.checkState(!onlineMode, "Profiles can not be faked in online mode!");
|
|
||||||
this.gameProfile = gameProfile;
|
this.gameProfile = gameProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,10 @@ public final class PlayerChatEvent implements ResultedEvent<PlayerChatEvent.Chat
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<String> getMessage() {
|
||||||
|
return Optional.ofNullable(message);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAllowed() {
|
public boolean isAllowed() {
|
||||||
return status;
|
return status;
|
||||||
@ -96,10 +100,6 @@ public final class PlayerChatEvent implements ResultedEvent<PlayerChatEvent.Chat
|
|||||||
return DENIED;
|
return DENIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getMessage() {
|
|
||||||
return Optional.ofNullable(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows the message to be sent, but silently replaced with another.
|
* Allows the message to be sent, but silently replaced with another.
|
||||||
* @param message the message to use instead
|
* @param message the message to use instead
|
||||||
@ -110,6 +110,4 @@ public final class PlayerChatEvent implements ResultedEvent<PlayerChatEvent.Chat
|
|||||||
return new ChatResult(true, message);
|
return new ChatResult(true, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,15 @@ public interface ProxyConfig {
|
|||||||
*/
|
*/
|
||||||
boolean isOnlineMode();
|
boolean isOnlineMode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If client's ISP/AS sent from this proxy is different from the one from Mojang's
|
||||||
|
* authentication server, the player is kicked. This disallows some VPN and proxy
|
||||||
|
* connections but is a weak form of protection.
|
||||||
|
*
|
||||||
|
* @return whether to prevent client proxy connections by checking the IP with Mojang servers
|
||||||
|
*/
|
||||||
|
boolean shouldPreventClientProxyConnections();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a Map of all servers registered in <code>velocity.toml</code>. This method does
|
* Get a Map of all servers registered in <code>velocity.toml</code>. This method does
|
||||||
* <strong>not</strong> return all the servers currently in memory, although in most cases it
|
* <strong>not</strong> return all the servers currently in memory, although in most cases it
|
||||||
|
@ -24,7 +24,7 @@ allprojects {
|
|||||||
junitVersion = '5.3.0-M1'
|
junitVersion = '5.3.0-M1'
|
||||||
slf4jVersion = '1.7.25'
|
slf4jVersion = '1.7.25'
|
||||||
log4jVersion = '2.11.2'
|
log4jVersion = '2.11.2'
|
||||||
nettyVersion = '4.1.43.Final'
|
nettyVersion = '4.1.50.Final'
|
||||||
guavaVersion = '25.1-jre'
|
guavaVersion = '25.1-jre'
|
||||||
checkerFrameworkVersion = '2.7.0'
|
checkerFrameworkVersion = '2.7.0'
|
||||||
configurateVersion = '3.6'
|
configurateVersion = '3.6'
|
||||||
|
@ -65,7 +65,7 @@ dependencies {
|
|||||||
compile 'it.unimi.dsi:fastutil:8.2.3'
|
compile 'it.unimi.dsi:fastutil:8.2.3'
|
||||||
compile 'net.kyori:event-method-asm:3.0.0'
|
compile 'net.kyori:event-method-asm:3.0.0'
|
||||||
|
|
||||||
compile 'com.mojang:brigadier:1.0.15'
|
compile 'com.mojang:brigadier:1.0.17'
|
||||||
|
|
||||||
compile 'org.asynchttpclient:async-http-client:2.10.4'
|
compile 'org.asynchttpclient:async-http-client:2.10.4'
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -95,7 +95,7 @@ public class VelocityServer implements ProxyServer {
|
|||||||
private @MonotonicNonNull VelocityConfiguration configuration;
|
private @MonotonicNonNull VelocityConfiguration configuration;
|
||||||
private @MonotonicNonNull KeyPair serverKeyPair;
|
private @MonotonicNonNull KeyPair serverKeyPair;
|
||||||
private final ServerMap servers;
|
private final ServerMap servers;
|
||||||
private final VelocityCommandManager commandManager = new VelocityCommandManager();
|
private final VelocityCommandManager commandManager;
|
||||||
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
|
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
|
||||||
private boolean shutdown = false;
|
private boolean shutdown = false;
|
||||||
private final VelocityPluginManager pluginManager;
|
private final VelocityPluginManager pluginManager;
|
||||||
@ -111,6 +111,7 @@ public class VelocityServer implements ProxyServer {
|
|||||||
VelocityServer(final ProxyOptions options) {
|
VelocityServer(final ProxyOptions options) {
|
||||||
pluginManager = new VelocityPluginManager(this);
|
pluginManager = new VelocityPluginManager(this);
|
||||||
eventManager = new VelocityEventManager(pluginManager);
|
eventManager = new VelocityEventManager(pluginManager);
|
||||||
|
commandManager = new VelocityCommandManager(eventManager);
|
||||||
scheduler = new VelocityScheduler(pluginManager);
|
scheduler = new VelocityScheduler(pluginManager);
|
||||||
console = new VelocityConsole(this);
|
console = new VelocityConsole(this);
|
||||||
cm = new ConnectionManager(this);
|
cm = new ConnectionManager(this);
|
||||||
|
@ -7,10 +7,8 @@ import com.velocitypowered.api.permission.Tristate;
|
|||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import net.kyori.text.TextComponent;
|
import net.kyori.text.TextComponent;
|
||||||
import net.kyori.text.format.TextColor;
|
import net.kyori.text.format.TextColor;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
@ -12,8 +12,6 @@ import com.velocitypowered.api.proxy.ProxyServer;
|
|||||||
import com.velocitypowered.api.proxy.ServerConnection;
|
import com.velocitypowered.api.proxy.ServerConnection;
|
||||||
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;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -25,6 +23,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
|
|
||||||
public class ServerCommand implements Command {
|
public class ServerCommand implements Command {
|
||||||
|
|
||||||
|
public static final int MAX_SERVERS_TO_LIST = 50;
|
||||||
private final ProxyServer server;
|
private final ProxyServer server;
|
||||||
|
|
||||||
public ServerCommand(ProxyServer server) {
|
public ServerCommand(ProxyServer server) {
|
||||||
@ -61,10 +60,16 @@ public class ServerCommand implements Command {
|
|||||||
executor.sendMessage(of("You are currently connected to " + currentServer + ".",
|
executor.sendMessage(of("You are currently connected to " + currentServer + ".",
|
||||||
TextColor.YELLOW));
|
TextColor.YELLOW));
|
||||||
|
|
||||||
|
List<RegisteredServer> servers = BuiltinCommandUtil.sortedServerList(server);
|
||||||
|
if (servers.size() > MAX_SERVERS_TO_LIST) {
|
||||||
|
executor.sendMessage(of("Too many servers to list. Tab-complete to show all servers.",
|
||||||
|
TextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Assemble the list of servers as components
|
// Assemble the list of servers as components
|
||||||
TextComponent.Builder serverListBuilder = TextComponent.builder("Available servers: ")
|
TextComponent.Builder serverListBuilder = TextComponent.builder("Available servers: ")
|
||||||
.color(TextColor.YELLOW);
|
.color(TextColor.YELLOW);
|
||||||
List<RegisteredServer> servers = BuiltinCommandUtil.sortedServerList(server);
|
|
||||||
for (int i = 0; i < servers.size(); i++) {
|
for (int i = 0; i < servers.size(); i++) {
|
||||||
RegisteredServer rs = servers.get(i);
|
RegisteredServer rs = servers.get(i);
|
||||||
serverListBuilder.append(formatServerComponent(currentServer, rs));
|
serverListBuilder.append(formatServerComponent(currentServer, rs));
|
||||||
|
@ -3,8 +3,6 @@ package com.velocitypowered.proxy.command;
|
|||||||
import com.velocitypowered.api.command.Command;
|
import com.velocitypowered.api.command.Command;
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import net.kyori.text.TextComponent;
|
|
||||||
import net.kyori.text.format.TextColor;
|
|
||||||
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
|
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import com.velocitypowered.api.plugin.PluginDescription;
|
|||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import com.velocitypowered.api.util.ProxyVersion;
|
import com.velocitypowered.api.util.ProxyVersion;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -20,7 +19,6 @@ import java.util.stream.Collectors;
|
|||||||
import net.kyori.text.TextComponent;
|
import net.kyori.text.TextComponent;
|
||||||
import net.kyori.text.event.ClickEvent;
|
import net.kyori.text.event.ClickEvent;
|
||||||
import net.kyori.text.event.HoverEvent;
|
import net.kyori.text.event.HoverEvent;
|
||||||
import net.kyori.text.event.HoverEvent.Action;
|
|
||||||
import net.kyori.text.format.TextColor;
|
import net.kyori.text.format.TextColor;
|
||||||
import net.kyori.text.format.TextDecoration;
|
import net.kyori.text.format.TextDecoration;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
@ -166,7 +164,7 @@ public class VelocityCommand implements Command {
|
|||||||
.append(TextComponent.of(version.getVersion()).decoration(TextDecoration.BOLD, false))
|
.append(TextComponent.of(version.getVersion()).decoration(TextDecoration.BOLD, false))
|
||||||
.build();
|
.build();
|
||||||
TextComponent copyright = TextComponent
|
TextComponent copyright = TextComponent
|
||||||
.of("Copyright 2018-2019 " + version.getVendor() + ". " + version.getName()
|
.of("Copyright 2018-2020 " + version.getVendor() + ". " + version.getName()
|
||||||
+ " is freely licensed under the terms of the MIT License.");
|
+ " is freely licensed under the terms of the MIT License.");
|
||||||
source.sendMessage(velocity);
|
source.sendMessage(velocity);
|
||||||
source.sendMessage(copyright);
|
source.sendMessage(copyright);
|
||||||
|
@ -7,16 +7,25 @@ import com.velocitypowered.api.command.Command;
|
|||||||
import com.velocitypowered.api.command.CommandManager;
|
import com.velocitypowered.api.command.CommandManager;
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.command.RawCommand;
|
import com.velocitypowered.api.command.RawCommand;
|
||||||
|
import com.velocitypowered.api.event.command.CommandExecuteEvent;
|
||||||
|
import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult;
|
||||||
|
import com.velocitypowered.proxy.plugin.VelocityEventManager;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class VelocityCommandManager implements CommandManager {
|
public class VelocityCommandManager implements CommandManager {
|
||||||
|
|
||||||
private final Map<String, RawCommand> commands = new HashMap<>();
|
private final Map<String, RawCommand> commands = new HashMap<>();
|
||||||
|
private final VelocityEventManager eventManager;
|
||||||
|
|
||||||
|
public VelocityCommandManager(VelocityEventManager eventManager) {
|
||||||
|
this.eventManager = eventManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@ -47,9 +56,36 @@ public class VelocityCommandManager implements CommandManager {
|
|||||||
this.commands.remove(alias.toLowerCase(Locale.ENGLISH));
|
this.commands.remove(alias.toLowerCase(Locale.ENGLISH));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls CommandExecuteEvent.
|
||||||
|
* @param source the command's source
|
||||||
|
* @param cmd the command
|
||||||
|
* @return CompletableFuture of event
|
||||||
|
*/
|
||||||
|
public CompletableFuture<CommandExecuteEvent> callCommandEvent(CommandSource source, String cmd) {
|
||||||
|
Preconditions.checkNotNull(source, "source");
|
||||||
|
Preconditions.checkNotNull(cmd, "cmd");
|
||||||
|
return eventManager.fire(new CommandExecuteEvent(source, cmd));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(CommandSource source, String cmdLine) {
|
public boolean execute(CommandSource source, String cmdLine) {
|
||||||
Preconditions.checkNotNull(source, "invoker");
|
Preconditions.checkNotNull(source, "source");
|
||||||
|
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||||
|
|
||||||
|
CommandExecuteEvent event = callCommandEvent(source, cmdLine).join();
|
||||||
|
CommandResult commandResult = event.getResult();
|
||||||
|
if (commandResult.isForwardToServer() || !commandResult.isAllowed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cmdLine = commandResult.getCommand().orElse(event.getCommand());
|
||||||
|
|
||||||
|
return executeImmediately(source, cmdLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean executeImmediately(CommandSource source, String cmdLine) {
|
||||||
|
Preconditions.checkNotNull(source, "source");
|
||||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||||
|
|
||||||
String alias = cmdLine;
|
String alias = cmdLine;
|
||||||
@ -75,6 +111,40 @@ public class VelocityCommandManager implements CommandManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Boolean> executeAsync(CommandSource source, String cmdLine) {
|
||||||
|
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||||
|
callCommandEvent(source, cmdLine).thenAccept(event -> {
|
||||||
|
CommandResult commandResult = event.getResult();
|
||||||
|
if (commandResult.isForwardToServer() || !commandResult.isAllowed()) {
|
||||||
|
result.complete(false);
|
||||||
|
}
|
||||||
|
String command = commandResult.getCommand().orElse(event.getCommand());
|
||||||
|
try {
|
||||||
|
result.complete(executeImmediately(source, command));
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Boolean> executeImmediatelyAsync(CommandSource source, String cmdLine) {
|
||||||
|
Preconditions.checkNotNull(source, "source");
|
||||||
|
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||||
|
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||||
|
eventManager.getService().execute(() -> {
|
||||||
|
try {
|
||||||
|
result.complete(executeImmediately(source, cmdLine));
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasCommand(String command) {
|
public boolean hasCommand(String command) {
|
||||||
return commands.containsKey(command);
|
return commands.containsKey(command);
|
||||||
}
|
}
|
||||||
@ -167,7 +237,14 @@ public class VelocityCommandManager implements CommandManager {
|
|||||||
if (line.isEmpty()) {
|
if (line.isEmpty()) {
|
||||||
return new String[0];
|
return new String[0];
|
||||||
}
|
}
|
||||||
return line.trim().split(" ", -1);
|
|
||||||
|
String[] trimmed = line.trim().split(" ", -1);
|
||||||
|
if (line.endsWith(" ") && !line.trim().isEmpty()) {
|
||||||
|
// To work around a 1.13+ specific bug we have to inject a space at the end of the arguments
|
||||||
|
trimmed = Arrays.copyOf(trimmed, trimmed.length + 1);
|
||||||
|
trimmed[trimmed.length - 1] = "";
|
||||||
|
}
|
||||||
|
return trimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -54,6 +54,14 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi
|
|||||||
@ConfigKey("online-mode")
|
@ConfigKey("online-mode")
|
||||||
private boolean onlineMode = true;
|
private boolean onlineMode = true;
|
||||||
|
|
||||||
|
@Comment({
|
||||||
|
"If client's ISP/AS sent from this proxy is different from the one from Mojang's",
|
||||||
|
"authentication server, the player is kicked. This disallows some VPN and proxy",
|
||||||
|
"connections but is a weak form of protection."
|
||||||
|
})
|
||||||
|
@ConfigKey("prevent-client-proxy-connections")
|
||||||
|
private boolean preventClientProxyConnections = false;
|
||||||
|
|
||||||
@Comment({
|
@Comment({
|
||||||
"Should we forward IP addresses and other data to backend servers?",
|
"Should we forward IP addresses and other data to backend servers?",
|
||||||
"Available options:",
|
"Available options:",
|
||||||
@ -332,6 +340,11 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi
|
|||||||
return onlineMode;
|
return onlineMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldPreventClientProxyConnections() {
|
||||||
|
return preventClientProxyConnections;
|
||||||
|
}
|
||||||
|
|
||||||
public PlayerInfoForwarding getPlayerInfoForwardingMode() {
|
public PlayerInfoForwarding getPlayerInfoForwardingMode() {
|
||||||
return playerInfoForwardingMode;
|
return playerInfoForwardingMode;
|
||||||
}
|
}
|
||||||
@ -406,6 +419,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi
|
|||||||
return pingPassthrough;
|
return pingPassthrough;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBungeePluginChannelEnabled() {
|
||||||
|
return advanced.isBungeePluginMessageChannel();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(this)
|
return MoreObjects.toStringHelper(this)
|
||||||
@ -658,6 +675,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi
|
|||||||
@ConfigKey("tcp-fast-open")
|
@ConfigKey("tcp-fast-open")
|
||||||
private boolean tcpFastOpen = false;
|
private boolean tcpFastOpen = false;
|
||||||
|
|
||||||
|
@Comment("Enables BungeeCord plugin messaging channel support on Velocity.")
|
||||||
|
@ConfigKey("bungee-plugin-message-channel")
|
||||||
|
private boolean bungeePluginMessageChannel = true;
|
||||||
|
|
||||||
private Advanced() {
|
private Advanced() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,6 +691,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi
|
|||||||
this.readTimeout = toml.getLong("read-timeout", 30000L).intValue();
|
this.readTimeout = toml.getLong("read-timeout", 30000L).intValue();
|
||||||
this.proxyProtocol = toml.getBoolean("proxy-protocol", false);
|
this.proxyProtocol = toml.getBoolean("proxy-protocol", false);
|
||||||
this.tcpFastOpen = toml.getBoolean("tcp-fast-open", false);
|
this.tcpFastOpen = toml.getBoolean("tcp-fast-open", false);
|
||||||
|
this.bungeePluginMessageChannel = toml.getBoolean("bungee-plugin-message-channel", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,6 +723,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi
|
|||||||
return tcpFastOpen;
|
return tcpFastOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBungeePluginMessageChannel() {
|
||||||
|
return bungeePluginMessageChannel;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Advanced{"
|
return "Advanced{"
|
||||||
@ -711,6 +737,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi
|
|||||||
+ ", readTimeout=" + readTimeout
|
+ ", readTimeout=" + readTimeout
|
||||||
+ ", proxyProtocol=" + proxyProtocol
|
+ ", proxyProtocol=" + proxyProtocol
|
||||||
+ ", tcpFastOpen=" + tcpFastOpen
|
+ ", tcpFastOpen=" + tcpFastOpen
|
||||||
|
+ ", bungeePluginMessageChannel=" + bungeePluginMessageChannel
|
||||||
+ '}';
|
+ '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import com.mojang.brigadier.arguments.StringArgumentType;
|
|||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import com.velocitypowered.api.event.command.PlayerAvailableCommandsEvent;
|
||||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
@ -175,7 +176,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
.build();
|
.build();
|
||||||
commands.getRootNode().addChild(root);
|
commands.getRootNode().addChild(root);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
server.getEventManager().fire(
|
||||||
|
new PlayerAvailableCommandsEvent(serverConn.getPlayer(), commands.getRootNode()))
|
||||||
|
.thenAcceptAsync(event -> playerConnection.write(commands), playerConnection.eventLoop());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -15,6 +15,7 @@ import com.velocitypowered.proxy.protocol.util.ByteBufDataOutput;
|
|||||||
import com.velocitypowered.proxy.server.VelocityRegisteredServer;
|
import com.velocitypowered.proxy.server.VelocityRegisteredServer;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.StringJoiner;
|
import java.util.StringJoiner;
|
||||||
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
|
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
|
||||||
|
|
||||||
@ -43,8 +44,11 @@ class BungeeCordMessageResponder {
|
|||||||
String playerName = in.readUTF();
|
String playerName = in.readUTF();
|
||||||
String serverName = in.readUTF();
|
String serverName = in.readUTF();
|
||||||
|
|
||||||
proxy.getPlayer(playerName).flatMap(player -> proxy.getServer(serverName))
|
Optional<Player> referencedPlayer = proxy.getPlayer(playerName);
|
||||||
.ifPresent(server -> player.createConnectionRequest(server).fireAndForget());
|
Optional<RegisteredServer> referencedServer = proxy.getServer(serverName);
|
||||||
|
if (referencedPlayer.isPresent() && referencedServer.isPresent()) {
|
||||||
|
referencedPlayer.get().createConnectionRequest(referencedServer.get()).fireAndForget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processIp(ByteBufDataInput in) {
|
private void processIp(ByteBufDataInput in) {
|
||||||
@ -269,6 +273,10 @@ class BungeeCordMessageResponder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean process(PluginMessage message) {
|
boolean process(PluginMessage message) {
|
||||||
|
if (!proxy.getConfiguration().isBungeePluginChannelEnabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!MODERN_CHANNEL.getId().equals(message.getChannel()) && !LEGACY_CHANNEL.getId()
|
if (!MODERN_CHANNEL.getId().equals(message.getChannel()) && !LEGACY_CHANNEL.getId()
|
||||||
.equals(message.getChannel())) {
|
.equals(message.getChannel())) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -83,6 +83,13 @@ public class TransitionSessionHandler implements MinecraftSessionHandler {
|
|||||||
.fire(new ServerConnectedEvent(serverConn.getPlayer(), serverConn.getServer(),
|
.fire(new ServerConnectedEvent(serverConn.getPlayer(), serverConn.getServer(),
|
||||||
existingConnection != null ? existingConnection.getServer() : null))
|
existingConnection != null ? existingConnection.getServer() : null))
|
||||||
.whenCompleteAsync((x, error) -> {
|
.whenCompleteAsync((x, error) -> {
|
||||||
|
// Make sure we can still transition (player might have disconnected here).
|
||||||
|
if (!serverConn.isActive()) {
|
||||||
|
// Connection is obsolete.
|
||||||
|
serverConn.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Strap on the ClientPlaySessionHandler if required.
|
// Strap on the ClientPlaySessionHandler if required.
|
||||||
ClientPlaySessionHandler playHandler;
|
ClientPlaySessionHandler playHandler;
|
||||||
if (serverConn.getPlayer().getConnection().getSessionHandler()
|
if (serverConn.getPlayer().getConnection().getSessionHandler()
|
||||||
|
@ -2,15 +2,7 @@ package com.velocitypowered.proxy.connection.backend;
|
|||||||
|
|
||||||
import static com.velocitypowered.proxy.VelocityServer.GSON;
|
import static com.velocitypowered.proxy.VelocityServer.GSON;
|
||||||
import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN;
|
import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN;
|
||||||
import static com.velocitypowered.proxy.network.Connections.FLOW_HANDLER;
|
|
||||||
import static com.velocitypowered.proxy.network.Connections.FLUSH_CONSOLIDATION;
|
|
||||||
import static com.velocitypowered.proxy.network.Connections.FLUSH_CONSOLIDATION_AMOUNT;
|
|
||||||
import static com.velocitypowered.proxy.network.Connections.FRAME_DECODER;
|
|
||||||
import static com.velocitypowered.proxy.network.Connections.FRAME_ENCODER;
|
|
||||||
import static com.velocitypowered.proxy.network.Connections.HANDLER;
|
import static com.velocitypowered.proxy.network.Connections.HANDLER;
|
||||||
import static com.velocitypowered.proxy.network.Connections.MINECRAFT_DECODER;
|
|
||||||
import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER;
|
|
||||||
import static com.velocitypowered.proxy.network.Connections.READ_TIMEOUT;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@ -26,19 +18,13 @@ import com.velocitypowered.proxy.connection.MinecraftConnection;
|
|||||||
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
|
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
|
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
|
||||||
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.MinecraftEncoder;
|
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder;
|
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder;
|
|
||||||
import com.velocitypowered.proxy.protocol.packet.Handshake;
|
import com.velocitypowered.proxy.protocol.packet.Handshake;
|
||||||
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
||||||
import com.velocitypowered.proxy.protocol.packet.ServerLogin;
|
import com.velocitypowered.proxy.protocol.packet.ServerLogin;
|
||||||
import com.velocitypowered.proxy.server.VelocityRegisteredServer;
|
import com.velocitypowered.proxy.server.VelocityRegisteredServer;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.handler.flow.FlowControlHandler;
|
import io.netty.handler.flow.FlowControlHandler;
|
||||||
|
@ -4,6 +4,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
|
|||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
|
||||||
import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.constructChannelsPacket;
|
import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.constructChannelsPacket;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult;
|
||||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||||
import com.velocitypowered.api.event.player.PlayerChatEvent;
|
import com.velocitypowered.api.event.player.PlayerChatEvent;
|
||||||
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
||||||
@ -41,6 +42,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import net.kyori.text.TextComponent;
|
import net.kyori.text.TextComponent;
|
||||||
import net.kyori.text.format.TextColor;
|
import net.kyori.text.format.TextColor;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
@ -123,17 +125,18 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
String msg = packet.getMessage();
|
String msg = packet.getMessage();
|
||||||
if (msg.startsWith("/")) {
|
if (msg.startsWith("/")) {
|
||||||
try {
|
String originalCommand = msg.substring(1);
|
||||||
if (!server.getCommandManager().execute(player, msg.substring(1))) {
|
server.getCommandManager().callCommandEvent(player, msg.substring(1))
|
||||||
return false;
|
.thenComposeAsync(event -> processCommandExecuteResult(originalCommand,
|
||||||
}
|
event.getResult()))
|
||||||
} catch (Exception e) {
|
.exceptionally(e -> {
|
||||||
logger.info("Exception occurred while running command for {}", player.getUsername(),
|
logger.info("Exception occurred while running command for {}",
|
||||||
e);
|
player.getUsername(), e);
|
||||||
player.sendMessage(
|
player.sendMessage(
|
||||||
TextComponent.of("An error occurred while running this command.", TextColor.RED));
|
TextComponent.of("An error occurred while running this command.",
|
||||||
return true;
|
TextColor.RED));
|
||||||
}
|
return null;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
PlayerChatEvent event = new PlayerChatEvent(player, msg);
|
PlayerChatEvent event = new PlayerChatEvent(player, msg);
|
||||||
server.getEventManager().fire(event)
|
server.getEventManager().fire(event)
|
||||||
@ -365,12 +368,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
private boolean handleCommandTabComplete(TabCompleteRequest packet) {
|
private boolean handleCommandTabComplete(TabCompleteRequest packet) {
|
||||||
// In 1.13+, we need to do additional work for the richer suggestions available.
|
// In 1.13+, we need to do additional work for the richer suggestions available.
|
||||||
String command = packet.getCommand().substring(1);
|
String command = packet.getCommand().substring(1);
|
||||||
int spacePos = command.indexOf(' ');
|
int commandEndPosition = command.indexOf(' ');
|
||||||
if (spacePos == -1) {
|
if (commandEndPosition == -1) {
|
||||||
spacePos = command.length();
|
commandEndPosition = command.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
String commandLabel = command.substring(0, spacePos);
|
String commandLabel = command.substring(0, commandEndPosition);
|
||||||
if (!server.getCommandManager().hasCommand(commandLabel)) {
|
if (!server.getCommandManager().hasCommand(commandLabel)) {
|
||||||
if (player.getProtocolVersion().compareTo(MINECRAFT_1_13) < 0) {
|
if (player.getProtocolVersion().compareTo(MINECRAFT_1_13) < 0) {
|
||||||
// Outstanding tab completes are recorded for use with 1.12 clients and below to provide
|
// Outstanding tab completes are recorded for use with 1.12 clients and below to provide
|
||||||
@ -461,6 +464,27 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
}, player.getConnection().eventLoop());
|
}, player.getConnection().eventLoop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Void> processCommandExecuteResult(String originalCommand,
|
||||||
|
CommandResult result) {
|
||||||
|
if (result == CommandResult.denied()) {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
MinecraftConnection smc = player.ensureAndGetCurrentServer().ensureConnected();
|
||||||
|
String commandToRun = result.getCommand().orElse(originalCommand);
|
||||||
|
if (result.isForwardToServer()) {
|
||||||
|
return CompletableFuture.runAsync(() -> smc.write(Chat.createServerbound("/"
|
||||||
|
+ commandToRun)), smc.eventLoop());
|
||||||
|
} else {
|
||||||
|
return server.getCommandManager().executeImmediatelyAsync(player, commandToRun)
|
||||||
|
.thenAcceptAsync(hasRun -> {
|
||||||
|
if (!hasRun) {
|
||||||
|
smc.write(Chat.createServerbound("/" + commandToRun));
|
||||||
|
}
|
||||||
|
}, smc.eventLoop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immediately send any queued messages to the server.
|
* Immediately send any queued messages to the server.
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package com.velocitypowered.proxy.connection.client;
|
package com.velocitypowered.proxy.connection.client;
|
||||||
|
|
||||||
|
import static com.velocitypowered.proxy.connection.util.ConnectionRequestResults.plainResult;
|
||||||
|
import static java.util.concurrent.CompletableFuture.completedFuture;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||||
@ -33,7 +36,6 @@ import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
|
|||||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||||
import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants;
|
import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants;
|
||||||
import com.velocitypowered.proxy.connection.util.ConnectionMessages;
|
import com.velocitypowered.proxy.connection.util.ConnectionMessages;
|
||||||
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
|
|
||||||
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
|
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
import com.velocitypowered.proxy.protocol.packet.Chat;
|
import com.velocitypowered.proxy.protocol.packet.Chat;
|
||||||
@ -711,8 +713,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Optional<ConnectionRequestBuilder.Status> checkServer(RegisteredServer server) {
|
private Optional<ConnectionRequestBuilder.Status> checkServer(RegisteredServer server) {
|
||||||
Preconditions
|
Preconditions.checkArgument(server instanceof VelocityRegisteredServer,
|
||||||
.checkState(server instanceof VelocityRegisteredServer, "Not a valid Velocity server.");
|
"Not a valid Velocity server.");
|
||||||
if (connectionInFlight != null || (connectedServer != null
|
if (connectionInFlight != null || (connectedServer != null
|
||||||
&& !connectedServer.hasCompletedJoin())) {
|
&& !connectedServer.hasCompletedJoin())) {
|
||||||
return Optional.of(ConnectionRequestBuilder.Status.CONNECTION_IN_PROGRESS);
|
return Optional.of(ConnectionRequestBuilder.Status.CONNECTION_IN_PROGRESS);
|
||||||
@ -723,49 +725,58 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<Impl> internalConnect() {
|
private CompletableFuture<Optional<Status>> getInitialStatus() {
|
||||||
Optional<ConnectionRequestBuilder.Status> initialCheck = checkServer(toConnect);
|
return CompletableFuture.supplyAsync(() -> checkServer(toConnect), connection.eventLoop());
|
||||||
if (initialCheck.isPresent()) {
|
|
||||||
return CompletableFuture
|
|
||||||
.completedFuture(ConnectionRequestResults.plainResult(initialCheck.get(), toConnect));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, initiate the connection.
|
private CompletableFuture<Impl> internalConnect() {
|
||||||
ServerPreConnectEvent event = new ServerPreConnectEvent(ConnectedPlayer.this, toConnect);
|
return this.getInitialStatus()
|
||||||
|
.thenCompose(initialCheck -> {
|
||||||
|
if (initialCheck.isPresent()) {
|
||||||
|
return completedFuture(plainResult(initialCheck.get(), toConnect));
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerPreConnectEvent event = new ServerPreConnectEvent(ConnectedPlayer.this,
|
||||||
|
toConnect);
|
||||||
return server.getEventManager().fire(event)
|
return server.getEventManager().fire(event)
|
||||||
.thenCompose(newEvent -> {
|
.thenComposeAsync(newEvent -> {
|
||||||
Optional<RegisteredServer> connectTo = newEvent.getResult().getServer();
|
Optional<RegisteredServer> newDest = newEvent.getResult().getServer();
|
||||||
if (!connectTo.isPresent()) {
|
if (!newDest.isPresent()) {
|
||||||
return CompletableFuture.completedFuture(
|
return completedFuture(
|
||||||
ConnectionRequestResults
|
plainResult(ConnectionRequestBuilder.Status.CONNECTION_CANCELLED, toConnect)
|
||||||
.plainResult(ConnectionRequestBuilder.Status.CONNECTION_CANCELLED, toConnect)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisteredServer rs = connectTo.get();
|
RegisteredServer realDestination = newDest.get();
|
||||||
Optional<ConnectionRequestBuilder.Status> lastCheck = checkServer(rs);
|
Optional<ConnectionRequestBuilder.Status> check = checkServer(realDestination);
|
||||||
if (lastCheck.isPresent()) {
|
if (check.isPresent()) {
|
||||||
return CompletableFuture
|
return completedFuture(plainResult(check.get(), realDestination));
|
||||||
.completedFuture(ConnectionRequestResults.plainResult(lastCheck.get(), rs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VelocityRegisteredServer vrs = (VelocityRegisteredServer) rs;
|
VelocityRegisteredServer vrs = (VelocityRegisteredServer) realDestination;
|
||||||
VelocityServerConnection con = new VelocityServerConnection(vrs, ConnectedPlayer.this,
|
VelocityServerConnection con = new VelocityServerConnection(vrs,
|
||||||
server);
|
ConnectedPlayer.this, server);
|
||||||
connectionInFlight = con;
|
connectionInFlight = con;
|
||||||
return con.connect();
|
return con.connect().whenCompleteAsync((result, throwable) ->
|
||||||
|
this.cleanupIfRequired(con), connection.eventLoop());
|
||||||
|
}, connection.eventLoop());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cleanupIfRequired(VelocityServerConnection establishedConnection) {
|
||||||
|
if (establishedConnection == connectionInFlight) {
|
||||||
|
resetInFlightConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Result> connect() {
|
public CompletableFuture<Result> connect() {
|
||||||
return this.internalConnect()
|
return this.internalConnect()
|
||||||
.whenCompleteAsync((status, throwable) -> {
|
.whenCompleteAsync((status, throwable) -> {
|
||||||
if (status != null && !status.isSafe()) {
|
if (status != null && !status.isSuccessful()) {
|
||||||
// If it's not safe to continue the connection we need to shut it down.
|
if (!status.isSafe()) {
|
||||||
handleConnectionException(status.getAttemptedConnection(), throwable, true);
|
handleConnectionException(status.getAttemptedConnection(), throwable, false);
|
||||||
} else if ((status != null && !status.isSuccessful())) {
|
}
|
||||||
resetInFlightConnection();
|
|
||||||
}
|
}
|
||||||
}, connection.eventLoop())
|
}, connection.eventLoop())
|
||||||
.thenApply(x -> x);
|
.thenApply(x -> x);
|
||||||
|
@ -6,7 +6,6 @@ import static com.velocitypowered.proxy.VelocityServer.GSON;
|
|||||||
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
|
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
|
||||||
import static com.velocitypowered.proxy.util.EncryptionUtils.decryptRsa;
|
import static com.velocitypowered.proxy.util.EncryptionUtils.decryptRsa;
|
||||||
import static com.velocitypowered.proxy.util.EncryptionUtils.generateServerId;
|
import static com.velocitypowered.proxy.util.EncryptionUtils.generateServerId;
|
||||||
import static org.asynchttpclient.Dsl.asyncHttpClient;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.event.connection.LoginEvent;
|
import com.velocitypowered.api.event.connection.LoginEvent;
|
||||||
@ -21,7 +20,6 @@ import com.velocitypowered.api.util.GameProfile;
|
|||||||
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.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
import com.velocitypowered.proxy.protocol.packet.Disconnect;
|
import com.velocitypowered.proxy.protocol.packet.Disconnect;
|
||||||
import com.velocitypowered.proxy.protocol.packet.EncryptionRequest;
|
import com.velocitypowered.proxy.protocol.packet.EncryptionRequest;
|
||||||
@ -50,7 +48,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class);
|
private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class);
|
||||||
private static final String MOJANG_HASJOINED_URL =
|
private static final String MOJANG_HASJOINED_URL =
|
||||||
"https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s&ip=%s";
|
"https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s";
|
||||||
|
|
||||||
private final VelocityServer server;
|
private final VelocityServer server;
|
||||||
private final MinecraftConnection mcConnection;
|
private final MinecraftConnection mcConnection;
|
||||||
@ -96,8 +94,11 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
String playerIp = ((InetSocketAddress) mcConnection.getRemoteAddress()).getHostString();
|
String playerIp = ((InetSocketAddress) mcConnection.getRemoteAddress()).getHostString();
|
||||||
String url = String.format(MOJANG_HASJOINED_URL,
|
String url = String.format(MOJANG_HASJOINED_URL,
|
||||||
urlFormParameterEscaper().escape(login.getUsername()), serverId,
|
urlFormParameterEscaper().escape(login.getUsername()), serverId);
|
||||||
urlFormParameterEscaper().escape(playerIp));
|
|
||||||
|
if (server.getConfiguration().shouldPreventClientProxyConnections()) {
|
||||||
|
url += "&ip=" + urlFormParameterEscaper().escape(playerIp);
|
||||||
|
}
|
||||||
|
|
||||||
ListenableFuture<Response> hasJoinedResponse = server.getAsyncHttpClient().prepareGet(url)
|
ListenableFuture<Response> hasJoinedResponse = server.getAsyncHttpClient().prepareGet(url)
|
||||||
.execute();
|
.execute();
|
||||||
|
@ -22,7 +22,6 @@ import com.velocitypowered.proxy.server.VelocityRegisteredServer;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
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.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.velocitypowered.proxy.connection.util;
|
package com.velocitypowered.proxy.connection.util;
|
||||||
|
|
||||||
import com.velocitypowered.api.proxy.ConnectionRequestBuilder;
|
import com.velocitypowered.api.proxy.ConnectionRequestBuilder;
|
||||||
import com.velocitypowered.api.proxy.ConnectionRequestBuilder.Result;
|
|
||||||
import com.velocitypowered.api.proxy.ConnectionRequestBuilder.Status;
|
import com.velocitypowered.api.proxy.ConnectionRequestBuilder.Status;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.proxy.protocol.packet.Disconnect;
|
import com.velocitypowered.proxy.protocol.packet.Disconnect;
|
||||||
|
@ -18,7 +18,6 @@ import io.netty.channel.WriteBufferWaterMark;
|
|||||||
import io.netty.channel.epoll.EpollChannelOption;
|
import io.netty.channel.epoll.EpollChannelOption;
|
||||||
import io.netty.resolver.dns.DnsAddressResolverGroup;
|
import io.netty.resolver.dns.DnsAddressResolverGroup;
|
||||||
import io.netty.resolver.dns.DnsNameResolverBuilder;
|
import io.netty.resolver.dns.DnsNameResolverBuilder;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -15,9 +15,6 @@ public class Connections {
|
|||||||
public static final String MINECRAFT_DECODER = "minecraft-decoder";
|
public static final String MINECRAFT_DECODER = "minecraft-decoder";
|
||||||
public static final String MINECRAFT_ENCODER = "minecraft-encoder";
|
public static final String MINECRAFT_ENCODER = "minecraft-encoder";
|
||||||
public static final String READ_TIMEOUT = "read-timeout";
|
public static final String READ_TIMEOUT = "read-timeout";
|
||||||
public static final String FLUSH_CONSOLIDATION = "flush-consolidation";
|
|
||||||
|
|
||||||
public static final int FLUSH_CONSOLIDATION_AMOUNT = 10;
|
|
||||||
|
|
||||||
private Connections() {
|
private Connections() {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
|
@ -185,6 +185,10 @@ public class VelocityEventManager implements EventManager {
|
|||||||
fireEvent(new ProxyShutdownEvent());
|
fireEvent(new ProxyShutdownEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExecutorService getService() {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
private static class VelocityMethodScanner implements MethodScanner<Object> {
|
private static class VelocityMethodScanner implements MethodScanner<Object> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,7 +16,6 @@ import com.velocitypowered.api.plugin.meta.PluginDependency;
|
|||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
|
||||||
import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription;
|
|
||||||
import com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader;
|
import com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader;
|
||||||
import com.velocitypowered.proxy.plugin.util.PluginDependencyUtils;
|
import com.velocitypowered.proxy.plugin.util.PluginDependencyUtils;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -3,7 +3,6 @@ package com.velocitypowered.proxy.plugin.loader;
|
|||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package com.velocitypowered.proxy.protocol;
|
package com.velocitypowered.proxy.protocol;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import static com.velocitypowered.proxy.protocol.util.NettyPreconditions.checkFrame;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.util.GameProfile;
|
import com.velocitypowered.api.util.GameProfile;
|
||||||
|
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
||||||
|
import com.velocitypowered.proxy.util.except.QuietException;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
|
import io.netty.handler.codec.CorruptedFrameException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -16,6 +19,7 @@ import java.util.UUID;
|
|||||||
public enum ProtocolUtils {
|
public enum ProtocolUtils {
|
||||||
;
|
;
|
||||||
private static final int DEFAULT_MAX_STRING_SIZE = 65536; // 64KiB
|
private static final int DEFAULT_MAX_STRING_SIZE = 65536; // 64KiB
|
||||||
|
private static final QuietException BAD_VARINT_CACHED = new QuietException("Bad varint decoded");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a Minecraft-style VarInt from the specified {@code buf}.
|
* Reads a Minecraft-style VarInt from the specified {@code buf}.
|
||||||
@ -23,13 +27,32 @@ public enum ProtocolUtils {
|
|||||||
* @return the decoded VarInt
|
* @return the decoded VarInt
|
||||||
*/
|
*/
|
||||||
public static int readVarInt(ByteBuf buf) {
|
public static int readVarInt(ByteBuf buf) {
|
||||||
|
int read = readVarIntSafely(buf);
|
||||||
|
if (read == Integer.MIN_VALUE) {
|
||||||
|
throw MinecraftDecoder.DEBUG ? new CorruptedFrameException("Bad varint decoded")
|
||||||
|
: BAD_VARINT_CACHED;
|
||||||
|
}
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a Minecraft-style VarInt from the specified {@code buf}. The difference between this
|
||||||
|
* method and {@link #readVarInt(ByteBuf)} is that this function returns a sentinel value if the
|
||||||
|
* varint is invalid.
|
||||||
|
* @param buf the buffer to read from
|
||||||
|
* @return the decoded VarInt, or {@code Integer.MIN_VALUE} if the varint is invalid
|
||||||
|
*/
|
||||||
|
public static int readVarIntSafely(ByteBuf buf) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (!buf.isReadable()) {
|
||||||
|
return Integer.MIN_VALUE;
|
||||||
|
}
|
||||||
int k = buf.readByte();
|
int k = buf.readByte();
|
||||||
i |= (k & 0x7F) << j++ * 7;
|
i |= (k & 0x7F) << j++ * 7;
|
||||||
if (j > 5) {
|
if (j > 5) {
|
||||||
throw new RuntimeException("VarInt too big");
|
return Integer.MIN_VALUE;
|
||||||
}
|
}
|
||||||
if ((k & 0x80) != 128) {
|
if ((k & 0x80) != 128) {
|
||||||
break;
|
break;
|
||||||
@ -68,17 +91,21 @@ public enum ProtocolUtils {
|
|||||||
*/
|
*/
|
||||||
public static String readString(ByteBuf buf, int cap) {
|
public static String readString(ByteBuf buf, int cap) {
|
||||||
int length = readVarInt(buf);
|
int length = readVarInt(buf);
|
||||||
checkArgument(length >= 0, "Got a negative-length string (%s)", length);
|
return readString(buf, cap, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readString(ByteBuf buf, int cap, int length) {
|
||||||
|
checkFrame(length >= 0, "Got a negative-length string (%s)", length);
|
||||||
// `cap` is interpreted as a UTF-8 character length. To cover the full Unicode plane, we must
|
// `cap` is interpreted as a UTF-8 character length. To cover the full Unicode plane, we must
|
||||||
// consider the length of a UTF-8 character, which can be up to a 4 bytes. We do an initial
|
// consider the length of a UTF-8 character, which can be up to 4 bytes. We do an initial
|
||||||
// sanity check and then check again to make sure our optimistic guess was good.
|
// sanity check and then check again to make sure our optimistic guess was good.
|
||||||
checkArgument(length <= cap * 4, "Bad string size (got %s, maximum is %s)", length, cap);
|
checkFrame(length <= cap * 4, "Bad string size (got %s, maximum is %s)", length, cap);
|
||||||
checkState(buf.isReadable(length),
|
checkFrame(buf.isReadable(length),
|
||||||
"Trying to read a string that is too long (wanted %s, only have %s)", length,
|
"Trying to read a string that is too long (wanted %s, only have %s)", length,
|
||||||
buf.readableBytes());
|
buf.readableBytes());
|
||||||
String str = buf.toString(buf.readerIndex(), length, StandardCharsets.UTF_8);
|
String str = buf.toString(buf.readerIndex(), length, StandardCharsets.UTF_8);
|
||||||
buf.skipBytes(length);
|
buf.skipBytes(length);
|
||||||
checkState(str.length() <= cap, "Got a too-long string (got %s, max %s)",
|
checkFrame(str.length() <= cap, "Got a too-long string (got %s, max %s)",
|
||||||
str.length(), cap);
|
str.length(), cap);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
@ -107,9 +134,9 @@ public enum ProtocolUtils {
|
|||||||
*/
|
*/
|
||||||
public static byte[] readByteArray(ByteBuf buf, int cap) {
|
public static byte[] readByteArray(ByteBuf buf, int cap) {
|
||||||
int length = readVarInt(buf);
|
int length = readVarInt(buf);
|
||||||
checkArgument(length >= 0, "Got a negative-length array (%s)", length);
|
checkFrame(length >= 0, "Got a negative-length array (%s)", length);
|
||||||
checkArgument(length <= cap, "Bad array size (got %s, maximum is %s)", length, cap);
|
checkFrame(length <= cap, "Bad array size (got %s, maximum is %s)", length, cap);
|
||||||
checkState(buf.isReadable(length),
|
checkFrame(buf.isReadable(length),
|
||||||
"Trying to read an array that is too long (wanted %s, only have %s)", length,
|
"Trying to read an array that is too long (wanted %s, only have %s)", length,
|
||||||
buf.readableBytes());
|
buf.readableBytes());
|
||||||
byte[] array = new byte[length];
|
byte[] array = new byte[length];
|
||||||
@ -228,7 +255,7 @@ public enum ProtocolUtils {
|
|||||||
// No vanilla packet should give a 3 byte packet
|
// No vanilla packet should give a 3 byte packet
|
||||||
int len = readExtendedForgeShort(buf);
|
int len = readExtendedForgeShort(buf);
|
||||||
|
|
||||||
Preconditions.checkArgument(len <= (FORGE_MAX_ARRAY_LENGTH),
|
checkFrame(len <= (FORGE_MAX_ARRAY_LENGTH),
|
||||||
"Cannot receive array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, len);
|
"Cannot receive array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, len);
|
||||||
|
|
||||||
return buf.readRetainedSlice(len);
|
return buf.readRetainedSlice(len);
|
||||||
@ -243,12 +270,11 @@ public enum ProtocolUtils {
|
|||||||
*/
|
*/
|
||||||
public static void writeByteArray17(byte[] b, ByteBuf buf, boolean allowExtended) {
|
public static void writeByteArray17(byte[] b, ByteBuf buf, boolean allowExtended) {
|
||||||
if (allowExtended) {
|
if (allowExtended) {
|
||||||
Preconditions
|
checkFrame(b.length <= (FORGE_MAX_ARRAY_LENGTH),
|
||||||
.checkArgument(b.length <= (FORGE_MAX_ARRAY_LENGTH),
|
|
||||||
"Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH,
|
"Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH,
|
||||||
b.length);
|
b.length);
|
||||||
} else {
|
} else {
|
||||||
Preconditions.checkArgument(b.length <= Short.MAX_VALUE,
|
checkFrame(b.length <= Short.MAX_VALUE,
|
||||||
"Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.length);
|
"Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.length);
|
||||||
}
|
}
|
||||||
// Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for
|
// Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for
|
||||||
@ -268,12 +294,11 @@ public enum ProtocolUtils {
|
|||||||
*/
|
*/
|
||||||
public static void writeByteBuf17(ByteBuf b, ByteBuf buf, boolean allowExtended) {
|
public static void writeByteBuf17(ByteBuf b, ByteBuf buf, boolean allowExtended) {
|
||||||
if (allowExtended) {
|
if (allowExtended) {
|
||||||
Preconditions
|
checkFrame(b.readableBytes() <= (FORGE_MAX_ARRAY_LENGTH),
|
||||||
.checkArgument(b.readableBytes() <= (FORGE_MAX_ARRAY_LENGTH),
|
|
||||||
"Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH,
|
"Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH,
|
||||||
b.readableBytes());
|
b.readableBytes());
|
||||||
} else {
|
} else {
|
||||||
Preconditions.checkArgument(b.readableBytes() <= Short.MAX_VALUE,
|
checkFrame(b.readableBytes() <= Short.MAX_VALUE,
|
||||||
"Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.readableBytes());
|
"Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.readableBytes());
|
||||||
}
|
}
|
||||||
// Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for
|
// Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for
|
||||||
@ -326,21 +351,7 @@ public enum ProtocolUtils {
|
|||||||
* @return the decoded string
|
* @return the decoded string
|
||||||
*/
|
*/
|
||||||
public static String readStringWithoutLength(ByteBuf buf) {
|
public static String readStringWithoutLength(ByteBuf buf) {
|
||||||
int length = buf.readableBytes();
|
return readString(buf, DEFAULT_MAX_STRING_SIZE, buf.readableBytes());
|
||||||
int cap = DEFAULT_MAX_STRING_SIZE;
|
|
||||||
checkArgument(length >= 0, "Got a negative-length string (%s)", length);
|
|
||||||
// `cap` is interpreted as a UTF-8 character length. To cover the full Unicode plane, we must
|
|
||||||
// consider the length of a UTF-8 character, which can be up to a 4 bytes. We do an initial
|
|
||||||
// sanity check and then check again to make sure our optimistic guess was good.
|
|
||||||
checkArgument(length <= cap * 4, "Bad string size (got %s, maximum is %s)", length, cap);
|
|
||||||
checkState(buf.isReadable(length),
|
|
||||||
"Trying to read a string that is too long (wanted %s, only have %s)", length,
|
|
||||||
buf.readableBytes());
|
|
||||||
String str = buf.toString(buf.readerIndex(), length, StandardCharsets.UTF_8);
|
|
||||||
buf.skipBytes(length);
|
|
||||||
checkState(str.length() <= cap, "Got a too-long string (got %s, max %s)",
|
|
||||||
str.length(), cap);
|
|
||||||
return str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Direction {
|
public enum Direction {
|
||||||
|
@ -24,8 +24,6 @@ import java.util.Collections;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import net.kyori.text.serializer.plain.PlainComponentSerializer;
|
import net.kyori.text.serializer.plain.PlainComponentSerializer;
|
||||||
|
@ -14,7 +14,7 @@ import java.util.List;
|
|||||||
|
|
||||||
public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
|
public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||||
|
|
||||||
private static final boolean DEBUG = Boolean.getBoolean("velocity.packet-decode-logging");
|
public static final boolean DEBUG = Boolean.getBoolean("velocity.packet-decode-logging");
|
||||||
private static final QuietException DECODE_FAILED =
|
private static final QuietException DECODE_FAILED =
|
||||||
new QuietException("A packet did not decode successfully (invalid data). If you are a "
|
new QuietException("A packet did not decode successfully (invalid data). If you are a "
|
||||||
+ "developer, launch Velocity with -Dvelocity.packet-decode-logging=true to see more.");
|
+ "developer, launch Velocity with -Dvelocity.packet-decode-logging=true to see more.");
|
||||||
@ -30,8 +30,8 @@ public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
|
|||||||
*/
|
*/
|
||||||
public MinecraftDecoder(ProtocolUtils.Direction direction) {
|
public MinecraftDecoder(ProtocolUtils.Direction direction) {
|
||||||
this.direction = Preconditions.checkNotNull(direction, "direction");
|
this.direction = Preconditions.checkNotNull(direction, "direction");
|
||||||
this.registry = direction
|
this.registry = direction.getProtocolRegistry(StateRegistry.HANDSHAKE,
|
||||||
.getProtocolRegistry(StateRegistry.HANDSHAKE, ProtocolVersion.MINIMUM_VERSION);
|
ProtocolVersion.MINIMUM_VERSION);
|
||||||
this.state = StateRegistry.HANDSHAKE;
|
this.state = StateRegistry.HANDSHAKE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,9 +78,9 @@ public class TabCompleteRequest implements MinecraftPacket {
|
|||||||
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
||||||
if (version.compareTo(MINECRAFT_1_13) >= 0) {
|
if (version.compareTo(MINECRAFT_1_13) >= 0) {
|
||||||
this.transactionId = ProtocolUtils.readVarInt(buf);
|
this.transactionId = ProtocolUtils.readVarInt(buf);
|
||||||
this.command = ProtocolUtils.readString(buf);
|
this.command = ProtocolUtils.readString(buf, Chat.MAX_SERVERBOUND_MESSAGE_LENGTH);
|
||||||
} else {
|
} else {
|
||||||
this.command = ProtocolUtils.readString(buf);
|
this.command = ProtocolUtils.readString(buf, Chat.MAX_SERVERBOUND_MESSAGE_LENGTH);
|
||||||
if (version.compareTo(MINECRAFT_1_9) >= 0) {
|
if (version.compareTo(MINECRAFT_1_9) >= 0) {
|
||||||
this.assumeCommand = buf.readBoolean();
|
this.assumeCommand = buf.readBoolean();
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import static com.velocitypowered.proxy.protocol.packet.brigadier.DoubleArgument
|
|||||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.DummyVoidArgumentPropertySerializer.DUMMY;
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.DummyVoidArgumentPropertySerializer.DUMMY;
|
||||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.FloatArgumentPropertySerializer.FLOAT;
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.FloatArgumentPropertySerializer.FLOAT;
|
||||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.IntegerArgumentPropertySerializer.INTEGER;
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.IntegerArgumentPropertySerializer.INTEGER;
|
||||||
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.LongArgumentPropertySerializer.LONG;
|
||||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.StringArgumentPropertySerializer.STRING;
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.StringArgumentPropertySerializer.STRING;
|
||||||
|
|
||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
@ -11,6 +12,7 @@ import com.mojang.brigadier.arguments.BoolArgumentType;
|
|||||||
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
||||||
import com.mojang.brigadier.arguments.FloatArgumentType;
|
import com.mojang.brigadier.arguments.FloatArgumentType;
|
||||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||||
|
import com.mojang.brigadier.arguments.LongArgumentType;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
@ -90,6 +92,7 @@ public class ArgumentPropertyRegistry {
|
|||||||
register("brigadier:double", DoubleArgumentType.class, DOUBLE);
|
register("brigadier:double", DoubleArgumentType.class, DOUBLE);
|
||||||
register("brigadier:bool", BoolArgumentType.class,
|
register("brigadier:bool", BoolArgumentType.class,
|
||||||
VoidArgumentPropertySerializer.create(BoolArgumentType::bool));
|
VoidArgumentPropertySerializer.create(BoolArgumentType::bool));
|
||||||
|
register("brigadier:long", LongArgumentType.class, LONG);
|
||||||
|
|
||||||
// Minecraft argument types with extra properties
|
// Minecraft argument types with extra properties
|
||||||
dummy("minecraft:entity", ByteArgumentPropertySerializer.BYTE);
|
dummy("minecraft:entity", ByteArgumentPropertySerializer.BYTE);
|
||||||
|
@ -2,7 +2,6 @@ package com.velocitypowered.proxy.protocol.packet.brigadier;
|
|||||||
|
|
||||||
import com.mojang.brigadier.StringReader;
|
import com.mojang.brigadier.StringReader;
|
||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
class DummyProperty<T> implements ArgumentType<T> {
|
class DummyProperty<T> implements ArgumentType<T> {
|
||||||
@ -17,11 +16,6 @@ class DummyProperty<T> implements ArgumentType<T> {
|
|||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <S> T parse(StringReader reader) throws CommandSyntaxException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIdentifier() {
|
public String getIdentifier() {
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
@ -33,4 +27,9 @@ class DummyProperty<T> implements ArgumentType<T> {
|
|||||||
public @Nullable T getResult() {
|
public @Nullable T getResult() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T parse(StringReader reader) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||||
|
|
||||||
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.IntegerArgumentPropertySerializer.HAS_MAXIMUM;
|
||||||
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.IntegerArgumentPropertySerializer.HAS_MINIMUM;
|
||||||
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.IntegerArgumentPropertySerializer.getFlags;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.arguments.LongArgumentType;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
class LongArgumentPropertySerializer implements ArgumentPropertySerializer<LongArgumentType> {
|
||||||
|
|
||||||
|
static final LongArgumentPropertySerializer LONG = new LongArgumentPropertySerializer();
|
||||||
|
|
||||||
|
private LongArgumentPropertySerializer() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LongArgumentType deserialize(ByteBuf buf) {
|
||||||
|
byte flags = buf.readByte();
|
||||||
|
long minimum = (flags & HAS_MINIMUM) != 0 ? buf.readLong() : Long.MIN_VALUE;
|
||||||
|
long maximum = (flags & HAS_MAXIMUM) != 0 ? buf.readLong() : Long.MAX_VALUE;
|
||||||
|
return LongArgumentType.longArg(minimum, maximum);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(LongArgumentType object, ByteBuf buf) {
|
||||||
|
boolean hasMinimum = object.getMinimum() != Long.MIN_VALUE;
|
||||||
|
boolean hasMaximum = object.getMaximum() != Long.MAX_VALUE;
|
||||||
|
byte flag = getFlags(hasMinimum, hasMaximum);
|
||||||
|
|
||||||
|
buf.writeByte(flag);
|
||||||
|
if (hasMinimum) {
|
||||||
|
buf.writeLong(object.getMinimum());
|
||||||
|
}
|
||||||
|
if (hasMaximum) {
|
||||||
|
buf.writeLong(object.getMaximum());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,18 @@
|
|||||||
package com.velocitypowered.proxy.protocol.util;
|
package com.velocitypowered.proxy.protocol.util;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
||||||
|
import com.velocitypowered.proxy.util.except.QuietException;
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
import io.netty.handler.codec.CorruptedFrameException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends {@link com.google.common.base.Preconditions} for Netty's {@link CorruptedFrameException}.
|
* Extends {@link com.google.common.base.Preconditions} for Netty's {@link CorruptedFrameException}.
|
||||||
*/
|
*/
|
||||||
public class NettyPreconditions {
|
public class NettyPreconditions {
|
||||||
|
private static final QuietException BAD = new QuietException(
|
||||||
|
"Invalid packet received. Launch Velocity with -Dvelocity.packet-decode-logging=true "
|
||||||
|
+ "to see more.");
|
||||||
|
|
||||||
private NettyPreconditions() {
|
private NettyPreconditions() {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
@ -18,7 +24,7 @@ public class NettyPreconditions {
|
|||||||
*/
|
*/
|
||||||
public static void checkFrame(boolean b, String message) {
|
public static void checkFrame(boolean b, String message) {
|
||||||
if (!b) {
|
if (!b) {
|
||||||
throw new CorruptedFrameException(message);
|
throw MinecraftDecoder.DEBUG ? new CorruptedFrameException(message) : BAD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +38,11 @@ public class NettyPreconditions {
|
|||||||
*/
|
*/
|
||||||
public static void checkFrame(boolean b, String message, Object arg1) {
|
public static void checkFrame(boolean b, String message, Object arg1) {
|
||||||
if (!b) {
|
if (!b) {
|
||||||
|
if (MinecraftDecoder.DEBUG) {
|
||||||
throw new CorruptedFrameException(Strings.lenientFormat(message, arg1));
|
throw new CorruptedFrameException(Strings.lenientFormat(message, arg1));
|
||||||
|
} else {
|
||||||
|
throw BAD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +57,11 @@ public class NettyPreconditions {
|
|||||||
*/
|
*/
|
||||||
public static void checkFrame(boolean b, String message, Object arg1, Object arg2) {
|
public static void checkFrame(boolean b, String message, Object arg1, Object arg2) {
|
||||||
if (!b) {
|
if (!b) {
|
||||||
|
if (MinecraftDecoder.DEBUG) {
|
||||||
throw new CorruptedFrameException(Strings.lenientFormat(message, arg1, arg2));
|
throw new CorruptedFrameException(Strings.lenientFormat(message, arg1, arg2));
|
||||||
|
} else {
|
||||||
|
throw BAD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +75,11 @@ public class NettyPreconditions {
|
|||||||
*/
|
*/
|
||||||
public static void checkFrame(boolean b, String message, Object... args) {
|
public static void checkFrame(boolean b, String message, Object... args) {
|
||||||
if (!b) {
|
if (!b) {
|
||||||
|
if (MinecraftDecoder.DEBUG) {
|
||||||
throw new CorruptedFrameException(Strings.lenientFormat(message, args));
|
throw new CorruptedFrameException(Strings.lenientFormat(message, args));
|
||||||
|
} else {
|
||||||
|
throw BAD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import com.google.common.base.Preconditions;
|
|||||||
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.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.ServerConnection;
|
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||||
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;
|
||||||
@ -25,7 +24,6 @@ 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.MinecraftVarintFrameDecoder;
|
||||||
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder;
|
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder;
|
||||||
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
|
@ -7,7 +7,6 @@ import com.velocitypowered.api.util.GameProfile;
|
|||||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||||
import com.velocitypowered.proxy.protocol.packet.HeaderAndFooter;
|
import com.velocitypowered.proxy.protocol.packet.HeaderAndFooter;
|
||||||
import com.velocitypowered.proxy.protocol.packet.PlayerListItem;
|
import com.velocitypowered.proxy.protocol.packet.PlayerListItem;
|
||||||
import com.velocitypowered.proxy.protocol.packet.PlayerListItem.Item;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -12,8 +12,6 @@ import java.util.Optional;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import net.kyori.text.Component;
|
import net.kyori.text.Component;
|
||||||
import net.kyori.text.serializer.legacy.LegacyComponentSerializer;
|
|
||||||
import net.kyori.text.serializer.plain.PlainComponentSerializer;
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
public class VelocityTabListLegacy extends VelocityTabList {
|
public class VelocityTabListLegacy extends VelocityTabList {
|
||||||
|
@ -6,13 +6,11 @@ import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
|||||||
import com.velocitypowered.api.proxy.messages.ChannelRegistrar;
|
import com.velocitypowered.api.proxy.messages.ChannelRegistrar;
|
||||||
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.proxy.protocol.util.PluginMessageUtil;
|
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
public class VelocityChannelRegistrar implements ChannelRegistrar {
|
public class VelocityChannelRegistrar implements ChannelRegistrar {
|
||||||
|
@ -2,7 +2,6 @@ package com.velocitypowered.proxy.util.collect;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ForwardingSet;
|
import com.google.common.collect.ForwardingSet;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren