Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 48 additions & 2 deletions src/main/java/com/falsepattern/chunk/api/DataManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -84,20 +85,65 @@ interface PacketDataManager extends 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);

/**
* 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,27 @@ 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 {
public final int maxPacketSize;
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.");
Expand Down Expand Up @@ -100,6 +108,12 @@ public static void registerDataManager(DataManager manager, int ordering) throws
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) {
Expand Down Expand Up @@ -127,6 +141,10 @@ public static void disableDataManager(String domain, String id) {
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);
Expand All @@ -141,6 +159,10 @@ public static int maxPacketSize() {
return maxPacketSize;
}

public static int maxPacketSizeCubic() {
return maxPacketSizeCubic;
}

private static void writeString(ByteBuffer buffer, String string) {
val bytes = string.getBytes();
buffer.putInt(bytes.length);
Expand Down Expand Up @@ -177,6 +199,29 @@ public static void readFromBuffer(Chunk chunk, int subChunkMask, boolean forceUp
}
}

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);
Expand All @@ -195,6 +240,24 @@ public static int writeToBuffer(Chunk chunk, int subChunkMask, boolean forceUpda
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
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;
Expand Down Expand Up @@ -100,6 +100,36 @@ public void readFromBuffer(Chunk chunk, int subChunkMask, boolean forceUpdate, B
}
}

@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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

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);
Expand Down Expand Up @@ -67,4 +67,19 @@ public void readFromBuffer(Chunk chunk, int subChunkMask, boolean forceUpdate, B
}
}
}

@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);
}
}