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 instances, Int2ObjectOpenHashMap> entityRefs) implements ServerSnapshot { @Override public @NotNull Collection 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 serverRef, DimensionType dimensionType, long worldAge, long time, Map> 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 entities() { return new IntMappedArray<>(entitiesIds, id -> server().entity(id)); } @Override public @NotNull ServerSnapshot server() { return serverRef.getPlain(); } @Override public @UnknownNullability T getTag(@NotNull Tag tag) { return tagReadable.getTag(tag); } } public record Chunk(int minSection, int chunkX, int chunkZ, Section[] sections, Int2ObjectOpenHashMap blockEntries, int[] entitiesIds, AtomicReference 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 @UnknownNullability T getTag(@NotNull Tag 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 instanceRef, int chunkX, int chunkZ, int[] viewersId, int[] passengersId, int vehicleId, TagReadable tagReadable) implements EntitySnapshot { @Override public @UnknownNullability T getTag(@NotNull Tag 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 @UnknownNullability T getTag(@NotNull Tag tag) { return snapshot.getTag(tag); } } }