public class GuiAutoCrafter extends AdvancedGui {

	private final AutoCrafter tile;

	public GuiAutoCrafter(IGuiData tile, EntityPlayer player) {
		super(new TileContainer(tile, player));
		this.tile = (AutoCrafter)tile;
		this.MAIN_TEX = new ResourceLocation(Main.ID, "textures/gui/auto_craft.png");
	}

	@Override
	public void initGui() {
		this.xSize = 176;
		this.ySize = 168;
		super.initGui();
		this.tabsY = -52;
		for (int j = 0; j < 3; j++)
			for (int i = 0; i < 3; i++)
				guiComps.add(new Button(i + j * 3, i * 18 + 19, j * 18 + 18, 12, 12, 1).texture(176, 12).setTooltip("craft.in"));
		guiComps.add(new Button(9, 82, 54, 12, 12, 2).texture(176, 12).setTooltip("craft.out"));
		guiComps.add(new NumberSel(10, 124, 33, 18, 18, "%d", 0, 64, 8).setTooltip("craftAm"));
		guiComps.add(new Button(11, 142, 33, 18, 18, 0, ()-> tile.rsMode & 1, (b)-> sendCommand(11)).texture(188, 0).setTooltip("craft.rsIn#"));
		guiComps.add(new Button(12, 79, 15, 18, 18, 0, ()-> tile.rsMode >> 1 & 1, (b)-> sendCommand(12)).texture(206, 0).setTooltip("craft.rsOut#"));
		guiComps.add(new InfoTab(13, 7, 8, 7, 9, "craft.infoB"));
	}

	@Override
	protected Object getDisplVar(int id) {
		if (id < 10) {
			int b = tile.grid[id];
			return b < 0 || b >= 6 ? b : EnumFacing.VALUES[b];
		} else if (id == 10) return tile.amount;
		else return null;
	}

