mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-06 16:37:38 +01:00
hollow-cube/prevent-block-updates
(cherry picked from commit 55a1349049
)
This commit is contained in:
parent
7a374a5079
commit
bb6a93da89
@ -6,6 +6,7 @@ import net.kyori.adventure.text.format.Style;
|
|||||||
import net.kyori.adventure.text.format.TextColor;
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
import net.kyori.adventure.text.format.TextDecoration;
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
import net.minestom.demo.block.TestBlockHandler;
|
import net.minestom.demo.block.TestBlockHandler;
|
||||||
|
import net.minestom.demo.block.placement.DripstonePlacementRule;
|
||||||
import net.minestom.demo.commands.*;
|
import net.minestom.demo.commands.*;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.command.CommandManager;
|
import net.minestom.server.command.CommandManager;
|
||||||
@ -28,6 +29,7 @@ public class Main {
|
|||||||
MinecraftServer minecraftServer = MinecraftServer.init();
|
MinecraftServer minecraftServer = MinecraftServer.init();
|
||||||
|
|
||||||
BlockManager blockManager = MinecraftServer.getBlockManager();
|
BlockManager blockManager = MinecraftServer.getBlockManager();
|
||||||
|
blockManager.registerBlockPlacementRule(new DripstonePlacementRule());
|
||||||
blockManager.registerHandler(TestBlockHandler.INSTANCE.getNamespaceId(), () -> TestBlockHandler.INSTANCE);
|
blockManager.registerHandler(TestBlockHandler.INSTANCE.getNamespaceId(), () -> TestBlockHandler.INSTANCE);
|
||||||
|
|
||||||
CommandManager commandManager = MinecraftServer.getCommandManager();
|
CommandManager commandManager = MinecraftServer.getCommandManager();
|
||||||
|
@ -133,6 +133,9 @@ public class PlayerInit {
|
|||||||
|
|
||||||
event.getInstance().setBlock(event.getPosition(), block);
|
event.getInstance().setBlock(event.getPosition(), block);
|
||||||
|
|
||||||
|
})
|
||||||
|
.addListener(PlayerBlockPlaceEvent.class, event -> {
|
||||||
|
event.setDoBlockUpdates(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package net.minestom.demo.block.placement;
|
||||||
|
|
||||||
|
import net.minestom.server.instance.block.Block;
|
||||||
|
import net.minestom.server.instance.block.rule.BlockPlacementRule;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class DripstonePlacementRule extends BlockPlacementRule {
|
||||||
|
private static final String PROP_VERTICAL_DIRECTION = "vertical_direction"; // Tip, frustum, middle(0 or more), base
|
||||||
|
private static final String PROP_THICKNESS = "thickness";
|
||||||
|
|
||||||
|
public DripstonePlacementRule() {
|
||||||
|
super(Block.POINTED_DRIPSTONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Block blockPlace(@NotNull PlacementState placementState) {
|
||||||
|
var blockFace = placementState.blockFace();
|
||||||
|
var y = placementState.cursorPosition().y();
|
||||||
|
return block.withProperty(PROP_VERTICAL_DIRECTION, switch (blockFace) {
|
||||||
|
case TOP -> "up";
|
||||||
|
case BOTTOM -> "down";
|
||||||
|
default -> y < 0.5 ? "up" : "down";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Block blockUpdate(@NotNull UpdateState updateState) {
|
||||||
|
return updateState.currentBlock()
|
||||||
|
.withProperty(PROP_THICKNESS, getThickness(updateState));
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull String getThickness(@NotNull UpdateState updateState) {
|
||||||
|
var direction = updateState.currentBlock().getProperty(PROP_VERTICAL_DIRECTION).equals("up");
|
||||||
|
var abovePosition = updateState.blockPosition().add(0, direction ? 1 : -1, 0);
|
||||||
|
var aboveBlock = updateState.instance().getBlock(abovePosition, Block.Getter.Condition.TYPE);
|
||||||
|
|
||||||
|
// If there is no dripstone above, it is always a tip
|
||||||
|
if (aboveBlock.id() != Block.POINTED_DRIPSTONE.id())
|
||||||
|
return "tip";
|
||||||
|
// If there is an opposite facing dripstone above, it is always a merged tip
|
||||||
|
if ((direction ? "down" : "up").equals(aboveBlock.getProperty(PROP_VERTICAL_DIRECTION)))
|
||||||
|
return "tip_merge";
|
||||||
|
|
||||||
|
// If the dripstone above this is a tip, it is a frustum
|
||||||
|
var aboveThickness = aboveBlock.getProperty(PROP_THICKNESS);
|
||||||
|
if ("tip".equals(aboveThickness) || "tip_merge".equals(aboveThickness))
|
||||||
|
return "frustum";
|
||||||
|
|
||||||
|
// At this point we know that there is a dripstone above, and that the dripstone is facing the same direction.
|
||||||
|
var belowPosition = updateState.blockPosition().add(0, direction ? -1 : 1, 0);
|
||||||
|
var belowBlock = updateState.instance().getBlock(belowPosition, Block.Getter.Condition.TYPE);
|
||||||
|
|
||||||
|
// If there is no dripstone below, it is always a base
|
||||||
|
if (belowBlock.id() != Block.POINTED_DRIPSTONE.id())
|
||||||
|
return "base";
|
||||||
|
|
||||||
|
// Otherwise it is a middle
|
||||||
|
return "middle";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,7 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C
|
|||||||
private final Player.Hand hand;
|
private final Player.Hand hand;
|
||||||
|
|
||||||
private boolean consumeBlock;
|
private boolean consumeBlock;
|
||||||
|
private boolean doBlockUpdates;
|
||||||
|
|
||||||
private boolean cancelled;
|
private boolean cancelled;
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C
|
|||||||
this.blockPosition = blockPosition;
|
this.blockPosition = blockPosition;
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
this.consumeBlock = true;
|
this.consumeBlock = true;
|
||||||
|
this.doBlockUpdates = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,6 +96,22 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C
|
|||||||
return consumeBlock;
|
return consumeBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the place trigger updates (on self and neighbors)
|
||||||
|
* @param doBlockUpdates true if this placement should do block updates
|
||||||
|
*/
|
||||||
|
public void setDoBlockUpdates(boolean doBlockUpdates) {
|
||||||
|
this.doBlockUpdates = doBlockUpdates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the place trigger updates (on self and neighbors)
|
||||||
|
* @return true if this placement should do block updates
|
||||||
|
*/
|
||||||
|
public boolean shouldDoBlockUpdates() {
|
||||||
|
return doBlockUpdates;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCancelled() {
|
public boolean isCancelled() {
|
||||||
return cancelled;
|
return cancelled;
|
||||||
|
@ -153,8 +153,24 @@ public abstract class Instance implements Block.Getter, Block.Setter,
|
|||||||
this.scheduler.scheduleNextTick(() -> callback.accept(this));
|
this.scheduler.scheduleNextTick(() -> callback.accept(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlock(int x, int y, int z, @NotNull Block block) {
|
||||||
|
setBlock(x, y, z, block, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlock(@NotNull Point blockPosition, @NotNull Block block, boolean doBlockUpdates) {
|
||||||
|
setBlock(blockPosition.blockX(), blockPosition.blockY(), blockPosition.blockZ(), block, doBlockUpdates);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void setBlock(int x, int y, int z, @NotNull Block block, boolean doBlockUpdates);
|
||||||
|
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public abstract boolean placeBlock(@NotNull BlockHandler.Placement placement);
|
public boolean placeBlock(@NotNull BlockHandler.Placement placement) {
|
||||||
|
return placeBlock(placement, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public abstract boolean placeBlock(@NotNull BlockHandler.Placement placement, boolean doBlockUpdates);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does call {@link net.minestom.server.event.player.PlayerBlockBreakEvent}
|
* Does call {@link net.minestom.server.event.player.PlayerBlockBreakEvent}
|
||||||
@ -165,7 +181,21 @@ public abstract class Instance implements Block.Getter, Block.Setter,
|
|||||||
* @return true if the block has been broken, false if it has been cancelled
|
* @return true if the block has been broken, false if it has been cancelled
|
||||||
*/
|
*/
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public abstract boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace);
|
public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace) {
|
||||||
|
return breakBlock(player, blockPosition, blockFace, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does call {@link net.minestom.server.event.player.PlayerBlockBreakEvent}
|
||||||
|
* and send particle packets
|
||||||
|
*
|
||||||
|
* @param player the {@link Player} who break the block
|
||||||
|
* @param blockPosition the position of the broken block
|
||||||
|
* @param doBlockUpdates true to do block updates, false otherwise
|
||||||
|
* @return true if the block has been broken, false if it has been cancelled
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public abstract boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace, boolean doBlockUpdates);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forces the generation of a {@link Chunk}, even if no file and {@link ChunkGenerator} are defined.
|
* Forces the generation of a {@link Chunk}, even if no file and {@link ChunkGenerator} are defined.
|
||||||
|
@ -100,14 +100,14 @@ public class InstanceContainer extends Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBlock(int x, int y, int z, @NotNull Block block) {
|
public void setBlock(int x, int y, int z, @NotNull Block block, boolean doBlockUpdates) {
|
||||||
Chunk chunk = getChunkAt(x, z);
|
Chunk chunk = getChunkAt(x, z);
|
||||||
if (chunk == null) {
|
if (chunk == null) {
|
||||||
Check.stateCondition(!hasEnabledAutoChunkLoad(),
|
Check.stateCondition(!hasEnabledAutoChunkLoad(),
|
||||||
"Tried to set a block to an unloaded chunk with auto chunk load disabled");
|
"Tried to set a block to an unloaded chunk with auto chunk load disabled");
|
||||||
chunk = loadChunk(getChunkCoordinate(x), getChunkCoordinate(z)).join();
|
chunk = loadChunk(getChunkCoordinate(x), getChunkCoordinate(z)).join();
|
||||||
}
|
}
|
||||||
if (isLoaded(chunk)) UNSAFE_setBlock(chunk, x, y, z, block, null, null);
|
if (isLoaded(chunk)) UNSAFE_setBlock(chunk, x, y, z, block, null, null, doBlockUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,7 +122,8 @@ public class InstanceContainer extends Instance {
|
|||||||
* @param block the block to place
|
* @param block the block to place
|
||||||
*/
|
*/
|
||||||
private synchronized void UNSAFE_setBlock(@NotNull Chunk chunk, int x, int y, int z, @NotNull Block block,
|
private synchronized void UNSAFE_setBlock(@NotNull Chunk chunk, int x, int y, int z, @NotNull Block block,
|
||||||
@Nullable BlockHandler.Placement placement, @Nullable BlockHandler.Destroy destroy) {
|
@Nullable BlockHandler.Placement placement, @Nullable BlockHandler.Destroy destroy,
|
||||||
|
boolean doBlockUpdates) {
|
||||||
if (chunk.isReadOnly()) return;
|
if (chunk.isReadOnly()) return;
|
||||||
synchronized (chunk) {
|
synchronized (chunk) {
|
||||||
// Refresh the last block change time
|
// Refresh the last block change time
|
||||||
@ -137,7 +138,7 @@ public class InstanceContainer extends Instance {
|
|||||||
|
|
||||||
// Change id based on neighbors
|
// Change id based on neighbors
|
||||||
final BlockPlacementRule blockPlacementRule = MinecraftServer.getBlockManager().getBlockPlacementRule(block);
|
final BlockPlacementRule blockPlacementRule = MinecraftServer.getBlockManager().getBlockPlacementRule(block);
|
||||||
if (blockPlacementRule != null) {
|
if (blockPlacementRule != null && doBlockUpdates) {
|
||||||
block = blockPlacementRule.blockUpdate(new BlockPlacementRule.UpdateState(this, blockPosition, block));
|
block = blockPlacementRule.blockUpdate(new BlockPlacementRule.UpdateState(this, blockPosition, block));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +146,9 @@ public class InstanceContainer extends Instance {
|
|||||||
chunk.setBlock(x, y, z, block, placement, destroy);
|
chunk.setBlock(x, y, z, block, placement, destroy);
|
||||||
|
|
||||||
// Refresh neighbors since a new block has been placed
|
// Refresh neighbors since a new block has been placed
|
||||||
executeNeighboursBlockPlacementRule(blockPosition);
|
if (doBlockUpdates) {
|
||||||
|
executeNeighboursBlockPlacementRule(blockPosition);
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh player chunk block
|
// Refresh player chunk block
|
||||||
{
|
{
|
||||||
@ -160,17 +163,17 @@ public class InstanceContainer extends Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean placeBlock(@NotNull BlockHandler.Placement placement) {
|
public boolean placeBlock(@NotNull BlockHandler.Placement placement, boolean doBlockUpdates) {
|
||||||
final Point blockPosition = placement.getBlockPosition();
|
final Point blockPosition = placement.getBlockPosition();
|
||||||
final Chunk chunk = getChunkAt(blockPosition);
|
final Chunk chunk = getChunkAt(blockPosition);
|
||||||
if (!isLoaded(chunk)) return false;
|
if (!isLoaded(chunk)) return false;
|
||||||
UNSAFE_setBlock(chunk, blockPosition.blockX(), blockPosition.blockY(), blockPosition.blockZ(),
|
UNSAFE_setBlock(chunk, blockPosition.blockX(), blockPosition.blockY(), blockPosition.blockZ(),
|
||||||
placement.getBlock(), placement, null);
|
placement.getBlock(), placement, null, doBlockUpdates);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace) {
|
public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace, boolean doBlockUpdates) {
|
||||||
final Chunk chunk = getChunkAt(blockPosition);
|
final Chunk chunk = getChunkAt(blockPosition);
|
||||||
Check.notNull(chunk, "You cannot break blocks in a null chunk!");
|
Check.notNull(chunk, "You cannot break blocks in a null chunk!");
|
||||||
if (chunk.isReadOnly()) return false;
|
if (chunk.isReadOnly()) return false;
|
||||||
@ -192,7 +195,7 @@ public class InstanceContainer extends Instance {
|
|||||||
// Break or change the broken block based on event result
|
// Break or change the broken block based on event result
|
||||||
final Block resultBlock = blockBreakEvent.getResultBlock();
|
final Block resultBlock = blockBreakEvent.getResultBlock();
|
||||||
UNSAFE_setBlock(chunk, x, y, z, resultBlock, null,
|
UNSAFE_setBlock(chunk, x, y, z, resultBlock, null,
|
||||||
new BlockHandler.PlayerDestroy(block, this, blockPosition, player));
|
new BlockHandler.PlayerDestroy(block, this, blockPosition, player), doBlockUpdates);
|
||||||
// Send the block break effect packet
|
// Send the block break effect packet
|
||||||
PacketUtils.sendGroupedPacket(chunk.getViewers(),
|
PacketUtils.sendGroupedPacket(chunk.getViewers(),
|
||||||
new EffectPacket(2001 /*Block break + block break sound*/, blockPosition, block.stateId(), false),
|
new EffectPacket(2001 /*Block break + block break sound*/, blockPosition, block.stateId(), false),
|
||||||
|
@ -27,18 +27,18 @@ public class SharedInstance extends Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBlock(int x, int y, int z, @NotNull Block block) {
|
public void setBlock(int x, int y, int z, @NotNull Block block, boolean doBlockUpdates) {
|
||||||
this.instanceContainer.setBlock(x, y, z, block);
|
this.instanceContainer.setBlock(x, y, z, block, doBlockUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean placeBlock(@NotNull BlockHandler.Placement placement) {
|
public boolean placeBlock(@NotNull BlockHandler.Placement placement, boolean doBlockUpdates) {
|
||||||
return instanceContainer.placeBlock(placement);
|
return instanceContainer.placeBlock(placement, doBlockUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace) {
|
public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace, boolean doBlockUpdates) {
|
||||||
return instanceContainer.breakBlock(player, blockPosition, blockFace);
|
return instanceContainer.breakBlock(player, blockPosition, blockFace, doBlockUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -153,7 +153,7 @@ public class BlockPlacementListener {
|
|||||||
// BlockPlacementRule check
|
// BlockPlacementRule check
|
||||||
Block resultBlock = playerBlockPlaceEvent.getBlock();
|
Block resultBlock = playerBlockPlaceEvent.getBlock();
|
||||||
final BlockPlacementRule blockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(resultBlock);
|
final BlockPlacementRule blockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(resultBlock);
|
||||||
if (blockPlacementRule != null) {
|
if (blockPlacementRule != null && playerBlockPlaceEvent.shouldDoBlockUpdates()) {
|
||||||
// Get id from block placement rule instead of the event
|
// Get id from block placement rule instead of the event
|
||||||
resultBlock = blockPlacementRule.blockPlace(new BlockPlacementRule.PlacementState(
|
resultBlock = blockPlacementRule.blockPlace(new BlockPlacementRule.PlacementState(
|
||||||
instance, resultBlock, blockFace,
|
instance, resultBlock, blockFace,
|
||||||
@ -168,7 +168,7 @@ public class BlockPlacementListener {
|
|||||||
// Place the block
|
// Place the block
|
||||||
player.sendPacket(new AcknowledgeBlockChangePacket(packet.sequence()));
|
player.sendPacket(new AcknowledgeBlockChangePacket(packet.sequence()));
|
||||||
instance.placeBlock(new BlockHandler.PlayerPlacement(resultBlock, instance, placementPosition, player, hand, blockFace,
|
instance.placeBlock(new BlockHandler.PlayerPlacement(resultBlock, instance, placementPosition, player, hand, blockFace,
|
||||||
packet.cursorPositionX(), packet.cursorPositionY(), packet.cursorPositionZ()));
|
packet.cursorPositionX(), packet.cursorPositionY(), packet.cursorPositionZ()), playerBlockPlaceEvent.shouldDoBlockUpdates());
|
||||||
// Block consuming
|
// Block consuming
|
||||||
if (playerBlockPlaceEvent.doesConsumeBlock()) {
|
if (playerBlockPlaceEvent.doesConsumeBlock()) {
|
||||||
// Consume the block in the player's hand
|
// Consume the block in the player's hand
|
||||||
|
Loading…
Reference in New Issue
Block a user