hollow-cube/prevent-block-updates

(cherry picked from commit 55a1349049)
This commit is contained in:
mworzala 2023-07-16 07:48:05 -04:00 committed by Matt Worzala
parent 7a374a5079
commit bb6a93da89
8 changed files with 137 additions and 19 deletions

View File

@ -6,6 +6,7 @@ import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.demo.block.TestBlockHandler;
import net.minestom.demo.block.placement.DripstonePlacementRule;
import net.minestom.demo.commands.*;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandManager;
@ -28,6 +29,7 @@ public class Main {
MinecraftServer minecraftServer = MinecraftServer.init();
BlockManager blockManager = MinecraftServer.getBlockManager();
blockManager.registerBlockPlacementRule(new DripstonePlacementRule());
blockManager.registerHandler(TestBlockHandler.INSTANCE.getNamespaceId(), () -> TestBlockHandler.INSTANCE);
CommandManager commandManager = MinecraftServer.getCommandManager();

View File

@ -133,6 +133,9 @@ public class PlayerInit {
event.getInstance().setBlock(event.getPosition(), block);
})
.addListener(PlayerBlockPlaceEvent.class, event -> {
event.setDoBlockUpdates(false);
});
static {

View File

@ -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";
}
}

View File

@ -21,6 +21,7 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C
private final Player.Hand hand;
private boolean consumeBlock;
private boolean doBlockUpdates;
private boolean cancelled;
@ -33,6 +34,7 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C
this.blockPosition = blockPosition;
this.hand = hand;
this.consumeBlock = true;
this.doBlockUpdates = true;
}
/**
@ -94,6 +96,22 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C
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
public boolean isCancelled() {
return cancelled;

View File

@ -153,8 +153,24 @@ public abstract class Instance implements Block.Getter, Block.Setter,
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
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}
@ -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
*/
@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.

View File

@ -100,14 +100,14 @@ public class InstanceContainer extends Instance {
}
@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);
if (chunk == null) {
Check.stateCondition(!hasEnabledAutoChunkLoad(),
"Tried to set a block to an unloaded chunk with auto chunk load disabled");
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
*/
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;
synchronized (chunk) {
// Refresh the last block change time
@ -137,7 +138,7 @@ public class InstanceContainer extends Instance {
// Change id based on neighbors
final BlockPlacementRule blockPlacementRule = MinecraftServer.getBlockManager().getBlockPlacementRule(block);
if (blockPlacementRule != null) {
if (blockPlacementRule != null && doBlockUpdates) {
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);
// Refresh neighbors since a new block has been placed
executeNeighboursBlockPlacementRule(blockPosition);
if (doBlockUpdates) {
executeNeighboursBlockPlacementRule(blockPosition);
}
// Refresh player chunk block
{
@ -160,17 +163,17 @@ public class InstanceContainer extends Instance {
}
@Override
public boolean placeBlock(@NotNull BlockHandler.Placement placement) {
public boolean placeBlock(@NotNull BlockHandler.Placement placement, boolean doBlockUpdates) {
final Point blockPosition = placement.getBlockPosition();
final Chunk chunk = getChunkAt(blockPosition);
if (!isLoaded(chunk)) return false;
UNSAFE_setBlock(chunk, blockPosition.blockX(), blockPosition.blockY(), blockPosition.blockZ(),
placement.getBlock(), placement, null);
placement.getBlock(), placement, null, doBlockUpdates);
return true;
}
@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);
Check.notNull(chunk, "You cannot break blocks in a null chunk!");
if (chunk.isReadOnly()) return false;
@ -192,7 +195,7 @@ public class InstanceContainer extends Instance {
// Break or change the broken block based on event result
final Block resultBlock = blockBreakEvent.getResultBlock();
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
PacketUtils.sendGroupedPacket(chunk.getViewers(),
new EffectPacket(2001 /*Block break + block break sound*/, blockPosition, block.stateId(), false),

View File

@ -27,18 +27,18 @@ public class SharedInstance extends Instance {
}
@Override
public void setBlock(int x, int y, int z, @NotNull Block block) {
this.instanceContainer.setBlock(x, y, z, block);
public void setBlock(int x, int y, int z, @NotNull Block block, boolean doBlockUpdates) {
this.instanceContainer.setBlock(x, y, z, block, doBlockUpdates);
}
@Override
public boolean placeBlock(@NotNull BlockHandler.Placement placement) {
return instanceContainer.placeBlock(placement);
public boolean placeBlock(@NotNull BlockHandler.Placement placement, boolean doBlockUpdates) {
return instanceContainer.placeBlock(placement, doBlockUpdates);
}
@Override
public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace) {
return instanceContainer.breakBlock(player, blockPosition, blockFace);
public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace, boolean doBlockUpdates) {
return instanceContainer.breakBlock(player, blockPosition, blockFace, doBlockUpdates);
}
@Override

View File

@ -153,7 +153,7 @@ public class BlockPlacementListener {
// BlockPlacementRule check
Block resultBlock = playerBlockPlaceEvent.getBlock();
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
resultBlock = blockPlacementRule.blockPlace(new BlockPlacementRule.PlacementState(
instance, resultBlock, blockFace,
@ -168,7 +168,7 @@ public class BlockPlacementListener {
// Place the block
player.sendPacket(new AcknowledgeBlockChangePacket(packet.sequence()));
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
if (playerBlockPlaceEvent.doesConsumeBlock()) {
// Consume the block in the player's hand