	@Override
	protected void setDisplVar(int id, Object obj, boolean send) {
		PacketBuffer data = BlockGuiHandler.getPacketTargetData(tile.pos());
		if (id < 10) {
			data.writeByte(id);
			int b = tile.grid[id];
			b = (b + ((Integer)obj == 0 ? 2 : 7)) % 7 - 1;
			tile.grid[id] = (byte)b;
			data.writeByte(b);
		} else if (id == 10) {
			data.writeByte(10);
			data.writeByte(tile.amount = (Integer)obj);
		} else return;
		if (send) BlockGuiHandler.sendPacketToServer(data);
	}

}
 public class GuiDropInterface extends AdvancedGui {

	private DropedItemInterface tile;

	public GuiDropInterface(IGuiData tile, EntityPlayer player) {
		super(new DataContainer(tile, player));
		this.tile = (DropedItemInterface)tile;
		this.MAIN_TEX = new ResourceLocation("indlog:textures/gui/drop_interface.png");
	}

	@Override
	public void initGui() {
		xSize = 104;
		ySize = 76;
		super.initGui();
		guiComps.add(new NumberSel(0, 79, 33, 18, 18, "%d", 1, MAX_RANGE, 8).setTooltip("dropInterf.drop"));
		guiComps.add(new NumberSel(1, 25, 33, 18, 18, "%d", 1, MAX_RANGE, 8).setTooltip("dropInterf.front"));
		guiComps.add(new NumberSel(2, 43, 33, 18, 18, "%d", 0, MAX_RANGE, 8).setTooltip("dropInterf.right"));
		guiComps.add(new NumberSel(3, 7, 33, 18, 18, "%d", 0, MAX_RANGE, 8).setTooltip("dropInterf.left"));
		guiComps.add(new NumberSel(4, 25, 51, 18, 18, "%d", 0, MAX_RANGE, 8).setTooltip("dropInterf.down"));
		guiComps.add(new NumberSel(5, 25, 15, 18, 18, "%d", 0, MAX_RANGE, 8).setTooltip("dropInterf.up"));
		guiComps.add(new Text<Object[]>(6, 43, 15, 54, 8, "dropInterf.size", ()-> new Object[] {
				tile.settings[2] + tile.settings[3] + 1,
				tile.settings[4] + tile.settings[5] + 1,
				tile.settings[1]
		}).center().setTooltip("dropInterf.sizeI"));
	}

	@Override
	protected Object getDisplVar(int id) {
		return tile.settings[id];
	}

	@Override
	protected void setDisplVar(int id, Object obj, boolean send) {
		PacketBuffer data = BlockGuiHandler.getPacketTargetData(tile.pos());
		int v = (Integer)obj;
		if (id == 0 && v > tile.settings[1]) v = tile.settings[1];
		data.writeByte(id).writeByte(tile.settings[id] = v);
		if (send) BlockGuiHandler.sendPacketToServer(data);
	}

}
 public class GuiFluidIO extends AdvancedGui {

	private static final char[] dirs = {'B', 'T', 'N', 'S', 'W', 'E', '!', '?'};

	private final FluidIO tile;

	public GuiFluidIO(IGuiData tile, EntityPlayer player) {
		super(new TileContainer(tile, player));
		this.tile = (FluidIO)tile;
		this.MAIN_TEX = new ResourceLocation("indlog:textures/gui/tank.png");
	}

	@Override
	public void initGui() {
		this.xSize = 226;
		this.ySize = 98;
		super.initGui();
		this.titleX = xSize * 7 / 8;
		boolean in = tile.tank.output;
		guiComps.add(new Button(1, 183, 73, 18, 18, 0, ()-> tile.blockNotify ? 1 : 0, (b)-> sendCommand(0)).texture(226, in ? 72:36).setTooltip("fluidIO.update#"));
		guiComps.add(new NumberSel(2, 201, 73, 18, 18, "%d", 0, FluidOutlet.MAX_SIZE, 8, ()-> tile.range, (n)-> {
			PacketBuffer dos = BlockGuiHandler.getPacketTargetData(tile.pos());
			dos.writeByte(1).writeByte(tile.range = n);
			BlockGuiHandler.sendPacketToServer(dos);
		}).setTooltip("fluidIO.range"));
		guiComps.add(new Text<Object[]>(3, 0, ySize, xSize, 8, "fluidIO.pos", ()-> new Object[]{
				dirs[tile.debugI >> 24 & 7],
				dirs[tile.debugI >> 27 & 7],
				dirs[tile.debugI >> 30 & 1 | 6],
				(byte)tile.debugI,
				(byte)(tile.debugI >> 8),
				(byte)(tile.debugI >> 16)
			}).font(0xffffffff, 8).center());
		GuiTank.addTankButtons(this, 2);
	}

} public class GuiItemBuffer extends AdvancedGui {

	private final Buffer tile;
	private final int N;
	private final float StepSlot, StepStack;

	public GuiItemBuffer(IGuiData tile, EntityPlayer player) {
		super(new TileContainer(tile, player));
		this.tile = (Buffer)tile;
		this.MAIN_TEX = new ResourceLocation(Main.ID, "textures/gui/buffer.png");
		this.drawBG = 6;
		this.N = this.tile.inventory.items.length;
		this.StepSlot = 1F / (float)Buffer.SLOTS[this.tile.type];
		this.StepStack = 1F / (float)Buffer.STACKS[this.tile.type];
	}

	@Override
	public void initGui() {
		int ofs = (N + 11) / 12 * 18;
		xSize = 230;
		ySize = 114 + ofs;
		super.initGui();
		tabsY = ofs + 31;
		guiComps.add(new InfoTab(0, 7, 6, 7, 8, "buffer.info"));
		guiComps.add(new Slider(1, 194, 42 + ofs, 64, 230, 0, 10, 2, false).scroll(StepSlot));
		guiComps.add(new Slider(2, 212, 42 + ofs, 64, 240, 0, 10, 2, false).scroll(StepStack));
		guiComps.add(new Slider(3, 176, 42 + ofs, 64, 230, 2, 10, 2, false).scroll(StepSlot));
		guiComps.add(new Tooltip<Object[]>(4, 194, 42 + ofs, 10, 64, "buffer.slots"));
		guiComps.add(new Tooltip<Object[]>(5, 212, 42 + ofs, 10, 64, "buffer.stack"));
		guiComps.add(new Tooltip<Integer>(6, 176, 42 + ofs, 10, 64, "buffer.start"));
		guiComps.add(new Button(7, 193, 17 + ofs, 12, 12, 0).texture(242, 29).setTooltip("buffer.side#"));
		guiComps.add(new Button(8, 175, 17 + ofs, 12, 12, 0).texture(230, 17).setTooltip("buffer.reset"));
		guiComps.add(new Button(9, 211, 17 + ofs, 12, 12, 0).texture(230, 107).setTooltip("buffer.lock#"));//TODO texture coord
		setEnabled(3, false);
		setEnabled(6, false);
	}

	@Override
	protected Object getDisplVar(int id) {
		switch(id) {
		case 1: return 1F - (float)tile.getEnd() * StepSlot;
		case 2: return 1F - (float)tile.inventory.stackSize * StepStack;
		case 3: return 1F - (float)tile.getStart() * StepSlot;
		case 4: return new Object[] {tile.getEnd(), Buffer.SLOTS[tile.type]};
		case 5: return new Object[] {tile.inventory.stackSize, Buffer.STACKS[tile.type]};
		case 6: return tile.getStart();
		case 7: return (int)tile.selSide;
		case 8: return tile.selSide >= 0 && tile.sideAccs[tile.selSide] != null ? 0 : -1;
		case 9: return tile.inventory.locked ? 1 : 0;
		default: return null;
		}
	}

	@Override
	protected void setDisplVar(int id, Object obj, boolean send) {
		PacketBuffer data = BlockGuiHandler.getPacketTargetData(tile.pos());
		switch(id) {
		case 1: {
			int v = Math.round((1F - (Float)obj) / StepSlot + 0.4F);
			v = Math.min(Math.max(v, 0), Buffer.SLOTS[tile.type]);
			if (tile.selSide < 0 || tile.selSide >= 6) {
				data.writeByte(0);
				data.writeByte(tile.inventory.slots = v);
			} else {
				data.writeByte(3);
				data.writeByte(tile.selSide);
				data.writeByte(tile.getStart());
				data.writeByte(v);
				GroupAccess acc = tile.sideAccs[tile.selSide];
				if (acc != null) acc.setRange(acc.start, v);
			}
		} break;
		case 2: {
			int v = Math.round((1F - (Float)obj) / StepStack + 0.4F);
			data.writeByte(1);
			data.writeInt(tile.inventory.stackSize = Math.min(Math.max(v, 1), Buffer.STACKS[tile.type]));
		} break;
		case 3: {
			int v = Math.round((1F - (Float)obj) / StepSlot + 0.4F);
			if (tile.selSide < 0 || tile.selSide >= 6) return;
			data.writeByte(3);
			data.writeByte(tile.selSide);
			data.writeByte(v);
			data.writeByte(tile.getEnd());
			GroupAccess acc = tile.sideAccs[tile.selSide];
			if (acc != null) acc.setRange(v, acc.start + acc.size);
		} break;
		case 7:
			if ((Integer)obj == 0) {
				if (++tile.selSide >= 6) tile.selSide = -1;
			} else if (--tile.selSide < -1) tile.selSide = 5;
			setEnabled(3, tile.selSide >= 0);
			setEnabled(6, tile.selSide >= 0);
			return;
		case 8:
			data.writeByte(2);
			data.writeByte(tile.selSide);
			break;
		case 9:
			data.writeByte(4);
			break;
		}
		if (send) BlockGuiHandler.sendPacketToServer(data);
	}

	@Override
	protected void drawGuiContainerBackgroundLayer(float t, int mouseX, int mouseY) {
		mc.renderEngine.bindTexture(MAIN_TEX);
		drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, 15);
		int s = tile.getStart(), e = tile.getEnd();
		drawSlots(0, s, 132);
		drawSlots(s, e, 114);
		drawSlots(e, N, 132);
		drawSlots(N, (N + 11) / 12 * 12, 150);
		drawTexturedModalRect(guiLeft, guiTop + ySize - 99, 0, 15, xSize, 99);
		if (tile.selSide >= 0)
			drawTexturedModalRect(guiLeft + 175, guiTop + ySize - 85, 230, 29, 12, 78);
		super.drawGuiContainerBackgroundLayer(t, mouseX, mouseY);
	}

	@Override
	protected void drawGuiContainerForegroundLayer(int mx, int my) {
		super.drawGuiContainerForegroundLayer(mx, my);
		if (tile.selSide >= 0 && tile.selSide < 6)
			drawSideCube(tabsX - 64, tabsY, tile.selSide, (byte) 3);
	}

	@Override
	public void updateScreen() {
		super.updateScreen();
		setEnabled(8, tile.selSide >= 0 && tile.selSide < 6 && tile.sideAccs[tile.selSide] != null);
	}

	private void drawSlots(int i0, int i1, int ty) {
		if (i1 <= i0) return;
		int w0 = i0 % 12 * 18 + 7, w1 = i1 % 12 * 18 + 7;
		int h0 = i0 / 12, h1 = i1 / 12;
		if (h0 == h1 && w0 > 7) drawTexturedModalRect(guiLeft + w0, guiTop + h0 * 18 + 15, w0, ty, w1 - w0, 18);
		else {
			if (w0 > 7) {drawTexturedModalRect(guiLeft + w0, guiTop + h0 * 18 + 15, w0, ty, xSize - w0, 18); h0++;}
			for (int h = h0; h < h1; h++) drawTexturedModalRect(guiLeft, guiTop + h * 18 + 15, 0, ty, xSize, 18);
			if (w1 > 7) drawTexturedModalRect(guiLeft, guiTop + h1 * 18 + 15, 0, ty, w1, 18);
		}
	}

	@Override
	public void setWorldAndResolution(Minecraft mc, int width, int height) {
		super.setWorldAndResolution(mc, width, height);
		this.itemRender = RenderItemOverride.instance();
	}

}
 public class GuiTank extends AdvancedGui {
	private static final long interval = 5000;
	private final Tank tile;
	private static long clickTime = 0;

	public GuiTank(IGuiData tile, EntityPlayer player) {
		super(new TileContainer(tile, player));
		this.tile = (Tank)tile;
		this.MAIN_TEX = new ResourceLocation("indlog:textures/gui/tank.png");
	}

	@Override
	public void initGui() {
		this.xSize = 226;
		this.ySize = 98;
		super.initGui();
		this.titleX = xSize * 3 / 4;
		guiComps.add(new Button(1, 201, 73, 18, 18, 0, ()-> tile.tank.output ? 1:0, (t)-> sendCommand(2)).texture(226, 0).setTooltip("tank.dir#"));
		guiComps.add(new Button(2, 176, 84, 7, 7, 0, ()-> tile.auto ? 1:0, (t)-> sendCommand(3)).texture(249, 28).setTooltip("tank.auto#"));
		addTankButtons(this, 0);
	}

	private static boolean confirm() {
		return System.currentTimeMillis() - clickTime < interval;
	}

	public static void addTankButtons(AdvancedGui gui, int cmd) {
		int i = gui.guiComps.size();
		TileContainer cont = (TileContainer)gui.inventorySlots;
		for (TankSlot tank : cont.tankSlots) {
			gui.guiComps.add(gui.new Button(i++, tank.xPos - 8, tank.yPos, 7, 7, 0, ()-> confirm() ? 1:0, (t)-> {
			if(confirm()) {
				gui.sendCommand(cmd + tank.tankNumber * 2);
				clickTime = 0;
			} else clickTime = System.currentTimeMillis();
		}).texture(249, 14).setTooltip("tank.del#"));
			if (tank.inventory instanceof AdvancedTank) {
				AdvancedTank at = (AdvancedTank)tank.inventory;
				gui.guiComps.add(gui.new Button(i++, tank.xPos - 8, tank.yPos + (tank.size & 0xf) * 18 - 9, 7, 7, 0, ()-> at.lock ? 1:0, (t)-> gui.sendCommand(cmd + tank.tankNumber * 2 + 1)).texture(249, 0).setTooltip("tank.lock#"));
			}
		}
	}

}
 public class FluidPipeRenderer extends TileEntitySpecialRenderer<FluidPipe> {

	@Override
	public void renderTileEntityAt(FluidPipe te, double x, double y, double z, float partialTicks, int destroyStage) {
		if (te.content != null) FluidRenderer.instance.render(te.content, te, x, y + 0.3755, z, 0.249, 0.249);
	}

}
 @SideOnly(Side.CLIENT)
