Search in sources :

Example 6 with IMultiblockComponent

use of forestry.api.multiblock.IMultiblockComponent in project ForestryMC by ForestryMC.

the class MultiblockControllerBase method attachBlock.

@Override
public void attachBlock(IMultiblockComponent part) {
    BlockPos coord = part.getCoordinates();
    if (!connectedParts.add(part)) {
        Log.warning("[%s] Controller %s is double-adding part %d @ %s. This is unusual. " + "If you encounter odd behavior, please tear down the machine and rebuild it.", world.isRemote ? "CLIENT" : "SERVER", hashCode(), part.hashCode(), coord);
    }
    MultiblockLogic logic = (MultiblockLogic) part.getMultiblockLogic();
    logic.setController(this);
    this.onBlockAdded(part);
    if (logic.hasMultiblockSaveData()) {
        NBTTagCompound savedData = logic.getMultiblockSaveData();
        onAttachedPartWithMultiblockData(part, savedData);
        logic.onMultiblockDataAssimilated();
    }
    if (this.referenceCoord == null) {
        referenceCoord = coord;
        logic.becomeMultiblockSaveDelegate();
    } else if (coord.compareTo(referenceCoord) < 0) {
        TileUtil.actOnTile(world, referenceCoord, IMultiblockComponent.class, tile -> {
            MultiblockLogic teLogic = (MultiblockLogic) tile.getMultiblockLogic();
            teLogic.forfeitMultiblockSaveDelegate();
        });
        referenceCoord = coord;
        logic.becomeMultiblockSaveDelegate();
    } else {
        logic.forfeitMultiblockSaveDelegate();
    }
    if (minimumCoord != null) {
        if (coord.getX() < minimumCoord.getX()) {
            minimumCoord = new BlockPos(coord.getX(), minimumCoord.getY(), minimumCoord.getZ());
        }
        if (coord.getY() < minimumCoord.getY()) {
            minimumCoord = new BlockPos(minimumCoord.getX(), coord.getY(), minimumCoord.getZ());
        }
        if (coord.getZ() < minimumCoord.getZ()) {
            minimumCoord = new BlockPos(minimumCoord.getX(), minimumCoord.getY(), coord.getZ());
        }
    }
    if (maximumCoord != null) {
        if (coord.getX() > maximumCoord.getX()) {
            maximumCoord = new BlockPos(coord.getX(), maximumCoord.getY(), maximumCoord.getZ());
        }
        if (coord.getY() > maximumCoord.getY()) {
            maximumCoord = new BlockPos(maximumCoord.getX(), coord.getY(), maximumCoord.getZ());
        }
        if (coord.getZ() > maximumCoord.getZ()) {
            maximumCoord = new BlockPos(maximumCoord.getX(), maximumCoord.getY(), coord.getZ());
        }
    }
    MultiblockRegistry.addDirtyController(world, this);
}
Also used : NBTTagCompound(net.minecraft.nbt.NBTTagCompound) Translator(forestry.core.utils.Translator) World(net.minecraft.world.World) Collection(java.util.Collection) Set(java.util.Set) Random(java.util.Random) BlockPos(net.minecraft.util.math.BlockPos) Log(forestry.core.utils.Log) HashSet(java.util.HashSet) List(java.util.List) Block(net.minecraft.block.Block) Side(net.minecraftforge.fml.relauncher.Side) IChunkProvider(net.minecraft.world.chunk.IChunkProvider) IMultiblockComponent(forestry.api.multiblock.IMultiblockComponent) TileUtil(forestry.core.tiles.TileUtil) TileEntity(net.minecraft.tileentity.TileEntity) Chunk(net.minecraft.world.chunk.Chunk) LinkedList(java.util.LinkedList) SideOnly(net.minecraftforge.fml.relauncher.SideOnly) Collections(java.util.Collections) Nullable(javax.annotation.Nullable) NBTTagCompound(net.minecraft.nbt.NBTTagCompound) BlockPos(net.minecraft.util.math.BlockPos) IMultiblockComponent(forestry.api.multiblock.IMultiblockComponent)

Example 7 with IMultiblockComponent

