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) { - - } }