Skip to content

Commit

Permalink
Merge pull request #99 from InfernalSuite/develop
Browse files Browse the repository at this point in the history
1.20.4; V12 SRF; 1.8 migration; chunk duplication fix
  • Loading branch information
kyngs authored Feb 18, 2024
2 parents 2967518 + 9836ccd commit dc11277
Show file tree
Hide file tree
Showing 46 changed files with 1,715 additions and 627 deletions.
17 changes: 8 additions & 9 deletions SLIME_FORMAT
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-------------------------------------
“Slime” file format
2 bytes - magic = 0xB10B
1 byte (ubyte) - version, current = 0x0B
1 byte (ubyte) - version, current = 0x0C
4 bytes (int) - world version (see version list below)
4 bytes (int) - compressed chunks size
4 bytes (int) - uncompressed chunks size
Expand All @@ -10,7 +10,7 @@

4 bytes (int) - compressed “extra” size
4 bytes (int) - uncompressed “extra” size
[depends] - compound tag compressed using zstd
[depends] - extra compound tag compressed using zstd (used for PDC, and/or custom data)
-------------------------------------

Custom chunk format
Expand All @@ -31,19 +31,17 @@ Custom chunk format
4 bytes (int) - heightmaps size
<array of heightmap nbt compounds>
same format as mc, uncompressed
4 bytes (int) - compressed tile entities size
4 bytes (int) - uncompressed tile entities size
4 bytes (int) - tile entities size
<array of tile entity nbt compounds>
Same format as mc
inside an nbt list named “tiles”, in a global compound, no gzip anywhere
compressed using zstd
4 bytes (int) compressed entities size
4 bytes (int) uncompressed entities size
uncompressed
4 bytes (int) entities size
<array of entity nbt compounds>
Same format as mc EXCEPT optional “CustomId”
inside an nbt list named “entities”, in a global compound
Compressed using zstd

[depends] - compound tag uncompressed (used for PDC, and/or custom data)
-------------------------------------

World version list:
Expand All @@ -68,4 +66,5 @@ Version history:
- v8: Variable biomes size
- v9: Fix issue with biomes size, causing old worlds to be corrupted
- v10: Use minecraft version id, remove legacy version artifacts
- v11: Move entities and tile entities into the chunk structure
- v11: Move entities and tile entities into the chunk structure
- v12: Add support for chunk-based PDC
2 changes: 1 addition & 1 deletion api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dependencies {
api("com.flowpowered:flow-nbt:2.0.2")
api("org.jetbrains:annotations:23.0.0")

compileOnly("io.papermc.paper:paper-api:1.20.2-R0.1-SNAPSHOT")
compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT")
}