use of forestry.api.multiblock.IMultiblockComponent in project ForestryMC by ForestryMC.

the class MultiblockControllerBase method disassembleMachine.

/**
 * Called when the machine needs to be disassembled.
 * It is not longer "whole" and should not be functional, usually
 * as a result of a block being removed.
 * Calls onMachineBroken on all attached parts.
 */
private void disassembleMachine() {
    this.assemblyState = AssemblyState.Disassembled;
    for (IMultiblockComponent part : connectedParts) {
        part.onMachineBroken();
    }
    onMachineDisassembled();
}
Also used : IMultiblockComponent(forestry.api.multiblock.IMultiblockComponent)

Example 8 with IMultiblockComponent

use of forestry.api.multiblock.IMultiblockComponent in project ForestryMC by ForestryMC.

the class MultiblockControllerBase method assimilate.

@Override
public void assimilate(IMultiblockControllerInternal other) {
    BlockPos otherReferenceCoord = other.getReferenceCoord();
    BlockPos referenceCoord = getReferenceCoord();
    if (otherReferenceCoord != null && referenceCoord != null && referenceCoord.compareTo(otherReferenceCoord) >= 0) {
        throw new IllegalArgumentException("The controller with the lowest minimum-coord value must consume the one with the higher coords");
    }
    Set<IMultiblockComponent> partsToAcquire = new HashSet<>(other.getComponents());
    // releases all blocks and references gently so they can be incorporated into another multiblock
    other._onAssimilated(this);
    for (IMultiblockComponent acquiredPart : partsToAcquire) {
        // By definition, none of these can be the minimum block.
        if (isInvalid(acquiredPart)) {
            continue;
        }
        connectedParts.add(acquiredPart);
        MultiblockLogic logic = (MultiblockLogic) acquiredPart.getMultiblockLogic();
        logic.setController(this);
        this.onBlockAdded(acquiredPart);
    }
    this.onAssimilate(other);
    other.onAssimilated(this);
}
Also used : BlockPos(net.minecraft.util.math.BlockPos) IMultiblockComponent(forestry.api.multiblock.IMultiblockComponent) HashSet(java.util.HashSet)

Example 9 with IMultiblockComponent

use of forestry.api.multiblock.IMultiblockComponent in project ForestryMC by ForestryMC.

the class MultiblockUtil method getNeighboringParts.

/**
 * Returns an array containing references to neighboring IMultiblockComponent tile entities.
 * Primarily a utility method. Only works after tileentity construction.
 * <p>
 * This method is chunk-safe on the server; it will not query for parts in chunks that are unloaded.
 * Note that no method is chunk-safe on the client, because ChunkProviderClient is stupid.
 *
 * @return An array of references to neighboring IMultiblockComponent tile entities.
 */
public static List<IMultiblockComponent> getNeighboringParts(World world, IMultiblockComponent part) {
    BlockPos partCoord = part.getCoordinates();
    List<BlockPos> neighbors = new ArrayList<>(EnumFacing.values().length);
    for (EnumFacing facing : EnumFacing.values()) {
        BlockPos neighborCoord = new BlockPos(partCoord);
        neighborCoord = neighborCoord.offset(facing);
        neighbors.add(neighborCoord);
    }
    List<IMultiblockComponent> neighborParts = new ArrayList<>();
    IChunkProvider chunkProvider = world.getChunkProvider();
    for (BlockPos neighbor : neighbors) {
        if (chunkProvider.getLoadedChunk(neighbor.getX() >> 4, neighbor.getZ() >> 4) == null) {
            // Chunk not loaded, skip it.
            continue;
        }
        TileUtil.actOnTile(world, neighbor, IMultiblockComponent.class, neighborParts::add);
    }
    return neighborParts;
}
Also used : IChunkProvider(net.minecraft.world.chunk.IChunkProvider) EnumFacing(net.minecraft.util.EnumFacing) ArrayList(java.util.ArrayList) BlockPos(net.minecraft.util.math.BlockPos) IMultiblockComponent(forestry.api.multiblock.IMultiblockComponent)

Example 10 with IMultiblockComponent

use of forestry.api.multiblock.IMultiblockComponent in project ForestryMC by ForestryMC.

