From deb338f2c8f9e66383b7c2de1e51b5a2e85fa168 Mon Sep 17 00:00:00 2001 From: Hedzer Kuijlman Date: Mon, 19 May 2025 11:59:32 +0200 Subject: [PATCH] Added configurable Vein Miner animation --- README.md | 13 ++-- .../hak/survivalfabric/SurvivalFabric.java | 2 + .../wtf/hak/survivalfabric/config/Config.java | 1 + .../features/veinminer/drills/DrillBase.java | 51 ++++++++++++-- .../veinminer/drills/LeavesDrill.java | 49 +------------ .../features/veinminer/drills/OreDrill.java | 50 +------------- .../features/veinminer/drills/WoodDrill.java | 68 +------------------ .../hak/survivalfabric/utils/Scheduler.java | 45 ++++++++++++ 8 files changed, 103 insertions(+), 176 deletions(-) create mode 100644 src/main/java/wtf/hak/survivalfabric/utils/Scheduler.java diff --git a/README.md b/README.md index 1dbc2f3..284b331 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ As a challenge I'm trying to make it as user-friendly as possible. - Open/close EC block while opening/closing SEC - Play open & close sounds - Vein miner +Code inspired by Inferis! ![VeinMiner](https://i.imgur.com/zOXWMNa.gif) - Chat Calculator @@ -52,14 +53,14 @@ As a challenge I'm trying to make it as user-friendly as possible. # Currently working on ## Server Side +- [x] Chat Calculator + - [x] Check if operator is present before calculating - Vein Miner - - [ ] Configurable animation + - [x] Configurable animation (tick delay) + - [x] Leaves veinmineable using shears - [x] Replenish -- Patches - - [x] Made leaves veinmineable using shears - ## Client Side - [x] Render block entities from a longer range @@ -72,8 +73,6 @@ As a challenge I'm trying to make it as user-friendly as possible. - Boolean - Float - Integer -- [x] Chat Calculator - - [x] Check if operator is present before calculating - [x] Zoom - [ ] Configurable @@ -81,7 +80,7 @@ As a challenge I'm trying to make it as user-friendly as possible. ## General - Rework config system - - Store server settings in world folder for better singleplayer use +- Store server settings in world folder for better singleplayer use ## Client side diff --git a/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java b/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java index 6bb8876..3d98cad 100644 --- a/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java +++ b/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java @@ -12,6 +12,7 @@ import wtf.hak.survivalfabric.commands.SlimeChunkCommand; import wtf.hak.survivalfabric.commands.SpectatorCommand; import wtf.hak.survivalfabric.features.sharedenderchest.SharedEnderChest; import wtf.hak.survivalfabric.features.veinminer.VeinMinerEvents; +import wtf.hak.survivalfabric.utils.Scheduler; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; @@ -23,6 +24,7 @@ public class SurvivalFabric implements ModInitializer { @Override public void onInitialize() { + Scheduler.initialize(); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SpectatorCommand.register(dispatcher, "spectator", "s", "S", "camera", "c", "C")); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> ReloadConfigCommand.register(dispatcher, "reloadsurvivalconfig")); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SlimeChunkCommand.register(dispatcher, "slimechunk", "sc")); diff --git a/src/main/java/wtf/hak/survivalfabric/config/Config.java b/src/main/java/wtf/hak/survivalfabric/config/Config.java index c22301e..672cfa8 100644 --- a/src/main/java/wtf/hak/survivalfabric/config/Config.java +++ b/src/main/java/wtf/hak/survivalfabric/config/Config.java @@ -37,6 +37,7 @@ public class Config { public boolean veinMinerEnabled = true; public int maxVeinSize = 99999; + public long veinAnimationTicks = 0; public boolean chatCalcEnabled = true; diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java index edf69da..d6b6630 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/DrillBase.java @@ -1,6 +1,8 @@ package wtf.hak.survivalfabric.features.veinminer.drills; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.registry.tag.TagKey; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.MutableText; import net.minecraft.text.PlainTextContent; @@ -8,27 +10,66 @@ import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import wtf.hak.survivalfabric.features.veinminer.Drill; import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession; +import wtf.hak.survivalfabric.utils.Scheduler; import java.util.ArrayList; +import java.util.List; import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER; +import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; public class DrillBase implements Drill { protected VeinMinerSession session; - public DrillBase(VeinMinerSession session) { + protected TagKey tag; + + public DrillBase(VeinMinerSession session, TagKey tag) { this.session = session; + this.tag = tag; } @Override - public boolean canHandle(BlockState blockId) { - return false; + public boolean canHandle(BlockState blockState) { + return blockState.isIn(tag); } @Override - public boolean drill(BlockPos blockPos) { - return false; + public boolean drill(BlockPos startPos) { + handleBlock(session.world.getBlockState(startPos).getBlock(), new ArrayList<>(), startPos, 0); + return true; + } + + private void handleBlock(Block initialBlock, List history, BlockPos pos, int brokenBlocks) { + if(brokenBlocks < getConfig().maxVeinSize) { + history.add(pos); + if(tryBreakBlock(pos)) { + brokenBlocks++; + int finalBrokenBlocks = brokenBlocks; + + // Put everything in a list to avoid scheduling a lot of tasks. + // Has the added benefit of only playing one sound + + List toBreak = new ArrayList<>(); + forXYZ(pos, 1, newPos -> { + if (!history.contains(newPos) && session.world.getBlockState(newPos).getBlock() == initialBlock) { + toBreak.add(newPos); + } + }); + + long delay = getConfig().veinAnimationTicks; + if(delay <= 0) + for(BlockPos newPos : toBreak) + handleBlock(initialBlock, history, newPos, finalBrokenBlocks); + else { + Scheduler.get().scheduleTask(() -> { + for(BlockPos newPos : toBreak) + handleBlock(initialBlock, history, newPos, finalBrokenBlocks); + }, delay); + } + + } + } } @Override diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java index eb614c0..7de9a6c 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/LeavesDrill.java @@ -1,30 +1,16 @@ package wtf.hak.survivalfabric.features.veinminer.drills; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; import net.minecraft.item.Items; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.tag.TagKey; -import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession; -import java.util.ArrayDeque; - -import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; - public class LeavesDrill extends DrillBase { - public static final TagKey leavesTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "leaves")); - public LeavesDrill(VeinMinerSession session) { - super(session); - } - - @Override - public boolean canHandle(BlockState blockState) { - return blockState.isIn(leavesTag); + super(session, TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "leaves"))); } @Override @@ -33,37 +19,4 @@ public class LeavesDrill extends DrillBase { return session.player.getMainHandStack().isSuitableFor(blockState) || session.player.getMainHandStack().getItem() == Items.SHEARS; } - @Override - public boolean drill(BlockPos startPos) { - ServerWorld world = session.world; - Block initialBlock = world.getBlockState(startPos).getBlock(); - int brokenLeaves = 0; - ArrayDeque pending = new ArrayDeque(); - pending.add(startPos); - - while (!pending.isEmpty() && brokenLeaves < getConfig().maxVeinSize) { - BlockPos leavesPos = pending.remove(); - Block leavesBlock = world.getBlockState(leavesPos).getBlock(); - if (tryBreakBlock(leavesPos)) { - if (leavesBlock == initialBlock) { - brokenLeaves += 1; - } - - if (leavesBlock == initialBlock) { - // look around current block - forXYZ(leavesPos, 1, newPos -> { - BlockState newBlockState = world.getBlockState(newPos); - Block newBlock = newBlockState.getBlock(); - boolean isSameOreBlock = newBlock == leavesBlock; - if (!pending.contains(newPos) && isSameOreBlock) { - pending.add(newPos); - } - }); - } - } - } - - return true; - } - } \ No newline at end of file diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/OreDrill.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/OreDrill.java index 55675cc..e607bf4 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/OreDrill.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/OreDrill.java @@ -1,62 +1,14 @@ package wtf.hak.survivalfabric.features.veinminer.drills; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.tag.TagKey; -import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession; -import java.util.ArrayDeque; - -import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; - public class OreDrill extends DrillBase { - public static final TagKey oreTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "ore")); - public OreDrill(VeinMinerSession session) { - super(session); - } - - @Override - public boolean canHandle(BlockState blockState) { - return blockState.isIn(oreTag); - } - - @Override - public boolean drill(BlockPos startPos) { - ServerWorld world = session.world; - Block initialBlock = world.getBlockState(startPos).getBlock(); - int brokenOre = 0; - ArrayDeque pending = new ArrayDeque(); - pending.add(startPos); - - while (!pending.isEmpty() && brokenOre < getConfig().maxVeinSize) { - BlockPos orePos = pending.remove(); - Block oreBlock = world.getBlockState(orePos).getBlock(); - if (tryBreakBlock(orePos)) { - if (oreBlock == initialBlock) { - brokenOre += 1; - } - - if (oreBlock == initialBlock) { - // look around current block - forXYZ(orePos, 1, newPos -> { - BlockState newBlockState = world.getBlockState(newPos); - Block newBlock = newBlockState.getBlock(); - boolean isSameOreBlock = newBlock == oreBlock; - if (!pending.contains(newPos) && isSameOreBlock) { - pending.add(newPos); - } - }); - } - } - } - - return true; + super(session, TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "ore"))); } } \ No newline at end of file diff --git a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/WoodDrill.java b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/WoodDrill.java index 1c32d9b..d7d5bb1 100644 --- a/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/WoodDrill.java +++ b/src/main/java/wtf/hak/survivalfabric/features/veinminer/drills/WoodDrill.java @@ -1,80 +1,14 @@ package wtf.hak.survivalfabric.features.veinminer.drills; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.registry.Registries; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.tag.TagKey; -import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession; -import java.util.ArrayDeque; - -import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; - public class WoodDrill extends DrillBase { - public static final TagKey woodTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "wood")); - public WoodDrill(VeinMinerSession session) { - super(session); - } - - @Override - public boolean canHandle(BlockState blockState) { - return blockState.isIn(woodTag); - } - - @Override - public boolean drill(BlockPos startPos) { - ServerWorld world = session.world; - int broken = 0; - ArrayDeque pendingLogs = new ArrayDeque<>(); - ArrayDeque logBlocks = new ArrayDeque<>(); - pendingLogs.add(startPos); - - String leavesBlockId = Registries.BLOCK.getId(world.getBlockState(startPos).getBlock()).toString().replace("_log", "_leaves"); - - while (!pendingLogs.isEmpty() && broken < getConfig().maxVeinSize) { - BlockPos woodPos = pendingLogs.remove(); - Block woodBlock = world.getBlockState(woodPos).getBlock(); - - if (tryBreakBlock(woodPos)) { - logBlocks.add(woodPos); - broken += 1; - - // look around current block - forXYZ(woodPos, 1, newPos -> { - Block newBlock = world.getBlockState(newPos).getBlock(); - if (newBlock == woodBlock && !pendingLogs.contains(newPos)) { - pendingLogs.add(newPos); - } - }, true); - } - } - - ArrayDeque pendingLeaves = logBlocks; - while (!pendingLeaves.isEmpty() && broken < getConfig().maxVeinSize) { - broken += forXYZ(pendingLeaves.remove(), 1, newPos -> { - int brokenLeaves = 0; - Block newBlock = world.getBlockState(newPos).getBlock(); - String newBlockId = Registries.BLOCK.getId(newBlock).toString(); - if (newBlockId.equals(leavesBlockId)) { - if (tryBreakBlock(newPos)) { - brokenLeaves += 1; - } - } - return brokenLeaves; - }, true); - } - - return true; - } - - private boolean isLeaf(Block block) { - return Registries.BLOCK.getId(block).toString().endsWith("_leaves"); + super(session, TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "wood"))); } } \ No newline at end of file diff --git a/src/main/java/wtf/hak/survivalfabric/utils/Scheduler.java b/src/main/java/wtf/hak/survivalfabric/utils/Scheduler.java new file mode 100644 index 0000000..37c5e99 --- /dev/null +++ b/src/main/java/wtf/hak/survivalfabric/utils/Scheduler.java @@ -0,0 +1,45 @@ +package wtf.hak.survivalfabric.utils; + +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class Scheduler { + + private static Scheduler INSTANCE; + + private final Map tasks = new ConcurrentHashMap<>(); + + public Scheduler() { + ServerTickEvents.END_SERVER_TICK.register((server) -> { + for(Runnable task : tasks.keySet()) { + long delay = tasks.get(task); + if(delay <= 0) { + task.run(); + tasks.remove(task); + } else { + tasks.put(task, delay-1); + } + } + }); + } + + public void scheduleTask(Runnable task) { + scheduleTask(task, 0L); + } + + public void scheduleTask(Runnable task, long delay) { + tasks.put(task, delay); + } + + public static Scheduler get() { + return INSTANCE; + } + + public static Scheduler initialize() { + INSTANCE = new Scheduler(); + return INSTANCE; + } +}