Skip to content

Commit

Permalink
Add entities with inventories to /cfinditem. Also prevent multiple …
Browse files Browse the repository at this point in the history
…`/cfinditem` tasks from occurring at once or with a `/cfindblock` task. Closes #582
  • Loading branch information
Earthcomputer committed May 29, 2024
1 parent 6f9d6a9 commit 316afb6
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.entity.Entity;

import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -75,6 +76,10 @@ public static Component getGlowCoordsTextComponent(MutableComponent component, B
return getCommandTextComponent(component, String.format("/cglow block %d %d %d 10", pos.getX(), pos.getY(), pos.getZ()));
}

public static Component getGlowEntityTextComponent(MutableComponent component, Entity entity) {
return getCommandTextComponent(component, "/cglow entities " + entity.getStringUUID());
}

public static Component getCommandTextComponent(@Translatable String translationKey, String command) {
return getCommandTextComponent(Component.translatable(translationKey), command);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Either;
import net.earthcomputer.clientcommands.features.ClientcommandsDataQueryHandler;
import net.earthcomputer.clientcommands.util.CUtil;
import net.earthcomputer.clientcommands.util.GuiBlocker;
import net.earthcomputer.clientcommands.util.MathUtil;
import net.earthcomputer.clientcommands.command.arguments.WithStringArgument;
Expand All @@ -31,8 +33,10 @@
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.animal.horse.AbstractChestedHorse;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.vehicle.ContainerEntity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
Expand All @@ -48,6 +52,7 @@
import net.minecraft.world.level.block.state.properties.ChestType;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
Expand All @@ -56,6 +61,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.function.Predicate;

import static dev.xpple.clientarguments.arguments.CItemPredicateArgument.*;
Expand Down Expand Up @@ -104,6 +110,8 @@ private static SimpleTask makeFindItemsTask(String searchingForName, Predicate<I
}

private static abstract class AbstractFindItemsTask extends SimpleTask {
private static final Set<Object> MUTEX_KEYS = Set.of(TaskManager.INTENSIVE_TASK_MUTEX);

protected final String searchingForName;
protected final Predicate<ItemStack> searchingFor;
protected final boolean searchShulkerBoxes;
Expand Down Expand Up @@ -145,23 +153,41 @@ protected int countItems(ListTag inventory) {
return result;
}

protected void printEntityLocation(Entity entity, int count) {
sendFeedback(Component.translatable("commands.cfinditem.match.entity.left", count, searchingForName, entity.getName())
.append(getLookCoordsTextComponent(BlockPos.containing(entity.position())))
.append(Component.translatable("commands.cfinditem.match.entity.right", count, searchingForName, entity.getName()))
.append(" ")
.append(getGlowEntityTextComponent(Component.translatable("commands.cfindblock.success.glow"), entity)));
}

protected void printLocation(BlockPos pos, int count) {
sendFeedback(Component.translatable("commands.cfinditem.match.left", count, searchingForName)
.append(getLookCoordsTextComponent(pos))
.append(Component.translatable("commands.cfinditem.match.right", count, searchingForName))
.append(" ")
.append(getGlowCoordsTextComponent(Component.translatable("commands.cfindblock.success.glow"), pos))
.append(Component.translatable("commands.cfinditem.match.right", count, searchingForName)));
.append(getGlowCoordsTextComponent(Component.translatable("commands.cfindblock.success.glow"), pos)));
}

protected boolean canSearchEntity(Entity entity) {
return entity instanceof ContainerEntity || entity instanceof AbstractChestedHorse;
}

@Override
public void onCompleted() {
Minecraft.getInstance().gui.getChat().addMessage(Component.translatable("commands.cfinditem.total", totalFound, searchingForName).withStyle(ChatFormatting.BOLD));
}

@Override
public Set<Object> getMutexKeys() {
return MUTEX_KEYS;
}
}