the class MultiblockWorldRegistry method processMultiblockChanges.

/**
 * Called prior to processing multiblock controllers. Do bookkeeping.
 */
public void processMultiblockChanges() {
    IChunkProvider chunkProvider = world.getChunkProvider();
    BlockPos coord;
    // Merge pools - sets of adjacent machines which should be merged later on in processing
    List<Set<IMultiblockControllerInternal>> mergePools = null;
    if (!orphanedParts.isEmpty()) {
        Set<IMultiblockComponent> orphansToProcess = null;
        // It's possible to polyfill this, but the polyfill is too slow for comfort.
        synchronized (orphanedPartsMutex) {
            if (!orphanedParts.isEmpty()) {
                orphansToProcess = orphanedParts;
                orphanedParts = new HashSet<>();
            }
        }
        if (orphansToProcess != null && !orphansToProcess.isEmpty()) {
            Set<IMultiblockControllerInternal> compatibleControllers;
            // These are blocks that exist in a valid chunk and require a controller
            for (IMultiblockComponent orphan : orphansToProcess) {
                coord = orphan.getCoordinates();
                if (chunkProvider.getLoadedChunk(coord.getX() >> 4, coord.getZ() >> 4) == null) {
                    continue;
                }
                // This can occur on slow machines.
                if (orphan instanceof TileEntity && ((TileEntity) orphan).isInvalid()) {
                    continue;
                }
                if (TileUtil.getTile(world, coord) != orphan) {
                    // This block has been replaced by another.
                    continue;
                }
                // THIS IS THE ONLY PLACE WHERE PARTS ATTACH TO MACHINES
                // Try to attach to a neighbor's master controller
                compatibleControllers = attachToNeighbors(orphan);
                if (compatibleControllers.isEmpty()) {
                    // FOREVER ALONE! Create and register a new controller.
                    // THIS IS THE ONLY PLACE WHERE NEW CONTROLLERS ARE CREATED.
                    MultiblockLogic logic = (MultiblockLogic) orphan.getMultiblockLogic();
                    IMultiblockControllerInternal newController = logic.createNewController(world);
                    newController.attachBlock(orphan);
                    this.controllers.add(newController);
                } else if (compatibleControllers.size() > 1) {
                    if (mergePools == null) {
                        mergePools = new ArrayList<>();
                    }
                    // THIS IS THE ONLY PLACE WHERE MERGES ARE DETECTED
                    // Multiple compatible controllers indicates an impending merge.
                    // Locate the appropriate merge pool(s)
                    List<Set<IMultiblockControllerInternal>> candidatePools = new ArrayList<>();
                    for (Set<IMultiblockControllerInternal> candidatePool : mergePools) {
                        if (!Collections.disjoint(candidatePool, compatibleControllers)) {
                            // They share at least one element, so that means they will all touch after the merge
                            candidatePools.add(candidatePool);
                        }
                    }
                    if (candidatePools.size() <= 0) {
                        // No pools nearby, create a new merge pool
                        mergePools.add(compatibleControllers);
                    } else if (candidatePools.size() == 1) {
                        // Only one pool nearby, simply add to that one
                        candidatePools.get(0).addAll(compatibleControllers);
                    } else {
                        // Multiple pools- merge into one, then add the compatible controllers
                        Set<IMultiblockControllerInternal> masterPool = candidatePools.get(0);
                        Set<IMultiblockControllerInternal> consumedPool;
                        for (int i = 1; i < candidatePools.size(); i++) {
                            consumedPool = candidatePools.get(i);
                            masterPool.addAll(consumedPool);
                            mergePools.remove(consumedPool);
                        }
                        masterPool.addAll(compatibleControllers);
                    }
                }
            }
        }
    }
    if (mergePools != null && !mergePools.isEmpty()) {
        // should voltron the fuck up.
        for (Set<IMultiblockControllerInternal> mergePool : mergePools) {
            // Search for the new master machine, which will take over all the blocks contained in the other machines
            IMultiblockControllerInternal newMaster = null;
            for (IMultiblockControllerInternal controller : mergePool) {
                if (newMaster == null || controller.shouldConsume(newMaster)) {
                    newMaster = controller;
                }
            }
            if (newMaster == null) {
                Log.error("Multiblock system checked a merge pool of size %d, found no master candidates. This should never happen.", mergePool.size());
            } else {
                // Merge all the other machines into the master machine, then unregister them
                addDirtyController(newMaster);
                for (IMultiblockControllerInternal controller : mergePool) {
                    if (controller != newMaster) {
                        newMaster.assimilate(controller);
                        addDeadController(controller);
                        addDirtyController(newMaster);
                    }
                }
            }
        }
    }
    // physically connected to their master.
    if (!dirtyControllers.isEmpty()) {
        for (IMultiblockControllerInternal controller : dirtyControllers) {
            if (controller == null) {
                continue;
            }
            // Tell the machine to check if any parts are disconnected.
            // It should return a set of parts which are no longer connected.
            // POSTCONDITION: The controller must have informed those parts that
            // they are no longer connected to this machine.
            Set<IMultiblockComponent> newlyDetachedParts = controller.checkForDisconnections();
            if (!controller.isEmpty()) {
                controller.recalculateMinMaxCoords();
                controller.checkIfMachineIsWhole();
            } else {
                addDeadController(controller);
            }
            if (!newlyDetachedParts.isEmpty()) {
                // Controller has shed some parts - add them to the detached list for delayed processing
                detachedParts.addAll(newlyDetachedParts);
            }
        }
        dirtyControllers.clear();
    }
    // Unregister dead controllers
    if (!deadControllers.isEmpty()) {
        for (IMultiblockControllerInternal controller : deadControllers) {
            // Validate that they are empty/dead, then unregister them.
            if (!controller.isEmpty()) {
                Log.error("Found a non-empty controller. Forcing it to shed its blocks and die. This should never happen!");
                detachedParts.addAll(controller.detachAllBlocks());
            }
            // THIS IS THE ONLY PLACE WHERE CONTROLLERS ARE UNREGISTERED.
            this.controllers.remove(controller);
        }
        deadControllers.clear();
    }
    // list, and will be checked next tick to see if their chunk is still loaded.
    for (IMultiblockComponent part : detachedParts) {
        // Ensure parts know they're detached
        MultiblockLogic logic = (MultiblockLogic) part.getMultiblockLogic();
        logic.assertDetached(part);
    }
    addAllOrphanedPartsThreadsafe(detachedParts);
    detachedParts.clear();
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) ArrayList(java.util.ArrayList) IMultiblockComponent(forestry.api.multiblock.IMultiblockComponent) TileEntity(net.minecraft.tileentity.TileEntity) IChunkProvider(net.minecraft.world.chunk.IChunkProvider) BlockPos(net.minecraft.util.math.BlockPos) IMultiblockLogic(forestry.api.multiblock.IMultiblockLogic) ArrayList(java.util.ArrayList) List(java.util.List)

Aggregations

IMultiblockComponent (forestry.api.multiblock.IMultiblockComponent)22 BlockPos (net.minecraft.util.math.BlockPos)13 TileEntity (net.minecraft.tileentity.TileEntity)7 ICamouflagedTile (forestry.api.core.ICamouflagedTile)6 HashSet (java.util.HashSet)6 IChunkProvider (net.minecraft.world.chunk.IChunkProvider)6 ICamouflageHandler (forestry.api.core.ICamouflageHandler)4 IMultiblockController (forestry.api.multiblock.IMultiblockController)4 Nullable (javax.annotation.Nullable)3 IMultiblockLogic (forestry.api.multiblock.IMultiblockLogic)2 ArrayList (java.util.ArrayList)2 LinkedList (java.util.LinkedList)2 List (java.util.List)2 Set (java.util.Set)2 ItemStack (net.minecraft.item.ItemStack)2 HashMultiset (com.google.common.collect.HashMultiset)1 Multiset (com.google.common.collect.Multiset)1 GameProfile (com.mojang.authlib.GameProfile)1 MultiblockValidationException (forestry.core.multiblock.MultiblockValidationException)1 TileUtil (forestry.core.tiles.TileUtil)1