Cleanup + comments

This commit is contained in:
Felix Cravic 2020-08-03 06:36:42 +02:00
parent 7617042b7d
commit dfa4cb8b37
10 changed files with 113 additions and 423 deletions

View File

@ -163,77 +163,12 @@ public class CommandManager {
return buildPacket(player);
}
/*private DeclareCommandsPacket buildPacket(Player player) {
DeclareCommandsPacket declareCommandsPacket = new DeclareCommandsPacket();
List<String> commands = new ArrayList<>();
for (Command<CommandSender> command : dispatcher.getCommands()) {
CommandCondition<Player> commandCondition = command.getCondition();
if (commandCondition != null) {
// Do not show command if return false
if (!commandCondition.apply(player)) {
continue;
}
}
commands.add(command.getName());
for (String alias : command.getAliases()) {
commands.add(alias);
}
}
for (CommandProcessor commandProcessor : commandProcessorMap.values()) {
// Do not show command if return false
if (!commandProcessor.hasAccess(player))
continue;
commands.add(commandProcessor.getCommandName());
String[] aliases = commandProcessor.getAliases();
if (aliases == null || aliases.length == 0)
continue;
for (String alias : aliases) {
commands.add(alias);
}
}
List<DeclareCommandsPacket.Node> nodes = new ArrayList<>();
ArrayList<Integer> rootChildren = new ArrayList<>();
DeclareCommandsPacket.Node argNode = new DeclareCommandsPacket.Node();
argNode.flags = 0b10;
argNode.name = "arg";
argNode.parser = "brigadier:string";
argNode.properties = packetWriter -> {
packetWriter.writeVarInt(0);
};
int argOffset = nodes.size();
nodes.add(argNode);
argNode.children = new int[]{argOffset};
for (String commandName : commands) {
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
literalNode.flags = 0b1;
literalNode.name = commandName;
literalNode.children = new int[]{argOffset};
rootChildren.add(nodes.size());
nodes.add(literalNode);
}
DeclareCommandsPacket.Node rootNode = new DeclareCommandsPacket.Node();
rootNode.flags = 0;
rootNode.children = ArrayUtils.toArray(rootChildren);
nodes.add(rootNode);
declareCommandsPacket.nodes = nodes.toArray(new DeclareCommandsPacket.Node[0]);
declareCommandsPacket.rootIndex = nodes.size() - 1;
return declareCommandsPacket;
}*/
/**
* Build the {@link DeclareCommandsPacket} for a player
*
* @param player the player to build the packet for
* @return the commands packet for the specific player
*/
private DeclareCommandsPacket buildPacket(Player player) {
DeclareCommandsPacket declareCommandsPacket = new DeclareCommandsPacket();
@ -243,7 +178,7 @@ public class CommandManager {
for (Command command : dispatcher.getCommands()) {
// Check if player should see this command
CommandCondition commandCondition = command.getCondition();
final CommandCondition commandCondition = command.getCondition();
if (commandCondition != null) {
// Do not show command if return false
if (!commandCondition.apply(player)) {
@ -273,7 +208,7 @@ public class CommandManager {
continue;
simpleCommands.add(commandProcessor.getCommandName());
String[] aliases = commandProcessor.getAliases();
final String[] aliases = commandProcessor.getAliases();
if (aliases == null || aliases.length == 0)
continue;
for (String alias : aliases) {
@ -305,6 +240,15 @@ public class CommandManager {
return declareCommandsPacket;
}
/**
* Add a command's syntaxes to the nodes list
*
* @param nodes the nodes of the packet
* @param cmdChildren the main root of this command
* @param name the name of the command (or the alias)
* @param syntaxes the syntaxes of the command
* @param rootChildren the children of the main node (all commands name)
*/
private void createCommand(List<DeclareCommandsPacket.Node> nodes, ArrayList<Integer> cmdChildren, String name, Collection<CommandSyntax> syntaxes, ArrayList<Integer> rootChildren) {
DeclareCommandsPacket.Node literalNode = createMainNode(name, syntaxes.isEmpty());
@ -378,9 +322,17 @@ public class CommandManager {
return literalNode;
}
/**
* Convert an argument to a node with the correct brigadier parser
*
* @param argument the argument to convert
* @param executable true if this is the last argument, false otherwise
* @return the list of nodes that the argument require
*/
private List<DeclareCommandsPacket.Node> toNodes(Argument<?> argument, boolean executable) {
List<DeclareCommandsPacket.Node> nodes = new ArrayList<>();
// You can uncomment this to test any brigadier parser on the client
/*DeclareCommandsPacket.Node testNode = simpleArgumentNode(nodes, argument, executable);
testNode.parser = "minecraft:entity";
testNode.properties = packetWriter -> packetWriter.writeByte((byte) 0x0);
@ -524,6 +476,14 @@ public class CommandManager {
return result;
}
/**
* Build an argument nod and add it to the nodes list
*
* @param nodes the current nodes list
* @param argument the argument
* @param executable true if this will be the last argument, false otherwise
* @return the created {@link DeclareCommandsPacket.Node}
*/
private DeclareCommandsPacket.Node simpleArgumentNode(List<DeclareCommandsPacket.Node> nodes,
Argument<?> argument, boolean executable) {
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();

View File

@ -1,7 +1,6 @@
package net.minestom.server.command.builder;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.command.builder.structure.Structure;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.item.Enchantment;
@ -15,14 +14,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Class used to retrieve argument data
*/
public class Arguments {
private final Map<String, Object> args = new HashMap<>();
public Structure getStructure(String id) {
return (Structure) getObject(id);
}
public boolean getBoolean(String id) {
return (boolean) getObject(id);
}

View File

@ -20,21 +20,28 @@ public class CommandDispatcher {
this.commands.add(command);
}
/**
* Parse the given command
*
* @param commandString the command (containing the command name and the args if any)
* @return the result of the parsing, null if the command doesn't exist
*/
public CommandResult parse(String commandString) {
commandString = commandString.trim();
// Split space
String spaceRegex = " ";
String[] splitted = commandString.split(spaceRegex);
String commandName = splitted[0];
final String spaceRegex = " ";
final String[] splitted = commandString.split(spaceRegex);
final String commandName = splitted[0];
String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(spaceRegex);
final String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(spaceRegex);
Command command = findCommand(commandName);
// TODO change return
final Command command = findCommand(commandName);
// Check if the command exists
if (command == null)
return null;
// Find the used syntax, or check which argument is wrong
return findCommandResult(command, args);
}
@ -67,15 +74,15 @@ public class CommandDispatcher {
// Find syntax
Collection<CommandSyntax> syntaxes = command.getSyntaxes();
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
List<CommandSyntax> validSyntaxes = new ArrayList<>();
Map<CommandSyntax, String[]> syntaxesValues = new HashMap<>();
TreeMap<Integer, CommandSuggestionHolder> syntaxesSuggestions = new TreeMap<>(Collections.reverseOrder());
for (CommandSyntax syntax : syntaxes) {
Argument[] arguments = syntax.getArguments();
String[] argsValues = new String[arguments.length];
final Argument[] arguments = syntax.getArguments();
final String[] argsValues = new String[arguments.length];
boolean syntaxCorrect = true;
int argIndex = 0;
@ -91,7 +98,7 @@ public class CommandDispatcher {
if (useRemaining) {
for (int i = argIndex; i < args.length; i++) {
String arg = args[i];
final String arg = args[i];
if (argValue.length() > 0)
argValue += " ";
argValue += arg;
@ -104,7 +111,7 @@ public class CommandDispatcher {
}
} else {
for (int i = argIndex; i < args.length; i++) {
String arg = args[i];
final String arg = args[i];
argValue += arg;
@ -153,14 +160,14 @@ public class CommandDispatcher {
// Verify args conditions of finalSyntax
if (finalSyntax != null) {
Argument[] arguments = finalSyntax.getArguments();
String[] argsValues = syntaxesValues.get(finalSyntax);
final Argument[] arguments = finalSyntax.getArguments();
final String[] argsValues = syntaxesValues.get(finalSyntax);
for (int i = 0; i < arguments.length; i++) {
Argument argument = arguments[i];
String argValue = argsValues[i];
final Argument argument = arguments[i];
final String argValue = argsValues[i];
// Finally parse it
Object parsedValue = argument.parse(argValue);
int conditionResult = argument.getConditionResult(parsedValue);
final Object parsedValue = argument.parse(argValue);
final int conditionResult = argument.getConditionResult(parsedValue);
if (conditionResult == Argument.SUCCESS) {
executorArgs.setArg(argument.getId(), parsedValue);
} else {
@ -177,12 +184,12 @@ public class CommandDispatcher {
if (finalSyntax == null) {
// Get closest valid syntax
if (!syntaxesSuggestions.isEmpty()) {
int max = syntaxesSuggestions.firstKey();
CommandSuggestionHolder suggestionHolder = syntaxesSuggestions.get(max);
CommandSyntax syntax = suggestionHolder.syntax;
String argValue = suggestionHolder.argValue;
int correctionResult = suggestionHolder.correctionResult;
int argIndex = suggestionHolder.argIndex;
final int max = syntaxesSuggestions.firstKey();
final CommandSuggestionHolder suggestionHolder = syntaxesSuggestions.get(max);
final CommandSyntax syntax = suggestionHolder.syntax;
final String argValue = suggestionHolder.argValue;
final int correctionResult = suggestionHolder.correctionResult;
final int argIndex = suggestionHolder.argIndex;
if (argValue.length() > 0) {
Argument argument = syntax.getArguments()[argIndex];
@ -227,18 +234,29 @@ public class CommandDispatcher {
private String value;
private int error;
/**
* Execute the command for the given source
* <p>
* The command will not be executed if the {@link CommandCondition} of the command
* is not validated
*
* @param source the command source
*/
public void execute(CommandSender source) {
// Condition check
CommandCondition condition = command.getCondition();
final CommandCondition condition = command.getCondition();
if (condition != null) {
boolean result = condition.apply(source);
final boolean result = condition.apply(source);
if (!result)
return;
}
// Condition is respected
if (executor != null) {
// An executor has been found
executor.apply(source, arguments);
} else if (callback != null) {
// No syntax has been validated but the faulty argument has been found
// Execute the faulty argument callback
callback.apply(source, value, error);
}
}

View File

@ -1,253 +0,0 @@
package net.minestom.server.command.builder.arguments;
import net.minestom.server.command.builder.structure.Structure;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ArgumentStructure extends Argument<Structure> {
public static final int BRACKET_ERROR = 1;
public static final int UNKNOWN_MEMBER_ERROR = 2;
public static final int UNDEFINED_MEMBER_ERROR = 3;
public static final int INVALID_MEMBER_ERROR = 4;
private Map<String, Argument> argByKey;
private Map<String, MemberType> memberTypeByKey;
private Map<String, Object> optionalDefaultValues;
public ArgumentStructure(String id) {
super(id, true);
this.argByKey = new HashMap<>();
this.memberTypeByKey = new HashMap<>();
this.optionalDefaultValues = new HashMap<>();
}
public void addNecessaryMember(Argument argument) {
if (argument.useRemaining()) {
System.err.println("Array type is unsupported with structure");
return;
}
String key = argument.getId().toLowerCase();
this.argByKey.put(key, argument);
this.memberTypeByKey.put(key, MemberType.NECESSARY);
}
public void addOptionalMember(Argument argument, Object defaultValue) {
if (argument.useRemaining()) {
System.err.println("Array type is unsupported with structure");
return;
}
String key = argument.getId().toLowerCase();
this.argByKey.put(key, argument);
this.memberTypeByKey.put(key, MemberType.OPTIONAL);
this.optionalDefaultValues.put(key, defaultValue);
}
@Override
public int getCorrectionResult(String value) {
// Check if value start and end with bracket
char first = value.charAt(0);
char last = value.charAt(value.length() - 1);
boolean bracket = first == '[' && last == ']';
if (!bracket)
return BRACKET_ERROR;
// Remove them
value = value.substring(1, value.length() - 1);
// Start analyze
// Get all necessary keys
List<String> necessaryMembers = memberTypeByKey.keySet().stream().filter(id -> memberTypeByKey.get(id).equals(MemberType.NECESSARY)).collect(Collectors.toList());
int index = 0;
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
if (c == '=') {
if (index > i)
continue;
String varName = value.substring(index, i).trim().toLowerCase();
//System.out.println("VARNAME: " + varName);
int declarationStartIndex = i + 1;
Argument arg = argByKey.getOrDefault(varName, null);
if (arg == null) {
// There isn't any member with varName name
return UNKNOWN_MEMBER_ERROR;
}
int[] semicolons = getSemiColonsIndex(value, declarationStartIndex);
String argValue;
// There isn't any other initialization
if (semicolons[0] == -1) {
argValue = value.substring(i + 1).trim();
if (!isValueValid(arg, argValue)) {
// No valid arg
return INVALID_MEMBER_ERROR;
}
necessaryMembers.remove(varName);
// END
break;
} else {
boolean correct = false;
boolean shouldBreak = false;
for (int semicolonIndex : semicolons) {
if (semicolonIndex == -1) {
argValue = value.substring(i + 1).trim();
if (!isValueValid(arg, argValue)) {
// No valid arg
return INVALID_MEMBER_ERROR;
}
shouldBreak = true;
correct = true;
necessaryMembers.remove(varName);
break;
}
argValue = value.substring(i + 1, semicolonIndex).trim();
if (isValueValid(arg, argValue)) {
index = semicolonIndex + 1;
correct = true;
necessaryMembers.remove(varName);
break;
}
}
if (!correct) {
return INVALID_MEMBER_ERROR;
}
if (shouldBreak) {
break;
}
}
}
}
return necessaryMembers.isEmpty() ? SUCCESS : UNDEFINED_MEMBER_ERROR;
}
@Override
public Structure parse(String value) {
Structure struct = new Structure();
// Remove first and last characters (bracket)
value = value.substring(1, value.length() - 1);
List<String> optionalMembers = memberTypeByKey.keySet().stream().filter(id -> memberTypeByKey.get(id).equals(MemberType.OPTIONAL)).collect(Collectors.toList());
int index = 0;
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
if (c == '=') {
if (index > i)
continue;
String varName = value.substring(index, i).trim().toLowerCase();
int declarationStartIndex = i + 1;
Argument arg = argByKey.getOrDefault(varName, null);
int[] semicolons = getSemiColonsIndex(value, declarationStartIndex);
String argValue;
// There isn't any other initialization
if (semicolons[0] == -1) {
argValue = value.substring(i + 1).trim();
struct.setValue(varName, arg.parse(argValue));
optionalMembers.remove(varName);
// END
break;
} else {
boolean shouldBreak = false;
for (int semicolonIndex : semicolons) {
if (semicolonIndex == -1) {
argValue = value.substring(i + 1).trim();
if (isValueValid(arg, argValue)) {
shouldBreak = true;
struct.setValue(varName, arg.parse(argValue));
optionalMembers.remove(varName);
}
break;
}
argValue = value.substring(i + 1, semicolonIndex).trim();
if (isValueValid(arg, argValue)) {
index = semicolonIndex + 1;
struct.setValue(varName, arg.parse(argValue));
optionalMembers.remove(varName);
break;
}
}
if (shouldBreak) {
break;
}
}
// Check where is next "valid" semicolon (after definition)
// Take value between member initialization as arg and check validity
// continue
}
}
if (!optionalMembers.isEmpty()) {
for (String id : optionalMembers) {
struct.setValue(id, optionalDefaultValues.get(id));
}
}
return struct;
}
@Override
public int getConditionResult(Structure value) {
return SUCCESS;
}
private boolean isValueValid(Argument argument, String argValue) {
return argument.getCorrectionResult(argValue) == SUCCESS &&
argument.getConditionResult(argValue) == SUCCESS;
}
private int[] getSemiColonsIndex(String value, int index) {
int[] array = new int[value.length() - index];
// Used to see if array has been modified
Arrays.fill(array, -1);
int count = 0;
for (int i = index; i < value.length(); i++) {
if (value.charAt(i) == ';') {
array[count] = i;
count++;
}
}
return array;
}
private enum MemberType {
NECESSARY, OPTIONAL
}
}

View File

@ -10,12 +10,11 @@ import net.minestom.server.command.builder.arguments.number.ArgumentFloat;
import net.minestom.server.command.builder.arguments.number.ArgumentInteger;
import net.minestom.server.command.builder.arguments.number.ArgumentLong;
/**
* Class listing all the basic arguments
*/
public class ArgumentType {
public static ArgumentStructure Structure(String id) {
return new ArgumentStructure(id);
}
public static ArgumentBoolean Boolean(String id) {
return new ArgumentBoolean(id);
}

View File

@ -2,6 +2,9 @@ package net.minestom.server.command.builder.condition;
import net.minestom.server.command.CommandSender;
/**
* Used to know if the command source is allowed to run the command
*/
public interface CommandCondition {
boolean apply(CommandSender source);
}

View File

@ -1,50 +0,0 @@
package net.minestom.server.command.builder.structure;
import java.util.HashMap;
import java.util.Map;
public class Structure {
private Map<String, Object> values = new HashMap<>();
public Structure getStructure(String id) {
return (Structure) getObject(id);
}
public boolean getBoolean(String id) {
return (boolean) getObject(id);
}
public long getLong(String id) {
return (long) getObject(id);
}
public int getInteger(String id) {
return (int) getObject(id);
}
public double getDouble(String id) {
return (double) getObject(id);
}
public float getFloat(String id) {
return (float) getObject(id);
}
public String getString(String id) {
return (String) getObject(id);
}
public String getWord(String id) {
return getString(id);
}
public Object getObject(String id) {
return values.getOrDefault(id, null);
}
public void setValue(String key, Object value) {
this.values.put(key, value);
}
}

View File

@ -3,9 +3,6 @@ package net.minestom.server.entity;
import net.minestom.server.network.packet.server.play.SpawnExperienceOrbPacket;
import net.minestom.server.network.player.PlayerConnection;
import java.util.HashSet;
import java.util.Set;
public class ExperienceOrb extends Entity {
private short experienceCount;
@ -29,7 +26,11 @@ public class ExperienceOrb extends Entity {
@Override
public boolean addViewer(Player player) {
PlayerConnection playerConnection = player.getPlayerConnection();
final boolean result = super.addViewer(player); // Add player to viewers list
if (!result)
return false;
final PlayerConnection playerConnection = player.getPlayerConnection();
SpawnExperienceOrbPacket experienceOrbPacket = new SpawnExperienceOrbPacket();
experienceOrbPacket.entityId = getEntityId();
@ -39,27 +40,29 @@ public class ExperienceOrb extends Entity {
playerConnection.sendPacket(experienceOrbPacket);
playerConnection.sendPacket(getVelocityPacket());
return super.addViewer(player); // Add player to viewers list
return result;
}
/**
* @return the experience amount contained in the entity
* Get the experience count
*
* @return the experience count
*/
public short getExperienceCount() {
return experienceCount;
}
/**
* @param experienceCount the new experience amount
* Change the experience count
*
* @param experienceCount the new experience count
*/
public void setExperienceCount(short experienceCount) {
// Remove the entity in order to respawn it with the correct experience count
Set<Player> viewerCache = new HashSet<>(getViewers());
viewerCache.forEach(player -> removeViewer(player));
getViewers().forEach(player -> removeViewer(player));
this.experienceCount = experienceCount;
viewerCache.forEach(player -> addViewer(player));
getViewers().forEach(player -> addViewer(player));
}
}

View File

@ -15,7 +15,9 @@ public class EntityDamage extends DamageType {
}
/**
* @return the source of the damage
* Get the source of the damage
*
* @return the source
*/
public Entity getSource() {
return source;

View File

@ -7,7 +7,7 @@ import net.minestom.server.entity.Entity;
*/
public class EntityProjectileDamage extends DamageType {
private Entity shooter;
private final Entity shooter;
private final Entity projectile;
public EntityProjectileDamage(Entity shooter, Entity projectile) {
@ -16,10 +16,20 @@ public class EntityProjectileDamage extends DamageType {
this.projectile = projectile;
}
/**
* Get the projectile responsive for the damage
*
* @return the projectile
*/
public Entity getProjectile() {
return projectile;
}
/**
* Get the shooter of the projectile
*
* @return the shooter of the projectile, null if not any
*/
public Entity getShooter() {
return shooter;
}