    public static String getDatabaseUrl(DatabaseConfiguration dbConfig) {
        return "jdbc:" + dbConfig.getDatabaseType().toLowerCase() + ":" + dbConfig.getDatabaseName();
    }


    protected AbstractRequestContextBuilder(boolean server, RpcRequest rpcReq, URI uri) {
        this.server = server;
        req = null;
        this.rpcReq = requireNonNull(rpcReq, "rpcReq");
        method = HttpMethod.POST;

        requireNonNull(uri, "uri");
        authority = firstNonNull(uri.getRawAuthority(), FALLBACK_AUTHORITY);
        sessionProtocol = getSessionProtocol(uri);

        if (server) {
            reqTarget = DefaultRequestTarget.createWithoutValidation(
                    RequestTargetForm.ORIGIN, null, null,
                    uri.getRawPath(), uri.getRawQuery(), null);
        } else {
            reqTarget = DefaultRequestTarget.createWithoutValidation(
                    RequestTargetForm.ORIGIN, null, null,
                    uri.getRawPath(), uri.getRawQuery(), uri.getRawFragment());
        }
    }


    static RequestTarget forServer(String reqTarget) {
        requireNonNull(reqTarget, "reqTarget");
        return DefaultRequestTarget.forServer(reqTarget, Flags.allowDoubleDotsInQueryString());
    }


    private static RequestTarget slowAbsoluteFormForClient(String reqTarget, int authorityPos) {
        // Extract scheme and authority while looking for path.
        final URI schemeAndAuthority;
        final String scheme = reqTarget.substring(0, authorityPos - 3);
        final int nextPos = findNextComponent(reqTarget, authorityPos);
        final String authority;
        if (nextPos < 0) {
            // Found no other components after authority
            authority = reqTarget.substring(authorityPos);
        } else {
            // Path, query or fragment exists.
            authority = reqTarget.substring(authorityPos, nextPos);
        }

        // Reject a URI with an empty authority.
        if (authority.isEmpty()) {
            return null;
        }

        // Normalize scheme and authority.
        schemeAndAuthority = normalizeSchemeAndAuthority(scheme, authority);
        if (schemeAndAuthority == null) {
            // Invalid scheme or authority.
            return null;
        }

        if (nextPos < 0) {
            return new DefaultRequestTarget(RequestTargetForm.ABSOLUTE,
                                            schemeAndAuthority.getScheme(),
                                            schemeAndAuthority.getRawAuthority(),
                                            "/",
                                            null,
                                            null);
        }

        return slowForClient(reqTarget, schemeAndAuthority, nextPos);
    }


    public RequestTarget withPath(String path) {
        if (this.path == path) {
            return this;
        }

        return new DefaultRequestTarget(form, scheme, authority, path, query, fragment);
    }


    public static RequestTarget forServer(String reqTarget, boolean allowDoubleDotsInQueryString) {
        final RequestTarget cached = RequestTargetCache.getForServer(reqTarget);
        if (cached != null) {
            return cached;
        }

        return slowForServer(reqTarget, allowDoubleDotsInQueryString);
    }


    private static RequestTarget slowForServer(String reqTarget, boolean allowDoubleDotsInQueryString) {
        final Bytes path;
        final Bytes query;

        // Split by the first '?'.
        final int queryPos = reqTarget.indexOf('?');
        if (queryPos >= 0) {
            if ((path = decodePercentsAndEncodeToUtf8(
                    reqTarget, 0, queryPos,
                    ComponentType.SERVER_PATH, null)) == null) {
                return null;
            }
            if ((query = decodePercentsAndEncodeToUtf8(
                    reqTarget, queryPos + 1, reqTarget.length(),
                    ComponentType.QUERY, EMPTY_BYTES)) == null) {
                return null;
            }
        } else {
            if ((path = decodePercentsAndEncodeToUtf8(
                    reqTarget, 0, reqTarget.length(),
                    ComponentType.SERVER_PATH, null)) == null) {
                return null;
            }
            query = null;
        }

        // Reject a relative path and accept an asterisk (e.g. OPTIONS * HTTP/1.1).
        if (isRelativePath(path)) {
            if (query == null && path.length == 1 && path.data[0] == '*') {
                return INSTANCE_ASTERISK;
            } else {
                // Do not accept a relative path.
                return null;
            }
        }

        // Reject the prohibited patterns.
        if (pathContainsDoubleDots(path)) {
            return null;
        }
        if (!allowDoubleDotsInQueryString && queryContainsDoubleDots(query)) {
            return null;
        }

        return new DefaultRequestTarget(RequestTargetForm.ORIGIN,
                                        null,
                                        null,
                                        encodePathToPercents(path),
                                        encodeQueryToPercents(query),
                                        null);
    }


    public static RequestTarget createWithoutValidation(
            RequestTargetForm form, @Nullable String scheme, @Nullable String authority,
            String path, @Nullable String query, @Nullable String fragment) {
        return new DefaultRequestTarget(form, scheme, authority, path, query, fragment);
    }


    private DefaultRequestTarget(RequestTargetForm form, @Nullable String scheme, @Nullable String authority,
                                 String path, @Nullable String query, @Nullable String fragment) {

        assert (scheme != null && authority != null) ||
               (scheme == null && authority == null) : "scheme: " + scheme + ", authority: " + authority;

        this.form = form;
        this.scheme = scheme;
        this.authority = authority;
        this.path = path;
        this.query = query;
        this.fragment = fragment;
    }


    private static RequestTarget forServer(String rawPath, boolean allowDoubleDotsInQueryString) {
        final RequestTarget res = DefaultRequestTarget.forServer(rawPath, allowDoubleDotsInQueryString);
        if (res != null) {
            logger.info("forServer({}) => path: {}, query: {}", rawPath, res.path(), res.query());
        } else {
            logger.info("forServer({}) => null", rawPath);
        }
        return res;
    }


    void shouldNormalizeSemicolon(Mode mode) {
        assertAccepted(parse(mode, "/;?a=b;c=d"), "/;", "a=b;c=d");
        // '%3B' in a query string should never be decoded into ';'.
        assertAccepted(parse(mode, "/%3b?a=b%3Bc=d"), "/;", "a=b%3Bc=d");
    }


    void shouldReserveQuestionMark() {
        // '%3F' must not be decoded into '?'.
        assertAccepted(forServer("/abc%3F.json?a=%3F"), "/abc%3F.json", "a=%3F");
        assertAccepted(forClient("/abc%3F.json?a=%3F"), "/abc%3F.json", "a=%3F");
    }


new TypeReference<Map<String, Object>>() {};


    private static void fillRequest(
            ServiceRequestContext ctx, AggregatedHttpRequest aReq, Request jReq) {
        DispatcherTypeUtil.setRequestType(jReq);
        jReq.setAsyncSupported(true, null);
        jReq.setSecure(ctx.sessionProtocol().isTls());
        jReq.setMetaData(toRequestMetadata(ctx, aReq));
        final HttpHeaders trailers = aReq.trailers();
        if (!trailers.isEmpty()) {
            final HttpField[] httpFields = trailers.stream()
                                                   .map(e -> new HttpField(e.getKey().toString(), e.getValue()))
                                                   .toArray(HttpField[]::new);
            jReq.setTrailerHttpFields(HttpFields.from(httpFields));
        }
    }


    private static MetaData.Request toRequestMetadata(ServiceRequestContext ctx, AggregatedHttpRequest aReq) {
        // Construct the HttpURI
        final StringBuilder uriBuf = new StringBuilder();
        final RequestHeaders aHeaders = aReq.headers();

        uriBuf.append(ctx.sessionProtocol().isTls() ? "https" : "http");
        uriBuf.append("://");
        uriBuf.append(aHeaders.authority());
        uriBuf.append(aHeaders.path());

        final HttpURI uri = HttpURI.build(HttpURI.build(uriBuf.toString()).path(ctx.mappedPath()))
                                   .asImmutable();
        final HttpField[] fields = aHeaders.stream().map(header -> {
            final AsciiString k = header.getKey();
            final String v = header.getValue();
            if (k.charAt(0) != ':') {
                return new HttpField(k.toString(), v);
            }
            if (HttpHeaderNames.AUTHORITY.equals(k) && !aHeaders.contains(HttpHeaderNames.HOST)) {
                // Convert `:authority` to `host`.
                return new HttpField(HttpHeaderNames.HOST.toString(), v);
            }
            return null;
        }).filter(Objects::nonNull).toArray(HttpField[]::new);
        final HttpFields jHeaders = HttpFields.from(fields);

        return new MetaData.Request(aHeaders.method().name(), uri,
                                    ctx.sessionProtocol().isMultiplex() ? HttpVersion.HTTP_2
                                                                        : HttpVersion.HTTP_1_1,
                                    jHeaders, aReq.content().length());
    }


    private static URI uri(HttpRequest req) {
        final String scheme = req.scheme();
        final String authority = req.authority();
        // Server side Armeria HTTP request always has the scheme and authority.
        assert scheme != null;
        assert authority != null;
        return URI.create(scheme + "://" + authority + req.path());
    }


    ArmeriaServerHttpRequest(ServiceRequestContext ctx,
                             HttpRequest req,
                             DataBufferFactoryWrapper<?> factoryWrapper) {
        super(uri(req), null, springHeaders(req.headers()));
        this.ctx = requireNonNull(ctx, "ctx");
        this.req = req;

        body = Flux.from(req).cast(HttpData.class).map(factoryWrapper::toDataBuffer)
                   // Guarantee that the context is accessible from a controller method
                   // when a user specify @RequestBody in order to convert a request body into an object.
                   .publishOn(Schedulers.fromExecutor(ctx.eventLoop()));
    }


	public void initGui() {
		super.initGui();

		controlList.add(new SmallGuiButton(0, (width-xSize) / 2 + 164, (height - ySize) / 2 + 50, 10,10, ">"));
		controlList.add(new SmallGuiButton(1, (width-xSize) / 2 + 129, (height - ySize) / 2 + 50, 10,10, "<"));
		//controlList.add(new SmallGuiButton(2, (width-xSize) / 2 + 138, (height - ySize) / 2 + 75, 30,10, "Paint"));
		controlList.add(new SmallGuiButton(3, (width-xSize) / 2 + 47, (height - ySize) / 2 + 50, 37,10, "Import"));
		controlList.add(new SmallGuiButton(4, (width-xSize) / 2 + 15, (height - ySize) / 2 + 50, 28,10, "Open"));
		for(int i = 0; i < 6; i++) {
			controlList.add(buttonarray[i] = new SmallGuiButton(5 + i, (width-xSize) / 2 + 20 + 18 * i, (height - ySize) / 2 + 37, 10,10, ">"));
			buttonarray[i].drawButton = false;
		}
		controlList.add(new SmallGuiButton(20, (width-xSize) / 2 + 164, (height - ySize) / 2 + 85, 10,10, ">"));
		controlList.add(new SmallGuiButton(21, (width-xSize) / 2 + 129, (height - ySize) / 2 + 85, 10,10, "<"));
	}

The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid.


	protected void actionPerformed(GuiButton guibutton) {
		if(5 <= guibutton.id && guibutton.id < 11) {
			_logic.handleStackMove(guibutton.id - 5);
		}
		switch(guibutton.id){
		case 0:
			_logic.setNextSatellite(_player);
			return;
		case 1:
			_logic.setPrevSatellite(_player);
			return;
		case 2:
			_logic.paintPathToSatellite();
			return;
		case 3:
			_logic.importFromCraftingTable(_player);
			return;
		case 4:
			_logic.openAttachedGui(_player);
			return;
		case 20:
			_logic.priorityUp(_player);
			return;
		case 21:
			_logic.priorityDown(_player);
			return;
		default:
			super.actionPerformed(guibutton);
			return;
		}
	}


	public GuiCraftingPipe(EntityPlayer player, IInventory dummyInventory, BaseLogicCrafting logic) {
		super(null);
		_player = player;
		DummyContainer dummy = new DummyContainer(player.inventory, dummyInventory);
		dummy.addNormalSlotsForPlayerInventory(18, 97);

		//Input slots
        for(int l = 0; l < 9; l++) {
        	dummy.addDummySlot(l, 18 + l * 18, 18);
        }

        //Output slot
        dummy.addDummySlot(9, 90, 64);

        this.inventorySlots = dummy;
		_logic = logic;
		xSize = 195;
		ySize = 187;
		buttonarray = new GuiButton[6];
	}


	protected void drawGuiContainerBackgroundLayer(float f, int x, int y) {
		int i = mc.renderEngine.getTexture("/logisticspipes/gui/crafting.png");
		GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
		mc.renderEngine.bindTexture(i);
		int j = guiLeft;
		int k = guiTop;

		drawTexturedModalRect(j, k, 0, 0, xSize, ySize);

		drawRect(400, 400, 0, 0, 0x404040);

		for(int count=36; count<42;count++) {
			Slot slot = inventorySlots.getSlot(count);
			if(slot != null && slot.getStack() != null && slot.getStack().getMaxStackSize() < 2) {
				drawRect(guiLeft + 18 + (18 * (count-36)), guiTop + 18, guiLeft + 18 + (18 * (count-36)) + 16, guiTop + 18 + 16, 0xFFFF0000);
				buttonarray[count - 36].drawButton = true;
			} else {
				buttonarray[count - 36].drawButton = false;
			}
		}
	}


	protected void drawGuiContainerForegroundLayer(int par1, int par2) {
		fontRenderer.drawString("Inputs", 18, 7, 0x404040);
		fontRenderer.drawString("Output", 48, 67, 0x404040);
		fontRenderer.drawString("Inventory", 18, 86, 0x404040);
		fontRenderer.drawString("Satellite", 132, 7, 0x404040);

		if (_logic.satelliteId == 0){
			fontRenderer.drawString("Off", 144, 52, 0x404040);
		} else {
			fontRenderer.drawString(""+_logic.satelliteId , 155 - fontRenderer.getStringWidth(""+_logic.satelliteId) , 52, 0x404040);
			/*
			if (_logic.isSatelliteConnected()){
				MinecraftForgeClient.bindTexture(mod_LogisticsPipes.LOGISTICSPIPE_ROUTED_TEXTURE_FILE);
			}else{
				MinecraftForgeClient.bindTexture(mod_LogisticsPipes.LOGISTICSPIPE_NOTROUTED_TEXTURE_FILE);
			}*/
			//TODO /\ /\ ???

			//GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
			//drawRect(0,1000,0,10000, 0xFFFF0000);
			//drawTexturedModalRect(155, 50, 10 * (xSize / 16) , 0, 10, 10);
			//MinecraftForgeClient.unbindTexture();
		}
		fontRenderer.drawString("Priority:" , 132 , 75, 0x404040);
		fontRenderer.drawString(""+_logic.priority , 152 - (fontRenderer.getStringWidth(""+_logic.priority) / 2) , 87, 0x404040);
	}


	public void setNextSatellite(EntityPlayer player) {
		if (MainProxy.isClient(player.worldObj)) {
			final PacketCoordinates packet = new PacketCoordinates(NetworkConstants.CRAFTING_PIPE_NEXT_SATELLITE, xCoord, yCoord, zCoord);
			MainProxy.sendPacketToServer(packet.getPacket());
		} else {
			satelliteId = getNextConnectSatelliteId(false);
			final PacketPipeInteger packet = new PacketPipeInteger(NetworkConstants.CRAFTING_PIPE_SATELLITE_ID, xCoord, yCoord, zCoord, satelliteId);
			MainProxy.sendPacketToPlayer(packet.getPacket(), (Player)player);
		}

	}


	protected int getNextConnectSatelliteId(boolean prev) {
		final List<ExitRoute> routes = getRoutedPipe().getRouter().getIRoutersByCost();
		int closestIdFound = prev ? 0 : Integer.MAX_VALUE;
		for (final BaseLogicSatellite satellite : BaseLogicSatellite.AllSatellites) {
			CoreRoutedPipe satPipe = satellite.getRoutedPipe();
			if(satPipe == null || satPipe.stillNeedReplace() || satPipe.getRouter() == null)
				continue;
			IRouter satRouter = satPipe.getRouter();
			for (ExitRoute route:routes){
				if (route.destination == satRouter) {
					if (!prev && satellite.satelliteId > satelliteId && satellite.satelliteId < closestIdFound) {
						closestIdFound = satellite.satelliteId;
					} else if (prev && satellite.satelliteId < satelliteId && satellite.satelliteId > closestIdFound) {
						closestIdFound = satellite.satelliteId;
					}
				}
			}
		}
		if (closestIdFound == Integer.MAX_VALUE) {
			return satelliteId;
		}

		return closestIdFound;

	}


	public void setPrevSatellite(EntityPlayer player) {
		if (MainProxy.isClient(player.worldObj)) {
			final PacketCoordinates packet = new PacketCoordinates(NetworkConstants.CRAFTING_PIPE_PREV_SATELLITE, xCoord, yCoord, zCoord);
			MainProxy.sendPacketToServer(packet.getPacket());
		} else {
			satelliteId = getNextConnectSatelliteId(true);
			final PacketPipeInteger packet = new PacketPipeInteger(NetworkConstants.CRAFTING_PIPE_SATELLITE_ID, xCoord, yCoord, zCoord, satelliteId);
			MainProxy.sendPacketToPlayer(packet.getPacket(), (Player)player);
		}
	}


	public void setSatelliteId(int satelliteId) {
		this.satelliteId = satelliteId;
	}


	public boolean isSatelliteConnected() {
		final List<ExitRoute> routes = getRoutedPipe().getRouter().getIRoutersByCost();
		for (final BaseLogicSatellite satellite : BaseLogicSatellite.AllSatellites) {
			if (satellite.satelliteId == satelliteId) {
				CoreRoutedPipe satPipe = satellite.getRoutedPipe();
				if(satPipe == null || satPipe.stillNeedReplace() || satPipe.getRouter() == null)
					continue;
				IRouter satRouter = satPipe.getRouter();
				for (ExitRoute route:routes) {
					if (route.destination == satRouter) {
						return true;
					}
				}
			}
		}
		return false;
	}


	public IRouter getSatelliteRouter() {
		for (final BaseLogicSatellite satellite : BaseLogicSatellite.AllSatellites) {
			if (satellite.satelliteId == satelliteId) {
				CoreRoutedPipe satPipe = satellite.getRoutedPipe();
				if(satPipe == null || satPipe.stillNeedReplace() || satPipe.getRouter() == null)
					continue;
				return satPipe.getRouter();
			}
		}
		return null;
	}


	public void paintPathToSatellite() {
		final IRouter satelliteRouter = getSatelliteRouter();
		if (satelliteRouter == null) {
			return;
		}

		getRoutedPipe().getRouter().displayRouteTo(satelliteRouter.getSimpleID());
	}


	public Pair<Integer, Integer> getBestReply(LiquidStack stack, IRouter sourceRouter, List<Integer> jamList) {
		for (ExitRoute candidateRouter : sourceRouter.getIRoutersByCost()){
			if(!candidateRouter.containsFlag(PipeRoutingConnectionType.canRouteTo)) continue;
			if(candidateRouter.destination.getSimpleID() == sourceRouter.getSimpleID()) continue;
			if(jamList.contains(candidateRouter.destination.getSimpleID())) continue;

			if (candidateRouter.destination.getPipe() == null || !candidateRouter.destination.getPipe().isEnabled()) continue;
			CoreRoutedPipe pipe = candidateRouter.destination.getPipe();

			if(!(pipe instanceof ILiquidSink)) continue;

			int amount = ((ILiquidSink)pipe).sinkAmount(stack);
			if(amount > 0) {
				Pair<Integer, Integer> result = new Pair<Integer, Integer>(candidateRouter.destination.getSimpleID(), amount);
				return result;
			}
		}
		Pair<Integer, Integer> result = new Pair<Integer, Integer>(null, 0);
		return result;
	}


	private static void onCraftingPipeSetSatellite(PacketPipeInteger packet) {
		final TileGenericPipe pipe = getPipe(FMLClientHandler.instance().getClient().theWorld, packet.posX, packet.posY, packet.posZ);
		if (pipe == null) {
			return;
		}

		if (!(pipe.pipe.logic instanceof BaseLogicCrafting)) {
			return;
		}

		((BaseLogicCrafting) pipe.pipe.logic).setSatelliteId(packet.integer);
	}


	public CraftingTemplate addCrafting() {

		if (!isEnabled()){
			return null;
		}

		BaseLogicCrafting craftingLogic = (BaseLogicCrafting) this.logic;
		ItemStack stack = craftingLogic.getCraftedItem();
		if ( stack == null) return null;

		boolean hasSatellite = craftingLogic.isSatelliteConnected();
		if(craftingLogic.satelliteId != 0 && !hasSatellite) return null;

		CraftingTemplate template = new CraftingTemplate(ItemIdentifierStack.GetFromStack(stack), this, craftingLogic.priority);

		//Check all materials
		for (int i = 0; i < 9; i++){
			ItemStack resourceStack = craftingLogic.getMaterials(i);
			if (resourceStack == null || resourceStack.stackSize == 0) continue;
			if (i < 6 || !hasSatellite) {
				template.addRequirement(ItemIdentifierStack.GetFromStack(resourceStack), this);
			} else {
				template.addRequirement(ItemIdentifierStack.GetFromStack(resourceStack), (IRequestItems)craftingLogic.getSatelliteRouter().getPipe());
			}

		}
		return template;
	}


	public boolean tryIserting(World world, EntityPlayer entityplayer) {
		if(entityplayer.getCurrentEquippedItem() != null && entityplayer.getCurrentEquippedItem().itemID == LogisticsPipes.UpgradeItem.itemID) {
			if(MainProxy.isClient(world)) return true;
			for(int i=0;i<inv.getSizeInventory() - 1;i++) {
				ItemStack item = inv.getStackInSlot(i);
				if(item == null) {
					inv.setInventorySlotContents(i, entityplayer.getCurrentEquippedItem().splitStack(1));
					InventoryChanged(inv);
					return true;
				} else if(item.getItemDamage() == entityplayer.getCurrentEquippedItem().getItemDamage()) {
					if(item.stackSize < inv.getInventoryStackLimit()) {
						item.stackSize++;
						entityplayer.getCurrentEquippedItem().splitStack(1);
						return true;
					}
				}
			}
		}
		if(entityplayer.getCurrentEquippedItem() != null && entityplayer.getCurrentEquippedItem().itemID == LogisticsPipes.LogisticsItemCard.itemID && entityplayer.getCurrentEquippedItem().getItemDamage() == LogisticsItemCard.SEC_CARD) {
			if(MainProxy.isClient(world)) return true;
			if(inv.getStackInSlot(8) == null) {
				inv.setInventorySlotContents(8, entityplayer.getCurrentEquippedItem().copy());
				inv.getStackInSlot(8).stackSize = 1;
				entityplayer.getCurrentEquippedItem().splitStack(1);
				return true;
			}
		}
		return false;
	}


	public DummyContainer getDummyContainer(EntityPlayer player) {
		DummyContainer dummy = new DummyContainer(player.inventory, inv);
		dummy.addNormalSlotsForPlayerInventory(8, 60);

		//Pipe slots
	    for(int pipeSlot = 0; pipeSlot < 8; pipeSlot++){
	    	dummy.addRestrictedSlot(pipeSlot, inv, 8 + pipeSlot * 18, 18, LogisticsPipes.UpgradeItem.itemID);
	    }
    	dummy.addRestrictedSlot(8, inv, 8 + 8 * 18, 18, new ISlotCheck() {
			@Override
			public boolean isStackAllowed(ItemStack itemStack) {
				if(itemStack == null) return false;
				if(itemStack.itemID != LogisticsPipes.LogisticsItemCard.itemID) return false;
				if(itemStack.getItemDamage() != LogisticsItemCard.SEC_CARD) return false;
				return true;
			}
    	});
	    return dummy;
	}


	public void writeToLPData(LPDataOutput output) throws IOException {
		output.writeLongArray(amountRecorded);
		output.writeInt(arrayPos);
		output.writeItemIdentifier(item);
	}


	public void readFromLPData(LPDataInput input) throws IOException {
		amountRecorded = input.readLongArray();
		arrayPos = input.readInt();
		item = input.readItemIdentifier();
	}


	public void setUseNewRenderer(boolean flag) {
		useNewRenderer = flag;
	}


	public void readData(LPDataInput input) throws IOException {
		useNewRenderer = input.readBoolean();
		useFallbackRenderer = input.readBoolean();
		renderPipeDistance = input.readInt();
		renderPipeContentDistance = input.readInt();
		isUninitialised = false;
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeBoolean(useNewRenderer);
		output.writeBoolean(useFallbackRenderer);
		output.writeInt(renderPipeDistance);
		output.writeInt(renderPipeContentDistance);
	}


	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		super.handlerAdded(ctx);
		ctx.attr(PacketHandler.INBOUNDPACKETTRACKER).set(new ThreadLocal<>());
	}


	private static void onPacketData(ModernPacket packet, final EntityPlayer player) {
		try {
			packet.processPacket(player);
			if (LPConstants.DEBUG) {
				PacketHandler.debugMap.remove(packet.getDebugId());
			}
		} catch (DelayPacketException e) {
			if (packet.retry() && MainProxy.isClient(player.getEntityWorld())) {
				SimpleServiceLocator.clientBufferHandler.queueFailedPacket(packet, player);
			} else if (LPConstants.DEBUG) {
				LogisticsPipes.log.error(packet.getClass().getName());
				LogisticsPipes.log.error(packet.toString());
				e.printStackTrace();
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}


	public static void onPacketData(final LPDataInput data, final EntityPlayer player) throws IOException {
		if (player == null) {
			return;
		}
		final int packetID = data.readShort();
		final ModernPacket packet = PacketHandler.packetlist.get(packetID).template();
		packet.setDebugId(data.readInt());
		packet.readData(data);
		PacketHandler.onPacketData(packet, player);
	}


		InboundModernPacketWrapper(ModernPacket p, EntityPlayer e) {
			packet = p;
			player = e;
		}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(flag);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		flag = input.readBoolean();
	}


	public BooleanModuleCoordinatesGuiProvider(int id) {
		super(id);
	}


	public void writeData(LPDataOutput output) throws IOException {

		output.writeInt(posX);
		output.writeInt(posY);
		output.writeInt(posZ);
	}


	public void readData(LPDataInput input) throws IOException {

		posX = input.readInt();
		posY = input.readInt();
		posZ = input.readInt();

	}


	public CoordinatesGuiProvider(int id) {
		super(id);
	}


	public CoordinatesPopupGuiProvider(int id) {
		super(id);
	}


public void writeData(LPDataOutput output) throws IOException {}


public void readData(LPDataInput input) throws IOException {}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		slot = input.readEnum(ModulePositionType.class);
		positionInt = input.readInt();
	}


	public ModuleCoordinatesGuiProvider(int id) {
		super(id);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeEnum(slot);
		output.writeInt(positionInt);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(invSlot);
	}


	public ModuleInHandGuiProvider(int id) {
		super(id);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		invSlot = input.readInt();
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		nbt = input.readNBTTagCompound();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeNBTTagCompound(nbt);
	}


	public UpgradeCoordinatesGuiProvider(int id) {
		super(id);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		positionInt = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(positionInt);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBitSet(getFlags());
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		setFlags(input.readBitSet());
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(flag);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		flag = input.readBoolean();
	}


	public void readData(LPDataInput input) throws IOException {

		posX = input.readInt();
		posY = input.readInt();
		posZ = input.readInt();

	}


	public CoordinatesPacket(int id) {
		super(id);
	}


	public void writeData(LPDataOutput output) throws IOException {

		output.writeInt(posX);
		output.writeInt(posY);
		output.writeInt(posZ);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		direction = input.readForgeDirection();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeForgeDirection(direction);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		setInteger2(input.readInt());
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(getInteger2());
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(getInteger());
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		setInteger(input.readInt());
	}


	public void readData(LPDataInput input) throws IOException {
		setInteger(input.readInt());
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(getInteger());
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);

		if (inventory != null) {
			output.writeByte(STACK_MARKER);
			output.writeInt(inventory.getSizeInventory());
			for (int i = 0; i < inventory.getSizeInventory(); i++) {
				output.writeItemStack(inventory.getStackInSlot(i));
			}
		} else if (stackList != null) {
			output.writeByte(STACK_MARKER);
			output.writeCollection(stackList, LPDataOutput::writeItemStack);
		} else if (identList != null) {
			output.writeByte(IDENT_MARKER);
			output.writeCollection(identList, LPDataOutput::writeItemIdentifierStack);
		} else if (identSet != null) {
			output.writeByte(IDENT_MARKER);
			output.writeCollection(identSet, LPDataOutput::writeItemIdentifierStack);
		} else {
			throw new IllegalStateException("Wont send packet without content");
		}
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);

		byte marker = input.readByte();
		if (marker == STACK_MARKER) {
			stackList = input.readLinkedList(LPDataInput::readItemStack);
		} else if (marker == IDENT_MARKER) {
			identList = input.readLinkedList(LPDataInput::readItemIdentifierStack);
		} else {
			throw new UnsupportedOperationException("Unknown marker: " + marker);
		}
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);

		final int itemID = input.readInt();
		if (itemID != 0) {
			int stackSize = input.readInt();
			int damage = input.readInt();
			setStack(new ItemStack(Item.getItemById(itemID), stackSize, damage));
			getStack().setTagCompound(input.readNBTTagCompound());
		} else {
			setStack(null);
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		if (getStack() != null) {
			output.writeInt(Item.getIdFromItem(getStack().getItem()));
			output.writeInt(getStack().stackSize);
			output.writeInt(getStack().getItemDamage());
			output.writeNBTTagCompound(getStack().getTagCompound());
		} else {
			output.writeInt(0);
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeCollection(list, this);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		list = input.readArrayList(this);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(type != null);
		if (type != null) {
			output.writeEnum(type);
			output.writeInt(positionInt);
		}
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		if (input.readBoolean()) {
			type = input.readEnum(ModulePositionType.class);
			positionInt = input.readInt();
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeNBTTagCompound(tag);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		tag = input.readNBTTagCompound();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeItemIdentifierStack(stack);
		output.writeInt(dimension);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		stack = input.readItemIdentifierStack();
		dimension = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeUTF(getString());
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		setString(input.readUTF());
	}


	public void readData(LPDataInput input) throws IOException {
		int size = input.readInt();
		for (int i = 0; i < size; i++) {
			getStringList().add(input.readUTF());
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(getStringList().size());
		for (int i = 0; i < getStringList().size(); i++) {
			output.writeUTF(getStringList().get(i));
		}
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		targetType = input.readItemIdentifier();
		if (input.readBoolean()) {
			for (int i = 0; i < 9; i++) {
				ignore_dmg[i] = input.readBoolean();
				ignore_nbt[i] = input.readBoolean();
				use_od[i] = input.readBoolean();
				use_category[i] = input.readBoolean();
			}
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeItemIdentifier(targetType);
		output.writeBoolean(isFuzzy);
		if (isFuzzy) {
			for (int i = 0; i < 9; i++) {
				output.writeBoolean(ignore_dmg[i]);
				output.writeBoolean(ignore_nbt[i]);
				output.writeBoolean(use_od[i]);
				output.writeBoolean(use_category[i]);
			}
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeCollection(trackingList, (output1, object) -> object.writeToLPData(output1));

	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		trackingList = input.readArrayList(data1 -> {
			TrackingTask object = new TrackingTask();
			object.readFromLPData(data1);
			return object;
		});
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		dir = input.readForgeDirection();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeForgeDirection(dir);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeIntArray(amount);
		output.writeBoolean(cleanupExclude);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		amount = input.readIntArray();
		cleanupExclude = input.readBoolean();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(patternUpgarde);
		output.writeIntArray(slotArray);
		output.writeBoolean(isLimit);
		output.writeInt(mode);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		patternUpgarde = input.readBoolean();
		slotArray = input.readIntArray();
		isLimit = input.readBoolean();
		mode = input.readInt();
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		areItemsIncluded = input.readBoolean();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(areItemsIncluded);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(extractorMode);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		extractorMode = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(timeOut);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		timeOut = input.readInt();
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		isAdvancedSat = input.readBoolean();
		liquidCrafter = input.readInt();
		amount = input.readIntArray();
		hasByproductExtractor = input.readBoolean();
		isFuzzy = input.readBoolean();
		cleanupSize = input.readInt();
		cleanupExclude = input.readBoolean();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(isAdvancedSat);
		output.writeInt(liquidCrafter);
		output.writeIntArray(amount);
		output.writeBoolean(hasByproductExtractor);
		output.writeBoolean(isFuzzy);
		output.writeInt(cleanupSize);
		output.writeBoolean(cleanupExclude);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeForgeDirection(sneakyOrientation);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		sneakyOrientation = input.readForgeDirection();
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		isDefaultRoute = input.readBoolean();
		hasFuzzyUpgrade = input.readBoolean();
		ignoreData = input.readBitSet();
		ignoreNBT = input.readBitSet();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(isDefaultRoute);
		output.writeBoolean(hasFuzzyUpgrade);
		output.writeBitSet(ignoreData);
		output.writeBitSet(ignoreNBT);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		exclude = input.readBoolean();
		extractorMode = input.readInt();
		isActive = input.readBoolean();
		sneakyOrientation = input.readForgeDirection();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(exclude);
		output.writeInt(extractorMode);
		output.writeBoolean(isActive);
		output.writeForgeDirection(sneakyOrientation);
	}


public void writeData(LPDataOutput output) throws IOException {}


public void readData(LPDataInput input) throws IOException {}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeByteArray(content);
	}


	public void readData(LPDataInput input) throws IOException {
		content = input.readByteArray();
	}


	public void readData(LPDataInput input) throws IOException {
		enumSet = EnumSet.noneOf(CraftingDependency.class);
		for (CraftingDependency type : CraftingDependency.values()) {
			if (input.readBoolean()) {
				enumSet.add(type);
			}
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		for (CraftingDependency type : CraftingDependency.values()) {
			output.writeBoolean(enumSet.contains(type));
		}
	}


	public void readData(LPDataInput input) throws IOException {
		throw new RuntimeException("This packet should never be used");
	}


	public void writeData(LPDataOutput output) throws IOException {
		throw new RuntimeException("This packet should never be used");
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);

		output.writeInt(content.length);

		for (int i = 0; i < content.length; i++) {
			final ItemStack itemstack = content[i];

			if (itemstack != null) {
				output.writeByte(i);
				output.writeInt(Item.getIdFromItem(itemstack.getItem()));
				output.writeInt(itemstack.stackSize);
				output.writeInt(itemstack.getItemDamage());
				output.writeNBTTagCompound(itemstack.getTagCompound());
			}
		}
		output.writeByte(-1); // mark packet end
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);

		content = new ItemStack[input.readInt()];

		byte index = input.readByte();

		while (index != -1) { // read until the end
			final int itemID = input.readInt();
			int stackSize = input.readInt();
			int damage = input.readInt();
			ItemStack stack = new ItemStack(Item.getItemById(itemID), stackSize, damage);
			stack.setTagCompound(input.readNBTTagCompound());
			content[index] = stack;
			index = input.readByte(); // read the next slot
		}
	}


	public void readData(LPDataInput input) throws IOException {
		LogisticsPipes.getClientPlayerConfig().readData(input);
	}


	public void writeData(LPDataOutput output) throws IOException {
		config.writeData(output);
	}


	public void readData(LPDataInput input) throws IOException {
		config = new PlayerConfig(null);
		config.readData(input);
	}


	public void readData(LPDataInput input) throws IOException {
		ident = input.readItemIdentifierStack().getItem();
		name = input.readUTF();
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeItemIdentifierStack(ident.makeStack(0));
		output.writeUTF(name);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		item = input.readItemIdentifier();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeItemIdentifier(item);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(down);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		down = input.readBoolean();
	}


	public CraftingCycleRecipe(int id) {
		super(id);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		targetType = input.readItemIdentifier();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeItemIdentifier(targetType);
	}


	public CraftingSetType(int id) {
		super(id);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		length = input.readFloat();
		dir = input.readForgeDirection();
		color = input.readInt();
		reverse = input.readBoolean();
		renderBall = input.readBoolean();
		remove = input.readBoolean();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeFloat(length);
		output.writeForgeDirection(dir);
		output.writeInt(color);
		output.writeBoolean(reverse);
		output.writeBoolean(renderBall);
		output.writeBoolean(remove);
	}


	public PowerPacketLaser(int id) {
		super(id);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeDouble(getDouble());
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		setDouble(input.readDouble());
	}


	public void readData(LPDataInput input) throws IOException {
		identList = input.readArrayList(LPDataInput::readItemIdentifierStack);
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeCollection(identList, LPDataOutput::writeItemIdentifierStack);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeLong(uuid.getMostSignificantBits());
		output.writeLong(uuid.getLeastSignificantBits());
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		uuid = new UUID(input.readLong(), input.readLong());
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(buttonID);
		super.writeData(output);
	}


	public void readData(LPDataInput input) throws IOException {
		buttonID = input.readInt();
		super.readData(input);
	}


public void readData(LPDataInput input) throws IOException {}


public void writeData(LPDataOutput output) throws IOException {}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		mode = input.readBoolean();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(mode);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		pipeId = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(pipeId);
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(windowID);
		output.writeUTF(line);
	}


	public void readData(LPDataInput input) throws IOException {
		windowID = input.readInt();
		line = input.readUTF();
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(windowID);
		output.writeUTF(title);
	}


	public void readData(LPDataInput input) throws IOException {
		windowID = input.readInt();
		title = input.readUTF();
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(windowID);
		output.writeCollection(status, new IWriteListObject<StatusEntry>() {

			@Override
			public void writeObject(LPDataOutput output, StatusEntry entry) throws IOException {
				output.writeUTF(entry.name);
				output.writeBoolean(entry.subEntry != null);
				if (entry.subEntry != null) {
					output.writeCollection(entry.subEntry, this);
				}
			}
		});
	}


	public void readData(LPDataInput input) throws IOException {
		windowID = input.readInt();
		status = input.readArrayList(new IReadListObject<StatusEntry>() {

			@Override
			public StatusEntry readObject(LPDataInput input) throws IOException {
				StatusEntry status = new StatusEntry();
				status.name = input.readUTF();
				if (input.readBoolean()) {
					status.subEntry = input.readArrayList(this);
				}
				return status;
			}
		});
	}


	public void processPacket(EntityPlayer player) {
		MovingObjectPosition box = FMLClientHandler.instance().getClient().objectMouseOver;
		if (box == null) {
			MainProxy.sendPacketToServer(PacketHandler.getPacket(DebugTargetResponse.class).setMode(DebugTargetResponse.TargetMode.None));
		} else if (box.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) {
			MainProxy.sendPacketToServer(PacketHandler.getPacket(DebugTargetResponse.class).setMode(DebugTargetResponse.TargetMode.Block)
					.setAdditions(new Object[] { box.blockX, box.blockY, box.blockZ }));
		} else if (box.typeOfHit == MovingObjectPosition.MovingObjectType.ENTITY) {
			MainProxy.sendPacketToServer(PacketHandler.getPacket(DebugTargetResponse.class).setMode(DebugTargetResponse.TargetMode.Entity)
					.setAdditions(new Object[] { box.entityHit.getEntityId() }));
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeByteArray(payload);
		output.writeInt(identifier);
	}


	public void readData(LPDataInput input) throws IOException {
		payload = input.readByteArray();
		identifier = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeUTF(getName());
		output.writeInt(getIdentification());
	}


	public void readData(LPDataInput input) throws IOException {
		setName(input.readUTF());
		setIdentification(input.readInt());
	}


	public DebugTargetResponse(int id) {
		super(id);
	}


	public void readData(LPDataInput input) throws IOException {
		mode = TargetMode.values()[input.readByte()];
		int size = input.readInt();
		additions = new Object[size];
		for (int i = 0; i < size; i++) {
			byte[] bytes = input.readByteArray();
			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
			ObjectInput in = new ObjectInputStream(bis);
			try {
				Object o = in.readObject();
				additions[i] = o;
			} catch (ClassNotFoundException e) {
				throw new UnsupportedOperationException(e);
			}
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeByte(mode.ordinal());
		output.writeInt(additions.length);
		for (Object addition : additions) {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutput out = null;
			out = new ObjectOutputStream(bos);
			out.writeObject(addition);
			output.writeByteArray(bos.toByteArray());
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(slotId);
		output.writeItemIdentifierStack(ItemIdentifierStack.getFromStack(stack));
		output.writeInt(button);
	}


	public void readData(LPDataInput input) throws IOException {
		slotId = input.readInt();
		stack = input.readItemIdentifierStack().makeNormalStack();
		button = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(slotNumber);
		output.writeBitSet(flags);
	}


	public void readData(LPDataInput input) throws IOException {
		slotNumber = input.readInt();
		flags = input.readBitSet();
	}


	public void readData(LPDataInput input) throws IOException {
		guiID = input.readInt();
		windowID = input.readInt();
		guiData = input.readByteArray();
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(guiID);
		output.writeInt(windowID);
		output.writeByteArray(guiData);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		guiID = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(getGuiID());
	}


	public GuiReopenPacket(int id) {
		super(id);
	}


public void writeData(LPDataOutput output) throws IOException {}


public void readData(LPDataInput input) throws IOException {}


	public void readData(LPDataInput input) throws IOException {
		buttonId = input.readInt();
		state = input.readBoolean();
		slot = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(buttonId);
		output.writeBoolean(state);
		output.writeInt(slot);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		mode = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(mode);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(integer2);
		output.writeInt(integer3);
		output.writeInt(integer4);
		if (string1 == null) {
			string1 = "";
		}
		output.writeUTF(string1);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		integer2 = input.readInt();
		integer3 = input.readInt();
		integer4 = input.readInt();
		string1 = input.readUTF();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(isDefault);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		isDefault = input.readBoolean();
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		nbt = input.readNBTTagCompound();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeNBTTagCompound(nbt);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		isLimited = input.readBoolean();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(isLimited);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(timeOut);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		timeOut = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(sinkSize);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		sinkSize = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(pos);
		if (pos != -1) {
			output.writeBoolean(isNBT);
		} else {
			output.writeBitSet(ignoreData);
			output.writeBitSet(ignoreNBT);
		}
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		pos = input.readInt();
		if (pos != -1) {
			isNBT = input.readBoolean();
		} else {
			ignoreData = input.readBitSet();
			ignoreNBT = input.readBitSet();
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(hasPatternUpgrade);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		hasPatternUpgrade = input.readBoolean();
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		targetPos = input.readSet(LPDataInput::readLPPosition);
		subTypes = input.readArrayList(data1 -> data1.readEnum(CoreMultiBlockPipe.SubBlockTypeForShare.class));
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeCollection(targetPos, LPDataOutput::writeLPPosition);
		output.writeCollection(subTypes, LPDataOutput::writeEnum);
	}


	public MultiBlockCoordinatesPacket(int id) {
		super(id);
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeCollection(used, LPDataOutput::writeResource);
		output.writeCollection(missing, LPDataOutput::writeResource);
		// ToDo: ???
		output.writeByte(0);
	}


	public void readData(LPDataInput input) throws IOException {
		used = input.readArrayList(LPDataInput::readResource);
		missing = input.readArrayList(LPDataInput::readResource);
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeCollection(items, LPDataOutput::writeResource);
		output.writeBoolean(isFlag());
	}


	public void readData(LPDataInput input) throws IOException {
		items = input.readArrayList(LPDataInput::readResource);
		setFlag(input.readBoolean());
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeResource(stack);
		output.writeLinkedLogisticsOrderList(orders);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		stack = input.readResource();
		orders = input.readLinkedLogisticsOrderList();
	}


public void readData(LPDataInput input) throws IOException {}


public void writeData(LPDataOutput output) throws IOException {}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeIntArray(amount);
		output.writeIntArray(liquidSatelliteIdArray);
		output.writeInt(liquidSatelliteId);
		output.writeInt(satelliteId);
		output.writeIntArray(advancedSatelliteIdArray);
		output.writeInt(priority);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		amount = input.readIntArray();
		liquidSatelliteIdArray = input.readIntArray();
		liquidSatelliteId = input.readInt();
		satelliteId = input.readInt();
		advancedSatelliteIdArray = input.readIntArray();
		priority = input.readInt();
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		content = input.readArrayList(input1 -> {
			GuiRecipeImport.Canidates can = new GuiRecipeImport.Canidates(new TreeSet<>());
			can.order = input1.readArrayList(LPDataInput::readItemIdentifierStack);
			return can;
		});
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeCollection(content, (data, object) -> data.writeCollection(object.order,
				(IWriteListObject<ItemIdentifierStack>) LPDataOutput::writeItemIdentifierStack));
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		if (input.readBoolean()) {
			stack = input.readItemIdentifierStack();
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		if (stack == null) {
			output.writeBoolean(false);
		} else {
			output.writeBoolean(true);
			output.writeItemIdentifierStack(stack);
		}
	}


	public Triplet<ItemIdentifierStack, Pair<Integer /* Time */, Integer /* BufferCounter */>, LPTravelingItemServer> readObject(LPDataInput input)
			throws IOException {
		return new Triplet<>(input.readItemIdentifierStack(), null, null);
	}


	public void writeObject(LPDataOutput output,
			Triplet<ItemIdentifierStack, Pair<Integer /* Time */, Integer /* BufferCounter */>, LPTravelingItemServer> object) throws IOException {
		output.writeItemIdentifierStack(object.getValue1());
	}


	public void readData(LPDataInput input) throws IOException {
		response = input.readArrayList(LPDataInput::readInt);
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeCollection(response, LPDataOutput::writeInt);
	}


	public ParticleFX(int id) {
		super(id);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(particles.size());
		for (ParticleCount pc : particles) {
			output.writeByte(pc.getParticle().ordinal());
			output.writeInt(pc.getAmount());
		}
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		int nparticles = input.readInt();
		particles = new ArrayList<>(nparticles);
		for (int i = 0; i < nparticles; i++) {
			int particle = input.readByte();
			int amount = input.readInt();
			particles.add(new ParticleCount(Particles.values()[particle], amount));
		}
	}


	public PipeContentPacket(int id) {
		super(id);
	}


	public void readData(LPDataInput input) throws IOException {
		item = input.readItemIdentifierStack();
		travelId = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeItemIdentifierStack(item);
		output.writeInt(travelId);
	}


	public void readData(LPDataInput input) throws IOException {
		isServer = input.readBoolean();
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeBoolean(isServer);
	}


	public PipeFluidUpdate(int id) {
		super(id);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		bits = input.readBitSet();
		for (int i = 0; i < renderCache.length; i++) {
			if (bits.get(i)) {
				renderCache[i] = new FluidStack(FluidRegistry.getFluid(input.readInt()), input.readInt(), input.readNBTTagCompound());
			}
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		for (int i = 0; i < renderCache.length; i++) {
			bits.set(i, renderCache[i] != null);
		}
		output.writeBitSet(bits);
		for (FluidStack aRenderCache : renderCache) {
			if (aRenderCache != null) {
				output.writeInt(FluidRegistry.getFluidID(aRenderCache.getFluid()));
				output.writeInt(aRenderCache.amount);
				output.writeNBTTagCompound(aRenderCache.tag);
			}
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		for (LogisticsOrder order : manager) {
			output.writeByte(1);
			output.writeOrderInfo(order);
		}
		output.writeByte(0);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		clientOrder = new LinkedList<>();
		while (input.readByte() == 1) {
			clientOrder.add(input.readOrderInfo());
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeBoolean(start);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		start = input.readBoolean();
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		travelId = input.readInt();
		speed = input.readFloat();
		position = input.readFloat();
		this.input = input.readForgeDirection();
		output = input.readForgeDirection();
		yaw = input.readFloat();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(travelId);
		output.writeFloat(speed);
		output.writeFloat(position);
		output.writeForgeDirection(input);
		output.writeForgeDirection(this.output);
		output.writeFloat(yaw);
	}


	public PipeSignTypes(int id) {
		super(id);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		types = input.readArrayList(LPDataInput::readInt);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeCollection(types, LPDataOutput::writeInt);
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);

		IClientState[] clientStates = new IClientState[] { renderState, coreState, bcPluggableState, pipe };
		byte[][] clientStateBuffers = new byte[][] { bytesRenderState, bytesCoreState, bytesBCPluggableState, bytesPipe };
		for (int i = 0; i < clientStates.length; i++) {
			clientStateBuffers[i] = LPDataIOWrapper.collectData(clientStates[i]::writeData);
			output.writeByteArray(clientStateBuffers[i]);
		}
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);

		bytesRenderState = input.readByteArray();
		bytesCoreState = input.readByteArray();
		bytesBCPluggableState = input.readByteArray();
		bytesPipe = input.readByteArray();
	}


	public void processPacket(EntityPlayer player) {
		LogisticsTileGenericPipe pipe = this.getPipe(player.getEntityWorld());
		if (pipe == null) {
			return;
		}
		try {
			LPDataIOWrapper.provideData(bytesRenderState, pipe.renderState::readData);
			LPDataIOWrapper.provideData(bytesCoreState, pipe.coreState::readData);
			LPDataIOWrapper.provideData(bytesBCPluggableState, pipe.bcPlugableState::readData);
			pipe.afterStateUpdated();
			LPDataIOWrapper.provideData(bytesPipe, pipe.pipe::readData);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}


	public void readData(LPDataInput input) throws IOException {
		while (input.readBoolean()) {
			lasers.add(new LaserData().readData(input));
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		for (LaserData laser : lasers) {
			output.writeBoolean(true);
			laser.writeData(output);
		}
		output.writeBoolean(false);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		tagetPosX = input.readInt();
		tagetPosY = input.readInt();
		tagetPosZ = input.readInt();
		slot = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(tagetPosX);
		output.writeInt(tagetPosY);
		output.writeInt(tagetPosZ);
		output.writeInt(slot);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		inventorySlot = input.readInt();
		slot = input.readInt();
		pipePosX = input.readInt();
		pipePosY = input.readInt();
		pipePosZ = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(inventorySlot);
		output.writeInt(slot);
		output.writeInt(pipePosX);
		output.writeInt(pipePosY);
		output.writeInt(pipePosZ);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		slot = input.readInt();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeInt(slot);
	}


	public void writeData(LPDataOutput output) throws IOException {
		initData();
		super.writeData(output);
		output.writeInt(stat_session_sent);
		output.writeInt(stat_session_recieved);
		output.writeInt(stat_session_relayed);
		output.writeLong(stat_lifetime_sent);
		output.writeLong(stat_lifetime_recieved);
		output.writeLong(stat_lifetime_relayed);
		output.writeInt(server_routing_table_size);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		stat_session_sent = input.readInt();
		stat_session_recieved = input.readInt();
		stat_session_relayed = input.readInt();
		stat_lifetime_sent = input.readLong();
		stat_lifetime_recieved = input.readLong();
		stat_lifetime_relayed = input.readLong();
		server_routing_table_size = input.readInt();
	}


	public void processPacket(EntityPlayer player) {
		MovingObjectPosition box = FMLClientHandler.instance().getClient().objectMouseOver;
		if (box == null) {
			MainProxy.sendPacketToServer(PacketHandler.getPacket(RoutingUpdateTargetResponse.class).setMode(TargetMode.None));
		} else if (box.typeOfHit == MovingObjectType.BLOCK) {
			MainProxy.sendPacketToServer(PacketHandler.getPacket(RoutingUpdateTargetResponse.class).setMode(TargetMode.Block)
					.setAdditions(new Object[] { box.blockX, box.blockY, box.blockZ }));
		} else if (box.typeOfHit == MovingObjectType.ENTITY) {
			MainProxy.sendPacketToServer(PacketHandler.getPacket(RoutingUpdateTargetResponse.class).setMode(TargetMode.Entity)
					.setAdditions(new Object[] { box.entityHit.getEntityId() }));
		}
	}


public void readData(LPDataInput input) throws IOException {}


public void writeData(LPDataOutput output) throws IOException {}


	public void readData(LPDataInput input) throws IOException {
		exitRoute = input.readExitRoute(MainProxy.getClientMainWorld());
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeExitRoute(exitRoute);
	}


	public void readData(LPDataInput input) throws IOException {
		msg = new ExitRoute[input.readInt()];
		for (int i = 0; i < msg.length; i++) {
			msg[i] = input.readExitRoute(MainProxy.getClientMainWorld());
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(msg.length);
		for (ExitRoute element : msg) {
			output.writeExitRoute(element);
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeEnumSet(set, PipeRoutingConnectionType.class);
		output.writeLPPosition(pos);
	}


	public void readData(LPDataInput input) throws IOException {
		set = input.readEnumSet(PipeRoutingConnectionType.class);
		pos = input.readLPPosition();
	}


	public void readData(LPDataInput input) throws IOException {
		pos = input.readLPPosition();
		filterPositions = new EnumMap<>(PipeRoutingConnectionType.class);
		short id;
		while ((id = input.readShort()) != -1) {
			PipeRoutingConnectionType type = PipeRoutingConnectionType.values[id];
			List<List<DoubleCoordinates>> typeFilters = new ArrayList<>();
			int length;
			while ((length = input.readShort()) != -1) {
				List<DoubleCoordinates> linkedFilter = new ArrayList<>();
				for (int i = 0; i < length; i++) {
					linkedFilter.add(input.readLPPosition());
				}
				typeFilters.add(linkedFilter);
			}
			filterPositions.put(type, typeFilters);
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeLPPosition(pos);
		for (PipeRoutingConnectionType type : filters.keySet()) {
			output.writeShort(type.ordinal());
			for (List<IFilter> linkedFilter : filters.get(type)) {
				output.writeShort(linkedFilter.size());
				for (IFilter filter : linkedFilter) {
					output.writeLPPosition(filter.getLPPosition());
				}
			}
			output.writeShort(-1);
		}
		output.writeShort(-1);
	}


	public RoutingUpdateTargetResponse(int id) {
		super(id);
	}


	public void processPacket(final EntityPlayer player) {
		if (mode == TargetMode.None) {
			player.addChatMessage(new ChatComponentText(ChatColor.RED + "No Target Found"));
		} else if (mode == TargetMode.Block) {
			int x = (Integer) additions[0];
			int y = (Integer) additions[1];
			int z = (Integer) additions[2];
			player.addChatMessage(new ChatComponentText("Checking Block at: x:" + x + " y:" + y + " z:" + z));
			Block id = player.worldObj.getBlock(x, y, z);
			player.addChatMessage(new ChatComponentText("Found Block with Id: " + Block.getIdFromBlock(id)));
			final TileEntity tile = player.worldObj.getTileEntity(x, y, z);
			if (tile == null) {
				player.addChatMessage(new ChatComponentText(ChatColor.RED + "No TileEntity found"));
			} else if (!(tile instanceof LogisticsTileGenericPipe)) {
				player.addChatMessage(new ChatComponentText(ChatColor.RED + "No LogisticsTileGenericPipe found"));
			} else if (!(((LogisticsTileGenericPipe) tile).pipe instanceof CoreRoutedPipe)) {
				player.addChatMessage(new ChatComponentText(ChatColor.RED + "No CoreRoutedPipe found"));
			} else {
				LPChatListener.addTask(() -> {
					player.addChatMessage(new ChatComponentText(ChatColor.GREEN + "Starting RoutingTable debug update."));
					DebugController.instance(player).debug(((ServerRouter) ((CoreRoutedPipe) ((LogisticsTileGenericPipe) tile).pipe).getRouter()));
					MainProxy.sendPacketToPlayer(PacketHandler.getPacket(OpenChatGui.class), player);
					return true;
				}, player);
				player.addChatMessage(new ChatComponentText(
						ChatColor.AQUA + "Start RoutingTable debug update ? " + ChatColor.RESET + "<" + ChatColor.GREEN + "yes" + ChatColor.RESET + "/"
								+ ChatColor.RED + "no" + ChatColor.RESET + ">"));
				MainProxy.sendPacketToPlayer(PacketHandler.getPacket(OpenChatGui.class), player);
			}
		} else if (mode == TargetMode.Entity) {
			player.addChatMessage(new ChatComponentText(ChatColor.RED + "Entity not allowed"));
		}
	}


	public void readData(LPDataInput input) throws IOException {
		mode = TargetMode.values()[input.readByte()];
		int size = input.readInt();
		additions = new Object[size];
		for (int i = 0; i < size; i++) {
			byte[] bytes = input.readByteArray();
			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
			ObjectInput in = new ObjectInputStream(bis);
			try {
				Object o = in.readObject();
				additions[i] = o;
			} catch (ClassNotFoundException e) {
				throw new UnsupportedOperationException(e);
			}
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeByte(mode.ordinal());
		output.writeInt(additions.length);
		for (Object addition : additions) {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutput out = new ObjectOutputStream(bos);
			out.writeObject(addition);
			output.writeByteArray(bos.toByteArray());
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(satID);
		super.writeData(output);
	}


	public void readData(LPDataInput input) throws IOException {
		satID = input.readInt();
		super.readData(input);
	}


	public void readData(LPDataInput input) throws IOException {
		super.readData(input);
		side = input.readForgeDirection();
	}


	public void writeData(LPDataOutput output) throws IOException {
		super.writeData(output);
		output.writeForgeDirection(side);
	}


	public boolean getTurtleConnect() {
		if (container instanceof LogisticsTileGenericPipe) {
			return container.getTurtleConnect();
		}
		return false;
	}


	public void readData(LPDataInput input) throws IOException {
		isOpaqueClientSide = input.readBoolean();
	}


	public void setTurtleConnect(Boolean flag) {
		if (container instanceof LogisticsTileGenericPipe) {
			container.setTurtleConnect(flag);
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeBoolean(isOpaque());
	}


		public void readData(LPDataInput input) throws IOException {
			pipeId = input.readInt();

		}


	public void setTurtleConnect(boolean flag) {
		SimpleServiceLocator.ccProxy.setTurtleConnect(flag, this);
	}


		public void writeData(LPDataOutput output) throws IOException {
			output.writeInt(pipeId);

		}


	public void writeData(LPDataOutput output) throws IOException {
		if (orientation == null) {
			output.writeBoolean(false);
		} else {
			output.writeBoolean(true);
			output.writeEnum(orientation);
		}
	}


		public void setOnPipe(CoreMultiBlockPipe pipe) {
			((HSTubeCurve) pipe).orientation = this;
		}


	public void readData(LPDataInput input) throws IOException {
		if (input.readBoolean()) {
			orientation = input.readEnum(CurveOrientation.class);
		}
	}


		public void rotatePositions(IPositionRotateble set) {
			renderOrientation.rotatePositions(set);
		}


		TubeGainRenderOrientation(ForgeDirection dir) {
			this.dir = dir;
		}


		public void rotatePositions(IPositionRotateble set) {
			renderOrientation.rotateOrientation(set);
		}


		public void setOnPipe(CoreMultiBlockPipe pipe) {
			((HSTubeGain) pipe).orientation = this;
		}


		public void rotateOrientation(IPositionRotateble set) {
			if (this == EAST) {
				set.rotateRight();
			} else if (this == WEST) {
				set.rotateLeft();
			} else if (this == SOUTH) {
				set.rotateLeft();
				set.rotateLeft();
			}
		}


	public void readData(LPDataInput input) throws IOException {
		if (input.readBoolean()) {
			orientation = input.readEnum(TubeGainOrientation.class);
		}
	}


		TubeGainOrientation(TubeGainRenderOrientation render, DoubleCoordinates off, ForgeDirection dir) {
			renderOrientation = render;
			offset = off;
			this.dir = dir;
		}


		public void setOnPipe(CoreMultiBlockPipe pipe) {
			((HSTubeLine) pipe).orientation = this;
		}


	public void readData(LPDataInput input) throws IOException {
		if (input.readBoolean()) {
			orientation = input.readEnum(TubeLineOrientation.class);
		}
	}


		TubeLineRenderOrientation(ForgeDirection dir) {
			this.dir = dir;
		}


		TubeLineOrientation(TubeLineRenderOrientation render, DoubleCoordinates off, ForgeDirection dir) {
			renderOrientation = render;
			offset = off;
			this.dir = dir;
		}


		public void rotateOrientation(IPositionRotateble set) {
			if (this == EAST_WEST) {
				set.rotateLeft();
			}
		}


	public void readData(LPDataInput input) throws IOException {
		if (input.readBoolean()) {
			orientation = input.readEnum(CurveSOrientation.class);
		}
	}


		public void setOnPipe(CoreMultiBlockPipe pipe) {
			((HSTubeSCurve) pipe).orientation = this;
		}


	public void writeData(LPDataOutput output) throws IOException {
		if (orientation == null) {
			output.writeBoolean(false);
		} else {
			output.writeBoolean(true);
			output.writeEnum(orientation);
		}
	}


		public void rotatePositions(IPositionRotateble set) {
			if (this == SOUTH) {
				set.rotateLeft();
				set.rotateLeft();
			} else if (this == EAST) {
				set.rotateRight();
			} else if (this == WEST) {
				set.rotateLeft();
			}
		}


		public ITubeRenderOrientation getRenderOrientation() {
			return this;
		}


		public DoubleCoordinates getOffset() {
			return new DoubleCoordinates(0, 0, 0);
		}


	public void readData(LPDataInput input) throws IOException {
		orientation = input.readEnum(SpeedupDirection.class);
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeEnum(orientation);
	}


		public void setOnPipe(CoreMultiBlockPipe pipe) {
			((HSTubeSpeedup) pipe).orientation = this;
		}


public void readData(LPDataInput input) throws IOException {}


public void writeData(LPDataOutput output) throws IOException {}


	public void readData_LP(LPDataInput input) throws IOException {
		if (input.readBoolean()) {
			ByteBuf buf = input.readByteBuf();
			readData(buf);
		}
	}


	public void writeData_LP(LPDataOutput output) throws IOException {
		output.writeBoolean(true);
		ByteBuf buf = Unpooled.buffer(128);
		writeData(buf);
		output.writeByteBuf(buf);
	}


	public void writeData(LPDataOutput output) throws IOException {
		ByteBuf buf = Unpooled.buffer(128);
		this.writeData(buf);
		output.writeByteBuf(buf);
	}


	public void readData(LPDataInput input) throws IOException {
		ByteBuf buf = input.readByteBuf();
		this.readData(buf);
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeByte(mask);
		output.writeByte(isBCPipeMask);
		output.writeByte(isTDPipeMask);
	}


	public void readData(LPDataInput input) throws IOException {
		byte newMask = input.readByte();

		if (newMask != mask) {
			mask = newMask;
			dirty = true;
		}

		newMask = input.readByte();
		if (newMask != isBCPipeMask) {
			isBCPipeMask = newMask;
			dirty = true;
		}

		newMask = input.readByte();
		if (newMask != isTDPipeMask) {
			isTDPipeMask = newMask;
			dirty = true;
		}
	}


	public void writeData(LPDataOutput output) throws IOException {
		pipeConnectionMatrix.writeData(output);
		textureMatrix.writeData(output);
		bcRenderState.writeData_LP(output); //Always needs to be last. Different length depending on proxy loading state.
	}


	public void readData(LPDataInput input) throws IOException {
		pipeConnectionMatrix.readData(input);
		textureMatrix.readData(input);
		bcRenderState.readData_LP(input); //Always needs to be last. Different length depending on proxy loading state.
	}


	public void readData(LPDataInput input) throws IOException {
		for (int i = 0; i < iconIndexes.length; i++) {
			int icon = input.readByte();
			if (iconIndexes[i] != icon) {
				iconIndexes[i] = icon;
				dirty = true;
			}
		}
		textureIndex = input.readInt();
		isRouted = input.readBoolean();
		isRoutedInDir = input.readBooleanArray();
		isSubPowerInDir = input.readBooleanArray();
		hasPowerUpgrade = input.readBoolean();
		hasPower = input.readBoolean();
		isFluid = input.readBoolean();
		pointedOrientation = input.readForgeDirection();
	}


	public void writeData(LPDataOutput output) throws IOException {
		for (int iconIndexe : iconIndexes) {
			output.writeByte(iconIndexe);
		}
		output.writeInt(textureIndex);
		output.writeBoolean(isRouted);
		output.writeBooleanArray(isRoutedInDir);
		output.writeBooleanArray(isSubPowerInDir);
		output.writeBoolean(hasPowerUpgrade);
		output.writeBoolean(hasPower);
		output.writeBoolean(isFluid);
		output.writeForgeDirection(pointedOrientation);
	}


	public void setCCType(Object type) {
		ccObject = type;
	}


	public DictResource(LPDataInput input) throws IOException {
		stack = input.readItemIdentifierStack();
		requester = null;
		BitSet bits = input.readBitSet();
		use_od = bits.get(0);
		ignore_dmg = bits.get(1);
		ignore_nbt = bits.get(2);
		use_category = bits.get(3);
	}


	public Object getCCType() {
		return ccObject;
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeItemIdentifierStack(stack);
		BitSet bits = new BitSet();
		bits.set(0, use_od);
		bits.set(1, ignore_dmg);
		bits.set(2, ignore_nbt);
		bits.set(3, use_category);
		output.writeBitSet(bits);
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeItemIdentifier(liquid.getItemIdentifier());
		output.writeInt(amount);
	}


	public FluidResource(LPDataInput input) throws IOException {
		liquid = FluidIdentifier.get(input.readItemIdentifier());
		amount = input.readInt();
		target = null;
	}


	public ItemResource(LPDataInput input) throws IOException {
		stack = input.readItemIdentifierStack();
		requester = null;
	}


	public void writeData(LPDataOutput output) throws IOException {
		output.writeItemIdentifierStack(stack);
	}


	public static IResource readResource(LPDataInput input) throws IOException {
		int id = input.readInt();
		if (id == -1) {
			return null;
		}
		return ResourceNetwork.values()[id].readData(input);
	}


	ItemResource(ItemResource.class) {
		@Override
		protected IResource readData(LPDataInput input) throws IOException {
			return new ItemResource(input);
		}
	},


	public static void writeResource(LPDataOutput output, IResource resource) throws IOException {
		if (resource == null) {
			output.writeInt(-1);
			return;
		}
		ResourceNetwork[] values = ResourceNetwork.values();
		for (ResourceNetwork value : values) {
			if (value.clazz.isAssignableFrom(resource.getClass())) {
				output.writeInt(value.ordinal());
				resource.writeData(output);
				return;
			}
		}
		throw new UnsupportedOperationException(resource.getClass().toString());
	}


	FluidResource(FluidResource.class) {
		@Override
		protected IResource readData(LPDataInput input) throws IOException {
			return new FluidResource(input);
		}
	};


	DictResource(DictResource.class) {
		@Override
		protected IResource readData(LPDataInput input) throws IOException {
			return new DictResource(input);
		}
	},


	public void writeData(LPDataOutput output) throws IOException {
		output.writeInt(posX);
		output.writeInt(posY);
		output.writeInt(posZ);
		output.writeByte(dir.ordinal());
		output.writeBoolean(finalPipe);
		output.writeBoolean(startPipe);
		output.writeInt(length);
		for (PipeRoutingConnectionType type : PipeRoutingConnectionType.values()) {
			output.writeBoolean(connectionType.contains(type));
		}
	}


	public LaserData readData(LPDataInput input) throws IOException {
		posX = input.readInt();
		posY = input.readInt();
		posZ = input.readInt();
		dir = ForgeDirection.values()[input.readByte()];
		finalPipe = input.readBoolean();
		startPipe = input.readBoolean();
		length = input.readInt();
		connectionType = EnumSet.noneOf(PipeRoutingConnectionType.class);
		for (PipeRoutingConnectionType type : PipeRoutingConnectionType.values()) {
			if (input.readBoolean()) {
				connectionType.add(type);
			}
		}
		return this;
	}


	private static byte[] compress(byte[] content) {
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		try {
			GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
			gzipOutputStream.write(content);
			gzipOutputStream.close();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return byteArrayOutputStream.toByteArray();
	}


	public void setPause(boolean flag) {
		clientCompressorThread.setPause(flag);
	}


	public void clear() {
		clientCompressorThread.clear();
		clientDecompressorThread.clear();
	}


		private void handlePacketData(final Pair<EntityPlayer, byte[]> playerDataPair) {
			try {
				LPDataIOWrapper.provideData(playerDataPair.getValue2(), input -> {
					PacketHandler.onPacketData(input, playerDataPair.getValue1());
				});
			} catch (IOException e) {
				System.err.println("IO Error in handlePacketData for player " + playerDataPair.getValue1().getCommandSenderName());
				e.printStackTrace();
			}
		}


	private static byte[] decompress(byte[] contentBytes) {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(contentBytes));
			int buffer = 0;
			while ((buffer = gzip.read()) != -1) {
				out.write(buffer);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return out.toByteArray();
	}


		public void run() {
			while (true) {
				boolean flag;
				do {
					flag = false;
					byte[] buffer = null;
					synchronized (queue) {
						if (queue.size() > 0) {
							flag = true;
							buffer = queue.getFirst();
							queue.removeFirst();
						}
					}
					if (flag && buffer != null) {
						byte[] packetbytes = ClientPacketBufferHandlerThread.decompress(buffer);
						byte[] newBuffer = new byte[packetbytes.length + ByteBuffer.length];
						System.arraycopy(ByteBuffer, 0, newBuffer, 0, ByteBuffer.length);
						System.arraycopy(packetbytes, 0, newBuffer, ByteBuffer.length, packetbytes.length);
						ByteBuffer = newBuffer;
					}
				} while (flag);

				while (ByteBuffer.length >= 4) {
					int size = ((ByteBuffer[0] & 255) << 24) + ((ByteBuffer[1] & 255) << 16) + ((ByteBuffer[2] & 255) << 8) + ((ByteBuffer[3] & 255) << 0);
					if (size + 4 > ByteBuffer.length) {
						break;
					}
					byte[] packet = Arrays.copyOfRange(ByteBuffer, 4, size + 4);
					ByteBuffer = Arrays.copyOfRange(ByteBuffer, size + 4, ByteBuffer.length);
					packetBufferLock.lock();
					try {
						PacketBuffer.add(new Pair<>(MainProxy.proxy.getClientPlayer(), packet));
					} finally {
						packetBufferLock.unlock();
					}
				}
				synchronized (queue) {
					while (queue.size() == 0) {
						try {
							queue.wait();
						} catch (InterruptedException e) { }
					}
				}
				if (clear) {
					clear = false;
					ByteBuffer = new byte[] {};
				}
			}
		}


	public void clientTick(ClientTickEvent event) {
		if (event.phase != Phase.END) {
			return;
		}
		clientDecompressorThread.clientTickEnd();
	}


		public void run() {
			while (true) {
				try {
					synchronized (clientList) {
						if (!pause && clientList.size() > 0) {
							clientBuffer = LPDataIOWrapper.collectData(output -> {
								output.writeBytes(clientBuffer);
								clearLock.lock();
								try {
									for (ModernPacket packet : clientList) {
										output.writeShort(packet.getId());
										output.writeInt(packet.getDebugId());
										packet.writeData(output);
									}
								} finally {
									clientList.clear();
									clearLock.unlock();
								}
							});
						}
					}
					//Send Content
					if (clientBuffer.length > 0) {
						while (clientBuffer.length > 1024 * 32) {
							byte[] sendbuffer = Arrays.copyOf(clientBuffer, 1024 * 32);
							clientBuffer = Arrays.copyOfRange(clientBuffer, 1024 * 32, clientBuffer.length);
							byte[] compressed = ClientPacketBufferHandlerThread.compress(sendbuffer);
							MainProxy.sendPacketToServer(PacketHandler.getPacket(BufferTransfer.class).setContent(compressed));
						}
						byte[] sendbuffer = clientBuffer;
						clientBuffer = new byte[] {};
						byte[] compressed = ClientPacketBufferHandlerThread.compress(sendbuffer);
						MainProxy.sendPacketToServer(PacketHandler.getPacket(BufferTransfer.class).setContent(compressed));
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				synchronized (clientList) {
					while (pause || clientList.size() == 0) {
						try {
							clientList.wait();
						} catch (InterruptedException e) {}
					}
				}
				if (clear) {
					clear = false;
					clientBuffer = new byte[] {};
				}
			}
		}


	public void addPacketToCompressor(ModernPacket packet) {
		clientCompressorThread.addPacketToCompressor(packet);
	}


	public void handlePacket(byte[] content) {
		clientDecompressorThread.handlePacket(content);
	}


	public void queueFailedPacket(ModernPacket packet, EntityPlayer player) {
		clientDecompressorThread.queueFailedPacket(packet, player);
	}


	public void handlePacket(byte[] content, EntityPlayer player) {
		serverDecompressorThread.handlePacket(content, player);
	}


	public void clear(final EntityPlayer player) {
		new Thread() {

			@Override
			public void run() {
				serverCompressorThread.clear(player);
				serverDecompressorThread.clear(player);
			}
		}.start();
	}


	public void serverTick(ServerTickEvent event) {
		if (event.phase != Phase.END) {
			return;
		}
		serverDecompressorThread.serverTickEnd();
	}


	public void addPacketToCompressor(ModernPacket packet, EntityPlayer player) {
		serverCompressorThread.addPacketToCompressor(packet, player);
	}


	public void setPause(boolean flag) {
		serverCompressorThread.setPause(flag);
	}


	public ExitRoute readExitRoute(World world) throws IOException {
		IRouter destination = readIRouter(world);
		IRouter root = readIRouter(world);
		ForgeDirection exitOri = readForgeDirection();
		ForgeDirection insertOri = readForgeDirection();
		EnumSet<PipeRoutingConnectionType> connectionDetails = readEnumSet(PipeRoutingConnectionType.class);
		double distanceToDestination = localBuffer.readDouble();
		double destinationDistanceToRoot = localBuffer.readDouble();
		int blockDistance = localBuffer.readInt();
		List<DoubleCoordinates> positions = readArrayList(LPDataInput::readLPPosition);
		ExitRoute e = new ExitRoute(root, destination, exitOri, insertOri, destinationDistanceToRoot, connectionDetails, blockDistance);
		e.distanceToDestination = distanceToDestination;
		e.debug.filterPosition = positions;
		e.debug.toStringNetwork = readUTF();
		e.debug.isNewlyAddedCanidate = localBuffer.readBoolean();
		e.debug.isTraced = localBuffer.readBoolean();
		e.debug.index = localBuffer.readInt();
		return e;
	}


	public void writeExitRoute(ExitRoute route) throws IOException {
		writeIRouter(route.destination);
		writeIRouter(route.root);
		writeForgeDirection(route.exitOrientation);
		writeForgeDirection(route.insertOrientation);
		writeEnumSet(route.connectionDetails, PipeRoutingConnectionType.class);
		writeDouble(route.distanceToDestination);
		writeDouble(route.destinationDistanceToRoot);
		writeInt(route.blockDistance);
		writeCollection(route.filters, (data, filter) -> data.writeLPPosition(filter.getLPPosition()));
		writeUTF(route.toString());
		writeBoolean(route.debug.isNewlyAddedCanidate);
		writeBoolean(route.debug.isTraced);
		writeInt(route.debug.index);
	}


	public String readUTF() throws IOException {
		byte[] arr = readByteArray();
		if (arr == null) {
			return null;
		} else {
			return new String(arr, UTF_8);
		}
	}


	public <T> LinkedList<T> readLinkedList(IReadListObject<T> reader) throws IOException {
		int size = readInt();
		if (size == -1) {
			return null;
		}

		LinkedList<T> list = new LinkedList<>();
		for (int i = 0; i < size; i++) {
			list.add(reader.readObject(this));
		}
		return list;
	}


	public <T> ArrayList<T> readArrayList(IReadListObject<T> reader) throws IOException {
		int size = readInt();
		if (size == -1) {
			return null;
		}

		ArrayList<T> list = new ArrayList<>(size);
		for (int i = 0; i < size; i++) {
			list.add(reader.readObject(this));
		}
		return list;
	}


	public IOrderInfoProvider readOrderInfo() throws IOException {
		ItemIdentifierStack stack = readItemIdentifierStack();
		int routerId = localBuffer.readInt();
		boolean isFinished = localBuffer.readBoolean();
		boolean inProgress = localBuffer.readBoolean();
		IOrderInfoProvider.ResourceType type = readEnum(IOrderInfoProvider.ResourceType.class);
		List<Float> list = readArrayList(LPDataInput::readFloat);
		byte machineProgress = localBuffer.readByte();
		DoubleCoordinates pos = readLPPosition();
		ItemIdentifier ident = readItemIdentifier();
		return new ClientSideOrderInfo(stack, isFinished, type, inProgress, routerId, list, machineProgress, pos, ident);
	}


	public static void writeData(ByteBuf dataBuffer, LPDataOutputConsumer dataOutputConsumer) throws IOException {
		LPDataIOWrapper lpData = getInstance(dataBuffer);

		dataOutputConsumer.accept(lpData);

		lpData.unsetBuffer();
	}


	public ItemIdentifier readItemIdentifier() throws IOException {
		final int itemId = readInt();
		if (itemId == 0) {
			return null;
		}

		int damage = readInt();
		NBTTagCompound tag = readNBTTagCompound();
		return ItemIdentifier.get(Item.getItemById(itemId), damage, tag);
	}


	public void writeUTF(String s) throws IOException {
		if (s == null) {
			writeInt(-1);
		} else {
			writeByteArray(s.getBytes(UTF_8));
		}
	}


	public boolean[] readBooleanArray() throws IOException {
		final int bitCount = localBuffer.readInt();
		if (bitCount == -1) {
			return null;
		}

		byte[] data = readByteArray();
		if (bitCount == 0) {
			return new boolean[0];
		} else if (data == null) {
			throw new NullPointerException("Boolean's byte array is null");
		}

		BitSet bits = BitSet.valueOf(data);

		final boolean[] arr = new boolean[bitCount];
		IntStream.range(0, bitCount).forEach(i -> arr[i] = bits.get(i));
		return arr;
	}


	private void unsetBuffer() {
		if (localBuffer.hasMemoryAddress()) {
			if (--reference < 1) {
				BUFFER_WRAPPER_MAP.remove(localBuffer.memoryAddress());
			}
		}
		localBuffer = null;
	}


	public byte[] readByteArray() throws IOException {
		final int length = readInt();
		if (length == -1) {
			return null;
		}

		return readBytes(length);
	}


	public void writeItemIdentifierStack(ItemIdentifierStack stack) throws IOException {
		if (stack == null) {
			writeInt(-1);
		} else {
			writeInt(stack.getStackSize());
			writeItemIdentifier(stack.getItem());
		}
	}


	public ItemStack readItemStack() throws IOException {
		final int itemId = readInt();
		if (itemId == 0) {
			return null;
		}

		int stackSize = readInt();
		int damage = readInt();
		ItemStack stack = new ItemStack(Item.getItemById(itemId), stackSize, damage);
		stack.setTagCompound(readNBTTagCompound());
		return stack;
	}


	public <T> Set<T> readSet(IReadListObject<T> handler) throws IOException {
		int size = readInt();
		if (size == -1) {
			return null;
		}

		Set<T> set = new HashSet<>(size);
		for (int i = 0; i < size; i++) {
			set.add(handler.readObject(this));
		}
		return set;
	}


	public void writeNBTTagCompound(NBTTagCompound tag) throws IOException {
		if (tag == null) {
			writeByte(0);
		} else {
			writeByte(1);
			CompressedStreamTools.write(tag, new ByteBufOutputStream(localBuffer));
		}
	}


	public void writeBytes(byte[] arr) throws IOException {
		localBuffer.writeBytes(arr);
	}


	public NBTTagCompound readNBTTagCompound() throws IOException {
		boolean isEmpty = (readByte() == 0);
		if (isEmpty) {
			return null;
		}

		return CompressedStreamTools.func_152456_a(new ByteBufInputStream(localBuffer), NBTSizeTracker.field_152451_a);
	}


	public <T extends Enum<T>> EnumSet<T> readEnumSet(Class<T> clazz) throws IOException {
		EnumSet<T> types = EnumSet.noneOf(clazz);
		byte[] arr = readByteArray();
		if (arr != null) {
			T[] parts = clazz.getEnumConstants();
			for (T part : parts) {
				if ((arr[part.ordinal() / 8] & (1 << (part.ordinal() % 8))) != 0) {
					types.add(part);
				}
			}
		}
		return types;
	}


	public ByteBuf readByteBuf() throws IOException {
		byte[] arr = readByteArray();
		if (arr == null) {
			throw new NullPointerException("Buffer may not be null, but read null");
		} else {
			return wrappedBuffer(arr);
		}
	}


	public void writeByteArray(byte[] arr) throws IOException {
		if (arr == null) {
			writeInt(-1);
		} else {
			writeInt(arr.length);
			writeBytes(arr);
		}
	}


	public ItemIdentifierStack readItemIdentifierStack() throws IOException {
		int stacksize = readInt();
		if (stacksize == -1) {
			return null;
		}

		ItemIdentifier item = readItemIdentifier();
		return new ItemIdentifierStack(item, stacksize);
	}


	public void writeItemStack(ItemStack itemstack) throws IOException {
		if (itemstack == null) {
			writeInt(0);
		} else {
			writeInt(Item.getIdFromItem(itemstack.getItem()));
			writeInt(itemstack.stackSize);
			writeInt(itemstack.getItemDamage());
			writeNBTTagCompound(itemstack.getTagCompound());
		}
	}


	public void writeResource(IResource res) throws IOException {
		ResourceNetwork.writeResource(this, res);
	}


	public <T extends Enum<T>> void writeEnumSet(EnumSet<T> types, Class<T> clazz) throws IOException {
		T[] parts = clazz.getEnumConstants();
		final int length = parts.length / 8 + (parts.length % 8 == 0 ? 0 : 1);
		byte[] set = new byte[length];

		for (T part : parts) {
			if (types.contains(part)) {
				byte i = (byte) (1 << (part.ordinal() % 8));
				set[part.ordinal() / 8] |= i;
			}
		}
		writeByteArray(set);
	}


	public void writeBooleanArray(boolean[] arr) throws IOException {
		if (arr == null) {
			writeInt(-1);
		} else if (arr.length == 0) {
			writeInt(0);
			writeByteArray(null);
		} else {
			BitSet bits = new BitSet(arr.length);
			for (int i = 0; i < arr.length; i++) {
				bits.set(i, arr[i]);
			}
			writeInt(arr.length);
			writeByteArray(bits.toByteArray());
		}
	}


	public byte[] readBytes(int length) throws IOException {
		byte[] arr = new byte[length];
		localBuffer.readBytes(arr, 0, length);
		return arr;
	}


	public void writeItemIdentifier(ItemIdentifier item) throws IOException {
		if (item == null) {
			writeInt(0);
		} else {
			writeInt(Item.getIdFromItem(item.item));
			writeInt(item.itemDamage);
			writeNBTTagCompound(item.tag);
		}
	}


	public LinkedLogisticsOrderList readLinkedLogisticsOrderList() throws IOException {
		LinkedLogisticsOrderList list = new LinkedLogisticsOrderList();

		List<IOrderInfoProvider> orderInfoProviders = readArrayList(LPDataInput::readOrderInfo);
		if (orderInfoProviders == null) {
			throw new IOException("Expected order info provider list");
		}
		list.addAll(orderInfoProviders);

		List<LinkedLogisticsOrderList> orderLists = readArrayList(LPDataInput::readLinkedLogisticsOrderList);
		if (orderLists == null) {
			throw new IOException("Expected logistics order list");
		}
		list.getSubOrders().addAll(orderLists);

		return list;
	}


	public IResource readResource() throws IOException {
		return ResourceNetwork.readResource(this);
	}


	public void writeBitSet(BitSet bits) throws IOException {
		if (bits == null) {
			throw new NullPointerException("BitSet may not be null");
		}
		writeByteArray(bits.toByteArray());
	}


	public static byte[] collectData(LPDataOutputConsumer dataOutputConsumer) throws IOException {
		ByteBuf dataBuffer = buffer();
		LPDataIOWrapper lpData = getInstance(dataBuffer);

		dataOutputConsumer.accept(lpData);

		lpData.unsetBuffer();

		byte[] data = new byte[dataBuffer.readableBytes()];
		dataBuffer.getBytes(0, data);

		dataBuffer.release();
		return data;
	}


	public void writeOrderInfo(IOrderInfoProvider order) throws IOException {
		writeItemIdentifierStack(order.getAsDisplayItem());
		writeInt(order.getRouterId());
		writeBoolean(order.isFinished());
		writeBoolean(order.isInProgress());
		writeEnum(order.getType());
		writeCollection(order.getProgresses(), LPDataOutput::writeFloat);
		writeByte(order.getMachineProgress());
		writeLPPosition(order.getTargetPosition());
		writeItemIdentifier(order.getTargetType());
	}


	public <T> void writeCollection(Collection<T> collection, IWriteListObject<T> handler) throws IOException {
		if (collection == null) {
			writeInt(-1);
		} else {
			writeInt(collection.size());
			for (T obj : collection) {
				handler.writeObject(this, obj);
			}
		}
	}


	public static void provideData(ByteBuf dataBuffer, LPDataInputConsumer dataInputConsumer) throws IOException {
		LPDataIOWrapper lpData = getInstance(dataBuffer);

		dataInputConsumer.accept(lpData);

		lpData.unsetBuffer();
	}


	public BitSet readBitSet() throws IOException {
		byte[] arr = readByteArray();
		if (arr == null) {
			return new BitSet();
		} else {
			return BitSet.valueOf(arr);
		}
	}


	public static void provideData(byte[] data, LPDataInputConsumer dataInputConsumer) throws IOException {
		ByteBuf dataBuffer = wrappedBuffer(data);
		LPDataIOWrapper lpData = getInstance(dataBuffer);

		dataInputConsumer.accept(lpData);

		lpData.unsetBuffer();
		dataBuffer.release();
	}


	public void writeLinkedLogisticsOrderList(LinkedLogisticsOrderList orderList) throws IOException {
		writeCollection(orderList, LPDataOutput::writeOrderInfo);
		writeCollection(orderList.getSubOrders(), LPDataOutput::writeLinkedLogisticsOrderList);
	}


	public void testNullArrayList() throws Exception {
		byte[] data = LPDataIOWrapper.collectData(output -> output.writeCollection(null, LPDataOutput::writeUTF));

		LPDataIOWrapper.provideData(data, input -> {
			assertEquals(null, input.readArrayList(LPDataInput::readUTF));

			assertEquals(BUFFER_EMPTY_MSG, 0, ((LPDataIOWrapper) input).localBuffer.readableBytes());
		});
	}


	public void testArrayList() throws Exception {
		ArrayList<String> arrayList = new ArrayList<>();
		arrayList.add("drölf");
		arrayList.add("text");

		byte[] data = LPDataIOWrapper.collectData(output -> output.writeCollection(arrayList, LPDataOutput::writeUTF));

		LPDataIOWrapper.provideData(data, input -> {
			assertEquals(arrayList, input.readArrayList(LPDataInput::readUTF));

			assertEquals(BUFFER_EMPTY_MSG, 0, ((LPDataIOWrapper) input).localBuffer.readableBytes());
		});
	}


	public void testLinkedList() throws Exception {
		LinkedList<String> linkedList = new LinkedList<>();
		linkedList.add("drölf");
		linkedList.add("text");

		byte[] data = LPDataIOWrapper.collectData(output -> output.writeCollection(linkedList, LPDataOutput::writeUTF));

		LPDataIOWrapper.provideData(data, input -> {
			assertEquals(linkedList, input.readLinkedList(LPDataInput::readUTF));

			assertEquals(BUFFER_EMPTY_MSG, 0, ((LPDataIOWrapper) input).localBuffer.readableBytes());
		});
	}


	public void testNullSet() throws Exception {
		byte[] data = LPDataIOWrapper.collectData(output -> output.writeCollection(null, LPDataOutput::writeUTF));

		LPDataIOWrapper.provideData(data, input -> {
			assertEquals(null, input.readSet(LPDataInput::readUTF));

			assertEquals(BUFFER_EMPTY_MSG, 0, ((LPDataIOWrapper) input).localBuffer.readableBytes());
		});
	}


	public void testSet() throws Exception {
		HashSet<String> set = new HashSet<>();
		set.add("drölf");
		set.add("text");

		byte[] data = LPDataIOWrapper.collectData(output -> output.writeCollection(set, LPDataOutput::writeUTF));

		LPDataIOWrapper.provideData(data, input -> {
			assertEquals(set, input.readSet(LPDataInput::readUTF));

			assertEquals(BUFFER_EMPTY_MSG, 0, ((LPDataIOWrapper) input).localBuffer.readableBytes());
		});
	}