public class FluidRenderer implements IModeledTESR {

	private static final String name = "fluid_block";
	public static final FluidRenderer instance = new FluidRenderer();
	private IntArrayModel baseModel = new IntArrayModel(0);
	private HashMap<Fluid, IntArrayModel> fluidModels = new HashMap<Fluid, IntArrayModel>();
	private HashMap<Fluid, Integer> fluidColors = new HashMap<Fluid, Integer>();

	public IntArrayModel getFor(Fluid fluid) {
		IntArrayModel m = fluidModels.get(fluid);
		if (m != null) return m;
		ResourceLocation res;
		if ((res = fluid.getStill()) == null && (res = fluid.getFlowing()) == null) return null;
		TextureAtlasSprite tex = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(res.toString());
		if (tex == null) return null;
		fluidModels.put(fluid, m = baseModel.withTexture(tex));
		return m;
	}

	public int fluidColor(Fluid fluid) {
		Integer c = fluidColors.get(fluid);
		if (c != null) return c;
		int fc = fluid.getColor();
		ResourceLocation res;
		if ((res = fluid.getStill()) == null && (res = fluid.getFlowing()) == null) return fc;
		TextureAtlasSprite tex = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(res.toString());
		if (tex == null) return fc;
		int r = 0, g = 0, b = 0, n = 0;
		for (int i = 0; i < tex.getFrameCount(); i++)
			for (int[] arr : tex.getFrameTextureData(i)) {
				for (int k : arr) {
					int a = k >> 24 & 0xff;
					n += a;
					r += (k >> 16 & 0xff) * a;
					g += (k >> 8 & 0xff) * a;
					b += (k & 0xff) * a;
				}
			}
		r = r / n * (fc >> 16 & 0xff) / 255 & 0xff;
		g = g / n * (fc >> 8 & 0xff) / 255 & 0xff;
		b = b / n * (fc & 0xff) / 255 & 0xff;
		fc = r << 16 | g << 8 | b;
		fluidColors.put(fluid, fc);
		return fc;
	}

	@Override
	public void bakeModels(IResourceManager manager) {
		try {
			baseModel = SpecialModelLoader.loadTESRModel(Main.ID, name);
			fluidModels.clear();
			fluidColors.clear();
		} catch (Exception e) {
			FMLLog.log("InductiveAutomation", Level.ERROR, e, "failed to load fluid model: ", name);
		}
	}

	public static int RGBtoBGR(int c) {
		return c & 0xff00ff00 | c << 16 & 0xff0000 | c >> 16 & 0xff;
	}

