mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-04 23:47:59 +01:00
Simplify InstanceContainer
This commit is contained in:
parent
af50bbb440
commit
6c242cbc7f
@ -20,6 +20,7 @@ import net.minestom.server.network.packet.server.play.EffectPacket;
|
|||||||
import net.minestom.server.network.packet.server.play.UnloadChunkPacket;
|
import net.minestom.server.network.packet.server.play.UnloadChunkPacket;
|
||||||
import net.minestom.server.storage.StorageLocation;
|
import net.minestom.server.storage.StorageLocation;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketUtils;
|
||||||
|
import net.minestom.server.utils.async.AsyncUtils;
|
||||||
import net.minestom.server.utils.chunk.ChunkSupplier;
|
import net.minestom.server.utils.chunk.ChunkSupplier;
|
||||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||||
import net.minestom.server.utils.validate.Check;
|
import net.minestom.server.utils.validate.Check;
|
||||||
@ -32,9 +33,8 @@ import java.util.*;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.function.Supplier;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* InstanceContainer is an instance that contains chunks in contrary to SharedInstance.
|
* InstanceContainer is an instance that contains chunks in contrary to SharedInstance.
|
||||||
@ -50,7 +50,7 @@ public class InstanceContainer extends Instance {
|
|||||||
// used as a monitor when access is required
|
// used as a monitor when access is required
|
||||||
private final Long2ObjectMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
|
private final Long2ObjectMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
private final ReadWriteLock changingBlockLock = new ReentrantReadWriteLock();
|
private final Lock changingBlockLock = new ReentrantLock();
|
||||||
private final Map<Point, Block> currentlyChangingBlocks = new HashMap<>();
|
private final Map<Point, Block> currentlyChangingBlocks = new HashMap<>();
|
||||||
|
|
||||||
// the chunk loader, used when trying to load/save a chunk from another source
|
// the chunk loader, used when trying to load/save a chunk from another source
|
||||||
@ -74,10 +74,8 @@ public class InstanceContainer extends Instance {
|
|||||||
*/
|
*/
|
||||||
public InstanceContainer(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType) {
|
public InstanceContainer(@NotNull UUID uniqueId, @NotNull DimensionType dimensionType) {
|
||||||
super(uniqueId, dimensionType);
|
super(uniqueId, dimensionType);
|
||||||
|
|
||||||
// Set the default chunk supplier using DynamicChunk
|
// Set the default chunk supplier using DynamicChunk
|
||||||
setChunkSupplier(DynamicChunk::new);
|
setChunkSupplier(DynamicChunk::new);
|
||||||
|
|
||||||
// Set the default chunk loader which use the Anvil format
|
// Set the default chunk loader which use the Anvil format
|
||||||
setChunkLoader(new AnvilLoader("world"));
|
setChunkLoader(new AnvilLoader("world"));
|
||||||
this.chunkLoader.loadInstance(this);
|
this.chunkLoader.loadInstance(this);
|
||||||
@ -110,10 +108,7 @@ public class InstanceContainer extends Instance {
|
|||||||
*/
|
*/
|
||||||
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 BlockHandler.Placement placement, @Nullable BlockHandler.Destroy destroy) {
|
@Nullable BlockHandler.Placement placement, @Nullable BlockHandler.Destroy destroy) {
|
||||||
// Cannot place block in a read-only chunk
|
if (chunk.isReadOnly()) return;
|
||||||
if (chunk.isReadOnly()) {
|
|
||||||
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();
|
||||||
@ -123,13 +118,16 @@ public class InstanceContainer extends Instance {
|
|||||||
// 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
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setAlreadyChanged(blockPosition, block);
|
this.currentlyChangingBlocks.put(blockPosition, block);
|
||||||
|
|
||||||
final Block previousBlock = chunk.getBlock(blockPosition);
|
final Block previousBlock = chunk.getBlock(blockPosition);
|
||||||
final BlockHandler previousHandler = previousBlock.handler();
|
final BlockHandler previousHandler = previousBlock.handler();
|
||||||
|
|
||||||
// Change id based on neighbors
|
// Change id based on neighbors
|
||||||
block = executeBlockPlacementRule(block, blockPosition);
|
final BlockPlacementRule blockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(block);
|
||||||
|
if (blockPlacementRule != null) {
|
||||||
|
block = blockPlacementRule.blockUpdate(this, blockPosition, block);
|
||||||
|
}
|
||||||
|
|
||||||
// Set the block
|
// Set the block
|
||||||
chunk.setBlock(x, y, z, block);
|
chunk.setBlock(x, y, z, block);
|
||||||
@ -138,7 +136,7 @@ public class InstanceContainer extends Instance {
|
|||||||
executeNeighboursBlockPlacementRule(blockPosition);
|
executeNeighboursBlockPlacementRule(blockPosition);
|
||||||
|
|
||||||
// Refresh player chunk block
|
// Refresh player chunk block
|
||||||
sendBlockChange(chunk, blockPosition, block);
|
chunk.sendPacketToViewers(new BlockChangePacket(blockPosition, block.stateId()));
|
||||||
|
|
||||||
if (previousHandler != null) {
|
if (previousHandler != null) {
|
||||||
// Previous destroy
|
// Previous destroy
|
||||||
@ -159,8 +157,7 @@ public class InstanceContainer extends Instance {
|
|||||||
public boolean placeBlock(@NotNull Player player, @NotNull Block block, @NotNull Point blockPosition,
|
public boolean placeBlock(@NotNull Player player, @NotNull Block block, @NotNull Point blockPosition,
|
||||||
@NotNull BlockFace blockFace, float cursorX, float cursorY, float cursorZ) {
|
@NotNull BlockFace blockFace, float cursorX, float cursorY, float cursorZ) {
|
||||||
final Chunk chunk = getChunkAt(blockPosition);
|
final Chunk chunk = getChunkAt(blockPosition);
|
||||||
if (!ChunkUtils.isLoaded(chunk))
|
if (!ChunkUtils.isLoaded(chunk)) return false;
|
||||||
return false;
|
|
||||||
UNSAFE_setBlock(chunk, blockPosition.blockX(), blockPosition.blockY(), blockPosition.blockZ(), block,
|
UNSAFE_setBlock(chunk, blockPosition.blockX(), blockPosition.blockY(), blockPosition.blockZ(), block,
|
||||||
new BlockHandler.PlayerPlacement(block, this, blockPosition, player, blockFace, cursorX, cursorY, cursorZ), null);
|
new BlockHandler.PlayerPlacement(block, this, blockPosition, player, blockFace, cursorX, cursorY, cursorZ), null);
|
||||||
return true;
|
return true;
|
||||||
@ -170,24 +167,18 @@ public class InstanceContainer extends Instance {
|
|||||||
public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition) {
|
public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition) {
|
||||||
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!");
|
||||||
// Cancel if the chunk is read-only
|
if (chunk.isReadOnly()) return false;
|
||||||
if (chunk.isReadOnly()) {
|
if (!ChunkUtils.isLoaded(chunk)) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Chunk unloaded, stop here
|
|
||||||
if (!ChunkUtils.isLoaded(chunk))
|
|
||||||
return false;
|
|
||||||
final Block block = getBlock(blockPosition);
|
|
||||||
|
|
||||||
|
final Block block = getBlock(blockPosition);
|
||||||
final int x = blockPosition.blockX();
|
final int x = blockPosition.blockX();
|
||||||
final int y = blockPosition.blockY();
|
final int y = blockPosition.blockY();
|
||||||
final int z = blockPosition.blockZ();
|
final int z = blockPosition.blockZ();
|
||||||
// The player probably have a wrong version of this chunk section, send it
|
|
||||||
if (block.isAir()) {
|
if (block.isAir()) {
|
||||||
|
// The player probably have a wrong version of this chunk section, send it
|
||||||
chunk.sendChunk(player);
|
chunk.sendChunk(player);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerBlockBreakEvent blockBreakEvent = new PlayerBlockBreakEvent(player, block, Block.AIR, blockPosition);
|
PlayerBlockBreakEvent blockBreakEvent = new PlayerBlockBreakEvent(player, block, Block.AIR, blockPosition);
|
||||||
EventDispatcher.call(blockBreakEvent);
|
EventDispatcher.call(blockBreakEvent);
|
||||||
final boolean allowed = !blockBreakEvent.isCancelled();
|
final boolean allowed = !blockBreakEvent.isCancelled();
|
||||||
@ -207,31 +198,12 @@ public class InstanceContainer extends Instance {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull CompletableFuture<Chunk> loadChunk(int chunkX, int chunkZ) {
|
public @NotNull CompletableFuture<Chunk> loadChunk(int chunkX, int chunkZ) {
|
||||||
final Chunk chunk = getChunk(chunkX, chunkZ);
|
return loadOrRetrieve(chunkX, chunkZ, () -> retrieveChunk(chunkX, chunkZ));
|
||||||
if (chunk != null) {
|
|
||||||
// Chunk already loaded
|
|
||||||
return CompletableFuture.completedFuture(chunk);
|
|
||||||
} else {
|
|
||||||
// Retrieve chunk from somewhere else (file or create a new one using the ChunkGenerator)
|
|
||||||
return retrieveChunk(chunkX, chunkZ);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull CompletableFuture<Chunk> loadOptionalChunk(int chunkX, int chunkZ) {
|
public @NotNull CompletableFuture<Chunk> loadOptionalChunk(int chunkX, int chunkZ) {
|
||||||
final Chunk chunk = getChunk(chunkX, chunkZ);
|
return loadOrRetrieve(chunkX, chunkZ, () -> hasEnabledAutoChunkLoad() ? retrieveChunk(chunkX, chunkZ) : AsyncUtils.empty());
|
||||||
if (chunk != null) {
|
|
||||||
// Chunk already loaded
|
|
||||||
return CompletableFuture.completedFuture(chunk);
|
|
||||||
} else {
|
|
||||||
if (hasEnabledAutoChunkLoad()) {
|
|
||||||
// Use `IChunkLoader` or `ChunkGenerator`
|
|
||||||
return retrieveChunk(chunkX, chunkZ);
|
|
||||||
} else {
|
|
||||||
// Chunk not loaded, return null
|
|
||||||
return CompletableFuture.completedFuture(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -246,7 +218,7 @@ public class InstanceContainer extends Instance {
|
|||||||
chunk.removeViewer(viewer);
|
chunk.removeViewer(viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
callChunkUnloadEvent(chunkX, chunkZ);
|
EventDispatcher.call(new InstanceChunkUnloadEvent(this, chunkX, chunkZ));
|
||||||
// Remove all entities in chunk
|
// Remove all entities in chunk
|
||||||
getChunkEntities(chunk).forEach(entity -> {
|
getChunkEntities(chunk).forEach(entity -> {
|
||||||
if (!(entity instanceof Player)) entity.remove();
|
if (!(entity instanceof Player)) entity.remove();
|
||||||
@ -287,57 +259,38 @@ public class InstanceContainer extends Instance {
|
|||||||
|
|
||||||
protected @NotNull CompletableFuture<@NotNull Chunk> retrieveChunk(int chunkX, int chunkZ) {
|
protected @NotNull CompletableFuture<@NotNull Chunk> retrieveChunk(int chunkX, int chunkZ) {
|
||||||
CompletableFuture<Chunk> completableFuture = new CompletableFuture<>();
|
CompletableFuture<Chunk> completableFuture = new CompletableFuture<>();
|
||||||
final Runnable loader = () -> chunkLoader.loadChunk(this, chunkX, chunkZ)
|
final IChunkLoader loader = chunkLoader;
|
||||||
.whenComplete((chunk, throwable) -> {
|
final Runnable retriever = () -> loader.loadChunk(this, chunkX, chunkZ)
|
||||||
if (chunk != null) {
|
.thenCompose(chunk -> chunk != null ? CompletableFuture.completedFuture(chunk) : createChunk(chunkX, chunkZ))
|
||||||
// Successfully loaded
|
.whenComplete((chunk, throwable) -> scheduleNextTick(instance -> {
|
||||||
cacheChunk(chunk);
|
cacheChunk(chunk);
|
||||||
UPDATE_MANAGER.signalChunkLoad(chunk);
|
EventDispatcher.call(new InstanceChunkLoadEvent(this, chunkX, chunkZ));
|
||||||
// Execute callback and event in the instance thread
|
completableFuture.complete(chunk);
|
||||||
scheduleNextTick(instance -> {
|
}));
|
||||||
callChunkLoadEvent(chunkX, chunkZ);
|
if (loader.supportsParallelLoading()) {
|
||||||
completableFuture.complete(chunk);
|
CompletableFuture.runAsync(retriever);
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Not present
|
|
||||||
createChunk(chunkX, chunkZ).thenAccept(completableFuture::complete);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (chunkLoader.supportsParallelLoading()) {
|
|
||||||
CompletableFuture.runAsync(loader);
|
|
||||||
} else {
|
} else {
|
||||||
loader.run();
|
retriever.run();
|
||||||
}
|
}
|
||||||
// Chunk is being loaded
|
|
||||||
return completableFuture;
|
return completableFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected @NotNull CompletableFuture<@NotNull Chunk> createChunk(int chunkX, int chunkZ) {
|
protected @NotNull CompletableFuture<@NotNull Chunk> createChunk(int chunkX, int chunkZ) {
|
||||||
|
final ChunkGenerator generator = this.chunkGenerator;
|
||||||
Biome[] biomes = new Biome[Biome.getBiomeCount(getDimensionType())];
|
Biome[] biomes = new Biome[Biome.getBiomeCount(getDimensionType())];
|
||||||
if (chunkGenerator == null) {
|
if (generator == null) {
|
||||||
Arrays.fill(biomes, MinecraftServer.getBiomeManager().getById(0));
|
Arrays.fill(biomes, MinecraftServer.getBiomeManager().getById(0));
|
||||||
} else {
|
} else {
|
||||||
chunkGenerator.fillBiomes(biomes, chunkX, chunkZ);
|
generator.fillBiomes(biomes, chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Chunk chunk = chunkSupplier.createChunk(this, biomes, chunkX, chunkZ);
|
final Chunk chunk = chunkSupplier.createChunk(this, biomes, chunkX, chunkZ);
|
||||||
Check.notNull(chunk, "Chunks supplied by a ChunkSupplier cannot be null.");
|
Check.notNull(chunk, "Chunks supplied by a ChunkSupplier cannot be null.");
|
||||||
|
if (generator != null && chunk.shouldGenerate()) {
|
||||||
cacheChunk(chunk);
|
|
||||||
|
|
||||||
final Consumer<Chunk> chunkRegisterCallback = (c) -> {
|
|
||||||
UPDATE_MANAGER.signalChunkLoad(c);
|
|
||||||
callChunkLoadEvent(chunkX, chunkZ);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (chunkGenerator != null && chunk.shouldGenerate()) {
|
|
||||||
// Execute the chunk generator to populate the chunk
|
// Execute the chunk generator to populate the chunk
|
||||||
final ChunkGenerationBatch chunkBatch = new ChunkGenerationBatch(this, chunk);
|
final ChunkGenerationBatch chunkBatch = new ChunkGenerationBatch(this, chunk);
|
||||||
return chunkBatch.generate(chunkGenerator)
|
return chunkBatch.generate(generator);
|
||||||
.whenComplete((c, t) -> chunkRegisterCallback.accept(c));
|
|
||||||
} else {
|
} else {
|
||||||
// No chunk generator, execute the callback with the empty chunk
|
// No chunk generator, execute the callback with the empty chunk
|
||||||
chunkRegisterCallback.accept(chunk);
|
|
||||||
return CompletableFuture.completedFuture(chunk);
|
return CompletableFuture.completedFuture(chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -431,11 +384,8 @@ public class InstanceContainer extends Instance {
|
|||||||
for (Chunk chunk : chunks.values()) {
|
for (Chunk chunk : chunks.values()) {
|
||||||
final int chunkX = chunk.getChunkX();
|
final int chunkX = chunk.getChunkX();
|
||||||
final int chunkZ = chunk.getChunkZ();
|
final int chunkZ = chunk.getChunkZ();
|
||||||
|
|
||||||
final Chunk copiedChunk = chunk.copy(copiedInstance, chunkX, chunkZ);
|
final Chunk copiedChunk = chunk.copy(copiedInstance, chunkX, chunkZ);
|
||||||
|
|
||||||
copiedInstance.cacheChunk(copiedChunk);
|
copiedInstance.cacheChunk(copiedChunk);
|
||||||
UPDATE_MANAGER.signalChunkLoad(copiedChunk);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return copiedInstance;
|
return copiedInstance;
|
||||||
@ -449,8 +399,7 @@ public class InstanceContainer extends Instance {
|
|||||||
* @return the instance source, null if not created by a copy
|
* @return the instance source, null if not created by a copy
|
||||||
* @see #copy() to create a copy of this instance with 'this' as the source
|
* @see #copy() to create a copy of this instance with 'this' as the source
|
||||||
*/
|
*/
|
||||||
@Nullable
|
public @Nullable InstanceContainer getSrcInstance() {
|
||||||
public InstanceContainer getSrcInstance() {
|
|
||||||
return srcInstance;
|
return srcInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,21 +421,6 @@ public class InstanceContainer extends Instance {
|
|||||||
this.lastBlockChangeTime = System.currentTimeMillis();
|
this.lastBlockChangeTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a {@link Chunk} to the internal instance map.
|
|
||||||
* <p>
|
|
||||||
* WARNING: the chunk will not automatically be sent to players and
|
|
||||||
* {@link net.minestom.server.UpdateManager#signalChunkLoad(Chunk)} must be called manually.
|
|
||||||
*
|
|
||||||
* @param chunk the chunk to cache
|
|
||||||
*/
|
|
||||||
public void cacheChunk(@NotNull Chunk chunk) {
|
|
||||||
final long index = ChunkUtils.getChunkIndex(chunk);
|
|
||||||
synchronized (chunks) {
|
|
||||||
this.chunks.put(index, chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChunkGenerator getChunkGenerator() {
|
public ChunkGenerator getChunkGenerator() {
|
||||||
return chunkGenerator;
|
return chunkGenerator;
|
||||||
@ -527,34 +461,17 @@ public class InstanceContainer extends Instance {
|
|||||||
this.chunkLoader = chunkLoader;
|
this.chunkLoader = chunkLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a {@link BlockChangePacket} at the specified position to set the block as {@code blockStateId}.
|
|
||||||
* <p>
|
|
||||||
* WARNING: this does not change the internal block data, this is strictly visual for the players.
|
|
||||||
*
|
|
||||||
* @param chunk the chunk where the block is
|
|
||||||
* @param blockPosition the block position
|
|
||||||
* @param block the new block
|
|
||||||
*/
|
|
||||||
private void sendBlockChange(@NotNull Chunk chunk, @NotNull Point blockPosition, @NotNull Block block) {
|
|
||||||
chunk.sendPacketToViewers(new BlockChangePacket(blockPosition, block.stateId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick(long time) {
|
public void tick(long time) {
|
||||||
// Time/world border
|
// Time/world border
|
||||||
super.tick(time);
|
super.tick(time);
|
||||||
|
// Clear block change map
|
||||||
Lock wrlock = changingBlockLock.writeLock();
|
Lock wrlock = this.changingBlockLock;
|
||||||
wrlock.lock();
|
wrlock.lock();
|
||||||
currentlyChangingBlocks.clear();
|
this.currentlyChangingBlocks.clear();
|
||||||
wrlock.unlock();
|
wrlock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAlreadyChanged(@NotNull Point blockPosition, Block block) {
|
|
||||||
currentlyChangingBlocks.put(blockPosition, block);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Has this block already changed since last update?
|
* Has this block already changed since last update?
|
||||||
* Prevents StackOverflow with blocks trying to modify their position in onDestroy or onPlace.
|
* Prevents StackOverflow with blocks trying to modify their position in onDestroy or onPlace.
|
||||||
@ -565,24 +482,7 @@ public class InstanceContainer extends Instance {
|
|||||||
*/
|
*/
|
||||||
private boolean isAlreadyChanged(@NotNull Point blockPosition, @NotNull Block block) {
|
private boolean isAlreadyChanged(@NotNull Point blockPosition, @NotNull Block block) {
|
||||||
final Block changedBlock = currentlyChangingBlocks.get(blockPosition);
|
final Block changedBlock = currentlyChangingBlocks.get(blockPosition);
|
||||||
if (changedBlock == null)
|
return changedBlock != null && changedBlock.id() == block.id();
|
||||||
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 Point blockPosition) {
|
|
||||||
final BlockPlacementRule blockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(block);
|
|
||||||
if (blockPlacementRule != null) {
|
|
||||||
return blockPlacementRule.blockUpdate(this, blockPosition, block);
|
|
||||||
}
|
|
||||||
return block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -602,33 +502,37 @@ public class InstanceContainer extends Instance {
|
|||||||
final int neighborY = blockPosition.blockY() + offsetY;
|
final int neighborY = blockPosition.blockY() + offsetY;
|
||||||
final int neighborZ = blockPosition.blockZ() + offsetZ;
|
final int neighborZ = blockPosition.blockZ() + offsetZ;
|
||||||
final Chunk chunk = getChunkAt(neighborX, neighborZ);
|
final Chunk chunk = getChunkAt(neighborX, neighborZ);
|
||||||
|
if (chunk == null) continue;
|
||||||
// Do not try to get neighbour in an unloaded chunk
|
|
||||||
if (chunk == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
final Block neighborBlock = chunk.getBlock(neighborX, neighborY, neighborZ);
|
final Block neighborBlock = chunk.getBlock(neighborX, neighborY, neighborZ);
|
||||||
final BlockPlacementRule neighborBlockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(neighborBlock);
|
final BlockPlacementRule neighborBlockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(neighborBlock);
|
||||||
if (neighborBlockPlacementRule != null) {
|
if (neighborBlockPlacementRule == null) continue;
|
||||||
final Vec neighborPosition = new Vec(neighborX, neighborY, neighborZ);
|
|
||||||
final Block newNeighborBlock = neighborBlockPlacementRule.blockUpdate(this,
|
final Vec neighborPosition = new Vec(neighborX, neighborY, neighborZ);
|
||||||
neighborPosition, neighborBlock);
|
final Block newNeighborBlock = neighborBlockPlacementRule.blockUpdate(this,
|
||||||
if (neighborBlock != newNeighborBlock) {
|
neighborPosition, neighborBlock);
|
||||||
setBlock(neighborPosition, newNeighborBlock);
|
if (neighborBlock != newNeighborBlock) {
|
||||||
}
|
setBlock(neighborPosition, newNeighborBlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callChunkLoadEvent(int chunkX, int chunkZ) {
|
private CompletableFuture<Chunk> loadOrRetrieve(int chunkX, int chunkZ, Supplier<CompletableFuture<Chunk>> supplier) {
|
||||||
InstanceChunkLoadEvent chunkLoadEvent = new InstanceChunkLoadEvent(this, chunkX, chunkZ);
|
final Chunk chunk = getChunk(chunkX, chunkZ);
|
||||||
EventDispatcher.call(chunkLoadEvent);
|
if (chunk != null) {
|
||||||
|
// Chunk already loaded
|
||||||
|
return CompletableFuture.completedFuture(chunk);
|
||||||
|
}
|
||||||
|
return supplier.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callChunkUnloadEvent(int chunkX, int chunkZ) {
|
private void cacheChunk(@NotNull Chunk chunk) {
|
||||||
InstanceChunkUnloadEvent chunkUnloadEvent = new InstanceChunkUnloadEvent(this, chunkX, chunkZ);
|
final long index = ChunkUtils.getChunkIndex(chunk);
|
||||||
EventDispatcher.call(chunkUnloadEvent);
|
synchronized (chunks) {
|
||||||
|
this.chunks.put(index, chunk);
|
||||||
|
}
|
||||||
|
UPDATE_MANAGER.signalChunkLoad(chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,6 +10,10 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
public final class AsyncUtils {
|
public final class AsyncUtils {
|
||||||
public static final CompletableFuture<Void> VOID_FUTURE = CompletableFuture.completedFuture(null);
|
public static final CompletableFuture<Void> VOID_FUTURE = CompletableFuture.completedFuture(null);
|
||||||
|
|
||||||
|
public static <T> CompletableFuture<T> empty() {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
public static @NotNull CompletableFuture<Void> runAsync(@NotNull Runnable runnable) {
|
public static @NotNull CompletableFuture<Void> runAsync(@NotNull Runnable runnable) {
|
||||||
return CompletableFuture.runAsync(() -> {
|
return CompletableFuture.runAsync(() -> {
|
||||||
try {
|
try {
|
||||||
|
Loading…
Reference in New Issue
Block a user