diff --git a/src/main/java/fr/themode/demo/Main.java b/src/main/java/fr/themode/demo/Main.java index bbb61e152..fa0d7a7e9 100644 --- a/src/main/java/fr/themode/demo/Main.java +++ b/src/main/java/fr/themode/demo/Main.java @@ -7,6 +7,7 @@ import fr.themode.demo.commands.SimpleCommand; import fr.themode.minestom.MinecraftServer; import fr.themode.minestom.command.CommandManager; import fr.themode.minestom.instance.block.BlockManager; +import fr.themode.minestom.instance.block.rule.vanilla.RedstonePlacementRule; import fr.themode.minestom.item.ItemStack; import fr.themode.minestom.item.Material; import fr.themode.minestom.net.packet.server.play.DeclareRecipesPacket; @@ -23,6 +24,8 @@ public class Main { blockManager.registerCustomBlock(new StoneBlock()); blockManager.registerCustomBlock(new UpdatableBlockDemo()); + blockManager.registerBlockPlacementRule(new RedstonePlacementRule()); + CommandManager commandManager = MinecraftServer.getCommandManager(); commandManager.register(new HealthCommand()); commandManager.register(new SimpleCommand()); diff --git a/src/main/java/fr/themode/minestom/instance/Chunk.java b/src/main/java/fr/themode/minestom/instance/Chunk.java index ba9f9ed7e..5762b2bcb 100644 --- a/src/main/java/fr/themode/minestom/instance/Chunk.java +++ b/src/main/java/fr/themode/minestom/instance/Chunk.java @@ -90,7 +90,6 @@ public class Chunk implements Viewable { } private void setBlock(int index, short blockId, short customId, Data data, UpdateConsumer updateConsumer) { - if (blockId != 0 || (blockId == 0 && customId != 0 && updateConsumer != null)) { // Allow custom air block for update purpose, refused if no update consumer has been found refreshBlockValue(index, blockId, customId); diff --git a/src/main/java/fr/themode/minestom/instance/InstanceContainer.java b/src/main/java/fr/themode/minestom/instance/InstanceContainer.java index 043416162..85967b849 100644 --- a/src/main/java/fr/themode/minestom/instance/InstanceContainer.java +++ b/src/main/java/fr/themode/minestom/instance/InstanceContainer.java @@ -7,6 +7,7 @@ import fr.themode.minestom.event.PlayerBlockBreakEvent; import fr.themode.minestom.instance.batch.BlockBatch; import fr.themode.minestom.instance.batch.ChunkBatch; import fr.themode.minestom.instance.block.CustomBlock; +import fr.themode.minestom.instance.block.rule.BlockPlacementRule; import fr.themode.minestom.net.PacketWriterUtils; import fr.themode.minestom.net.packet.server.play.BlockChangePacket; import fr.themode.minestom.net.packet.server.play.ParticlePacket; @@ -52,8 +53,14 @@ public class InstanceContainer extends Instance { callBlockDestroy(chunk, index, x, y, z); + BlockPosition blockPosition = new BlockPosition(x, y, z); + + blockId = executeBlockPlacementRule(blockId, blockPosition); + chunk.UNSAFE_setBlock(index, blockId, data); + executeNeighboursBlockPlacementRule(blockId, blockPosition); + // TODO instead of sending a block change packet each time, cache changed blocks and flush them every tick with a MultiBlockChangePacket sendBlockChange(chunk, x, y, z, blockId); } @@ -70,12 +77,18 @@ public class InstanceContainer extends Instance { callBlockDestroy(chunk, index, x, y, z); + BlockPosition blockPosition = new BlockPosition(x, y, z); + + blockId = executeBlockPlacementRule(blockId, blockPosition); + chunk.UNSAFE_setCustomBlock(index, blockId, data); - short id = BLOCK_MANAGER.getBlock(blockId).getType(); + + executeNeighboursBlockPlacementRule(blockId, blockPosition); callBlockPlace(chunk, index, x, y, z); // TODO instead of sending a block change packet each time, cache changed blocks and flush them every tick with a MultiBlockChangePacket + short id = BLOCK_MANAGER.getBlock(blockId).getType(); sendBlockChange(chunk, x, y, z, id); } } @@ -110,6 +123,38 @@ public class InstanceContainer extends Instance { actualBlock.onPlace(this, new BlockPosition(x, y, z), previousData); } + private short executeBlockPlacementRule(short blockId, BlockPosition blockPosition) { + + BlockPlacementRule blockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(blockId); + if (blockPlacementRule != null) { + return blockPlacementRule.blockRefresh(this, blockPosition); + } + return blockId; + } + + private void executeNeighboursBlockPlacementRule(short blockId, BlockPosition blockPosition) { + for (int offsetX = -1; offsetX < 2; offsetX++) { + for (int offsetY = -1; offsetY < 2; offsetY++) { + for (int offsetZ = -1; offsetZ < 2; offsetZ++) { + if (offsetX == 0 && offsetY == 0 && offsetZ == 0) + continue; + int neighborX = blockPosition.getX() + offsetX; + int neighborY = blockPosition.getY() + offsetY; + int neighborZ = blockPosition.getZ() + offsetZ; + short neighborId = getBlockId(neighborX, neighborY, neighborZ); + BlockPlacementRule neighborBlockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(neighborId); + if (neighborBlockPlacementRule != null) { + short newNeighborId = neighborBlockPlacementRule.blockRefresh(this, + new BlockPosition(neighborX, neighborY, neighborZ)); + if (neighborId != newNeighborId) { + refreshBlockId(neighborX, neighborY, neighborZ, newNeighborId); + } + } + } + } + } + } + @Override public void breakBlock(Player player, BlockPosition blockPosition) { Chunk chunk = getChunkAt(blockPosition); diff --git a/src/main/java/fr/themode/minestom/instance/block/Block.java b/src/main/java/fr/themode/minestom/instance/block/Block.java index 33802c9cd..d6dfda6a7 100644 --- a/src/main/java/fr/themode/minestom/instance/block/Block.java +++ b/src/main/java/fr/themode/minestom/instance/block/Block.java @@ -1,5 +1,7 @@ package fr.themode.minestom.instance.block; +import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -688,6 +690,12 @@ public enum Block { HONEY_BLOCK, HONEYCOMB_BLOCK; + private static Short2ObjectOpenHashMap blocksMap = new Short2ObjectOpenHashMap<>(); + + public static Block getBlockFromId(short blockId) { + return blocksMap.getOrDefault(blockId, AIR); + } + private short blockId; private List blockAlternatives = new ArrayList<>(); @@ -697,6 +705,7 @@ public enum Block { public void addBlockAlternative(short id, String... properties) { this.blockAlternatives.add(new BlockAlternative(id, properties)); + blocksMap.put(id, this); } public short withProperties(String... properties) { @@ -705,8 +714,8 @@ public enum Block { return blockAlternative.id; } } - // No id found - return 0; + // No id found, return default + return blockId; } public short getBlockId() { @@ -734,5 +743,13 @@ public enum Block { public String[] getProperties() { return properties; } + + @Override + public String toString() { + return "BlockAlternative{" + + "id=" + id + + ", properties=" + Arrays.toString(properties) + + '}'; + } } } diff --git a/src/main/java/fr/themode/minestom/instance/block/BlockManager.java b/src/main/java/fr/themode/minestom/instance/block/BlockManager.java index e2951d2d8..5149abcc2 100644 --- a/src/main/java/fr/themode/minestom/instance/block/BlockManager.java +++ b/src/main/java/fr/themode/minestom/instance/block/BlockManager.java @@ -25,6 +25,11 @@ public class BlockManager { this.placementRules.put(blockPlacementRule.getBlock(), blockPlacementRule); } + public BlockPlacementRule getBlockPlacementRule(short blockId) { + Block block = Block.getBlockFromId(blockId); + return this.placementRules.get(block); + } + public CustomBlock getBlock(String identifier) { return blocksId.get(identifier); } diff --git a/src/main/java/fr/themode/minestom/instance/block/rule/BlockPlacementRule.java b/src/main/java/fr/themode/minestom/instance/block/rule/BlockPlacementRule.java index 10c683cee..d08193bde 100644 --- a/src/main/java/fr/themode/minestom/instance/block/rule/BlockPlacementRule.java +++ b/src/main/java/fr/themode/minestom/instance/block/rule/BlockPlacementRule.java @@ -12,9 +12,7 @@ public abstract class BlockPlacementRule { this.block = block; } - public abstract void onPlace(Instance instance, BlockPosition blockPosition); - - public abstract void onNeighborPlace(Instance instance, int offsetX, int offsetY, int offsetZ); + public abstract short blockRefresh(Instance instance, BlockPosition blockPosition); public Block getBlock() { return block; diff --git a/src/main/java/fr/themode/minestom/instance/block/rule/vanilla/RedstonePlacementRule.java b/src/main/java/fr/themode/minestom/instance/block/rule/vanilla/RedstonePlacementRule.java new file mode 100644 index 000000000..e1d55f5ac --- /dev/null +++ b/src/main/java/fr/themode/minestom/instance/block/rule/vanilla/RedstonePlacementRule.java @@ -0,0 +1,68 @@ +package fr.themode.minestom.instance.block.rule.vanilla; + +import fr.themode.minestom.instance.Instance; +import fr.themode.minestom.instance.block.Block; +import fr.themode.minestom.instance.block.rule.BlockPlacementRule; +import fr.themode.minestom.utils.BlockPosition; + +public class RedstonePlacementRule extends BlockPlacementRule { + + + public RedstonePlacementRule() { + super(Block.REDSTONE_WIRE); + } + + @Override + public short blockRefresh(Instance instance, BlockPosition blockPosition) { + int x = blockPosition.getX(); + int y = blockPosition.getY(); + int z = blockPosition.getZ(); + + String east = "none"; + String north = "none"; + String power = "0"; + String south = "none"; + String west = "none"; + + if (isRedstone(instance, x + 1, y + 1, z)) { + east = "up"; + } else if (isRedstone(instance, x + 1, y, z)) { + east = "side"; + } else if (isRedstone(instance, x + 1, y - 1, z)) { + east = "side"; + } + + if (isRedstone(instance, x - 1, y + 1, z)) { + west = "up"; + } else if (isRedstone(instance, x - 1, y, z)) { + west = "side"; + } else if (isRedstone(instance, x - 1, y - 1, z)) { + west = "side"; + } + + if (isRedstone(instance, x, y + 1, z + 1)) { + south = "up"; + } else if (isRedstone(instance, x, y, z + 1)) { + south = "side"; + } else if (isRedstone(instance, x, y - 1, z + 1)) { + south = "side"; + } + + if (isRedstone(instance, x, y + 1, z - 1)) { + north = "up"; + } else if (isRedstone(instance, x, y, z - 1)) { + north = "side"; + } else if (isRedstone(instance, x, y - 1, z - 1)) { + north = "side"; + } + + + return Block.REDSTONE_WIRE.withProperties(east, north, power, south, west); + } + + private boolean isRedstone(Instance instance, int x, int y, int z) { + short blockId = instance.getBlockId(x, y, z); + return Block.getBlockFromId(blockId) == Block.REDSTONE_WIRE; + } + +} diff --git a/src/main/java/fr/themode/minestom/registry/RegistryMain.java b/src/main/java/fr/themode/minestom/registry/RegistryMain.java index 5db2fd015..67780a38e 100644 --- a/src/main/java/fr/themode/minestom/registry/RegistryMain.java +++ b/src/main/java/fr/themode/minestom/registry/RegistryMain.java @@ -45,7 +45,7 @@ public class RegistryMain { for (RegistryBlock.BlockState blockState : registryBlock.states) { short id = blockState.id; - String[] properties = blockState.propertiesValues.toArray(new String[registryBlock.states.size()]); + String[] properties = blockState.propertiesValues.toArray(new String[blockState.propertiesValues.size()]); block.addBlockAlternative(id, properties); } }