2020-04-24 03:25:58 +02:00
|
|
|
package net.minestom.server.instance;
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2021-03-11 20:54:30 +01:00
|
|
|
import net.minestom.server.Tickable;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.Viewable;
|
2021-07-08 18:56:40 +02:00
|
|
|
import net.minestom.server.coordinate.Point;
|
|
|
|
import net.minestom.server.coordinate.Vec;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.entity.Player;
|
|
|
|
import net.minestom.server.instance.block.Block;
|
2023-07-02 21:34:54 +02:00
|
|
|
import net.minestom.server.instance.block.BlockHandler;
|
2024-01-06 08:43:43 +01:00
|
|
|
import net.minestom.server.network.packet.server.SendablePacket;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
|
2022-03-03 07:44:57 +01:00
|
|
|
import net.minestom.server.snapshot.Snapshotable;
|
2021-06-26 00:31:04 +02:00
|
|
|
import net.minestom.server.tag.TagHandler;
|
2022-03-20 01:47:57 +01:00
|
|
|
import net.minestom.server.tag.Taggable;
|
2020-10-11 15:27:23 +02:00
|
|
|
import net.minestom.server.utils.chunk.ChunkSupplier;
|
2021-11-12 18:35:09 +01:00
|
|
|
import net.minestom.server.utils.chunk.ChunkUtils;
|
|
|
|
import net.minestom.server.world.biomes.Biome;
|
2024-01-06 08:43:43 +01:00
|
|
|
import org.jetbrains.annotations.ApiStatus;
|
2020-10-24 10:46:23 +02:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
2023-07-02 21:34:54 +02:00
|
|
|
import org.jetbrains.annotations.Nullable;
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2021-12-22 01:47:28 +01:00
|
|
|
import java.util.List;
|
2021-08-04 16:49:01 +02:00
|
|
|
import java.util.Set;
|
|
|
|
import java.util.UUID;
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2019-09-23 19:56:08 +02:00
|
|
|
// TODO light data & API
|
2020-10-04 23:33:36 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A chunk is a part of an {@link Instance}, limited by a size of 16x256x16 blocks and subdivided in 16 sections of 16 blocks height.
|
|
|
|
* Should contains all the blocks located at those positions and manage their tick updates.
|
2021-01-07 16:51:10 +01:00
|
|
|
* Be aware that implementations do not need to be thread-safe, all chunks are guarded by their own instance ('this').
|
2020-10-11 15:27:23 +02:00
|
|
|
* <p>
|
2020-11-15 08:33:03 +01:00
|
|
|
* You can create your own implementation of this class by extending it
|
|
|
|
* and create the objects in {@link InstanceContainer#setChunkSupplier(ChunkSupplier)}.
|
|
|
|
* <p>
|
|
|
|
* You generally want to avoid storing references of this object as this could lead to a huge memory leak,
|
|
|
|
* you should store the chunk coordinates instead.
|
2020-10-04 23:33:36 +02:00
|
|
|
*/
|
2022-03-20 01:47:57 +01:00
|
|
|
public abstract class Chunk implements Block.Getter, Block.Setter, Biome.Getter, Biome.Setter, Viewable, Tickable, Taggable, Snapshotable {
|
2019-08-25 20:03:43 +02:00
|
|
|
public static final int CHUNK_SIZE_X = 16;
|
|
|
|
public static final int CHUNK_SIZE_Z = 16;
|
2020-04-26 19:17:04 +02:00
|
|
|
public static final int CHUNK_SECTION_SIZE = 16;
|
2019-08-18 23:52:11 +02:00
|
|
|
|
2020-11-20 11:14:15 +01:00
|
|
|
private final UUID identifier;
|
|
|
|
|
2021-03-11 20:54:30 +01:00
|
|
|
protected Instance instance;
|
2020-10-22 12:55:53 +02:00
|
|
|
protected final int chunkX, chunkZ;
|
2022-01-07 22:48:07 +01:00
|
|
|
protected final int minSection, maxSection;
|
2019-09-15 13:42:36 +02:00
|
|
|
|
2020-10-10 05:50:49 +02:00
|
|
|
// Options
|
2020-10-15 08:48:13 +02:00
|
|
|
private final boolean shouldGenerate;
|
2020-10-24 00:17:23 +02:00
|
|
|
private boolean readOnly;
|
2020-10-10 05:50:49 +02:00
|
|
|
|
2020-10-05 10:03:25 +02:00
|
|
|
protected volatile boolean loaded = true;
|
2022-12-31 02:47:18 +01:00
|
|
|
private final Viewable viewable;
|
2019-08-22 14:52:32 +02:00
|
|
|
|
2020-10-03 18:51:33 +02:00
|
|
|
// Data
|
2022-03-20 01:47:57 +01:00
|
|
|
private final TagHandler tagHandler = TagHandler.newHandler();
|
2020-10-03 18:51:33 +02:00
|
|
|
|
2021-11-06 06:24:45 +01:00
|
|
|
public Chunk(@NotNull Instance instance, int chunkX, int chunkZ, boolean shouldGenerate) {
|
2020-11-20 11:14:15 +01:00
|
|
|
this.identifier = UUID.randomUUID();
|
2021-03-11 20:54:30 +01:00
|
|
|
this.instance = instance;
|
2019-08-11 07:42:56 +02:00
|
|
|
this.chunkX = chunkX;
|
|
|
|
this.chunkZ = chunkZ;
|
2020-10-10 05:50:49 +02:00
|
|
|
this.shouldGenerate = shouldGenerate;
|
2022-01-07 22:48:07 +01:00
|
|
|
this.minSection = instance.getDimensionType().getMinY() / CHUNK_SECTION_SIZE;
|
|
|
|
this.maxSection = (instance.getDimensionType().getMinY() + instance.getDimensionType().getHeight()) / CHUNK_SECTION_SIZE;
|
2022-12-31 02:47:18 +01:00
|
|
|
final List<SharedInstance> shared = instance instanceof InstanceContainer instanceContainer ?
|
|
|
|
instanceContainer.getSharedInstances() : List.of();
|
|
|
|
this.viewable = instance.getEntityTracker().viewable(shared, chunkX, chunkZ);
|
2019-08-11 07:42:56 +02:00
|
|
|
}
|
|
|
|
|
2020-09-12 12:14:53 +02:00
|
|
|
/**
|
2020-10-30 22:49:57 +01:00
|
|
|
* Sets a block at a position.
|
2020-09-12 12:14:53 +02:00
|
|
|
* <p>
|
2020-10-12 17:07:22 +02:00
|
|
|
* This is used when the previous block has to be destroyed/replaced, meaning that it clears the previous data and update method.
|
2020-09-12 12:45:37 +02:00
|
|
|
* <p>
|
2021-05-23 00:28:31 +02:00
|
|
|
* WARNING: this method is not thread-safe (in order to bring performance improvement with {@link net.minestom.server.instance.batch.Batch batches})
|
|
|
|
* The thread-safe version is {@link Instance#setBlock(int, int, int, Block)} (or any similar instance methods)
|
2020-10-12 03:18:02 +02:00
|
|
|
* Otherwise, you can simply do not forget to have this chunk synchronized when this is called.
|
2020-09-12 12:14:53 +02:00
|
|
|
*
|
2021-05-23 00:28:31 +02:00
|
|
|
* @param x the block X
|
|
|
|
* @param y the block Y
|
|
|
|
* @param z the block Z
|
|
|
|
* @param block the block to place
|
2020-09-12 12:14:53 +02:00
|
|
|
*/
|
2021-06-11 16:40:37 +02:00
|
|
|
@Override
|
2023-07-02 21:34:54 +02:00
|
|
|
public void setBlock(int x, int y, int z, @NotNull Block block) {
|
|
|
|
setBlock(x, y, z, block, null, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected abstract void setBlock(int x, int y, int z, @NotNull Block block,
|
|
|
|
@Nullable BlockHandler.Placement placement,
|
|
|
|
@Nullable BlockHandler.Destroy destroy);
|
2019-08-18 23:52:11 +02:00
|
|
|
|
2021-12-22 01:47:28 +01:00
|
|
|
public abstract @NotNull List<Section> getSections();
|
|
|
|
|
2021-06-21 16:32:46 +02:00
|
|
|
public abstract @NotNull Section getSection(int section);
|
2021-06-12 10:39:44 +02:00
|
|
|
|
2021-11-12 18:35:09 +01:00
|
|
|
public @NotNull Section getSectionAt(int blockY) {
|
2021-12-19 01:25:52 +01:00
|
|
|
return getSection(ChunkUtils.getChunkCoordinate(blockY));
|
2021-11-12 18:35:09 +01:00
|
|
|
}
|
|
|
|
|
2020-09-12 12:45:37 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Executes a chunk tick.
|
2020-10-05 10:03:25 +02:00
|
|
|
* <p>
|
2020-10-12 03:18:02 +02:00
|
|
|
* Should be used to update all the blocks in the chunk.
|
2020-10-05 10:03:25 +02:00
|
|
|
* <p>
|
2020-10-12 03:18:02 +02:00
|
|
|
* WARNING: this method doesn't necessary have to be thread-safe, proceed with caution.
|
2020-09-12 12:45:37 +02:00
|
|
|
*
|
2021-03-11 20:54:30 +01:00
|
|
|
* @param time the time of the update in milliseconds
|
2020-09-12 12:45:37 +02:00
|
|
|
*/
|
2021-03-11 20:54:30 +01:00
|
|
|
@Override
|
|
|
|
public abstract void tick(long time);
|
2019-09-14 18:00:18 +02:00
|
|
|
|
2020-11-20 08:39:06 +01:00
|
|
|
/**
|
|
|
|
* Gets the last time that this chunk changed.
|
|
|
|
* <p>
|
|
|
|
* "Change" means here data used in {@link ChunkDataPacket}.
|
|
|
|
* It is necessary to see if the cached version of this chunk can be used
|
|
|
|
* instead of re writing and compressing everything.
|
|
|
|
*
|
|
|
|
* @return the last change time in milliseconds
|
|
|
|
*/
|
|
|
|
public abstract long getLastChangeTime();
|
|
|
|
|
2020-10-05 10:03:25 +02:00
|
|
|
/**
|
2021-08-04 16:49:01 +02:00
|
|
|
* Sends the chunk data to {@code player}.
|
2020-10-24 00:25:37 +02:00
|
|
|
*
|
2021-08-04 16:49:01 +02:00
|
|
|
* @param player the player
|
2020-10-05 10:03:25 +02:00
|
|
|
*/
|
2024-01-06 08:43:43 +01:00
|
|
|
public void sendChunk(@NotNull Player player) {
|
|
|
|
player.sendChunk(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void sendChunk() {
|
|
|
|
getViewers().forEach(this::sendChunk);
|
|
|
|
}
|
2021-08-04 16:49:01 +02:00
|
|
|
|
2024-01-06 08:43:43 +01:00
|
|
|
@ApiStatus.Internal
|
|
|
|
public abstract @NotNull SendablePacket getFullDataPacket();
|
2020-10-05 10:03:25 +02:00
|
|
|
|
2020-10-31 19:22:23 +01:00
|
|
|
/**
|
|
|
|
* Creates a copy of this chunk, including blocks state id, custom block id, biomes, update data.
|
|
|
|
* <p>
|
2020-11-25 09:47:04 +01:00
|
|
|
* The chunk position (X/Z) can be modified using the given arguments.
|
2020-10-31 19:22:23 +01:00
|
|
|
*
|
2021-03-11 20:54:30 +01:00
|
|
|
* @param instance the chunk owner
|
|
|
|
* @param chunkX the chunk X of the copy
|
|
|
|
* @param chunkZ the chunk Z of the copy
|
2020-10-31 19:22:23 +01:00
|
|
|
* @return a copy of this chunk with a potentially new instance and position
|
|
|
|
*/
|
2021-07-11 20:20:01 +02:00
|
|
|
public abstract @NotNull Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ);
|
2020-10-31 19:22:23 +01:00
|
|
|
|
2020-11-25 09:47:04 +01:00
|
|
|
/**
|
|
|
|
* Resets the chunk, this means clearing all the data making it empty.
|
|
|
|
*/
|
|
|
|
public abstract void reset();
|
|
|
|
|
2020-11-20 11:14:15 +01:00
|
|
|
/**
|
|
|
|
* Gets the unique identifier of this chunk.
|
|
|
|
* <p>
|
2021-08-04 16:49:01 +02:00
|
|
|
* WARNING: this UUID is not persistent but randomized once the object is instantiated.
|
2020-11-20 11:14:15 +01:00
|
|
|
*
|
|
|
|
* @return the chunk identifier
|
|
|
|
*/
|
2021-07-11 20:20:01 +02:00
|
|
|
public @NotNull UUID getIdentifier() {
|
2020-11-20 11:14:15 +01:00
|
|
|
return identifier;
|
|
|
|
}
|
|
|
|
|
2021-03-11 20:54:30 +01:00
|
|
|
/**
|
|
|
|
* Gets the instance where this chunk is stored
|
|
|
|
*
|
|
|
|
* @return the linked instance
|
|
|
|
*/
|
2021-07-11 20:20:01 +02:00
|
|
|
public @NotNull Instance getInstance() {
|
2021-03-11 20:54:30 +01:00
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2020-09-12 12:45:37 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the chunk X.
|
2020-09-12 12:45:37 +02:00
|
|
|
*
|
|
|
|
* @return the chunk X
|
|
|
|
*/
|
2019-08-11 07:42:56 +02:00
|
|
|
public int getChunkX() {
|
|
|
|
return chunkX;
|
|
|
|
}
|
|
|
|
|
2020-09-12 12:45:37 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the chunk Z.
|
2020-09-12 12:45:37 +02:00
|
|
|
*
|
|
|
|
* @return the chunk Z
|
|
|
|
*/
|
2019-08-11 07:42:56 +02:00
|
|
|
public int getChunkZ() {
|
|
|
|
return chunkZ;
|
|
|
|
}
|
2022-01-07 22:48:07 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the lowest (inclusive) section Y available in this chunk
|
|
|
|
*
|
|
|
|
* @return the lowest (inclusive) section Y available in this chunk
|
|
|
|
*/
|
|
|
|
public int getMinSection() {
|
|
|
|
return minSection;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the highest (exclusive) section Y available in this chunk
|
|
|
|
*
|
|
|
|
* @return the highest (exclusive) section Y available in this chunk
|
|
|
|
*/
|
|
|
|
public int getMaxSection() {
|
|
|
|
return maxSection;
|
|
|
|
}
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2020-11-04 19:14:04 +01:00
|
|
|
/**
|
2021-07-08 18:56:40 +02:00
|
|
|
* Gets the world position of this chunk.
|
2020-11-04 19:14:04 +01:00
|
|
|
*
|
|
|
|
* @return the position of this chunk
|
|
|
|
*/
|
2021-07-08 18:56:40 +02:00
|
|
|
public @NotNull Point toPosition() {
|
2021-09-11 00:04:35 +02:00
|
|
|
return new Vec(CHUNK_SIZE_X * getChunkX(), 0, CHUNK_SIZE_Z * getChunkZ());
|
2020-11-04 19:14:04 +01:00
|
|
|
}
|
|
|
|
|
2020-10-10 05:50:49 +02:00
|
|
|
/**
|
2020-10-24 00:25:37 +02:00
|
|
|
* Gets if this chunk will or had been loaded with a {@link ChunkGenerator}.
|
2020-11-04 15:41:56 +01:00
|
|
|
* <p>
|
|
|
|
* If false, the chunk will be entirely empty when loaded.
|
2020-10-10 05:50:49 +02:00
|
|
|
*
|
|
|
|
* @return true if this chunk is affected by a {@link ChunkGenerator}
|
|
|
|
*/
|
|
|
|
public boolean shouldGenerate() {
|
|
|
|
return shouldGenerate;
|
|
|
|
}
|
|
|
|
|
2020-10-24 00:17:23 +02:00
|
|
|
/**
|
|
|
|
* Gets if this chunk is read-only.
|
2020-10-24 00:25:37 +02:00
|
|
|
* <p>
|
|
|
|
* Being read-only should prevent block placing/breaking and setting block from an {@link Instance}.
|
|
|
|
* It does not affect {@link IChunkLoader} and {@link ChunkGenerator}.
|
2020-10-24 00:17:23 +02:00
|
|
|
*
|
|
|
|
* @return true if the chunk is read-only
|
|
|
|
*/
|
|
|
|
public boolean isReadOnly() {
|
|
|
|
return readOnly;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Changes the read state of the chunk.
|
2020-10-24 00:25:37 +02:00
|
|
|
* <p>
|
|
|
|
* Being read-only should prevent block placing/breaking and setting block from an {@link Instance}.
|
|
|
|
* It does not affect {@link IChunkLoader} and {@link ChunkGenerator}.
|
2020-10-24 00:17:23 +02:00
|
|
|
*
|
|
|
|
* @param readOnly true to make the chunk read-only, false otherwise
|
|
|
|
*/
|
|
|
|
public void setReadOnly(boolean readOnly) {
|
|
|
|
this.readOnly = readOnly;
|
|
|
|
}
|
|
|
|
|
2020-05-15 15:38:06 +02:00
|
|
|
/**
|
2020-10-12 03:18:02 +02:00
|
|
|
* Used to verify if the chunk should still be kept in memory.
|
2020-05-15 15:38:06 +02:00
|
|
|
*
|
|
|
|
* @return true if the chunk is loaded
|
|
|
|
*/
|
|
|
|
public boolean isLoaded() {
|
|
|
|
return loaded;
|
|
|
|
}
|
|
|
|
|
2023-06-17 16:43:03 +02:00
|
|
|
/**
|
|
|
|
* Called when the chunk has been successfully loaded.
|
|
|
|
*/
|
|
|
|
protected void onLoad() {}
|
|
|
|
|
2024-03-24 21:23:26 +01:00
|
|
|
/**
|
|
|
|
* Called when the chunk generator has finished generating the chunk.
|
|
|
|
*/
|
|
|
|
public void onGenerate() {}
|
|
|
|
|
2019-08-25 20:03:43 +02:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
2020-10-05 10:20:25 +02:00
|
|
|
return getClass().getSimpleName() + "[" + chunkX + ":" + chunkZ + "]";
|
2019-08-25 20:03:43 +02:00
|
|
|
}
|
2019-09-01 06:18:41 +02:00
|
|
|
|
|
|
|
@Override
|
2020-10-24 10:46:23 +02:00
|
|
|
public boolean addViewer(@NotNull Player player) {
|
2022-12-31 02:47:18 +01:00
|
|
|
return viewable.addViewer(player);
|
2019-09-01 06:18:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 10:46:23 +02:00
|
|
|
public boolean removeViewer(@NotNull Player player) {
|
2022-12-31 02:47:18 +01:00
|
|
|
return viewable.removeViewer(player);
|
2019-09-01 06:18:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2021-11-01 18:04:00 +01:00
|
|
|
public @NotNull Set<Player> getViewers() {
|
2022-12-31 02:47:18 +01:00
|
|
|
return viewable.getViewers();
|
2019-09-01 06:18:41 +02:00
|
|
|
}
|
2020-04-26 16:51:00 +02:00
|
|
|
|
2021-06-26 00:31:04 +02:00
|
|
|
@Override
|
2022-03-20 01:47:57 +01:00
|
|
|
public @NotNull TagHandler tagHandler() {
|
|
|
|
return tagHandler;
|
2021-06-26 00:31:04 +02:00
|
|
|
}
|
|
|
|
|
2020-05-15 15:38:06 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Sets the chunk as "unloaded".
|
2020-05-15 15:38:06 +02:00
|
|
|
*/
|
|
|
|
protected void unload() {
|
|
|
|
this.loaded = false;
|
2020-05-24 22:37:42 +02:00
|
|
|
}
|
2024-02-11 00:02:02 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Invalidate the chunk caches
|
|
|
|
*/
|
|
|
|
public abstract void invalidate();
|
2020-02-16 19:11:36 +01:00
|
|
|
}
|