2019-08-11 07:42:56 +02:00
|
|
|
package fr.themode.minestom.instance;
|
|
|
|
|
2019-08-27 05:23:25 +02:00
|
|
|
import fr.themode.minestom.Main;
|
2019-08-31 07:54:53 +02:00
|
|
|
import fr.themode.minestom.entity.*;
|
2019-08-21 16:50:52 +02:00
|
|
|
import fr.themode.minestom.utils.BlockPosition;
|
2019-08-25 20:03:43 +02:00
|
|
|
import fr.themode.minestom.utils.ChunkUtils;
|
2019-08-20 17:41:07 +02:00
|
|
|
import fr.themode.minestom.utils.Position;
|
2019-09-02 06:02:12 +02:00
|
|
|
import simplenet.packet.Packet;
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2019-08-23 23:55:09 +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;
|
2019-08-23 23:55:09 +02:00
|
|
|
import java.util.function.Consumer;
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2019-08-24 21:41:43 +02:00
|
|
|
public abstract class Instance implements BlockModifier {
|
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();
|
|
|
|
// 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;
|
2019-08-23 23:55:09 +02:00
|
|
|
|
2019-08-24 21:41:43 +02:00
|
|
|
protected Instance(UUID uniqueId) {
|
|
|
|
this.uniqueId = uniqueId;
|
|
|
|
}
|
2019-08-18 23:52:11 +02:00
|
|
|
|
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-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);
|
|
|
|
|
2019-08-24 21:41:43 +02:00
|
|
|
public abstract Chunk getChunk(int chunkX, int chunkZ);
|
2019-08-20 22:40:57 +02:00
|
|
|
|
2019-08-24 21:41:43 +02:00
|
|
|
public abstract void saveToFolder(Runnable callback);
|
2019-08-23 23:55:09 +02:00
|
|
|
|
2019-08-24 21:41:43 +02:00
|
|
|
public abstract BlockBatch createBlockBatch();
|
2019-08-23 23:55:09 +02:00
|
|
|
|
2019-08-24 21:41:43 +02:00
|
|
|
public abstract ChunkBatch createChunkBatch(Chunk chunk);
|
2019-08-23 23:55:09 +02:00
|
|
|
|
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-09-01 06:18:41 +02:00
|
|
|
public abstract void sendChunkSectionUpdate(Chunk chunk, int section, Player player);
|
|
|
|
|
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) {
|
2019-09-02 06:02:12 +02:00
|
|
|
Packet 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-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
|
|
|
}
|
|
|
|
|
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) {
|
2019-08-24 20:34:01 +02:00
|
|
|
int chunkX = Math.floorDiv((int) position.getX(), 16);
|
|
|
|
int chunkZ = Math.floorDiv((int) position.getY(), 16);
|
|
|
|
loadChunk(chunkX, chunkZ, callback);
|
2019-08-11 07:42:56 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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-23 23:55:09 +02:00
|
|
|
}
|
|
|
|
|
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));
|
2019-08-23 23:55:09 +02:00
|
|
|
}
|
|
|
|
|
2019-08-24 21:41:43 +02:00
|
|
|
public Chunk getChunkAt(double x, double z) {
|
2019-08-24 20:34:01 +02:00
|
|
|
int chunkX = Math.floorDiv((int) x, 16);
|
|
|
|
int chunkZ = Math.floorDiv((int) z, 16);
|
|
|
|
return getChunk(chunkX, chunkZ);
|
2019-08-23 23:55:09 +02:00
|
|
|
}
|
|
|
|
|
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-20 17:41:07 +02:00
|
|
|
}
|
|
|
|
|
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-23 23:55:09 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2019-08-24 21:41:43 +02:00
|
|
|
public void saveToFolder() {
|
2019-08-24 20:34:01 +02:00
|
|
|
saveToFolder(null);
|
2019-08-19 17:04:19 +02:00
|
|
|
}
|
|
|
|
|
2019-08-24 21:41:43 +02:00
|
|
|
public UUID getUniqueId() {
|
|
|
|
return uniqueId;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2019-08-27 20:49:11 +02:00
|
|
|
long[] visibleChunksEntity = ChunkUtils.getChunksInRange(entity.getPosition(), Main.ENTITY_VIEW_DISTANCE);
|
|
|
|
boolean isPlayer = entity instanceof Player;
|
|
|
|
|
|
|
|
if (isPlayer) {
|
|
|
|
sendChunks((Player) entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send all visible entities
|
|
|
|
for (long chunkIndex : visibleChunksEntity) {
|
|
|
|
getEntitiesInChunk(chunkIndex).forEach(ent -> {
|
|
|
|
if (isPlayer)
|
|
|
|
ent.addViewer((Player) entity);
|
|
|
|
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) {
|
|
|
|
this.experienceOrbs.remove((ExperienceOrb) entity);
|
|
|
|
}
|
2019-08-24 21:41:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private Set<Entity> getEntitiesInChunk(long index) {
|
|
|
|
return chunkEntities.getOrDefault(index, new CopyOnWriteArraySet<>());
|
|
|
|
}
|
2019-08-11 07:42:56 +02:00
|
|
|
}
|