Skip to main content

MultiblockGenerator class

Namespace: DoriosCore · Package: DoriosCore/index.js

MultiblockGenerator extends Generator with multiblock detection, validation, activation, and controller spawning.

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

Definition

class MultiblockGenerator extends Generator

Configuration

interface GeneratorSettings {
required_case: string;
requirements?: Record<string, Requirement>;
entity: MachineEntityConfig;
generator: {
rate_speed_base?: number;
energy_cap?: number;
fluid_cap?: number;
fluid_types?: number;
gas_cap?: number;
gas_types?: number;
};
fillBlocksConfig?: { blockId?: string };
deactivateConfig?: { blockId?: string };
missingEnergyWarning?: string;
ignoreTick?: boolean;
}
SettingDescription
required_caseExact tag accepted on the outer casing. Required for a functioning scan.
requirementsMinimum component counts and player-facing warnings.
entityController helper identifier, inventory, localization, ranges, and fixed resource-type options.
generator.rate_speed_baseAverage generation or transfer rate per vanilla tick.
generator.energy_capInitial energy capacity before structure activation.
generator.fluid_cap, fluid_typesPer-storage liquid capacity and indexed storage count.
generator.gas_cap, gas_typesPer-storage gas capacity and indexed storage count.
fillBlocksConfig.blockIdBlock used to replace air inside the detected bounds during activation. Defaults to water when the config object is present without an ID.
deactivateConfig.blockIdFilled block removed during explicit deactivation.
missingEnergyWarningWhen provided, activation fails if recognized energy components produce zero capacity.
ignoreTickBypasses the shared scheduler.

Constructor

new MultiblockGenerator(block, config)

new MultiblockGenerator(block: Block, config: GeneratorSettings)

Creates a generator runtime for a controller block.

ParameterTypeDescription
blockBlockController block whose helper entity is resolved.
configGeneratorSettingsComplete generator and multiblock configuration.

Unlike MultiblockMachine, validity does not require dorios:state to be on. A generator may need to tick while inactive for displays or control logic.

const generator = new MultiblockGenerator(block, CONFIG);
if (!generator.valid) return;

const active =
generator.entity.getDynamicProperty(Multiblock.Constants.STATE_PROPERTY_ID) ===
Multiblock.Constants.ACTIVE_STATE_VALUE;
if (!active) return;

Add the state check when the addon must not generate or consume resources before successful activation.

Properties and inherited API

PropertyTypeDescription
configGeneratorSettingsFull config on a valid instance.
settingsGeneratorSettingsAlias for config.

The class inherits energy, rate, baseRate, setRate(), processIO(), on(), off(), setLabel(), display helpers, output-mode methods, and destruction behavior from Generator.

Controller lifecycle

defaultOnInteractWithoutWrench(context)

MultiblockGenerator.defaultOnInteractWithoutWrench(context: { entity?: Entity; player: Player }): void

When a controller entity exists, tells the player to use a wrench to scan and activate the multiblock. A missing entity produces no message.

spawnEntity(event, config, callback)

MultiblockGenerator.spawnEntity(event: PlacementEventLike, config: GeneratorSettings, callback?: (entity: Entity) => void): void

Spawns the helper entity half a block below the controller center and initializes generator storage.

ParameterTypeDescription
event.blockBlockPlaced controller block.
event.playerPlayerPlayer whose held controller item may contain preserved resources.
event.permutationToPlaceBlockPermutationPart of the shared placement event contract.
configGeneratorSettingsGenerator, entity, storage, and multiblock settings.
callback(entity: Entity) => voidOptional callback invoked on the following scheduled tick.

The method:

  • sets the configured initial energy capacity;
  • creates every configured liquid and gas manager and assigns per-storage capacity;
  • restores all indexed energy, liquid, and gas lore from the held item;
  • draws the energy display and first liquid display;
  • selects the fluid/gas generator entity event when required;
  • initializes gas IO state;
  • invokes the optional callback.

handlePlayerInteract(event, config, handlers)

MultiblockGenerator.handlePlayerInteract(
event: InteractionEventLike,
config: GeneratorSettings,
handlers?: InteractionHandlers<GeneratorSettings, ActivationContext<GeneratorSettings>>,
): Promise<unknown>

Handles controller interaction and wrench detection.

HandlerDescription
initializeEntity(entity, context)Runs only after this interaction spawned a missing controller entity.
onInteractWithoutWrench(context)Runs when the held item ID does not contain wrench. Defaults to the standard instruction message.
onActivate(context)Runs after structure validation and shared activation. Returning false deactivates and cancels success. It may be asynchronous.
successMessagesMessage array or callback returning messages from the activation context.

When the entity already exists, the method calls activateGeneratorController() directly and returns its result.

activateGeneratorController(event, config, entity, handlers)

MultiblockGenerator.activateGeneratorController(
event: InteractionEventLike,
config: GeneratorSettings,
entity: Entity,
handlers?: Pick<
InteractionHandlers<GeneratorSettings, ActivationContext<GeneratorSettings>>,
"onActivate" | "successMessages"
>,
): Promise<ActivationContext<GeneratorSettings> | undefined>

Runs the complete generator activation flow:

  1. Deactivates existing state using config.deactivateConfig.
  2. Detects the structure using config.required_case.
  3. Validates config.requirements.
  4. Activates ports and applies config.fillBlocksConfig.
  5. Calculates energy capacity from recognized components.
  6. Rejects zero capacity when missingEnergyWarning is configured.
  7. Runs the addon activation callback.
  8. Sends successful activation messages.

Returns the activation context on success or undefined when detection, validation, energy validation, or an addon callback cancels activation.

