package com.brandon3055.townbuilder.schematics;
import com.brandon3055.townbuilder.utills.LogHelper;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.relauncher.ReflectionHelper;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipException;
/**
* Created by Brandon on 21/02/2015.
*/
public class SchematicHandler {
private static String savePath;
private static File saveFolder;
public static void init(FMLPreInitializationEvent event) {
savePath = event.getModConfigurationDirectory().getParentFile().getAbsolutePath() + "/mods/townbuilder";
}
public static File getSaveFolder() {
if (saveFolder == null) {
saveFolder = new File(savePath);
}
if (!saveFolder.exists()) saveFolder.mkdir();
return saveFolder;
}
public static NBTTagCompound loadCompoundFromFile(String fileName) throws SchematicException {
File file = getFile(fileName);
if (!file.exists()) throw new SchematicException("Schematic dose not exist");
try {
LogHelper.info("Reading file [" + file.length() + " bytes]");
FileInputStream fis = new FileInputStream(file.getCanonicalFile());
DataInputStream is = new DataInputStream(new GZIPInputStream(fis));
int type = is.readByte();
String name = is.readUTF();
if (!name.equals("Schematic")) throw new SchematicException("Invalid Schematic [" + file.getName() + "]");
fis.close();
is.close();
fis = new FileInputStream(file.getCanonicalFile());
is = new DataInputStream(new GZIPInputStream(fis));
NBTTagCompound c = CompressedStreamTools.read(is);
fis.close();
is.close();
LogHelper.info("Read Complete");
return c;
}
catch (IOException e) {
if (e instanceof ZipException) {
try {
NBTTagCompound c = CompressedStreamTools.read(file);
c.setBoolean("UseOldLoader", true);
return c;
}
catch (IOException e1) {
e1.printStackTrace();
}
}
else e.printStackTrace();
}
throw new SchematicException("Failed to read schematic. Unknown error");
}
public static File getFile(String fileName) {
File file = new File(getSaveFolder(), fileName + ".schematic");
if (!file.exists()) return null;
return file;
}
public static void saveCompoundToFile(NBTTagCompound compound, String fileName) {
if (compound == null) return;
File schematicFile = new File(getSaveFolder(), fileName + ".schematic");
try {
LogHelper.info("Writing schematic to file...");
DataOutputStream os = new DataOutputStream(new GZIPOutputStream(new FileOutputStream(schematicFile)));
writeTag(compound, os);
os.close();
LogHelper.info("Write complete");
}
catch (IOException e) {
e.printStackTrace();
}
}
private static void writeTag(NBTBase nbtBase, DataOutput dataOutput) throws IOException {
dataOutput.writeByte(nbtBase.getId());
if (nbtBase.getId() != 0) {
dataOutput.writeUTF("Schematic");
try {
ReflectionHelper.findMethod(NBTBase.class, nbtBase, new String[]{"write", "func_74734_a"}, DataOutput.class).invoke(nbtBase, dataOutput);
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
public static void deleteCompoundFile(String fileName) {
File schematicFile = new File(getSaveFolder(), fileName + ".schematic");
if (schematicFile.exists()) schematicFile.delete();
}
public static NBTTagCompound getCompoundForArea(World world, int posX, int posY, int posZ, int width, int height, int length) {
NBTTagCompound compound = new NBTTagCompound();
compound.setShort("Width", (short) width);
compound.setShort("Height", (short) height);
compound.setShort("Length", (short) length);
NBTTagList tileList = new NBTTagList();
NBTTagCompound idNameConversion = new NBTTagCompound();
byte[] blocks = new byte[width * height * length];
byte[] addBlocks = null;
byte[] blockData = new byte[width * height * length];
Map<Integer, String> idToNameMap = new HashMap<Integer, String>();
int totalBlocks = width * height * length;
LogHelper.info("Creating schematic containing " + totalBlocks + " Blocks");
int i = totalBlocks / 10;
int blocksCopied = 0;
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
for (int z = 0; z < length; ++z) {
int index = y * width * length + z * width + x;
BlockPos pos = new BlockPos(x + posX, y + posY, z + posZ);
IBlockState state = world.getBlockState(pos);
Block block = state.getBlock();
// Save 4096 IDs in an AddBlocks section
if (Block.getIdFromBlock(block) > 255) {
if (addBlocks == null) { // Lazily create section
addBlocks = new byte[(blocks.length >> 1) + 1];
}
addBlocks[index >> 1] = (byte) (((index & 1) != 0) ? addBlocks[index >> 1] & 0xF0 | (Block.getIdFromBlock(block) >> 8) & 0xF : addBlocks[index >> 1] & 0xF | ((Block.getIdFromBlock(block) >> 8) & 0xF) << 4);
}
blocks[index] = (byte) Block.getIdFromBlock(block);
blockData[index] = (byte) block.getMetaFromState(state);//world.getBlockMetadata(x + posX, y + posY, z + posZ);
if (world.getTileEntity(pos) != null) {
NBTTagCompound tileCompound = new NBTTagCompound();
world.getTileEntity(pos).writeToNBT(tileCompound);
tileCompound.setInteger("x", x);
tileCompound.setInteger("y", y);
tileCompound.setInteger("z", z);
tileList.appendTag(tileCompound);
}
if (!idToNameMap.containsKey(Block.getIdFromBlock(block))) {
idToNameMap.put(Block.getIdFromBlock(block), Block.REGISTRY.getNameForObject(block).toString());//GameData.getBlockRegistry().getNameForObject(world.getBlock(x + posX, y + posY, z + posZ)));
idNameConversion.setString(String.valueOf(Block.getIdFromBlock(block)), Block.REGISTRY.getNameForObject(block).toString());//GameData.getBlockRegistry().getNameForObject(world.getBlock(x + posX, y + posY, z + posZ)));
}
blocksCopied++;
if (width > 1 && height > 1 && length > 1 && blocksCopied % i == 0) LogHelper.info("Progress: " + (((double) blocksCopied / (double) totalBlocks) * 100D) + "%%");
}
}
}
compound.setByteArray("Blocks", blocks);
compound.setByteArray("Data", blockData);
if (addBlocks != null) compound.setByteArray("AddBlocks", addBlocks);
compound.setTag("TileEntities", tileList);
compound.setTag("idConversions", idNameConversion);
return compound;
}
public static void loadAreaFromCompound(NBTTagCompound compound, World world, int posX, int posY, int posZ, boolean copyAir) throws SchematicException {
if (compound != null && compound.hasKey("UseOldLoader")) {
// loadAreaFromCompoundOld(compound, world, posX, posY, posZ, copyAir);
LogHelper.error("The old schematic loader is nolonger supported!");
return;
}
if (!compound.hasKey("Blocks")) throw new SchematicException("Schematic file is missing a \"Blocks\" tag");
short width = compound.getShort("Width");
short height = compound.getShort("Height");
short length = compound.getShort("Length");
byte[] blockId = compound.getByteArray("Blocks");
byte[] blockData = compound.getByteArray("Data");
byte[] addId = new byte[0];
short[] blocks = new short[blockId.length];
if (compound.hasKey("AddBlocks")) addId = compound.getByteArray("AddBlocks");
for (int index = 0; index < blockId.length; index++) {
if ((index >> 1) >= addId.length) { // No corresponding AddBlocks index
blocks[index] = (short) (blockId[index] & 0xFF);
}
else {
if ((index & 1) != 0) {
blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (short) (blockId[index] & 0xFF));
}
else {
blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (short) (blockId[index] & 0xFF));
}
}
}
NBTTagCompound idNameConversion = null;
if (compound.hasKey("idConversions")) idNameConversion = compound.getCompoundTag("idConversions");
NBTTagList tileList = compound.getTagList("TileEntities", 10);
Map<Integer, Block> blockMap = new HashMap<Integer, Block>();
int totalBlocks = width * height * length;
LogHelper.info("Pasting schematic containing " + totalBlocks + " Blocks");
int ii = totalBlocks / 10;
int blocksCopied = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
for (int z = 0; z < length; z++) {
BlockPos pos = new BlockPos(posX + x, posY + y, posZ + z);
int index = y * width * length + z * width + x;
int id = blocks[index];
if (!blockMap.containsKey(id)) {
Block block = Block.REGISTRY.getObject(new ResourceLocation(idNameConversion.getString(String.valueOf(id))));
if (idNameConversion != null && block != null)//GameData.getBlockRegistry().getObject(idNameConversion.getString(String.valueOf(id))) != null)
{
blockMap.put(id, block);//GameData.getBlockRegistry().getObject(idNameConversion.getString(String.valueOf(id))));
}
else blockMap.put(id, Block.getBlockById(id));
}
if (!copyAir && id == 0) continue;
Chunk chunk = world.getChunkFromBlockCoords(pos);
BlockPos pos2 = new BlockPos((posX + x) & 15, (posY + y), (posZ + z) & 15);
if (chunk.getTileEntity(pos2, Chunk.EnumCreateEntityType.CHECK) != null) {
chunk.getTileEntity(pos2, Chunk.EnumCreateEntityType.CHECK).invalidate();
}
chunk.removeInvalidTileEntity(pos2);
IBlockState oldState = world.getBlockState(pos);
IBlockState newState = blockMap.get(id).getStateFromMeta(blockData[(y * length + z) * width + x]);
chunk.setBlockState(pos, newState);
// chunk.setBlockMetadata((posX + x) & 15, (posY + y), (posZ + z) & 15, blockData[(y * length + z) * width + x]);
// world.markBlockForUpdate(posX + x, posY + y, posZ + z);TODO This probably needs to happen
world.notifyBlockUpdate(pos, oldState, newState, 3);
if (world.getTileEntity(pos) != null) {
TileEntity tile = world.getTileEntity(pos);
if (tileList != null) for (int i = 0; i < tileList.tagCount(); i++) {
if (tileList.getCompoundTagAt(i).getInteger("x") == x && tileList.getCompoundTagAt(i).getInteger("y") == y && tileList.getCompoundTagAt(i).getInteger("z") == z) {
tile.readFromNBT(tileList.getCompoundTagAt(i));
}
}
tile.setPos(pos);
// tile.xCoord = posX + x;
// tile.yCoord = posY + y;
// tile.zCoord = posZ + z;
}
blocksCopied++;
if (width > 1 && height > 1 && length > 1 && blocksCopied % ii == 0) LogHelper.info("Progress: " + (((double) blocksCopied / (double) totalBlocks) * 100D) + "%%");
}
}
}
}
// public static void loadAreaFromCompoundOld(NBTTagCompound compound, World world, int x, int y, int z, boolean copyAir) {
// int xSize = compound.getShort("Width");
// int ySize = compound.getShort("Height");
// int zSize = compound.getShort("Length");
//
// boolean isVanillaSchematic = compound.hasKey("Data") && compound.hasKey("Blocks") && compound.hasKey("Materials");
//
// NBTTagCompound idNameConversion = compound.getCompoundTag("idConversions");
//
// NBTTagList tileList = compound.getTagList("TileEntities", 10);
//
// Map<Integer, Block> blockMap = new HashMap<Integer, Block>();
//
// int[] ids = new int[0];
// byte[] blocks = new byte[0];
// byte[] addblocks = new byte[0];
// boolean hasAdditionalBlocks = false;
// if (!isVanillaSchematic) {
// ids = compound.getIntArray("Ids");
// if (ids.length < xSize * ySize + zSize) {
// LogHelper.error("invalid id array " + ids.length);
// return;
// }
// }
// else {
// blocks = compound.getByteArray("Blocks");
// if (blocks.length < xSize * ySize + zSize) {
// LogHelper.error("invalid block array " + blocks.length);
// return;
// }
// if (compound.hasKey("AddBlocks")) {
// hasAdditionalBlocks = true;
// addblocks = compound.getByteArray("AddBlocks");
// if (addblocks.length < xSize * ySize + zSize) {
// LogHelper.error("invalid additional blocks array " + addblocks.length);
// return;
// }
// }
// }
//
//
// byte[] metta = new byte[0];
// if (isVanillaSchematic) metta = compound.getByteArray("Data");
// else metta = compound.getByteArray("Metta");
//
// if (metta.length < xSize * ySize + zSize) {
// LogHelper.error("invalid metta array " + metta.length);
// return;
// }
//
// int totalBlocks = xSize * ySize * zSize;
//
// LogHelper.info("Pasting schematic containing " + totalBlocks + " Blocks");
//
// int ii = totalBlocks / 10;
// int blocksCopied = 0;
//
// for (int ry = 0; ry < ySize; ry++) {
// for (int rz = 0; rz < zSize; rz++) {
// for (int rx = 0; rx < xSize; rx++) {
// BlockPos pos = new BlockPos(rx + x, ry + y, rz + z);
// int id = 0;
// if (isVanillaSchematic) {
// id = blocks[(ry * zSize + rz) * xSize + rx] + (hasAdditionalBlocks ? addblocks[(ry * zSize + rz) * xSize + rx] : 0);
// }
// else id = ids[(ry * zSize + rz) * xSize + rx];
//
// if (!blockMap.containsKey(id)) {
// if (!isVanillaSchematic && GameData.getBlockRegistry().getObject(idNameConversion.getString(String.valueOf(id))) != null) {
// blockMap.put(id, GameData.getBlockRegistry().getObject(idNameConversion.getString(String.valueOf(id))));
// }
// else if (isVanillaSchematic) {
// blockMap.put(id, Block.getBlockById(id));
// }
// else continue;
// }
//
// if (!copyAir && id == 0) continue;
//
//
// Chunk chunk = world.getChunkFromBlockCoords(x + rx, z + rz);
//
// if (chunk.getTileEntityUnsafe((x + rx) & 15, (y + ry), (z + rz) & 15) != null) {
// chunk.getTileEntityUnsafe((x + rx) & 15, (y + ry), (z + rz) & 15).invalidate();
// }
// chunk.removeInvalidTileEntity((x + rx) & 15, (y + ry), (z + rz) & 15);
//
// chunk.func_150807_a((x + rx) & 15, (y + ry), (z + rz) & 15, blockMap.get(id), metta[(ry * zSize + rz) * xSize + rx]);
// chunk.setBlockMetadata((x + rx) & 15, (y + ry), (z + rz) & 15, metta[(ry * zSize + rz) * xSize + rx]);
// world.markBlockForUpdate(x + rx, y + ry, z + rz);
//
// if (world.getTileEntity(x + rx, y + ry, z + rz) != null) {
// TileEntity tile = world.getTileEntity(x + rx, y + ry, z + rz);
// if (tileList != null) for (int i = 0; i < tileList.tagCount(); i++) {
// if (tileList.getCompoundTagAt(i).getInteger("x") == rx && tileList.getCompoundTagAt(i).getInteger("y") == ry && tileList.getCompoundTagAt(i).getInteger("z") == rz) {
// tile.readFromNBT(tileList.getCompoundTagAt(i));
// }
// }
// tile.xCoord = x + rx;
// tile.yCoord = y + ry;
// tile.zCoord = z + rz;
// }
//
//
// blocksCopied++;
//
// if (xSize > 1 && ySize > 1 && zSize > 1 && blocksCopied % ii == 0) LogHelper.info("Progress: " + (((double) blocksCopied / (double) totalBlocks) * 100D) + "%%");
// }
// }
// }
// }
public static String[] getSchematics() {
String[] s = SchematicHandler.getSaveFolder().list();
for (int i = 0; i < s.length; i++) {
if (s[i].contains(".schematic")) s[i] = s[i].substring(0, s[i].lastIndexOf(".schematic"));
}
return s;
}
public static class SchematicException extends Exception {
private String msg;
public SchematicException(String msg) {
this.msg = msg;
}
@Override
public String getMessage() {
return msg;
}
}
}