From c3c6ebec322bef43ff177981d1daa2b2c0b98e6a Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Mon, 25 Aug 2025 00:54:17 +0200 Subject: [PATCH 1/6] Add the cpredictbrushables command --- .../clientcommands/ClientCommands.java | 1 + .../command/PredictBrushablesCommand.java | 152 ++++++++++++++++++ .../clientcommands/util/SeedfindingUtil.java | 46 ++++++ .../assets/clientcommands/lang/en_us.json | 4 + 4 files changed, 203 insertions(+) create mode 100644 src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java diff --git a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java index 5fd8593e1..8b8c0d89a 100644 --- a/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java +++ b/src/main/java/net/earthcomputer/clientcommands/ClientCommands.java @@ -175,6 +175,7 @@ public static void registerCommands(CommandDispatcher // PlayerInfoCommand.register(dispatcher); PluginsCommand.register(dispatcher); PosCommand.register(dispatcher); + PredictBrushablesCommand.register(dispatcher); RelogCommand.register(dispatcher); RenderCommand.register(dispatcher); ReplyCommand.register(dispatcher); diff --git a/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java new file mode 100644 index 000000000..bbacfc3bd --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java @@ -0,0 +1,152 @@ +package net.earthcomputer.clientcommands.command; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.seedfinding.mcfeature.loot.LootContext; +import com.seedfinding.mcfeature.loot.LootTable; +import com.seedfinding.mcfeature.loot.MCLootTables; +import com.seedfinding.mcfeature.loot.item.ItemStack; +import net.earthcomputer.clientcommands.task.RenderDistanceScanTask; +import net.earthcomputer.clientcommands.task.TaskManager; +import net.earthcomputer.clientcommands.util.SeedfindingUtil; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.SharedConstants; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.SectionPos; +import net.minecraft.core.component.DataComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.tags.BiomeTags; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.alchemy.PotionContents; +import net.minecraft.world.item.component.ItemLore; +import net.minecraft.world.item.component.SuspiciousStewEffects; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BrushableBlock; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static net.earthcomputer.clientcommands.command.ClientCommandHelper.*; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; + +public class PredictBrushablesCommand { + public static final Flag FLAG_KEEP_SEARCHING = Flag.ofFlag("keep-searching").build(); + + public static void register(CommandDispatcher dispatcher) { + var cpredictbrushables = dispatcher.register(literal("cpredictbrushables") + .executes(ctx -> predictSuspiciousSand(ctx, Component.translatable("commands.cpredictbrushables.starting")))); + FLAG_KEEP_SEARCHING.addToCommand(dispatcher, cpredictbrushables, ctx -> true); + } + + public static int predictSuspiciousSand(CommandContext ctx, Component startingMessage) throws CommandSyntaxException { + boolean keepSearching = getFlag(ctx, FLAG_KEEP_SEARCHING); + sendFeedback(startingMessage); + TaskManager.addTask("cpredictbrushables", new PredictBrushablesTask(keepSearching)); + return Command.SINGLE_SUCCESS; + } + + private static final class PredictBrushablesTask extends RenderDistanceScanTask { + PredictBrushablesTask(boolean keepSearching) { + super(keepSearching); + } + + @Override + protected void scanBlock(Entity cameraEntity, BlockPos pos) { + Minecraft minecraft = Minecraft.getInstance(); + LocalPlayer player = minecraft.player; + ClientLevel level = minecraft.level; + assert level != null; + BlockState blockState = level.getBlockState(pos); + + // best effort, may be inaccurate + Supplier lootTableSupplier; + long lootSeed; + + if (blockState.is(Blocks.SUSPICIOUS_SAND)) { + Holder biome = level.getBiome(pos); + if (biome.is(Biomes.DESERT)) { + // either desert well or desert pyramid, check for water + boolean hasWater = Stream.of(pos.atY(pos.getY() + 1), pos.atY(pos.getY() + 2)) + .map(level::getBlockState) + .anyMatch(s -> s.is(Blocks.WATER)); + lootTableSupplier = hasWater ? MCLootTables.DESERT_WELL_ARCHAEOLOGY : MCLootTables.DESERT_PYRAMID_ARCHAEOLOGY; + lootSeed = pos.asLong(); + } else if (biome.is(BiomeTags.HAS_OCEAN_RUIN_WARM)) { + lootTableSupplier = MCLootTables.OCEAN_RUIN_WARM_ARCHAEOLOGY; + RandomSource randomSource = RandomSource.create(Mth.getSeed(pos)); + lootSeed = randomSource.nextLong(); + } else { + return; + } + } else if (blockState.is(Blocks.SUSPICIOUS_GRAVEL)) { + Holder biome = level.getBiome(pos); + if (biome.is(BiomeTags.HAS_OCEAN_RUIN_COLD)) { + lootTableSupplier = MCLootTables.OCEAN_RUIN_COLD_ARCHAEOLOGY; + RandomSource randomSource = RandomSource.create(Mth.getSeed(pos)); + lootSeed = randomSource.nextLong(); + } else if (biome.is(BiomeTags.HAS_TRAIL_RUINS)) { + // TODO: check for rare or common loot + // Common loot is generated in TRAIL_RUINS_HOUSES_ARCHAEOLOGY, + // TRAIL_RUINS_ROADS_ARCHAEOLOGY and TRAIL_RUINS_TOWER_TOP_ARCHAEOLOGY + // Rare is generated in TRAIL_RUINS_HOUSES_ARCHAEOLOGY + // Ignored for now + return; + } else { + return; + } + } else { + return; + } + + LootContext context = new LootContext(lootSeed, SeedfindingUtil.getMCVersion()) + .withLuck((int) player.getLuck()); + + List items = lootTableSupplier.get().generate(context); + for (ItemStack item : items) { + var mcItemStack = SeedfindingUtil.fromSeedfindingItem(item, level.registryAccess()); + mcItemStack.set(DataComponents.LORE, getItemLore(mcItemStack)); + ClientCommandHelper.sendFeedback(Component.translatable("commands.cpredictbrushables.foundBrushableBlock", blockState.getBlock().getName(), ClientCommandHelper.getLookCoordsTextComponent(pos), mcItemStack.getDisplayName(), ClientCommandHelper.getGlowButtonTextComponent(pos))); + } + } + + // use lore to display effects + private static ItemLore getItemLore(net.minecraft.world.item.ItemStack itemStack) { + SuspiciousStewEffects effects = itemStack.get(DataComponents.SUSPICIOUS_STEW_EFFECTS); + if (effects == null) { + return ItemLore.EMPTY; + } + List components = new ArrayList<>(); + for (SuspiciousStewEffects.Entry entry : effects.effects()) { + MobEffectInstance mobEffectInstance = entry.createEffectInstance(); + MutableComponent description = PotionContents.getPotionDescription(mobEffectInstance.getEffect(), mobEffectInstance.getAmplifier()); + Component line = Component.translatable("commands.cpredictbrushables.stewEffect", description, entry.duration() / SharedConstants.TICKS_PER_SECOND); + components.add(line); + } + return new ItemLore(components); + } + + @Override + protected void onBlockStateUpdate(ClientLevel level, BlockPos pos, BlockState oldState, BlockState newState) { + } + + @Override + protected boolean canScanChunkSection(Entity cameraEntity, SectionPos pos) { + return hasBlockState(pos, s -> s.getBlock() instanceof BrushableBlock) && super.canScanChunkSection(cameraEntity, pos); + } + } +} diff --git a/src/main/java/net/earthcomputer/clientcommands/util/SeedfindingUtil.java b/src/main/java/net/earthcomputer/clientcommands/util/SeedfindingUtil.java index 82bb40c14..68f3a0009 100644 --- a/src/main/java/net/earthcomputer/clientcommands/util/SeedfindingUtil.java +++ b/src/main/java/net/earthcomputer/clientcommands/util/SeedfindingUtil.java @@ -4,17 +4,23 @@ import com.google.common.collect.HashBiMap; import com.seedfinding.mcbiome.biome.Biomes; import com.seedfinding.mccore.version.MCVersion; +import com.seedfinding.mcfeature.loot.effect.Effect; +import com.seedfinding.mcfeature.loot.effect.Effects; import net.minecraft.Util; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; +import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.effect.MobEffects; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.item.component.SuspiciousStewEffects; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.Level; @@ -65,6 +71,40 @@ public class SeedfindingUtil { map.put(Enchantments.VANISHING_CURSE, "vanishing_curse"); }); + private static final BiMap, Effect> SEEDFINDING_EFFECTS = Util.make(HashBiMap.create(), map -> { + map.put(MobEffects.SPEED, Effects.MOVEMENT_SPEED); + map.put(MobEffects.SLOWNESS, Effects.MOVEMENT_SLOWDOWN); + map.put(MobEffects.HASTE, Effects.DIG_SPEED); + map.put(MobEffects.MINING_FATIGUE, Effects.DIG_SLOWDOWN); + map.put(MobEffects.STRENGTH, Effects.DAMAGE_BOOST); + map.put(MobEffects.INSTANT_HEALTH, Effects.HEAL); + map.put(MobEffects.INSTANT_DAMAGE, Effects.HARM); + map.put(MobEffects.JUMP_BOOST, Effects.JUMP); + map.put(MobEffects.NAUSEA, Effects.CONFUSION); + map.put(MobEffects.REGENERATION, Effects.REGENERATION); + map.put(MobEffects.RESISTANCE, Effects.DAMAGE_RESISTANCE); + map.put(MobEffects.FIRE_RESISTANCE, Effects.FIRE_RESISTANCE); + map.put(MobEffects.WATER_BREATHING, Effects.WATER_BREATHING); + map.put(MobEffects.INVISIBILITY, Effects.INVISIBILITY); + map.put(MobEffects.BLINDNESS, Effects.BLINDNESS); + map.put(MobEffects.NIGHT_VISION, Effects.NIGHT_VISION); + map.put(MobEffects.HUNGER, Effects.HUNGER); + map.put(MobEffects.WEAKNESS, Effects.WEAKNESS); + map.put(MobEffects.POISON, Effects.POISON); + map.put(MobEffects.WITHER, Effects.WITHER); + map.put(MobEffects.HEALTH_BOOST, Effects.HEALTH_BOOST); + map.put(MobEffects.ABSORPTION, Effects.ABSORPTION); + map.put(MobEffects.SATURATION, Effects.SATURATION); + map.put(MobEffects.GLOWING, Effects.GLOWING); + map.put(MobEffects.LEVITATION, Effects.LEVITATION); + map.put(MobEffects.LUCK, Effects.LUCK); + map.put(MobEffects.UNLUCK, Effects.UNLUCK); + map.put(MobEffects.SLOW_FALLING, Effects.SLOW_FALLING); + map.put(MobEffects.CONDUIT_POWER, Effects.CONDUIT_POWER); + map.put(MobEffects.DOLPHINS_GRACE, Effects.DOLPHINS_GRACE); + map.put(MobEffects.BAD_OMEN, Effects.BAD_OMEN); + }); + private SeedfindingUtil() { } @@ -101,6 +141,12 @@ public static ItemStack fromSeedfindingItem(com.seedfinding.mcfeature.loot.item. ret.enchant(enchantment, enchAndLevel.getSecond()); }); } + + for (var effectAndDuration : stack.getItem().getEffects()) { + Holder effectHolder = Objects.requireNonNull(SEEDFINDING_EFFECTS.inverse().get(effectAndDuration.getFirst()), () -> "missing seedfinding effect " + effectAndDuration.getFirst()); + SuspiciousStewEffects.Entry entry = new SuspiciousStewEffects.Entry(effectHolder, effectAndDuration.getSecond()); + ret.update(DataComponents.SUSPICIOUS_STEW_EFFECTS, SuspiciousStewEffects.EMPTY, entry, SuspiciousStewEffects::withEffectAdded); + } return ret; } diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index bf836e5f3..5eef69cf1 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -234,6 +234,10 @@ "commands.cpos.level.the_end": "the End", "commands.cpos.level.the_nether": "the Nether", + "commands.cpredictbrushables.foundBrushableBlock": "Found %s at %s with item %s %s", + "commands.cpredictbrushables.starting": "Searching for brushable blocks", + "commands.cpredictbrushables.stewEffect": "%s (%s seconds)", + "commands.crelog.failed": "Failed to relog", "commands.crender.entities.success": "Entity rendering rules have been updated", From 214f6f79b07b87d68dd9dcec1376e24c3d00cc8c Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Tue, 26 Aug 2025 13:47:42 +0200 Subject: [PATCH 2/6] Rename function --- .../clientcommands/command/PredictBrushablesCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java index bbacfc3bd..e7557d6d3 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java @@ -49,11 +49,11 @@ public class PredictBrushablesCommand { public static void register(CommandDispatcher dispatcher) { var cpredictbrushables = dispatcher.register(literal("cpredictbrushables") - .executes(ctx -> predictSuspiciousSand(ctx, Component.translatable("commands.cpredictbrushables.starting")))); + .executes(ctx -> predictBrushables(ctx, Component.translatable("commands.cpredictbrushables.starting")))); FLAG_KEEP_SEARCHING.addToCommand(dispatcher, cpredictbrushables, ctx -> true); } - public static int predictSuspiciousSand(CommandContext ctx, Component startingMessage) throws CommandSyntaxException { + public static int predictBrushables(CommandContext ctx, Component startingMessage) throws CommandSyntaxException { boolean keepSearching = getFlag(ctx, FLAG_KEEP_SEARCHING); sendFeedback(startingMessage); TaskManager.addTask("cpredictbrushables", new PredictBrushablesTask(keepSearching)); From 6454446119166afe011447044ddb2809c4fd4fd4 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Wed, 27 Aug 2025 01:42:12 +0200 Subject: [PATCH 3/6] Update mc_feature_java --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index cb6a0861f..a404b27da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -27,7 +27,7 @@ org.gradle.jvmargs=-Xmx2G simplewaypoints_version=1.0.0-alpha.4 seedfinding_core_version=1.210.0 seedfinding_biome_version=1.171.1 - seedfinding_feature_version=1.171.10 + seedfinding_feature_version=1.171.11 seedfinding_seed_version=1.171.2 latticg_version=1.07 mapping_io_version=0.7.1 From c06a3d9fd1fde3963a933611423b8577738ce23d Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Thu, 28 Aug 2025 14:58:18 +0200 Subject: [PATCH 4/6] Increase click glow duration --- .../clientcommands/command/ClientCommandHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/ClientCommandHelper.java b/src/main/java/net/earthcomputer/clientcommands/command/ClientCommandHelper.java index dd2dce23c..fed8b5961 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/ClientCommandHelper.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/ClientCommandHelper.java @@ -73,7 +73,7 @@ public static Component getGlowCoordsTextComponent(BlockPos pos) { } public static Component getGlowButtonTextComponent(BlockPos pos) { - return getCommandTextComponent(Component.translatable("commands.client.glow"), String.format("/cglow block %d %d %d 10", pos.getX(), pos.getY(), pos.getZ())); + return getCommandTextComponent(Component.translatable("commands.client.glow"), String.format("/cglow block %d %d %d 60", pos.getX(), pos.getY(), pos.getZ())); } public static Component getGlowButtonTextComponent(Entity entity) { From fc628a49d5e8c985597f4cf8ebf98140f632a4cf Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Thu, 4 Sep 2025 16:01:12 +0200 Subject: [PATCH 5/6] Add cancel button to search start message --- .../clientcommands/command/PredictBrushablesCommand.java | 8 ++++---- src/main/resources/assets/clientcommands/lang/en_us.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java index e7557d6d3..a1572b68c 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java @@ -49,14 +49,14 @@ public class PredictBrushablesCommand { public static void register(CommandDispatcher dispatcher) { var cpredictbrushables = dispatcher.register(literal("cpredictbrushables") - .executes(ctx -> predictBrushables(ctx, Component.translatable("commands.cpredictbrushables.starting")))); + .executes(PredictBrushablesCommand::predictBrushables)); FLAG_KEEP_SEARCHING.addToCommand(dispatcher, cpredictbrushables, ctx -> true); } - public static int predictBrushables(CommandContext ctx, Component startingMessage) throws CommandSyntaxException { + private static int predictBrushables(CommandContext ctx) throws CommandSyntaxException { boolean keepSearching = getFlag(ctx, FLAG_KEEP_SEARCHING); - sendFeedback(startingMessage); - TaskManager.addTask("cpredictbrushables", new PredictBrushablesTask(keepSearching)); + String taskName = TaskManager.addTask("cpredictbrushables", new PredictBrushablesTask(keepSearching)); + sendFeedback(Component.translatable("commands.cpredictbrushables.starting", getCommandTextComponent("commands.client.cancel", "/ctask stop " + taskName))); return Command.SINGLE_SUCCESS; } diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index 5eef69cf1..30c94bec9 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -235,7 +235,7 @@ "commands.cpos.level.the_nether": "the Nether", "commands.cpredictbrushables.foundBrushableBlock": "Found %s at %s with item %s %s", - "commands.cpredictbrushables.starting": "Searching for brushable blocks", + "commands.cpredictbrushables.starting": "Searching for brushable blocks [%s]", "commands.cpredictbrushables.stewEffect": "%s (%s seconds)", "commands.crelog.failed": "Failed to relog", From 677e6ca842833a6293fac8d2fc0890331aa3385a Mon Sep 17 00:00:00 2001 From: joe Date: Thu, 25 Sep 2025 17:15:13 +0100 Subject: [PATCH 6/6] Add feedback for unsuccessful brushable block search; better message for non-infinite search --- .../command/PredictBrushablesCommand.java | 19 ++++++++++++++++++- .../assets/clientcommands/lang/en_us.json | 4 +++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java index a1572b68c..852fb31f4 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/PredictBrushablesCommand.java @@ -56,11 +56,18 @@ public static void register(CommandDispatcher dispatc private static int predictBrushables(CommandContext ctx) throws CommandSyntaxException { boolean keepSearching = getFlag(ctx, FLAG_KEEP_SEARCHING); String taskName = TaskManager.addTask("cpredictbrushables", new PredictBrushablesTask(keepSearching)); - sendFeedback(Component.translatable("commands.cpredictbrushables.starting", getCommandTextComponent("commands.client.cancel", "/ctask stop " + taskName))); + if (keepSearching) { + sendFeedback(Component.translatable("commands.cpredictbrushables.starting.keepSearching", getCommandTextComponent("commands.client.cancel", "/ctask stop " + taskName))); + } else { + sendFeedback(Component.translatable("commands.cpredictbrushables.starting")); + } + return Command.SINGLE_SUCCESS; } private static final class PredictBrushablesTask extends RenderDistanceScanTask { + private boolean found = false; + PredictBrushablesTask(boolean keepSearching) { super(keepSearching); } @@ -122,6 +129,8 @@ protected void scanBlock(Entity cameraEntity, BlockPos pos) { mcItemStack.set(DataComponents.LORE, getItemLore(mcItemStack)); ClientCommandHelper.sendFeedback(Component.translatable("commands.cpredictbrushables.foundBrushableBlock", blockState.getBlock().getName(), ClientCommandHelper.getLookCoordsTextComponent(pos), mcItemStack.getDisplayName(), ClientCommandHelper.getGlowButtonTextComponent(pos))); } + + found = true; } // use lore to display effects @@ -148,5 +157,13 @@ protected void onBlockStateUpdate(ClientLevel level, BlockPos pos, BlockState ol protected boolean canScanChunkSection(Entity cameraEntity, SectionPos pos) { return hasBlockState(pos, s -> s.getBlock() instanceof BrushableBlock) && super.canScanChunkSection(cameraEntity, pos); } + + @Override + public void onCompleted() { + super.onCompleted(); + if (!found) { + sendError(Component.translatable("commands.cpredictbrushables.notFound")); + } + } } } diff --git a/src/main/resources/assets/clientcommands/lang/en_us.json b/src/main/resources/assets/clientcommands/lang/en_us.json index 30c94bec9..8a09bc854 100644 --- a/src/main/resources/assets/clientcommands/lang/en_us.json +++ b/src/main/resources/assets/clientcommands/lang/en_us.json @@ -235,7 +235,9 @@ "commands.cpos.level.the_nether": "the Nether", "commands.cpredictbrushables.foundBrushableBlock": "Found %s at %s with item %s %s", - "commands.cpredictbrushables.starting": "Searching for brushable blocks [%s]", + "commands.cpredictbrushables.notFound": "No brushable blocks found", + "commands.cpredictbrushables.starting": "Searching for brushable blocks", + "commands.cpredictbrushables.starting.keepSearching": "Infinitely searching for brushable blocks [%s]", "commands.cpredictbrushables.stewEffect": "%s (%s seconds)", "commands.crelog.failed": "Failed to relog",