Implement AnvilChunkLoader coupling

This commit is contained in:
FalsePattern committed 2023-03-30 17:17:11 +02:00
1 parent b91c4e7342
commit e4618cf3b8
13 files changed
+477 -92

No files matched your search

@@ -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.
* <p>
* 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<T> {
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.
* <p>
* 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.
* <p>
* 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);
}
}
@@ -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);
}
@@ -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<String, ChunkDataManager> managers = new HashMap<>();
private static final Set<String> managers = new HashSet<>();
private static final Map<String, ChunkDataManager.PacketDataManager> packetManagers = new HashMap<>();
private static final Map<String, ChunkDataManager.ChunkNBTDataManager> chunkNBTManagers = new HashMap<>();
private static final Map<String, ChunkDataManager.SectionNBTDataManager> sectionNBTManagers = new HashMap<>();
private static final Set<String> 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));
}
}
}
@@ -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);
}
}
}
@@ -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()];
@@ -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"),
@@ -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"));
}
}
}
@@ -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));
}
}
}
@@ -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));
}
}
@@ -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));
}
}
@@ -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) {
}
}
@@ -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));
}
}
}
@@ -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<Chunk> {
public abstract class VanillaManager implements ChunkDataManager {
@Override
public String domain() {
return "minecraft";
}
@Override
public void init(Chunk chunk) {
}
}