From e4618cf3b8a7884d087eca577df16cb8ec424a2c Mon Sep 17 00:00:00 2001
From: FalsePattern <30945458+FalsePattern@users.noreply.github.com>
Date: Thu, 30 Mar 2023 17:17:11 +0200
Subject: [PATCH] Implement AnvilChunkLoader coupling
---
.../chunk/api/ChunkDataManager.java | 124 +++++++++----
.../chunk/api/ChunkDataRegistry.java | 2 +-
.../chunk/internal/ChunkDataRegistryImpl.java | 121 +++++++++---
.../common/vanilla/AnvilChunkLoaderMixin.java | 174 ++++++++++++++++++
.../vanilla/S21PacketChunkDataMixin.java | 7 +-
.../chunk/internal/mixin/plugin/Mixin.java | 1 +
.../chunk/internal/vanilla/BiomeManager.java | 23 ++-
.../internal/vanilla/BlockIDManager.java | 25 ++-
.../internal/vanilla/BlocklightManager.java | 21 ++-
.../internal/vanilla/MetadataManager.java | 21 ++-
.../chunk/internal/vanilla/NibbleManager.java | 15 +-
.../internal/vanilla/SkylightManager.java | 26 ++-
.../internal/vanilla/VanillaManager.java | 9 +-
13 files changed, 477 insertions(+), 92 deletions(-)
create mode 100644 src/main/java/com/falsepattern/chunk/internal/mixin/mixins/common/vanilla/AnvilChunkLoaderMixin.java
diff --git a/src/main/java/com/falsepattern/chunk/api/ChunkDataManager.java b/src/main/java/com/falsepattern/chunk/api/ChunkDataManager.java
index 1fc3681..e4cfbcd 100644
--- a/src/main/java/com/falsepattern/chunk/api/ChunkDataManager.java
+++ b/src/main/java/com/falsepattern/chunk/api/ChunkDataManager.java
@@ -1,62 +1,122 @@
package com.falsepattern.chunk.api;
-import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Contract;
import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import java.nio.ByteBuffer;
/**
- * Singleton instances that can manage custom in chunks.
- *
- * Implementations should be stateless and thread-safe.
- * @implNote For performance reasons, you should store any data in the chunk itself using a mixin. The type T is the type
- * of the interface that you are mixing into the chunk.
+ * Singleton instances that can manage custom in chunks. This class only manages the registration of
+ * managers. For the actual networking and data storage, see the internal interfaces of this interface.
+ * Note: This interface does nothing by itself, you also need to implement one or more of the internal interfaces.
+ * One class can have multiple
*/
-public interface ChunkDataManager {
+public interface ChunkDataManager {
/**
* @return The domain of this manager. Usually the modid of the mod that owns this manager.
*/
+ @Contract(pure = true)
String domain();
/**
* @return The id of this manager. Usually the name of the manager. Unique per domain.
*/
+
+ @Contract(pure = true)
String id();
/**
- * @implNote This is used to determine the size of the packet compression/decompression buffer.
- * @return The maximum amount of bytes your data can take up in a packet.
+ * Implement this interface if you want to synchronize your data with the client.
*/
- int maxPacketSize();
+ interface PacketDataManager extends ChunkDataManager {
+ /**
+ * @implNote This is used to determine the size of the packet compression/decompression buffer.
+ * @return The maximum amount of bytes your data can take up in a packet.
+ */
+ @Contract(pure = true)
+ int maxPacketSize();
+
+ /**
+ * Serializes your data into a packet.
+ *
+ * @param chunk The chunk to serialize.
+ */
+ void writeToBuffer(Chunk chunk, int ebsMask, boolean forceUpdate, ByteBuffer data);
+
+ /**
+ * Deserializes your data from a packet.
+ * Mutates this object.
+ *
+ * @param chunk The chunk to deserialize.
+ * @param buffer The packet buffer to read from.
+ */
+ void readFromBuffer(Chunk chunk, int ebsMask, boolean forceUpdate, ByteBuffer buffer);
+ }
/**
- * Serializes your data into a packet.
- *
- * @param chunk The chunk to serialize.
+ * Implement this interface if you want to save your data to disk. This is called once per chunk.
*/
- void writeToBuffer(T chunk, int ebsMask, boolean forceUpdate, ByteBuffer data);
+ interface ChunkNBTDataManager extends ChunkDataManager {
+ /**
+ * If false, the given nbt compound will be a freshly created object that gets inserted into the actual
+ * level NBT tag under the `domain:id` name.
+ *
+ * If true, the nbt tag passed in into the write/read methods will be the raw Level NBT tag without filtering.
+ *
+ * @implNote This is used internally for reimplementing the vanilla logic. Only change this if you know what you're doing.
+ */
+ @Contract(pure = true)
+ default boolean chunkPrivilegedAccess() {
+ return false;
+ }
+
+ /**
+ * Serializes your data into an NBT tag. This is used when saving the chunk to disk.
+ */
+ void writeChunkToNBT(Chunk chunk, NBTTagCompound nbt);
+
+
+ /**
+ * Deserializes your data from an NBT tag. This is used when loading the chunk from disk.
+ * The NBT *may* be null if the chunk was saved before this manager was registered
+ * (e.g., loading save before the mod was added), and the manager is not {@link #chunkPrivilegedAccess() privileged}.
+ * In this case, you should initialize the data to a sane default.
+ */
+ void readChunkFromNBT(Chunk chunk, NBTTagCompound nbt);
+ }
/**
- * Deserializes your data from a packet.
- * Mutates this object.
- *
- * @param chunk The chunk to deserialize.
- * @param buffer The packet buffer to read from.
+ * Implement this interface if you want to save your ExtendedBlockStorage data to disk. This is called once per
+ * ExtendedBlockStorage per chunk. (16 times per chunk)
*/
- void readFromBuffer(T chunk, int ebsMask, boolean forceUpdate, ByteBuffer buffer);
+ interface SectionNBTDataManager extends ChunkDataManager {
+ /**
+ * If false, the given nbt compound will be a freshly created object that gets inserted into the actual
+ * segment NBT tag under the `domain:id` name.
+ *
+ * If true, the nbt tag passed in into the write/read methods will be the raw segment NBT tag without filtering.
+ *
+ * @implNote This is used internally for reimplementing the vanilla logic. Only change this if you know what you're doing.
+ */
+ @Contract(pure = true)
+ default boolean sectionPrivilegedAccess() {
+ return false;
+ }
+ /**
+ * Serializes your data into an NBT tag. This is used when saving the chunk to disk.
+ */
+ void writeSectionToNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound section);
- /**
- * Serializes your data into an NBT tag. This is used when saving the chunk to disk.
- * @param tag The tag to write to.
- */
- void writeToNBT(T chunk, @NotNull NBTTagCompound tag);
-
- /**
- * Deserializes your data from an NBT tag. This is used when loading the chunk from disk.
- * Mutates this object.
- * @param tag The tag to read from.
- */
- void readFromNBT(T chunk, @NotNull NBTTagCompound tag);
+ /**
+ * Deserializes your data from an NBT tag. This is used when loading the chunk from disk.
+ * The NBT *may* be null if the chunk was saved before this manager was registered
+ * (e.g., loading save before the mod was added), and the manager is not {@link #sectionPrivilegedAccess() privileged}.
+ * In this case, you should initialize the data to a sane default.
+ */
+ void readSectionFromNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound section);
+ }
}
diff --git a/src/main/java/com/falsepattern/chunk/api/ChunkDataRegistry.java b/src/main/java/com/falsepattern/chunk/api/ChunkDataRegistry.java
index 987c317..3bfc68a 100644
--- a/src/main/java/com/falsepattern/chunk/api/ChunkDataRegistry.java
+++ b/src/main/java/com/falsepattern/chunk/api/ChunkDataRegistry.java
@@ -13,7 +13,7 @@ public class ChunkDataRegistry {
* @throws IllegalStateException If the registration stage is over.
* @throws IllegalArgumentException If the manager has a duplicate id.
*/
- public static void registerDataManager(ChunkDataManager> manager) throws IllegalStateException, IllegalArgumentException {
+ public static void registerDataManager(ChunkDataManager manager) throws IllegalStateException, IllegalArgumentException {
ChunkDataRegistryImpl.registerDataManager(manager);
}
diff --git a/src/main/java/com/falsepattern/chunk/internal/ChunkDataRegistryImpl.java b/src/main/java/com/falsepattern/chunk/internal/ChunkDataRegistryImpl.java
index 5840269..80d2916 100644
--- a/src/main/java/com/falsepattern/chunk/internal/ChunkDataRegistryImpl.java
+++ b/src/main/java/com/falsepattern/chunk/internal/ChunkDataRegistryImpl.java
@@ -4,28 +4,35 @@ import com.falsepattern.chunk.api.ChunkDataManager;
import lombok.val;
import lombok.var;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
public class ChunkDataRegistryImpl {
- @SuppressWarnings("rawtypes")
- private static final Map managers = new HashMap<>();
+ private static final Set managers = new HashSet<>();
+ private static final Map packetManagers = new HashMap<>();
+ private static final Map chunkNBTManagers = new HashMap<>();
+ private static final Map sectionNBTManagers = new HashMap<>();
private static final Set disabledManagers = new HashSet<>();
- private static int maxPacketSize = 0;
- public static void registerDataManager(ChunkDataManager> manager) throws IllegalStateException, IllegalArgumentException {
+ private static int maxPacketSize = 4;
+ public static void registerDataManager(ChunkDataManager manager) throws IllegalStateException, IllegalArgumentException {
if (!ChunkAPI.isRegistrationStage()) {
throw new IllegalStateException("ChunkDataManager registration is not allowed at this time! " +
"Please register your ChunkDataManager in the preInit phase of your mod.");
}
var id = manager.domain() + ":" + manager.id();
- if (managers.containsKey(id)) {
+ if (managers.contains(id)) {
throw new IllegalArgumentException("ChunkDataManager " + manager + " has a duplicate id!");
}
@@ -34,9 +41,19 @@ public class ChunkDataRegistryImpl {
return;
}
- managers.put(id, manager);
- maxPacketSize += 4 + id.getBytes(StandardCharsets.UTF_8).length;
- maxPacketSize += manager.maxPacketSize();
+ managers.add(id);
+ if (manager instanceof ChunkDataManager.PacketDataManager) {
+ val packetManager = (ChunkDataManager.PacketDataManager) manager;
+ maxPacketSize += 4 + id.getBytes(StandardCharsets.UTF_8).length;
+ maxPacketSize += packetManager.maxPacketSize();
+ packetManagers.put(id, packetManager);
+ }
+ if (manager instanceof ChunkDataManager.ChunkNBTDataManager) {
+ chunkNBTManagers.put(id, (ChunkDataManager.ChunkNBTDataManager) manager);
+ }
+ if (manager instanceof ChunkDataManager.SectionNBTDataManager) {
+ sectionNBTManagers.put(id, (ChunkDataManager.SectionNBTDataManager) manager);
+ }
}
public static void disableDataManager(String domain, String id) {
@@ -44,17 +61,22 @@ public class ChunkDataRegistryImpl {
throw new IllegalStateException("ChunkDataManager disabling is not allowed at this time! " +
"Please disable any ChunkDataManagers in the preInit/init phases.");
}
- id = domain + ":" + id;
- Common.LOG.warn("Disabling ChunkDataManager " + id + ". See the stacktrace for the source of this event.", new Throwable());
+ Common.LOG.warn("Disabling ChunkDataManager " + id + " in domain " + domain + ". See the stacktrace for the source of this event.", new Throwable());
+ val manager = domain + ":" + id;
//Remove the manager from the list of managers, if it exists
- val removed = managers.remove(id);
- if (removed != null) {
- maxPacketSize -= 4 + id.getBytes(StandardCharsets.UTF_8).length;
- maxPacketSize -= removed.maxPacketSize();
+ if (managers.remove(manager)) {
+ //Clear the maps
+ if (packetManagers.containsKey(id)) {
+ val removed = packetManagers.remove(id);
+ maxPacketSize -= 4 + id.getBytes(StandardCharsets.UTF_8).length;
+ maxPacketSize -= removed.maxPacketSize();
+ }
+ chunkNBTManagers.remove(id);
+ sectionNBTManagers.remove(id);
}
//Add the manager to the list of disabled managers, in case it gets registered after this disable call.
- disabledManagers.add(id);
+ disabledManagers.add(manager);
}
public static int maxPacketSize() {
@@ -74,7 +96,6 @@ public class ChunkDataRegistryImpl {
return new String(bytes);
}
- @SuppressWarnings("unchecked")
public static void readFromBuffer(Chunk chunk, int ebsMask, boolean forceUpdate, byte[] data) {
val buf = ByteBuffer.wrap(data);
buf.order(ByteOrder.LITTLE_ENDIAN);
@@ -82,9 +103,9 @@ public class ChunkDataRegistryImpl {
for (int i = 0; i < count; i++) {
val id = readString(buf);
val length = buf.getInt();
- val manager = managers.get(id);
+ val manager = packetManagers.get(id);
if (manager == null) {
- Common.LOG.error("Received data for unknown ChunkDataManager " + id + ". Skipping.");
+ Common.LOG.error("Received data for unknown PacketDataManager " + id + ". Skipping.");
buf.position(buf.position() + length);
continue;
}
@@ -95,14 +116,14 @@ public class ChunkDataRegistryImpl {
}
}
- @SuppressWarnings("unchecked")
public static int writeToBuffer(Chunk chunk, int ebsMask, boolean forceUpdate, byte[] data) {
val buf = ByteBuffer.wrap(data);
buf.order(ByteOrder.LITTLE_ENDIAN);
- buf.putInt(managers.size());
- for (val pair: managers.entrySet()) {
+ buf.putInt(packetManagers.size());
+ for (val pair: packetManagers.entrySet()) {
+ val id = pair.getKey();
val manager = pair.getValue();
- writeString(buf, pair.getKey());
+ writeString(buf, id);
int start = buf.position() + 4;
val slice = createSlice(buf, start, manager.maxPacketSize());
manager.writeToBuffer(chunk, ebsMask, forceUpdate, slice);
@@ -123,4 +144,60 @@ public class ChunkDataRegistryImpl {
buffer.position(oldPosition);
return slice;
}
+
+ private static NBTTagCompound createManagerNBT(boolean privileged, NBTTagCompound root, ChunkDataManager manager) {
+ if (privileged) {
+ return root;
+ }
+ val domain = manager.domain();
+ NBTTagCompound domainNBT;
+ if (root.hasKey(domain)) {
+ domainNBT = new NBTTagCompound();
+ root.setTag(domain, domainNBT);
+ } else {
+ domainNBT = root.getCompoundTag(domain);
+ }
+ val subNBT = new NBTTagCompound();
+ domainNBT.setTag(manager.id(), subNBT);
+ return subNBT;
+ }
+
+ private static NBTTagCompound getManagerNBT(boolean privileged, NBTTagCompound root, ChunkDataManager manager) {
+ if (privileged) {
+ return root;
+ }
+ val domain = manager.domain();
+ if (!root.hasKey(domain)) {
+ return null;
+ }
+ val domainNBT = root.getCompoundTag(domain);
+ if (!domainNBT.hasKey(manager.id())) {
+ return null;
+ }
+ return domainNBT.getCompoundTag(manager.id());
+ }
+
+ public static void writeSectionToNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound sectionNBT) {
+ for (val manager: sectionNBTManagers.values()) {
+ manager.writeSectionToNBT(chunk, ebs, createManagerNBT(manager.sectionPrivilegedAccess(), sectionNBT, manager));
+ }
+ }
+
+ public static void readSectionFromNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound sectionNBT) {
+ for (val manager: sectionNBTManagers.values()) {
+ manager.readSectionFromNBT(chunk, ebs, getManagerNBT(manager.sectionPrivilegedAccess(), sectionNBT, manager));
+ }
+ }
+
+ public static void writeChunkToNBT(Chunk chunk, NBTTagCompound chunkNBT) {
+ for (val manager: chunkNBTManagers.values()) {
+ manager.writeChunkToNBT(chunk, createManagerNBT(manager.chunkPrivilegedAccess(), chunkNBT, manager));
+ }
+ }
+
+ public static void readChunkFromNBT(Chunk chunk, NBTTagCompound chunkNBT) {
+ for (val manager: chunkNBTManagers.values()) {
+ manager.readChunkFromNBT(chunk, getManagerNBT(manager.chunkPrivilegedAccess(), chunkNBT, manager));
+ }
+ }
}
diff --git a/src/main/java/com/falsepattern/chunk/internal/mixin/mixins/common/vanilla/AnvilChunkLoaderMixin.java b/src/main/java/com/falsepattern/chunk/internal/mixin/mixins/common/vanilla/AnvilChunkLoaderMixin.java
new file mode 100644
index 0000000..21adab5
--- /dev/null
+++ b/src/main/java/com/falsepattern/chunk/internal/mixin/mixins/common/vanilla/AnvilChunkLoaderMixin.java
@@ -0,0 +1,174 @@
+package com.falsepattern.chunk.internal.mixin.mixins.common.vanilla;
+
+import com.falsepattern.chunk.internal.ChunkDataRegistryImpl;
+import lombok.val;
+import org.apache.logging.log4j.Level;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Overwrite;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.Entity;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.NextTickListEntry;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.NibbleArray;
+import net.minecraft.world.chunk.storage.AnvilChunkLoader;
+import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
+import cpw.mods.fml.common.FMLLog;
+
+import java.util.List;
+
+@Mixin(AnvilChunkLoader.class)
+public abstract class AnvilChunkLoaderMixin {
+ /**
+ * Writes the Chunk passed as an argument to the NBTTagCompound also passed, using the World argument to retrieve
+ * the Chunk's last update time.
+ * @author FalsePattern
+ * @reason Replace functionality
+ */
+ @Overwrite
+ private void writeChunkToNBT(Chunk chunk, World world, NBTTagCompound nbt) {
+ nbt.setByte("V", (byte)1);
+ nbt.setInteger("xPos", chunk.xPosition);
+ nbt.setInteger("zPos", chunk.zPosition);
+ System.out.println("Saving chunk " + chunk.xPosition + ", " + chunk.zPosition);
+ nbt.setLong("LastUpdate", world.getTotalWorldTime());
+ nbt.setIntArray("HeightMap", chunk.heightMap);
+ nbt.setBoolean("TerrainPopulated", chunk.isTerrainPopulated);
+ nbt.setBoolean("LightPopulated", chunk.isLightPopulated);
+ nbt.setLong("InhabitedTime", chunk.inhabitedTime);
+ writeSections(chunk, nbt);
+ writeCustomData(chunk, nbt);
+ writeEntities(chunk, world, nbt);
+ }
+
+ /**
+ * Reads the data stored in the given NBTTagCompound and creates a Chunk with that data in the given World.
+ * Returns the created Chunk.
+ * @author FalsePattern
+ * @reason Replace functionality
+ */
+ @Overwrite
+ private Chunk readChunkFromNBT(World world, NBTTagCompound nbt) {
+ int x = nbt.getInteger("xPos");
+ int z = nbt.getInteger("zPos");
+ System.out.println("Loading chunk " + x + ", " + z );
+ Chunk chunk = new Chunk(world, x, z);
+ chunk.heightMap = nbt.getIntArray("HeightMap");
+ chunk.isTerrainPopulated = nbt.getBoolean("TerrainPopulated");
+ chunk.isLightPopulated = nbt.getBoolean("LightPopulated");
+ chunk.inhabitedTime = nbt.getLong("InhabitedTime");
+ readSections(chunk, nbt);
+ readCustomData(chunk, nbt);
+
+ // End this method here and split off entity loading to another method
+ return chunk;
+ }
+
+ private void readCustomData(Chunk chunk, NBTTagCompound nbt) {
+ ChunkDataRegistryImpl.readChunkFromNBT(chunk, nbt);
+ }
+
+ private void readSections(Chunk chunk, NBTTagCompound nbt) {
+ NBTTagList sections = nbt.getTagList("Sections", 10);
+ byte segments = 16;
+ ExtendedBlockStorage[] ebsList = new ExtendedBlockStorage[segments];
+
+ for (int k = 0; k < sections.tagCount(); ++k) {
+ NBTTagCompound section = sections.getCompoundTagAt(k);
+ byte yLevel = section.getByte("Y");
+ ExtendedBlockStorage ebs = new ExtendedBlockStorage(yLevel << 4, !chunk.worldObj.provider.hasNoSky);
+ ChunkDataRegistryImpl.readSectionFromNBT(chunk, ebs, section);
+
+ ebs.removeInvalidBlocks();
+ ebsList[yLevel] = ebs;
+ }
+
+ chunk.setStorageArrays(ebsList);
+ }
+
+ private void writeCustomData(Chunk chunk, NBTTagCompound nbt) {
+ ChunkDataRegistryImpl.writeChunkToNBT(chunk, nbt);
+ }
+
+ private void writeSections(Chunk chunk, NBTTagCompound nbt) {
+ ExtendedBlockStorage[] ebsList = chunk.getBlockStorageArray();
+ NBTTagList sections = new NBTTagList();
+ NBTTagCompound section;
+
+ for (ExtendedBlockStorage ebs: ebsList) {
+ if (ebs != null) {
+ section = new NBTTagCompound();
+ section.setByte("Y", (byte) (ebs.getYLocation() >> 4 & 255));
+ ChunkDataRegistryImpl.writeSectionToNBT(chunk, ebs, section);
+ sections.appendTag(section);
+ }
+ }
+
+ nbt.setTag("Sections", sections);
+ }
+
+ private void writeEntities(Chunk chunk, World world, NBTTagCompound nbt) {
+ chunk.hasEntities = false;
+ NBTTagList entities = new NBTTagList();
+
+ for (int i = 0; i < chunk.entityLists.length; ++i) {
+ for (Object o: chunk.entityLists[i]) {
+ Entity entity = (Entity) o;
+ val entityNBT = new NBTTagCompound();
+
+ try {
+ if (entity.writeToNBTOptional(entityNBT)) {
+ chunk.hasEntities = true;
+ entities.appendTag(entityNBT);
+ }
+ } catch (Exception e) {
+ FMLLog.log(Level.ERROR, e,
+ "An Entity type %s has thrown an exception trying to write state. It will not persist. Report this to the mod author",
+ entity.getClass().getName());
+ }
+ }
+ }
+
+ nbt.setTag("Entities", entities);
+ NBTTagList tileEntities = new NBTTagList();
+
+ for (Object o: chunk.chunkTileEntityMap.values()) {
+ TileEntity tileentity = (TileEntity) o;
+ val tileEntityNBT = new NBTTagCompound();
+ try {
+ tileentity.writeToNBT(tileEntityNBT);
+ tileEntities.appendTag(tileEntityNBT);
+ } catch (Exception e) {
+ FMLLog.log(Level.ERROR, e,
+ "A TileEntity type %s has throw an exception trying to write state. It will not persist. Report this to the mod author",
+ tileentity.getClass().getName());
+ }
+ }
+
+ nbt.setTag("TileEntities", tileEntities);
+ List> pendingUpdates = world.getPendingBlockUpdates(chunk, false);
+
+ if (pendingUpdates != null) {
+ long k = world.getTotalWorldTime();
+ NBTTagList tileTicks = new NBTTagList();
+
+ for (Object o : pendingUpdates) {
+ NextTickListEntry update = (NextTickListEntry) o;
+ NBTTagCompound updateNBT = new NBTTagCompound();
+ updateNBT.setInteger("i", Block.getIdFromBlock(update.func_151351_a()));
+ updateNBT.setInteger("x", update.xCoord);
+ updateNBT.setInteger("y", update.yCoord);
+ updateNBT.setInteger("z", update.zCoord);
+ updateNBT.setInteger("t", (int) (update.scheduledTime - k));
+ updateNBT.setInteger("p", update.priority);
+ tileTicks.appendTag(updateNBT);
+ }
+
+ nbt.setTag("TileTicks", tileTicks);
+ }
+ }
+}
diff --git a/src/main/java/com/falsepattern/chunk/internal/mixin/mixins/common/vanilla/S21PacketChunkDataMixin.java b/src/main/java/com/falsepattern/chunk/internal/mixin/mixins/common/vanilla/S21PacketChunkDataMixin.java
index 93613bb..221fcfa 100644
--- a/src/main/java/com/falsepattern/chunk/internal/mixin/mixins/common/vanilla/S21PacketChunkDataMixin.java
+++ b/src/main/java/com/falsepattern/chunk/internal/mixin/mixins/common/vanilla/S21PacketChunkDataMixin.java
@@ -53,7 +53,6 @@ public abstract class S21PacketChunkDataMixin {
if (buffer.length < ChunkDataRegistryImpl.maxPacketSize()) {
buffer = new byte[ChunkDataRegistryImpl.maxPacketSize()];
}
- val data = buffer;
if (forceUpdate) {
chunk.sendUpdates = true;
@@ -65,10 +64,10 @@ public abstract class S21PacketChunkDataMixin {
}
}
- int length = ChunkDataRegistryImpl.writeToBuffer(chunk, extracted.field_150280_b, forceUpdate, data);
+ int length = ChunkDataRegistryImpl.writeToBuffer(chunk, extracted.field_150280_b, forceUpdate, buffer);
extracted.field_150282_a = new byte[length];
- System.arraycopy(data, 0, extracted.field_150282_a, 0, length);
+ System.arraycopy(buffer, 0, extracted.field_150282_a, 0, length);
return extracted;
}
@@ -90,6 +89,7 @@ public abstract class S21PacketChunkDataMixin {
}
data.writeInt(xPosition);
data.writeInt(zPosition);
+ System.out.println("Writing chunk packet (" + xPosition + ", " + zPosition + ")");
data.writeBoolean(forceUpdate);
data.writeShort((short)(ebsMask & 0xFFFF));
data.writeInt(this.data.length);
@@ -105,6 +105,7 @@ public abstract class S21PacketChunkDataMixin {
public void readPacketData(PacketBuffer data) throws IOException {
xPosition = data.readInt();
zPosition = data.readInt();
+ System.out.println("Reading chunk packet (" + xPosition + ", " + zPosition + ")");
forceUpdate = data.readBoolean();
ebsMask = data.readShort() & 0xFFFF;
this.data = new byte[data.readInt()];
diff --git a/src/main/java/com/falsepattern/chunk/internal/mixin/plugin/Mixin.java b/src/main/java/com/falsepattern/chunk/internal/mixin/plugin/Mixin.java
index a64bf7f..1cb3fe9 100644
--- a/src/main/java/com/falsepattern/chunk/internal/mixin/plugin/Mixin.java
+++ b/src/main/java/com/falsepattern/chunk/internal/mixin/plugin/Mixin.java
@@ -36,6 +36,7 @@ import static com.falsepattern.lib.mixin.IMixin.PredicateHelpers.always;
@RequiredArgsConstructor
public enum Mixin implements IMixin {
// @formatter:off
+ AnvilChunkLoaderMixin(Side.COMMON, always(), "vanilla.AnvilChunkLoaderMixin"),
S21PacketChunkDataMixin(Side.COMMON, always(), "vanilla.S21PacketChunkDataMixin"),
S26PacketMapChunkBulkMixin(Side.COMMON, always(), "vanilla.S26PacketMapChunkBulkMixin"),
diff --git a/src/main/java/com/falsepattern/chunk/internal/vanilla/BiomeManager.java b/src/main/java/com/falsepattern/chunk/internal/vanilla/BiomeManager.java
index 87b69e3..d14e2fe 100644
--- a/src/main/java/com/falsepattern/chunk/internal/vanilla/BiomeManager.java
+++ b/src/main/java/com/falsepattern/chunk/internal/vanilla/BiomeManager.java
@@ -1,13 +1,16 @@
package com.falsepattern.chunk.internal.vanilla;
+import com.falsepattern.chunk.api.ChunkDataManager;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import java.nio.ByteBuffer;
-public class BiomeManager extends VanillaManager {
+public class BiomeManager extends VanillaManager implements ChunkDataManager.PacketDataManager, ChunkDataManager.ChunkNBTDataManager {
public static final int BYTES_PER_CHUNK = 256;
@Override
@@ -21,26 +24,34 @@ public class BiomeManager extends VanillaManager {
}
@Override
- public void writeToBuffer(Chunk chunk, int ebsMask, boolean forceUpdate, ByteBuffer data) {
+ public void writeToBuffer(@NotNull Chunk chunk, int ebsMask, boolean forceUpdate, @NotNull ByteBuffer data) {
if (forceUpdate) {
data.put(chunk.getBiomeArray());
}
}
@Override
- public void readFromBuffer(Chunk chunk, int ebsMask, boolean forceUpdate, ByteBuffer buffer) {
+ public void readFromBuffer(@NotNull Chunk chunk, int ebsMask, boolean forceUpdate, @NotNull ByteBuffer buffer) {
if (forceUpdate) {
buffer.get(chunk.getBiomeArray());
}
}
@Override
- public void writeToNBT(Chunk chunk, @NotNull NBTTagCompound tag) {
+ public boolean chunkPrivilegedAccess() {
+ return true;
+ }
+
+ @Override
+ public void writeChunkToNBT(@NotNull Chunk chunk, @NotNull NBTTagCompound nbt) {
+ nbt.setByteArray("Biomes", chunk.getBiomeArray());
}
@Override
- public void readFromNBT(Chunk chunk, @NotNull NBTTagCompound tag) {
-
+ public void readChunkFromNBT(@NotNull Chunk chunk, @NotNull NBTTagCompound nbt) {
+ if (nbt.hasKey("Biomes", 7)) {
+ chunk.setBiomeArray(nbt.getByteArray("Biomes"));
+ }
}
}
diff --git a/src/main/java/com/falsepattern/chunk/internal/vanilla/BlockIDManager.java b/src/main/java/com/falsepattern/chunk/internal/vanilla/BlockIDManager.java
index 7826919..00f5dd5 100644
--- a/src/main/java/com/falsepattern/chunk/internal/vanilla/BlockIDManager.java
+++ b/src/main/java/com/falsepattern/chunk/internal/vanilla/BlockIDManager.java
@@ -1,17 +1,21 @@
package com.falsepattern.chunk.internal.vanilla;
+import com.falsepattern.chunk.api.ChunkDataManager;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.NibbleArray;
+import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import java.nio.ByteBuffer;
import static com.falsepattern.chunk.internal.Common.BLOCKS_PER_EBS;
import static com.falsepattern.chunk.internal.Common.EBS_PER_CHUNK;
-public class BlockIDManager extends VanillaManager {
+public class BlockIDManager extends VanillaManager implements ChunkDataManager.PacketDataManager, ChunkDataManager.SectionNBTDataManager {
private static final int LSB_BYTES_PER_EBS = BLOCKS_PER_EBS;
private static final int MSB_BYTES_PER_EBS = BLOCKS_PER_EBS / 2;
private static final int HEADER_SIZE = 2;
@@ -72,12 +76,25 @@ public class BlockIDManager extends VanillaManager {
}
@Override
- public void writeToNBT(Chunk chunk, @NotNull NBTTagCompound tag) {
-
+ public boolean sectionPrivilegedAccess() {
+ return true;
}
@Override
- public void readFromNBT(Chunk chunk, @NotNull NBTTagCompound tag) {
+ public void writeSectionToNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound section) {
+ section.setByteArray("Blocks", ebs.getBlockLSBArray());
+ if (ebs.getBlockMSBArray() != null) {
+ section.setByteArray("Add", ebs.getBlockMSBArray().data);
+ }
+ }
+
+ @Override
+ public void readSectionFromNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound section) {
+ ebs.setBlockLSBArray(section.getByteArray("Blocks"));
+
+ if (section.hasKey("Add", 7)) {
+ ebs.setBlockMSBArray(new NibbleArray(section.getByteArray("Add"), 4));
+ }
}
}
diff --git a/src/main/java/com/falsepattern/chunk/internal/vanilla/BlocklightManager.java b/src/main/java/com/falsepattern/chunk/internal/vanilla/BlocklightManager.java
index 431dea7..dd15888 100644
--- a/src/main/java/com/falsepattern/chunk/internal/vanilla/BlocklightManager.java
+++ b/src/main/java/com/falsepattern/chunk/internal/vanilla/BlocklightManager.java
@@ -1,9 +1,13 @@
package com.falsepattern.chunk.internal.vanilla;
+import com.falsepattern.chunk.api.ChunkDataManager;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.NibbleArray;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
-public class BlocklightManager extends NibbleManager {
+public class BlocklightManager extends NibbleManager implements ChunkDataManager.SectionNBTDataManager {
@Override
protected NibbleArray getNibbleArray(ExtendedBlockStorage ebs) {
return ebs.getBlocklightArray();
@@ -13,4 +17,19 @@ public class BlocklightManager extends NibbleManager {
public String id() {
return "blocklight";
}
+
+ @Override
+ public boolean sectionPrivilegedAccess() {
+ return true;
+ }
+
+ @Override
+ public void writeSectionToNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound section) {
+ section.setByteArray("BlockLight", ebs.getBlocklightArray().data);
+ }
+
+ @Override
+ public void readSectionFromNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound section) {
+ ebs.setBlocklightArray(new NibbleArray(section.getByteArray("BlockLight"), 4));
+ }
}
diff --git a/src/main/java/com/falsepattern/chunk/internal/vanilla/MetadataManager.java b/src/main/java/com/falsepattern/chunk/internal/vanilla/MetadataManager.java
index ef91296..0cefbba 100644
--- a/src/main/java/com/falsepattern/chunk/internal/vanilla/MetadataManager.java
+++ b/src/main/java/com/falsepattern/chunk/internal/vanilla/MetadataManager.java
@@ -1,9 +1,13 @@
package com.falsepattern.chunk.internal.vanilla;
+import com.falsepattern.chunk.api.ChunkDataManager;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.NibbleArray;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
-public class MetadataManager extends NibbleManager {
+public class MetadataManager extends NibbleManager implements ChunkDataManager.SectionNBTDataManager {
@Override
protected NibbleArray getNibbleArray(ExtendedBlockStorage ebs) {
return ebs.getMetadataArray();
@@ -13,4 +17,19 @@ public class MetadataManager extends NibbleManager {
public String id() {
return "metadata";
}
+
+ @Override
+ public boolean sectionPrivilegedAccess() {
+ return true;
+ }
+
+ @Override
+ public void writeSectionToNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound section) {
+ section.setByteArray("Data", ebs.getMetadataArray().data);
+ }
+
+ @Override
+ public void readSectionFromNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound section) {
+ ebs.setBlockMetadataArray(new NibbleArray(section.getByteArray("Data"), 4));
+ }
}
diff --git a/src/main/java/com/falsepattern/chunk/internal/vanilla/NibbleManager.java b/src/main/java/com/falsepattern/chunk/internal/vanilla/NibbleManager.java
index a45975c..d329306 100644
--- a/src/main/java/com/falsepattern/chunk/internal/vanilla/NibbleManager.java
+++ b/src/main/java/com/falsepattern/chunk/internal/vanilla/NibbleManager.java
@@ -1,17 +1,16 @@
package com.falsepattern.chunk.internal.vanilla;
+import com.falsepattern.chunk.api.ChunkDataManager;
import com.falsepattern.chunk.internal.Common;
import lombok.val;
-import org.jetbrains.annotations.NotNull;
-import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.NibbleArray;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import java.nio.ByteBuffer;
-public abstract class NibbleManager extends VanillaManager {
+public abstract class NibbleManager extends VanillaManager implements ChunkDataManager.PacketDataManager {
public static final int BYTES_PER_EBS = Common.BLOCKS_PER_EBS / 2;
protected abstract NibbleArray getNibbleArray(ExtendedBlockStorage ebs);
@@ -46,14 +45,4 @@ public abstract class NibbleManager extends VanillaManager {
}
}
}
-
- @Override
- public void writeToNBT(Chunk chunk, @NotNull NBTTagCompound tag) {
-
- }
-
- @Override
- public void readFromNBT(Chunk chunk, @NotNull NBTTagCompound tag) {
-
- }
}
diff --git a/src/main/java/com/falsepattern/chunk/internal/vanilla/SkylightManager.java b/src/main/java/com/falsepattern/chunk/internal/vanilla/SkylightManager.java
index 0a5d1df..eefe2f1 100644
--- a/src/main/java/com/falsepattern/chunk/internal/vanilla/SkylightManager.java
+++ b/src/main/java/com/falsepattern/chunk/internal/vanilla/SkylightManager.java
@@ -1,12 +1,15 @@
package com.falsepattern.chunk.internal.vanilla;
+import com.falsepattern.chunk.api.ChunkDataManager;
+
+import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.NibbleArray;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import java.nio.ByteBuffer;
-public class SkylightManager extends NibbleManager {
+public class SkylightManager extends NibbleManager implements ChunkDataManager.SectionNBTDataManager {
@Override
public String id() {
return "skylight";
@@ -30,4 +33,25 @@ public class SkylightManager extends NibbleManager {
super.readFromBuffer(chunk, ebsMask, forceUpdate, buffer);
}
}
+
+ @Override
+ public boolean sectionPrivilegedAccess() {
+ return true;
+ }
+
+ @Override
+ public void writeSectionToNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound section) {
+ if (!chunk.worldObj.provider.hasNoSky) {
+ section.setByteArray("SkyLight", ebs.getSkylightArray().data);
+ } else {
+ section.setByteArray("SkyLight", new byte[ebs.getBlocklightArray().data.length]);
+ }
+ }
+
+ @Override
+ public void readSectionFromNBT(Chunk chunk, ExtendedBlockStorage ebs, NBTTagCompound section) {
+ if (!chunk.worldObj.provider.hasNoSky) {
+ ebs.setSkylightArray(new NibbleArray(section.getByteArray("SkyLight"), 4));
+ }
+ }
}
diff --git a/src/main/java/com/falsepattern/chunk/internal/vanilla/VanillaManager.java b/src/main/java/com/falsepattern/chunk/internal/vanilla/VanillaManager.java
index 569f82d..c875355 100644
--- a/src/main/java/com/falsepattern/chunk/internal/vanilla/VanillaManager.java
+++ b/src/main/java/com/falsepattern/chunk/internal/vanilla/VanillaManager.java
@@ -2,16 +2,9 @@ package com.falsepattern.chunk.internal.vanilla;
import com.falsepattern.chunk.api.ChunkDataManager;
-import net.minecraft.world.chunk.Chunk;
-
-public abstract class VanillaManager implements ChunkDataManager {
+public abstract class VanillaManager implements ChunkDataManager {
@Override
public String domain() {
return "minecraft";
}
-
- @Override
- public void init(Chunk chunk) {
-
- }
}