	public void render(FluidStack stack, TileEntity te, double x, double y, double z, double dxz, double dy) {
		Fluid fluid = stack.getFluid();
		IntArrayModel m = getFor(fluid);
		GlStateManager.disableLighting();
		Profile.TRANSPARENT_MODEL.apply();
		m.setColor(RGBtoBGR(fluid.getColor(stack)));
		m.setBrightness(te.getWorld().getCombinedLight(te.getPos(), fluid.getLuminosity(stack)));
		Minecraft.getMinecraft().renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
		GlStateManager.pushMatrix();
		GlStateManager.translate(x + 0.5D, y, z + 0.5D);
		GlStateManager.scale(dxz, dy, dxz);
		VertexBuffer t = Tessellator.getInstance().getBuffer();
		t.begin(GL11.GL_QUADS, IntArrayModel.FORMAT);
		t.addVertexData(m.vertexData);
		Tessellator.getInstance().draw();
		GlStateManager.popMatrix();
		Profile.TRANSPARENT_MODEL.clean();
	}

}
 public class ItemPipeRenderer extends TileEntitySpecialRenderer<ItemPipe> {

	private static final double scale = 0.501D;
	private final RenderItem renderItem = Minecraft.getMinecraft().getRenderItem();

	@Override
	public void renderTileEntityAt(ItemPipe te, double x, double y, double z, float partialTicks, int destroyStage) {
		if (te.content != null) renderItem(te.content, x, y, z);
	}

	private void renderItem(ItemStack item, double x, double y, double z) {
		GlStateManager.pushMatrix();
		GlStateManager.translate(x + 0.5D, y + 0.5D, z + 0.5D);
		GlStateManager.scale(scale, scale, scale);
		renderItem.renderItem(item, ItemCameraTransforms.TransformType.FIXED);
		GlStateManager.popMatrix();
	}

}
 public class TankRenderer extends TileEntitySpecialRenderer<Tank> {

	@Override
	public void renderTileEntityAt(Tank te, double x, double y, double z, float partialTicks, int destroyStage) {
		FluidStack fluid = te.tank.fluid;
		if (fluid != null) FluidRenderer.instance.render(fluid, te, x, y, z, 0.875D, (double)fluid.amount / (double)te.tank.cap);
	}

} public class FluidPipe extends Pipe<FluidPipe, FluidStack, IFluidHandler> {

	public static int CAP;
	public static int TICKS;

	protected final IFluidHandler access = new LinkedTank(CAP, this::getFluid, this::setFluid);
	protected final IFluidHandler accessF = new Tank(CAP, this::getFluid, this::setFluid);

	public FluidPipe() {}
	public FluidPipe(IBlockState state) {super(state);}

	@Override
	protected Class<FluidPipe> pipeClass() {return FluidPipe.class;}
	@Override
	protected Capability<IFluidHandler> capability() {return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY;}
	@Override
	protected int resetTimer() {return TICKS;}
	@Override
	protected IFluidHandler getInv(boolean filtered) {return filtered ? accessF : access;}

	private FluidStack getFluid() {
		return content;
	}

	private void setFluid(FluidStack fluid) {
		content = fluid;
		markDirty();
	}

	@Override
	protected byte conDir(TileEntity te, EnumFacing side) {
		return te instanceof IFluidPipeCon ? ((IFluidPipeCon)te).getFluidConnectDir(side) : 0;
	}

	@Override
	protected boolean transferOut(IFluidHandler acc) {
		if (filter == null || filter.noEffect()) {
			if ((content.amount -= acc.fill(content, true)) > 0) return false;
		} else {
			int m = filter.insertAmount(content, acc);
			if (m <= 0 || (content.amount -= acc.fill(new FluidStack(content, m), true)) > 0) return false;
		}
		content = null;
		markDirty();
		return true;
	}

	@Override
	protected boolean transferIn(IFluidHandler acc) {
		if (filter == null || filter.noEffect()) {
			if (content == null) {
				if((content = acc.drain(CAP, true)) != null)
					markDirty();
			}
			else {
				int m = CAP - content.amount;
				if (m <= 0) return true;
				FluidStack fluid = acc.drain(new FluidStack(content, m), true);
				if (fluid != null) {
					content.amount += fluid.amount;
					markDirty();
				}
			}
		} else {
			int n;
			if (content == null) n = 0;
			else if ((n = content.amount) >= CAP) return true;
			FluidStack fluid = filter.getExtract(content, acc);
			if (fluid == null || fluid.amount <= 0) return false;
			int m = CAP - n;
			if (m < fluid.amount) fluid.amount = m;
			fluid.amount = acc.drain(fluid, true).amount + n;
			content = fluid;
			markDirty();
		}
		return false;
	}

	@Override
	public boolean onActivated(EntityPlayer player, EnumHand hand, ItemStack item, EnumFacing dir, float X, float Y, float Z) {
		if (super.onActivated(player, hand, item, dir, X, Y, Z)) return true;
		if (filter != null && !player.isSneaking() && item.getCount() == 0) {
			player.setHeldItem(hand, filter.getItemStack());
			filter = null;
			flow |= 0x8000;
			markUpdate();
			markDirty();
			return true;
		} else if (filter == null && type != 0 && item.getItem() instanceof FluidFilterProvider
				&& (filter = ((FluidFilterProvider)item.getItem()).getFluidFilter(item)) != null) {
			flow &= 0x7fff;
			item.grow(-1);
			player.setHeldItem(hand, item);
			markUpdate();
			markDirty();
			return true;
		} else return false;
	}

	@Override
	public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
		if (filter != null) nbt.setTag("filter", filter.writeNBT());
		if (content != null) nbt.setTag("fluid", content.writeToNBT(new NBTTagCompound()));
		return super.writeToNBT(nbt);
	}

	@Override
	public void readFromNBT(NBTTagCompound nbt) {
		super.readFromNBT(nbt);
		if (nbt.hasKey("filter")) filter = FluidFilterProvider.load(nbt.getCompoundTag("filter"));
		else filter = null;
		if (nbt.hasKey("fluid")) content = FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag("fluid"));
		else content = null;
	}

	@Override
	protected boolean onDataPacket(NBTTagCompound nbt) {
		if (nbt.hasKey("fl", 10)) content = FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag("fl"));
		else content = null;
		byte f = nbt.getByte("filt");
		if (f == -1 ^ filter != null) return false;
		if (f == -1) filter = null;
		else filter = new DummyFilter<FluidStack, IFluidHandler>(f);
		return true;
	}

	@Override
	protected void getUpdatePacket(NBTTagCompound nbt) {
		nbt.setByte("filt", (byte) (filter == null ? -1 : filter.blocking() ? 2 : 0));
		if (last != null) nbt.setTag("fl", last.writeToNBT(new NBTTagCompound()));
	}

	@Override
	public List<ItemStack> dropItem(IBlockState state, int fortune) {
		List<ItemStack> list = super.dropItem(state, fortune);
		if (filter != null) list.add(filter.getItemStack());
		return list;
	}

	@Override
	public boolean shouldRenderInPass(int pass) {
		return pass == 1 && !cover.opaque;
	}

	private class Tank extends LinkedTank {

		public Tank(int cap, Supplier<FluidStack> get, Consumer<FluidStack> set) {
			super(cap, get, set);
		}

		@Override
		public int fill(FluidStack res, boolean doFill) {
			if ((type & 1) == 0 && (filter == null || filter.matches(res)))
				return super.fill(res, doFill);
			return 0;
		}

		@Override
		public FluidStack drain(FluidStack res, boolean doDrain) {
			if (content != null && (type & 1) != 0 && (filter == null || filter.matches(content)))
				return super.drain(res, doDrain);
			return null;
		}

		@Override
		public FluidStack drain(int m, boolean doDrain) {
			if (content != null && (type & 1) != 0 && (filter == null || filter.matches(content)))
				return super.drain(m, doDrain);
			return null;
		}

	}

} public class ItemPipe extends Pipe<ItemPipe, ItemStack, IItemHandler> {

	public static int TICKS;

	protected final IItemHandler access = new LinkedInventory(1, 64, this::getItem, this::setItem);
	protected final IItemHandler accessF = new Inventory();

	public ItemPipe() {}
	public ItemPipe(IBlockState state) {super(state);}

	@Override
	protected Class<ItemPipe> pipeClass() {return ItemPipe.class;}
	@Override
	protected Capability<IItemHandler> capability() {return CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;}
	@Override
	protected int resetTimer() {return TICKS;}
	@Override
	protected IItemHandler getInv(boolean filtered) {return filtered ? accessF : access;}

	private ItemStack getItem(int i) {
		return content == null ? ItemStack.EMPTY : content;
	}

	private void setItem(ItemStack item, int i) {
		content = item.isEmpty() ? null : item;
		markDirty();
	}

	@Override
	protected byte conDir(TileEntity te, EnumFacing side) {
		return te instanceof IItemPipeCon ? ((IItemPipeCon)te).getItemConnectDir(side) : 0;
	}

	@Override
	protected boolean transferOut(IItemHandler acc) {
		if (filter == null || filter.noEffect()) {
			if ((content = ItemHandlerHelper.insertItemStacked(acc, content, false)).getCount() > 0) return false;
		} else {
			int m = filter.insertAmount(content, acc);
			if (m <= 0) return false;
			content.grow(ItemHandlerHelper.insertItemStacked(acc, content.splitStack(m), false).getCount());
			if (content.getCount() > 0) return false;
		}
		content = null;
		return true;
	}

	@Override
	protected boolean transferIn(IItemHandler acc) {
		if (filter == null || filter.noEffect()) {
			if (content == null) setItem(ItemFluidUtil.drain(acc, -1), 0);
			else {
				int m = content.getMaxStackSize() - content.getCount();
				if (m <= 0) return true;
				content.grow(ItemFluidUtil.drain(acc, content, m));
				markDirty();
			}
		} else {
			int n;
			if (content == null) n = 0;
			else if ((n = content.getCount()) >= content.getMaxStackSize()) return true;
			ItemStack extr = filter.getExtract(getItem(0), acc);
			if (extr.getCount() <= 0) return false;
			int m = Math.min(extr.getMaxStackSize() - n, extr.getCount());
			extr.setCount(ItemFluidUtil.drain(acc, extr, m) + n);
			content = extr;
			markDirty();
		}
		return false;
	}

	@Override
	public boolean onActivated(EntityPlayer player, EnumHand hand, ItemStack item, EnumFacing dir, float X, float Y, float Z) {
		if (super.onActivated(player, hand, item, dir, X, Y, Z)) return true;
		if (filter != null && !player.isSneaking() && item.getCount() == 0) {
			player.setHeldItem(hand, filter.getItemStack());
			filter = null;
			flow |= 0x8000;
			markUpdate();
			markDirty();
			return true;
		} else if (filter == null && type != 0 && item.getItem() instanceof ItemFilterProvider
				&& (filter = ((ItemFilterProvider)item.getItem()).getItemFilter(item)) != null) {
			flow &= 0x7fff;
			item.grow(-1);
			player.setHeldItem(hand, item);
			markUpdate();
			markDirty();
			return true;
		} else return false;
	}

	@Override
	public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
		if (filter != null) nbt.setTag("filter", filter.writeNBT());
		if (content != null) nbt.setTag("item", content.writeToNBT(new NBTTagCompound()));
		return super.writeToNBT(nbt);
	}

	@Override
	public void readFromNBT(NBTTagCompound nbt) {
		super.readFromNBT(nbt);
		if (nbt.hasKey("filter")) filter = ItemFilterProvider.load(nbt.getCompoundTag("filter"));
		else filter = null;
		if (nbt.hasKey("item")) content = new ItemStack(nbt.getCompoundTag("item"));
		else content = null;
	}

	@Override
	protected boolean onDataPacket(NBTTagCompound nbt) {
		if (nbt.hasKey("it", 10)) content = new ItemStack(nbt.getCompoundTag("it"));
		else content = null;
		byte f = nbt.getByte("filt");
		if (f == -1 ^ filter != null) return false;
		if (f == -1) filter = null;
		else filter = new DummyFilter<ItemStack, IItemHandler>(f);
		return true;
	}

	@Override
	protected void getUpdatePacket(NBTTagCompound nbt) {
		nbt.setByte("filt", (byte) (filter == null ? -1 : filter.blocking() ? 2 : 0));
		if (last != null) nbt.setTag("it", last.writeToNBT(new NBTTagCompound()));
	}

	@Override
	public List<ItemStack> dropItem(IBlockState state, int fortune) {
		List<ItemStack> list = super.dropItem(state, fortune);
		if (content != null) list.add(content);
		if (filter != null) list.add(filter.getItemStack());
		return list;
	}

	@Override
	public boolean shouldRenderInPass(int pass) {
		return pass == 0 && !cover.opaque;
	}

	private class Inventory extends AbstractInventory {

		@Override
		public int insertAm(int slot, ItemStack item) {
			if ((type & 1) == 0 && (filter == null || filter.matches(item)))
				return Math.min(64, item.getMaxStackSize());
			return 0;
		}

		@Override
		public ItemStack extractItem(int slot, int amount, boolean simulate) {
			if (content != null && (type & 1) != 0 && (filter == null || filter.matches(content)))
				return super.extractItem(slot, amount, simulate);
			return ItemStack.EMPTY;
		}

		@Override
		public void setStackInSlot(int slot, ItemStack stack) {
			setItem(stack, slot);
		}

		@Override
		public int getSlots() {
			return 1;
		}

		@Override
		public ItemStack getStackInSlot(int slot) {
			return getItem(slot);
		}

	}

} public class Tank extends BaseTileEntity implements INeighborAwareTile, ITilePlaceHarvest, ITickable, IGuiData, ClientPacketReceiver, IComparatorSource {

	public static final int[] CAP = {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

	public AdvancedTank tank = new AdvancedTank(this, CAP[0], false);
	private IItemHandler inventory = new Inventory();
	private TileEntity target;
	private byte type;
	public boolean auto, checkTarget = true;
	private int lastAmount;
	private int comparator;

	public Tank() {
		super();
	}

	public Tank(IBlockState state) {
		super(state);
		type = (byte)state.getBlock().getMetaFromState(state);
		tank.cap = CAP[type];
	}

	@Override
	public void update() {
		if (world.isRemote || (world.getTotalWorldTime() & 7) != 0) return;
		if (checkTarget || target != null && target.isInvalid()) {
			checkTarget = false;
			target = world.getTileEntity(pos.down());
			if (target != null && !target.hasCapability(FLUID_HANDLER_CAPABILITY, EnumFacing.UP)) target = null;
		}
		if (target != null && tank.amount() > 0) {
			IFluidHandler acc = target.getCapability(FLUID_HANDLER_CAPABILITY, EnumFacing.UP);
			if (acc != null) tank.decrement(acc.fill(tank.fluid, true));
		}
		int n = lastAmount - tank.amount();
		if (n != 0 && (lastAmount == 0 || tank.fluid == null || Math.abs(n) > tank.cap / 64)) {
			lastAmount -= n;
			markUpdate();
		}
	}

	@Override
	public boolean hasCapability(Capability<?> cap, EnumFacing facing) {
		return cap == FLUID_HANDLER_CAPABILITY || cap == ITEM_HANDLER_CAPABILITY;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T getCapability(Capability<T> cap, EnumFacing facing) {
		if (cap == FLUID_HANDLER_CAPABILITY) return (T)tank;
		if (cap == ITEM_HANDLER_CAPABILITY) return (T)(auto ? inventory : tank);
		return null;
	}

	@Override
	public void readFromNBT(NBTTagCompound nbt) {
		type = (byte)(nbt.getByte("type") & 0xf);
		tank.cap = CAP[type];
		tank.output = nbt.getBoolean("fill");
		tank.readNBT(nbt.getCompoundTag("tank"));
		auto = nbt.getBoolean("auto");
		
		//backward compatibility
		if (nbt.hasKey("fluid", Constants.NBT.TAG_COMPOUND)) tank.fluid = FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag("fluid"));
		if (nbt.hasKey("item", Constants.NBT.TAG_COMPOUND)) tank.setStackInSlot(0, new ItemStack(nbt.getCompoundTag("item")));
		tank.lock |= nbt.getBoolean("lock");
		
		//init
		lastAmount = tank.amount();
		checkTarget = true;
		super.readFromNBT(nbt);
	}

	@Override
	public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
		nbt.setTag("tank", tank.writeNBT(new NBTTagCompound()));
		nbt.setBoolean("fill", tank.output);
		nbt.setBoolean("auto", auto);
		nbt.setByte("type", type);
		return super.writeToNBT(nbt);
	}

	@Override
	public SPacketUpdateTileEntity getUpdatePacket() {
		NBTTagCompound nbt = new NBTTagCompound();
		if (tank.fluid != null) tank.fluid.writeToNBT(nbt);
		return new SPacketUpdateTileEntity(pos, -1, nbt);
	}

	@Override
	public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) {
		tank.fluid = FluidStack.loadFluidStackFromNBT(pkt.getNbtCompound());
	}

	@Override
	public void onPlaced(EntityLivingBase entity, ItemStack item) {
		if (item.hasTagCompound()) {
			tank.fluid = FluidStack.loadFluidStackFromNBT(item.getTagCompound());
			lastAmount = tank.amount();
		}
		tank.lock = tank.fluid != null && tank.fluid.amount == 0;
	}

	@Override
	public List<ItemStack> dropItem(IBlockState state, int fortune) {
		List<ItemStack> list = makeDefaultDrops(tank.fluid == null ? null : tank.fluid.writeToNBT(new NBTTagCompound()));
		tank.addToList(list);
		return list;
	}

	@Override
	public void neighborBlockChange(Block b, BlockPos src) {
		if (src.equals(pos.down())) checkTarget = true;
	}

	@Override
	public void neighborTileChange(TileEntity te, EnumFacing side) {
		if (side == EnumFacing.DOWN) {
			target = te;
			checkTarget = false;
		}
	}

	@Override
	public void initContainer(DataContainer container) {
		TileContainer cont = (TileContainer)container;
		cont.addTankSlot(new TankSlot(tank, 0, 184, 16, (byte)0x23));
		cont.addItemSlot(new GlitchSaveSlot(tank, 0, 184, 74));
		cont.addPlayerInventory(8, 16);
	}

	@Override
	public int[] getSyncVariables() {
		return new int[] {(tank.lock?1:0) | (tank.output?2:0) | (auto?4:0)};
	}

	@Override
	public void setSyncVariable(int i, int v) {
		tank.lock = (v & 1) != 0;
		tank.output = (v & 2) != 0;
		auto = (v & 4) != 0;
	}

	@Override
	public boolean detectAndSendChanges(DataContainer container, PacketBuffer dos) {
		return false;
	}

	@Override
	public void updateClientChanges(DataContainer container, PacketBuffer dis) {
	}

	@Override
	public void onPacketFromClient(PacketBuffer data, EntityPlayer sender) throws IOException {
		switch(data.readByte()) {
		case 0: if (tank.fluid != null) tank.decrement(tank.fluid.amount); break;
		case 1: tank.setLock(!tank.lock); break;
		case 2: tank.setOut(!tank.output); break;
		case 3:	auto = !auto; break;
		}
		markDirty();
	}

	@Override
	public boolean shouldRenderInPass(int pass) {
		return pass == 1;
	}

	private class Inventory extends AbstractInventory {

		@Override
		public void setStackInSlot(int slot, ItemStack stack) {
			tank.setStackInSlot(slot, stack);
		}

		@Override
		public int getSlots() {
			return tank.getSlots();
		}

		@Override
		public ItemStack getStackInSlot(int slot) {
			return tank.getStackInSlot(slot);
		}

		@Override
		public ItemStack extractItem(int slot, int amount, boolean simulate) {
			if (tank.transposing()) return ItemStack.EMPTY;
			return super.extractItem(slot, amount, simulate);
		}

		@Override
		public int getSlotLimit(int slot) {
			return 1;
		}

	}

	@Override
	public void markDirty() {
		if (!world.isRemote && comparator >= 0) {
			comparator = -1;
			TickRegistry.instance.updates.add(()-> world.updateComparatorOutputLevel(pos, blockType));
		}
		super.markDirty();
	}

	@Override
	public int comparatorValue() {
		return comparator < 0 ? comparator = tank.getComparatorValue() : comparator;
	}

}
 @EventBusSubscriber(modid = Main.ID)
@ObjectHolder(value = Main.ID)
public class Objects {

	public static TabMaterials tabIndLog = new TabMaterials(Main.ID);

	@CapabilityInject(WarpPipeNode.class)
	public static Capability<WarpPipeNode> WARP_PIPE_CAP;

	//Blocks
	public static final BlockCoveredPipe ITEM_PIPE = null;
	public static final BlockCoveredPipe FLUID_PIPE = null;
	public static final BlockCoveredPipe WARP_PIPE = null;
	public static final VariantBlock TANK = null;
	public static final VariantBlock BUFFER = null;
	public static final AdvancedBlock AUTO_CRAFT = null;
	public static final BlockCoveredPipe INV_CONNECTOR = null;
	public static final OrientedBlock TRASH = null;
	public static final OrientedBlock FLUID_INTAKE = null;
	public static final OrientedBlock FLUID_OUTLET = null;
	public static final OrientedBlock DROP_INTERFACE = null;
	public static final OrientedBlock ENTITY_INTERFACE = null;
	public static final OrientedBlock BLOCK_PLACER = null;

	//ItemBlocks
	public static final ItemItemPipe item_pipe = null;
	public static final ItemFluidPipe fluid_pipe = null;
	public static final BaseItemBlock warp_pipe = null;
	public static final ItemTank tank = null;
	public static final ItemBuffer buffer = null;
	public static final BaseItemBlock auto_craft = null;
	public static final BaseItemBlock inv_connector = null;
	public static final BaseItemBlock trash = null;
	public static final BaseItemBlock fluid_intake = null;
	public static final BaseItemBlock fluid_outlet = null;
	public static final BaseItemBlock drop_interface = null;
	public static final BaseItemBlock entity_interface = null;
	public static final BaseItemBlock block_placer = null;

