Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-12-23 23:00:35 +01:00
[WIP] Inject commands for Minecraft 1.13+
Dieser Commit ist enthalten in:
Ursprung
b6944bbec2
Commit
2cedb457ce
@ -33,9 +33,16 @@ allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
|
||||
// for kyoripowered dependencies
|
||||
maven {
|
||||
url 'https://oss.sonatype.org/content/groups/public/'
|
||||
}
|
||||
|
||||
// Brigadier
|
||||
maven {
|
||||
url "https://libraries.minecraft.net"
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
|
@ -47,6 +47,8 @@ dependencies {
|
||||
compile 'it.unimi.dsi:fastutil:8.2.1'
|
||||
compile 'net.kyori:event-method-asm:3.0.0'
|
||||
|
||||
compile 'com.mojang:brigadier:1.0.15'
|
||||
|
||||
testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
|
||||
testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.velocitypowered.proxy.command;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.velocitypowered.api.command.Command;
|
||||
import com.velocitypowered.api.command.CommandManager;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
@ -11,6 +12,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
public class VelocityCommandManager implements CommandManager {
|
||||
@ -68,6 +70,10 @@ public class VelocityCommandManager implements CommandManager {
|
||||
return commands.containsKey(command);
|
||||
}
|
||||
|
||||
public Set<String> getAllRegisteredCommands() {
|
||||
return ImmutableSet.copyOf(commands.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Offer suggestions to fill in the command.
|
||||
* @param source the source for the command
|
||||
@ -116,4 +122,31 @@ public class VelocityCommandManager implements CommandManager {
|
||||
"Unable to invoke suggestions for command " + alias + " for " + source, e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPermission(CommandSource source, String cmdLine) {
|
||||
Preconditions.checkNotNull(source, "source");
|
||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||
|
||||
String[] split = cmdLine.split(" ", -1);
|
||||
if (split.length == 0) {
|
||||
// No command available.
|
||||
return false;
|
||||
}
|
||||
|
||||
String alias = split[0];
|
||||
@SuppressWarnings("nullness")
|
||||
String[] actualArgs = Arrays.copyOfRange(split, 1, split.length);
|
||||
Command command = commands.get(alias.toLowerCase(Locale.ENGLISH));
|
||||
if (command == null) {
|
||||
// No such command.
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return command.hasPermission(source, actualArgs);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"Unable to invoke suggestions for command " + alias + " for " + source, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.velocitypowered.proxy.connection;
|
||||
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.AvailableCommands;
|
||||
import com.velocitypowered.proxy.protocol.packet.BossBar;
|
||||
import com.velocitypowered.proxy.protocol.packet.Chat;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientSettings;
|
||||
@ -67,6 +68,10 @@ public interface MinecraftSessionHandler {
|
||||
|
||||
}
|
||||
|
||||
default boolean handle(AvailableCommands commands) {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean handle(BossBar packet) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
package com.velocitypowered.proxy.connection.backend;
|
||||
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
@ -10,6 +14,8 @@ import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
|
||||
import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants;
|
||||
import com.velocitypowered.proxy.connection.util.ConnectionMessages;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.AvailableCommands;
|
||||
import com.velocitypowered.proxy.protocol.packet.AvailableCommands.ProtocolSuggestionProvider;
|
||||
import com.velocitypowered.proxy.protocol.packet.BossBar;
|
||||
import com.velocitypowered.proxy.protocol.packet.Disconnect;
|
||||
import com.velocitypowered.proxy.protocol.packet.JoinGame;
|
||||
@ -128,6 +134,25 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||
return false; //Forward packet to player
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(AvailableCommands commands) {
|
||||
// Inject commands from the proxy.
|
||||
for (String command : server.getCommandManager().getAllRegisteredCommands()) {
|
||||
if (!server.getCommandManager().hasPermission(serverConn.getPlayer(), command)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LiteralCommandNode<Object> root = LiteralArgumentBuilder.literal(command)
|
||||
.then(RequiredArgumentBuilder.argument("args", StringArgumentType.greedyString())
|
||||
.suggests(new ProtocolSuggestionProvider("minecraft:ask_server"))
|
||||
.build())
|
||||
.executes((ctx) -> 0)
|
||||
.build();
|
||||
commands.getRootNode().addChild(root);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleGeneric(MinecraftPacket packet) {
|
||||
serverConn.getPlayer().getConnection().write(packet);
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.velocitypowered.proxy.connection.client;
|
||||
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
|
||||
|
||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.event.player.PlayerChatEvent;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
@ -19,6 +21,7 @@ import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
||||
import com.velocitypowered.proxy.protocol.packet.Respawn;
|
||||
import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest;
|
||||
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse;
|
||||
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse.Offer;
|
||||
import com.velocitypowered.proxy.protocol.packet.TitlePacket;
|
||||
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||
import com.velocitypowered.proxy.util.ThrowableUtils;
|
||||
@ -133,23 +136,39 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
@Override
|
||||
public boolean handle(TabCompleteRequest packet) {
|
||||
// Record the request so that the outstanding request can be augmented later.
|
||||
if (!packet.isAssumeCommand() && packet.getCommand().startsWith("/")) {
|
||||
int spacePos = packet.getCommand().indexOf(' ');
|
||||
if (spacePos > 0) {
|
||||
String cmd = packet.getCommand().substring(1, spacePos);
|
||||
if (server.getCommandManager().hasCommand(cmd)) {
|
||||
List<String> suggestions = server.getCommandManager()
|
||||
.offerSuggestions(player, packet.getCommand().substring(1));
|
||||
if (!suggestions.isEmpty()) {
|
||||
TabCompleteResponse resp = new TabCompleteResponse();
|
||||
resp.getOffers().addAll(suggestions);
|
||||
player.getConnection().write(resp);
|
||||
return true;
|
||||
boolean is113 = player.getProtocolVersion().compareTo(MINECRAFT_1_13) >= 0;
|
||||
boolean isCommand = is113 || (!packet.isAssumeCommand() && packet.getCommand().startsWith("/"));
|
||||
|
||||
if (!isCommand) {
|
||||
// Outstanding tab completes are recorded for use with 1.12 clients and below to provide
|
||||
// tab list completion support for command names. In 1.13, Brigadier handles everything for
|
||||
// us.
|
||||
outstandingTabComplete = packet;
|
||||
return false;
|
||||
}
|
||||
|
||||
int spacePos = packet.getCommand().indexOf(' ');
|
||||
if (spacePos > 0) {
|
||||
String command = is113 ? packet.getCommand() : packet.getCommand().substring(1);
|
||||
String commandLabel = command.substring(0, spacePos);
|
||||
if (server.getCommandManager().hasCommand(commandLabel)) {
|
||||
List<String> suggestions = server.getCommandManager().offerSuggestions(player, command);
|
||||
if (!suggestions.isEmpty()) {
|
||||
List<Offer> offers = new ArrayList<>();
|
||||
for (String suggestion : suggestions) {
|
||||
offers.add(new Offer(suggestion, null));
|
||||
}
|
||||
TabCompleteResponse resp = new TabCompleteResponse();
|
||||
resp.setTransactionId(packet.getTransactionId());
|
||||
resp.setStart(spacePos);
|
||||
resp.setLength(command.length() - spacePos);
|
||||
resp.getOffers().addAll(offers);
|
||||
player.getConnection().write(resp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
outstandingTabComplete = packet;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -323,7 +342,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
// Tell the server about this client's plugin message channels.
|
||||
ProtocolVersion serverVersion = serverMc.getProtocolVersion();
|
||||
Collection<String> toRegister = new HashSet<>(knownChannels);
|
||||
if (serverVersion.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0) {
|
||||
if (serverVersion.compareTo(MINECRAFT_1_13) >= 0) {
|
||||
toRegister.addAll(server.getChannelRegistrar().getModernChannelIds());
|
||||
} else {
|
||||
toRegister.addAll(server.getChannelRegistrar().getIdsForLegacyConnections());
|
||||
@ -362,7 +381,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
&& outstandingTabComplete.getCommand().startsWith("/")) {
|
||||
String command = outstandingTabComplete.getCommand().substring(1);
|
||||
try {
|
||||
response.getOffers().addAll(server.getCommandManager().offerSuggestions(player, command));
|
||||
List<String> offers = server.getCommandManager().offerSuggestions(player, command);
|
||||
for (String offer : offers) {
|
||||
response.getOffers().add(new Offer(offer, null));
|
||||
}
|
||||
response.getOffers().sort(null);
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to provide tab list completions for {} for command '{}'",
|
||||
player.getUsername(),
|
||||
|
@ -109,6 +109,22 @@ public enum ProtocolUtils {
|
||||
buf.writeBytes(array);
|
||||
}
|
||||
|
||||
public static int[] readIntegerArray(ByteBuf buf) {
|
||||
int len = readVarInt(buf);
|
||||
int[] array = new int[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
array[i] = readVarInt(buf);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public static void writeIntegerArray(ByteBuf buf, int[] array) {
|
||||
writeVarInt(buf, array.length);
|
||||
for (int i : array) {
|
||||
writeVarInt(buf, i);
|
||||
}
|
||||
}
|
||||
|
||||
public static UUID readUuid(ByteBuf buf) {
|
||||
long msb = buf.readLong();
|
||||
long lsb = buf.readLong();
|
||||
|
@ -19,6 +19,7 @@ import static com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.protocol.packet.AvailableCommands;
|
||||
import com.velocitypowered.proxy.protocol.packet.BossBar;
|
||||
import com.velocitypowered.proxy.protocol.packet.Chat;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientSettings;
|
||||
@ -83,7 +84,8 @@ public enum StateRegistry {
|
||||
map(0x14, MINECRAFT_1_8, false),
|
||||
map(0x01, MINECRAFT_1_9, false),
|
||||
map(0x02, MINECRAFT_1_12, false),
|
||||
map(0x01, MINECRAFT_1_12_1, false));
|
||||
map(0x01, MINECRAFT_1_12_1, false),
|
||||
map(0x05, MINECRAFT_1_13, false));
|
||||
serverbound.register(Chat.class, Chat::new,
|
||||
map(0x01, MINECRAFT_1_8, false),
|
||||
map(0x02, MINECRAFT_1_9, false),
|
||||
@ -121,7 +123,10 @@ public enum StateRegistry {
|
||||
clientbound.register(TabCompleteResponse.class, TabCompleteResponse::new,
|
||||
map(0x3A, MINECRAFT_1_8, false),
|
||||
map(0x0E, MINECRAFT_1_9, false),
|
||||
map(0x0E, MINECRAFT_1_12, false));
|
||||
map(0x0E, MINECRAFT_1_12, false),
|
||||
map(0x10, MINECRAFT_1_13, false));
|
||||
clientbound.register(AvailableCommands.class, AvailableCommands::new,
|
||||
map(0x11, MINECRAFT_1_13, false));
|
||||
clientbound.register(PluginMessage.class, PluginMessage::new,
|
||||
map(0x3F, MINECRAFT_1_8, false),
|
||||
map(0x18, MINECRAFT_1_9, false),
|
||||
|
@ -0,0 +1,320 @@
|
||||
package com.velocitypowered.proxy.protocol.packet;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import com.mojang.brigadier.tree.RootCommandNode;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
|
||||
import com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentPropertyRegistry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class AvailableCommands implements MinecraftPacket {
|
||||
private static final byte NODE_TYPE_ROOT = 0x00;
|
||||
private static final byte NODE_TYPE_LITERAL = 0x01;
|
||||
private static final byte NODE_TYPE_ARGUMENT = 0x02;
|
||||
|
||||
private static final byte FLAG_NODE_TYPE = 0x03;
|
||||
private static final byte FLAG_EXECUTABLE = 0x04;
|
||||
private static final byte FLAG_IS_REDIRECT = 0x08;
|
||||
private static final byte FLAG_HAS_SUGGESTIONS = 0x10;
|
||||
|
||||
// Note: Velocity doesn't use Brigadier for command handling. This may change in Velocity 2.0.0.
|
||||
@MonotonicNonNull
|
||||
private RootCommandNode<Object> rootNode;
|
||||
|
||||
public RootCommandNode<Object> getRootNode() {
|
||||
if (rootNode == null) {
|
||||
throw new IllegalStateException("Packet not yet deserialized");
|
||||
}
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
|
||||
int commands = ProtocolUtils.readVarInt(buf);
|
||||
WireNode[] wireNodes = new WireNode[commands];
|
||||
for (int i = 0; i < commands; i++) {
|
||||
WireNode node = deserializeNode(buf, i);
|
||||
wireNodes[i] = node;
|
||||
}
|
||||
|
||||
// Iterate over the deserialized nodes and attempt to form a graph. We also resolve any cycles
|
||||
// that exist.
|
||||
Queue<WireNode> nodeQueue = new ArrayDeque<>(Arrays.asList(wireNodes));
|
||||
while (!nodeQueue.isEmpty()) {
|
||||
boolean cycling = false;
|
||||
|
||||
for (Iterator<WireNode> it = nodeQueue.iterator(); it.hasNext(); ) {
|
||||
WireNode node = it.next();
|
||||
if (node.toNode(wireNodes)) {
|
||||
cycling = true;
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (!cycling) {
|
||||
// Uh-oh. We can't cycle. This is bad.
|
||||
throw new IllegalStateException("Stopped cycling; the root node can't be built.");
|
||||
}
|
||||
}
|
||||
|
||||
int rootIdx = ProtocolUtils.readVarInt(buf);
|
||||
rootNode = (RootCommandNode<Object>) wireNodes[rootIdx].built;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
|
||||
// Assign all the children an index.
|
||||
Deque<CommandNode<Object>> childrenQueue = new ArrayDeque<>(ImmutableList.of(rootNode));
|
||||
Object2IntMap<CommandNode<Object>> idMappings = new Object2IntLinkedOpenHashMap<>();
|
||||
while (!childrenQueue.isEmpty()) {
|
||||
CommandNode<Object> child = childrenQueue.poll();
|
||||
if (!idMappings.containsKey(child)) {
|
||||
idMappings.put(child, idMappings.size());
|
||||
childrenQueue.addAll(child.getChildren());
|
||||
}
|
||||
}
|
||||
|
||||
// Now serialize the children
|
||||
ProtocolUtils.writeVarInt(buf, idMappings.size());
|
||||
for (CommandNode<Object> child : idMappings.keySet()) {
|
||||
serializeNode(child, buf, idMappings);
|
||||
}
|
||||
ProtocolUtils.writeVarInt(buf, idMappings.getInt(rootNode));
|
||||
}
|
||||
|
||||
private static void serializeNode(CommandNode<Object> node, ByteBuf buf,
|
||||
Object2IntMap<CommandNode<Object>> idMappings) {
|
||||
byte flags = 0;
|
||||
if (node.getRedirect() != null) {
|
||||
flags |= FLAG_IS_REDIRECT;
|
||||
}
|
||||
if (node.getCommand() != null) {
|
||||
flags |= FLAG_EXECUTABLE;
|
||||
}
|
||||
|
||||
if (node instanceof RootCommandNode<?>) {
|
||||
flags |= NODE_TYPE_ROOT;
|
||||
} else if (node instanceof LiteralCommandNode<?>) {
|
||||
flags |= NODE_TYPE_LITERAL;
|
||||
} else if (node instanceof ArgumentCommandNode<?, ?>) {
|
||||
flags |= NODE_TYPE_ARGUMENT;
|
||||
if (((ArgumentCommandNode) node).getCustomSuggestions() != null) {
|
||||
flags |= FLAG_HAS_SUGGESTIONS;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown node type " + node.getClass().getName());
|
||||
}
|
||||
|
||||
buf.writeByte(flags);
|
||||
ProtocolUtils.writeVarInt(buf, node.getChildren().size());
|
||||
for (CommandNode<Object> child : node.getChildren()) {
|
||||
ProtocolUtils.writeVarInt(buf, idMappings.getInt(child));
|
||||
}
|
||||
if (node.getRedirect() != null) {
|
||||
ProtocolUtils.writeVarInt(buf, idMappings.getInt(node.getRedirect()));
|
||||
}
|
||||
|
||||
if (node instanceof ArgumentCommandNode<?, ?>) {
|
||||
ProtocolUtils.writeString(buf, node.getName());
|
||||
ArgumentPropertyRegistry.serialize(buf, ((ArgumentCommandNode) node).getType());
|
||||
|
||||
if (((ArgumentCommandNode) node).getCustomSuggestions() != null) {
|
||||
// The unchecked cast is required, but it is not particularly relevant because we check for
|
||||
// a more specific type later. (Even then, we only pull out one field.)
|
||||
@SuppressWarnings("unchecked")
|
||||
SuggestionProvider<Object> provider = ((ArgumentCommandNode) node).getCustomSuggestions();
|
||||
|
||||
if (!(provider instanceof ProtocolSuggestionProvider)) {
|
||||
throw new IllegalArgumentException("Suggestion provider " + provider.getClass().getName()
|
||||
+ " is not valid.");
|
||||
}
|
||||
|
||||
ProtocolUtils.writeString(buf, ((ProtocolSuggestionProvider) provider).name);
|
||||
}
|
||||
} else if (node instanceof LiteralCommandNode<?>) {
|
||||
ProtocolUtils.writeString(buf, node.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(MinecraftSessionHandler handler) {
|
||||
return handler.handle(this);
|
||||
}
|
||||
|
||||
private static WireNode deserializeNode(ByteBuf buf, int idx) {
|
||||
byte flags = buf.readByte();
|
||||
int[] children = ProtocolUtils.readIntegerArray(buf);
|
||||
int redirectTo = -1;
|
||||
if ((flags & FLAG_IS_REDIRECT) > 0) {
|
||||
redirectTo = ProtocolUtils.readVarInt(buf);
|
||||
}
|
||||
|
||||
switch (flags & FLAG_NODE_TYPE) {
|
||||
case NODE_TYPE_ROOT:
|
||||
return new WireNode(idx, flags, children, redirectTo, null);
|
||||
case NODE_TYPE_LITERAL:
|
||||
return new WireNode(idx, flags, children, redirectTo, LiteralArgumentBuilder
|
||||
.literal(ProtocolUtils.readString(buf)));
|
||||
case NODE_TYPE_ARGUMENT:
|
||||
String name = ProtocolUtils.readString(buf);
|
||||
ArgumentType<?> argumentType = ArgumentPropertyRegistry.deserialize(buf);
|
||||
|
||||
RequiredArgumentBuilder<Object, ?> argumentBuilder = RequiredArgumentBuilder
|
||||
.argument(name, argumentType);
|
||||
if ((flags & FLAG_HAS_SUGGESTIONS) != 0) {
|
||||
argumentBuilder.suggests(new ProtocolSuggestionProvider(ProtocolUtils.readString(buf)));
|
||||
}
|
||||
|
||||
return new WireNode(idx, flags, children, redirectTo, argumentBuilder);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown node type " + (flags & FLAG_NODE_TYPE));
|
||||
}
|
||||
}
|
||||
|
||||
private static class WireNode {
|
||||
private final int idx;
|
||||
private final byte flags;
|
||||
private final int[] children;
|
||||
private final int redirectTo;
|
||||
@Nullable
|
||||
private final ArgumentBuilder<Object, ?> args;
|
||||
@MonotonicNonNull
|
||||
private CommandNode<Object> built;
|
||||
|
||||
private WireNode(int idx, byte flags, int[] children, int redirectTo,
|
||||
@Nullable ArgumentBuilder<Object, ?> args) {
|
||||
this.idx = idx;
|
||||
this.flags = flags;
|
||||
this.children = children;
|
||||
this.redirectTo = redirectTo;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public boolean toNode(WireNode[] wireNodes) {
|
||||
if (this.built == null) {
|
||||
// Ensure all children exist. Note that we delay checking if the node has been built yet;
|
||||
// that needs to come after this node is built.
|
||||
for (int child : children) {
|
||||
if (child >= wireNodes.length) {
|
||||
throw new IllegalStateException("Node points to non-existent index " + redirectTo);
|
||||
}
|
||||
}
|
||||
|
||||
int type = flags & FLAG_NODE_TYPE;
|
||||
if (type == NODE_TYPE_ROOT) {
|
||||
this.built = new RootCommandNode<>();
|
||||
} else {
|
||||
if (args == null) {
|
||||
throw new IllegalStateException("Non-root node without args builder!");
|
||||
}
|
||||
|
||||
// Add any redirects
|
||||
if (redirectTo != -1) {
|
||||
if (redirectTo >= wireNodes.length) {
|
||||
throw new IllegalStateException("Node points to non-existent index " + redirectTo);
|
||||
}
|
||||
|
||||
if (wireNodes[redirectTo].built != null) {
|
||||
args.redirect(wireNodes[redirectTo].built);
|
||||
} else {
|
||||
// Redirect node does not yet exist
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If executable, add a dummy command
|
||||
if ((flags & FLAG_EXECUTABLE) != 0) {
|
||||
args.executes((Command<Object>) context -> 0);
|
||||
}
|
||||
|
||||
this.built = args.build();
|
||||
}
|
||||
}
|
||||
|
||||
for (int child : children) {
|
||||
if (wireNodes[child].built == null) {
|
||||
// The child is not yet deserialized. The node can't be built now.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Associate children with nodes
|
||||
for (int child : children) {
|
||||
CommandNode<Object> childNode = wireNodes[child].built;
|
||||
if (!(childNode instanceof RootCommandNode)) {
|
||||
built.addChild(childNode);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this)
|
||||
.add("idx", idx)
|
||||
.add("flags", flags)
|
||||
.add("children", children)
|
||||
.add("redirectTo", redirectTo);
|
||||
|
||||
if (args != null) {
|
||||
if (args instanceof LiteralArgumentBuilder) {
|
||||
helper.add("argsLabel", ((LiteralArgumentBuilder) args).getLiteral());
|
||||
} else if (args instanceof RequiredArgumentBuilder) {
|
||||
helper.add("argsName", ((RequiredArgumentBuilder) args).getName());
|
||||
}
|
||||
}
|
||||
|
||||
return helper.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A placeholder {@link SuggestionProvider} used internally to preserve the suggestion provider
|
||||
* name.
|
||||
*/
|
||||
public static class ProtocolSuggestionProvider implements SuggestionProvider<Object> {
|
||||
|
||||
private final String name;
|
||||
|
||||
public ProtocolSuggestionProvider(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Suggestions> getSuggestions(CommandContext<Object> context,
|
||||
SuggestionsBuilder builder) throws CommandSyntaxException {
|
||||
return builder.buildFuture();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
package com.velocitypowered.proxy.protocol.packet;
|
||||
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
@ -10,6 +14,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
public class TabCompleteRequest implements MinecraftPacket {
|
||||
|
||||
private @Nullable String command;
|
||||
private int transactionId;
|
||||
private boolean assumeCommand;
|
||||
private boolean hasPosition;
|
||||
private long position;
|
||||
@ -49,25 +54,39 @@ public class TabCompleteRequest implements MinecraftPacket {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public int getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public void setTransactionId(int transactionId) {
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TabCompleteRequest{"
|
||||
+ "command='" + command + '\''
|
||||
+ ", assumeCommand=" + assumeCommand
|
||||
+ ", hasPosition=" + hasPosition
|
||||
+ ", position=" + position
|
||||
+ '}';
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("command", command)
|
||||
.add("transactionId", transactionId)
|
||||
.add("assumeCommand", assumeCommand)
|
||||
.add("hasPosition", hasPosition)
|
||||
.add("position", position)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
||||
this.command = ProtocolUtils.readString(buf);
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {
|
||||
this.assumeCommand = buf.readBoolean();
|
||||
}
|
||||
this.hasPosition = buf.readBoolean();
|
||||
if (hasPosition) {
|
||||
this.position = buf.readLong();
|
||||
if (version.compareTo(MINECRAFT_1_13) >= 0) {
|
||||
this.transactionId = ProtocolUtils.readVarInt(buf);
|
||||
this.command = ProtocolUtils.readString(buf);
|
||||
} else {
|
||||
this.command = ProtocolUtils.readString(buf);
|
||||
if (version.compareTo(MINECRAFT_1_9) >= 0) {
|
||||
this.assumeCommand = buf.readBoolean();
|
||||
}
|
||||
this.hasPosition = buf.readBoolean();
|
||||
if (hasPosition) {
|
||||
this.position = buf.readLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,13 +95,19 @@ public class TabCompleteRequest implements MinecraftPacket {
|
||||
if (command == null) {
|
||||
throw new IllegalStateException("Command is not specified");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, command);
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {
|
||||
buf.writeBoolean(assumeCommand);
|
||||
}
|
||||
buf.writeBoolean(hasPosition);
|
||||
if (hasPosition) {
|
||||
buf.writeLong(position);
|
||||
|
||||
if (version.compareTo(MINECRAFT_1_13) >= 0) {
|
||||
ProtocolUtils.writeVarInt(buf, transactionId);
|
||||
ProtocolUtils.writeString(buf, command);
|
||||
} else {
|
||||
ProtocolUtils.writeString(buf, command);
|
||||
if (version.compareTo(MINECRAFT_1_9) >= 0) {
|
||||
buf.writeBoolean(assumeCommand);
|
||||
}
|
||||
buf.writeBoolean(hasPosition);
|
||||
if (hasPosition) {
|
||||
buf.writeLong(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
package com.velocitypowered.proxy.protocol.packet;
|
||||
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
@ -7,35 +10,95 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class TabCompleteResponse implements MinecraftPacket {
|
||||
|
||||
private final List<String> offers = new ArrayList<>();
|
||||
private int transactionId;
|
||||
private int start;
|
||||
private int length;
|
||||
private final List<Offer> offers = new ArrayList<>();
|
||||
|
||||
public List<String> getOffers() {
|
||||
public int getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public void setTransactionId(int transactionId) {
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(int start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public List<Offer> getOffers() {
|
||||
return offers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TabCompleteResponse{"
|
||||
+ "offers=" + offers
|
||||
+ "transactionId=" + transactionId
|
||||
+ ", start=" + start
|
||||
+ ", length=" + length
|
||||
+ ", offers=" + offers
|
||||
+ '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
||||
int offersAvailable = ProtocolUtils.readVarInt(buf);
|
||||
for (int i = 0; i < offersAvailable; i++) {
|
||||
offers.add(ProtocolUtils.readString(buf));
|
||||
if (version.compareTo(MINECRAFT_1_13) >= 0) {
|
||||
this.transactionId = ProtocolUtils.readVarInt(buf);
|
||||
this.start = ProtocolUtils.readVarInt(buf);
|
||||
this.length = ProtocolUtils.readVarInt(buf);
|
||||
int offersAvailable = ProtocolUtils.readVarInt(buf);
|
||||
for (int i = 0; i < offersAvailable; i++) {
|
||||
String offer = ProtocolUtils.readString(buf);
|
||||
Component tooltip = buf.readBoolean() ? ComponentSerializers.JSON.deserialize(
|
||||
ProtocolUtils.readString(buf)) : null;
|
||||
offers.add(new Offer(offer, tooltip));
|
||||
}
|
||||
} else {
|
||||
int offersAvailable = ProtocolUtils.readVarInt(buf);
|
||||
for (int i = 0; i < offersAvailable; i++) {
|
||||
offers.add(new Offer(ProtocolUtils.readString(buf), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
||||
ProtocolUtils.writeVarInt(buf, offers.size());
|
||||
for (String offer : offers) {
|
||||
ProtocolUtils.writeString(buf, offer);
|
||||
if (version.compareTo(MINECRAFT_1_13) >= 0) {
|
||||
ProtocolUtils.writeVarInt(buf, this.transactionId);
|
||||
ProtocolUtils.writeVarInt(buf, this.start);
|
||||
ProtocolUtils.writeVarInt(buf, this.length);
|
||||
ProtocolUtils.writeVarInt(buf, offers.size());
|
||||
for (Offer offer : offers) {
|
||||
ProtocolUtils.writeString(buf, offer.text);
|
||||
buf.writeBoolean(offer.tooltip != null);
|
||||
if (offer.tooltip != null) {
|
||||
ProtocolUtils.writeString(buf, ComponentSerializers.JSON.serialize(offer.tooltip));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ProtocolUtils.writeVarInt(buf, offers.size());
|
||||
for (Offer offer : offers) {
|
||||
ProtocolUtils.writeString(buf, offer.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,4 +106,29 @@ public class TabCompleteResponse implements MinecraftPacket {
|
||||
public boolean handle(MinecraftSessionHandler handler) {
|
||||
return handler.handle(this);
|
||||
}
|
||||
|
||||
public static class Offer implements Comparable<Offer> {
|
||||
private final String text;
|
||||
@Nullable
|
||||
private final Component tooltip;
|
||||
|
||||
public Offer(String text,
|
||||
@Nullable Component tooltip) {
|
||||
this.text = text;
|
||||
this.tooltip = tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("text", text)
|
||||
.add("tooltip", tooltip)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Offer o) {
|
||||
return this.text.compareTo(o.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,122 @@
|
||||
package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||
|
||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.DoubleArgumentPropertySerializer.DOUBLE;
|
||||
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.IntegerArgumentPropertySerializer.INTEGER;
|
||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.StringArgumentPropertySerializer.STRING;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
||||
import com.mojang.brigadier.arguments.FloatArgumentType;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ArgumentPropertyRegistry {
|
||||
private ArgumentPropertyRegistry() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
private static final Map<String, ArgumentPropertySerializer<?>> byId = new HashMap<>();
|
||||
private static final Map<Class<? extends ArgumentType>,
|
||||
ArgumentPropertySerializer<?>> byClass = new HashMap<>();
|
||||
private static final Map<Class<? extends ArgumentType>, String> classToId = new HashMap<>();
|
||||
|
||||
private static <T extends ArgumentType<?>> void register(String identifier, Class<T> klazz,
|
||||
ArgumentPropertySerializer<T> serializer) {
|
||||
byId.put(identifier, serializer);
|
||||
byClass.put(klazz, serializer);
|
||||
classToId.put(klazz, identifier);
|
||||
}
|
||||
|
||||
private static <T> void dummy(String identifier, ArgumentPropertySerializer<T> serializer) {
|
||||
byId.put(identifier, serializer);
|
||||
}
|
||||
|
||||
public static ArgumentType<?> deserialize(ByteBuf buf) {
|
||||
String identifier = ProtocolUtils.readString(buf);
|
||||
ArgumentPropertySerializer<?> serializer = byId.get(identifier);
|
||||
if (serializer == null) {
|
||||
throw new IllegalArgumentException("Argument type identifier " + identifier + " unknown.");
|
||||
}
|
||||
Object result = serializer.deserialize(buf);
|
||||
|
||||
if (result instanceof ArgumentType) {
|
||||
return (ArgumentType<?>) result;
|
||||
} else {
|
||||
return new DummyProperty(identifier, serializer, result);
|
||||
}
|
||||
}
|
||||
|
||||
public static void serialize(ByteBuf buf, ArgumentType<?> type) {
|
||||
if (type instanceof DummyProperty) {
|
||||
DummyProperty property = (DummyProperty) type;
|
||||
ProtocolUtils.writeString(buf, property.getIdentifier());
|
||||
if (property.getResult() != null) {
|
||||
property.getSerializer().serialize(property.getResult(), buf);
|
||||
}
|
||||
} else {
|
||||
ArgumentPropertySerializer serializer = byClass.get(type.getClass());
|
||||
String id = classToId.get(type.getClass());
|
||||
if (serializer == null || id == null) {
|
||||
throw new IllegalArgumentException("Don't know how to serialize "
|
||||
+ type.getClass().getName());
|
||||
}
|
||||
ProtocolUtils.writeString(buf, id);
|
||||
serializer.serialize(type, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
// Base Brigadier argument types
|
||||
register("brigadier:string", StringArgumentType.class, STRING);
|
||||
register("brigadier:integer", IntegerArgumentType.class, INTEGER);
|
||||
register("brigadier:float", FloatArgumentType.class, FLOAT);
|
||||
register("brigadier:double", DoubleArgumentType.class, DOUBLE);
|
||||
register("brigadier:bool", BoolArgumentType.class,
|
||||
VoidArgumentPropertySerializer.create(BoolArgumentType::bool));
|
||||
|
||||
// Minecraft argument types with extra properties
|
||||
dummy("minecraft:entity", ByteArgumentPropertySerializer.BYTE);
|
||||
dummy("minecraft:score_holder", ByteArgumentPropertySerializer.BYTE);
|
||||
|
||||
// Minecraft argument types
|
||||
dummy("minecraft:game_profile", DUMMY);
|
||||
dummy("minecraft:block_pos", DUMMY);
|
||||
dummy("minecraft:column_pos", DUMMY);
|
||||
dummy("minecraft:vec3", DUMMY);
|
||||
dummy("minecraft:vec2", DUMMY);
|
||||
dummy("minecraft:block_state", DUMMY);
|
||||
dummy("minecraft:block_predicate", DUMMY);
|
||||
dummy("minecraft:item_stack", DUMMY);
|
||||
dummy("minecraft:item_predicate", DUMMY);
|
||||
dummy("minecraft:color", DUMMY);
|
||||
dummy("minecraft:component", DUMMY);
|
||||
dummy("minecraft:message", DUMMY);
|
||||
dummy("minecraft:nbt", DUMMY);
|
||||
dummy("minecraft:nbt_path", DUMMY);
|
||||
dummy("minecraft:objective", DUMMY);
|
||||
dummy("minecraft:objective_criteria", DUMMY);
|
||||
dummy("minecraft:operation", DUMMY);
|
||||
dummy("minecraft:particle", DUMMY);
|
||||
dummy("minecraft:rotation", DUMMY);
|
||||
dummy("minecraft:scoreboard_slot", DUMMY);
|
||||
dummy("minecraft:swizzle", DUMMY);
|
||||
dummy("minecraft:team", DUMMY);
|
||||
dummy("minecraft:item_slot", DUMMY);
|
||||
dummy("minecraft:resource_location", DUMMY);
|
||||
dummy("minecraft:mob_effect", DUMMY);
|
||||
dummy("minecraft:function", DUMMY);
|
||||
dummy("minecraft:entity_anchor", DUMMY);
|
||||
dummy("minecraft:item_enchantment", DUMMY);
|
||||
dummy("minecraft:entity_summon", DUMMY);
|
||||
dummy("minecraft:dimension", DUMMY);
|
||||
dummy("minecraft:int_range", DUMMY);
|
||||
dummy("minecraft:float_range", DUMMY);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public interface ArgumentPropertySerializer<T> {
|
||||
@Nullable T deserialize(ByteBuf buf);
|
||||
|
||||
void serialize(T object, ByteBuf buf);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
class ByteArgumentPropertySerializer implements ArgumentPropertySerializer<Byte> {
|
||||
|
||||
static final ByteArgumentPropertySerializer BYTE = new ByteArgumentPropertySerializer();
|
||||
|
||||
private ByteArgumentPropertySerializer() {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Byte deserialize(ByteBuf buf) {
|
||||
return buf.readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Byte object, ByteBuf buf) {
|
||||
buf.writeByte(object);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
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.DoubleArgumentType;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
class DoubleArgumentPropertySerializer implements ArgumentPropertySerializer<DoubleArgumentType> {
|
||||
|
||||
static final DoubleArgumentPropertySerializer DOUBLE = new DoubleArgumentPropertySerializer();
|
||||
|
||||
private DoubleArgumentPropertySerializer() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DoubleArgumentType deserialize(ByteBuf buf) {
|
||||
byte flags = buf.readByte();
|
||||
double minimum = (flags & HAS_MINIMUM) != 0 ? buf.readDouble() : Double.MIN_VALUE;
|
||||
double maximum = (flags & HAS_MAXIMUM) != 0 ? buf.readDouble() : Double.MAX_VALUE;
|
||||
return DoubleArgumentType.doubleArg(minimum, maximum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(DoubleArgumentType object, ByteBuf buf) {
|
||||
boolean hasMinimum = object.getMinimum() != Double.MIN_VALUE;
|
||||
boolean hasMaximum = object.getMaximum() != Double.MAX_VALUE;
|
||||
byte flag = getFlags(hasMinimum, hasMaximum);
|
||||
|
||||
buf.writeByte(flag);
|
||||
if (hasMinimum) {
|
||||
buf.writeDouble(object.getMinimum());
|
||||
}
|
||||
if (hasMaximum) {
|
||||
buf.writeDouble(object.getMaximum());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
class DummyProperty<T> implements ArgumentType<T> {
|
||||
|
||||
private final String identifier;
|
||||
private final ArgumentPropertySerializer<T> serializer;
|
||||
@Nullable
|
||||
private final T result;
|
||||
|
||||
DummyProperty(String identifier, ArgumentPropertySerializer<T> serializer, @Nullable T result) {
|
||||
this.identifier = identifier;
|
||||
this.serializer = serializer;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> T parse(StringReader reader) throws CommandSyntaxException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public ArgumentPropertySerializer<T> getSerializer() {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
public @Nullable T getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* An argument property serializer that will serialize and deserialize nothing.
|
||||
*/
|
||||
class DummyVoidArgumentPropertySerializer implements ArgumentPropertySerializer<Void> {
|
||||
|
||||
static final ArgumentPropertySerializer<Void> DUMMY =
|
||||
new DummyVoidArgumentPropertySerializer();
|
||||
|
||||
private DummyVoidArgumentPropertySerializer() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Void deserialize(ByteBuf buf) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Void object, ByteBuf buf) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
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.FloatArgumentType;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
class FloatArgumentPropertySerializer implements ArgumentPropertySerializer<FloatArgumentType> {
|
||||
|
||||
static FloatArgumentPropertySerializer FLOAT = new FloatArgumentPropertySerializer();
|
||||
|
||||
private FloatArgumentPropertySerializer() {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FloatArgumentType deserialize(ByteBuf buf) {
|
||||
byte flags = buf.readByte();
|
||||
float minimum = (flags & HAS_MINIMUM) != 0 ? buf.readFloat() : Float.MIN_VALUE;
|
||||
float maximum = (flags & HAS_MAXIMUM) != 0 ? buf.readFloat() : Float.MAX_VALUE;
|
||||
return FloatArgumentType.floatArg(minimum, maximum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(FloatArgumentType object, ByteBuf buf) {
|
||||
boolean hasMinimum = object.getMinimum() != Float.MIN_VALUE;
|
||||
boolean hasMaximum = object.getMaximum() != Float.MAX_VALUE;
|
||||
byte flag = getFlags(hasMinimum, hasMaximum);
|
||||
|
||||
buf.writeByte(flag);
|
||||
if (hasMinimum) {
|
||||
buf.writeFloat(object.getMinimum());
|
||||
}
|
||||
if (hasMaximum) {
|
||||
buf.writeFloat(object.getMaximum());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
class IntegerArgumentPropertySerializer implements ArgumentPropertySerializer<IntegerArgumentType> {
|
||||
|
||||
static final IntegerArgumentPropertySerializer INTEGER = new IntegerArgumentPropertySerializer();
|
||||
|
||||
static final byte HAS_MINIMUM = 0x01;
|
||||
static final byte HAS_MAXIMUM = 0x02;
|
||||
|
||||
private IntegerArgumentPropertySerializer() {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IntegerArgumentType deserialize(ByteBuf buf) {
|
||||
byte flags = buf.readByte();
|
||||
int minimum = (flags & HAS_MINIMUM) != 0 ? buf.readInt() : Integer.MIN_VALUE;
|
||||
int maximum = (flags & HAS_MAXIMUM) != 0 ? buf.readInt() : Integer.MAX_VALUE;
|
||||
return IntegerArgumentType.integer(minimum, maximum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(IntegerArgumentType object, ByteBuf buf) {
|
||||
boolean hasMinimum = object.getMinimum() != Integer.MIN_VALUE;
|
||||
boolean hasMaximum = object.getMaximum() != Integer.MAX_VALUE;
|
||||
byte flag = getFlags(hasMinimum, hasMaximum);
|
||||
|
||||
buf.writeByte(flag);
|
||||
if (hasMinimum) {
|
||||
buf.writeInt(object.getMinimum());
|
||||
}
|
||||
if (hasMaximum) {
|
||||
buf.writeInt(object.getMaximum());
|
||||
}
|
||||
}
|
||||
|
||||
static byte getFlags(boolean hasMinimum, boolean hasMaximum) {
|
||||
byte flags = 0;
|
||||
if (hasMinimum) {
|
||||
flags |= HAS_MINIMUM;
|
||||
}
|
||||
if (hasMaximum) {
|
||||
flags |= HAS_MAXIMUM;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Serializes properties for {@link StringArgumentType}.
|
||||
*/
|
||||
class StringArgumentPropertySerializer implements ArgumentPropertySerializer<StringArgumentType> {
|
||||
|
||||
public static final ArgumentPropertySerializer<StringArgumentType> STRING =
|
||||
new StringArgumentPropertySerializer();
|
||||
|
||||
private StringArgumentPropertySerializer() {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public StringArgumentType deserialize(ByteBuf buf) {
|
||||
int type = ProtocolUtils.readVarInt(buf);
|
||||
switch (type) {
|
||||
case 0:
|
||||
return StringArgumentType.word();
|
||||
case 1:
|
||||
return StringArgumentType.string();
|
||||
case 2:
|
||||
return StringArgumentType.greedyString();
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid string argument type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(StringArgumentType object, ByteBuf buf) {
|
||||
switch (object.getType()) {
|
||||
case SINGLE_WORD:
|
||||
ProtocolUtils.writeVarInt(buf, 0);
|
||||
break;
|
||||
case QUOTABLE_PHRASE:
|
||||
ProtocolUtils.writeVarInt(buf, 1);
|
||||
break;
|
||||
case GREEDY_PHRASE:
|
||||
ProtocolUtils.writeVarInt(buf, 2);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid string argument type " + object.getType());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.function.Supplier;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
class VoidArgumentPropertySerializer<T extends ArgumentType<?>>
|
||||
implements ArgumentPropertySerializer<T> {
|
||||
|
||||
private final Supplier<T> argumentSupplier;
|
||||
|
||||
public VoidArgumentPropertySerializer(Supplier<T> argumentSupplier) {
|
||||
this.argumentSupplier = argumentSupplier;
|
||||
}
|
||||
|
||||
public static <T extends ArgumentType<?>> ArgumentPropertySerializer<T> create(
|
||||
Supplier<T> supplier) {
|
||||
return new VoidArgumentPropertySerializer<T>(supplier);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T deserialize(ByteBuf buf) {
|
||||
return argumentSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(T object, ByteBuf buf) {
|
||||
|
||||
}
|
||||
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren