package com.denizenscript.denizen.nms.v1_14.impl.network.handlers;
import com.denizenscript.denizen.nms.util.ReflectionHelper;
import com.denizenscript.denizen.utilities.blocks.FakeBlock;
import com.denizenscript.denizen.utilities.debugging.Debug;
import io.netty.buffer.Unpooled;
import net.minecraft.server.v1_14_R1.*;
import org.bukkit.craftbukkit.v1_14_R1.block.data.CraftBlockData;
import java.lang.reflect.Field;
import java.util.List;
public class FakeBlockHelper {
public static Field BITMASK_MAPCHUNK = ReflectionHelper.getFields(PacketPlayOutMapChunk.class).get("c");
public static Field HEIGHTMAPS_MAPCHUNK = ReflectionHelper.getFields(PacketPlayOutMapChunk.class).get("d");
public static Field DATA_MAPCHUNK = ReflectionHelper.getFields(PacketPlayOutMapChunk.class).get("e");
public static Field BLOCKENTITIES_MAPCHUNK = ReflectionHelper.getFields(PacketPlayOutMapChunk.class).get("f");
public static IBlockData getNMSState(FakeBlock block) {
return ((CraftBlockData) block.material.getModernData().data).getState();
}
public static boolean anyBlocksInSection(List<FakeBlock> blocks, int y) {
int minY = y << 4;
int maxY = (y << 4) + 16;
for (FakeBlock block : blocks) {
int blockY = block.location.getBlockY();
if (blockY >= minY && blockY < maxY) {
return true;
}
}
return false;
}
public static void pushByteArrayToLongArray(byte[] bits, long[] longs) {
for (int i = 0; i < bits.length; i++) {
int longIndex = i >> 8;
int startBit = longIndex * 8;
longs[longIndex] |= ((long) bits[i]) << (i - startBit);
}
}
public static IBlockData blockInPalette(int paletteId) {
return ChunkSection.GLOBAL_PALETTE.a(paletteId);
}
public static void handleMapChunkPacket(PacketPlayOutMapChunk packet, List<FakeBlock> blocks) {
try {
// TODO: properly update HeightMap?
int bitmask = BITMASK_MAPCHUNK.getInt(packet);
byte[] data = (byte[]) DATA_MAPCHUNK.get(packet);
PacketDataSerializer serial = new PacketDataSerializer(Unpooled.wrappedBuffer(data));
PacketDataSerializer outputSerial = new PacketDataSerializer(Unpooled.buffer(data.length));
byte[] blockDataHelper = new byte[8 * 16 * 16 * 16];
boolean isFull = packet.f();
List<NBTTagCompound> blockEntities = (List<NBTTagCompound>) BLOCKENTITIES_MAPCHUNK.get(packet);
NBTTagList blockEntitiesList = new NBTTagList();
blockEntitiesList.addAll(blockEntities);
for (int y = 0; y < 16; y++) {
if ((bitmask & (1 << y)) != 0) {
int blockCount = serial.readShort();
int width = serial.readUnsignedByte();
int paletteLen = serial.i(); // readVarInt
int[] palette = new int[paletteLen];
for (int p = 0; p < paletteLen; p++) {
palette[p] = serial.i();
}
int dataLen = serial.i();
Debug.log("y: " + y + " count: " + blockCount + ", width: " + width + ", paletteLen: " + paletteLen + ", dataLen: " + dataLen);
serial.readBytes(blockDataHelper, 0, 512 * width);
outputSerial.writeShort(blockCount);
if (!anyBlocksInSection(blocks, y)) {
outputSerial.writeByte(width);
outputSerial.d(paletteLen); // writeVarInt
for (int p = 0; p < paletteLen; p++) {
outputSerial.d(palette[p]);
}
outputSerial.d(dataLen);
outputSerial.writeBytes(blockDataHelper, 0, 512 * width);
continue;
}
long[] blockListHelper = new long[width * (512 / 8)];
pushByteArrayToLongArray(blockDataHelper, blockListHelper);
DataPaletteBlock<IBlockData> blockPalette = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE,
Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData());
for (int p = 0; p < paletteLen; p++) {
// Workaround to stick the existing types into the palette
blockPalette.setBlock(0, 0, 0, blockInPalette(palette[p]));
}
blockPalette.a(blockEntitiesList, blockListHelper);
int minY = y << 4;
int maxY = (y << 4) + 16;
for (FakeBlock block : blocks) {
int blockY = block.location.getBlockY();
if (blockY >= minY && blockY < maxY) {
int blockX = block.location.getBlockX();
int blockZ = block.location.getBlockZ();
blockX -= (blockX >> 4) * 16;
blockY -= (blockY >> 4) * 16;
blockZ -= (blockZ >> 4) * 16;
blockPalette.setBlock(blockX, blockY, blockZ, getNMSState(block));
}
}
blockPalette.b(outputSerial);
}
}
if (isFull) {
// biomes
outputSerial.writeBytes(serial, 256 * 4);
}
byte[] outputBytes = outputSerial.array();
DATA_MAPCHUNK.set(packet, outputBytes);
}
catch (Exception ex) {
Debug.echoError(ex);
}
}
}