Minestom/src/main/java/net/minestom/server/instance/Instance.java

329 lines
12 KiB
Java
Raw Normal View History

2020-04-24 03:25:58 +02:00
package net.minestom.server.instance;
2019-08-11 07:42:56 +02:00
2020-04-17 01:16:02 +02:00
import io.netty.buffer.ByteBuf;
2020-04-24 03:25:58 +02:00
import net.minestom.server.MinecraftServer;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataContainer;
import net.minestom.server.entity.*;
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.BlockManager;
import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.network.PacketWriterUtils;
import net.minestom.server.network.packet.server.play.BlockActionPacket;
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.ChunkUtils;
import net.minestom.server.utils.Position;
2019-08-11 07:42:56 +02:00
import java.io.File;
2019-08-24 21:41:43 +02:00
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;
2019-08-11 07:42:56 +02:00
2020-02-09 15:34:09 +01:00
public abstract class Instance implements BlockModifier, DataContainer {
2019-08-11 07:42:56 +02:00
2019-08-24 21:41:43 +02:00
protected static final ChunkLoaderIO CHUNK_LOADER_IO = new ChunkLoaderIO();
2020-02-17 17:33:53 +01:00
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
2019-09-14 19:27:25 +02:00
2019-08-24 21:41:43 +02:00
// Entities present in this instance
protected Set<Player> players = new CopyOnWriteArraySet<>();
2019-08-31 07:54:53 +02:00
protected Set<EntityCreature> creatures = new CopyOnWriteArraySet<>();
protected Set<ObjectEntity> objectEntities = new CopyOnWriteArraySet<>();
protected Set<ExperienceOrb> experienceOrbs = new CopyOnWriteArraySet<>();
2019-08-24 21:41:43 +02:00
// Entities per chunk
protected Map<Long, Set<Entity>> chunkEntities = new ConcurrentHashMap<>();
private UUID uniqueId;
2020-02-09 15:34:09 +01:00
private Data data;
2019-08-24 21:41:43 +02:00
protected Instance(UUID uniqueId) {
this.uniqueId = uniqueId;
}
2019-08-18 23:52:11 +02:00
2020-04-11 17:21:53 +02:00
public abstract void refreshBlockId(int x, int y, int z, short blockId);
2019-09-06 16:05:36 +02:00
// Used to call BlockBreakEvent and sending particle packet if true
2019-08-25 20:03:43 +02:00
public abstract void breakBlock(Player player, BlockPosition blockPosition);
2019-08-11 07:42:56 +02:00
2019-09-06 16:05:36 +02:00
// Force the generation of the chunk, even if no file and ChunkGenerator are defined
2019-08-24 21:41:43 +02:00
public abstract void loadChunk(int chunkX, int chunkZ, Consumer<Chunk> callback);
2019-08-20 22:40:57 +02:00
2019-08-25 20:03:43 +02:00
// Load only if auto chunk load is enabled
public abstract void loadOptionalChunk(int chunkX, int chunkZ, Consumer<Chunk> callback);
2020-04-20 23:43:09 +02:00
public abstract void unloadChunk(int chunkX, int chunkZ);
2019-08-24 21:41:43 +02:00
public abstract Chunk getChunk(int chunkX, int chunkZ);
2019-08-20 22:40:57 +02:00
2020-02-09 15:34:09 +01:00
public abstract void saveChunkToFolder(Chunk chunk, Runnable callback);
public abstract void saveChunksToFolder(Runnable callback);
2019-08-24 21:41:43 +02:00
public abstract BlockBatch createBlockBatch();
2019-08-24 21:41:43 +02:00
public abstract ChunkBatch createChunkBatch(Chunk chunk);
2019-08-24 21:41:43 +02:00
public abstract void setChunkGenerator(ChunkGenerator chunkGenerator);
2019-08-19 17:04:19 +02:00
2019-08-24 21:41:43 +02:00
public abstract Collection<Chunk> getChunks();
2019-08-18 23:52:11 +02:00
2019-08-24 21:41:43 +02:00
public abstract File getFolder();
2019-08-20 22:40:57 +02:00
2019-08-24 21:41:43 +02:00
public abstract void setFolder(File folder);
2019-08-18 23:52:11 +02:00
2019-08-24 21:41:43 +02:00
public abstract void sendChunkUpdate(Player player, Chunk chunk);
2019-08-12 13:27:24 +02:00
2019-08-25 20:03:43 +02:00
protected abstract void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback);
2019-08-11 07:42:56 +02:00
2019-08-24 21:41:43 +02:00
public abstract void createChunk(int chunkX, int chunkZ, Consumer<Chunk> callback);
2019-08-11 07:42:56 +02:00
2019-08-24 21:41:43 +02:00
public abstract void sendChunks(Player player);
2019-08-21 16:50:52 +02:00
2019-08-25 20:03:43 +02:00
public abstract void sendChunk(Player player, Chunk chunk);
public abstract void enableAutoChunkLoad(boolean enable);
public abstract boolean hasEnabledAutoChunkLoad();
2019-08-24 20:34:01 +02:00
//
2019-08-24 21:41:43 +02:00
protected void sendChunkUpdate(Collection<Player> players, Chunk chunk) {
2020-04-17 01:16:02 +02:00
ByteBuf chunkData = chunk.getFullDataPacket();
2019-08-24 20:34:01 +02:00
players.forEach(player -> {
2019-09-02 06:02:12 +02:00
player.getPlayerConnection().sendPacket(chunkData);
2019-08-24 20:34:01 +02:00
});
2019-08-11 07:42:56 +02:00
}
2019-09-06 16:05:36 +02:00
protected void sendChunkSectionUpdate(Chunk chunk, int section, Collection<Player> players) {
PacketWriterUtils.writeAndSend(players, getChunkSectionUpdatePacket(chunk, section));
}
public void sendChunkSectionUpdate(Chunk chunk, int section, Player player) {
PacketWriterUtils.writeAndSend(player, getChunkSectionUpdatePacket(chunk, section));
}
protected ChunkDataPacket getChunkSectionUpdatePacket(Chunk chunk, int section) {
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
chunkDataPacket.fullChunk = false;
chunkDataPacket.chunk = chunk;
int[] sections = new int[16];
sections[section] = 1;
chunkDataPacket.sections = sections;
return chunkDataPacket;
}
2019-08-24 20:34:01 +02:00
//
2019-08-22 14:52:32 +02:00
2019-08-31 07:54:53 +02:00
public Set<Player> getPlayers() {
return Collections.unmodifiableSet(players);
2019-08-24 21:41:43 +02:00
}
public Set<EntityCreature> getCreatures() {
return Collections.unmodifiableSet(creatures);
}
2019-08-31 07:54:53 +02:00
public Set<ObjectEntity> getObjectEntities() {
return Collections.unmodifiableSet(objectEntities);
}
public Set<ExperienceOrb> getExperienceOrbs() {
return Collections.unmodifiableSet(experienceOrbs);
2019-08-24 21:41:43 +02:00
}
public Set<Entity> getChunkEntities(Chunk chunk) {
2019-08-25 20:03:43 +02:00
return Collections.unmodifiableSet(getEntitiesInChunk(ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ())));
2019-08-19 17:04:19 +02:00
}
2020-04-11 17:21:53 +02:00
public void refreshBlockId(int x, int y, int z, Block block) {
refreshBlockId(x, y, z, block.getBlockId());
}
2019-08-24 21:41:43 +02:00
public void loadChunk(int chunkX, int chunkZ) {
2019-08-24 20:34:01 +02:00
loadChunk(chunkX, chunkZ, null);
2019-08-11 07:42:56 +02:00
}
2019-08-24 21:41:43 +02:00
public void loadChunk(Position position, Consumer<Chunk> callback) {
2020-04-16 14:51:21 +02:00
int chunkX = ChunkUtils.getChunkCoordinate((int) position.getX());
int chunkZ = ChunkUtils.getChunkCoordinate((int) position.getZ());
2019-08-24 20:34:01 +02:00
loadChunk(chunkX, chunkZ, callback);
2019-08-11 07:42:56 +02:00
}
2020-04-16 14:51:21 +02:00
public void loadOptionalChunk(Position position, Consumer<Chunk> callback) {
int chunkX = ChunkUtils.getChunkCoordinate((int) position.getX());
int chunkZ = ChunkUtils.getChunkCoordinate((int) position.getZ());
loadOptionalChunk(chunkX, chunkZ, callback);
}
2019-08-24 21:41:43 +02:00
public short getBlockId(int x, int y, int z) {
2019-08-24 20:34:01 +02:00
Chunk chunk = getChunkAt(x, z);
return chunk.getBlockId((byte) (x % 16), (byte) y, (byte) (z % 16));
2019-08-11 07:42:56 +02:00
}
2020-04-09 14:25:42 +02:00
public short getBlockId(float x, float y, float z) {
return getBlockId(Math.round(x), Math.round(y), Math.round(z));
}
2019-08-24 21:41:43 +02:00
public short getBlockId(BlockPosition blockPosition) {
2019-08-24 20:34:01 +02:00
return getBlockId(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
}
2019-08-24 21:41:43 +02:00
public CustomBlock getCustomBlock(int x, int y, int z) {
2019-08-24 20:34:01 +02:00
Chunk chunk = getChunkAt(x, z);
return chunk.getCustomBlock((byte) (x % 16), (byte) y, (byte) (z % 16));
}
2020-03-30 19:48:25 +02:00
public CustomBlock getCustomBlock(BlockPosition blockPosition) {
return getCustomBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
}
2020-04-23 23:46:36 +02:00
public void sendBlockAction(BlockPosition blockPosition, byte actionId, byte actionParam) {
short blockId = getBlockId(blockPosition);
BlockActionPacket blockActionPacket = new BlockActionPacket();
blockActionPacket.blockPosition = blockPosition;
blockActionPacket.actionId = actionId;
blockActionPacket.actionParam = actionParam;
blockActionPacket.blockId = blockId; // FIXME: block id and not block state?
Chunk chunk = getChunkAt(blockPosition);
chunk.sendPacketToViewers(blockActionPacket);
}
2019-09-15 13:42:36 +02:00
public Data getBlockData(int x, int y, int z) {
Chunk chunk = getChunkAt(x, z);
return chunk.getData((byte) (x % 16), (byte) y, (byte) (z % 16));
}
2020-03-30 19:48:25 +02:00
public Data getBlockData(BlockPosition blockPosition) {
return getBlockData(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
}
2019-08-24 21:41:43 +02:00
public Chunk getChunkAt(double x, double z) {
2020-03-29 20:58:30 +02:00
int chunkX = ChunkUtils.getChunkCoordinate((int) x);
int chunkZ = ChunkUtils.getChunkCoordinate((int) z);
2019-08-24 20:34:01 +02:00
return getChunk(chunkX, chunkZ);
}
2019-08-24 21:41:43 +02:00
public boolean isChunkLoaded(int chunkX, int chunkZ) {
2019-08-24 20:34:01 +02:00
return getChunk(chunkX, chunkZ) != null;
}
2019-08-24 21:41:43 +02:00
public Chunk getChunkAt(BlockPosition blockPosition) {
2019-08-24 20:34:01 +02:00
return getChunkAt(blockPosition.getX(), blockPosition.getZ());
}
2019-08-24 21:41:43 +02:00
public Chunk getChunkAt(Position position) {
2019-08-24 20:34:01 +02:00
return getChunkAt(position.getX(), position.getZ());
2019-08-11 07:42:56 +02:00
}
2019-08-12 08:30:59 +02:00
2020-02-09 15:34:09 +01:00
public void saveChunkToFolder(Chunk chunk) {
saveChunkToFolder(chunk, null);
}
public void saveChunksToFolder() {
saveChunksToFolder(null);
2019-08-19 17:04:19 +02:00
}
2019-08-24 21:41:43 +02:00
public UUID getUniqueId() {
return uniqueId;
}
2020-02-09 15:34:09 +01:00
@Override
public Data getData() {
return data;
}
@Override
public void setData(Data data) {
this.data = data;
}
2019-08-24 21:41:43 +02:00
// UNSAFE METHODS (need most of time to be synchronized)
2019-08-19 17:04:19 +02:00
2019-08-24 21:41:43 +02:00
public void addEntity(Entity entity) {
Instance lastInstance = entity.getInstance();
if (lastInstance != null && lastInstance != this) {
2019-08-25 20:03:43 +02:00
lastInstance.removeEntity(entity); // If entity is in another instance, remove it from there and add it to this
2019-08-24 21:41:43 +02:00
}
2020-02-17 17:33:53 +01:00
long[] visibleChunksEntity = ChunkUtils.getChunksInRange(entity.getPosition(), MinecraftServer.ENTITY_VIEW_DISTANCE);
2019-08-27 20:49:11 +02:00
boolean isPlayer = entity instanceof Player;
if (isPlayer) {
sendChunks((Player) entity);
}
// Send all visible entities
for (long chunkIndex : visibleChunksEntity) {
getEntitiesInChunk(chunkIndex).forEach(ent -> {
2019-09-07 11:42:33 +02:00
if (isPlayer) {
2019-08-27 20:49:11 +02:00
ent.addViewer((Player) entity);
2019-09-07 11:42:33 +02:00
}
2019-08-27 20:49:11 +02:00
if (ent instanceof Player) {
entity.addViewer((Player) ent);
}
});
2019-08-24 21:41:43 +02:00
}
Chunk chunk = getChunkAt(entity.getPosition());
addEntityToChunk(entity, chunk);
}
public void removeEntity(Entity entity) {
Instance entityInstance = entity.getInstance();
if (entityInstance == null || entityInstance != this)
return;
2019-08-25 20:03:43 +02:00
entity.getViewers().forEach(p -> entity.removeViewer(p)); // Remove this entity from players viewable list and send delete entities packet
2019-08-24 21:41:43 +02:00
Chunk chunk = getChunkAt(entity.getPosition());
removeEntityFromChunk(entity, chunk);
}
public void addEntityToChunk(Entity entity, Chunk chunk) {
2019-08-25 20:03:43 +02:00
long chunkIndex = ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ());
2019-09-03 07:36:04 +02:00
synchronized (chunkEntities) {
Set<Entity> entities = getEntitiesInChunk(chunkIndex);
entities.add(entity);
this.chunkEntities.put(chunkIndex, entities);
if (entity instanceof Player) {
this.players.add((Player) entity);
} else if (entity instanceof EntityCreature) {
this.creatures.add((EntityCreature) entity);
} else if (entity instanceof ObjectEntity) {
this.objectEntities.add((ObjectEntity) entity);
} else if (entity instanceof ExperienceOrb) {
this.experienceOrbs.add((ExperienceOrb) entity);
}
2019-08-24 21:41:43 +02:00
}
}
public void removeEntityFromChunk(Entity entity, Chunk chunk) {
2019-08-25 20:03:43 +02:00
long chunkIndex = ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ());
2019-09-03 07:36:04 +02:00
synchronized (chunkEntities) {
Set<Entity> entities = getEntitiesInChunk(chunkIndex);
entities.remove(entity);
if (entities.isEmpty()) {
this.chunkEntities.remove(chunkIndex);
} else {
this.chunkEntities.put(chunkIndex, entities);
}
if (entity instanceof Player) {
this.players.remove(entity);
} else if (entity instanceof EntityCreature) {
this.creatures.remove(entity);
} else if (entity instanceof ObjectEntity) {
this.objectEntities.remove(entity);
} else if (entity instanceof ExperienceOrb) {
2019-09-06 16:05:36 +02:00
this.experienceOrbs.remove(entity);
2019-09-03 07:36:04 +02:00
}
2019-08-24 21:41:43 +02:00
}
}
private Set<Entity> getEntitiesInChunk(long index) {
2019-09-07 11:42:33 +02:00
Set<Entity> entities = chunkEntities.get(index);
return entities != null ? entities : new CopyOnWriteArraySet<>();
2019-08-24 21:41:43 +02:00
}
2020-02-16 19:11:36 +01:00
}