From 9ec131e49ba9c668eec4a90735ccfd694659602c Mon Sep 17 00:00:00 2001 From: Pedro270707 Date: Mon, 18 Sep 2023 18:14:40 -0300 Subject: [PATCH] Added more argument types and increased max suggestions --- .../net/pedroricardo/commander/Commander.java | 2 +- .../commander/CommanderHelper.java | 2 +- .../content/CommanderClientCommandSource.java | 11 ++ .../content/CommanderCommandManager.java | 15 ++- .../content/CommanderCommandSource.java | 6 + .../content/CommanderServerCommandSource.java | 11 ++ .../arguments/AchievementArgumentType.java | 5 +- .../content/arguments/BlockArgumentType.java | 61 +++++++++ .../BlockCoordinatesArgumentType.java | 108 ++++++++++++++++ .../content/arguments/EntityArgumentType.java | 85 +++++++++++++ .../arguments/EntitySummonArgumentType.java | 51 ++++++++ .../content/arguments/Vec3dArgumentType.java | 105 ++++++++++++++++ .../content/commands/AchievementCommand.java | 3 +- .../content/commands/KillCommand.java | 48 ++++++++ .../content/commands/SetBlockCommand.java | 42 +++++++ .../content/commands/SummonCommand.java | 49 ++++++++ .../content/commands/TestCommand.java | 8 -- .../exceptions/CommanderExceptions.java | 38 ++++++ .../content/helpers/BlockCoordinate.java | 51 ++++++++ .../content/helpers/BlockCoordinates.java | 47 +++++++ .../content/helpers/DoubleCoordinate.java | 51 ++++++++ .../content/helpers/DoubleCoordinates.java | 31 +++++ .../content/helpers/EntitySelector.java | 61 +++++++++ .../content/helpers/EntitySelectorParser.java | 116 ++++++++++++++++++ .../commander/gui/GuiChatSuggestions.java | 19 ++- src/main/resources/lang/commander/en_US.lang | 16 ++- 26 files changed, 1020 insertions(+), 22 deletions(-) create mode 100644 src/main/java/net/pedroricardo/commander/content/arguments/BlockArgumentType.java create mode 100644 src/main/java/net/pedroricardo/commander/content/arguments/BlockCoordinatesArgumentType.java create mode 100644 src/main/java/net/pedroricardo/commander/content/arguments/EntityArgumentType.java create mode 100644 src/main/java/net/pedroricardo/commander/content/arguments/EntitySummonArgumentType.java create mode 100644 src/main/java/net/pedroricardo/commander/content/arguments/Vec3dArgumentType.java create mode 100644 src/main/java/net/pedroricardo/commander/content/commands/KillCommand.java create mode 100644 src/main/java/net/pedroricardo/commander/content/commands/SetBlockCommand.java create mode 100644 src/main/java/net/pedroricardo/commander/content/commands/SummonCommand.java create mode 100644 src/main/java/net/pedroricardo/commander/content/exceptions/CommanderExceptions.java create mode 100644 src/main/java/net/pedroricardo/commander/content/helpers/BlockCoordinate.java create mode 100644 src/main/java/net/pedroricardo/commander/content/helpers/BlockCoordinates.java create mode 100644 src/main/java/net/pedroricardo/commander/content/helpers/DoubleCoordinate.java create mode 100644 src/main/java/net/pedroricardo/commander/content/helpers/DoubleCoordinates.java create mode 100644 src/main/java/net/pedroricardo/commander/content/helpers/EntitySelector.java create mode 100644 src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorParser.java diff --git a/src/main/java/net/pedroricardo/commander/Commander.java b/src/main/java/net/pedroricardo/commander/Commander.java index 7b517f1..c8ba6c8 100644 --- a/src/main/java/net/pedroricardo/commander/Commander.java +++ b/src/main/java/net/pedroricardo/commander/Commander.java @@ -10,7 +10,7 @@ public class Commander implements ModInitializer { public static final String MOD_ID = "commander"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); - public static int maxSuggestions = 4; + public static int maxSuggestions = 6; public static boolean suggestionsFollowParameters = true; @Override diff --git a/src/main/java/net/pedroricardo/commander/CommanderHelper.java b/src/main/java/net/pedroricardo/commander/CommanderHelper.java index d701851..e086031 100644 --- a/src/main/java/net/pedroricardo/commander/CommanderHelper.java +++ b/src/main/java/net/pedroricardo/commander/CommanderHelper.java @@ -17,7 +17,7 @@ public static List getLegacySuggestionList(String message, int curso for (Command command : Commands.commands) { List path = new ArrayList<>(); path.add(command.getName()); - if (CommanderCommandManager.getDispatcher().findNode(path) == null && command.getName().startsWith(textBeforeCursor.substring(1))) { + if (CommanderCommandManager.getDispatcher().findNode(path) == null && command.getName().startsWith(textBeforeCursor.substring(1)) && !command.getName().equals(textBeforeCursor.substring(1))) { list.add(new Suggestion(new StringRange(1, 1 + command.getName().length()), command.getName())); } } diff --git a/src/main/java/net/pedroricardo/commander/content/CommanderClientCommandSource.java b/src/main/java/net/pedroricardo/commander/content/CommanderClientCommandSource.java index 70f5e5d..b0e2b40 100644 --- a/src/main/java/net/pedroricardo/commander/content/CommanderClientCommandSource.java +++ b/src/main/java/net/pedroricardo/commander/content/CommanderClientCommandSource.java @@ -2,6 +2,12 @@ import net.minecraft.client.Minecraft; import net.minecraft.core.entity.player.EntityPlayer; +import net.minecraft.core.net.command.ClientCommandHandler; +import net.minecraft.core.net.command.ClientPlayerCommandSender; +import net.minecraft.core.net.command.CommandHandler; +import net.minecraft.core.net.command.CommandSender; +import net.minecraft.core.util.phys.Vec3d; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; @@ -37,4 +43,9 @@ public EntityPlayer getSender() { public boolean hasAdmin() { return true; } + + @Override + public @Nullable Vec3d getCoordinates() { + return this.getSender().getPosition(1.0f); + } } diff --git a/src/main/java/net/pedroricardo/commander/content/CommanderCommandManager.java b/src/main/java/net/pedroricardo/commander/content/CommanderCommandManager.java index b17fe3f..662cd64 100644 --- a/src/main/java/net/pedroricardo/commander/content/CommanderCommandManager.java +++ b/src/main/java/net/pedroricardo/commander/content/CommanderCommandManager.java @@ -2,17 +2,28 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.pedroricardo.commander.content.commands.AchievementCommand; -import net.pedroricardo.commander.content.commands.TestCommand; +import net.minecraft.core.achievement.Achievement; +import net.minecraft.core.net.command.*; +import net.pedroricardo.commander.Commander; +import net.pedroricardo.commander.content.commands.*; import org.jetbrains.annotations.Nullable; +import java.util.Collections; + public class CommanderCommandManager { public static int SINGLE_SUCCESS = 1; private static final CommandDispatcher DISPATCHER = new CommandDispatcher<>(); static { AchievementCommand.register(DISPATCHER); + SummonCommand.register(DISPATCHER); + SetBlockCommand.register(DISPATCHER); + KillCommand.register(DISPATCHER); + TestCommand.register(DISPATCHER); } diff --git a/src/main/java/net/pedroricardo/commander/content/CommanderCommandSource.java b/src/main/java/net/pedroricardo/commander/content/CommanderCommandSource.java index 06ad4ca..18832dc 100644 --- a/src/main/java/net/pedroricardo/commander/content/CommanderCommandSource.java +++ b/src/main/java/net/pedroricardo/commander/content/CommanderCommandSource.java @@ -1,6 +1,10 @@ package net.pedroricardo.commander.content; import net.minecraft.core.entity.player.EntityPlayer; +import net.minecraft.core.net.command.CommandHandler; +import net.minecraft.core.net.command.CommandSender; +import net.minecraft.core.util.phys.Vec3d; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; @@ -21,4 +25,6 @@ default Collection getEntitySuggestions() { EntityPlayer getSender(); boolean hasAdmin(); + + @Nullable Vec3d getCoordinates(); } diff --git a/src/main/java/net/pedroricardo/commander/content/CommanderServerCommandSource.java b/src/main/java/net/pedroricardo/commander/content/CommanderServerCommandSource.java index 47e18aa..ec1ed50 100644 --- a/src/main/java/net/pedroricardo/commander/content/CommanderServerCommandSource.java +++ b/src/main/java/net/pedroricardo/commander/content/CommanderServerCommandSource.java @@ -1,8 +1,14 @@ package net.pedroricardo.commander.content; import net.minecraft.core.entity.player.EntityPlayer; +import net.minecraft.core.net.command.CommandHandler; +import net.minecraft.core.net.command.CommandSender; +import net.minecraft.core.net.command.ServerCommandHandler; +import net.minecraft.core.net.command.ServerPlayerCommandSender; +import net.minecraft.core.util.phys.Vec3d; import net.minecraft.server.MinecraftServer; import net.minecraft.server.entity.player.EntityPlayerMP; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; @@ -40,4 +46,9 @@ public EntityPlayer getSender() { public boolean hasAdmin() { return false; } + + @Override + public @Nullable Vec3d getCoordinates() { + return this.getSender().getPosition(1.0f); + } } diff --git a/src/main/java/net/pedroricardo/commander/content/arguments/AchievementArgumentType.java b/src/main/java/net/pedroricardo/commander/content/arguments/AchievementArgumentType.java index 371f7de..0bf4193 100644 --- a/src/main/java/net/pedroricardo/commander/content/arguments/AchievementArgumentType.java +++ b/src/main/java/net/pedroricardo/commander/content/arguments/AchievementArgumentType.java @@ -24,17 +24,18 @@ public Achievement parse(StringReader reader) throws CommandSyntaxException { for (Achievement achievement : AchievementList.achievementList) { if (((StatNameAccessor)achievement).statName().equals(string)) { + reader.skip(); return achievement; } } - throw new CommandSyntaxException(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument(), () -> I18n.getInstance().translateKeyAndFormat("commands.commander.achievement.invalid_achievement", string)); + throw new CommandSyntaxException(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument(), () -> I18n.getInstance().translateKey("argument_types.commander.achievement.invalid_achievement")); } @Override public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { for (Achievement achievement : AchievementList.achievementList) { if (((StatNameAccessor)achievement).statName().startsWith(builder.getRemaining())) { - builder.suggest(((StatNameAccessor)achievement).statName()); + builder.suggest(((StatNameAccessor)achievement).statName(), achievement::getStatName); } } return builder.buildFuture(); diff --git a/src/main/java/net/pedroricardo/commander/content/arguments/BlockArgumentType.java b/src/main/java/net/pedroricardo/commander/content/arguments/BlockArgumentType.java new file mode 100644 index 0000000..adcfcce --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/arguments/BlockArgumentType.java @@ -0,0 +1,61 @@ +package net.pedroricardo.commander.content.arguments; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.core.block.Block; +import net.minecraft.core.entity.Entity; +import net.minecraft.core.entity.EntityDispatcher; +import net.minecraft.core.lang.I18n; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class BlockArgumentType implements ArgumentType { + private static final List EXAMPLES = Arrays.asList("tile.stone", "stone", "dirt"); + + public static ArgumentType block() { + return new BlockArgumentType(); + } + + @Override + public Block parse(StringReader reader) throws CommandSyntaxException { + final String string = reader.readString(); + + for (Block block : Block.blocksList) { + if (block == null) continue; + if (block.getKey().equals(string) || (block.getKey().startsWith("tile.") && block.getKey().substring("tile.".length()).equals(string))) { + return block; + } + } + throw new CommandSyntaxException(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument(), () -> I18n.getInstance().translateKey("argument_types.commander.block.invalid_block")); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + String remaining = builder.getRemaining(); + for (Block block : Block.blocksList) { + if (block == null) continue; + if ("tile.".startsWith(remaining) || remaining.startsWith("tile.")) { + if (block.getKey().startsWith(builder.getRemaining())) { + builder.suggest(block.getKey(), () -> I18n.getInstance().translateKey(block.getLanguageKey(0) + ".name")); + } + } else { + if (block.getKey().startsWith("tile.") && block.getKey().substring("tile.".length()).startsWith(builder.getRemaining())) { + builder.suggest(block.getKey().substring("tile.".length()), () -> I18n.getInstance().translateKey(block.getLanguageKey(0) + ".name")); + } + } + } + return builder.buildFuture(); + } + + @Override + public Collection getExamples() { + return EXAMPLES; + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/arguments/BlockCoordinatesArgumentType.java b/src/main/java/net/pedroricardo/commander/content/arguments/BlockCoordinatesArgumentType.java new file mode 100644 index 0000000..518123d --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/arguments/BlockCoordinatesArgumentType.java @@ -0,0 +1,108 @@ +package net.pedroricardo.commander.content.arguments; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.core.util.phys.Vec3d; +import net.pedroricardo.commander.content.CommanderCommandSource; +import net.pedroricardo.commander.content.exceptions.CommanderExceptions; +import net.pedroricardo.commander.content.helpers.BlockCoordinate; +import net.pedroricardo.commander.content.helpers.BlockCoordinates; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class BlockCoordinatesArgumentType implements ArgumentType { + private static final List EXAMPLES = Arrays.asList("~ ~ ~", "0 0 0", "~ ~60 ~", "~-20 ~10 -25"); + + public static BlockCoordinatesArgumentType blockCoordinates() { + return new BlockCoordinatesArgumentType(); + } + + @Override + public BlockCoordinates parse(StringReader reader) throws CommandSyntaxException { + int i = reader.getCursor(); + BlockCoordinate x = BlockCoordinate.parse(reader); + if (!reader.canRead() || reader.peek() != ' ') { + if (reader.peek() == 'f' || reader.peek() == 'd') { + reader.skip(); + if (!reader.canRead() || reader.peek() != ' ') { + reader.setCursor(i); + throw CommanderExceptions.incomplete().createWithContext(reader); + } + } else { + reader.setCursor(i); + throw CommanderExceptions.incomplete().createWithContext(reader); + } + } + reader.skip(); + BlockCoordinate y = BlockCoordinate.parse(reader); + if (!reader.canRead() || reader.peek() != ' ') { + if (reader.peek() == 'f' || reader.peek() == 'd') { + reader.skip(); + if (!reader.canRead() || reader.peek() != ' ') { + reader.setCursor(i); + throw CommanderExceptions.incomplete().createWithContext(reader); + } + } else { + reader.setCursor(i); + throw CommanderExceptions.incomplete().createWithContext(reader); + } + } + reader.skip(); + BlockCoordinate z = BlockCoordinate.parse(reader); + return new BlockCoordinates(x, y, z); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + String string = builder.getRemaining(); + Vec3d coordinates = ((CommanderCommandSource)context.getSource()).getCoordinates(); + + if (coordinates == null) return builder.buildFuture(); + + // Rounding the coordinates + int[] roundedCoordinates = new int[]{(int)Math.floor(coordinates.xCoord), (int)Math.floor(coordinates.yCoord - 1.6), (int)Math.floor(coordinates.zCoord)}; + + if (string.isEmpty()) { + String allCoordinates = roundedCoordinates[0] + " " + roundedCoordinates[1] + " " + roundedCoordinates[2]; + try { + this.parse(new StringReader(allCoordinates)); + builder.suggest(String.valueOf(roundedCoordinates[0])); + builder.suggest(roundedCoordinates[0] + " " + roundedCoordinates[1]); + builder.suggest(allCoordinates); + } catch (CommandSyntaxException ignored) {} + } else { + String[] strings = string.split(" "); + String allCoordinates; + switch (strings.length) { + case 1: + allCoordinates = strings[0] + " " + roundedCoordinates[1] + " " + roundedCoordinates[2]; + try { + this.parse(new StringReader(allCoordinates)); + builder.suggest(strings[0] + " " + roundedCoordinates[1]); + builder.suggest(allCoordinates); + } catch (CommandSyntaxException ignored) {} + break; + case 2: + allCoordinates = strings[0] + " " + strings[1] + " " + roundedCoordinates[2]; + try { + this.parse(new StringReader(allCoordinates)); + builder.suggest(allCoordinates); + } catch (CommandSyntaxException ignored) {} + break; + } + } + return builder.buildFuture(); + } + + @Override + public Collection getExamples() { + return EXAMPLES; + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/arguments/EntityArgumentType.java b/src/main/java/net/pedroricardo/commander/content/arguments/EntityArgumentType.java new file mode 100644 index 0000000..fd2e57e --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/arguments/EntityArgumentType.java @@ -0,0 +1,85 @@ +package net.pedroricardo.commander.content.arguments; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.core.entity.Entity; +import net.minecraft.core.entity.player.EntityPlayer; +import net.minecraft.core.lang.I18n; +import net.pedroricardo.commander.content.CommanderCommandSource; +import net.pedroricardo.commander.content.exceptions.CommanderExceptions; +import net.pedroricardo.commander.content.helpers.EntitySelector; +import net.pedroricardo.commander.content.helpers.EntitySelectorParser; + +import java.util.concurrent.CompletableFuture; + +public class EntityArgumentType implements ArgumentType { + private final boolean singleEntity, playerOnly; + + private EntityArgumentType(boolean singleEntity, boolean playerOnly) { + this.singleEntity = singleEntity; + this.playerOnly = playerOnly; + } + + public static EntityArgumentType entities() { + return new EntityArgumentType(false, false); + } + + public static EntityArgumentType entity() { + return new EntityArgumentType(true, false); + } + + public static EntityArgumentType players() { + return new EntityArgumentType(false, true); + } + + public static EntityArgumentType player() { + return new EntityArgumentType(true, true); + } + + @Override + public EntitySelector parse(StringReader reader) throws CommandSyntaxException { + int cursor = reader.getCursor(); + EntitySelectorParser entitySelectorParser = new EntitySelectorParser(reader); + if (reader.canRead() && reader.peek() == '@') { + EntitySelector entitySelector = entitySelectorParser.parse(); + if (this.singleEntity && entitySelector.getMaxResults() > 1) { + reader.setCursor(cursor); + if (this.playerOnly) { + throw CommanderExceptions.singlePlayerOnly().createWithContext(reader); + } + throw CommanderExceptions.singleEntityOnly().createWithContext(reader); + } + if (this.playerOnly && entitySelector.includesEntities() && !entitySelector.isCurrentEntity()) { + reader.setCursor(cursor); + throw CommanderExceptions.playerOnly().createWithContext(reader); + } + return entitySelector; + } + + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + if ("@p".startsWith(builder.getRemainingLowerCase())) + builder.suggest("@p", () -> I18n.getInstance().translateKey("argument_types.commander.entity.selector.nearest_player")); + if ("@a".startsWith(builder.getRemainingLowerCase())) + builder.suggest("@a", () -> I18n.getInstance().translateKey("argument_types.commander.entity.selector.all_players")); + if ("@r".startsWith(builder.getRemainingLowerCase())) + builder.suggest("@r", () -> I18n.getInstance().translateKey("argument_types.commander.entity.selector.random_player")); + if ("@s".startsWith(builder.getRemainingLowerCase())) + builder.suggest("@s", () -> I18n.getInstance().translateKey("argument_types.commander.entity.selector.self")); + if ("@e".startsWith(builder.getRemainingLowerCase())) + builder.suggest("@e", () -> I18n.getInstance().translateKey("argument_types.commander.entity.selector.all_entities")); + for (String name : ((CommanderCommandSource)context.getSource()).getEntitySuggestions()) { + if (name.startsWith(builder.getRemaining())) { + builder.suggest(name); + } + } + return builder.buildFuture(); + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/arguments/EntitySummonArgumentType.java b/src/main/java/net/pedroricardo/commander/content/arguments/EntitySummonArgumentType.java new file mode 100644 index 0000000..5c07a5f --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/arguments/EntitySummonArgumentType.java @@ -0,0 +1,51 @@ +package net.pedroricardo.commander.content.arguments; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.core.entity.Entity; +import net.minecraft.core.entity.EntityDispatcher; +import net.minecraft.core.lang.I18n; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class EntitySummonArgumentType implements ArgumentType> { + private static final List EXAMPLES = Arrays.asList("Creeper", "Skeleton", "Slime"); + + public static ArgumentType> entity() { + return new EntitySummonArgumentType(); + } + + @Override + public Class parse(StringReader reader) throws CommandSyntaxException { + final String string = reader.readString(); + + for (String entityName : EntityDispatcher.stringToClassMapping.keySet()) { + if (entityName.equals(string)) { + return EntityDispatcher.stringToClassMapping.get(entityName); + } + } + throw new CommandSyntaxException(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument(), () -> I18n.getInstance().translateKey("argument_types.commander.entity_summon.invalid_entity")); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + for (String entityName : EntityDispatcher.stringToClassMapping.keySet()) { + if (entityName.startsWith(builder.getRemaining())) { + builder.suggest(entityName); + } + } + return builder.buildFuture(); + } + + @Override + public Collection getExamples() { + return EXAMPLES; + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/arguments/Vec3dArgumentType.java b/src/main/java/net/pedroricardo/commander/content/arguments/Vec3dArgumentType.java new file mode 100644 index 0000000..d8f93e7 --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/arguments/Vec3dArgumentType.java @@ -0,0 +1,105 @@ +package net.pedroricardo.commander.content.arguments; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.core.util.phys.Vec3d; +import net.pedroricardo.commander.content.CommanderCommandSource; +import net.pedroricardo.commander.content.exceptions.CommanderExceptions; +import net.pedroricardo.commander.content.helpers.DoubleCoordinate; +import net.pedroricardo.commander.content.helpers.DoubleCoordinates; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class Vec3dArgumentType implements ArgumentType { + private static final List EXAMPLES = Arrays.asList("~ ~ ~", "0 0 0", "~ ~60 ~", "~-20 ~10 ~-25.5"); + + public static Vec3dArgumentType vec3d() { + return new Vec3dArgumentType(); + } + + @Override + public DoubleCoordinates parse(StringReader reader) throws CommandSyntaxException { + int i = reader.getCursor(); + DoubleCoordinate x = DoubleCoordinate.parse(reader); + if (!reader.canRead() || reader.peek() != ' ') { + if (reader.peek() == 'f' || reader.peek() == 'd') { + reader.skip(); + if (!reader.canRead() || reader.peek() != ' ') { + reader.setCursor(i); + throw CommanderExceptions.incomplete().createWithContext(reader); + } + } else { + reader.setCursor(i); + throw CommanderExceptions.incomplete().createWithContext(reader); + } + } + reader.skip(); + DoubleCoordinate y = DoubleCoordinate.parse(reader); + if (!reader.canRead() || reader.peek() != ' ') { + if (reader.peek() == 'f' || reader.peek() == 'd') { + reader.skip(); + if (!reader.canRead() || reader.peek() != ' ') { + reader.setCursor(i); + throw CommanderExceptions.incomplete().createWithContext(reader); + } + } else { + reader.setCursor(i); + throw CommanderExceptions.incomplete().createWithContext(reader); + } + } + reader.skip(); + DoubleCoordinate z = DoubleCoordinate.parse(reader); + return new DoubleCoordinates(x, y, z); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + String string = builder.getRemaining(); + Vec3d coordinates = ((CommanderCommandSource)context.getSource()).getCoordinates(); + + if (coordinates == null) return builder.buildFuture(); + + if (string.isEmpty()) { + String allCoordinates = coordinates.xCoord + " " + coordinates.yCoord + " " + coordinates.zCoord; + try { + this.parse(new StringReader(allCoordinates)); + builder.suggest(String.valueOf(coordinates.xCoord)); + builder.suggest(coordinates.xCoord + " " + coordinates.yCoord); + builder.suggest(allCoordinates); + } catch (CommandSyntaxException ignored) {} + } else { + String[] strings = string.split(" "); + String allCoordinates; + switch (strings.length) { + case 1: + allCoordinates = strings[0] + " " + coordinates.yCoord + " " + coordinates.zCoord; + try { + this.parse(new StringReader(allCoordinates)); + builder.suggest(strings[0] + " " + coordinates.yCoord); + builder.suggest(allCoordinates); + } catch (CommandSyntaxException ignored) {} + break; + case 2: + allCoordinates = strings[0] + " " + strings[1] + " " + coordinates.zCoord; + try { + this.parse(new StringReader(allCoordinates)); + builder.suggest(allCoordinates); + } catch (CommandSyntaxException ignored) {} + break; + } + } + return builder.buildFuture(); + } + + @Override + public Collection getExamples() { + return EXAMPLES; + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/commands/AchievementCommand.java b/src/main/java/net/pedroricardo/commander/content/commands/AchievementCommand.java index 7b0c91c..4ab38eb 100644 --- a/src/main/java/net/pedroricardo/commander/content/commands/AchievementCommand.java +++ b/src/main/java/net/pedroricardo/commander/content/commands/AchievementCommand.java @@ -10,7 +10,7 @@ import net.pedroricardo.commander.content.CommanderCommandSource; import net.pedroricardo.commander.content.arguments.AchievementArgumentType; import net.pedroricardo.commander.content.arguments.EntityArgumentType; -import net.pedroricardo.commander.content.arguments.EntitySelector; +import net.pedroricardo.commander.content.helpers.EntitySelector; import java.util.ArrayList; import java.util.List; @@ -19,6 +19,7 @@ public class AchievementCommand { public static void register(CommandDispatcher dispatcher) { dispatcher.register((LiteralArgumentBuilder)(((LiteralArgumentBuilder)LiteralArgumentBuilder.literal("achievement")) + .requires(c -> ((CommanderCommandSource)c).hasAdmin()) .then(((LiteralArgumentBuilder)LiteralArgumentBuilder.literal("grant") ).then(RequiredArgumentBuilder.argument("achievement", AchievementArgumentType.achievement()) .executes(c -> { diff --git a/src/main/java/net/pedroricardo/commander/content/commands/KillCommand.java b/src/main/java/net/pedroricardo/commander/content/commands/KillCommand.java new file mode 100644 index 0000000..b34d5be --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/commands/KillCommand.java @@ -0,0 +1,48 @@ +package net.pedroricardo.commander.content.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import net.minecraft.core.block.Block; +import net.minecraft.core.entity.Entity; +import net.minecraft.core.entity.EntityLiving; +import net.minecraft.core.entity.player.EntityPlayer; +import net.minecraft.core.util.phys.Vec3d; +import net.pedroricardo.commander.content.CommanderCommandManager; +import net.pedroricardo.commander.content.CommanderCommandSource; +import net.pedroricardo.commander.content.arguments.BlockArgumentType; +import net.pedroricardo.commander.content.arguments.BlockCoordinatesArgumentType; +import net.pedroricardo.commander.content.arguments.EntityArgumentType; +import net.pedroricardo.commander.content.helpers.BlockCoordinates; +import net.pedroricardo.commander.content.helpers.EntitySelector; + +import java.util.Collection; +import java.util.List; + +@SuppressWarnings("unchecked") +public class KillCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register((LiteralArgumentBuilder)LiteralArgumentBuilder.literal("kill") + .executes(c -> { + EntityPlayer sender = ((CommanderCommandSource)c.getSource()).getSender(); + if (sender != null) { + sender.killPlayer(); + } + return CommanderCommandManager.SINGLE_SUCCESS; + }) + .then((RequiredArgumentBuilder)RequiredArgumentBuilder.argument("entities", EntityArgumentType.entities()) + .executes(c -> { + EntitySelector entitySelector = c.getArgument("entities", EntitySelector.class); + List entities = entitySelector.get((CommanderCommandSource)c.getSource()); + while (!entities.isEmpty()) { + if (entities.get(0) instanceof EntityLiving) { + ((EntityLiving)entities.get(0)).hurt(null, 1000, null); + } else { + entities.get(0).remove(); + } + } + return CommanderCommandManager.SINGLE_SUCCESS; + }))); + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/commands/SetBlockCommand.java b/src/main/java/net/pedroricardo/commander/content/commands/SetBlockCommand.java new file mode 100644 index 0000000..606732d --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/commands/SetBlockCommand.java @@ -0,0 +1,42 @@ +package net.pedroricardo.commander.content.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import net.minecraft.core.block.Block; +import net.minecraft.core.util.phys.Vec3d; +import net.pedroricardo.commander.content.CommanderCommandManager; +import net.pedroricardo.commander.content.CommanderCommandSource; +import net.pedroricardo.commander.content.arguments.*; +import net.pedroricardo.commander.content.helpers.BlockCoordinates; + +@SuppressWarnings("unchecked") +public class SetBlockCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register((LiteralArgumentBuilder)LiteralArgumentBuilder.literal("setblock") + .requires(sourceStack -> ((CommanderCommandSource)sourceStack).hasAdmin()) + .then(RequiredArgumentBuilder.argument("pos", BlockCoordinatesArgumentType.blockCoordinates()) + .then(RequiredArgumentBuilder.argument("block", BlockArgumentType.block()) + .executes(c -> { + BlockCoordinates coordinates = c.getArgument("pos", BlockCoordinates.class); + Block block = c.getArgument("block", Block.class); + Vec3d sourceCoordinates = ((CommanderCommandSource)c.getSource()).getCoordinates(); + + ((CommanderCommandSource)c.getSource()).getSender().world.setBlockWithNotify(coordinates.getX(sourceCoordinates == null ? null : sourceCoordinates.xCoord), coordinates.getY(sourceCoordinates == null ? null : sourceCoordinates.yCoord - 1.6), coordinates.getZ(sourceCoordinates == null ? null : sourceCoordinates.zCoord), block.id); + + return CommanderCommandManager.SINGLE_SUCCESS; + }) + .then(RequiredArgumentBuilder.argument("metadata", IntegerArgumentType.integer(0, 255)) + .executes(c -> { + BlockCoordinates coordinates = c.getArgument("pos", BlockCoordinates.class); + Block block = c.getArgument("block", Block.class); + Vec3d sourceCoordinates = ((CommanderCommandSource)c.getSource()).getCoordinates(); + int metadata = c.getArgument("metadata", Integer.class); + + ((CommanderCommandSource)c.getSource()).getSender().world.setBlockAndMetadataWithNotify(coordinates.getX(sourceCoordinates == null ? null : sourceCoordinates.xCoord), coordinates.getY(sourceCoordinates == null ? null : sourceCoordinates.yCoord - 1.6), coordinates.getZ(sourceCoordinates == null ? null : sourceCoordinates.zCoord), block.id, metadata); + + return CommanderCommandManager.SINGLE_SUCCESS; + }))))); + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/commands/SummonCommand.java b/src/main/java/net/pedroricardo/commander/content/commands/SummonCommand.java new file mode 100644 index 0000000..7ad8d4d --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/commands/SummonCommand.java @@ -0,0 +1,49 @@ +package net.pedroricardo.commander.content.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import net.minecraft.core.entity.Entity; +import net.minecraft.core.world.World; +import net.pedroricardo.commander.content.CommanderCommandManager; +import net.pedroricardo.commander.content.CommanderCommandSource; +import net.pedroricardo.commander.content.arguments.EntitySummonArgumentType; +import net.pedroricardo.commander.content.arguments.Vec3dArgumentType; +import net.pedroricardo.commander.content.helpers.DoubleCoordinates; + +@SuppressWarnings("unchecked") +public class SummonCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register((LiteralArgumentBuilder)LiteralArgumentBuilder.literal("summon") + .requires(c -> ((CommanderCommandSource)c).hasAdmin()) + .then(RequiredArgumentBuilder.argument("entity", EntitySummonArgumentType.entity()) + .executes(c -> { + Class entityClass = c.getArgument("entity", Class.class); + Entity entity; + try { + entity = entityClass.getConstructor(World.class).newInstance(((CommanderCommandSource)c.getSource()).getSender().world); + } catch (Exception e) { + throw new RuntimeException(e); + } + entity.spawnInit(); + entity.moveTo(((CommanderCommandSource)c.getSource()).getSender().x, ((CommanderCommandSource)c.getSource()).getSender().y, ((CommanderCommandSource)c.getSource()).getSender().z, 0.0f, 0.0f); + ((CommanderCommandSource)c.getSource()).getSender().world.entityJoinedWorld(entity); + return CommanderCommandManager.SINGLE_SUCCESS; + }) + .then(RequiredArgumentBuilder.argument("pos", Vec3dArgumentType.vec3d()) + .executes(c -> { + Class entityClass = c.getArgument("entity", Class.class); + DoubleCoordinates coordinates = c.getArgument("pos", DoubleCoordinates.class); + Entity entity; + try { + entity = entityClass.getConstructor(World.class).newInstance(((CommanderCommandSource)c.getSource()).getSender().world); + } catch (Exception e) { + throw new RuntimeException(e); + } + entity.spawnInit(); + entity.moveTo(coordinates.getX(((CommanderCommandSource)c.getSource()).getSender().x), coordinates.getY(((CommanderCommandSource)c.getSource()).getSender().y), coordinates.getZ(((CommanderCommandSource)c.getSource()).getSender().z), 0.0f, 0.0f); + ((CommanderCommandSource)c.getSource()).getSender().world.entityJoinedWorld(entity); + return CommanderCommandManager.SINGLE_SUCCESS; + })))); + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/commands/TestCommand.java b/src/main/java/net/pedroricardo/commander/content/commands/TestCommand.java index 7563d0d..b931f94 100644 --- a/src/main/java/net/pedroricardo/commander/content/commands/TestCommand.java +++ b/src/main/java/net/pedroricardo/commander/content/commands/TestCommand.java @@ -3,17 +3,9 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import net.minecraft.core.achievement.Achievement; -import net.minecraft.core.entity.Entity; -import net.minecraft.core.entity.player.EntityPlayer; import net.pedroricardo.commander.content.CommanderCommandManager; import net.pedroricardo.commander.content.CommanderCommandSource; import net.pedroricardo.commander.content.arguments.AchievementArgumentType; -import net.pedroricardo.commander.content.arguments.EntityArgumentType; -import net.pedroricardo.commander.content.arguments.EntitySelector; - -import java.util.ArrayList; -import java.util.List; @SuppressWarnings("unchecked") public class TestCommand { diff --git a/src/main/java/net/pedroricardo/commander/content/exceptions/CommanderExceptions.java b/src/main/java/net/pedroricardo/commander/content/exceptions/CommanderExceptions.java new file mode 100644 index 0000000..2735809 --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/exceptions/CommanderExceptions.java @@ -0,0 +1,38 @@ +package net.pedroricardo.commander.content.exceptions; + +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import net.minecraft.core.lang.I18n; + +public class CommanderExceptions { + private static final SimpleCommandExceptionType INCOMPLETE_ARGUMENT = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.incomplete")); + private static final SimpleCommandExceptionType NOT_IN_WORLD = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.not_in_world")); + private static final SimpleCommandExceptionType INVALID_SELECTOR = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.entity.invalid_selector.generic")); + private static final SimpleCommandExceptionType SINGLE_ENTITY_ONLY = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.entity.invalid_selector.single_entity_only")); + private static final SimpleCommandExceptionType SINGLE_PLAYER_ONLY = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.entity.invalid_selector.single_player_only")); + private static final SimpleCommandExceptionType PLAYER_ONLY = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("argument_types.commander.entity.invalid_selector.player_only")); + + public static SimpleCommandExceptionType incomplete() { + return INCOMPLETE_ARGUMENT; + } + + public static SimpleCommandExceptionType notInWorld() { + return NOT_IN_WORLD; + } + + public static SimpleCommandExceptionType invalidSelector() { + return INVALID_SELECTOR; + } + + public static SimpleCommandExceptionType singleEntityOnly() { + return SINGLE_ENTITY_ONLY; + } + + public static SimpleCommandExceptionType singlePlayerOnly() { + return SINGLE_PLAYER_ONLY; + } + + public static SimpleCommandExceptionType playerOnly() { + return PLAYER_ONLY; + } + +} diff --git a/src/main/java/net/pedroricardo/commander/content/helpers/BlockCoordinate.java b/src/main/java/net/pedroricardo/commander/content/helpers/BlockCoordinate.java new file mode 100644 index 0000000..109b7a6 --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/helpers/BlockCoordinate.java @@ -0,0 +1,51 @@ +package net.pedroricardo.commander.content.helpers; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.pedroricardo.commander.content.exceptions.CommanderExceptions; +import org.jetbrains.annotations.Nullable; + +public class BlockCoordinate { + private final boolean isRelative; + private final int coordinate; + + public BlockCoordinate(boolean isRelative, int coordinate) { + this.isRelative = isRelative; + this.coordinate = coordinate; + } + + public int get(@Nullable Integer sourceCoordinate) throws CommandSyntaxException { + if (this.isRelative) { + if (sourceCoordinate != null) { + return Math.round(sourceCoordinate) + this.coordinate; + } else { + throw CommanderExceptions.notInWorld().create(); + } + } + + return this.coordinate; + } + + public static BlockCoordinate parse(StringReader reader) throws CommandSyntaxException { + if (!reader.canRead()) throw CommanderExceptions.incomplete().createWithContext(reader); + + if (reader.peek() == '~') { + reader.skip(); + if (reader.canRead() && reader.peek() != ' ') { + int coordinate = reader.readInt(); + return new BlockCoordinate(true, coordinate); + } else { + return new BlockCoordinate(true, 0); + } + } else if (reader.peek() != ' ') { + int coordinate = reader.readInt(); + return new BlockCoordinate(false, coordinate); + } + + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedInt().createWithContext(reader); + } + + public boolean isRelative() { + return this.isRelative; + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/helpers/BlockCoordinates.java b/src/main/java/net/pedroricardo/commander/content/helpers/BlockCoordinates.java new file mode 100644 index 0000000..2654ce4 --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/helpers/BlockCoordinates.java @@ -0,0 +1,47 @@ +package net.pedroricardo.commander.content.helpers; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import org.jetbrains.annotations.Nullable; + +public class BlockCoordinates { + private final BlockCoordinate x; + private final BlockCoordinate y; + private final BlockCoordinate z; + + public BlockCoordinates(BlockCoordinate x, BlockCoordinate y, BlockCoordinate z) { + this.x = x; + this.y = y; + this.z = z; + } + + public int getX(@Nullable Integer sourceX) throws CommandSyntaxException { + return this.x.get(sourceX); + } + + public int getY(@Nullable Integer sourceY) throws CommandSyntaxException { + return this.y.get(sourceY); + } + + public int getZ(@Nullable Integer sourceZ) throws CommandSyntaxException { + return this.z.get(sourceZ); + } + + public int getX(@Nullable Double sourceX) throws CommandSyntaxException { + if (sourceX == null) return this.x.get(null); + return this.x.get((int) Math.floor(sourceX)); + } + + public int getY(@Nullable Double sourceY) throws CommandSyntaxException { + if (sourceY == null) return this.y.get(null); + return this.y.get((int) Math.floor(sourceY)); + } + + public int getZ(@Nullable Double sourceZ) throws CommandSyntaxException { + if (sourceZ == null) return this.z.get(null); + return this.z.get((int) Math.floor(sourceZ)); + } + + public boolean hasRelativeCoordinate() { + return this.x.isRelative() || this.y.isRelative() || this.z.isRelative(); + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/helpers/DoubleCoordinate.java b/src/main/java/net/pedroricardo/commander/content/helpers/DoubleCoordinate.java new file mode 100644 index 0000000..bb6c30a --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/helpers/DoubleCoordinate.java @@ -0,0 +1,51 @@ +package net.pedroricardo.commander.content.helpers; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.pedroricardo.commander.Commander; +import net.pedroricardo.commander.content.exceptions.CommanderExceptions; + +public class DoubleCoordinate { + private final boolean isRelative; + private final double coordinate; + + public DoubleCoordinate(boolean isRelative, double coordinate) { + this.isRelative = isRelative; + this.coordinate = coordinate; + } + + public double get(Double sourceCoordinate) throws CommandSyntaxException { + if (this.isRelative) { + if (sourceCoordinate != null) { + return sourceCoordinate + this.coordinate; + } else { + throw CommanderExceptions.notInWorld().create(); + } + } + + return this.coordinate; + } + + public static DoubleCoordinate parse(StringReader reader) throws CommandSyntaxException { + if (!reader.canRead()) throw CommanderExceptions.incomplete().createWithContext(reader); + + if (reader.peek() == '~') { + reader.skip(); + if (reader.canRead() && reader.peek() != ' ') { + double coordinate = reader.readDouble(); + return new DoubleCoordinate(true, coordinate); + } else { + return new DoubleCoordinate(true, 0.0); + } + } else if (reader.peek() != ' ') { + double coordinate = reader.readDouble(); + return new DoubleCoordinate(false, coordinate); + } + + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedDouble().createWithContext(reader); + } + + public boolean isRelative() { + return this.isRelative; + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/helpers/DoubleCoordinates.java b/src/main/java/net/pedroricardo/commander/content/helpers/DoubleCoordinates.java new file mode 100644 index 0000000..73bcb13 --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/helpers/DoubleCoordinates.java @@ -0,0 +1,31 @@ +package net.pedroricardo.commander.content.helpers; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +public class DoubleCoordinates { + private final DoubleCoordinate x; + private final DoubleCoordinate y; + private final DoubleCoordinate z; + + public DoubleCoordinates(DoubleCoordinate x, DoubleCoordinate y, DoubleCoordinate z) { + this.x = x; + this.y = y; + this.z = z; + } + + public double getX(double sourceX) throws CommandSyntaxException { + return this.x.get(sourceX); + } + + public double getY(double sourceY) throws CommandSyntaxException { + return this.y.get(sourceY); + } + + public double getZ(double sourceZ) throws CommandSyntaxException { + return this.z.get(sourceZ); + } + + public boolean hasRelativeCoordinates() { + return this.x.isRelative() || this.y.isRelative() || this.z.isRelative(); + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelector.java b/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelector.java new file mode 100644 index 0000000..dc429f3 --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelector.java @@ -0,0 +1,61 @@ +package net.pedroricardo.commander.content.helpers; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.core.entity.Entity; +import net.pedroricardo.commander.content.CommanderCommandSource; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; + +public class EntitySelector { + private final int maxResults; + private final boolean includesEntities; + private final BiConsumer> order; + private final @Nullable Class limitToType; + private final boolean currentEntity; + + public EntitySelector(int maxResults, boolean includesEntities, BiConsumer> order, @Nullable Class limitToType, boolean currentEntity) { + this.maxResults = maxResults; + this.includesEntities = includesEntities; + this.order = order; + this.limitToType = limitToType; + this.currentEntity = currentEntity; + } + + public List get(CommanderCommandSource commandSource) throws CommandSyntaxException { + List entities; + if (this.includesEntities) { + entities = commandSource.getSender().world.loadedEntityList; + } else { + entities = commandSource.getSender().world.players; + } + if (limitToType != null) { + List temp = new ArrayList<>(entities); + for (Entity entity : entities) { + if (!limitToType.isInstance(entity)) { + temp.remove(entity); + } + } + entities = temp; + } + this.order.accept(commandSource.getSender(), entities); + if (this.maxResults < entities.size()) { + entities = entities.subList(0, this.maxResults); + } + return entities; + } + + public int getMaxResults() { + return this.maxResults; + } + + public boolean includesEntities() { + return this.includesEntities; + } + + public boolean isCurrentEntity() { + return this.currentEntity; + } +} diff --git a/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorParser.java b/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorParser.java new file mode 100644 index 0000000..0288914 --- /dev/null +++ b/src/main/java/net/pedroricardo/commander/content/helpers/EntitySelectorParser.java @@ -0,0 +1,116 @@ +package net.pedroricardo.commander.content.helpers; + +import com.google.common.primitives.Doubles; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.core.entity.Entity; +import net.minecraft.core.entity.player.EntityPlayer; +import net.pedroricardo.commander.content.exceptions.CommanderExceptions; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; + +public class EntitySelectorParser { + private final BiConsumer> ORDER_RANDOM = (sender, entities) -> Collections.shuffle(entities); + private final BiConsumer> ORDER_ARBITRARY = (sender, entities) -> {}; + private final BiConsumer> ORDER_CLOSEST = (sender, entities) -> entities.sort((firstEntity, secondEntity) -> Doubles.compare(firstEntity.distanceTo(sender), secondEntity.distanceTo(sender))); + private final BiConsumer> ORDER_FARTHEST = (sender, entities) -> entities.sort((firstEntity, secondEntity) -> Doubles.compare(secondEntity.distanceTo(sender), firstEntity.distanceTo(sender))); + + private final StringReader reader; + + private int maxResults; + private boolean includesEntities; + private BiConsumer> order; + private @Nullable Class limitToType; + private boolean currentEntity; + + public EntitySelectorParser(StringReader reader) { + this.reader = reader; + } + + private void parseSelector() throws CommandSyntaxException { + if (!this.reader.canRead()) throw CommanderExceptions.invalidSelector().createWithContext(this.reader); + + switch (this.reader.peek()) { + case 'p': + this.maxResults = 1; + this.includesEntities = false; + this.order = ORDER_CLOSEST; + this.limitToType = EntityPlayer.class; + this.currentEntity = false; + break; + case 'a': + this.maxResults = Integer.MAX_VALUE; + this.includesEntities = false; + this.order = ORDER_ARBITRARY; + this.limitToType = EntityPlayer.class; + this.currentEntity = false; + break; + case 'r': + this.maxResults = 1; + this.includesEntities = false; + this.order = ORDER_RANDOM; + this.limitToType = EntityPlayer.class; + this.currentEntity = false; + break; + case 's': + this.maxResults = 1; + this.includesEntities = true; + this.order = ORDER_ARBITRARY; + this.limitToType = EntityPlayer.class; + this.currentEntity = true; + break; + case 'e': + this.maxResults = Integer.MAX_VALUE; + this.includesEntities = true; + this.order = ORDER_ARBITRARY; + this.currentEntity = false; + break; + } + reader.skip(); + // Temporary; replace later with selectors (e.g. @e[type=Sheep,name="the sheep"]) + if (reader.canRead() && reader.peek() != ' ') { + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(this.reader); + } + } + + public EntitySelector parse() throws CommandSyntaxException { + if (this.reader.canRead() && this.reader.peek() == '@') { + this.reader.skip(); + this.parseSelector(); + } else { + // Temporary; change to allow entity names/class instance IDs + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(this.reader); + } + return new EntitySelector(this.maxResults, this.includesEntities, this.order, this.limitToType, this.currentEntity); + +// if (reader.canRead()) { +// switch (reader.peek()) { +// case 'a': +// reader.skip(); +// if (singleEntity) { +// throw new CommandSyntaxException(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException(), () -> I18n.getInstance().translateKey("argument_types.commander.entity.invalid_selector.single_entity")); +// } +// return new EntitySelector(singleEntity, playerOnly, EntitySelector.EntitySelectors.ALL_PLAYERS); +// case 'e': +// reader.skip(); +// if (playerOnly) { +// throw new CommandSyntaxException(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException(), () -> I18n.getInstance().translateKey("argument_types.commander.entity.invalid_selector.player_only")); +// } +// return new EntitySelector(singleEntity, playerOnly, EntitySelector.EntitySelectors.ALL_ENTITIES); +// case 'r': +// reader.skip(); +// return new EntitySelector(singleEntity, playerOnly, EntitySelector.EntitySelectors.RANDOM_PLAYER); +// case 'p': +// reader.skip(); +// return new EntitySelector(singleEntity, playerOnly, EntitySelector.EntitySelectors.CLOSEST_PLAYER); +// case 's': +// reader.skip(); +// return new EntitySelector(singleEntity, playerOnly, EntitySelector.EntitySelectors.SELF); +// } +// } +// throw new CommandSyntaxException(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument(), () -> I18n.getInstance().translateKey("argument_types.commander.entity.invalid_selector.generic")); + } +} diff --git a/src/main/java/net/pedroricardo/commander/gui/GuiChatSuggestions.java b/src/main/java/net/pedroricardo/commander/gui/GuiChatSuggestions.java index c5a96f7..d0d28dc 100644 --- a/src/main/java/net/pedroricardo/commander/gui/GuiChatSuggestions.java +++ b/src/main/java/net/pedroricardo/commander/gui/GuiChatSuggestions.java @@ -8,6 +8,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.GuiChat; +import net.minecraft.client.gui.GuiTooltip; import net.minecraft.client.gui.text.TextFieldEditor; import net.minecraft.client.render.FontRenderer; import net.pedroricardo.commander.*; @@ -36,6 +37,7 @@ public class GuiChatSuggestions extends Gui { private int tablessCursor; private List suggestions = new ArrayList<>(); private int scroll = 0; + GuiTooltip tooltip; public GuiChatSuggestions(Minecraft mc, TextFieldEditor textFieldEditor, GuiChat chat) { this.mc = mc; @@ -45,6 +47,7 @@ public GuiChatSuggestions(Minecraft mc, TextFieldEditor textFieldEditor, GuiChat this.chat = chat; this.tablessMessage = this.chat.getText(); this.tablessCursor = this.editor.getCursor(); + this.tooltip = new GuiTooltip(this.mc); } public void drawScreen() { @@ -52,11 +55,13 @@ public void drawScreen() { this.renderSuggestions(this.fontRenderer, this.tablessMessage, this.tablessCursor); } else if (this.parseResults != null) { if (!this.parseResults.getExceptions().isEmpty()) { + int i = 0; for (Exception e : this.parseResults.getExceptions().values()) { - this.renderSingleSuggestionLine(this.mc.fontRenderer, "§e" + e.getMessage()); + this.renderSingleSuggestionLine(this.mc.fontRenderer, "§e" + e.getMessage(), i); + i++; } } else if (CommanderCommandManager.getParseException(this.parseResults) != null) { - this.renderSingleSuggestionLine(this.mc.fontRenderer, "§e" + CommanderCommandManager.getParseException(this.parseResults).getMessage()); + this.renderSingleSuggestionLine(this.mc.fontRenderer, "§e" + CommanderCommandManager.getParseException(this.parseResults).getMessage(), 0); } } } @@ -91,10 +96,14 @@ private void renderSuggestions(FontRenderer fontRenderer, String message, int cu } fontRenderer.drawStringWithShadow(colorCode + suggestionText, leftMargin + 1, height - suggestionHeight, 0xE0E0E0); } + + if (this.isHoveringOverSuggestions(mouseX, mouseY) && this.suggestions.get(this.getIndexOfSuggestionBeingHoveredOver(mouseX, mouseY)).getTooltip() != null) { + this.tooltip.render(this.suggestions.get(this.getIndexOfSuggestionBeingHoveredOver(mouseX, mouseY)).getTooltip().getString(), mouseX, mouseY, 0, 0); + } } - private void renderSingleSuggestionLine(FontRenderer fontRenderer, String text) { - int height = this.mc.resolution.scaledHeight; + private void renderSingleSuggestionLine(FontRenderer fontRenderer, String text, int index) { + int height = this.mc.resolution.scaledHeight - index * 12; int leftMargin = 2; int stringWidth = fontRenderer.getStringWidth(text); @@ -146,7 +155,7 @@ private void updateSuggestions() { public void updateScreen(int dWheel) { int cursorX = GuiHelper.getScaledMouseX(this.mc); int cursorY = GuiHelper.getScaledMouseY(this.mc); - if (isHoveringOverSuggestions(cursorX, cursorY) && dWheel != 0) { + if (this.isHoveringOverSuggestions(cursorX, cursorY) && dWheel != 0) { this.scroll(Math.round(Math.signum(dWheel)) * -1); } } diff --git a/src/main/resources/lang/commander/en_US.lang b/src/main/resources/lang/commander/en_US.lang index 389d3d8..7e7f04c 100644 --- a/src/main/resources/lang/commander/en_US.lang +++ b/src/main/resources/lang/commander/en_US.lang @@ -1,2 +1,14 @@ -commands.commander.achievement.invalid_achievement=Invalid achievement: %s -commands.commander.achievement.invalid_entity=Invalid entity: %s \ No newline at end of file +argument_types.commander.achievement.invalid_achievement=Invalid achievement ID +argument_types.commander.entity_summon.invalid_entity=Invalid entity ID +argument_types.commander.block.invalid_block=Invalid block ID +argument_types.commander.entity.invalid_selector.single_player_only=Selector includes more than one entity, but command only accepts a single player as an argument +argument_types.commander.entity.invalid_selector.single_entity_only=Selector includes more than one entity, but command only accepts a single entity as an argument +argument_types.commander.entity.invalid_selector.player_only=Selector includes non-player entities, but command only accepts players as an argument +argument_types.commander.entity.invalid_selector.generic=Invalid selector +argument_types.commander.entity.selector.nearest_player=Nearest player +argument_types.commander.entity.selector.all_players=All players +argument_types.commander.entity.selector.random_player=Random player +argument_types.commander.entity.selector.self=Current entity +argument_types.commander.entity.selector.all_entities=All entities +argument_types.commander.incomplete=Incomplete argument +argument_types.commander.not_in_world=Argument requires sender to be in world, but they are not present \ No newline at end of file