package com.xcompwiz.lookingglass.network.packet;
import io.netty.buffer.ByteBuf;
import java.util.concurrent.Semaphore;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.play.server.S21PacketChunkData;
import net.minecraft.network.play.server.S21PacketChunkData.Extracted;
import net.minecraft.world.WorldProviderSurface;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.NibbleArray;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import com.xcompwiz.lookingglass.client.proxyworld.ProxyWorldManager;
import com.xcompwiz.lookingglass.client.proxyworld.WorldView;
import com.xcompwiz.lookingglass.log.LoggerUtils;
import com.xcompwiz.lookingglass.network.LookingGlassPacketManager;
import cpw.mods.fml.common.network.internal.FMLProxyPacket;
/**
* Based on code from Ken Butler/shadowking97
*/
public class PacketChunkInfo extends PacketHandlerBase {
private static byte[] inflatearray;
private static byte[] dataarray;
private static Semaphore deflateGate = new Semaphore(1);
private static int deflate(byte[] chunkData, byte[] compressedChunkData) {
Deflater deflater = new Deflater(-1);
if (compressedChunkData == null) return 0;
int bytesize = 0;
try {
deflater.setInput(chunkData, 0, chunkData.length);
deflater.finish();
bytesize = deflater.deflate(compressedChunkData);
} finally {
deflater.end();
}
return bytesize;
}
public static FMLProxyPacket createPacket(Chunk chunk, boolean includeinit, int subid, int dim) {
int xPos = chunk.xPosition;
int zPos = chunk.zPosition;
Extracted extracted = getMapChunkData(chunk, includeinit, subid);
int yMSBPos = extracted.field_150281_c;
int yPos = extracted.field_150280_b;
byte[] chunkData = extracted.field_150282_a;
deflateGate.acquireUninterruptibly();
byte[] compressedChunkData = new byte[chunkData.length];
int len = deflate(chunkData, compressedChunkData);
deflateGate.release();
// This line may look like black magic (and, well, it is), but it's actually just returning a class reference for this class. Copy-paste safe.
ByteBuf data = PacketHandlerBase.createDataBuffer((Class<? extends PacketHandlerBase>) new Object() {}.getClass().getEnclosingClass());
data.writeInt(dim);
data.writeInt(xPos);
data.writeInt(zPos);
data.writeBoolean(includeinit);
data.writeShort((short) (yPos & 65535));
data.writeShort((short) (yMSBPos & 65535));
data.writeInt(len);
data.writeInt(chunkData.length);
data.ensureWritable(len);
data.writeBytes(compressedChunkData, 0, len);
return buildPacket(data);
}
@Override
public void handle(ByteBuf in, EntityPlayer player) {
int dim = in.readInt();
int xPos = in.readInt();
int zPos = in.readInt();
boolean reqinit = in.readBoolean();
short yPos = in.readShort();
short yMSBPos = in.readShort();
int compressedsize = in.readInt();
int uncompressedsize = in.readInt();
byte[] chunkData = inflateChunkData(in, compressedsize, uncompressedsize);
if (chunkData == null) {
LookingGlassPacketManager.bus.sendToServer(PacketRequestChunk.createPacket(xPos, yPos, zPos, dim));
LoggerUtils.error("Chunk decompression failed: %d\t:\t%d\t\t%d : %d\n", yMSBPos, yPos, compressedsize, uncompressedsize);
return;
}
handle(player, chunkData, dim, xPos, zPos, reqinit, yPos, yMSBPos);
}
public void handle(EntityPlayer player, byte[] chunkData, int dim, int xPos, int zPos, boolean reqinit, short yPos, short yMSBPos) {
WorldClient proxyworld = ProxyWorldManager.getProxyworld(dim);
if (proxyworld == null) return;
if (proxyworld.provider.dimensionId != dim) return;
//TODO: Test to see if this first part is even necessary
Chunk chunk = proxyworld.getChunkProvider().provideChunk(xPos, zPos);
if (reqinit && (chunk == null || chunk.isEmpty())) {
if (yPos == 0) {
proxyworld.doPreChunk(xPos, zPos, false);
return;
}
proxyworld.doPreChunk(xPos, zPos, true);
}
// End possible removal section
proxyworld.invalidateBlockReceiveRegion(xPos << 4, 0, zPos << 4, (xPos << 4) + 15, 256, (zPos << 4) + 15);
chunk = proxyworld.getChunkFromChunkCoords(xPos, zPos);
if (reqinit && (chunk == null || chunk.isEmpty())) {
proxyworld.doPreChunk(xPos, zPos, true);
chunk = proxyworld.getChunkFromChunkCoords(xPos, zPos);
}
if (chunk != null) {
chunk.fillChunk(chunkData, yPos, yMSBPos, reqinit);
receivedChunk(proxyworld, xPos, zPos);
if (!reqinit || !(proxyworld.provider instanceof WorldProviderSurface)) {
chunk.resetRelightChecks();
}
}
}
public void receivedChunk(WorldClient worldObj, int cx, int cz) {
worldObj.markBlockRangeForRenderUpdate(cx << 4, 0, cz << 4, (cx << 4) + 15, 256, (cz << 4) + 15);
Chunk c = worldObj.getChunkFromChunkCoords(cx, cz);
if (c == null || c.isEmpty()) return;
for (WorldView activeview : ProxyWorldManager.getWorldViews(worldObj.provider.dimensionId)) {
activeview.onChunkReceived(cx, cz);
}
int x = (cx << 4);
int z = (cz << 4);
for (int y = 0; y < worldObj.getActualHeight(); y += 16) {
if (c.getAreLevelsEmpty(y, y)) continue;
for (int x2 = 0; x2 < 16; ++x2) {
for (int z2 = 0; z2 < 16; ++z2) {
for (int y2 = 0; y2 < 16; ++y2) {
int lx = x + x2;
int ly = y + y2;
int lz = z + z2;
if (worldObj.getBlock(lx, ly, lz).hasTileEntity(worldObj.getBlockMetadata(lx, ly, lz))) {
LookingGlassPacketManager.bus.sendToServer(PacketRequestTE.createPacket(lx, ly, lz, worldObj.provider.dimensionId));
}
}
}
}
}
}
private byte[] inflateChunkData(ByteBuf in, int compressedsize, int uncompressedsize) {
if (inflatearray == null || inflatearray.length < compressedsize) {
inflatearray = new byte[compressedsize];
}
in.readBytes(inflatearray, 0, compressedsize);
byte[] chunkData = new byte[uncompressedsize];
Inflater inflater = new Inflater();
inflater.setInput(inflatearray, 0, compressedsize);
try {
inflater.inflate(chunkData);
} catch (DataFormatException e) {
return null;
} finally {
inflater.end();
}
return chunkData;
}
public static Extracted getMapChunkData(Chunk chunk, boolean includeinit, int subid) {
int j = 0;
ExtendedBlockStorage[] aextendedblockstorage = chunk.getBlockStorageArray();
int k = 0;
S21PacketChunkData.Extracted extracted = new S21PacketChunkData.Extracted();
if (dataarray == null || dataarray.length < 196864) {
dataarray = new byte[196864];
}
byte[] abyte = dataarray;
if (includeinit) {
chunk.sendUpdates = true;
}
int l;
for (l = 0; l < aextendedblockstorage.length; ++l) {
if (aextendedblockstorage[l] != null && (!includeinit || !aextendedblockstorage[l].isEmpty()) && (subid & 1 << l) != 0) {
extracted.field_150280_b |= 1 << l;
if (aextendedblockstorage[l].getBlockMSBArray() != null) {
extracted.field_150281_c |= 1 << l;
++k;
}
}
}
for (l = 0; l < aextendedblockstorage.length; ++l) {
if (aextendedblockstorage[l] != null && (!includeinit || !aextendedblockstorage[l].isEmpty()) && (subid & 1 << l) != 0) {
byte[] abyte1 = aextendedblockstorage[l].getBlockLSBArray();
System.arraycopy(abyte1, 0, abyte, j, abyte1.length);
j += abyte1.length;
}
}
NibbleArray nibblearray;
for (l = 0; l < aextendedblockstorage.length; ++l) {
if (aextendedblockstorage[l] != null && (!includeinit || !aextendedblockstorage[l].isEmpty()) && (subid & 1 << l) != 0) {
nibblearray = aextendedblockstorage[l].getMetadataArray();
System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length);
j += nibblearray.data.length;
}
}
for (l = 0; l < aextendedblockstorage.length; ++l) {
if (aextendedblockstorage[l] != null && (!includeinit || !aextendedblockstorage[l].isEmpty()) && (subid & 1 << l) != 0) {
nibblearray = aextendedblockstorage[l].getBlocklightArray();
System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length);
j += nibblearray.data.length;
}
}
if (!chunk.worldObj.provider.hasNoSky) {
for (l = 0; l < aextendedblockstorage.length; ++l) {
if (aextendedblockstorage[l] != null && (!includeinit || !aextendedblockstorage[l].isEmpty()) && (subid & 1 << l) != 0) {
nibblearray = aextendedblockstorage[l].getSkylightArray();
System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length);
j += nibblearray.data.length;
}
}
}
if (k > 0) {
for (l = 0; l < aextendedblockstorage.length; ++l) {
if (aextendedblockstorage[l] != null && (!includeinit || !aextendedblockstorage[l].isEmpty()) && aextendedblockstorage[l].getBlockMSBArray() != null && (subid & 1 << l) != 0) {
nibblearray = aextendedblockstorage[l].getBlockMSBArray();
System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length);
j += nibblearray.data.length;
}
}
}
if (includeinit) {
byte[] abyte2 = chunk.getBiomeArray();
System.arraycopy(abyte2, 0, abyte, j, abyte2.length);
j += abyte2.length;
}
extracted.field_150282_a = new byte[j];
System.arraycopy(abyte, 0, extracted.field_150282_a, 0, j);
return extracted;
}
}