	//Items
	public static final ItemItemFilter item_filter = null;
	public static final ItemFluidFilter fluid_filter = null;
	public static final ItemAmountFilter amount_filter = null;
	public static final ItemItemPropertyFilter property_filter = null;
	public static final ItemNameFilter name_filter = null;
	public static final ItemPortableCrafter portable_craft = null;
	public static final ItemRemoteInv remote_inv = null;

	static void init() {
		CapabilityManager.INSTANCE.register(WarpPipeNode.class, new EmptyStorage<WarpPipeNode>(), new EmptyCallable<WarpPipeNode>());
		tabIndLog.item = new ItemStack(ITEM_PIPE);
	}

	@SubscribeEvent
	public static void registerBlocks(RegistryEvent.Register<Block> ev) {
		TooltipUtil.CURRENT_DOMAIN = Main.ID;
		ev.getRegistry().registerAll(
			BlockCoveredPipe.create("item_pipe", Material.WOOD, SoundType.WOOD, ItemPipe.class, 5).setSize(0.25).setCreativeTab(tabIndLog).setLightOpacity(0).setHardness(0.5F),
			BlockCoveredPipe.create("fluid_pipe", Material.GLASS, SoundType.GLASS, FluidPipe.class, 5).setSize(0.25).setCreativeTab(tabIndLog).setLightOpacity(0).setHardness(0.5F),
			BlockCoveredPipe.create("warp_pipe", Material.IRON, SoundType.METAL, WarpPipe.class, 1).setSize(0.25).setCreativeTab(tabIndLog).setLightOpacity(0).setHardness(1.0F).setResistance(20F),
			VariantBlock.create("tank", Material.GLASS, SoundType.GLASS, 2, 16, Tank.class).setCreativeTab(tabIndLog).setLightOpacity(0),
			VariantBlock.create("buffer", Material.WOOD, SoundType.WOOD, 0, 16, Buffer.class).setCreativeTab(tabIndLog),
			new AdvancedBlock("auto_craft", Material.IRON, SoundType.ANVIL, 0, AutoCrafter.class).setCreativeTab(tabIndLog),
			BlockCoveredPipe.create("inv_connector", Material.GLASS, SoundType.GLASS, InvConnector.class, 1).setSize(0.375).setLightOpacity(0).setCreativeTab(tabIndLog).setHardness(0.5F),
			OrientedBlock.create("trash", Material.ROCK, SoundType.STONE, 0, OverflowTrash.class, PropertyOrientation.ALL_AXIS).setCreativeTab(tabIndLog),
			OrientedBlock.create("fluid_intake", Material.IRON, SoundType.METAL, 0, FluidIntake.class, PropertyOrientation.ALL_AXIS).setCreativeTab(tabIndLog),
			OrientedBlock.create("fluid_outlet", Material.IRON, SoundType.METAL, 0, FluidOutlet.class, PropertyOrientation.ALL_AXIS).setCreativeTab(tabIndLog),
			OrientedBlock.create("drop_interface", Material.WOOD, SoundType.WOOD, 0, DropedItemInterface.class, PropertyOrientation.ALL_AXIS).setCreativeTab(tabIndLog),
			OrientedBlock.create("entity_interface", Material.WOOD, SoundType.WOOD, 0, EntityInterface.class, PropertyOrientation.ALL_AXIS).setCreativeTab(tabIndLog),
			OrientedBlock.create("block_placer", Material.IRON, SoundType.METAL, 0, BlockPlacer.class, PropertyOrientation.ALL_AXIS).setCreativeTab(tabIndLog)
		);
	}

