Seal snapshot interfaces

Signed-off-by: TheMode <themode@outlook.fr>
This commit is contained in:
TheMode 2022-05-04 13:25:24 +02:00
parent 23d7df7cbb
commit fdd3e2c53c
13 changed files with 238 additions and 258 deletions

View File

@ -23,10 +23,7 @@ import net.minestom.server.network.PacketProcessor;
import net.minestom.server.network.socket.Server;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.scoreboard.TeamManager;
import net.minestom.server.snapshot.EntitySnapshot;
import net.minestom.server.snapshot.InstanceSnapshot;
import net.minestom.server.snapshot.ServerSnapshot;
import net.minestom.server.snapshot.SnapshotUpdater;
import net.minestom.server.snapshot.*;
import net.minestom.server.terminal.MinestomTerminal;
import net.minestom.server.thread.Acquirable;
import net.minestom.server.thread.ThreadDispatcher;
@ -36,14 +33,12 @@ import net.minestom.server.utils.collection.MappedCollection;
import net.minestom.server.world.DimensionTypeManager;
import net.minestom.server.world.biomes.BiomeManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@ -274,21 +269,7 @@ final class ServerProcessImpl implements ServerProcess {
entityRefs.put(entity.getEntityId(), updater.reference(entity));
}
}
return new SnapshotImpl(MappedCollection.plainReferences(instanceRefs), entityRefs);
}
record SnapshotImpl(Collection<InstanceSnapshot> instances,
Int2ObjectOpenHashMap<AtomicReference<EntitySnapshot>> entityRefs) implements ServerSnapshot {
@Override
public @NotNull Collection<EntitySnapshot> entities() {
return MappedCollection.plainReferences(entityRefs.values());
}
@Override
public @UnknownNullability EntitySnapshot entity(int id) {
var ref = entityRefs.get(id);
return ref != null ? ref.getPlain() : null;
}
return new SnapshotImpl.Server(MappedCollection.plainReferences(instanceRefs), entityRefs);
}
private final class TickerImpl implements Ticker {

View File

@ -41,6 +41,7 @@ import net.minestom.server.potion.Potion;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.potion.TimedPotion;
import net.minestom.server.snapshot.EntitySnapshot;
import net.minestom.server.snapshot.SnapshotImpl;
import net.minestom.server.snapshot.SnapshotUpdater;
import net.minestom.server.snapshot.Snapshotable;
import net.minestom.server.tag.TagHandler;
@ -1588,7 +1589,7 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
final int[] viewersId = this.viewEngine.viewableOption.bitSet.toIntArray();
final int[] passengersId = ArrayUtils.mapToIntArray(passengers, Entity::getEntityId);
final Entity vehicle = this.vehicle;
return new EntitySnapshotImpl.Entity(entityType, uuid, id, position, velocity,
return new SnapshotImpl.Entity(entityType, uuid, id, position, velocity,
updater.reference(instance), chunk.getChunkX(), chunk.getChunkZ(),
viewersId, passengersId, vehicle == null ? -1 : vehicle.getEntityId(),
tagHandler.readableCopy());

View File

@ -1,116 +0,0 @@
package net.minestom.server.entity;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.snapshot.ChunkSnapshot;
import net.minestom.server.snapshot.EntitySnapshot;
import net.minestom.server.snapshot.InstanceSnapshot;
import net.minestom.server.snapshot.PlayerSnapshot;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagReadable;
import net.minestom.server.utils.collection.IntMappedArray;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import java.util.Collection;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
final class EntitySnapshotImpl {
record Entity(EntityType type, UUID uuid, int id, Pos position, Vec velocity,
AtomicReference<InstanceSnapshot> instanceRef, int chunkX, int chunkZ,
int[] viewersId, int[] passengersId, int vehicleId,
TagReadable tagReadable) implements EntitySnapshot {
@Override
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
return tagReadable.getTag(tag);
}
@Override
public @NotNull InstanceSnapshot instance() {
return instanceRef.getPlain();
}
@Override
public @NotNull ChunkSnapshot chunk() {
return Objects.requireNonNull(instance().chunk(chunkX, chunkZ));
}
@Override
public @NotNull Collection<@NotNull PlayerSnapshot> viewers() {
return new IntMappedArray<>(viewersId, id -> (PlayerSnapshot) instance().server().entity(id));
}
@Override
public @NotNull Collection<@NotNull EntitySnapshot> passengers() {
return new IntMappedArray<>(passengersId, id -> instance().server().entity(id));
}
@Override
public @Nullable EntitySnapshot vehicle() {
if (vehicleId == -1) return null;
return instance().server().entity(vehicleId);
}
}
record Player(EntitySnapshot snapshot, String username,
GameMode gameMode) implements PlayerSnapshot {
@Override
public @NotNull EntityType type() {
return snapshot.type();
}
@Override
public @NotNull UUID uuid() {
return snapshot.uuid();
}
@Override
public int id() {
return snapshot.id();
}
@Override
public @NotNull Pos position() {
return snapshot.position();
}
@Override
public @NotNull Vec velocity() {
return snapshot.velocity();
}
@Override
public @NotNull InstanceSnapshot instance() {
return snapshot.instance();
}
@Override
public @NotNull ChunkSnapshot chunk() {
return snapshot.chunk();
}
@Override
public @NotNull Collection<@NotNull PlayerSnapshot> viewers() {
return snapshot.viewers();
}
@Override
public @NotNull Collection<@NotNull EntitySnapshot> passengers() {
return snapshot.passengers();
}
@Override
public @Nullable EntitySnapshot vehicle() {
return snapshot.vehicle();
}
@Override
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
return snapshot.getTag(tag);
}
}
}

