6 Commits

Author SHA1 Message Date
f17519a547 Updated README for v1.3.0 2025-03-26 16:24:52 +01:00
8be2f2dc31 Prettified README 2025-03-26 16:00:09 +01:00
091efb900a Scrapped Telekinesis (for now) due to complexity 2025-03-26 15:10:27 +01:00
824c84e329 Fixed spelling mistake 2025-03-26 10:59:42 +01:00
73b8be0930 Added Vein Mining 2025-03-26 10:56:16 +01:00
645529a9f6 Added /slimechunk command 2025-03-26 10:04:28 +01:00
15 changed files with 600 additions and 27 deletions

View File

@ -1,38 +1,43 @@
# Survival Fabric
# ![Logo](https://i.imgur.com/ktkHND1.png)
This mod is FAR FROM FINISHED, and initially just created for our private Survival Server. This mod is FAR FROM FINISHED, and initially just created for our private Survival Server.
As a challenge I'm trying to make it as user-friendly as possible. (It ain't there yet tho ;) ) As a challenge I'm trying to make it as user-friendly as possible. (It ain't there yet tho ;) )
## Current feature-set ## Current feature-set
### Features ### Features
- Custom join message - Custom join message
- Custom quit message ![Join Message](https://i.imgur.com/7uv5lUb.png)
- Custom chat message - Custom quit message
- Tab list dimension indicator ![Quit Message](https://i.imgur.com/OhFq1BT.png)
- Custom chat message
![Chat Message](https://i.imgur.com/PDwRywP.png)
- Tab list dimension indicator
![Overworld](https://i.imgur.com/FB1Y7gD.png)
![Nether](https://i.imgur.com/XxJDL7u.png)
![End](https://i.imgur.com/t5u9goh.png)
![Spectator](https://i.imgur.com/eEn4V9S.png)
- Config
- Configurable messages
- Feature toggle
- Version "control"
- Shared Ender Chest
- Shared EC Access control (via config)
- Vein miner
![VeinMiner](https://i.imgur.com/zOXWMNa.gif)
### Commands ### Commands
- /spectator | Essentially server-side free-cam, you get put in spectator and are able to fly around, once you use the command again you get put back to where you were. - /spectator | Essentially server-side free-cam, you get put in spectator and are able to fly around, once you use the command again you get put back to where you were.
- /slimechunk (/sc) | See if you're currently in a slimechunk
## Currently working on v1.3.0
### Features
- [x] Config
- [x] Configurable messages
- [x] Feature toggle
- [x] Version "control"
- [x] Shared Ender Chest
- [x] Shared EC Access control (via config)
- [ ] Vein miner
- [ ] Telekinesis
### Commands
- [ ] /slimechunk (/sc) | See if you're currently in a slimechunk
### Misc ### Misc
- [x] Updated icon - Updated icon
## Features to come ## Features to come
For now there are no more feautures! - Telekinesis
Be sure to give your idea by [opening an issue](https://git.hak.wtf/hkuijlman/SurvivalFabric/issues/new) wit the label Feature Request
Other than that no more features!
Be sure to give your idea by [opening an issue](https://git.hak.wtf/hkuijlman/SurvivalFabric/issues/new) with the label Feature Request

View File

@ -3,11 +3,16 @@ package wtf.hak.survivalfabric;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
import net.minecraft.server.network.ServerPlayerEntity;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import wtf.hak.survivalfabric.commands.ReloadConfigCommand; import wtf.hak.survivalfabric.commands.ReloadConfigCommand;
import wtf.hak.survivalfabric.commands.SlimeChunkCommand;
import wtf.hak.survivalfabric.commands.SpectatorCommand; import wtf.hak.survivalfabric.commands.SpectatorCommand;
import wtf.hak.survivalfabric.sharedenderchest.SharedEnderChest; import wtf.hak.survivalfabric.sharedenderchest.SharedEnderChest;
import wtf.hak.survivalfabric.veinminer.VeinMinerEvents;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
@ -21,8 +26,22 @@ public class SurvivalFabric implements ModInitializer {
public void onInitialize() { public void onInitialize() {
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SpectatorCommand.register(dispatcher, new String[] { "spectator", "s", "S", "camera", "c", "C", })); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SpectatorCommand.register(dispatcher, new String[] { "spectator", "s", "S", "camera", "c", "C", }));
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> ReloadConfigCommand.register(dispatcher, new String[] { "reloadsurvivalconfig" })); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> ReloadConfigCommand.register(dispatcher, new String[] { "reloadsurvivalconfig" }));
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SlimeChunkCommand.register(dispatcher, new String[] { "slimechunk", "sc" }));
if(getConfig().sharedEnderChestEnabled) if(getConfig().sharedEnderChestEnabled)
new SharedEnderChest().onInitialize(); new SharedEnderChest().onInitialize();
if(getConfig().veinMinerEnabled) {
PlayerBlockBreakEvents.BEFORE.register((world, player, pos, state, blockEntity) -> {
if (player instanceof ServerPlayerEntity serverPlayer) {
return VeinMinerEvents.beforeBlockBreak(world, serverPlayer, pos, state);
}
else {
return true;
}
});
ServerEntityEvents.ENTITY_LOAD.register(VeinMinerEvents::onEntityLoad);
}
} }
} }

View File

@ -0,0 +1,40 @@
package wtf.hak.survivalfabric.commands;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.math.random.CheckedRandom;
import net.minecraft.util.math.random.ChunkRandom;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.chunk.Chunk;
import wtf.hak.survivalfabric.config.ConfigManager;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
public class SlimeChunkCommand {
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, String... aliases) {
for (String str : aliases)
dispatcher.register(CommandManager.literal(str)
.executes(SlimeChunkCommand::execute));
}
private static int execute(CommandContext<ServerCommandSource> context) {
ServerCommandSource source = context.getSource();
if (source.getEntity() == null) {
source.sendMessage(Text.literal("Console cannot go into spectator mode!"));
return 0;
}
ServerPlayerEntity p = (ServerPlayerEntity) source.getEntity();
Chunk chunk = p.getServerWorld().getChunk(p.getBlockPos());
Random slimeRandom = ChunkRandom.getSlimeRandom(chunk.getPos().x, chunk.getPos().z, p.getServerWorld().getSeed(), 987234911L);
if(slimeRandom.nextInt(10) == 0) {
p.sendMessage(Text.literal(getConfig().inSlimeChunkMessage));
} else
p.sendMessage(Text.literal(getConfig().notInSlimeChunkMessage));
return 1;
}
}

View File

@ -32,6 +32,12 @@ public class Config {
public boolean sharedEnderChestLimitedAccess = true; public boolean sharedEnderChestLimitedAccess = true;
public List<String> sharedEnderChestNames = Lists.newArrayList("AlwaysHAK", "LunaticFox"); public List<String> sharedEnderChestNames = Lists.newArrayList("AlwaysHAK", "LunaticFox");
public String inSlimeChunkMessage = "§aYou're currently in a slime chunk";
public String notInSlimeChunkMessage = "§cYou're currently not in a slime chunk";
public boolean veinMinerEnabled = true;
public int maxVeinSize = 99999;
public ScreenHandlerType<GenericContainerScreenHandler> screenHandlerType() { public ScreenHandlerType<GenericContainerScreenHandler> screenHandlerType() {
return switch (sharedEnderChestRows) { return switch (sharedEnderChestRows) {
case 1 -> ScreenHandlerType.GENERIC_9X1; case 1 -> ScreenHandlerType.GENERIC_9X1;

View File

@ -28,6 +28,7 @@ import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, ServerLifecycleEvents.ServerStarted, ServerTickEvents.EndTick { public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, ServerLifecycleEvents.ServerStarted, ServerTickEvents.EndTick {
@ -46,7 +47,7 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S
Inventories.readNbt(nbt, inventoryItemStacks, server.getRegistryManager()); Inventories.readNbt(nbt, inventoryItemStacks, server.getRegistryManager());
sharedInventory = new SharedInventory(inventoryItemStacks); sharedInventory = new SharedInventory(inventoryItemStacks);
} catch (Exception e) { } catch (Exception e) {
System.out.println("[ShareEnderChest] Error while loading inventory: " + e); LOGGER.error("Error while loading Shared Ender Chest: " + e);
sharedInventory = new SharedInventory(getConfig().sharedEnderChestRows); sharedInventory = new SharedInventory(getConfig().sharedEnderChestRows);
} }
} else { } else {
@ -64,10 +65,9 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S
inventoryFile.createNewFile(); inventoryFile.createNewFile();
NbtIo.writeCompressed(nbt, inventoryFileDataOutput); NbtIo.writeCompressed(nbt, inventoryFileDataOutput);
} catch (Exception e) { } catch (Exception e) {
System.out.println("[ShareEnderChest] Error while saving inventory: " + e); LOGGER.error("Error while saving Shared Ender Chest: " + e);
} }
} }
public void onServerStopping(MinecraftServer server) { public void onServerStopping(MinecraftServer server) {
saveInventory(server); saveInventory(server);
} }

View File

@ -0,0 +1,10 @@
package wtf.hak.survivalfabric.veinminer;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
public interface Drill {
boolean canHandle(BlockState blockState);
boolean isRightTool(BlockPos pos);
boolean drill(BlockPos blockPos);
}

View File

@ -0,0 +1,59 @@
package wtf.hak.survivalfabric.veinminer;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.registry.Registries;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import wtf.hak.survivalfabric.veinminer.drills.LeavesDrill;
import wtf.hak.survivalfabric.veinminer.drills.OreDrill;
import wtf.hak.survivalfabric.veinminer.drills.WoodDrill;
public class VeinMinerEvents {
public static boolean beforeBlockBreak(World world, ServerPlayerEntity player, BlockPos pos, BlockState state) {
if (world.isClient) {
return true;
}
boolean isVeinMining = VeinMinerSession.sessionForPlayer(player) != null;
boolean canVeinMine = player.isInSneakingPose();
if (canVeinMine && !isVeinMining) {
VeinMinerSession session = VeinMinerSession.start(player, (ServerWorld)world, pos);
boolean shouldContinue = !mine(session);
session.finish();
return shouldContinue;
}
else {
return true;
}
}
public static void onEntityLoad(Entity entity, ServerWorld world) {
BlockPos pos = entity.getBlockPos();
VeinMinerSession session = VeinMinerSession.sessionForPosition(pos);
if (session != null) {
entity.setPos(session.initialPos.getX(), session.initialPos.getY(), session.initialPos.getZ());
}
}
private static boolean mine(VeinMinerSession session) {
Drill[] drills = new Drill[] {
new OreDrill(session),
new WoodDrill(session),
new LeavesDrill(session)
};
BlockPos pos = session.initialPos;
BlockState blockState = session.world.getBlockState(pos);
for (Drill drill : drills) {
if (drill.canHandle(blockState) && drill.isRightTool(pos)) {
return drill.drill(pos);
}
}
return false;
}
}

View File

@ -0,0 +1,66 @@
package wtf.hak.survivalfabric.veinminer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
public class VeinMinerSession {
private static ArrayList<VeinMinerSession> sessions = new ArrayList<>();
public ServerPlayerEntity player;
public ServerWorld world;
public Set<BlockPos> positions;
public BlockPos initialPos;
public static VeinMinerSession sessionForPlayer(ServerPlayerEntity player) {
for (var session: sessions) {
if (session.player == player) {
return session;
}
}
return null;
}
public static VeinMinerSession sessionForPosition(BlockPos position) {
for (var session: sessions) {
if (session.positions.contains(position)) {
return session;
}
}
return null;
}
public static VeinMinerSession start(ServerPlayerEntity player, ServerWorld world, BlockPos initialPos) {
var session = new VeinMinerSession(player, world, initialPos);
sessions.add(session);
return session;
}
private static void finish(VeinMinerSession session) {
sessions.remove(session);
}
private VeinMinerSession(ServerPlayerEntity player, ServerWorld world, BlockPos initialPos) {
this.player = player;
this.world = world;
this.initialPos = initialPos;
this.positions = new HashSet<>();
positions.add(initialPos);
}
public void addPosition(BlockPos pos) {
positions.add(pos);
}
public void removePosition(BlockPos pos) {
positions.remove(pos);
}
public void finish() {
finish(this);
}
}

View File

@ -0,0 +1,133 @@
package wtf.hak.survivalfabric.veinminer.drills;
import net.minecraft.block.BlockState;
import java.util.ArrayList;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.MutableText;
import net.minecraft.text.PlainTextContent;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import wtf.hak.survivalfabric.veinminer.Drill;
import wtf.hak.survivalfabric.veinminer.VeinMinerSession;
import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER;
public class DrillBase implements Drill {
protected VeinMinerSession session;
public DrillBase(VeinMinerSession session) {
this.session = session;
}
@Override
public boolean canHandle(BlockState blockId) {
return false;
}
@Override
public boolean drill(BlockPos blockPos) {
return false;
}
@Override
public boolean isRightTool(BlockPos pos) {
var blockState = session.world.getBlockState(pos);
return session.player.getMainHandStack().isSuitableFor(blockState);
}
protected interface ForXYZHandler {
public void handle(BlockPos pos);
}
protected interface ForXYZCounter {
public int handle(BlockPos pos);
}
protected void forXYZ(BlockPos pos, int max, ForXYZHandler handler) {
forXYZ(pos, max, handlerPos -> {
handler.handle(handlerPos);
return 0;
}, false);
}
protected int forXYZ(BlockPos pos, int max, ForXYZCounter handler) {
return forXYZ(pos, max, handler, false);
}
protected void forXYZ(BlockPos pos, int max, ForXYZHandler handler, boolean forceVertical) {
forXYZ(pos, max, handlerPos -> {
handler.handle(handlerPos);
return 0;
}, forceVertical);
}
protected int forXYZ(BlockPos pos, int max, ForXYZCounter handler, boolean forceVertical) {
ArrayList<Integer> offsets = new ArrayList<Integer>();
for (int d = 0; d <= max; ++d) {
offsets.add(d);
if (d != -d) {
offsets.add(-d);
}
}
String[] order = new String[] { "x", "y", "z" };
if (forceVertical) {
order = new String[] { "y", "x", "z" };
}
else {
ServerPlayerEntity player = session.player;
boolean majorPitchChange = player.getPitch() < -45.0 || player.getPitch() > 45.0;
boolean majorYawChange = (player.getYaw() > 45.0 && player.getYaw() < 135.0) || (player.getYaw() < -45.0 && player.getYaw() > -135.0);
if (majorPitchChange) {
if (majorYawChange) {
order = new String[] { "y", "z", "x" };
}
else {
order = new String[] { "y", "x", "z" };
}
}
else {
if (majorYawChange) {
order = new String[] { "z", "y", "x" };
}
}
}
int counter = 0;
for (int i1: offsets) {
for (int i2: offsets) {
for (int i3: offsets) {
int ix = order[0] == "x" ? i1 : order[1] == "x" ? i2 : i3;
int iy = order[0] == "y" ? i1 : order[1] == "y" ? i2 : i3;
int iz = order[0] == "z" ? i1 : order[1] == "z" ? i2 : i3;
int px = pos.getX() + ix;
int py = pos.getY() + iy;
int pz = pos.getZ() + iz;
counter += handler.handle(new BlockPos(px, py, pz));
}
}
}
return counter;
}
protected boolean tryBreakBlock(BlockPos blockPos) {
session.addPosition(blockPos);
boolean success = isRightTool(blockPos) && session.player.interactionManager.tryBreakBlock(blockPos);
if (!success) {
session.removePosition(blockPos);
}
return success;
}
protected void _log(String message) {
PlainTextContent literal = new PlainTextContent.Literal(message);
Text text = MutableText.of(literal);
session.player.sendMessage(text);
LOGGER.info(message);
}
}

View File

@ -0,0 +1,62 @@
package wtf.hak.survivalfabric.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.veinminer.VeinMinerSession;
import java.util.ArrayDeque;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
public class LeavesDrill extends DrillBase {
public LeavesDrill(VeinMinerSession session) {
super(session);
}
public static final TagKey<Block> leavesTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "leaves"));
@Override
public boolean canHandle(BlockState blockState) {
return blockState.isIn(leavesTag);
}
@Override
public boolean drill(BlockPos startPos) {
ServerWorld world = session.world;
Block initialBlock = world.getBlockState(startPos).getBlock();
int brokenLeaves = 0;
ArrayDeque<BlockPos> pending = new ArrayDeque<BlockPos>();
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;
}
}

View File

@ -0,0 +1,64 @@
package wtf.hak.survivalfabric.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 java.util.ArrayDeque;
import net.minecraft.registry.Registries;
import net.minecraft.util.math.BlockPos;
import wtf.hak.survivalfabric.veinminer.VeinMinerSession;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
public class OreDrill extends DrillBase {
public OreDrill(VeinMinerSession session) {
super(session);
}
public static final TagKey<Block> oreTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "ore"));
@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<BlockPos> pending = new ArrayDeque<BlockPos>();
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;
}
}

