#2479: Allow injection of BungeeCord commands to 1.13 with inject_commands option
This commit is contained in:
parent
7793894621
commit
02a65e34cf
@ -11,6 +11,7 @@ import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -428,4 +429,14 @@ public class PluginManager
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmodifiable collection of all registered commands.
|
||||
*
|
||||
* @return commands
|
||||
*/
|
||||
public Collection<String> getCommands()
|
||||
{
|
||||
return Collections.unmodifiableCollection( commandMap.keySet() );
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ public class Bootstrap
|
||||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
if ( Float.parseFloat( System.getProperty( "java.class.version" ) ) < 51.0 )
|
||||
if ( Float.parseFloat( System.getProperty( "java.class.version" ) ) < 52.0 )
|
||||
{
|
||||
System.err.println( "*** ERROR *** BungeeCord requires Java 7 or above to function! Please download and install it!" );
|
||||
System.err.println( "*** ERROR *** BungeeCord requires Java 8 or above to function! Please download and install it!" );
|
||||
System.out.println( "You can check your Java version with the command: java -version" );
|
||||
return;
|
||||
}
|
||||
|
2
pom.xml
2
pom.xml
@ -133,7 +133,7 @@
|
||||
<configuration>
|
||||
<signature>
|
||||
<groupId>org.codehaus.mojo.signature</groupId>
|
||||
<artifactId>java17</artifactId>
|
||||
<artifactId>java18</artifactId>
|
||||
<version>1.0</version>
|
||||
</signature>
|
||||
</configuration>
|
||||
|
@ -19,6 +19,12 @@
|
||||
<description>Minimal implementation of the Minecraft protocol for use in BungeeCord</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>brigadier</artifactId>
|
||||
<version>1.0.16-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
|
@ -6,6 +6,7 @@ import net.md_5.bungee.protocol.packet.ClientSettings;
|
||||
import net.md_5.bungee.protocol.packet.ClientStatus;
|
||||
import net.md_5.bungee.protocol.packet.Login;
|
||||
import net.md_5.bungee.protocol.packet.Chat;
|
||||
import net.md_5.bungee.protocol.packet.Commands;
|
||||
import net.md_5.bungee.protocol.packet.EncryptionRequest;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem;
|
||||
@ -163,4 +164,8 @@ public abstract class AbstractPacketHandler
|
||||
public void handle(EntityStatus status) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void handle(Commands commands) throws Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,19 @@ public abstract class DefinedPacket
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int[] readVarIntArray(ByteBuf buf)
|
||||
{
|
||||
int len = readVarInt( buf );
|
||||
int[] ret = new int[ len ];
|
||||
|
||||
for ( int i = 0; i < len; i++ )
|
||||
{
|
||||
ret[i] = readVarInt( buf );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void writeStringArray(List<String> s, ByteBuf buf)
|
||||
{
|
||||
writeVarInt( s.size(), buf );
|
||||
|
@ -14,6 +14,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.protocol.packet.BossBar;
|
||||
import net.md_5.bungee.protocol.packet.Chat;
|
||||
import net.md_5.bungee.protocol.packet.ClientSettings;
|
||||
import net.md_5.bungee.protocol.packet.Commands;
|
||||
import net.md_5.bungee.protocol.packet.EncryptionRequest;
|
||||
import net.md_5.bungee.protocol.packet.EncryptionResponse;
|
||||
import net.md_5.bungee.protocol.packet.EntityStatus;
|
||||
@ -179,6 +180,13 @@ public enum Protocol
|
||||
map( ProtocolConstants.MINECRAFT_1_12, 0x1B ),
|
||||
map( ProtocolConstants.MINECRAFT_1_13, 0x1C )
|
||||
);
|
||||
if ( Boolean.getBoolean( "net.md-5.bungee.protocol.register_commands" ) )
|
||||
{
|
||||
TO_CLIENT.registerPacket(
|
||||
Commands.class,
|
||||
map( ProtocolConstants.MINECRAFT_1_13, 0x11 )
|
||||
);
|
||||
}
|
||||
|
||||
TO_SERVER.registerPacket(
|
||||
KeepAlive.class,
|
||||
|
@ -0,0 +1,638 @@
|
||||
package net.md_5.bungee.protocol.packet;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
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.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 io.netty.buffer.ByteBuf;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.md_5.bungee.protocol.AbstractPacketHandler;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class Commands extends DefinedPacket
|
||||
{
|
||||
|
||||
private static final int FLAG_TYPE = 0x3;
|
||||
private static final int FLAG_EXECUTABLE = 0x4;
|
||||
private static final int FLAG_REDIRECT = 0x8;
|
||||
private static final int FLAG_SUGGESTIONS = 0x10;
|
||||
//
|
||||
private static final int NODE_ROOT = 0;
|
||||
private static final int NODE_LITERAL = 1;
|
||||
private static final int NODE_ARGUMENT = 2;
|
||||
//
|
||||
private RootCommandNode root;
|
||||
|
||||
@Override
|
||||
public void read(ByteBuf buf)
|
||||
{
|
||||
int nodeCount = readVarInt( buf );
|
||||
NetworkNode[] nodes = new NetworkNode[ nodeCount ];
|
||||
Deque<NetworkNode> nodeQueue = new ArrayDeque<>( nodes.length );
|
||||
|
||||
for ( int i = 0; i < nodeCount; i++ )
|
||||
{
|
||||
byte flags = buf.readByte();
|
||||
int[] children = readVarIntArray( buf );
|
||||
int redirectNode = ( ( flags & FLAG_REDIRECT ) != 0 ) ? readVarInt( buf ) : 0;
|
||||
ArgumentBuilder argumentBuilder;
|
||||
|
||||
switch ( flags & FLAG_TYPE )
|
||||
{
|
||||
case NODE_ROOT:
|
||||
argumentBuilder = null;
|
||||
break;
|
||||
case NODE_LITERAL:
|
||||
argumentBuilder = LiteralArgumentBuilder.literal( readString( buf ) );
|
||||
break;
|
||||
case NODE_ARGUMENT:
|
||||
String name = readString( buf );
|
||||
String parser = readString( buf );
|
||||
|
||||
argumentBuilder = RequiredArgumentBuilder.argument( name, ArgumentRegistry.read( parser, buf ) );
|
||||
|
||||
if ( ( flags & FLAG_SUGGESTIONS ) != 0 )
|
||||
{
|
||||
String suggster = readString( buf );
|
||||
( (RequiredArgumentBuilder) argumentBuilder ).suggests( SuggestionRegistry.getProvider( suggster ) );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unhandled node type " + flags );
|
||||
}
|
||||
|
||||
NetworkNode node = new NetworkNode( argumentBuilder, flags, redirectNode, children );
|
||||
|
||||
nodes[i] = node;
|
||||
nodeQueue.add( node );
|
||||
}
|
||||
|
||||
boolean mustCycle;
|
||||
do
|
||||
{
|
||||
if ( nodeQueue.isEmpty() )
|
||||
{
|
||||
int rootIndex = readVarInt( buf );
|
||||
root = (RootCommandNode<?>) nodes[rootIndex].command;
|
||||
return;
|
||||
}
|
||||
|
||||
mustCycle = false;
|
||||
|
||||
for ( Iterator<NetworkNode> iter = nodeQueue.iterator(); iter.hasNext(); )
|
||||
{
|
||||
NetworkNode node = iter.next();
|
||||
if ( node.buildSelf( nodes ) )
|
||||
{
|
||||
iter.remove();
|
||||
mustCycle = true;
|
||||
}
|
||||
}
|
||||
} while ( mustCycle );
|
||||
|
||||
throw new IllegalStateException( "Did not finish building root node" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buf)
|
||||
{
|
||||
Map<CommandNode, Integer> indexMap = new LinkedHashMap<>();
|
||||
Deque<CommandNode> nodeQueue = new ArrayDeque<>();
|
||||
nodeQueue.add( root );
|
||||
|
||||
while ( !nodeQueue.isEmpty() )
|
||||
{
|
||||
CommandNode command = nodeQueue.pollFirst();
|
||||
|
||||
if ( !indexMap.containsKey( command ) )
|
||||
{
|
||||
// Index the new node
|
||||
int currentIndex = indexMap.size();
|
||||
indexMap.put( command, currentIndex );
|
||||
|
||||
// Queue children and redirect for processing
|
||||
nodeQueue.addAll( command.getChildren() );
|
||||
if ( command.getRedirect() != null )
|
||||
{
|
||||
nodeQueue.add( command.getRedirect() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write out size
|
||||
writeVarInt( indexMap.size(), buf );
|
||||
|
||||
int currentIndex = 0;
|
||||
for ( Map.Entry<CommandNode, Integer> entry : indexMap.entrySet() )
|
||||
{
|
||||
// Using a LinkedHashMap, but sanity check this assumption
|
||||
Preconditions.checkState( entry.getValue() == currentIndex++, "Iteration out of order!" );
|
||||
|
||||
CommandNode node = entry.getKey();
|
||||
byte flags = 0;
|
||||
|
||||
if ( node.getRedirect() != null )
|
||||
{
|
||||
flags |= FLAG_REDIRECT;
|
||||
}
|
||||
if ( node.getCommand() != null )
|
||||
{
|
||||
flags |= FLAG_EXECUTABLE;
|
||||
}
|
||||
|
||||
if ( node instanceof RootCommandNode )
|
||||
{
|
||||
flags |= NODE_ROOT;
|
||||
} else if ( node instanceof LiteralCommandNode )
|
||||
{
|
||||
flags |= NODE_LITERAL;
|
||||
} else if ( node instanceof ArgumentCommandNode )
|
||||
{
|
||||
flags |= NODE_ARGUMENT;
|
||||
if ( ( (ArgumentCommandNode) node ).getCustomSuggestions() != null )
|
||||
{
|
||||
flags |= FLAG_SUGGESTIONS;
|
||||
}
|
||||
} else
|
||||
{
|
||||
throw new IllegalArgumentException( "Unhandled node type " + node );
|
||||
}
|
||||
|
||||
buf.writeByte( flags );
|
||||
|
||||
writeVarInt( node.getChildren().size(), buf );
|
||||
for ( CommandNode child : (Collection<CommandNode>) node.getChildren() )
|
||||
{
|
||||
writeVarInt( indexMap.get( child ), buf );
|
||||
}
|
||||
if ( node.getRedirect() != null )
|
||||
{
|
||||
writeVarInt( indexMap.get( node.getRedirect() ), buf );
|
||||
}
|
||||
|
||||
if ( node instanceof LiteralCommandNode )
|
||||
{
|
||||
writeString( ( (LiteralCommandNode) node ).getLiteral(), buf );
|
||||
} else if ( node instanceof ArgumentCommandNode )
|
||||
{
|
||||
ArgumentCommandNode argumentNode = (ArgumentCommandNode) node;
|
||||
|
||||
writeString( argumentNode.getName(), buf );
|
||||
ArgumentRegistry.write( argumentNode.getType(), buf );
|
||||
|
||||
if ( argumentNode.getCustomSuggestions() != null )
|
||||
{
|
||||
writeString( SuggestionRegistry.getKey( argumentNode.getCustomSuggestions() ), buf );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get, check, and write the root index (should be first)
|
||||
int rootIndex = indexMap.get( root );
|
||||
Preconditions.checkState( rootIndex == 0, "How did root not land up at index 0?!?" );
|
||||
writeVarInt( rootIndex, buf );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(AbstractPacketHandler handler) throws Exception
|
||||
{
|
||||
handler.handle( this );
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class NetworkNode
|
||||
{
|
||||
|
||||
private final ArgumentBuilder argumentBuilder;
|
||||
private final byte flags;
|
||||
private final int redirectNode;
|
||||
private final int[] children;
|
||||
private CommandNode command;
|
||||
|
||||
private boolean buildSelf(NetworkNode[] otherNodes)
|
||||
{
|
||||
// First cycle
|
||||
if ( command == null )
|
||||
{
|
||||
// Root node is merely the root
|
||||
if ( argumentBuilder == null )
|
||||
{
|
||||
command = new RootCommandNode();
|
||||
} else
|
||||
{
|
||||
// Add the redirect
|
||||
if ( ( flags & FLAG_REDIRECT ) != 0 )
|
||||
{
|
||||
if ( otherNodes[redirectNode].command == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
argumentBuilder.redirect( otherNodes[redirectNode].command );
|
||||
}
|
||||
|
||||
// Add dummy executable
|
||||
if ( ( flags & FLAG_EXECUTABLE ) != 0 )
|
||||
{
|
||||
argumentBuilder.executes( new Command()
|
||||
{
|
||||
@Override
|
||||
public int run(CommandContext context) throws CommandSyntaxException
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
// Build our self command
|
||||
command = argumentBuilder.build();
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we have processed all children thus far
|
||||
for ( int childIndex : children )
|
||||
{
|
||||
if ( otherNodes[childIndex].command == null )
|
||||
{
|
||||
// If not, we have to do another cycle
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for ( int childIndex : children )
|
||||
{
|
||||
CommandNode<?> child = otherNodes[childIndex].command;
|
||||
Preconditions.checkArgument( !( child instanceof RootCommandNode ), "Cannot have RootCommandNode as child" );
|
||||
|
||||
command.addChild( child );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class ArgumentRegistry
|
||||
{
|
||||
|
||||
private static final Map<String, ArgumentSerializer> PROVIDERS = new HashMap<>();
|
||||
private static final Map<Class<?>, ProperArgumentSerializer<?>> PROPER_PROVIDERS = new HashMap<>();
|
||||
//
|
||||
private static final ArgumentSerializer<Void> VOID = new ArgumentSerializer<Void>()
|
||||
{
|
||||
@Override
|
||||
protected Void read(ByteBuf buf)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(ByteBuf buf, Void t)
|
||||
{
|
||||
}
|
||||
};
|
||||
private static final ArgumentSerializer<Boolean> BOOLEAN = new ArgumentSerializer<Boolean>()
|
||||
{
|
||||
@Override
|
||||
protected Boolean read(ByteBuf buf)
|
||||
{
|
||||
return buf.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(ByteBuf buf, Boolean t)
|
||||
{
|
||||
buf.writeBoolean( t );
|
||||
}
|
||||
};
|
||||
private static final ArgumentSerializer<Byte> BYTE = new ArgumentSerializer<Byte>()
|
||||
{
|
||||
@Override
|
||||
protected Byte read(ByteBuf buf)
|
||||
{
|
||||
return buf.readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(ByteBuf buf, Byte t)
|
||||
{
|
||||
buf.writeByte( t );
|
||||
}
|
||||
};
|
||||
private static final ArgumentSerializer<FloatArgumentType> FLOAT = new ArgumentSerializer<FloatArgumentType>()
|
||||
{
|
||||
@Override
|
||||
protected FloatArgumentType read(ByteBuf buf)
|
||||
{
|
||||
byte flags = buf.readByte();
|
||||
float min = ( flags & 0x1 ) != 0 ? buf.readFloat() : -Float.MAX_VALUE;
|
||||
float max = ( flags & 0x2 ) != 0 ? buf.readFloat() : -Float.MAX_VALUE;
|
||||
|
||||
return FloatArgumentType.floatArg( min, max );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(ByteBuf buf, FloatArgumentType t)
|
||||
{
|
||||
boolean hasMin = t.getMinimum() != -Float.MAX_VALUE;
|
||||
boolean hasMax = t.getMaximum() != Float.MAX_VALUE;
|
||||
|
||||
buf.writeByte( binaryFlag( hasMin, hasMax ) );
|
||||
if ( hasMin )
|
||||
{
|
||||
buf.writeFloat( t.getMinimum() );
|
||||
}
|
||||
if ( hasMax )
|
||||
{
|
||||
buf.writeFloat( t.getMaximum() );
|
||||
}
|
||||
}
|
||||
};
|
||||
private static final ArgumentSerializer<DoubleArgumentType> DOUBLE = new ArgumentSerializer<DoubleArgumentType>()
|
||||
{
|
||||
@Override
|
||||
protected DoubleArgumentType read(ByteBuf buf)
|
||||
{
|
||||
byte flags = buf.readByte();
|
||||
double min = ( flags & 0x1 ) != 0 ? buf.readDouble() : -Double.MAX_VALUE;
|
||||
double max = ( flags & 0x2 ) != 0 ? buf.readDouble() : -Double.MAX_VALUE;
|
||||
|
||||
return DoubleArgumentType.doubleArg( min, max );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(ByteBuf buf, DoubleArgumentType t)
|
||||
{
|
||||
boolean hasMin = t.getMinimum() != -Double.MAX_VALUE;
|
||||
boolean hasMax = t.getMaximum() != Double.MAX_VALUE;
|
||||
|
||||
buf.writeByte( binaryFlag( hasMin, hasMax ) );
|
||||
if ( hasMin )
|
||||
{
|
||||
buf.writeDouble( t.getMinimum() );
|
||||
}
|
||||
if ( hasMax )
|
||||
{
|
||||
buf.writeDouble( t.getMaximum() );
|
||||
}
|
||||
}
|
||||
};
|
||||
private static final ArgumentSerializer<IntegerArgumentType> INTEGER = new ArgumentSerializer<IntegerArgumentType>()
|
||||
{
|
||||
@Override
|
||||
protected IntegerArgumentType read(ByteBuf buf)
|
||||
{
|
||||
byte flags = buf.readByte();
|
||||
int min = ( flags & 0x1 ) != 0 ? buf.readInt() : Integer.MIN_VALUE;
|
||||
int max = ( flags & 0x2 ) != 0 ? buf.readInt() : Integer.MAX_VALUE;
|
||||
|
||||
return IntegerArgumentType.integer( min, max );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(ByteBuf buf, IntegerArgumentType t)
|
||||
{
|
||||
boolean hasMin = t.getMinimum() != Integer.MIN_VALUE;
|
||||
boolean hasMax = t.getMaximum() != Integer.MAX_VALUE;
|
||||
|
||||
buf.writeByte( binaryFlag( hasMin, hasMax ) );
|
||||
if ( hasMin )
|
||||
{
|
||||
buf.writeInt( t.getMinimum() );
|
||||
}
|
||||
if ( hasMax )
|
||||
{
|
||||
buf.writeInt( t.getMaximum() );
|
||||
}
|
||||
}
|
||||
};
|
||||
private static final ProperArgumentSerializer<StringArgumentType> STRING = new ProperArgumentSerializer<StringArgumentType>()
|
||||
{
|
||||
@Override
|
||||
protected StringArgumentType read(ByteBuf buf)
|
||||
{
|
||||
int val = readVarInt( buf );
|
||||
switch ( val )
|
||||
{
|
||||
case 0:
|
||||
return StringArgumentType.word();
|
||||
case 1:
|
||||
return StringArgumentType.string();
|
||||
case 2:
|
||||
return StringArgumentType.greedyString();
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unknown string type " + val );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(ByteBuf buf, StringArgumentType t)
|
||||
{
|
||||
writeVarInt( t.getType().ordinal(), buf );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKey()
|
||||
{
|
||||
return "brigadier:string";
|
||||
}
|
||||
};
|
||||
|
||||
static
|
||||
{
|
||||
PROVIDERS.put( "brigadier:bool", VOID );
|
||||
PROVIDERS.put( "brigadier:float", FLOAT );
|
||||
PROVIDERS.put( "brigadier:double", DOUBLE );
|
||||
PROVIDERS.put( "brigadier:integer", INTEGER );
|
||||
|
||||
PROVIDERS.put( "brigadier:string", STRING );
|
||||
PROPER_PROVIDERS.put( StringArgumentType.class, STRING );
|
||||
|
||||
PROVIDERS.put( "minecraft:entity", BYTE );
|
||||
PROVIDERS.put( "minecraft:game_profile", VOID );
|
||||
PROVIDERS.put( "minecraft:block_pos", VOID );
|
||||
PROVIDERS.put( "minecraft:column_pos", VOID );
|
||||
PROVIDERS.put( "minecraft:vec3", VOID );
|
||||
PROVIDERS.put( "minecraft:vec2", VOID );
|
||||
PROVIDERS.put( "minecraft:block_state", VOID );
|
||||
PROVIDERS.put( "minecraft:block_predicate", VOID );
|
||||
PROVIDERS.put( "minecraft:item_stack", VOID );
|
||||
PROVIDERS.put( "minecraft:item_predicate", VOID );
|
||||
PROVIDERS.put( "minecraft:color", VOID );
|
||||
PROVIDERS.put( "minecraft:component", VOID );
|
||||
PROVIDERS.put( "minecraft:message", VOID );
|
||||
PROVIDERS.put( "minecraft:nbt", VOID );
|
||||
PROVIDERS.put( "minecraft:nbt_path", VOID );
|
||||
PROVIDERS.put( "minecraft:objective", VOID );
|
||||
PROVIDERS.put( "minecraft:objective_criteria", VOID );
|
||||
PROVIDERS.put( "minecraft:operation", VOID );
|
||||
PROVIDERS.put( "minecraft:particle", VOID );
|
||||
PROVIDERS.put( "minecraft:rotation", VOID );
|
||||
PROVIDERS.put( "minecraft:scoreboard_slot", VOID );
|
||||
PROVIDERS.put( "minecraft:score_holder", BYTE );
|
||||
PROVIDERS.put( "minecraft:swizzle", VOID );
|
||||
PROVIDERS.put( "minecraft:team", VOID );
|
||||
PROVIDERS.put( "minecraft:item_slot", VOID );
|
||||
PROVIDERS.put( "minecraft:resource_location", VOID );
|
||||
PROVIDERS.put( "minecraft:mob_effect", VOID );
|
||||
PROVIDERS.put( "minecraft:function", VOID );
|
||||
PROVIDERS.put( "minecraft:entity_anchor", VOID );
|
||||
PROVIDERS.put( "minecraft:int_range", VOID );
|
||||
PROVIDERS.put( "minecraft:float_range", VOID );
|
||||
PROVIDERS.put( "minecraft:item_enchantment", VOID );
|
||||
PROVIDERS.put( "minecraft:entity_summon", VOID );
|
||||
PROVIDERS.put( "minecraft:dimension", VOID );
|
||||
}
|
||||
|
||||
private static ArgumentType<?> read(String key, ByteBuf buf)
|
||||
{
|
||||
ArgumentSerializer reader = PROVIDERS.get( key );
|
||||
Preconditions.checkArgument( reader != null, "No provider for argument " + key );
|
||||
|
||||
Object val = reader.read( buf );
|
||||
return val != null && PROPER_PROVIDERS.containsKey( val.getClass() ) ? (ArgumentType<?>) val : new DummyType( key, reader, val );
|
||||
}
|
||||
|
||||
private static void write(ArgumentType<?> arg, ByteBuf buf)
|
||||
{
|
||||
ProperArgumentSerializer proper = PROPER_PROVIDERS.get( arg.getClass() );
|
||||
if ( proper != null )
|
||||
{
|
||||
writeString( proper.getKey(), buf );
|
||||
proper.write( buf, arg );
|
||||
} else
|
||||
{
|
||||
Preconditions.checkArgument( arg instanceof DummyType, "Non dummy arg " + arg.getClass() );
|
||||
|
||||
DummyType dummy = (DummyType) arg;
|
||||
writeString( dummy.key, buf );
|
||||
dummy.serializer.write( buf, dummy.value );
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class DummyType<T> implements ArgumentType<T>
|
||||
{
|
||||
|
||||
private final String key;
|
||||
private final ArgumentSerializer<T> serializer;
|
||||
private final T value;
|
||||
|
||||
@Override
|
||||
public T parse(StringReader reader) throws CommandSyntaxException
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported." );
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class ArgumentSerializer<T>
|
||||
{
|
||||
|
||||
protected abstract T read(ByteBuf buf);
|
||||
|
||||
protected abstract void write(ByteBuf buf, T t);
|
||||
}
|
||||
|
||||
private static abstract class ProperArgumentSerializer<T> extends ArgumentSerializer<T>
|
||||
{
|
||||
|
||||
protected abstract String getKey();
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class SuggestionRegistry
|
||||
{
|
||||
|
||||
public static final SuggestionProvider ASK_SERVER = new DummyProvider( "minecraft:ask_server" );
|
||||
private static final Map<String, SuggestionProvider<DummyProvider>> PROVIDERS = new HashMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
PROVIDERS.put( "minecraft:ask_server", ASK_SERVER );
|
||||
registerDummy( "minecraft:all_recipes" );
|
||||
registerDummy( "minecraft:available_sounds" );
|
||||
registerDummy( "minecraft:summonable_entities" );
|
||||
}
|
||||
|
||||
private static void registerDummy(String name)
|
||||
{
|
||||
PROVIDERS.put( name, new DummyProvider( name ) );
|
||||
}
|
||||
|
||||
private static SuggestionProvider<DummyProvider> getProvider(String key)
|
||||
{
|
||||
SuggestionProvider<DummyProvider> provider = PROVIDERS.get( key );
|
||||
Preconditions.checkArgument( provider != null, "Unknown completion provider " + key );
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
private static String getKey(SuggestionProvider<DummyProvider> provider)
|
||||
{
|
||||
Preconditions.checkArgument( provider instanceof DummyProvider, "Non dummy provider " + provider );
|
||||
|
||||
return ( (DummyProvider) provider ).key;
|
||||
}
|
||||
|
||||
@Data
|
||||
private static final class DummyProvider implements SuggestionProvider<DummyProvider>
|
||||
{
|
||||
|
||||
private final String key;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Suggestions> getSuggestions(CommandContext<DummyProvider> context, SuggestionsBuilder builder) throws CommandSyntaxException
|
||||
{
|
||||
return builder.buildFuture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte binaryFlag(boolean first, boolean second)
|
||||
{
|
||||
byte ret = 0;
|
||||
|
||||
if ( first )
|
||||
{
|
||||
ret = (byte) ( ret | 0x1 );
|
||||
}
|
||||
if ( second )
|
||||
{
|
||||
ret = (byte) ( ret | 0x2 );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
package net.md_5.bungee.protocol.packet;
|
||||
|
||||
import com.mojang.brigadier.LiteralMessage;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
@ -18,17 +21,14 @@ public class TabCompleteResponse extends DefinedPacket
|
||||
{
|
||||
|
||||
private int transactionId;
|
||||
private int start;
|
||||
private int length;
|
||||
private List<CommandMatch> matches;
|
||||
private Suggestions suggestions;
|
||||
//
|
||||
private List<String> commands;
|
||||
|
||||
public TabCompleteResponse(int transactionId, int start, int length, List<CommandMatch> matches)
|
||||
public TabCompleteResponse(int transactionId, Suggestions suggestions)
|
||||
{
|
||||
this.transactionId = transactionId;
|
||||
this.start = start;
|
||||
this.length = length;
|
||||
this.matches = matches;
|
||||
this.suggestions = suggestions;
|
||||
}
|
||||
|
||||
public TabCompleteResponse(List<String> commands)
|
||||
@ -42,18 +42,21 @@ public class TabCompleteResponse extends DefinedPacket
|
||||
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 )
|
||||
{
|
||||
transactionId = readVarInt( buf );
|
||||
start = readVarInt( buf );
|
||||
length = readVarInt( buf );
|
||||
int start = readVarInt( buf );
|
||||
int length = readVarInt( buf );
|
||||
StringRange range = StringRange.between( start, start + length );
|
||||
|
||||
int cnt = readVarInt( buf );
|
||||
matches = new LinkedList<>();
|
||||
List<Suggestion> matches = new LinkedList<>();
|
||||
for ( int i = 0; i < cnt; i++ )
|
||||
{
|
||||
String match = readString( buf );
|
||||
String tooltip = buf.readBoolean() ? readString( buf ) : null;
|
||||
|
||||
matches.add( new CommandMatch( match, tooltip ) );
|
||||
matches.add( new Suggestion( range, match, new LiteralMessage( tooltip ) ) );
|
||||
}
|
||||
|
||||
suggestions = new Suggestions( range, matches );
|
||||
}
|
||||
|
||||
if ( protocolVersion < ProtocolConstants.MINECRAFT_1_13 )
|
||||
@ -68,15 +71,18 @@ public class TabCompleteResponse extends DefinedPacket
|
||||
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 )
|
||||
{
|
||||
writeVarInt( transactionId, buf );
|
||||
writeVarInt( start, buf );
|
||||
writeVarInt( length, buf );
|
||||
writeVarInt( suggestions.getRange().getStart(), buf );
|
||||
writeVarInt( suggestions.getRange().getLength(), buf );
|
||||
|
||||
writeVarInt( matches.size(), buf );
|
||||
for ( CommandMatch match : matches )
|
||||
writeVarInt( suggestions.getList().size(), buf );
|
||||
for ( Suggestion suggestion : suggestions.getList() )
|
||||
{
|
||||
writeString( match.match, buf );
|
||||
buf.writeBoolean( match.tooltip != null );
|
||||
writeString( match.tooltip, buf );
|
||||
writeString( suggestion.getText(), buf );
|
||||
buf.writeBoolean( suggestion.getTooltip() != null );
|
||||
if ( suggestion.getTooltip() != null )
|
||||
{
|
||||
writeString( suggestion.getTooltip().getString(), buf );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,14 +97,4 @@ public class TabCompleteResponse extends DefinedPacket
|
||||
{
|
||||
handler.handle( this );
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class CommandMatch
|
||||
{
|
||||
|
||||
private String match;
|
||||
private String tooltip;
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ public class Configuration implements ProxyConfig
|
||||
private int compressionThreshold = 256;
|
||||
private boolean preventProxyConnections;
|
||||
private boolean forgeSupport;
|
||||
private boolean injectCommands;
|
||||
|
||||
public void load()
|
||||
{
|
||||
@ -90,6 +91,11 @@ public class Configuration implements ProxyConfig
|
||||
compressionThreshold = adapter.getInt( "network_compression_threshold", compressionThreshold );
|
||||
preventProxyConnections = adapter.getBoolean( "prevent_proxy_connections", preventProxyConnections );
|
||||
forgeSupport = adapter.getBoolean( "forge_support", forgeSupport );
|
||||
injectCommands = adapter.getBoolean( "inject_commands", injectCommands );
|
||||
if ( injectCommands )
|
||||
{
|
||||
System.setProperty( "net.md-5.bungee.protocol.register_commands", "true" );
|
||||
}
|
||||
|
||||
disabledCommands = new CaseInsensitiveSet( (Collection<String>) adapter.getList( "disabled_commands", Arrays.asList( "disabledcommandhere" ) ) );
|
||||
|
||||
|
@ -3,11 +3,17 @@ package net.md_5.bungee.connection;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.ServerConnection;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.event.ServerDisconnectEvent;
|
||||
@ -32,6 +38,7 @@ import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.PacketWrapper;
|
||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||
import net.md_5.bungee.protocol.packet.BossBar;
|
||||
import net.md_5.bungee.protocol.packet.Commands;
|
||||
import net.md_5.bungee.protocol.packet.KeepAlive;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem;
|
||||
import net.md_5.bungee.protocol.packet.Respawn;
|
||||
@ -526,6 +533,35 @@ public class DownstreamBridge extends PacketHandler
|
||||
con.setDimension( respawn.getDimension() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Commands commands) throws Exception
|
||||
{
|
||||
boolean modified = false;
|
||||
|
||||
if ( BungeeCord.getInstance().config.isInjectCommands() )
|
||||
{
|
||||
for ( String command : bungee.getPluginManager().getCommands() )
|
||||
{
|
||||
if ( commands.getRoot().getChild( command ) == null )
|
||||
{
|
||||
LiteralCommandNode dummy = LiteralArgumentBuilder.literal( command )
|
||||
.then( RequiredArgumentBuilder.argument( "args", StringArgumentType.greedyString() )
|
||||
.suggests( Commands.SuggestionRegistry.ASK_SERVER ) )
|
||||
.build();
|
||||
commands.getRoot().addChild( dummy );
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( modified )
|
||||
{
|
||||
con.unsafe().sendPacket( commands );
|
||||
throw CancelSendSignal.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
@ -1,8 +1,12 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
@ -172,6 +176,19 @@ public class UpstreamBridge extends PacketHandler
|
||||
if ( con.getPendingConnection().getVersion() < ProtocolConstants.MINECRAFT_1_13 )
|
||||
{
|
||||
con.unsafe().sendPacket( new TabCompleteResponse( results ) );
|
||||
} else if ( BungeeCord.getInstance().config.isInjectCommands() )
|
||||
{
|
||||
int start = tabComplete.getCursor().lastIndexOf( ' ' ) + 1;
|
||||
int end = tabComplete.getCursor().length();
|
||||
StringRange range = StringRange.between( start, end );
|
||||
|
||||
List<Suggestion> brigadier = new LinkedList<>();
|
||||
for ( String s : results )
|
||||
{
|
||||
brigadier.add( new Suggestion( range, s ) );
|
||||
}
|
||||
|
||||
con.unsafe().sendPacket( new TabCompleteResponse( tabComplete.getTransactionId(), new Suggestions( range, brigadier ) ) );
|
||||
}
|
||||
throw CancelSendSignal.INSTANCE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user