	@SubscribeEvent
	public static void registerItems(RegistryEvent.Register<Item> ev) {
		TooltipUtil.CURRENT_DOMAIN = Main.ID;
		ev.getRegistry().registerAll(
			new ItemItemPipe(ITEM_PIPE),
			new ItemFluidPipe(FLUID_PIPE),
			new BaseItemBlock(WARP_PIPE),
			new ItemTank(TANK),
			new ItemBuffer(BUFFER),
			new BaseItemBlock(AUTO_CRAFT),
			new BaseItemBlock(INV_CONNECTOR),
			new BaseItemBlock(TRASH),
			new BaseItemBlock(FLUID_INTAKE),
			new BaseItemBlock(FLUID_OUTLET),
			new BaseItemBlock(DROP_INTERFACE),
			new BaseItemBlock(ENTITY_INTERFACE),
			new BaseItemBlock(BLOCK_PLACER),
			new ItemFluidFilter("fluid_filter").setCreativeTab(tabIndLog),
			new ItemItemFilter("item_filter").setCreativeTab(tabIndLog),
			new ItemAmountFilter("amount_filter").setCreativeTab(tabIndLog),
			new ItemItemPropertyFilter("property_filter").setCreativeTab(tabIndLog),
			new ItemNameFilter("name_filter").setCreativeTab(tabIndLog),
			new ItemPortableCrafter("portable_craft").setCreativeTab(tabIndLog),
			new ItemRemoteInv("remote_inv").setCreativeTab(tabIndLog)
		);
	}

}

import cd4017be.lib.BlockGuiHandler;
import cd4017be.lib.BlockItemRegistry;
import cd4017be.lib.ClientInputHandler;
import cd4017be.lib.render.SpecialModelLoader;
import cd4017be.lib.render.model.MultipartModel;
import net.minecraft.util.BlockRenderLayer;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import cd4017be.indlog.render.gui.GuiAutoCrafter;
import cd4017be.indlog.render.gui.GuiDropInterface;
import cd4017be.indlog.render.gui.GuiFluidIO;
import cd4017be.indlog.render.gui.GuiItemBuffer;
import cd4017be.indlog.render.gui.GuiTank;
import cd4017be.indlog.render.tesr.FluidPipeRenderer;
import cd4017be.indlog.render.tesr.FluidRenderer;
import cd4017be.indlog.render.tesr.ItemPipeRenderer;
import cd4017be.indlog.render.tesr.TankRenderer;
import cd4017be.indlog.tileentity.FluidPipe;
import cd4017be.indlog.tileentity.ItemPipe;
import cd4017be.indlog.tileentity.Tank;
import static cd4017be.indlog.Objects.*;

package cd4017be.indlog;



public class ClientProxy extends CommonProxy {

	@Override
	public void init() {
		super.init();
		ClientInputHandler.init();
		SpecialModelLoader.setMod(Main.ID);
		SpecialModelLoader.registerBlockModel(ITEM_PIPE, new MultipartModel(ITEM_PIPE).setPipeVariants(7));
		SpecialModelLoader.registerBlockModel(FLUID_PIPE, new MultipartModel(FLUID_PIPE).setPipeVariants(7));
		SpecialModelLoader.registerBlockModel(WARP_PIPE, new MultipartModel(WARP_PIPE).setPipeVariants(31));
		
		Objects.ITEM_PIPE.setBlockLayer(BlockRenderLayer.TRANSLUCENT);
		Objects.FLUID_PIPE.setBlockLayer(BlockRenderLayer.TRANSLUCENT);
		Objects.WARP_PIPE.setBlockLayer(BlockRenderLayer.TRANSLUCENT);
		Objects.TANK.setBlockLayer(BlockRenderLayer.CUTOUT);
		INV_CONNECTOR.setBlockLayer(BlockRenderLayer.CUTOUT);
		
		BlockGuiHandler.registerGui(TANK, GuiTank.class);
		BlockGuiHandler.registerGui(BUFFER, GuiItemBuffer.class);
		BlockGuiHandler.registerGui(AUTO_CRAFT, GuiAutoCrafter.class);
		BlockGuiHandler.registerGui(FLUID_INTAKE, GuiFluidIO.class);
		BlockGuiHandler.registerGui(FLUID_OUTLET, GuiFluidIO.class);
		BlockGuiHandler.registerGui(DROP_INTERFACE, GuiDropInterface.class);
		SpecialModelLoader.registerBlockModel(INV_CONNECTOR, new MultipartModel(INV_CONNECTOR).setPipeVariants(3));
	}

	@Override
	public void registerRenderers() {
		super.registerRenderers();
		BlockItemRegistry.registerRenderBS(ITEM_PIPE, 0, 4);
		BlockItemRegistry.registerRenderBS(FLUID_PIPE, 0, 4);
		BlockItemRegistry.registerRender(WARP_PIPE);
		BlockItemRegistry.registerRenderBS(TANK, 0, 15);
		BlockItemRegistry.registerRenderBS(BUFFER, 0, 15);
		BlockItemRegistry.registerRender(AUTO_CRAFT);
		BlockItemRegistry.registerRender(INV_CONNECTOR);
		BlockItemRegistry.registerRender(TRASH);
		BlockItemRegistry.registerRender(FLUID_INTAKE);
		BlockItemRegistry.registerRender(FLUID_OUTLET);
		BlockItemRegistry.registerRender(DROP_INTERFACE);
		BlockItemRegistry.registerRender(ENTITY_INTERFACE);
		BlockItemRegistry.registerRender(BLOCK_PLACER);
		
		BlockItemRegistry.registerRender(fluid_filter);
		BlockItemRegistry.registerRender(item_filter);
		BlockItemRegistry.registerRender(amount_filter);
		BlockItemRegistry.registerRender(property_filter);
		BlockItemRegistry.registerRender(name_filter);
		BlockItemRegistry.registerRender(portable_craft);
		BlockItemRegistry.registerRender(remote_inv);
		
		ClientRegistry.bindTileEntitySpecialRenderer(ItemPipe.class, new ItemPipeRenderer());
		ClientRegistry.bindTileEntitySpecialRenderer(FluidPipe.class, new FluidPipeRenderer());
		ClientRegistry.bindTileEntitySpecialRenderer(Tank.class, new TankRenderer());

		SpecialModelLoader.instance.tesrs.add(FluidRenderer.instance);