interface ActivationContext<TConfig> {
block: Block;
components: Record<string, number>;
config: TConfig;
settings: TConfig;
energyCap: number;
entity: Entity;
player: Player;
structure: DetectedStructure;
}

validateRequirements(components, requirements)

MultiblockGenerator.validateRequirements(components: Record<string, number>, requirements: Record<string, Requirement>): Requirement | undefined

Returns the first requirement whose detected count is too low, or undefined when every requirement passes.

Component-derived generator behavior

DoriosCore automatically derives energy capacity only for keys listed in Multiblock.Constants.ENERGY_PER_UNIT. Other detected component keys remain available to onActivate.

Use onActivate to calculate addon-owned values such as:

  • liquid or gas capacity per internal cell;
  • burn rate per heating component;
  • transfer rate as a fraction of energy capacity;
  • passive generation per solar or thermal component;
  • venting and cooling limits;
  • custom dynamic properties used by the tick simulation.
onActivate({ entity, components, energyCap, structure }) {
const gasCapacity = (components.gas_cell ?? 0) * 128_000;
const exhaustRate = (components.vent ?? 0) * 250;

entity.setDynamicProperty("example:gas_capacity", gasCapacity);
entity.setDynamicProperty("example:exhaust_rate", exhaustRate);
entity.setDynamicProperty("example:volume", JSON.stringify(structure.bounds));
entity.setDynamicProperty("dorios:rateSpeed", Math.max(100, energyCap / 10_000));
}

Filled structures

For reactor-like generators:

const CONFIG = {
// ...entity, generator, casing, and requirements
fillBlocksConfig: { blockId: "minecraft:water" },
deactivateConfig: { blockId: "minecraft:water" },
};

ActivationManager.fillBlocks() only replaces air. DeactivationManager.emptyBlocks() reads cleanup bounds from reactorStats.bounds, so an activation callback that relies on this cleanup must persist them:

onActivate({ entity, structure }) {
entity.setDynamicProperty(
"reactorStats",
JSON.stringify({ bounds: structure.bounds }),
);
}

Example: liquid-fueled dynamo

import * as DoriosLib from "DoriosLib/index.js";
import {
EnergyStorage,
FluidStorage,
Multiblock,
MultiblockGenerator,
registerLinkNodeIO,
} from "DoriosCore/index.js";

const BLOCK_ID = "example:biofuel_dynamo";
const CONFIG = {
required_case: "dorios:multiblock.case.example",
entity: {
identifier: "example:multiblock_generator",
type: "fluid_generator",
inventory_size: 5,
fixed_fluid_types: true,
},
generator: {
energy_cap: 1,
fluid_cap: 64_000,
fluid_types: 1,
rate_speed_base: 400,
},
requirements: {
energy_cell: {
amount: 1,
warning: "§r§c[Dynamo] Add at least one energy cell.",
},
fluid_cell: {
amount: 1,
warning: "§r§c[Dynamo] Add at least one fluid cell.",
},
},
missingEnergyWarning: "§r§c[Dynamo] The structure needs energy capacity.",
};

registerLinkNodeIO(BLOCK_ID, {
liquids: {
anyInputIndices: [0],
anyOutputIndices: [],
inputs: [{ id: "fuel", label: "Biofuel Input", indices: [0] }],
outputs: [],
},
});

DoriosLib.registry.blockComponent(BLOCK_ID, {
onPlayerInteract(event) {
return MultiblockGenerator.handlePlayerInteract(event, CONFIG, {
onActivate({ entity, components }) {
const fuel = FluidStorage.initializeSingle(entity);
fuel.setType("example_biofuel");
fuel.setCap((components.fluid_cell ?? 0) * 128_000);
},
successMessages: ["§r§a[Dynamo] Structure activated."],
});
},

onPlayerBreak({ block, player }) {
Multiblock.DeactivationManager.handleBreakController(block, player);
},

onTick({ block }) {
const generator = new MultiblockGenerator(block, CONFIG);
if (!generator.valid) return;

const active =
generator.entity.getDynamicProperty(Multiblock.Constants.STATE_PROPERTY_ID) ===
Multiblock.Constants.ACTIVE_STATE_VALUE;
if (!active) return;

generator.processIO();
generator.energy.transferToNetwork(generator.rate);

const fuel = FluidStorage.initializeSingle(generator.entity);
generator.displayEnergy();
fuel.display(4);
const produced = Math.min(
generator.rate,
generator.energy.getFreeSpace(),
fuel.get() * 45,
);

if (fuel.getType() !== "example_biofuel" || produced <= 0) {
generator.off();
generator.setLabel([
"§r§eDynamo Waiting",
`§r§7Fuel: §f${FluidStorage.formatFluid(fuel.get())}`,
]);
return;
}

fuel.consume(produced / 45);
generator.energy.add(produced);
generator.on();
generator.setLabel([
"§r§aDynamo Running",
`§r§7Stored: §f${EnergyStorage.formatEnergyToText(generator.energy.get())}`,
`§r§7Fuel: §f${FluidStorage.formatFluid(fuel.get())}`,
`§r§7Generated: §f${EnergyStorage.formatEnergyToText(produced)}/t`,
]);
},
});

See the complete Biofuel Dynamo template and Heavy Machinery Power Condenser.

Remarks

  • Check active state explicitly when generation must stop before activation or after deactivation.
  • onActivate is the correct place for addon-specific capacities and component formulas.
  • Register liquid and gas ports with registerLinkNodeIO; UtilityCore owns network discovery and routing.
  • Keep custom behavior in an addon-owned class such as ExampleCore/ExampleMultiblockGenerator.js. Do not modify DoriosCore.