diff --git a/Minebot/src/net/famzangl/minecraft/minebot/ai/command/ArgumentDefinition.java b/Minebot/src/net/famzangl/minecraft/minebot/ai/command/ArgumentDefinition.java index 04338e1b..468da3dd 100644 --- a/Minebot/src/net/famzangl/minecraft/minebot/ai/command/ArgumentDefinition.java +++ b/Minebot/src/net/famzangl/minecraft/minebot/ai/command/ArgumentDefinition.java @@ -17,6 +17,7 @@ package net.famzangl.minecraft.minebot.ai.command; import java.util.Collection; +import java.util.List; /** * This is the definition of a single argument to be passed on the command line. @@ -42,10 +43,20 @@ public ArgumentDefinition(String descriptionType, String descriptionInfo) { this.descriptionInfo = descriptionInfo; } + public void getTabCompleteOptions(List previousArguments, String currentStart, + Collection addTo) { + getTabCompleteOptions(currentStart, addTo); + } + public void getTabCompleteOptions(String currentStart, Collection addTo) { } + + public boolean couldEvaluateAgainst(List previousArguments, String string) { + return couldEvaluateAgainst(string); + } + /** * Check if this argument could be from that parameter. Checks e.g. for * integer values. diff --git a/Minebot/src/net/famzangl/minecraft/minebot/ai/command/BlockStateNameBuilder.java b/Minebot/src/net/famzangl/minecraft/minebot/ai/command/BlockStateNameBuilder.java new file mode 100644 index 00000000..445c2941 --- /dev/null +++ b/Minebot/src/net/famzangl/minecraft/minebot/ai/command/BlockStateNameBuilder.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * This file is part of Minebot. + * + * Minebot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Minebot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Minebot. If not, see . + *******************************************************************************/ +package net.famzangl.minecraft.minebot.ai.command; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import net.famzangl.minecraft.minebot.ai.AIHelper; +import net.famzangl.minecraft.minebot.ai.command.AICommandParameter.BlockFilter; +import net.minecraft.block.Block; +import net.minecraft.block.properties.IProperty; +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandFill; +import net.minecraft.command.InvalidBlockStateException; +import net.minecraft.command.NumberInvalidException; + +public class BlockStateNameBuilder extends ParameterBuilder { + + + private static final String DEFAULT_STATE = "default"; + + public BlockStateNameBuilder(AICommandParameter annot) { + super(annot); + } + + @Override + public void addArguments(ArrayList list) { + Class blockFilterClass = annot.blockFilter(); + list.add(new ArgumentDefinition("Block", annot.description()) { + private final BlockFilter blockFilter; + { + BlockFilter blockFilter; + try { + blockFilter = blockFilterClass.newInstance(); + } catch (InstantiationException e) { + e.printStackTrace(); + blockFilter = new AICommandParameter.AnyBlockFilter(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + blockFilter = new AICommandParameter.AnyBlockFilter(); + } + this.blockFilter = blockFilter; + } + + @Override + public boolean couldEvaluateAgainst(String string) { + try { + Block block = CommandBase.getBlockByText(null, string); + return blockFilter.matches(new BlockWithDontcare(block)); + } catch (NumberInvalidException e) { + return false; + } + } + + @Override + public void getTabCompleteOptions(String currentStart, + Collection addTo) { + Block.REGISTRY.getKeys() + .stream() + .filter(name -> blockFilter.matches(new BlockWithDontcare(Block.REGISTRY.getObject(name)))) + .map(Object::toString) + .filter(name -> name.startsWith(currentStart)) + .forEach(addTo::add); + } + }); + list.add(new ArgumentDefinition("Meta", "Meta value for that block, '" + DEFAULT_STATE + "' for default") { + + public boolean couldEvaluateAgainst(List previousArguments, String string) { + if (DEFAULT_STATE.equals(string)) { + return true; + } + try { + Block block = CommandBase.getBlockByText(null, previousArguments.get(previousArguments.size() - 1)); + CommandFill.convertArgToBlockState(block, string); + return true; + } catch (NumberInvalidException | InvalidBlockStateException e) { + return false; + } + + }; + + @Override + public void getTabCompleteOptions(List previousArguments, String currentStart, + Collection addTo) { + if (DEFAULT_STATE.startsWith(currentStart)) { + addTo.add(DEFAULT_STATE); + } + + try { + Block block = CommandBase.getBlockByText(null, previousArguments.get(previousArguments.size() - 1)); + + Matcher inValuePart = Pattern.compile("^(.*,([^,=]*)=)([^=,]*)$").matcher(currentStart); + if (inValuePart.matches()) { + String key = inValuePart.group("key"); + String valueStart = inValuePart.group("value"); + + IProperty property = block.getBlockState().getProperty(key); + if (property != null) { + propertyNames(property) + .filter(name -> name.startsWith(valueStart)) + .forEach(name -> addTo.add(inValuePart.group("start") + name)); + } + + } else { + Matcher inKeyPart = Pattern.compile("^(.*,|)([^,=]*)$").matcher(currentStart); + if (inKeyPart.matches()) { + String keyStart = inValuePart.group("key"); + + block.getBlockState().getProperties().stream() + .map(p -> p.getName()) + .filter(name -> name.startsWith(keyStart)) + .forEach(name -> addTo.add(inKeyPart.group("start") + name)); + } + } + } catch (NumberInvalidException e) { + //ignore + } + } + + private > Stream propertyNames(IProperty property) { + return property.getAllowedValues() + .stream() + .map(value -> property.getName(value)); + } + }); + + } + + @Override + public Object getParameter(AIHelper helper, String[] arguments) { + try { + Block block = CommandBase.getBlockByText(null, arguments[0]); + if (DEFAULT_STATE.equals(arguments[1])) { + return block.getDefaultState(); + } else { + return CommandFill.convertArgToBlockState(block, arguments[1]); + } + + } catch (NumberInvalidException e) { + // Should not happen - we have an arg def for this + throw new RuntimeException(e); + } catch (InvalidBlockStateException e) { + // Should not happen - we have an arg def for this + throw new RuntimeException(e); + } + } + +} diff --git a/Minebot/src/net/famzangl/minecraft/minebot/ai/command/CommandDefinition.java b/Minebot/src/net/famzangl/minecraft/minebot/ai/command/CommandDefinition.java index a115f6d9..b46b1559 100644 --- a/Minebot/src/net/famzangl/minecraft/minebot/ai/command/CommandDefinition.java +++ b/Minebot/src/net/famzangl/minecraft/minebot/ai/command/CommandDefinition.java @@ -103,6 +103,9 @@ private static ParameterBuilder getParameter(Method method, case STRING: builder = new StringNameBuilder(annot); break; + case BLOCK_STATE: + builder = new BlockStateNameBuilder(annot); + break; default: throw new IllegalArgumentException("Unknown type: " + annot.type()); @@ -210,7 +213,7 @@ public boolean couldEvaluateStartingWith(String[] arguments2) { return false; } for (int i = 0; i < arguments2.length; i++) { - if (!arguments.get(i).couldEvaluateAgainst(arguments2[i])) { + if (!arguments.get(i).couldEvaluateAgainst(Arrays.asList(arguments2).subList(0, i), arguments2[i])) { return false; } } diff --git a/Minebot/src/net/famzangl/minecraft/minebot/ai/command/ParameterType.java b/Minebot/src/net/famzangl/minecraft/minebot/ai/command/ParameterType.java index b95cee86..226ae3ff 100644 --- a/Minebot/src/net/famzangl/minecraft/minebot/ai/command/ParameterType.java +++ b/Minebot/src/net/famzangl/minecraft/minebot/ai/command/ParameterType.java @@ -26,5 +26,6 @@ public enum ParameterType { FILE, POSITION, ENUM, - STRING + STRING, + BLOCK_STATE } diff --git a/Minebot/src/net/famzangl/minecraft/minebot/build/blockbuild/BuildNormalStairsTask.java b/Minebot/src/net/famzangl/minecraft/minebot/build/blockbuild/BuildNormalStairsTask.java index 7de2d1cf..87d893a1 100644 --- a/Minebot/src/net/famzangl/minecraft/minebot/build/blockbuild/BuildNormalStairsTask.java +++ b/Minebot/src/net/famzangl/minecraft/minebot/build/blockbuild/BuildNormalStairsTask.java @@ -24,6 +24,7 @@ import net.famzangl.minecraft.minebot.ai.task.place.SneakAndPlaceAtSideTask; import net.minecraft.block.Block; import net.minecraft.block.BlockStairs.EnumHalf; +import net.minecraft.command.CommandFill; import net.minecraft.init.Blocks; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; diff --git a/Minebot/src/net/famzangl/minecraft/minebot/build/commands/CommandScheduleBuild.java b/Minebot/src/net/famzangl/minecraft/minebot/build/commands/CommandScheduleBuild.java index 40c9891e..cb81499f 100644 --- a/Minebot/src/net/famzangl/minecraft/minebot/build/commands/CommandScheduleBuild.java +++ b/Minebot/src/net/famzangl/minecraft/minebot/build/commands/CommandScheduleBuild.java @@ -35,7 +35,9 @@ import net.famzangl.minecraft.minebot.build.blockbuild.SlabBuildTask; import net.famzangl.minecraft.minebot.build.blockbuild.StandingSignBuildTask; import net.famzangl.minecraft.minebot.build.blockbuild.StandingSignBuildTask.SignDirection; +import net.minecraft.block.BlockStairs; import net.minecraft.block.BlockStairs.EnumHalf; +import net.minecraft.block.state.IBlockState; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; @@ -56,7 +58,7 @@ private ScheduleTaskStrategy(BuildTask task) { @Override protected void singleRun(AIHelper helper) { - addTask(helper, task); + helper.buildManager.addTask(task); } } @@ -88,6 +90,22 @@ public static AIStrategy runSimple( return new ScheduleTaskStrategy(task); } + + @AICommandInvocation() + public static AIStrategy runSimple( + AIHelper helper, + @AICommandParameter(type = ParameterType.FIXED, fixedName = "schedule", description = "") String nameArg, + @AICommandParameter(type = ParameterType.POSITION, description = "Where to place it (relative is to your current pos)") BlockPos forPosition, + @AICommandParameter(type = ParameterType.BLOCK_STATE, description = "The block", blockFilter=StairsBlockFilter.class) IBlockState blockToPlace) { + BuildNormalStairsTask task; + if (BuildNormalStairsTask.BLOCKS.contains(blockToPlace)) { + task = new BuildNormalStairsTask(forPosition, blockToPlace.getBlock(), blockToPlace.getValue(BlockStairs.FACING), blockToPlace.getValue(BlockStairs.HALF)); + } else { + throw new CommandEvaluationException("Cannot build " + blockToPlace); + } + return new ScheduleTaskStrategy(task); + } + // public static final class RunColoredFilter extends BlockFilter { // @Override // public boolean matches(Block b) { @@ -184,21 +202,21 @@ public boolean matches(BlockWithDataOrDontcare b) { // TODO: Merge with Factory - @AICommandInvocation() - public static AIStrategy run(AIHelper helper, - @AICommandParameter(type = ParameterType.FIXED, fixedName = "schedule", description = "") String nameArg, - @AICommandParameter(type = ParameterType.POSITION, description = "Where to place it (relative is to your current pos)") BlockPos forPosition, - @AICommandParameter(type = ParameterType.BLOCK_NAME, description = "The block", blockFilter = StairsBlockFilter.class) BlockWithDataOrDontcare blockToPlace, - @AICommandParameter(type = ParameterType.ENUM, description = "The direction the stairs face") EnumFacing direction, - @AICommandParameter(type = ParameterType.ENUM, description = "Upper for inverted stairs", optional = true) EnumHalf half) { - if (BuildNormalStairsTask.BLOCKS.contains(blockToPlace)) { - addTask(helper, - new BuildNormalStairsTask(forPosition, blockToPlace.getBlock(), direction, half == null ? EnumHalf.BOTTOM : half)); - } else { - throw new CommandEvaluationException("Cannot build " + blockToPlace); - } - return null; - } +// @AICommandInvocation() +// public static AIStrategy run(AIHelper helper, +// @AICommandParameter(type = ParameterType.FIXED, fixedName = "schedule", description = "") String nameArg, +// @AICommandParameter(type = ParameterType.POSITION, description = "Where to place it (relative is to your current pos)") BlockPos forPosition, +// @AICommandParameter(type = ParameterType.BLOCK_NAME, description = "The block", blockFilter = StairsBlockFilter.class) BlockWithDataOrDontcare blockToPlace, +// @AICommandParameter(type = ParameterType.ENUM, description = "The direction the stairs face") EnumFacing direction, +// @AICommandParameter(type = ParameterType.ENUM, description = "Upper for inverted stairs", optional = true) EnumHalf half) { +// if (BuildNormalStairsTask.BLOCKS.contains(blockToPlace)) { +// addTask(helper, +// new BuildNormalStairsTask(forPosition, blockToPlace.getBlock(), direction, half == null ? EnumHalf.BOTTOM : half)); +// } else { +// throw new CommandEvaluationException("Cannot build " + blockToPlace); +// } +// return null; +// } public static final class SignBlockFilter extends BlockFilter { @Override @@ -226,8 +244,4 @@ public static AIStrategy run( throw new CommandEvaluationException("Cannot build " + blockToPlace); } } - - private static void addTask(AIHelper helper, BuildTask blockBuildTask) { - helper.buildManager.addTask(blockBuildTask); - } }