This commit is contained in:
rjnasers committed 2026-03-07 15:22:21 +01:00
commit 68f297faba
4 files changed
+158 -4

No files matched your search

@@ -22,6 +22,7 @@
package com.falsepattern.chunk.api;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -84,6 +85,9 @@ public interface DataManager {
* Serializes your data into a packet.
*
* @param chunk The chunk to serialize.
* @param subChunkMask The mask that controls which subchunks need to be serialized. This will be 0 when cubic chunks is
* present - only chunk-specific information (like biomes) should be sent in this case.
* @param forceUpdate True when the chunk is first synced.
*/
@Contract(mutates = "param4")
void writeToBuffer(Chunk chunk, int subChunkMask, boolean forceUpdate, ByteBuffer buffer);
@@ -91,13 +95,55 @@ public interface DataManager {
/**
* Deserializes your data from a packet.
*
* @param chunk The chunk to deserialize.
* @param buffer The packet buffer to read from.
* @param chunk The chunk to deserialize.
* @param subChunkMask The mask that controls which subchunks need to be deserialized. This will be 0 when cubic chunks is
* present - only chunk-specific information (like biomes) should be sent in this case.
* @param forceUpdate True when the chunk is first synced.
*/
@Contract(mutates = "param1,param4")
void readFromBuffer(Chunk chunk, int subChunkMask, boolean forceUpdate, ByteBuffer buffer);
}
/**
* A cube-specific variant of {@link PacketDataManager}. This is only used by cubic chunks.
*
* @author RecursivePineapple
* @since 0.7.1
* @apiNote Cubes, subchunks, and ExtendedBlockStorages are equivalent. A cube is a subchunk and a cube contains an EBS.
* To keep dependencies simple, cube-specific information is not available to this interface, but a cube's location can be
* retrieved by combining the chunk location and the EBS Y level. Note that the EBS's Y level can be <0 and >= 16.
*/
@ApiStatus.Experimental
interface CubicPacketDataManager extends DataManager {
/**
* @return The maximum amount of bytes your data can take up in a packet.
*
* @implSpec This is used to determine the size of the packet compression/decompression buffer.
* Only called ONCE, during registration!
*/
@Contract(pure = true)
int maxPacketSizeCubic();
/**
* Serializes your data into a packet.
*
* @param chunk The chunk that contains the subchunk.
* @param blockStorage The subchunk (cube) that was updated.
*/
@Contract(mutates = "param3")
void writeToBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer);
/**
* Deserializes your data from a packet.
*
* @param chunk The chunk that contains the subchunk.
* @param blockStorage The subchunk (cube) that was updated.
*/
@Contract(mutates = "param1,param2,param3")
void readFromBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer);
}
/**
* Implement this interface if you additionally want to synchronize your data on single and multi-block updates,
* not just chunk updates.
@@ -59,12 +59,14 @@ public class DataRegistryImpl {
private static final Map<String, OrderedManager> managersUnordered = new HashMap<>();
private static final SortedSet<OrderedManager> managers = new TreeSet<>();
private static final DualMap<PacketManagerInfo> packetManagers = new DualMap<>();
private static final DualMap<CubicPacketManagerInfo> cubicPacketManagers = new DualMap<>();
private static final DualMap<DataManager.BlockPacketDataManager> blockPacketManagers = new DualMap<>();
private static final DualMap<DataManager.StorageDataManager> NBTManagers = new DualMap<>();
private static final SortedMap<OrderedManager, DataManager.ChunkDataManager> chunkNBTManagers = new TreeMap<>();
private static final SortedMap<OrderedManager, DataManager.SubChunkDataManager> subChunkNBTManagers = new TreeMap<>();
private static final Set<String> disabledManagers = new HashSet<>();
private static int maxPacketSize = 4;
private static int maxPacketSizeCubic = 4;
@Data
private static class PacketManagerInfo {
@@ -72,6 +74,12 @@ public class DataRegistryImpl {
public final DataManager.PacketDataManager manager;
}
@Data
private static class CubicPacketManagerInfo {
public final int maxPacketSize;
public final DataManager.CubicPacketDataManager manager;
}
public static void registerDataManager(DataManager manager, int ordering) throws IllegalStateException, IllegalArgumentException {
if (Loader.instance().getLoaderState() != LoaderState.INITIALIZATION) {
throw new IllegalStateException("ChunkDataManager registration is not allowed at this time! Please register your ChunkDataManager in the init phase.");
@@ -100,6 +108,12 @@ public class DataRegistryImpl {
val blockPacketManager = (DataManager.BlockPacketDataManager) manager;
blockPacketManagers.put(ord, blockPacketManager);
}
if (manager instanceof DataManager.CubicPacketDataManager) {
val cubicPacketManager = (DataManager.CubicPacketDataManager) manager;
val maxSize = cubicPacketManager.maxPacketSizeCubic();
maxPacketSizeCubic += 4 + id.getBytes(StandardCharsets.UTF_8).length + 4 + maxSize;
cubicPacketManagers.put(ord, new CubicPacketManagerInfo(maxSize, cubicPacketManager));
}
if (manager instanceof DataManager.StorageDataManager) {
NBTManagers.put(ord, (DataManager.StorageDataManager) manager);
if (manager instanceof DataManager.ChunkDataManager) {
@@ -127,6 +141,10 @@ public class DataRegistryImpl {
val removed = packetManagers.remove(ord);
maxPacketSize -= 4 + id.getBytes(StandardCharsets.UTF_8).length + 4 + removed.maxPacketSize;
}
if (cubicPacketManagers.containsKey(ord)) {
val removed = cubicPacketManagers.remove(ord);
maxPacketSizeCubic -= 4 + id.getBytes(StandardCharsets.UTF_8).length + 4 + removed.maxPacketSize;
}
blockPacketManagers.remove(ord);
chunkNBTManagers.remove(ord);
subChunkNBTManagers.remove(ord);
@@ -141,6 +159,10 @@ public class DataRegistryImpl {
return maxPacketSize;
}
public static int maxPacketSizeCubic() {
return maxPacketSizeCubic;
}
private static void writeString(ByteBuffer buffer, String string) {
val bytes = string.getBytes();
buffer.putInt(bytes.length);
@@ -177,6 +199,29 @@ public class DataRegistryImpl {
}
}
public static void readFromBufferCubic(Chunk chunk, ExtendedBlockStorage blockStorage, byte[] data) {
val buf = ByteBuffer.wrap(data);
buf.order(ByteOrder.LITTLE_ENDIAN);
int count = buf.getInt();
for (int i = 0; i < count; i++) {
val id = readString(buf);
val length = buf.getInt();
val managerInfo = cubicPacketManagers.get(id);
if (managerInfo == null) {
Common.LOG.error("Received data for unknown CubicPacketDataManager " + id + ". Skipping.");
buf.position(buf.position() + length);
continue;
}
if (length > managerInfo.maxPacketSize) {
Common.LOG.error("Received packet larger than max size for CubicPacketDataManager " + id + "! Continuing anyways, things might break!");
}
int start = buf.position();
val slice = createSlice(buf, start, length);
managerInfo.manager.readFromBuffer(chunk, blockStorage, slice);
buf.position(start + length);
}
}
public static int writeToBuffer(Chunk chunk, int subChunkMask, boolean forceUpdate, byte[] data) {
val buf = ByteBuffer.wrap(data);
buf.order(ByteOrder.LITTLE_ENDIAN);
@@ -195,6 +240,24 @@ public class DataRegistryImpl {
return buf.position();
}
public static int writeToBufferCubic(Chunk chunk, ExtendedBlockStorage blockStorage, byte[] data) {
val buf = ByteBuffer.wrap(data);
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.putInt(cubicPacketManagers.size());
for (val pair : cubicPacketManagers.entrySet()) {
val ord = pair.getKey();
val managerInfo = pair.getValue();
writeString(buf, ord.id);
int start = buf.position() + 4;
val slice = createSlice(buf, start, managerInfo.maxPacketSize);
managerInfo.manager.writeToBuffer(chunk, blockStorage, slice);
int length = slice.position();
buf.putInt(length);
buf.position(start + length);
}
return buf.position();
}
public static void writeBlockToPacket(Chunk chunk, int x, int y, int z, S23PacketBlockChange packet) {
for (val manager : blockPacketManagers.values()) {
manager.writeBlockToPacket(chunk, x, y, z, packet);
@@ -40,7 +40,7 @@ import java.nio.ByteBuffer;
import static com.falsepattern.chunk.internal.Common.BLOCKS_PER_SUBCHUNK;
import static com.falsepattern.chunk.internal.Common.SUBCHUNKS_PER_CHUNK;
public class BlockIDManager extends VanillaManager implements DataManager.PacketDataManager, DataManager.BlockPacketDataManager, DataManager.SubChunkDataManager {
public class BlockIDManager extends VanillaManager implements DataManager.PacketDataManager, DataManager.CubicPacketDataManager, DataManager.BlockPacketDataManager, DataManager.SubChunkDataManager {
private static final int LSB_BYTES_PER_SUBCHUNK = BLOCKS_PER_SUBCHUNK;
private static final int MSB_BYTES_PER_SUBCHUNK = BLOCKS_PER_SUBCHUNK / 2;
private static final int HEADER_SIZE = 2;
@@ -100,6 +100,36 @@ public class BlockIDManager extends VanillaManager implements DataManager.Packet
}
}
@Override
public int maxPacketSizeCubic() {
return HEADER_SIZE + LSB_BYTES_PER_SUBCHUNK + MSB_BYTES_PER_SUBCHUNK;
}
@Override
public void writeToBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer) {
buffer.put(blockStorage.getBlockLSBArray());
if (blockStorage.getBlockMSBArray() != null) {
buffer.put((byte) 1);
buffer.put(blockStorage.getBlockMSBArray().data);
} else {
buffer.put((byte) 0);
}
}
@Override
public void readFromBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer) {
buffer.get(blockStorage.getBlockLSBArray());
if (buffer.get() != 0) {
if (blockStorage.getBlockMSBArray() == null) {
blockStorage.createBlockMSBArray();
}
buffer.get(blockStorage.getBlockMSBArray().data);
}
}
@Override
public boolean subChunkPrivilegedAccess() {
return true;
@@ -32,7 +32,7 @@ import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import java.nio.ByteBuffer;
public abstract class NibbleManager extends VanillaManager implements DataManager.PacketDataManager {
public abstract class NibbleManager extends VanillaManager implements DataManager.PacketDataManager, DataManager.CubicPacketDataManager {
public static final int BYTES_PER_SUBCHUNK = Common.BLOCKS_PER_SUBCHUNK / 2;
protected abstract NibbleArray getNibbleArray(ExtendedBlockStorage subChunk);
@@ -67,4 +67,19 @@ public abstract class NibbleManager extends VanillaManager implements DataManage
}
}
}
@Override
public int maxPacketSizeCubic() {
return BYTES_PER_SUBCHUNK;
}
@Override
public void writeToBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer) {
buffer.put(getNibbleArray(blockStorage).data, 0, BYTES_PER_SUBCHUNK);
}
@Override
public void readFromBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer) {
buffer.get(getNibbleArray(blockStorage).data, 0, BYTES_PER_SUBCHUNK);
}
}