Skip to content

Commit

Permalink
Add --keep-searching option to /cfindblock (#635)
Browse files Browse the repository at this point in the history
* Fix #627 + Fix issue with pasting client command truncating at 256 chars.

* Added --keep-searching option to cfindblock

* Add #612

* Fixed issue I just noticed with new cfindblock command

* that's probably better

* undo feature

* whoop

* Add #612

* Added cminesweeper command

* temporary to move to desktop

* Changes in response to review

* Changes in response to review

* did this do something

* Changes in response to review

* whoop

* undid the bullshit whoopsie
  • Loading branch information
RealRTTV authored May 29, 2024
1 parent 283fe9c commit 7f3ea54
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.earthcomputer.clientcommands.command.arguments.ClientBlockPredicateArgument;
import net.earthcomputer.clientcommands.task.RenderDistanceScanTask;
Expand All @@ -26,18 +27,22 @@
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*;

public class FindBlockCommand {
public static final Flag<Boolean> FLAG_KEEP_SEARCHING = Flag.ofFlag("keep-searching").build();

public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandBuildContext context) {
dispatcher.register(literal("cfindblock")
var cfindblock = dispatcher.register(literal("cfindblock")
.then(argument("block", withString(blockPredicate(context)))
.executes(ctx -> {
var blockWithString = getWithString(ctx, "block", ClientBlockPredicateArgument.ParseResult.class);
return findBlock(Component.translatable("commands.cfindblock.starting", blockWithString.string()), getBlockPredicate(blockWithString.value()));
return findBlock(ctx, Component.translatable("commands.cfindblock.starting", blockWithString.string()), getBlockPredicate(blockWithString.value()));
})));
FLAG_KEEP_SEARCHING.addToCommand(dispatcher, cfindblock, ctx -> true);
}

public static int findBlock(Component startingMessage, ClientBlockPredicate block) throws CommandSyntaxException {
public static int findBlock(CommandContext<FabricClientCommandSource> ctx, Component startingMessage, ClientBlockPredicate block) throws CommandSyntaxException {
boolean keepSearching = getFlag(ctx, FLAG_KEEP_SEARCHING);
sendFeedback(startingMessage);
TaskManager.addTask("cfindblock", new FindBlockTask(block));
TaskManager.addTask("cfindblock", new FindBlockTask(block, keepSearching));
return Command.SINGLE_SUCCESS;
}

Expand All @@ -47,8 +52,8 @@ private static final class FindBlockTask extends RenderDistanceScanTask {
@Nullable
private BlockPos closestBlock;

FindBlockTask(ClientBlockPredicate predicate) {
super(false);
FindBlockTask(ClientBlockPredicate predicate, boolean keepSearching) {
super(keepSearching);
this.predicate = predicate;
}

Expand All @@ -59,6 +64,7 @@ protected void scanBlock(Entity cameraEntity, BlockPos pos) throws CommandSyntax
Vec3 cameraPos = cameraEntity.getEyePosition(0);
if ((closestBlock == null || pos.distToCenterSqr(cameraPos) < closestBlock.distToCenterSqr(cameraPos)) && predicate.test(level.registryAccess(), level, pos)) {
closestBlock = pos.immutable();
keepSearching = false;
}
}

Expand All @@ -75,6 +81,7 @@ protected boolean canScanChunkSection(Entity cameraEntity, SectionPos pos) {

@Override
public void onCompleted() {
super.onCompleted();
if (closestBlock == null) {
sendError(Component.translatable("commands.cfindblock.notFound"));
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,22 @@
import net.minecraft.world.level.block.entity.SignText;
import net.minecraft.world.level.block.state.BlockState;

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

import static com.mojang.brigadier.arguments.StringArgumentType.*;
import static net.earthcomputer.clientcommands.command.arguments.RegexArgument.*;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*;

public class SignSearchCommand {

public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
dispatcher.register(literal("csignsearch")
var csignsearch = dispatcher.register(literal("csignsearch")
.then(literal("text")
.then(argument("query", greedyString())
.executes(ctx -> FindBlockCommand.findBlock(Component.translatable("commands.csignsearch.starting"), predicate(getString(ctx, "query"))))))
.executes(ctx -> FindBlockCommand.findBlock(ctx, Component.translatable("commands.csignsearch.starting"), predicate(getString(ctx, "query"))))))
.then(literal("regex")
.then(argument("query", greedyRegex())
.executes(ctx -> FindBlockCommand.findBlock(Component.translatable("commands.csignsearch.starting"), predicate(getRegex(ctx, "query")))))));
.executes(ctx -> FindBlockCommand.findBlock(ctx, Component.translatable("commands.csignsearch.starting"), predicate(getRegex(ctx, "query")))))));
FindBlockCommand.FLAG_KEEP_SEARCHING.addToCommand(dispatcher, csignsearch, ctx -> true);
}

private static ClientBlockPredicateArgument.ClientBlockPredicate predicate(String query) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;

public final class ClientLevelEvents {
public static final Event<LoadLevel> LOAD_LEVEL = EventFactory.createArrayBacked(LoadLevel.class, listeners -> level -> {
Expand All @@ -17,6 +21,24 @@ public final class ClientLevelEvents {
}
});

public static final Event<ChunkUpdate> CHUNK_UPDATE = EventFactory.createArrayBacked(ChunkUpdate.class, listeners -> (level, pos, oldState, newState) -> {
for (ChunkUpdate listener : listeners) {
listener.onBlockStateUpdate(level, pos, oldState, newState);
}
});

public static final Event<LoadChunk> LOAD_CHUNK = EventFactory.createArrayBacked(LoadChunk.class, listeners -> (level, pos) -> {
for (LoadChunk listener : listeners) {
listener.onLoadChunk(level, pos);
}
});

public static final Event<UnloadChunk> UNLOAD_CHUNK = EventFactory.createArrayBacked(UnloadChunk.class, listeners -> (level, pos) -> {
for (UnloadChunk listener : listeners) {
listener.onUnloadChunk(level, pos);
}
});

@FunctionalInterface
public interface LoadLevel {
void onLoadLevel(ClientLevel level);
Expand All @@ -26,4 +48,19 @@ public interface LoadLevel {
public interface UnloadLevel {
void onUnloadLevel(boolean isDisconnect);
}

@FunctionalInterface
public interface ChunkUpdate {
void onBlockStateUpdate(ClientLevel level, BlockPos pos, BlockState oldState, BlockState newState);
}

@FunctionalInterface
public interface LoadChunk {
void onLoadChunk(ClientLevel level, ChunkPos chunkPos);
}

@FunctionalInterface
public interface UnloadChunk {
void onUnloadChunk(ClientLevel level, ChunkPos chunkPos);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.earthcomputer.clientcommands.mixin.commands.findblock;

import net.earthcomputer.clientcommands.event.ClientLevelEvents;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.LevelChunk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(ClientLevel.class)
public class ClientLevelMixin {
@Inject(method = "unload", at = @At("HEAD"))
private void onChunkUnload(LevelChunk chunk, CallbackInfo ci) {
ClientLevelEvents.UNLOAD_CHUNK.invoker().onUnloadChunk((ClientLevel) (Object) this, chunk.getPos());
}

@Inject(method = "onChunkLoaded", at = @At("HEAD"))
private void onChunkLoad(ChunkPos chunkPos, CallbackInfo ci) {
ClientLevelEvents.LOAD_CHUNK.invoker().onLoadChunk((ClientLevel) (Object) this, chunkPos);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package net.earthcomputer.clientcommands.mixin.commands.findblock;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.earthcomputer.clientcommands.event.ClientLevelEvents;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(LevelChunk.class)
public class LevelChunkMixin {

@Shadow @Final private Level level;

@WrapOperation(method = "setBlockState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/LevelChunkSection;setBlockState(IIILnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/world/level/block/state/BlockState;"))
private BlockState onSetBlockState(LevelChunkSection instance, int x, int y, int z, BlockState state, Operation<BlockState> original, BlockPos pos, BlockState redundant, boolean isMoving) {
BlockState oldState = original.call(instance, x, y, z, state);
if (level.isClientSide) {
ClientLevelEvents.CHUNK_UPDATE.invoker().onBlockStateUpdate((ClientLevel) level, pos, oldState, state);
}
return oldState;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package net.earthcomputer.clientcommands.task;

import com.mojang.brigadier.exceptions.CommandSyntaxException;
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
import net.earthcomputer.clientcommands.command.ClientCommandHelper;
import net.earthcomputer.clientcommands.event.ClientLevelEvents;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
Expand All @@ -16,29 +18,70 @@
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;

import java.util.Iterator;
import java.lang.ref.WeakReference;
import java.util.Set;
import java.util.function.Predicate;

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);

static {
ClientLevelEvents.CHUNK_UPDATE.register((level, pos, oldState, newState) -> {
WeakReference<RenderDistanceScanTask> currentScanTask = RenderDistanceScanTask.currentScanTask;
if (currentScanTask != null) {
RenderDistanceScanTask scanTask = currentScanTask.get();
if (scanTask != null) {
scanTask.onBlockStateUpdate(level, pos, oldState, newState);
}
}
});
ClientLevelEvents.UNLOAD_CHUNK.register((level, pos) -> {
WeakReference<RenderDistanceScanTask> currentScanTask = RenderDistanceScanTask.currentScanTask;
if (currentScanTask != null) {
RenderDistanceScanTask scanTask = currentScanTask.get();
if (scanTask != null) {
scanTask.onUnloadChunk(level, pos);
}
}
});
ClientLevelEvents.LOAD_CHUNK.register((level, pos) -> {
WeakReference<RenderDistanceScanTask> currentScanTask = RenderDistanceScanTask.currentScanTask;
if (currentScanTask != null) {
RenderDistanceScanTask scanTask = currentScanTask.get();
if (scanTask != null) {
scanTask.onLoadChunk(level, pos);
}
}
});
}

private final boolean keepSearching;

private Iterator<BlockPos.MutableBlockPos> squarePosIterator;
@Nullable
private static WeakReference<RenderDistanceScanTask> currentScanTask = null;
protected boolean keepSearching;
private LongLinkedOpenHashSet remainingChunks;

protected RenderDistanceScanTask(boolean keepSearching) {
this.keepSearching = keepSearching;
}

@Override
public void initialize() {
squarePosIterator = createIterator();
remainingChunks = new LongLinkedOpenHashSet();
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null) {
_break();
return;
}
BlockPos.spiralAround(new BlockPos(Mth.floor(cameraEntity.getX()) >> 4, 0, Mth.floor(cameraEntity.getZ()) >> 4), Minecraft.getInstance().options.renderDistance().get(), Direction.EAST, Direction.SOUTH).iterator().forEachRemaining(pos -> remainingChunks.add(ChunkPos.asLong(pos.getX(), pos.getZ())));
currentScanTask = new WeakReference<>(this);
}

@Override
public boolean condition() {
return squarePosIterator != null;
return hasChunksRemaining() || keepSearching;
}

@Override
Expand All @@ -50,6 +93,10 @@ protected final void onTick() {
}
}

protected boolean hasChunksRemaining() {
return !remainingChunks.isEmpty();
}

protected void doTick() throws CommandSyntaxException {
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null) {
Expand All @@ -61,9 +108,8 @@ protected void doTick() throws CommandSyntaxException {
assert level != null;

long startTime = System.nanoTime();
while (squarePosIterator.hasNext()) {
BlockPos chunkPosAsBlockPos = squarePosIterator.next();
ChunkPos chunkPos = new ChunkPos(chunkPosAsBlockPos.getX(), chunkPosAsBlockPos.getZ());
while (hasChunksRemaining()) {
ChunkPos chunkPos = new ChunkPos(remainingChunks.removeFirst());

if (canScanChunk(cameraEntity, chunkPos)) {
int minSection = level.getMinSection();
Expand All @@ -81,18 +127,38 @@ protected void doTick() throws CommandSyntaxException {
return;
}
}
}

@Override
public void onCompleted() {
currentScanTask = null;
}

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

protected void onBlockStateUpdate(ClientLevel level, BlockPos pos, BlockState oldState, BlockState newState) {
if (keepSearching) {
if (canKeepSearchingNow()) {
squarePosIterator = createIterator();
try {
scanBlock(Minecraft.getInstance().cameraEntity, pos);
} catch (CommandSyntaxException e) {
ClientCommandHelper.sendError(ComponentUtils.fromMessage(e.getRawMessage()));
}
} else {
squarePosIterator = null;
}
}

protected boolean canKeepSearchingNow() {
return true;
protected void onLoadChunk(ClientLevel level, ChunkPos pos) {
if (keepSearching) {
remainingChunks.add(pos.toLong());
}
}

protected void onUnloadChunk(ClientLevel level, ChunkPos pos) {
if (keepSearching) {
remainingChunks.add(pos.toLong());
}
}

protected boolean canScanChunk(Entity cameraEntity, ChunkPos pos) {
Expand Down Expand Up @@ -142,13 +208,4 @@ private void scanChunkSection(Entity cameraEntity, SectionPos sectionPos) throws
}

protected abstract void scanBlock(Entity cameraEntity, BlockPos pos) throws CommandSyntaxException;

private Iterator<BlockPos.MutableBlockPos> createIterator() {
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null) {
_break();
return null;
}
return BlockPos.spiralAround(new BlockPos(Mth.floor(cameraEntity.getX()) >> 4, 0, Mth.floor(cameraEntity.getZ()) >> 4), Minecraft.getInstance().options.renderDistance().get(), Direction.EAST, Direction.SOUTH).iterator();
}
}
2 changes: 2 additions & 0 deletions src/main/resources/mixins.clientcommands.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"commands.enchant.EnchantmentHelperMixin",
"commands.enchant.EnchantmentScreenMixin",
"commands.enchant.ProtectionEnchantmentMixin",
"commands.findblock.LevelChunkMixin",
"commands.fish.FishingHookMixin",
"commands.fish.FishingRodItemMixin",
"commands.fish.ItemEntityMixin",
Expand Down Expand Up @@ -67,6 +68,7 @@
"c2c.ClientPacketListenerMixin",
"commands.alias.ClientSuggestionProviderMixin",
"commands.enchant.MultiPlayerGameModeMixin",
"commands.findblock.ClientLevelMixin",
"commands.generic.CommandSuggestionsMixin",
"dataqueryhandler.ClientPacketListenerMixin",
"events.ClientPacketListenerMixin",
Expand Down

0 comments on commit 7f3ea54

Please sign in to comment.