View File

@ -20,7 +20,6 @@ import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.adventure.Localizable;
import net.minestom.server.adventure.audience.Audiences;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.collision.BoundingBox;
import net.minestom.server.command.CommandManager;
import net.minestom.server.command.CommandSender;
import net.minestom.server.coordinate.Point;
@ -67,6 +66,7 @@ import net.minestom.server.scoreboard.BelowNameTag;
import net.minestom.server.scoreboard.Team;
import net.minestom.server.snapshot.EntitySnapshot;
import net.minestom.server.snapshot.PlayerSnapshot;
import net.minestom.server.snapshot.SnapshotImpl;
import net.minestom.server.snapshot.SnapshotUpdater;
import net.minestom.server.statistic.PlayerStatistic;
import net.minestom.server.timer.Scheduler;
@ -1988,7 +1988,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
@Override
public @NotNull PlayerSnapshot updateSnapshot(@NotNull SnapshotUpdater updater) {
final EntitySnapshot snapshot = super.updateSnapshot(updater);
return new EntitySnapshotImpl.Player(snapshot, username, gameMode);
return new SnapshotImpl.Player(snapshot, username, gameMode);
}
/**

View File

@ -15,6 +15,7 @@ import net.minestom.server.network.packet.server.play.UpdateLightPacket;
import net.minestom.server.network.packet.server.play.data.ChunkData;
import net.minestom.server.network.packet.server.play.data.LightData;
import net.minestom.server.snapshot.ChunkSnapshot;
import net.minestom.server.snapshot.SnapshotImpl;
import net.minestom.server.snapshot.SnapshotUpdater;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.utils.MathUtils;
@ -253,7 +254,7 @@ public class DynamicChunk extends Chunk {
clonedSections[i] = sections.get(i).clone();
var entities = instance.getEntityTracker().chunkEntities(chunkX, chunkZ, EntityTracker.Target.ENTITIES);
final int[] entityIds = ArrayUtils.mapToIntArray(entities, Entity::getEntityId);
return new InstanceSnapshotImpl.Chunk(minSection, chunkX, chunkZ,
return new SnapshotImpl.Chunk(minSection, chunkX, chunkZ,
clonedSections, entries.clone(), entityIds, updater.reference(instance),
tagHandler().readableCopy());
}

View File

@ -24,10 +24,7 @@ import net.minestom.server.instance.block.BlockHandler;
import net.minestom.server.instance.generator.Generator;
import net.minestom.server.network.packet.server.play.BlockActionPacket;
import net.minestom.server.network.packet.server.play.TimeUpdatePacket;
import net.minestom.server.snapshot.ChunkSnapshot;
import net.minestom.server.snapshot.InstanceSnapshot;
import net.minestom.server.snapshot.SnapshotUpdater;
import net.minestom.server.snapshot.Snapshotable;
import net.minestom.server.snapshot.*;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.tag.Taggable;
import net.minestom.server.thread.ThreadDispatcher;
@ -641,7 +638,7 @@ public abstract class Instance implements Block.Getter, Block.Setter,
public @NotNull InstanceSnapshot updateSnapshot(@NotNull SnapshotUpdater updater) {
final Map<Long, AtomicReference<ChunkSnapshot>> chunksMap = updater.referencesMapLong(getChunks(), ChunkUtils::getChunkIndex);
final int[] entities = ArrayUtils.mapToIntArray(entityTracker.entities(), Entity::getEntityId);
return new InstanceSnapshotImpl.Instance(updater.reference(MinecraftServer.process()),
return new SnapshotImpl.Instance(updater.reference(MinecraftServer.process()),
getDimensionType(), getWorldAge(), getTime(), chunksMap, entities,
tagHandler.readableCopy());
}

View File

@ -1,107 +0,0 @@
package net.minestom.server.instance;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.instance.block.Block;
import net.minestom.server.snapshot.ChunkSnapshot;
import net.minestom.server.snapshot.EntitySnapshot;
import net.minestom.server.snapshot.InstanceSnapshot;
import net.minestom.server.snapshot.ServerSnapshot;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagReadable;
import net.minestom.server.utils.collection.IntMappedArray;
import net.minestom.server.utils.collection.MappedCollection;
import net.minestom.server.world.DimensionType;
import net.minestom.server.world.biomes.Biome;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import static net.minestom.server.utils.chunk.ChunkUtils.*;
final class InstanceSnapshotImpl {
record Instance(AtomicReference<ServerSnapshot> serverRef,
DimensionType dimensionType, long worldAge, long time,
Map<Long, AtomicReference<ChunkSnapshot>> chunksMap,
int[] entitiesIds,
TagReadable tagReadable) implements InstanceSnapshot {
@Override
public @Nullable ChunkSnapshot chunk(int chunkX, int chunkZ) {
var ref = chunksMap.get(getChunkIndex(chunkX, chunkZ));
return Objects.requireNonNull(ref, "Chunk not found").getPlain();
}
@Override
public @NotNull Collection<@NotNull ChunkSnapshot> chunks() {
return MappedCollection.plainReferences(chunksMap.values());
}
@Override
public @NotNull Collection<EntitySnapshot> entities() {
return new IntMappedArray<>(entitiesIds, id -> server().entity(id));
}
@Override
public @NotNull ServerSnapshot server() {
return serverRef.getPlain();
}
@Override
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
return tagReadable.getTag(tag);
}
}
record Chunk(int minSection, int chunkX, int chunkZ,
Section[] sections,
Int2ObjectOpenHashMap<Block> blockEntries,
int[] entitiesIds,
AtomicReference<InstanceSnapshot> instanceRef,
TagReadable tagReadable) implements ChunkSnapshot {
@Override
public @UnknownNullability Block getBlock(int x, int y, int z, @NotNull Condition condition) {
// Verify if the block object is present
if (condition != Condition.TYPE) {
final Block entry = !blockEntries.isEmpty() ?
blockEntries.get(getBlockIndex(x, y, z)) : null;
if (entry != null || condition == Condition.CACHED) {
return entry;
}
}
// Retrieve the block from state id
final Section section = sections[getChunkCoordinate(y) - minSection];
final int blockStateId = section.blockPalette()
.get(toSectionRelativeCoordinate(x), toSectionRelativeCoordinate(y), toSectionRelativeCoordinate(z));
return Objects.requireNonNullElse(Block.fromStateId((short) blockStateId), Block.AIR);
}
@Override
public @NotNull Biome getBiome(int x, int y, int z) {
final Section section = sections[getChunkCoordinate(y) - minSection];
final int id = section.biomePalette()
.get(toSectionRelativeCoordinate(x) / 4, toSectionRelativeCoordinate(y) / 4, toSectionRelativeCoordinate(z) / 4);
return MinecraftServer.getBiomeManager().getById(id);
}
@Override
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
return tagReadable.getTag(tag);
}
@Override
public @NotNull InstanceSnapshot instance() {
return instanceRef.getPlain();
}
@Override
public @NotNull Collection<@NotNull EntitySnapshot> entities() {
return new IntMappedArray<>(entitiesIds, id -> instance().server().entity(id));
}
}
}

View File

@ -7,7 +7,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collection;
public interface ChunkSnapshot extends Snapshot, Block.Getter, Biome.Getter, TagReadable {
public sealed interface ChunkSnapshot extends Snapshot, Block.Getter, Biome.Getter, TagReadable
permits SnapshotImpl.Chunk {
int chunkX();
int chunkZ();

View File

@ -10,7 +10,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.UUID;
public interface EntitySnapshot extends Snapshot, TagReadable {
public sealed interface EntitySnapshot extends Snapshot, TagReadable
permits PlayerSnapshot, SnapshotImpl.Entity {
@NotNull EntityType type();
@NotNull UUID uuid();

View File

@ -14,7 +14,8 @@ import java.util.Objects;
import static net.minestom.server.utils.chunk.ChunkUtils.getChunkCoordinate;
public interface InstanceSnapshot extends Snapshot, Block.Getter, Biome.Getter, TagReadable {
public sealed interface InstanceSnapshot extends Snapshot, Block.Getter, Biome.Getter, TagReadable
permits SnapshotImpl.Instance {
@NotNull DimensionType dimensionType();
long worldAge();

View File

@ -3,7 +3,8 @@ package net.minestom.server.snapshot;
import net.minestom.server.entity.GameMode;
import org.jetbrains.annotations.NotNull;
public interface PlayerSnapshot extends EntitySnapshot {
public sealed interface PlayerSnapshot extends EntitySnapshot
permits SnapshotImpl.Player {
@NotNull String username();
@NotNull GameMode gameMode();

View File

@ -12,7 +12,8 @@ import java.util.Collection;
/**
* Represents the complete state of the server at a given moment.
*/
public interface ServerSnapshot extends Snapshot {
public sealed interface ServerSnapshot extends Snapshot
permits SnapshotImpl.Server {
@NotNull Collection<@NotNull InstanceSnapshot> instances();
@NotNull Collection<EntitySnapshot> entities();

View File

@ -0,0 +1,218 @@
package net.minestom.server.snapshot;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.GameMode;
import net.minestom.server.instance.Section;
import net.minestom.server.instance.block.Block;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagReadable;
import net.minestom.server.utils.collection.IntMappedArray;
import net.minestom.server.utils.collection.MappedCollection;
import net.minestom.server.world.DimensionType;
import net.minestom.server.world.biomes.Biome;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import static net.minestom.server.utils.chunk.ChunkUtils.*;
@ApiStatus.Internal
public final class SnapshotImpl {
public record Server(Collection<InstanceSnapshot> instances,
Int2ObjectOpenHashMap<AtomicReference<EntitySnapshot>> entityRefs) implements ServerSnapshot {
@Override
public @NotNull Collection<EntitySnapshot> entities() {
return MappedCollection.plainReferences(entityRefs.values());
}
@Override
public @UnknownNullability EntitySnapshot entity(int id) {
var ref = entityRefs.get(id);
return ref != null ? ref.getPlain() : null;
}
}
public record Instance(AtomicReference<ServerSnapshot> serverRef,
DimensionType dimensionType, long worldAge, long time,
Map<Long, AtomicReference<ChunkSnapshot>> chunksMap,
int[] entitiesIds,
TagReadable tagReadable) implements InstanceSnapshot {
@Override
public @Nullable ChunkSnapshot chunk(int chunkX, int chunkZ) {
var ref = chunksMap.get(getChunkIndex(chunkX, chunkZ));
return Objects.requireNonNull(ref, "Chunk not found").getPlain();
}
@Override
public @NotNull Collection<@NotNull ChunkSnapshot> chunks() {
return MappedCollection.plainReferences(chunksMap.values());
}
@Override
public @NotNull Collection<EntitySnapshot> entities() {
return new IntMappedArray<>(entitiesIds, id -> server().entity(id));
}
@Override
public @NotNull ServerSnapshot server() {
return serverRef.getPlain();
}
@Override
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
return tagReadable.getTag(tag);
}
}
public record Chunk(int minSection, int chunkX, int chunkZ,
Section[] sections,
Int2ObjectOpenHashMap<Block> blockEntries,
int[] entitiesIds,
AtomicReference<InstanceSnapshot> instanceRef,
TagReadable tagReadable) implements ChunkSnapshot {
@Override
public @UnknownNullability Block getBlock(int x, int y, int z, @NotNull Condition condition) {
// Verify if the block object is present
if (condition != Condition.TYPE) {
final Block entry = !blockEntries.isEmpty() ?
blockEntries.get(getBlockIndex(x, y, z)) : null;
if (entry != null || condition == Condition.CACHED) {
return entry;
}
}
// Retrieve the block from state id
final Section section = sections[getChunkCoordinate(y) - minSection];
final int blockStateId = section.blockPalette()
.get(toSectionRelativeCoordinate(x), toSectionRelativeCoordinate(y), toSectionRelativeCoordinate(z));
return Objects.requireNonNullElse(Block.fromStateId((short) blockStateId), Block.AIR);
}
@Override
public @NotNull Biome getBiome(int x, int y, int z) {
final Section section = sections[getChunkCoordinate(y) - minSection];
final int id = section.biomePalette()
.get(toSectionRelativeCoordinate(x) / 4, toSectionRelativeCoordinate(y) / 4, toSectionRelativeCoordinate(z) / 4);
return MinecraftServer.getBiomeManager().getById(id);
}
@Override
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
return tagReadable.getTag(tag);
}
@Override
public @NotNull InstanceSnapshot instance() {
return instanceRef.getPlain();
}
@Override
public @NotNull Collection<@NotNull EntitySnapshot> entities() {
return new IntMappedArray<>(entitiesIds, id -> instance().server().entity(id));
}
}
public record Entity(EntityType type, UUID uuid, int id, Pos position, Vec velocity,
AtomicReference<InstanceSnapshot> instanceRef, int chunkX, int chunkZ,
int[] viewersId, int[] passengersId, int vehicleId,
TagReadable tagReadable) implements EntitySnapshot {
@Override
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
return tagReadable.getTag(tag);
}
@Override
public @NotNull InstanceSnapshot instance() {
return instanceRef.getPlain();
}
@Override
public @NotNull ChunkSnapshot chunk() {
return Objects.requireNonNull(instance().chunk(chunkX, chunkZ));
}
@Override
public @NotNull Collection<@NotNull PlayerSnapshot> viewers() {
return new IntMappedArray<>(viewersId, id -> (PlayerSnapshot) instance().server().entity(id));
}
@Override
public @NotNull Collection<@NotNull EntitySnapshot> passengers() {
return new IntMappedArray<>(passengersId, id -> instance().server().entity(id));
}
@Override
public @Nullable EntitySnapshot vehicle() {
if (vehicleId == -1) return null;
return instance().server().entity(vehicleId);
}
}
public record Player(EntitySnapshot snapshot, String username,
GameMode gameMode) implements PlayerSnapshot {
@Override
public @NotNull EntityType type() {
return snapshot.type();
}
@Override
public @NotNull UUID uuid() {
return snapshot.uuid();
}
@Override
public int id() {
return snapshot.id();
}
@Override
public @NotNull Pos position() {
return snapshot.position();
}
@Override
public @NotNull Vec velocity() {
return snapshot.velocity();
}
@Override
public @NotNull InstanceSnapshot instance() {
return snapshot.instance();
}
@Override
public @NotNull ChunkSnapshot chunk() {
return snapshot.chunk();
}
@Override
public @NotNull Collection<@NotNull PlayerSnapshot> viewers() {
return snapshot.viewers();
}
@Override
public @NotNull Collection<@NotNull EntitySnapshot> passengers() {
return snapshot.passengers();
}
@Override
public @Nullable EntitySnapshot vehicle() {
return snapshot.vehicle();
}
@Override
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
return snapshot.getTag(tag);
}
}
}