mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-09 01:47:54 +01:00
Add Placement/Destroy implementation for player
This commit is contained in:
parent
22ba15f390
commit
2b89fa1527
@ -17,6 +17,7 @@ import net.minestom.server.event.EventCallback;
|
|||||||
import net.minestom.server.event.EventDispatcher;
|
import net.minestom.server.event.EventDispatcher;
|
||||||
import net.minestom.server.event.EventFilter;
|
import net.minestom.server.event.EventFilter;
|
||||||
import net.minestom.server.event.EventNode;
|
import net.minestom.server.event.EventNode;
|
||||||
|
import net.minestom.server.event.handler.EventHandler;
|
||||||
import net.minestom.server.event.instance.AddEntityToInstanceEvent;
|
import net.minestom.server.event.instance.AddEntityToInstanceEvent;
|
||||||
import net.minestom.server.event.instance.InstanceTickEvent;
|
import net.minestom.server.event.instance.InstanceTickEvent;
|
||||||
import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent;
|
import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent;
|
||||||
@ -44,8 +45,6 @@ import org.jetbrains.annotations.ApiStatus;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import net.minestom.server.event.handler.EventHandler;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
@ -146,6 +145,8 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ev
|
|||||||
this.nextTick.add(callback);
|
this.nextTick.add(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract boolean placeBlock(@NotNull Player player, @NotNull Block block, @NotNull BlockPosition blockPosition);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does call {@link net.minestom.server.event.player.PlayerBlockBreakEvent}
|
* Does call {@link net.minestom.server.event.player.PlayerBlockBreakEvent}
|
||||||
* and send particle packets
|
* and send particle packets
|
||||||
|
@ -106,13 +106,13 @@ public class InstanceContainer extends Instance {
|
|||||||
public synchronized void setBlock(int x, int y, int z, @NotNull Block block) {
|
public synchronized void setBlock(int x, int y, int z, @NotNull Block block) {
|
||||||
final Chunk chunk = getChunkAt(x, z);
|
final Chunk chunk = getChunkAt(x, z);
|
||||||
if (ChunkUtils.isLoaded(chunk)) {
|
if (ChunkUtils.isLoaded(chunk)) {
|
||||||
UNSAFE_setBlock(chunk, x, y, z, block);
|
UNSAFE_setBlock(chunk, x, y, z, block, null);
|
||||||
} else {
|
} else {
|
||||||
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");
|
||||||
final int chunkX = ChunkUtils.getChunkCoordinate(x);
|
final int chunkX = ChunkUtils.getChunkCoordinate(x);
|
||||||
final int chunkZ = ChunkUtils.getChunkCoordinate(z);
|
final int chunkZ = ChunkUtils.getChunkCoordinate(z);
|
||||||
loadChunk(chunkX, chunkZ, c -> UNSAFE_setBlock(c, x, y, z, block));
|
loadChunk(chunkX, chunkZ, c -> UNSAFE_setBlock(c, x, y, z, block, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,20 +127,16 @@ public class InstanceContainer extends Instance {
|
|||||||
* @param z the block Z
|
* @param z the block Z
|
||||||
* @param block the block to place
|
* @param block the block to place
|
||||||
*/
|
*/
|
||||||
private void UNSAFE_setBlock(@NotNull Chunk chunk, int x, int y, int z, @NotNull Block block) {
|
private void UNSAFE_setBlock(@NotNull Chunk chunk, int x, int y, int z, @NotNull Block block,
|
||||||
|
@Nullable Player player) {
|
||||||
// Cannot place block in a read-only chunk
|
// Cannot place block in a read-only chunk
|
||||||
if (chunk.isReadOnly()) {
|
if (chunk.isReadOnly()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (chunk) {
|
synchronized (chunk) {
|
||||||
|
|
||||||
// Refresh the last block change time
|
// Refresh the last block change time
|
||||||
this.lastBlockChangeTime = System.currentTimeMillis();
|
this.lastBlockChangeTime = System.currentTimeMillis();
|
||||||
|
|
||||||
final BlockPosition blockPosition = new BlockPosition(x, y, z);
|
final BlockPosition blockPosition = new BlockPosition(x, y, z);
|
||||||
|
|
||||||
if (isAlreadyChanged(blockPosition, block)) { // do NOT change the block again.
|
if (isAlreadyChanged(blockPosition, block)) { // do NOT change the block again.
|
||||||
// Avoids StackOverflowExceptions when onDestroy tries to destroy the block itself
|
// Avoids StackOverflowExceptions when onDestroy tries to destroy the block itself
|
||||||
// This can happen with nether portals which break the entire frame when a portal block is broken
|
// This can happen with nether portals which break the entire frame when a portal block is broken
|
||||||
@ -165,85 +161,29 @@ public class InstanceContainer extends Instance {
|
|||||||
|
|
||||||
if (previousHandler != null) {
|
if (previousHandler != null) {
|
||||||
// Previous destroy
|
// Previous destroy
|
||||||
previousHandler.onDestroy(BlockHandler.Destroy.from(previousBlock, this, blockPosition));
|
final var destroy = player != null ?
|
||||||
|
new BlockHandler.PlayerDestroy(block, this, blockPosition, player) :
|
||||||
|
BlockHandler.Destroy.from(previousBlock, this, blockPosition);
|
||||||
|
previousHandler.onDestroy(destroy);
|
||||||
}
|
}
|
||||||
final BlockHandler handler = block.handler();
|
final BlockHandler handler = block.handler();
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
// New placement
|
// New placement
|
||||||
handler.onPlace(BlockHandler.Placement.from(block, this, blockPosition));
|
final var placement = player != null ?
|
||||||
|
new BlockHandler.PlayerPlacement(block, this, blockPosition, player) :
|
||||||
|
BlockHandler.Placement.from(block, this, blockPosition);
|
||||||
|
handler.onPlace(placement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAlreadyChanged(@NotNull BlockPosition blockPosition, Block block) {
|
@Override
|
||||||
currentlyChangingBlocks.put(blockPosition, block);
|
public boolean placeBlock(@NotNull Player player, @NotNull Block block, @NotNull BlockPosition blockPosition) {
|
||||||
}
|
final Chunk chunk = getChunkAt(blockPosition);
|
||||||
|
if (!ChunkUtils.isLoaded(chunk))
|
||||||
/**
|
|
||||||
* Has this block already changed since last update?
|
|
||||||
* Prevents StackOverflow with blocks trying to modify their position in onDestroy or onPlace.
|
|
||||||
*
|
|
||||||
* @param blockPosition the block position
|
|
||||||
* @param block the block
|
|
||||||
* @return true if the block changed since the last update
|
|
||||||
*/
|
|
||||||
private boolean isAlreadyChanged(@NotNull BlockPosition blockPosition, @NotNull Block block) {
|
|
||||||
final Block changedBlock = currentlyChangingBlocks.get(blockPosition);
|
|
||||||
if (changedBlock == null)
|
|
||||||
return false;
|
return false;
|
||||||
return changedBlock.id() == block.id();
|
UNSAFE_setBlock(chunk, blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), block, player);
|
||||||
}
|
return true;
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls the {@link BlockPlacementRule} for the specified block state id.
|
|
||||||
*
|
|
||||||
* @param block the block to modify
|
|
||||||
* @param blockPosition the block position
|
|
||||||
* @return the modified block state id
|
|
||||||
*/
|
|
||||||
private Block executeBlockPlacementRule(Block block, @NotNull BlockPosition blockPosition) {
|
|
||||||
final BlockPlacementRule blockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(block);
|
|
||||||
if (blockPlacementRule != null) {
|
|
||||||
return blockPlacementRule.blockUpdate(this, blockPosition, block);
|
|
||||||
}
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executed when a block is modified, this is used to modify the states of neighbours blocks.
|
|
||||||
* <p>
|
|
||||||
* For example, this can be used for redstone wires which need an understanding of its neighborhoods to take the right shape.
|
|
||||||
*
|
|
||||||
* @param blockPosition the position of the modified block
|
|
||||||
*/
|
|
||||||
private void executeNeighboursBlockPlacementRule(@NotNull 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;
|
|
||||||
final int neighborX = blockPosition.getX() + offsetX;
|
|
||||||
final int neighborY = blockPosition.getY() + offsetY;
|
|
||||||
final int neighborZ = blockPosition.getZ() + offsetZ;
|
|
||||||
final Chunk chunk = getChunkAt(neighborX, neighborZ);
|
|
||||||
|
|
||||||
// Do not try to get neighbour in an unloaded chunk
|
|
||||||
if (chunk == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
final Block neighborBlock = chunk.getBlock(neighborX, neighborY, neighborZ);
|
|
||||||
final BlockPlacementRule neighborBlockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(neighborBlock);
|
|
||||||
if (neighborBlockPlacementRule != null) {
|
|
||||||
final BlockPosition neighborPosition = new BlockPosition(neighborX, neighborY, neighborZ);
|
|
||||||
final Block newNeighborBlock = neighborBlockPlacementRule.blockUpdate(this,
|
|
||||||
neighborPosition, neighborBlock);
|
|
||||||
if (neighborBlock != newNeighborBlock) {
|
|
||||||
setBlock(neighborPosition, newNeighborBlock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -700,6 +640,77 @@ public class InstanceContainer extends Instance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setAlreadyChanged(@NotNull BlockPosition blockPosition, Block block) {
|
||||||
|
currentlyChangingBlocks.put(blockPosition, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has this block already changed since last update?
|
||||||
|
* Prevents StackOverflow with blocks trying to modify their position in onDestroy or onPlace.
|
||||||
|
*
|
||||||
|
* @param blockPosition the block position
|
||||||
|
* @param block the block
|
||||||
|
* @return true if the block changed since the last update
|
||||||
|
*/
|
||||||
|
private boolean isAlreadyChanged(@NotNull BlockPosition blockPosition, @NotNull Block block) {
|
||||||
|
final Block changedBlock = currentlyChangingBlocks.get(blockPosition);
|
||||||
|
if (changedBlock == null)
|
||||||
|
return false;
|
||||||
|
return changedBlock.id() == block.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the {@link BlockPlacementRule} for the specified block state id.
|
||||||
|
*
|
||||||
|
* @param block the block to modify
|
||||||
|
* @param blockPosition the block position
|
||||||
|
* @return the modified block state id
|
||||||
|
*/
|
||||||
|
private Block executeBlockPlacementRule(Block block, @NotNull BlockPosition blockPosition) {
|
||||||
|
final BlockPlacementRule blockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(block);
|
||||||
|
if (blockPlacementRule != null) {
|
||||||
|
return blockPlacementRule.blockUpdate(this, blockPosition, block);
|
||||||
|
}
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executed when a block is modified, this is used to modify the states of neighbours blocks.
|
||||||
|
* <p>
|
||||||
|
* For example, this can be used for redstone wires which need an understanding of its neighborhoods to take the right shape.
|
||||||
|
*
|
||||||
|
* @param blockPosition the position of the modified block
|
||||||
|
*/
|
||||||
|
private void executeNeighboursBlockPlacementRule(@NotNull 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;
|
||||||
|
final int neighborX = blockPosition.getX() + offsetX;
|
||||||
|
final int neighborY = blockPosition.getY() + offsetY;
|
||||||
|
final int neighborZ = blockPosition.getZ() + offsetZ;
|
||||||
|
final Chunk chunk = getChunkAt(neighborX, neighborZ);
|
||||||
|
|
||||||
|
// Do not try to get neighbour in an unloaded chunk
|
||||||
|
if (chunk == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
final Block neighborBlock = chunk.getBlock(neighborX, neighborY, neighborZ);
|
||||||
|
final BlockPlacementRule neighborBlockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(neighborBlock);
|
||||||
|
if (neighborBlockPlacementRule != null) {
|
||||||
|
final BlockPosition neighborPosition = new BlockPosition(neighborX, neighborY, neighborZ);
|
||||||
|
final Block newNeighborBlock = neighborBlockPlacementRule.blockUpdate(this,
|
||||||
|
neighborPosition, neighborBlock);
|
||||||
|
if (neighborBlock != newNeighborBlock) {
|
||||||
|
setBlock(neighborPosition, newNeighborBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void callChunkLoadEvent(int chunkX, int chunkZ) {
|
private void callChunkLoadEvent(int chunkX, int chunkZ) {
|
||||||
InstanceChunkLoadEvent chunkLoadEvent = new InstanceChunkLoadEvent(this, chunkX, chunkZ);
|
InstanceChunkLoadEvent chunkLoadEvent = new InstanceChunkLoadEvent(this, chunkX, chunkZ);
|
||||||
EventDispatcher.call(chunkLoadEvent);
|
EventDispatcher.call(chunkLoadEvent);
|
||||||
|
@ -30,6 +30,11 @@ public class SharedInstance extends Instance {
|
|||||||
this.instanceContainer.setBlock(x, y, z, block);
|
this.instanceContainer.setBlock(x, y, z, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean placeBlock(@NotNull Player player, @NotNull Block block, @NotNull BlockPosition blockPosition) {
|
||||||
|
return instanceContainer.placeBlock(player, block, blockPosition);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean breakBlock(@NotNull Player player, @NotNull BlockPosition blockPosition) {
|
public boolean breakBlock(@NotNull Player player, @NotNull BlockPosition blockPosition) {
|
||||||
return instanceContainer.breakBlock(player, blockPosition);
|
return instanceContainer.breakBlock(player, blockPosition);
|
||||||
|
@ -112,6 +112,39 @@ public interface BlockHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class PlayerPlacement implements Placement {
|
||||||
|
private final Block block;
|
||||||
|
private final Instance instance;
|
||||||
|
private final BlockPosition blockPosition;
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
public PlayerPlacement(Block block, Instance instance, BlockPosition blockPosition, Player player) {
|
||||||
|
this.block = block;
|
||||||
|
this.instance = instance;
|
||||||
|
this.blockPosition = blockPosition;
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Block block() {
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Instance instance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull BlockPosition blockPosition() {
|
||||||
|
return blockPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Player player() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ApiStatus.NonExtendable
|
@ApiStatus.NonExtendable
|
||||||
interface Destroy {
|
interface Destroy {
|
||||||
@NotNull Block block();
|
@NotNull Block block();
|
||||||
@ -140,6 +173,39 @@ public interface BlockHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class PlayerDestroy implements Destroy {
|
||||||
|
private final Block block;
|
||||||
|
private final Instance instance;
|
||||||
|
private final BlockPosition blockPosition;
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
public PlayerDestroy(Block block, Instance instance, BlockPosition blockPosition, Player player) {
|
||||||
|
this.block = block;
|
||||||
|
this.instance = instance;
|
||||||
|
this.blockPosition = blockPosition;
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Block block() {
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Instance instance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull BlockPosition blockPosition() {
|
||||||
|
return blockPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Player player() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ApiStatus.NonExtendable
|
@ApiStatus.NonExtendable
|
||||||
interface Interaction {
|
interface Interaction {
|
||||||
@NotNull Block block();
|
@NotNull Block block();
|
||||||
|
@ -142,7 +142,7 @@ public class BlockPlacementListener {
|
|||||||
final boolean placementRuleCheck = resultBlock != null;
|
final boolean placementRuleCheck = resultBlock != null;
|
||||||
if (placementRuleCheck) {
|
if (placementRuleCheck) {
|
||||||
// Place the block
|
// Place the block
|
||||||
instance.setBlock(blockPosition, resultBlock);
|
instance.placeBlock(player, resultBlock, blockPosition);
|
||||||
// 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