View File

@ -0,0 +1,85 @@
package wtf.hak.survivalfabric.veinminer.drills;
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 java.util.ArrayDeque;
import net.minecraft.block.Block;
import net.minecraft.registry.Registries;
import net.minecraft.util.math.BlockPos;
import wtf.hak.survivalfabric.veinminer.VeinMinerSession;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
public class WoodDrill extends DrillBase {
public WoodDrill(VeinMinerSession session) {
super(session);
}
public static final TagKey<Block> woodTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "wood"));
@Override
public boolean canHandle(BlockState blockState) {
return blockState.isIn(woodTag);
}
@Override
public boolean drill(BlockPos startPos) {
ServerWorld world = session.world;
int broken = 0;
ArrayDeque<BlockPos> pendingLogs = new ArrayDeque<BlockPos>();
ArrayDeque<BlockPos> logBlocks = new ArrayDeque<BlockPos>();
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);
}
}
// second round, leaves
// The pending blocks are all air now,
ArrayDeque<BlockPos> pendingLeaves = logBlocks;
while (!pendingLeaves.isEmpty() && broken < getConfig().maxVeinSize) {
// remove the immediately surrounding leaves around the log blocks
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");
}
}

View File

@ -0,0 +1,5 @@
{
"values": [
"#minecraft:leaves"
]
}

View File

@ -0,0 +1,14 @@
{
"values": [
"#minecraft:iron_ores",
"#minecraft:gold_ores",
"#minecraft:lapis_ores",
"#minecraft:redstone_ores",
"#minecraft:diamond_ores",
"#minecraft:emerald_ores",
"#minecraft:coal_ores",
"#minecraft:copper_ores",
"minecraft:ancient_debris",
"minecraft:magma_block"
]
}

View File

@ -0,0 +1,5 @@
{
"values": [
"#minecraft:logs"
]
}