private static class ClickInventoriesFindItemsTask extends AbstractFindItemsTask {
private final Set<BlockPos> searchedBlocks = new HashSet<>();
private BlockPos currentlySearching = null;
private final Set<UUID> searchedEntities = new HashSet<>();
private Either<BlockPos, Entity> currentlySearching = null;
private int currentlySearchingTimeout;
private boolean hasSearchedEnderChest = false;

Expand Down Expand Up @@ -195,6 +221,17 @@ protected void onTick() {
return;
}
Vec3 origin = entity.getEyePosition(0);

double entityReachDistance = player.entityInteractionRange();
for (Entity entityToSearch : level.getEntities(player, AABB.ofSize(origin, entityReachDistance, entityReachDistance, entityReachDistance), this::canSearchEntity)) {
Vec3 closestPos = MathUtil.getClosestPoint(entityToSearch.getBoundingBox(), origin);
if (closestPos.distanceToSqr(origin) < entityReachDistance * entityReachDistance && searchedEntities.add(entityToSearch.getUUID())) {
startSearch(Either.right(entityToSearch), origin, closestPos);
scheduleDelay();
return;
}
}

double reachDistance = player.blockInteractionRange();
int minX = Mth.floor(origin.x - reachDistance);
int minY = Mth.floor(origin.y - reachDistance);
Expand Down Expand Up @@ -229,7 +266,7 @@ protected void onTick() {
searchedBlocks.add(offsetPos);
}
}
startSearch(pos, origin, closestPos);
startSearch(Either.left(pos), origin, closestPos);
scheduleDelay();
return;
}
Expand Down Expand Up @@ -259,9 +296,9 @@ private boolean canSearch(Level level, BlockPos pos) {
return true;
}

private void startSearch(BlockPos pos, Vec3 cameraPos, Vec3 clickPos) {
private void startSearch(Either<BlockPos, Entity> thing, Vec3 cameraPos, Vec3 clickPos) {
Minecraft mc = Minecraft.getInstance();
currentlySearching = pos;
currentlySearching = thing;
currentlySearchingTimeout = 100;
GuiBlocker.addBlocker(new GuiBlocker() {
@Override
Expand Down Expand Up @@ -311,7 +348,12 @@ public void initializeContents(int revision, List<ItemStack> stacks, ItemStack c
}
}
if (matchingItems > 0) {
printLocation(currentlySearching, matchingItems);
int matchingItems_f = matchingItems;
CUtil.forEither(
currentlySearching,
pos -> printLocation(pos, matchingItems_f),
entity -> printEntityLocation(entity, matchingItems_f)
);
totalFound += matchingItems;
}
currentlySearching = null;
Expand All @@ -323,22 +365,31 @@ public void initializeContents(int revision, List<ItemStack> stacks, ItemStack c
}
});
assert mc.gameMode != null;
mc.gameMode.useItemOn(mc.player, InteractionHand.MAIN_HAND,

CUtil.forEither(thing,
pos -> mc.gameMode.useItemOn(
mc.player,
InteractionHand.MAIN_HAND,
new BlockHitResult(clickPos,
Direction.getNearest((float) (clickPos.x - cameraPos.x), (float) (clickPos.y - cameraPos.y), (float) (clickPos.z - cameraPos.z)),
pos, false));
Direction.getNearest((float) (clickPos.x - cameraPos.x), (float) (clickPos.y - cameraPos.y), (float) (clickPos.z - cameraPos.z)),
pos, false)),
entity -> mc.gameMode.interact(
mc.player,
entity,
InteractionHand.MAIN_HAND));
}
}

