Skip to main content

Multiblock facade

Namespace: DoriosCore · Package: DoriosCore/index.js

Multiblock exposes the structure scanner and shared lifecycle managers used by MultiblockMachine and MultiblockGenerator.

import {
Multiblock,
MultiblockGenerator,
MultiblockMachine,
} from "DoriosCore/index.js";

Definition

const Multiblock: {
Constants: MultiblockConstants;
ActivationManager: MultiblockActivationManager;
DeactivationManager: MultiblockDeactivationManager;
EntityManager: MultiblockEntityManager;
StructureDetector: MultiblockStructureDetector;
}

The manager classes are exposed through this facade. Addons should not import DoriosCore's internal files.

Structure lifecycle

  1. A player interacts with the controller while holding an item whose ID contains wrench.
  2. DoriosCore finds the inclusive casing bounds and scans every block inside them.
  3. The controller class validates component requirements.
  4. Activation stores bounds, activates link-node ports, calculates built-in energy capacity, and marks the controller on.
  5. Addon-owned onActivate code derives custom capacities, rates, and behavior from detected components.
  6. Breaking a casing or controller deactivates ports and clears runtime state.

Detected structure contract

interface DetectedStructure {
bounds: Bounds;
components: Record<string, number>;
inputBlocks: string[];
caseBlocks?: Vector3[];
ventBlocks: Vector3[];
center: Vector3;
}
PropertyDescription
boundsInclusive minimum and maximum casing corners.
componentsCounts keyed by the block identifier without its namespace. It can also contain air and vent.
inputBlocksSerialized link-node position tags copied to the controller entity during activation.
caseBlocksReserved optional casing-position list. The current built-in scanner does not populate it.
ventBlocksPositions of vent-tagged casing blocks on the top layer.
centerGeometric center of the inclusive bounds.

Valid structure rules

Outer shell

  • Every outer-shell position except the controller must have the exact required_case tag supplied by the controller config.
  • The controller must be part of the shell and have contiguous casing beside it along X or Z so bounds can be discovered.
  • A shell block can also be a DoriosLib link node. Those blocks become activatable item, energy, liquid, or gas ports.
  • A top-layer shell block tagged dorios:vent_block is counted as vent and added to ventBlocks.

Interior

Interior positions may contain:

  • air, counted as components.air;
  • any liquid block;
  • a block tagged dorios:multiblock_component, counted by its identifier after the namespace.

Any other interior block fails the scan and its coordinates are reported to the player.

important

DoriosCore does not need to know every addon component. Custom blocks still appear in components; calculate their effects inside an addon-owned core or the activation callback. Do not modify DoriosCore to add component IDs.

StructureDetector

detectFromController(event, caseTag)

Multiblock.StructureDetector.detectFromController(event: InteractionEventLike, caseTag: string): Promise<false | DetectedStructure>

Finds casing bounds, validates the structure, shows a formation particle outline, and returns the full structure result.

ParameterTypeDescription
eventInteractionEventLikeObject containing the controller block and interacting player. The player receives scan messages.
caseTagstringExact block tag accepted for every casing position.

Returns false when bounds cannot be found or any position is invalid.

showFormationEffect(bounds, dimension)

Multiblock.StructureDetector.showFormationEffect(bounds: Bounds, dimension: Dimension): Promise<void>

Draws a redstone-dust outline around the vertical sides of bounds, yielding two ticks between layers.

findMultiblockBounds(start, dimension, caseTag)

Multiblock.StructureDetector.findMultiblockBounds(start: Vector3, dimension: Dimension, caseTag: string): Promise<Bounds | null>

Expands from the controller across contiguous casing to find inclusive X, Z, and Y bounds.

ParameterTypeDescription
startVector3Controller position.
dimensionDimensionDimension containing the candidate structure.
caseTagstringExact casing tag tested during expansion.

Each direction is limited by Multiblock.Constants.MAX_SIZE. Returns null when the controller has no adjacent casing along X or Z or when a perpendicular casing line cannot be established.

scanStructure(min, max, dimension, controller, caseTag)

Multiblock.StructureDetector.scanStructure(
min: Vector3,
max: Vector3,
dimension: Dimension,
controller: Vector3,
caseTag: string,
): Promise<{
components: Record<string, number>;
inputBlocks: string[];
ventBlocks: Vector3[];
} | string>

Validates every position in inclusive bounds using the shell and interior rules above. A successful scan returns component, port, and vent data. Failure returns a coordinate string identifying the first invalid block.

ActivationManager

fillBlocks(bounds, dimension, blockId)

Multiblock.ActivationManager.fillBlocks(bounds: Bounds, dimension: Dimension, blockId?: string): void

Schedules one fill command per Y layer, replacing only air inside the inclusive bounds.

ParameterTypeDefaultDescription
boundsBoundsArea to fill.
dimensionDimensionDimension in which commands run.
blockIdstringminecraft:waterBlock placed into air positions.

Layers are separated by four ticks to distribute command cost.

activateMultiblock(entity, structure, fillBlocksConfig)

Multiblock.ActivationManager.activateMultiblock(entity: Entity, structure: Partial<DetectedStructure>, fillBlocksConfig?: FillBlocksConfig): number

Applies shared activation state to an already spawned controller entity.

ParameterTypeDescription
entityEntityController helper entity.
structurePartial<DetectedStructure>Detected ports, bounds, vents, and component counts.
fillBlocksConfig{ blockId?: string }Optional internal fill request.

The method:

  • triggers utilitycraft:show on the entity;
  • copies link-node tags to the entity;
  • sets each resolved link node's utilitycraft:active state to 1;
  • requests UtilityCore network refreshes for tagged energy, item, liquid, and gas ports;
  • persists bounds and vent positions;
  • optionally fills air inside the bounds;
  • calculates and applies built-in energy capacity;
  • stores dorios:state as on.

