mirror of https://github.com/Minestom/Minestom.git
Do not crash with a stack overflow when trying to change the block at 'blockPosition' in CustomBlock
This commit is contained in:
parent
cad5dfb8eb
commit
8b41c7550b
|
@ -20,7 +20,12 @@ public class StoneBlock extends CustomBlock {
|
|||
|
||||
@Override
|
||||
public void onDestroy(Instance instance, BlockPosition blockPosition, Data data) {
|
||||
|
||||
BlockPosition above = blockPosition.clone().add(0, 1, 0);
|
||||
CustomBlock blockAbove = instance.getCustomBlock(above);
|
||||
if(blockAbove == this) {
|
||||
instance.setBlock(above, Block.AIR);
|
||||
instance.setBlock(blockPosition, Block.AIR); // this should NOT create a stack overflow simply because we are trying to remove this same block
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -365,4 +365,10 @@ public abstract class Instance implements BlockModifier, DataContainer {
|
|||
*/
|
||||
public abstract void scheduleUpdate(int time, TimeUnit unit, BlockPosition position);
|
||||
|
||||
/**
|
||||
* Performs a single tick in the instance.
|
||||
* By default, does nothing
|
||||
* @param time the current time
|
||||
*/
|
||||
public void tick(long time) { }
|
||||
}
|
|
@ -7,6 +7,7 @@ import net.minestom.server.entity.Player;
|
|||
import net.minestom.server.event.PlayerBlockBreakEvent;
|
||||
import net.minestom.server.instance.batch.BlockBatch;
|
||||
import net.minestom.server.instance.batch.ChunkBatch;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.instance.block.rule.BlockPlacementRule;
|
||||
import net.minestom.server.network.PacketWriterUtils;
|
||||
|
@ -28,6 +29,9 @@ import net.minestom.server.world.Dimension;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
|
@ -42,6 +46,9 @@ public class InstanceContainer extends Instance {
|
|||
private ChunkGenerator chunkGenerator;
|
||||
private Map<Long, Chunk> chunks = new ConcurrentHashMap<>();
|
||||
|
||||
private ReadWriteLock changingBlockLock = new ReentrantReadWriteLock();
|
||||
private Map<BlockPosition, Block> currentlyChangingBlocks = new HashMap<>();
|
||||
|
||||
private boolean autoChunkLoad;
|
||||
|
||||
protected InstanceContainer(UUID uniqueId, Dimension dimension, StorageFolder storageFolder) {
|
||||
|
@ -76,6 +83,13 @@ public class InstanceContainer extends Instance {
|
|||
|
||||
BlockPosition blockPosition = new BlockPosition(x, y, z);
|
||||
|
||||
if(isAlreadyChanged(blockPosition, blockId)) { // do NOT change the block again.
|
||||
// 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
|
||||
return;
|
||||
}
|
||||
setAlreadyChanged(blockPosition, blockId);
|
||||
|
||||
// Call the destroy listener if previous block was a custom block
|
||||
callBlockDestroy(chunk, index, blockPosition);
|
||||
|
||||
|
@ -102,6 +116,23 @@ public class InstanceContainer extends Instance {
|
|||
}
|
||||
}
|
||||
|
||||
private void setAlreadyChanged(BlockPosition blockPosition, short blockId) {
|
||||
currentlyChangingBlocks.put(blockPosition, Block.fromId(blockId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Has this block already changed since last update? Prevents StackOverflow with blocks trying to modify their position in onDestroy or onPlace
|
||||
* @param blockPosition
|
||||
* @param blockId
|
||||
* @return
|
||||
*/
|
||||
private boolean isAlreadyChanged(BlockPosition blockPosition, short blockId) {
|
||||
Block changedBlock = currentlyChangingBlocks.get(blockPosition);
|
||||
if(changedBlock == null)
|
||||
return false;
|
||||
return changedBlock.getBlockId() == blockId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshBlockId(BlockPosition blockPosition, short blockId) {
|
||||
Chunk chunk = getChunkAt(blockPosition.getX(), blockPosition.getZ());
|
||||
|
@ -416,4 +447,13 @@ public class InstanceContainer extends Instance {
|
|||
}
|
||||
}, new UpdateOption(time, unit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(long time) {
|
||||
super.tick(time);
|
||||
Lock wrlock = changingBlockLock.writeLock();
|
||||
wrlock.lock();
|
||||
currentlyChangingBlocks.clear();
|
||||
wrlock.unlock();
|
||||
}
|
||||
}
|
|
@ -54,6 +54,7 @@ public class InstanceManager {
|
|||
if (instance instanceof InstanceContainer) { // SharedInstance should be updated at the same time (verify?)
|
||||
|
||||
blocksPool.execute(() -> {
|
||||
instance.tick(time);
|
||||
for (Chunk chunk : instance.getChunks()) {
|
||||
chunk.updateBlocks(time, instance);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue