buildcraft.builders.tile.TileBuilder_Neptune.java Source code

Java tutorial

Introduction

Here is the source code for buildcraft.builders.tile.TileBuilder_Neptune.java

Source

/* Copyright (c) 2016 SpaceToad and the BuildCraft team
 * 
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. */
package buildcraft.builders.tile;

import java.io.IOException;
import java.util.List;

import com.google.common.collect.ImmutableList;

import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumFacing.Axis;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.ITickable;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;

import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.items.IItemHandlerModifiable;

import buildcraft.api.bpt.SchematicBlock;
import buildcraft.api.core.EnumPipePart;
import buildcraft.api.core.IPathProvider;
import buildcraft.api.mj.MjAPI;
import buildcraft.api.mj.MjBattery;
import buildcraft.api.tiles.IDebuggable;

import buildcraft.builders.BCBuildersItems;
import buildcraft.builders.bpt.TickingBlueprintBuilder;
import buildcraft.builders.bpt.TickingBlueprintBuilder.EnumBuilderMessage;
import buildcraft.builders.item.ItemBlueprint.BptStorage;
import buildcraft.lib.block.BlockBCBase_Neptune;
import buildcraft.lib.bpt.Blueprint;
import buildcraft.lib.fluids.Tank;
import buildcraft.lib.fluids.TankManager;
import buildcraft.lib.misc.BoundingBoxUtil;
import buildcraft.lib.misc.CapUtil;
import buildcraft.lib.misc.NBTUtilBC;
import buildcraft.lib.misc.PositionUtil;
import buildcraft.lib.misc.data.Box;
import buildcraft.lib.misc.data.EnumAxisOrder;
import buildcraft.lib.net.IPayloadWriter;
import buildcraft.lib.net.PacketBufferBC;
import buildcraft.lib.tile.TileBC_Neptune;
import buildcraft.lib.tile.item.ItemHandlerManager.EnumAccess;

public class TileBuilder_Neptune extends TileBC_Neptune implements ITickable, IDebuggable {
    public static final int NET_BOX = 10;
    public static final int NET_PATH = 11;
    public static final int NET_CLEAR = 12;
    public static final int NET_BUILD = 13;
    public static final int NET_ANIM_ITEM = 14;
    public static final int NET_ANIM_BLOCK = 15;
    public static final int NET_ANIM_FLUID = 16;
    public static final int NET_ANIM_POWER = 17;
    public static final int NET_ANIM_STATE = 18;

    public final IItemHandlerModifiable invBlueprint = itemManager.addInvHandler("blueprint", 1, EnumAccess.BOTH,
            EnumPipePart.VALUES);
    public final IItemHandlerModifiable invResources = itemManager.addInvHandler("resources", 27, EnumAccess.NONE,
            EnumPipePart.VALUES);

    private final Tank[] tanks = new Tank[] { //
            new Tank("fluid1", Fluid.BUCKET_VOLUME * 8, this), //
            new Tank("fluid2", Fluid.BUCKET_VOLUME * 8, this), //
            new Tank("fluid3", Fluid.BUCKET_VOLUME * 8, this), //
            new Tank("fluid4", Fluid.BUCKET_VOLUME * 8, this) //
    };

    private final TankManager<Tank> tankManager = new TankManager<>(tanks);

    private final MjBattery battery = new MjBattery(1000 * MjAPI.MJ);

    public final TickingBlueprintBuilder tickingBuilder = new TickingBlueprintBuilder(this::sendMessage,
            this::getSchematic);
    private BuilderAccessor accessor = null;

    /** Stores the real path - just a few block positions. */
    private ImmutableList<BlockPos> path = null;
    /** Stores the real path plus all possible block positions inbetween. Not saved, regenerated from path. */
    private ImmutableList<BlockPos> pathInterpCache = null;
    private int lastIndex = -1;

    private BlockPos lastBptPos;
    private Box lastBox = null;
    private Blueprint currentBpt = null;
    private int cooldown = 0;

    @Override
    protected void onSlotChange(IItemHandlerModifiable itemHandler, int slot, ItemStack before, ItemStack after) {
        if (itemHandler == invBlueprint) {
            // Update bpt + builder
        }
        super.onSlotChange(itemHandler, slot, before, after);
    }

    @Override
    public void onPlacedBy(EntityLivingBase placer, ItemStack stack) {
        super.onPlacedBy(placer, stack);
        EnumFacing thisFacing = getWorld().getBlockState(getPos()).getValue(BlockBCBase_Neptune.PROP_FACING);
        TileEntity inFront = getWorld().getTileEntity(getPos().offset(thisFacing.getOpposite()));
        if (inFront instanceof IPathProvider) {
            IPathProvider provider = (IPathProvider) inFront;
            ImmutableList<BlockPos> copiedPath = ImmutableList.copyOf(provider.getPath());
            if (copiedPath.size() < 2) {
                setPath(null);
            } else {
                setPath(copiedPath);
                provider.removeFromWorld();
            }
            sendNetworkUpdate(NET_RENDER_DATA);
        }
    }

    @Override
    public void update() {
        battery.tick(getWorld(), getPos());
        if (world.isRemote) {
            tickingBuilder.tick(Side.CLIENT);
            // client stuffs
        } else {
            if (accessor != null) {
                accessor.tick();
            }
            ItemStack bpt = invBlueprint.getStackInSlot(0);
            if (bpt != null && bpt.getItem() == BCBuildersItems.blueprint) {
                if (tickingBuilder.tick(Side.SERVER)) {
                    cooldown--;
                    if (cooldown <= 0) {
                        BptStorage storage = BCBuildersItems.blueprint.createStorage(bpt);
                        currentBpt = new Blueprint(storage.getSaved());
                        EnumFacing thisFacing = getWorld().getBlockState(getPos())
                                .getValue(BlockBCBase_Neptune.PROP_FACING);
                        Rotation rotation = PositionUtil.getRotatedFacing(currentBpt.facing, thisFacing, Axis.Y);
                        currentBpt.rotate(Axis.Y, rotation);

                        boolean immediateRestart = false;

                        if (getPath() == null) {
                            BlockPos bptPos = getPos().add(thisFacing.getOpposite().getDirectionVec());
                            lastBptPos = bptPos;
                            BlockPos start = bptPos.add(currentBpt.offset);
                            BlockPos max = currentBpt.size.add(-1, -1, -1);
                            BlockPos end = start.add(max);
                            lastBox = new Box(start, end);
                            lastIndex = -1;
                        } else if (lastBox == null || lastBptPos == null) {
                            BlockPos bptPos = getPath().get(0);
                            lastBptPos = bptPos;
                            BlockPos start = bptPos.add(currentBpt.offset);
                            BlockPos max = currentBpt.size.add(-1, -1, -1);
                            BlockPos end = start.add(max);
                            lastBox = new Box(start, end);
                            lastIndex = 0;
                            immediateRestart = true;
                        } else {
                            BlockPos toStartAt = null;
                            Box toUse = null;
                            for (int i = lastIndex + 1; i < pathInterpCache.size(); i++) {
                                BlockPos toTest = pathInterpCache.get(i);
                                BlockPos start = toTest.add(currentBpt.offset);
                                BlockPos max = currentBpt.size.add(-1, -1, -1);
                                BlockPos end = start.add(max);
                                Box nBox = new Box(start, end);
                                if (!nBox.getBoundingBox().intersectsWith(lastBox.getBoundingBox())) {
                                    toStartAt = toTest;
                                    toUse = nBox;
                                    lastIndex = i;
                                    break;
                                }
                            }
                            if (toStartAt == null) {
                                // failed to find a position on the path
                                lastBox = null;
                                lastBptPos = null;
                                lastIndex = -1;
                            } else {
                                lastBptPos = toStartAt;
                                lastBox = toUse;
                                immediateRestart = true;
                            }
                        }
                        if (immediateRestart) {
                            cooldown = 30;
                        } else {
                            cooldown = 300;
                        }
                        if (accessor != null) {
                            accessor.releaseAll();
                        }
                        accessor = new BuilderAccessor(this, tickingBuilder);
                        tickingBuilder.reset(lastBox, EnumAxisOrder.XZY.getMaxToMinOrder(), accessor);
                        sendNetworkUpdate(NET_RENDER_DATA);
                    }
                }
            } else {
                tickingBuilder.cancel();
                if (lastBox != null) {
                    lastBox = null;
                    lastBptPos = null;
                    sendNetworkUpdate(NET_RENDER_DATA);
                }
            }
        }
    }

    private void setPath(ImmutableList<BlockPos> path) {
        this.path = path;
        if (path != null) {
            int max = path.size() - 1;
            // Create a list of all the possible block positions on the path that could be used
            ImmutableList.Builder<BlockPos> interp = ImmutableList.builder();
            interp.add(path.get(0));
            for (int i = 1; i <= max; i++) {
                final BlockPos from = path.get(i - 1);
                final BlockPos to = path.get(i);
                interp.addAll(PositionUtil.getAllOnPath(from, to));
            }
            pathInterpCache = interp.build();
        } else {
            pathInterpCache = null;
        }
    }

    private SchematicBlock getSchematic(BlockPos bptPos) {
        return currentBpt.getSchematicAt(bptPos);
    }

    // Networking

    private void sendMessage(EnumBuilderMessage type, IPayloadWriter writer) {
        final int id;
        if (type == EnumBuilderMessage.ANIMATION_BLOCK)
            id = NET_ANIM_BLOCK;
        else if (type == EnumBuilderMessage.ANIMATION_ITEM)
            id = NET_ANIM_ITEM;
        else if (type == EnumBuilderMessage.ANIMATION_FLUID)
            id = NET_ANIM_FLUID;
        else if (type == EnumBuilderMessage.ANIMATION_POWER)
            id = NET_ANIM_POWER;
        else if (type == EnumBuilderMessage.ANIMATION_STATE)
            id = NET_ANIM_STATE;
        else if (type == EnumBuilderMessage.BOX)
            id = NET_BOX;
        else if (type == EnumBuilderMessage.BUILD)
            id = NET_BUILD;
        else if (type == EnumBuilderMessage.CLEAR)
            id = NET_CLEAR;
        else
            throw new IllegalArgumentException("Unknown type " + type);
        createAndSendMessage(id, writer);
    }

    @Override
    public void writePayload(int id, PacketBufferBC buffer, Side side) {
        super.writePayload(id, buffer, side);
        if (side == Side.SERVER) {
            if (id == NET_RENDER_DATA) {
                writePayload(NET_BOX, buffer, side);
                writePayload(NET_PATH, buffer, side);
                writePayload(NET_ANIM_STATE, buffer, side);
            } else if (id == NET_BOX) {
                tickingBuilder.writePayload(EnumBuilderMessage.BOX, buffer, side);
            } else if (id == NET_PATH) {
                if (getPath() == null) {
                    buffer.writeInt(0);
                } else {
                    buffer.writeInt(getPath().size());
                    for (BlockPos p : getPath()) {
                        buffer.writeBlockPos(p);
                    }
                }
            } else if (id == NET_ANIM_STATE) {
                tickingBuilder.writePayload(EnumBuilderMessage.ANIMATION_STATE, buffer, side);
            }
        }
    }

    @Override
    public void readPayload(int id, PacketBufferBC buffer, Side side, MessageContext ctx) throws IOException {
        super.readPayload(id, buffer, side, ctx);
        if (side == Side.CLIENT) {
            if (id == NET_RENDER_DATA) {
                readPayload(NET_BOX, buffer, side, ctx);
                readPayload(NET_PATH, buffer, side, ctx);
                readPayload(NET_ANIM_STATE, buffer, side, ctx);
            } else if (id == NET_BOX) {
                tickingBuilder.readPayload(EnumBuilderMessage.BOX, buffer, side);
            } else if (id == NET_PATH) {
                int count = buffer.readInt();
                if (count <= 0) {
                    setPath(null);
                } else {
                    ImmutableList.Builder<BlockPos> nPath = ImmutableList.builder();
                    for (int i = 0; i < count; i++) {
                        nPath.add(buffer.readBlockPos());
                    }
                    setPath(nPath.build());
                }
            } else if (id == NET_CLEAR || id == NET_BUILD) {
                BlockPos changeAt = buffer.readBlockPos();
                double x = changeAt.getX() + 0.5;
                double y = changeAt.getY() + 0.5;
                double z = changeAt.getZ() + 0.5;
                EnumParticleTypes type = id == NET_CLEAR ? EnumParticleTypes.SMOKE_NORMAL : EnumParticleTypes.CLOUD;
                world.spawnParticle(type, x, y, z, 0, 0, 0);
            }
            // All animation types
            else if (id == NET_ANIM_ITEM)
                tickingBuilder.readPayload(EnumBuilderMessage.ANIMATION_ITEM, buffer, side);
            else if (id == NET_ANIM_BLOCK)
                tickingBuilder.readPayload(EnumBuilderMessage.ANIMATION_BLOCK, buffer, side);
            else if (id == NET_ANIM_FLUID)
                tickingBuilder.readPayload(EnumBuilderMessage.ANIMATION_FLUID, buffer, side);
            else if (id == NET_ANIM_POWER)
                tickingBuilder.readPayload(EnumBuilderMessage.ANIMATION_POWER, buffer, side);
            else if (id == NET_ANIM_STATE)
                tickingBuilder.readPayload(EnumBuilderMessage.ANIMATION_STATE, buffer, side);
        }
    }

    // Read-write

    @Override
    public void readFromNBT(NBTTagCompound nbt) {
        super.readFromNBT(nbt);

        NBTTagList list = nbt.getTagList("path", Constants.NBT.TAG_INT_ARRAY);
        if (list.hasNoTags()) {
            setPath(null);
        } else {
            ImmutableList.Builder<BlockPos> builder = ImmutableList.builder();
            for (int i = 0; i < list.tagCount(); i++) {
                builder.add(NBTUtilBC.readBlockPos(list.get(i)));
            }
            setPath(builder.build());
        }
    }

    @Override
    public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
        super.writeToNBT(nbt);

        if (getPath() != null) {
            NBTTagList list = new NBTTagList();
            for (BlockPos p : getPath()) {
                list.appendTag(NBTUtilBC.writeBlockPos(p));
            }
            nbt.setTag("path", list);
        }

        return nbt;
    }

    // Capability

    @Override
    public <T> T getCapability(Capability<T> capability, EnumFacing facing) {
        if (capability == CapUtil.CAP_FLUIDS) {
            return (T) tankManager;
        }
        return super.getCapability(capability, facing);
    }

    // Rendering

    public ImmutableList<BlockPos> getPath() {
        return path;
    }

    @SideOnly(Side.CLIENT)
    public Box getBox() {
        return tickingBuilder.box;
    }

    @Override
    @SideOnly(Side.CLIENT)
    public boolean hasFastRenderer() {
        return true;
    }

    @Override
    @SideOnly(Side.CLIENT)
    public AxisAlignedBB getRenderBoundingBox() {
        return BoundingBoxUtil.makeFrom(getPos(), getBox(), getPath());
    }

    @Override
    @SideOnly(Side.CLIENT)
    public double getMaxRenderDistanceSquared() {
        return Double.MAX_VALUE;
    }

    @Override
    @SideOnly(Side.CLIENT)
    public void getDebugInfo(List<String> left, List<String> right, EnumFacing side) {
        left.add("");
        left.add("cooldown = " + cooldown);
        left.add("lastBptPos = " + lastBptPos);
        left.add("lastBox = " + lastBox);
        left.add("pathInterpCache = " + (pathInterpCache == null ? "null" : pathInterpCache.size()));
        left.add("lastIndex = " + lastIndex);
    }
}