Returns the calculated energy capacity, including 0 when no recognized energy component exists.

calculateEnergyCapacity(components)

Multiblock.ActivationManager.calculateEnergyCapacity(components: Record<string, number>): number multiplies recognized component counts by ENERGY_PER_UNIT and returns their sum. Unknown component IDs contribute 0.

DeactivationManager

emptyBlocks(entity, blockId)

Multiblock.DeactivationManager.emptyBlocks(entity: Entity, blockId?: string): void

Reads bounds from the compatibility property reactorStats, then schedules top-to-bottom commands that replace blockId with air.

ParameterTypeDefaultDescription
entityEntityController containing serialized reactorStats.bounds.
blockIdstringminecraft:waterFilled block to remove.

If reactorStats is absent, the method does nothing. A custom fill-based multiblock must store its bounds there when it depends on this cleanup method.

deactivateMultiblock(block, player, emptyBlocksConfig)

Multiblock.DeactivationManager.deactivateMultiblock(block: Block, player?: Player, emptyBlocksConfig?: FillBlocksConfig): Entity | undefined

Resolves the owning controller from block, hides it, removes its link-node tags, resets active port states, requests UtilityCore network refreshes, sets dorios:rateSpeed to 0, clears stored bounds, and stores dorios:state as off.

The optional player receives a deactivation message. When emptyBlocksConfig is provided, emptyBlocks() is also called. Returns the controller entity or undefined when none can be resolved.

handleBreakController(block, player, emptyBlocksConfig)

Multiblock.DeactivationManager.handleBreakController(block: Block, player?: Player, emptyBlocksConfig?: FillBlocksConfig): Entity | undefined runs deactivateMultiblock() and removes the controller entity two ticks later. Use it when the controller block itself is broken.

EntityManager

getCenter(min, max)

Multiblock.EntityManager.getCenter(min: Vector3, max: Vector3): Vector3 returns the arithmetic midpoint of both corners.

getVolume(bounds)

Multiblock.EntityManager.getVolume(bounds: Bounds): number returns inclusive block volume: (Δx + 1) × (Δy + 1) × (Δz + 1).

isInsideBounds(position, bounds)

Multiblock.EntityManager.isInsideBounds(position: Vector3, bounds: Bounds): boolean includes every position on the minimum and maximum faces.

getEntityFromBlock(block)

Multiblock.EntityManager.getEntityFromBlock(block: Block): Entity | undefined

Returns the first entity directly at the block position when one exists. Otherwise, it searches nearby entities in the dorios:multiblock family and returns the first whose stored dorios:bounds contains the position. Invalid serialized bounds are ignored.

Constants

Scan and energy constants

ConstantValueDescription
MAX_SIZE99Maximum expansion distance in each scan direction.
SCAN_SPEED64Pacing value used by the structure scan.
ENERGY_PER_UNIT.energy_cell4,000,000DE capacity per standard energy cell.
ENERGY_PER_UNIT.basic_power_condenser_unit40,000,000DE capacity per basic condenser unit.
ENERGY_PER_UNIT.advanced_power_condenser_unit320,000,000DE capacity per advanced condenser unit.
ENERGY_PER_UNIT.expert_power_condenser_unit2,560,000,000DE capacity per expert condenser unit.
ENERGY_PER_UNIT.ultimate_power_condenser_unit64,000,000,000DE capacity per ultimate condenser unit.

Tags, states, events, and properties

ConstantValue
SHOW_EVENT_IDutilitycraft:show
HIDE_EVENT_IDutilitycraft:hide
ACTIVE_STATE_IDutilitycraft:active
MULTIBLOCK_CASE_TAG_PREFIXdorios:multiblock.case
MULTIBLOCK_COMPONENT_TAGdorios:multiblock_component
MULTIBLOCK_FAMILYdorios:multiblock
VENT_BLOCK_TAGdorios:vent_block
ENERGY_BLOCK_TAGdorios:energy
FLUID_BLOCK_TAGdorios:fluid
ITEM_BLOCK_TAGdorios:item
GAS_BLOCK_TAGdorios:gas
BOUNDS_PROPERTY_IDdorios:bounds
STATE_PROPERTY_IDdorios:state
ACTIVE_STATE_VALUEon
INACTIVE_STATE_VALUEoff
RATE_SPEED_PROPERTY_IDdorios:rateSpeed
ENERGY_CAP_PROPERTY_IDdorios:energyCap
VENT_BLOCKS_PROPERTY_IDventBlocks
LEGACY_REACTOR_STATS_PROPERTY_IDreactorStats
UPDATE_PIPES_EVENT_IDdorios:updatePipes

All values are read through Multiblock.Constants; they are not independent root exports.

Automatic casing listeners

Importing DoriosCore installs listeners for player breaks and block explosions. When a broken permutation has a tag beginning with dorios:multiblock.case, DoriosCore resolves and deactivates the owning structure. The automatic cleanup requests water removal.

Authoring checklist

  • Give every valid shell block the exact casing tag configured by required_case.
  • Give interior components dorios:multiblock_component.
  • Use DoriosLib link-node blocks for configurable ports and register their groups with registerLinkNodeIO.
  • Declare utilitycraft:active on port blocks so activation can write it.
  • Give the controller helper entity the dorios:multiblock family, show/hide events, required properties, and enough inventory slots.
  • Put addon-specific component calculations in an addon-owned core or onActivate callback.
  • Use handleBreakController() for the controller block and let global casing listeners handle shell damage.

Complete examples

These examples keep custom component formulas in addon-owned scripts and use DoriosCore only through DoriosCore/index.js.