/** * Copyright (c) 2011-2015, SpaceToad and the BuildCraft Team * http://www.mod-buildcraft.com * <p/> * BuildCraft is distributed under the terms of the Minecraft Mod Public * License 1.0, or MMPL. Please check the contents of the license located in * http://www.mod-buildcraft.com/MMPL-1.0.txt */ package buildcraft.builders; import java.io.IOException; import java.util.List; import io.netty.buffer.ByteBuf; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTSizeTracker; import net.minecraft.nbt.NBTTagCompound; import cpw.mods.fml.relauncher.Side; import buildcraft.BuildCraftBuilders; import buildcraft.BuildCraftCore; import buildcraft.api.library.LibraryAPI; import buildcraft.api.library.LibraryTypeHandler; import buildcraft.api.library.LibraryTypeHandlerByteArray; import buildcraft.api.library.LibraryTypeHandlerNBT; import buildcraft.core.blueprints.LibraryId; import buildcraft.core.lib.block.TileBuildCraft; import buildcraft.core.lib.inventory.SimpleInventory; import buildcraft.core.lib.network.command.CommandWriter; import buildcraft.core.lib.network.command.ICommandReceiver; import buildcraft.core.lib.network.command.PacketCommand; import buildcraft.core.lib.utils.NBTUtils; import buildcraft.core.lib.utils.NetworkUtils; /** * In this implementation, the blueprint library is the interface to the * *local* player blueprint. The player will be able to load blueprint on his * environment, and save blueprints to the server environment. */ public class TileBlueprintLibrary extends TileBuildCraft implements IInventory, ICommandReceiver { private static final int PROGRESS_TIME = 100; private static final int CHUNK_SIZE = 16384; public SimpleInventory inv = new SimpleInventory(4, "Electronic Library", 1); public int progressIn = 0; public int progressOut = 0; public List<LibraryId> entries; public int selected = -1; public EntityPlayer uploadingPlayer = null; public EntityPlayer downloadingPlayer = null; private LibraryId blueprintDownloadId; private byte[] blueprintDownload; public TileBlueprintLibrary() { } public void refresh() { if (worldObj.isRemote) { BuildCraftBuilders.clientDB.refresh(); entries = BuildCraftBuilders.clientDB.getBlueprintIds(); selected = -1; } } @Override public void initialize() { super.initialize(); refresh(); } public void deleteSelectedBpt() { if (selected != -1) { BuildCraftBuilders.clientDB.deleteBlueprint(entries.get(selected)); entries = BuildCraftBuilders.clientDB.getBlueprintIds(); if (selected >= entries.size()) { selected--; } } } @Override public void readFromNBT(NBTTagCompound nbttagcompound) { super.readFromNBT(nbttagcompound); inv.readFromNBT(nbttagcompound); } @Override public void writeToNBT(NBTTagCompound nbttagcompound) { super.writeToNBT(nbttagcompound); inv.writeToNBT(nbttagcompound); } @Override public int getSizeInventory() { return 4; } @Override public ItemStack getStackInSlot(int i) { return inv.getStackInSlot(i); } @Override public ItemStack decrStackSize(int i, int j) { ItemStack result = inv.decrStackSize(i, j); if (i == 0) { if (getStackInSlot(0) == null) { progressIn = 0; } } if (i == 2) { if (getStackInSlot(2) == null) { progressOut = 0; } } return result; } @Override public void setInventorySlotContents(int i, ItemStack itemstack) { inv.setInventorySlotContents(i, itemstack); if (i == 0) { if (getStackInSlot(0) != null && findHandler(0, LibraryTypeHandler.HandlerType.STORE) != null) { progressIn = 1; } else { progressIn = 0; } } if (i == 2) { if (getStackInSlot(2) != null && findHandler(2, LibraryTypeHandler.HandlerType.LOAD) != null) { progressOut = 1; } else { progressOut = 0; } } } @Override public ItemStack getStackInSlotOnClosing(int slot) { return inv.getStackInSlotOnClosing(slot); } @Override public String getInventoryName() { return ""; } @Override public int getInventoryStackLimit() { return 1; } @Override public boolean isItemValidForSlot(int i, ItemStack itemstack) { return false; } @Override public boolean isUseableByPlayer(EntityPlayer entityplayer) { return worldObj.getTileEntity(xCoord, yCoord, zCoord) == this; } @Override public void openInventory() { } @Override public void closeInventory() { } private LibraryTypeHandler findHandler(int slot, LibraryTypeHandler.HandlerType type) { if (!worldObj.isRemote) { ItemStack stack = getStackInSlot(slot); for (LibraryTypeHandler h : LibraryAPI.getHandlerSet()) { if (h.isHandler(stack, type)) { return h; } } } return null; } @Override public void updateEntity() { super.updateEntity(); if (worldObj.isRemote) { return; } if (progressIn > 0 && progressIn < PROGRESS_TIME) { progressIn++; } if (progressOut > 0 && progressOut < PROGRESS_TIME) { if (selected != -1) { progressOut++; } else { progressOut = 1; } } // On progress IN, we'll download the blueprint from the server to the // client, and then store it to the client. if (progressIn == 100 && getStackInSlot(1) == null) { LibraryTypeHandler handler = findHandler(0, LibraryTypeHandler.HandlerType.STORE); if (handler == null) { uploadingPlayer = null; return; } byte[] data = null; if (handler instanceof LibraryTypeHandlerNBT) { final NBTTagCompound nbt = new NBTTagCompound(); if (((LibraryTypeHandlerNBT) handler).store(getStackInSlot(0), nbt)) { data = NBTUtils.save(nbt); } } else if (handler instanceof LibraryTypeHandlerByteArray) { data = ((LibraryTypeHandlerByteArray) handler).store(getStackInSlot(0)); } if (data == null) { uploadingPlayer = null; return; } setInventorySlotContents(1, getStackInSlot(0)); setInventorySlotContents(0, null); final byte[] dataOut = data; final LibraryId id = new LibraryId(); id.name = handler.getName(getStackInSlot(1)); id.extension = handler.getOutputExtension(); if (uploadingPlayer != null) { BuildCraftCore.instance.sendToPlayer(uploadingPlayer, new PacketCommand(this, "downloadBlueprintToClient", new CommandWriter() { public void write(ByteBuf data) { id.generateUniqueId(dataOut); id.writeData(data); NetworkUtils.writeByteArray(data, dataOut); } })); uploadingPlayer = null; } } if (progressOut == 100 && getStackInSlot(3) == null) { BuildCraftCore.instance.sendToPlayer(downloadingPlayer, new PacketCommand(this, "requestSelectedBlueprint", null)); progressOut = 0; } } @Override public boolean hasCustomInventoryName() { return false; } @Override public void receiveCommand(String command, Side side, Object sender, ByteBuf stream) { if (side.isClient()) { if ("requestSelectedBlueprint".equals(command)) { if (isOutputConsistent()) { if (selected > -1 && selected < entries.size()) { // Work around 32k max limit on client->server final NBTTagCompound compound = BuildCraftBuilders.clientDB .load(entries.get(selected)); compound.setString("__filename", entries.get(selected).name); final byte[] bptData = NBTUtils.save(compound); final int chunks = (bptData.length + CHUNK_SIZE - 1) / CHUNK_SIZE; BuildCraftCore.instance.sendToServer(new PacketCommand(this, "uploadServerBegin", new CommandWriter() { public void write(ByteBuf data) { entries.get(selected).writeData(data); data.writeShort(chunks); } })); for (int i = 0; i < chunks; i++) { final int chunk = i; final int start = CHUNK_SIZE * chunk; final int length = Math.min(CHUNK_SIZE, bptData.length - start); BuildCraftCore.instance.sendToServer(new PacketCommand(this, "uploadServerChunk", new CommandWriter() { public void write(ByteBuf data) { data.writeShort(chunk); data.writeShort(length); data.writeBytes(bptData, start, length); } })); } BuildCraftCore.instance.sendToServer(new PacketCommand(this, "uploadServerEnd", null)); } else { BuildCraftCore.instance.sendToServer(new PacketCommand(this, "uploadNothingToServer", null)); } } } else if ("downloadBlueprintToClient".equals(command)) { LibraryId id = new LibraryId(); id.readData(stream); byte[] data = NetworkUtils.readByteArray(stream); try { LibraryTypeHandler handler = LibraryAPI.getHandlerFor(id.extension); if (handler == null) { return; } NBTTagCompound nbt = CompressedStreamTools.func_152457_a(data, NBTSizeTracker.field_152451_a); BuildCraftBuilders.clientDB.add(id, nbt); entries = BuildCraftBuilders.clientDB.getBlueprintIds(); } catch (IOException e) { e.printStackTrace(); } } } else if (side.isServer()) { if ("uploadNothingToServer".equals(command)) { setInventorySlotContents(3, getStackInSlot(2)); setInventorySlotContents(2, null); downloadingPlayer = null; } else if ("uploadServerBegin".equals(command)) { blueprintDownloadId = new LibraryId(); blueprintDownloadId.readData(stream); blueprintDownload = new byte[CHUNK_SIZE * stream.readUnsignedShort()]; } else if ("uploadServerChunk".equals(command)) { int start = stream.readUnsignedShort() * CHUNK_SIZE; int length = stream.readUnsignedShort(); if (blueprintDownload != null) { stream.readBytes(blueprintDownload, start, length); } else { stream.skipBytes(length); } } else if ("uploadServerEnd".equals(command)) { try { LibraryTypeHandler handler = LibraryAPI.getHandlerFor(blueprintDownloadId.extension); if (handler != null) { ItemStack output = null; if (handler instanceof LibraryTypeHandlerNBT) { NBTTagCompound nbt = CompressedStreamTools.func_152457_a(blueprintDownload, NBTSizeTracker.field_152451_a); output = ((LibraryTypeHandlerNBT) handler).load(getStackInSlot(2), nbt); } else if (handler instanceof LibraryTypeHandlerByteArray) { output = ((LibraryTypeHandlerByteArray) handler).load(getStackInSlot(2), blueprintDownload); } if (output != null) { setInventorySlotContents(3, output); setInventorySlotContents(2, null); } } } catch (IOException e) { e.printStackTrace(); } blueprintDownloadId = null; blueprintDownload = null; downloadingPlayer = null; } else if ("selectBlueprint".equals(command)) { selected = stream.readInt(); } } } public void selectBlueprint(int index) { selected = index; BuildCraftCore.instance.sendToServer(new PacketCommand(this, "selectBlueprint", new CommandWriter() { @Override public void write(ByteBuf data) { data.writeInt(selected); } })); } private boolean isOutputConsistent() { if (selected <= -1 || selected >= entries.size() || getStackInSlot(2) == null) { return false; } return LibraryAPI.getHandlerFor(entries.get(selected).extension).isHandler(getStackInSlot(2), LibraryTypeHandler.HandlerType.LOAD); } }