private static class NbtQueryFindItemsTask extends AbstractFindItemsTask {
private static final long MAX_SCAN_TIME = 30_000_000L; // 30ms
private static final int NO_RESPONSE_TIMEOUT = 100; // ticks


private final Set<BlockPos> searchedBlocks = new HashSet<>();
private final Set<UUID> searchedEntities = new HashSet<>();
private boolean isScanning = true;
private Iterator<BlockPos.MutableBlockPos> scanningIterator;
private final Set<BlockPos> waitingOnBlocks = new HashSet<>();
private final Set<UUID> waitingOnEntities = new HashSet<>();
private int currentlySearchingTimeout;
@Nullable
private BlockPos enderChestPosition = null;
Expand All @@ -364,9 +415,34 @@ protected void onTick() {
}
ClientLevel level = Minecraft.getInstance().level;
assert level != null;
ClientPacketListener packetListener = Minecraft.getInstance().getConnection();
assert packetListener != null;

if (isScanning) {
long startTime = System.nanoTime();

for (Entity entity : level.entitiesForRendering()) {
if (canSearchEntity(entity) && searchedEntities.add(entity.getUUID())) {
waitingOnEntities.add(entity.getUUID());
currentlySearchingTimeout = NO_RESPONSE_TIMEOUT;
ClientcommandsDataQueryHandler.get(packetListener).queryEntityNbt(entity.getId(), entityNbt -> {
waitingOnEntities.remove(entity.getUUID());
if (!entity.isRemoved() && entityNbt != null && entityNbt.contains("Items", Tag.TAG_LIST)) {
int count = countItems(entityNbt.getList("Items", Tag.TAG_COMPOUND));
if (count > 0) {
totalFound += count;
printEntityLocation(entity, count);
}
}
currentlySearchingTimeout = NO_RESPONSE_TIMEOUT;
});
if (System.nanoTime() - startTime > MAX_SCAN_TIME) {
// wait a tick
return;
}
}
}

if (scanningIterator == null) {
Vec3 cameraPos = cameraEntity.getEyePosition(0);
scanningIterator = BlockPos.spiralAround(new BlockPos(Mth.floor(cameraPos.x) >> 4, 0, Mth.floor(cameraPos.z) >> 4), Minecraft.getInstance().options.renderDistance().get(), Direction.EAST, Direction.SOUTH).iterator();
Expand All @@ -385,7 +461,7 @@ protected void onTick() {
isScanning = false;
}

if (waitingOnBlocks.isEmpty() && (enderChestPosition == null || numItemsInEnderChest != null)) {
if (waitingOnBlocks.isEmpty() && waitingOnEntities.isEmpty() && (enderChestPosition == null || numItemsInEnderChest != null)) {
if (keepSearching) {
isScanning = true;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

public abstract class RenderDistanceScanTask extends SimpleTask {
private static final long MAX_SCAN_TIME = 30_000_000L; // 30ms
private static final Set<Object> MUTEX_KEYS = Set.of(RenderDistanceScanTask.class);
private static final Set<Object> MUTEX_KEYS = Set.of(TaskManager.INTENSIVE_TASK_MUTEX);

static {
ClientLevelEvents.CHUNK_UPDATE.register((level, pos, oldState, newState) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public class TaskManager {
ClientLevelEvents.UNLOAD_LEVEL.register(TaskManager::onLevelUnload);
}

/**
* The mutex for all intensive tasks, to make sure only one can run at a time
*/
public static final Object INTENSIVE_TASK_MUTEX = new Object();

private static final Map<String, LongTask> tasks = new LinkedHashMap<>();
private static long nextTaskId = 1;
private static String forceAddedTaskName = null;
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/net/earthcomputer/clientcommands/util/CUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.datafixers.util.Either;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.NotNull;

import java.util.function.Consumer;
import java.util.regex.Pattern;

public final class CUtil {
Expand All @@ -28,6 +30,16 @@ private static <T extends Throwable> void sneakyThrowHelper(Throwable e) throws
throw (T) e;
}

public static <L, R> void forEither(Either<L, R> either, Consumer<? super L> left, Consumer<? super R> right) {
either.<Void>map(l -> {
left.accept(l);
return null;
}, r -> {
right.accept(r);
return null;
});
}

private static class FusedRegexInput implements CharSequence {
private static final long FUSE_LENGTH = 50_000_000; // 50ms should be more than enough for a normal regex to do its matching

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ public static Vec3 getClosestPoint(BlockPos blockPos, VoxelShape voxel, Vec3 pos
AABB box = new AABB(x1, y1, z1, x2, y2, z2).move(blockPos);
for (Direction face : dirs) {
AABB faceBox = getFace(box, face);
// Since the faces are axis aligned, it's a simple clamp operation
Vec3 val = new Vec3(Mth.clamp(pos.x, faceBox.minX, faceBox.maxX),
Mth.clamp(pos.y, faceBox.minY, faceBox.maxY),
Mth.clamp(pos.z, faceBox.minZ, faceBox.maxZ));
Vec3 val = getClosestPoint(faceBox, pos);
double distanceSq = val.distanceToSqr(pos);
if (distanceSq < result.distanceSq) {
result.val = val;
Expand All @@ -47,6 +44,13 @@ public static Vec3 getClosestPoint(BlockPos blockPos, VoxelShape voxel, Vec3 pos
return result.val;
}

public static Vec3 getClosestPoint(AABB aabb, Vec3 pos) {
// Since the faces are axis aligned, it's a simple clamp operation
return new Vec3(Mth.clamp(pos.x, aabb.minX, aabb.maxX),
Mth.clamp(pos.y, aabb.minY, aabb.maxY),
Mth.clamp(pos.z, aabb.minZ, aabb.maxZ));
}

public static Vec3 getClosestVisiblePoint(Level level, BlockPos targetPos, Vec3 sourcePos, Entity excludingEntity) {
return getClosestVisiblePoint(level, targetPos, sourcePos, excludingEntity, null);
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/assets/clientcommands/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
"commands.cfindblock.success.glow": "[Glow]",
"commands.cfindblock.success.right": ", %1$s blocks away",

"commands.cfinditem.match.entity.left": "%dx %s found in %s at ",
"commands.cfinditem.match.entity.right": "",
"commands.cfinditem.match.left": "%dx %s found at ",
"commands.cfinditem.match.right": "",
"commands.cfinditem.starting": "Searching for %s",
Expand Down

0 comments on commit 316afb6

Please sign in to comment.