java {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.infernalsuite.aswm.api;

import com.flowpowered.nbt.CompoundMap;
import com.flowpowered.nbt.CompoundTag;
import com.infernalsuite.aswm.api.world.SlimeWorld;
import com.infernalsuite.aswm.api.world.SlimeWorldInstance;
import net.kyori.adventure.util.Services;
Expand Down Expand Up @@ -47,4 +48,6 @@ static class Holder {

}

CompoundTag convertChunkTo1_13(CompoundTag tag);

}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public interface SlimeLoader {
* @throws WorldLockedException If the world is already locked
* @throws IOException If the world could not be locked
*/
@Deprecated(forRemoval = true)
void acquireLock(String worldName) throws UnknownWorldException, WorldLockedException, IOException;

/**
Expand All @@ -78,6 +79,7 @@ public interface SlimeLoader {
* @throws UnknownWorldException if the world could not be found.
* @throws IOException if the world could not be obtained.
*/
@Deprecated(forRemoval = true)
boolean isWorldLocked(String worldName) throws UnknownWorldException, IOException;

/**
Expand All @@ -87,6 +89,7 @@ public interface SlimeLoader {
* @throws UnknownWorldException If the world could not be found
* @throws IOException If the world could not be unlocked
*/
@Deprecated(forRemoval = true)
void unlockWorld(String worldName) throws UnknownWorldException, IOException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ public class SlimeFormat {
public static final byte[] SLIME_HEADER = new byte[] { -79, 11 };

/** Latest version of the SRF that SWM supports **/
public static final byte SLIME_VERSION = 11;
public static final byte SLIME_VERSION = 12;
}
21 changes: 21 additions & 0 deletions api/src/main/java/com/infernalsuite/aswm/api/world/SlimeChunk.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.flowpowered.nbt.CompoundTag;

import java.util.List;
import javax.annotation.Nullable;

/**
* In-memory representation of a SRF chunk.
Expand Down Expand Up @@ -53,4 +54,24 @@ public interface SlimeChunk {
*/
List<CompoundTag> getEntities();

/**
* Returns the extra data of the chunk.
* Inside this {@link CompoundTag}
* can be stored any information to then be retrieved later, as it's
* saved alongside the chunk data.
* <br>
* <b>Beware, a compound tag under the key "ChunkBukkitValues" will be stored here.
* It is used for storing chunk-based Bukkit PDC. Do not overwrite it.</b>
*
* @return A {@link CompoundTag} containing the extra data of the chunk,
*/
CompoundTag getExtraData();

/**
* Upgrade data used to fix the chunks.
* Not intended to be serialized.
* @return A {@link CompoundTag} containing the upgrade data of the chunk,
*/
@Nullable
CompoundTag getUpgradeData();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.flowpowered.nbt.Tag;
import com.flowpowered.nbt.stream.NBTInputStream;
import com.flowpowered.nbt.stream.NBTOutputStream;
import com.google.common.base.Preconditions;
import org.bukkit.NamespacedKey;
import org.bukkit.persistence.PersistentDataAdapterContext;
import org.bukkit.persistence.PersistentDataContainer;
Expand Down Expand Up @@ -97,6 +98,21 @@ public boolean isEmpty() {
return root.getValue().isEmpty();
}

@Override
public void copyTo(@NotNull PersistentDataContainer other, boolean replace) {
Preconditions.checkNotNull(other, "The target container cannot be null");

if (other instanceof FlowPersistentDataContainer otherFlow) {
if (replace) {
otherFlow.root.setValue(this.root.getValue());
} else {
otherFlow.root.getValue().forEach((k, v) -> otherFlow.root.getValue().putIfAbsent(k, v));
}
} else {
throw new IllegalStateException("Cannot copy to a container that isn't a FlowPersistentDataContainer");
}
}

@Override
public @NotNull PersistentDataAdapterContext getAdapterContext() {
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,12 @@ private static SlimeChunk readChunk(CompoundTag compound, int worldVersion) {
sectionArray[index - minSectionY] = new SlimeChunkSectionSkeleton(/*paletteTag, blockStatesArray,*/ blockStatesTag, biomeTag, blockLightArray, skyLightArray);
}

CompoundTag extraTag = new CompoundTag("", new CompoundMap());
compound.getAsCompoundTag("ChunkBukkitValues").ifPresent(chunkBukkitValues -> extraTag.getValue().put(chunkBukkitValues));

for (SlimeChunkSection section : sectionArray) {
if (section != null) { // Chunk isn't empty
return new SlimeChunkSkeleton(chunkX, chunkZ, sectionArray, heightMapsCompound, tileEntities, entities);
return new SlimeChunkSkeleton(chunkX, chunkZ, sectionArray, heightMapsCompound, tileEntities, entities, extraTag, null);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import com.infernalsuite.aswm.api.world.SlimeChunkSection;
import com.infernalsuite.aswm.api.world.SlimeWorld;
import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
Expand All @@ -21,6 +23,8 @@

public class SlimeSerializer {

private static final Logger LOGGER = LoggerFactory.getLogger(SlimeSerializer.class);

public static byte[] serialize(SlimeWorld world) {
CompoundTag extraData = world.getExtraData();
SlimePropertyMap propertyMap = world.getPropertyMap();
Expand Down Expand Up @@ -73,17 +77,13 @@ static byte[] serializeChunks(SlimeWorld world, Collection<SlimeChunk> chunks) t
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(16384);
DataOutputStream outStream = new DataOutputStream(outByteStream);

List<SlimeChunk> emptyChunks = new ArrayList<>(chunks);
for (SlimeChunk chunk : chunks) {
if (!ChunkPruner.canBePruned(world, chunk)) {
emptyChunks.add(chunk);
} else {
System.out.println("PRUNED: " + chunk);
}
}
// Prune chunks
List<SlimeChunk> chunksToSave = chunks.stream()
.filter(chunk -> !ChunkPruner.canBePruned(world, chunk))
.toList();

outStream.writeInt(chunks.size());
for (SlimeChunk chunk : emptyChunks) {
outStream.writeInt(chunksToSave.size());
for (SlimeChunk chunk : chunksToSave) {
outStream.writeInt(chunk.getX());
outStream.writeInt(chunk.getZ());

Expand Down Expand Up @@ -127,21 +127,28 @@ static byte[] serializeChunks(SlimeWorld world, Collection<SlimeChunk> chunks) t
ListTag<CompoundTag> tileEntitiesNbtList = new ListTag<>("tileEntities", TagType.TAG_COMPOUND, chunk.getTileEntities());
CompoundTag tileEntitiesCompound = new CompoundTag("", new CompoundMap(Collections.singletonList(tileEntitiesNbtList)));
byte[] tileEntitiesData = serializeCompoundTag(tileEntitiesCompound);
byte[] compressedTileEntitiesData = Zstd.compress(tileEntitiesData);

outStream.writeInt(compressedTileEntitiesData.length);
outStream.writeInt(tileEntitiesData.length);
outStream.write(compressedTileEntitiesData);
outStream.write(tileEntitiesData);

// Entities
ListTag<CompoundTag> entitiesNbtList = new ListTag<>("entities", TagType.TAG_COMPOUND, chunk.getEntities());
CompoundTag entitiesCompound = new CompoundTag("", new CompoundMap(Collections.singletonList(entitiesNbtList)));
byte[] entitiesData = serializeCompoundTag(entitiesCompound);
byte[] compressedEntitiesData = Zstd.compress(entitiesData);

outStream.writeInt(compressedEntitiesData.length);
outStream.writeInt(entitiesData.length);
outStream.write(compressedEntitiesData);
outStream.write(entitiesData);

// Extra Tag
{
if (chunk.getExtraData() == null) {
LOGGER.warn("Chunk at " + chunk.getX() + ", " + chunk.getZ() + " from world " + world.getName() + " has no extra data! When deserialized, this chunk will have an empty extra data tag!");
}
byte[] extra = serializeCompoundTag(chunk.getExtraData());

outStream.writeInt(extra.length);
outStream.write(extra);
}
}

return outByteStream.toByteArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
import com.infernalsuite.aswm.api.world.SlimeWorld;
import com.infernalsuite.aswm.api.world.properties.SlimePropertyMap;
import com.infernalsuite.aswm.serialization.slime.reader.impl.v11.v11WorldFormat;
import com.infernalsuite.aswm.serialization.slime.reader.impl.v19.v1_9WorldFormat;
import com.infernalsuite.aswm.serialization.slime.reader.impl.v12.v12WorldFormat;
import com.infernalsuite.aswm.serialization.slime.reader.impl.v1_9.v1_9WorldFormat;
import com.infernalsuite.aswm.serialization.slime.reader.impl.v10.v10WorldFormat;

import java.io.ByteArrayInputStream;
Expand All @@ -25,6 +26,7 @@ public class SlimeWorldReaderRegistry {
register(v1_9WorldFormat.FORMAT, 1, 2, 3, 4, 5, 6, 7, 8, 9);
register(v10WorldFormat.FORMAT, 10);
register(v11WorldFormat.FORMAT, 11);
register(v12WorldFormat.FORMAT, 12);
}

private static void register(VersionedByteSlimeWorldReader<SlimeWorld> format, int... bytes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ private static Map<ChunkPos, SlimeChunk> readChunks(SlimePropertyMap slimeProper
}

chunkMap.put(new ChunkPos(x, z),
new SlimeChunkSkeleton(x, z, chunkSectionArray, heightMaps, new ArrayList<>(), new ArrayList<>())
new SlimeChunkSkeleton(x, z, chunkSectionArray, heightMaps, new ArrayList<>(), new ArrayList<>(), new CompoundTag("", new CompoundMap()), null)
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ private static Map<ChunkPos, SlimeChunk> readChunks(SlimePropertyMap slimeProper
List<CompoundTag> serializedEntities = ((ListTag<CompoundTag>) entitiesCompound.getValue().get("entities")).getValue();

chunkMap.put(new ChunkPos(x, z),
new SlimeChunkSkeleton(x, z, chunkSections, heightMaps, serializedTileEntities, serializedEntities));
new SlimeChunkSkeleton(x, z, chunkSections, heightMaps, serializedTileEntities, serializedEntities, new CompoundTag("", new CompoundMap()), null));
}
return chunkMap;
}
Expand Down
Loading

0 comments on commit dc11277

Please sign in to comment.