Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,11 @@ Fake players can be spawned using the `/dummy` command. Dummies won't save or lo
* `dummy <name> sneak [true|false]`: makes the dummy hold shift or un-shift (not the same as currently crouching)
* `dummy <name> sprint [true|false]`: makes the dummy sprint or un-sprint
* `dummy <name> drop [all]`: makes the dummy drop the current mainhand, either one item or the entire stack
* `dummy <name> drop from <slot> [all]`: makes the dummy drop from an inventory slot, either one item or the entire stack
* `dummy <name> swap`: makes the dummy swap its mainhand and offhand
* `dummy <name> selectslot`: makes the dummy select a different hotbar slot
* `dummy <name> use item`: makes the dummy use its hand item, either mainhand or offhand
* `dummy <name> selectslot <slot>`: makes the dummy select a different hotbar slot, from `1` to `9`
* `dummy <name> mainhand <slot>`: makes the dummy select a different hotbar slot, from `0` to `8`
* `dummy <name> use [item]`: makes the dummy use its hand item, either mainhand or offhand
* `dummy <name> use block <pos> [<direction>]`: makes the dummy use its hand item on a block position
* `dummy <name> use entity <entity>`: makes the dummy use its hand item on an entity
* `dummy <name> attack <entity>`: makes the dummy attack an entity with its mainhand
Expand Down
37 changes: 32 additions & 5 deletions src/main/java/io/github/misode/packtest/commands/DummyCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.commands.arguments.SlotArgument;
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import net.minecraft.commands.arguments.coordinates.Vec3Argument;
import net.minecraft.commands.arguments.selector.EntitySelector;
Expand All @@ -26,7 +27,9 @@
import net.minecraft.server.players.PlayerList;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.util.Prediction;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.SlotAccess;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.SwingAnimation;
import net.minecraft.world.phys.BlockHitResult;
Expand Down Expand Up @@ -104,13 +107,22 @@ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
.then(literal("drop")
.executes(ctx -> dropMainhand(ctx, false))
.then(literal("all")
.executes(ctx -> dropMainhand(ctx, true))))
.executes(ctx -> dropMainhand(ctx, true)))
.then(literal("from")
.then(argument("slot", SlotArgument.slot())
.executes(ctx -> dropFromSlot(ctx, false))
.then(literal("all")
.executes(ctx -> dropFromSlot(ctx, true))))))
.then(literal("swap")
.executes(DummyCommand::swap))
.then(literal("selectslot")
.then(argument("slot", IntegerArgumentType.integer(1, 9))
.executes(DummyCommand::selectSlot)))
.executes(ctx -> selectSlot(ctx, 1))))
.then(literal("mainhand")
.then(argument("slot", IntegerArgumentType.integer(0, 8))
.executes(ctx -> selectSlot(ctx, 0))))
.then(literal("use")
.executes(DummyCommand::useItem)
.then(literal("item")
.executes(DummyCommand::useItem))
.then(literal("block")
Expand Down Expand Up @@ -224,13 +236,28 @@ private static int swap(CommandContext<CommandSourceStack> ctx) throws CommandSy
return 1;
}

private static int selectSlot(CommandContext<CommandSourceStack> ctx) throws CommandSyntaxException {
private static int dropFromSlot(CommandContext<CommandSourceStack> ctx, boolean stack) throws CommandSyntaxException {
Dummy dummy = getDummy(ctx);
int slot = SlotArgument.getSlot(ctx, "slot");
SlotAccess access = dummy.getSlot(slot);
if (access == null) {
return 0;
}
ItemStack current = access.get();
ItemStack removed = current.split(stack ? current.count() : 1);
access.set(current);
dummy.containerMenu.findSlot(dummy.getInventory(), slot).ifPresent(i -> dummy.containerMenu.setRemoteSlot(i, access.get()));
dummy.drop(removed, true, Prediction.PREDICTED);
return removed.count();
}

private static int selectSlot(CommandContext<CommandSourceStack> ctx, int first) throws CommandSyntaxException {
Dummy dummy = getDummy(ctx);
int slot = IntegerArgumentType.getInteger(ctx, "slot");
if (dummy.getInventory().getSelectedSlot() == slot - 1) {
if (dummy.getInventory().getSelectedSlot() == slot - first) {
throw ERROR_SLOT_SELECTED.create(dummy.getUsername(), slot);
}
dummy.getInventory().setSelectedSlot(slot - 1);
dummy.getInventory().setSelectedSlot(slot - first);
return 1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.NbtPathArgument;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.commands.ArgProvider;
import net.minecraft.server.commands.data.DataAccessor;
import net.minecraft.server.commands.data.DataCommands;

import java.util.List;
import java.util.Objects;

class DataAssertion implements Assertion {
@Override
public void attach(
Expand All @@ -34,9 +38,49 @@ private static int run(CommandContext<CommandSourceStack> context, Mode mode, Ar
DataAccessor accessor = provider.access(context);
CompoundTag data = accessor.getData();

return AssertResult.of(path.countMatching(data), negated -> Component.literal(!negated
? String.format("Expected data at %s, found %s", path.asString(), data)
: String.format("Did not expect data at %s", path.asString())));
return AssertResult.of(path.countMatching(data), negated -> negated
? Component.literal(String.format("Did not expect data at %s", path.asString()))
: mismatch(path, data));
});
}

private static Component mismatch(NbtPathArgument.NbtPath path, Tag data) {
String raw = path.asString();
List<Tag> found = List.of(data);
String reached = "";

for (NbtPathArgument.Node node : path.nodes) {
List<Tag> next = node.get(found);
if (next.isEmpty()) {
return describe(raw, found, reached, node);
}
found = next;
reached = raw.substring(0, path.nodeToOriginalPosition.getInt(node));
}

return describe(raw, found, reached);
}

private static Component describe(String raw, List<Tag> found, String at) {
String value = found.size() == 1 ? found.getFirst().toString() : found.toString();

return Component.literal(at.isEmpty()
? String.format("Expected data at %s, found %s", raw, value)
: String.format("Expected data at %s, found %s at %s", raw, value, at));
}

private static Component describe(String raw, List<Tag> found, String reached, NbtPathArgument.Node unmatched) {
if (!(unmatched instanceof NbtPathArgument.MatchObjectNode filter)) {
return describe(raw, found, reached);
}

List<Tag> filtered = found.stream()
.map(tag -> tag instanceof CompoundTag compound ? compound.get(filter.name) : null)
.filter(Objects::nonNull)
.toList();

return filtered.isEmpty()
? describe(raw, found, reached)
: describe(raw, filtered, reached.isEmpty() ? filter.name : reached + "." + filter.name);
}
}
5 changes: 5 additions & 0 deletions src/main/resources/packtest.accesswidener
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
accessWidener v2 official

accessible class net/minecraft/core/MappedRegistry$TagSet
accessible class net/minecraft/commands/arguments/NbtPathArgument$Node
accessible class net/minecraft/commands/arguments/NbtPathArgument$MatchObjectNode
accessible field net/minecraft/commands/arguments/NbtPathArgument$MatchObjectNode name Ljava/lang/String;
accessible field net/minecraft/commands/arguments/NbtPathArgument$NbtPath nodes [Lnet/minecraft/commands/arguments/NbtPathArgument$Node;
accessible field net/minecraft/commands/arguments/NbtPathArgument$NbtPath nodeToOriginalPosition Lit/unimi/dsi/fastutil/objects/Object2IntMap;
accessible field net/minecraft/commands/arguments/selector/EntitySelector playerName Ljava/lang/String;
accessible field net/minecraft/gametest/framework/GameTestHelper testInfo Lnet/minecraft/gametest/framework/GameTestInfo;
accessible method net/minecraft/server/commands/ExecuteCommand checkCustomPredicate (Lnet/minecraft/commands/CommandSourceStack;Lnet/minecraft/core/Holder;)Z
Expand Down
Loading