BCCE API Developer Guide · 2.1.0Find a contract ↗
BuildCraft Community Edition

BCCE API

Developer Guide

How to build integrations, manage state and resources, and migrate addons from the original BuildCraft API.

2.1.0API version
32topic chapters
43packages
438API source files

BCCE 8.0.24 · API 2.1.0

Introduction

How to Use This Guide

This guide is intended for developers of BuildCraft Community Edition addons and integrations. It describes the public contracts of API 2.1.0 for BCCE 8.0.24, their use in game systems, and the responsibilities that remain with the addon.

Start with your first integration, then proceed to the game system you need. The built-in reference contains public types and their declarations. The relevant chapters distinguish what the contract provides from what the standard game runtime actually does.

Choose a Reading Path

Your GoalStart HereContinue With
Your first BCCE addonPurpose and Scope of the API; Your First IntegrationLifecycle and Extension Registration; the relevant topic chapter
Migrating an existing addonDifferences from the Original BuildCraft APIMigrating an Addon from the Old API; State Persistence and Schema Evolution
Your own machine or engineMachines: Your Own Block and Runtime RulesMJ; Engines; Recipes; Fuels and Coolants
A pipe, filter, or distributorPipes: Types, Components, and Executable FlowsItem Movement; Fluids and Energy in Pipes; Signals
Automation and resource deliveryOperation Context and PermissionsAutomation Actions and Stripes; Request Logistics
A robot task or construction integrationAreas, Maps, and World AccessRobots; Schematics; Templates and Filler
Dynamic rules and world-save compatibilityState Persistence and Schema EvolutionReloadable Definitions; Diagnostics and Integration Testing
Looking up a specific type or methodThe full API index in this HTML pageThe related chapter and built-in source declaration reference

The chapters are grouped into five parts: integration fundamentals; machines and energy; transport and logistics; world, robots, and presentation; and maintenance. After learning the general rules for registration, operation modes, and context lifetimes, you can read individual chapters independently.

Four Questions to Ask Before Implementing Anything

For any integration, first determine what is registered, who executes the behavior, who owns the resources, and what survives saving the world. These questions are connected, but they are not the same. A machine type describes its structure; a host runs components; ports transfer resources; and a codec handles data. Confusing these responsibilities causes more problems than forgetting a particular class name.

Addon codeTypes · components · handlers
buildcraft.api.v2Registries · services · ports · state
BCCE runtimeHost · gameplay systems · loader adapters
An addon's shared code depends on the public contract. The BCCE implementation connects that contract to the specific game environment.

Terminology

Java contract names and some established English technical terms are used throughout this guide. They have the following meanings.

TermMeaning in This Guide
RuntimeThe running API implementation that provides services and manages their lifecycles.
BackendA concrete executor of a public contract, such as a bridge to standard transport or loader-native storage.
HostThe entity responsible for running the components of a particular game object. It creates and activates them, invokes the specified callbacks, and manages persistence and synchronization within its contract.
Descriptor / type definitionData describing an extension: its stable ID, properties, and references to components or factories. Registering a definition does not create a Minecraft block.
Callback / handlerAn addon method invoked by BCCE at a specific execution point. Its context and permitted operations depend on that point.
PortA resource-access contract for insertion, extraction, and optionally inspection. Obtaining a port does not reserve any resources.
ViewAccess to state through a restricted interface. If a view exposes a port, permitted operations are governed by the port's own contract.
CodecA conversion between a runtime value and its serialized representation, with explicit error reporting.
PayloadThe contents of persisted or transmitted state. The schema belongs to the particular data type.
SnapshotA value captured for a specific read, save, or registry generation. An immutable wrapper does not necessarily make nested objects immutable.
FreezeThe point at which type registries are frozen. Their contents can no longer be changed by ordinary registration.
ReloadA new attempt to assemble and publish a set of definitions for an already registered type. It is not another type-registration step.
ProvenanceInformation about a definition's origin: owner, source, and priority. Used to resolve conflicts and diagnose problems.
Native / built-inAn existing BCCE game mechanic connected through an API adapter.
FixtureA small API consumer used to validate specific contracts. It may demonstrate only registration or one operation, and need not be a complete playable mod.

Reading the Examples

The examples assume that the required Minecraft objects and loader dependencies have already been registered. Complete standalone Java classes are explicitly labeled. Shorter snippets demonstrate an operation in the stated context; they do not invent an enclosing mod, fields, or event handlers merely to show one call.

IDs such as examplemod:* and example:*, fuel parameters, and task names are illustrative. The shared API uses ResourceLocation; chapter one explains the vanilla naming differences in 26.X and the corresponding builds. The examples do not promise binary compatibility between Minecraft versions.

Each topic chapter provides links to source declarations and implementations, which can be opened on this page. Where the public contract allows more than the installed game runtime implements, the practical limitations are explained in the relevant chapter.

I. Integration Fundamentals

01Purpose and Scope of the API

BCCE API is intended for developers of BuildCraft Community Edition addons and integrations. It lets you describe new kinds of pipes, machines, and robot tasks; add recipes and resource-processing rules; connect automation to permission systems; and expose data to interfaces. You should already know Java, the basics of Minecraft modding, and your chosen loader's lifecycle. Knowledge of BuildCraft internal classes is not required.

The supported public Java contracts live in the buildcraft.api.v2 namespace. BuildCraftApi.VERSION is 2.1.0, while the mod version is 8.0.24. The API version identifies the extension interface; the BCCE version identifies the game-mod release. Do not confuse the two.

The API spans 43 packages and 438 Java files, including the root package description. The main guide groups them by developer task. The separate reference maps each source file to its chapter and lets you inspect exact declarations of methods, nested types, and constants. You do not need to read the entire reference in sequence.

What Counts as a Public Contract

A Java class being declared public does not automatically make it part of the API. For example, classes in buildcraft.lib.internal and buildcraft.transport.internal, Tile* implementations, and the internal expression API support BCCE itself. Importing them ties an addon to implementation details. The supported addon boundary is defined by the buildcraft.api.v2 namespace, not by whether the compiler can resolve a class from the JAR.

That boundary has three main elements:

ElementPurposeExample
Registered typeDescribes an extension with a stable ID; exists independently of any particular block in the worldPipeType, MachineType, RobotTaskType
ServicePerforms operations, discovers objects, or manages a domainPipeService, EnergyService, SchematicService
View or contextProvides constrained access to the state of a particular object or operationPipeView, MachineView, WorldAccessContext

A pipe type is not a pipe already placed in the world. A pipe view does not grant permission to rewrite its internal graph arbitrarily. A lookup service does not guarantee that a suitable object exists at the given position. These distinctions underpin all subsequent integrations.

In many areas, BCCE provides a standard implementation of lifecycles and persistence, while the addon supplies components and handlers. Elsewhere, a public type describes a capability whose in-game implementation must still be provided by the addon or a specific module. You must therefore distinguish the existence of a class, a registered ID, and actual working game behavior.

Loader Independence

The shared API contracts do not import Forge, NeoForge, Fabric, or Minecraft client classes. They do not expose Capability, loader-specific FluidStack, DeferredRegister, or loader-specific transaction objects. For example, shared code uses FluidPort and FluidVolume, while the platform adapter bridges to native storage.

The API is still coupled to Minecraft. Its signatures include ItemStack, BlockPos, Direction, Level, resource identifiers, and other vanilla types. Their names and methods, the Java toolchain, mod metadata formats, and loader integration points can differ between game versions. The shared API design therefore simplifies maintenance across builds but does not promise a single binary JAR for every Minecraft version.

BCCE 8.0.24 supports the following build configurations:

MinecraftLoaderJavaBuild family
1.19.2Forge17old
1.20.1Forge17old
1.21.1NeoForge211.21.X
1.21.11NeoForge211.21.X
26.1.2NeoForge2526.X
26.2NeoForge2526.X
26.3NeoForge2526.X

This table lists BCCE 8.0.24 build configurations. The presence of preparatory Fabric directories does not imply Fabric support: those targets are absent from targets.properties. Before releasing an addon, confirm that a build exists for its intended Minecraft version and loader.

Shared Java code uses net.minecraft.resources.ResourceLocation. In the Minecraft 26.X family, the corresponding vanilla type is called Identifier. BCCE adapts shared sources to the target platform; external addon code must use the Minecraft names and signatures of its own target version.

Availability Checks

ApiVersion indicates the contract version. ApiFeatureSet lets you check a specific capability and its level. ModuleService reports which modules are physically loaded. Finally, ApiRuntime.service(...) returns a specific available service.

A feature level is not a completion percentage or a Minecraft version. For example, BuildCraftFeatures.PIPES at level 2 declares a particular transport API surface. Working with a pipe still requires the transport service, a loaded world, and a suitable object. Further limits of individual services are covered in their chapters.

For required dependencies, use requireService or BuildCraftApi.service so missing components are detected explicitly. For optional integrations, use runtime.service(key) and handle an empty Optional. Checking a feature is useful before using newer contracts, but it is no substitute for looking up the service.

For example, the Builders module installs the SCHEMATICS and LIBRARY services during bootstrap. The presence of a runtime feature does not mean a module has already installed its service at an arbitrary early point. This is ordinary modular initialization before freeze, not a promise that service will return a deferred result.

Source Code

Sources: common.propertiesS020; targets.propertiesS021; package-info.javaS286; BuildCraftApi.javaS071; BuildCraftFeatures.javaS072; BuildCraftApiRuntime.javaS536.

I. Integration Fundamentals

02Differences from the Original BuildCraft API

The main change in BCCE is the boundary between addon dependencies and BuildCraft internals. The original API offered integration points alongside base game-object classes, loader capabilities, network callbacks, and global registries. BCCE API v2 defines a separate contract layer: extension descriptors, services, restricted object views, explicit operations, and state formats.

This comparison uses the original BuildCraft/BuildCraftAPI branch 8.0.x-1.12.2 (commit b12940daa173f6b3fa7482eb7fcc2f210a018208). It applies to that original API lineage and should not automatically be generalized to every BuildCraft release. BCCE combines the BC8 foundation with restored BC7 mechanics, including robots and construction.

What the Original API Already Had

Original BuildCraft had an extensive extension system: pipe registration, behavior factories, MJ interfaces, triggers and actions, schematic adapters, and recipe and fuel integrations. PipeDefinition already used ResourceLocation; IMjReceiver already supported simulation; MjAPI already used microjoules and supported RF conversion; and the registry package already included data-reload contracts. None of these ideas should be presented as having originated in BCCE.

What changed is how these ideas fit together and what constitutes a supported addon dependency. Original PipeFlow, for instance, exposed pipe references, NBT, network payloads, Forge ICapabilityProvider, and gameplay methods together. BCCE separates those concerns into shared contracts and provides domain-specific tools for each.

Comparison of Integration Models

AreaOriginal API, selected lineageBCCE API 2.1
Entry pointMultiple static holders: PipeApi, BuildcraftFuelRegistry, StatementManager, MjAPIBuildCraftApi and typed service and registry keys
Loader dependencyForge capabilities and loader types appear in public contractsShared signatures use API-owned ports and vanilla types; loader adapters reside in the implementation
Pipe extensionFactories and subclassing public PipeBehaviour / PipeFlow, with direct ties to the holder and networkPipeType, components, rules, attachments, and separate definitions for custom flows/behaviors
RegistriesSome domains expose mutable maps and replaceable static servicesType registries with stable IDs, owners, aliases, and freeze; services have domain-specific policies
StateDomain-specific NBT formats; robot API associates a registered name with a Java class instantiated using reflectionPersistentType, codecs, schema migration, and typed/unknown payloads where the host supports them
Resource transferDifferent historical result formats, boolean simulate, and loader-specific interfacesShared OperationMode, explicit item/fluid/MJ transfer results, and a separate external-energy contract
Automation contextFakePlayer provider and context tied to individual game objectsAutomationActor, operation target and kind, and a permission service recording decision provenance
StatementsString tags, generic parameter maps, readers, and runtime interfacesRegistered TriggerType, ActionType, and ParameterType, schemas, and dedicated presentation contracts
Client-side dataTextures, network callbacks, and some client dependencies intermingled with semanticsDeclarative presentation contracts and typed channels; client implementation remains in BCCE
Version supportStrong coupling to the relevant Minecraft/Forge APIShared contract core across targets, with separate Minecraft and loader adaptations
DistributionOfficial API maintained in a separate repository with its own historyAPI included in the ordinary BCCE JAR; no separate SDK/API JAR is published

The observation about maps applies to genuinely exposed original holders, not every class in the old API. Likewise, do not assume every BCCE method named register follows freeze rules: the permission registry, for example, has its own behavior. The domain chapters clarify such distinctions.

Composition and Object Access

In the original model, subclasses obtain many features through a base runtime class. This is convenient when extending a particular implementation, but couples the addon to its lifecycle, fields, and state representation. Porting to another Minecraft version may require porting those dependencies as well.

In BCCE, a type describes an extension's composition, a component adds a specific behavior, and a host manages the instance. Allowed operations are exposed through an API view and context. For example, an item filter only needs to know the offered stack and eligible directions; it does not need access to the whole TilePipeHolder.

For deeper customization, BCCE provides PipeFlowDefinition and PipeBehaviourDefinition. These are public factories for custom API components, not permission to subclass old transport internals. Choose the narrowest contract that expresses your intended behavior: a connection rule, a routing component, an attachment, or a complete custom flow.

This separation comes at a cost: a type descriptor does not automatically supply missing capabilities. If a built-in machine type has only configuration components and no executable factory, copying its definition does not create a functioning new quarry. If a generic signal channel is registered but the backend handles only classic wires, a new ID will not create its own network. These limits matter when selecting an integration point.

Transfer Results Have Different Semantics

When migrating MJ code, compare return-value semantics carefully. The old IMjReceiver.receivePower(offered, simulate) returned the excess that was not accepted. BCCE's MjTransferResult.transferred() reports the accepted amount, while remainder() reports what is left.

Simply renaming a method can therefore reverse your energy accounting. Previously, the accepted amount was offered - excess; the new API exposes it directly. ExternalEnergyPort.insert returns the accepted quantity in the external system's units. Normalize these results at adapter boundaries and test zero, partial, and complete transfers.

Both systems support simulation, but BCCE also makes limitations of generic policies explicit. The default ALL_OR_NOTHING method refuses to execute without an appropriate atomic backend. This distinguishes a real port guarantee from two independent calls in sequence.

Persisted Data Is Independent of Java Class Names

In the original robot API, saved data can contain a registered AI/resource name. RobotManager then locates the corresponding Java class, instantiated through reflection by AIRobot or ResourceId. Legacy class-name fields also participate in migration. Thus the original model did have named registration, but restoration was still tied to specific classes and constructors.

In BCCE, a public persistent type describes a codec and schema version. An addon can rename its Java class while keeping the same ID and data format. If the format changes, it supplies a migration. For unknown types, a supporting host retains the raw payload instead of instantiating an arbitrary class from saved data.

These guarantees are domain-specific: check how the actual host uses aliases, migrations, and unknown state. The existence of a shared PersistentType does not prove that every domain automatically performs all these steps.

Shared API and Actual Guarantees

BCCE declares buildcraft.api.v2 as the supported namespace and exposes a contract version. This separates addon code from mod implementation details: new integrations should depend on public types rather than internal classes, client code, or loader-specific APIs.

However, an identical API version does not guarantee identical edge-case behavior on every target. Backends for different Minecraft families, including 26.X, vary in callback exception handling. A feature flag alone cannot promise full arbitrary-transport support, automatic permission checks in every method, or atomic writes across multiple worlds.

The practical benefit of this new layer is more explicit, localized dependencies. An addon depends on a clear contract for resources, state, and operations; adaptation to Minecraft and its loader remains a separate concern. Actual feature coverage depends on the hosts and consumers discussed in the domain chapters.

Source Code

BCCE sources: package-info.javaS286; BuildCraftApi.javaS071; PipeFlowDefinition.javaS363; MjTransferResult.javaS153; PersistentType.javaS307; forge-target.gradleS022.

Source Code

Original API sources: official repository at the pinned commit; PipeApi.javaS597; PipeFlow.javaS599; PipeDefinition.javaS598; IMjReceiver.javaS591; MjAPI.javaS592; StatementManager.javaS596; AIRobot.javaS594; ResourceId.javaS595; BuildCraftRegistryManager.javaS593.

I. Integration Fundamentals

03Your First Integration

Adding fuel is a good starting point: the extension has a stable ID, numeric parameters, a clear consumer, and a verifiable outcome. This example assumes the addon has already registered the examplemod:biofuel fluid in Minecraft. The BCCE API assigns fuel properties to it; creating the fluid, block, and bucket still uses standard Minecraft content registration.

Adding the Dependency

Public API classes are included in the regular BCCE JAR. No separate SDK/API JAR or Maven artifact named buildcraft-api-v2-* is published. Add BCCE as a development dependency for your target Minecraft version and loader.

Add the appropriate BCCE build to your loader's development environment and declare dependencies on the modules you use. The core API requires buildcraftlib; actually using fuel in gameplay also requires buildcraftenergy. Loader module IDs differ from public API module IDs: for example, BuildCraftModules.LIB has the value buildcraft:lib. Choose the dependency syntax in mods.toml or neoforge.mods.toml according to your loader version.

Do not bundle a copy of buildcraft.api.v2 in your addon's released JAR. BCCE provides these classes at runtime. Duplicating the API can create incompatible copies of types or break runtime discovery via ServiceLoader.

Shared addon code should compile without access to internal BCCE packages. You can test this boundary with a separate source set that depends only on the public API contracts and necessary Minecraft dependencies.

Registering Fuel

The following class contains the shared part of the integration. The service is passed as a parameter, so the same code can be called by a loader adapter or an isolated test.

java
package example.bcce;

import buildcraft.api.v2.energy.MjAmount;
import buildcraft.api.v2.fuels.EnergyFluidService;
import buildcraft.api.v2.fuels.FluidSelector;
import buildcraft.api.v2.fuels.FuelProfile;
import buildcraft.api.v2.reload.DefinitionProvenance;
import java.util.Objects;
import net.minecraft.resources.ResourceLocation;

public final class BiofuelIntegration {
    private BiofuelIntegration() { }

    public static void register(EnergyFluidService fuels) {
        ResourceLocation fluidId = id("examplemod:biofuel");
        ResourceLocation profileId = id("examplemod:biofuel_combustion");

        FuelProfile profile = FuelProfile.clean(
            FluidSelector.fluid(fluidId),
            MjAmount.ofMj(4).microMj(),
            12_000
        );

        fuels.register(profileId, profile,
            new DefinitionProvenance(
                "examplemod", "code:biofuel", 0
            ));
    }

    private static ResourceLocation id(String value) {
        return Objects.requireNonNull(ResourceLocation.tryParse(value));
    }
}

These numbers illustrate game balance: 4 MJ per tick and 12,000 burn ticks per bucket. FuelProfile uses micro-MJ for power, hence MjAmount.ofMj(4).microMj(). Passing the number 4 directly would mean four millionths of an MJ per tick. Fully using this profile yields 48,000 MJ of energy per bucket. This calculation describes the profile; actual engine operation also depends on its state and mode.

The fluid ID and profile ID are intentionally different. One identifies a Minecraft resource; the other identifies a BCCE rule. Stable names matter for diagnostics, conflict resolution, and maintaining integrations. Use FluidSelector.exact to match components exactly, or FluidSelector.tag for a group of fluids.

Retrieve the service from a loader adapter as follows:

java
BiofuelIntegration.register(
    BuildCraftApi.service(BuildCraftServices.ENERGY_FLUIDS)
);

This is a call-site snippet requiring imports of BuildCraftApi and BuildCraftServices. Register once during addon initialization. For ordinary API registration on existing Forge/NeoForge targets, an appropriate point is work scheduled with enqueueWork during common setup, before BCCE freezes registries at load-complete. Register Minecraft blocks, items, and entities earlier through the loader's normal registration events. The API does not define a universal RegisterBCCEAddonEvent.

Verifying the Result

After registration, the profile should be found through EnergyFluidService.findFuel(...), while its ID and provenance should appear in the service snapshot. Merely constructing a FuelProfile says nothing about successful registration. Likewise, a successful API registration does not prove that the Minecraft fluid was created.

For a gameplay test, launch the correct target build, place the fuel in a combustion engine, and test it under load. A fluid with the right ID but unsupported components or an incorrect platform adapter may never match its profile. Check the entire chain: Minecraft resource → platform port → FluidVariant → profile → consumer.

The existence of a profile codec does not imply automatic loading of arbitrary JSON files from datapacks. This example uses the actual public register method; you can implement your own reloadable definitions through DefinitionReloadService.

Source Code

Sources: forge-target.gradleS022; neoforge-target.gradleS023; BCLibRegistries.javaS532; EnergyFluidService.javaS195; FuelProfile.javaS198; FluidSelector.javaS196; MjAmount.javaS139; ApiV2FixtureAddon.javaS005.

I. Integration Fundamentals

04Lifecycle and Extension Registration

Registration tells BCCE which extension types exist. Execution operates on instances of those types in a particular world. Keeping these stages separate allows ID conflicts and references to be checked in advance, followed by a stable registry layout that does not change on every tick.

Obtaining the Runtime

BuildCraftApi.runtime() discovers the implementation through Java ServiceLoader and caches the result in a private holder. The API expects exactly one provider. If none is found, it returns ApiRuntime.empty() with version 0.0.0 and empty services and registries. Discovery fails if multiple providers exist or the runtime's major version differs from the facade's major version.

There is no public runtime installer. An addon must not provide its own service-provider descriptor for ApiRuntime or invoke the internal bootstrap. For tests, pass the runtime to your own code as a parameter instead of replacing the game's singleton.

If BCCE is an optional dependency, the loader must determine that the mod is present before loading an integration class that imports BCCE. An empty runtime cannot help when the API classes themselves are missing: the JVM may fail to resolve those references before the method is ever called.

Bootstrap Phases

PhaseMeaning for Integration
DISCOVERYThe runtime has been discovered; the module set is still being assembled
TYPE_REGISTRATIONPreparing extension types, codecs, and handlers
CONTENT_REGISTRATIONRegistering content and connections between types
FROZENType registries are frozen; new types and aliases are not accepted
RUNNINGOperations on game instances and reads of registered definitions
STOPPINGThe shutdown stage defined by the contract

BCCE controls phase transitions. Its runtime forbids skipping phases or moving backwards. At freeze, it first validates aliases in all ApiRegistry instances and external module dependencies; only then does it freeze registries, modules, reload types, and world-event handlers. A preflight validation error should not leave half the registries frozen.

BCCE enters FROZEN and RUNNING at load-complete, before a world has started. RUNNING therefore does not mean a Level is available or that the server has loaded the desired dimension. The enum includes STOPPING, but this does not by itself create a public addon shutdown callback.

The distinction between TYPE_REGISTRATION and CONTENT_REGISTRATION helps organize code. ApiRegistry itself enforces the freeze boundary, not a separate phase for each entry type. Do not depend on constructor ordering across modules, and do not wait until load-complete to register: BCCE is already finishing its freeze there.

Type Registries

Obtain a registry using a key from BuildCraftRegistries:

java
ApiRegistry<PipeType> pipes = runtime.requireRegistry(
    BuildCraftRegistries.PIPE_TYPES
);
pipes.register(type.id(), type, () -> "examplemod");

Here, runtime and type are previously obtained objects. This pattern is shared by many domains, but constructing PipeType and its gameplay components is explained in the pipes chapter.

Prefer the overload with RegistrationContext: its owner() is stored in RegistryEntry to explain where the entry came from. The shorter overload records an owner of unknown in the current backend. In a normal registry, owner is metadata, not automatic proof of namespace ownership. Some domain services check the namespace against the owner separately.

Duplicate IDs are forbidden even if the new value appears equivalent to the old one. Repeated register calls are not updates. This also applies when an entry ID conflicts with an alias. Do not hide duplicates with an “if ID exists, skip” check: that can silently accept another addon's extension as yours.

For reads, prefer entry(id), which returns Optional<RegistryEntry<T>>. One important exception to the API's usual conventions: get(id) returns null when the entry is absent. values() returns an immutable list of values; entries() returns an unmodifiable view of entries. Before freeze, the latter may reflect later registrations; after freeze, the set is stable. An unmodifiable collection does not make arbitrary values stored inside it immutable.

Aliases and Identity

An alias resolves an old ID to a new canonical ID. Register it before freeze using registerAlias(oldId, newId, registrationContext). The third argument is a RegistrationContext, for example () -> "examplemod". The target may be added later during the same registration stage. By freeze, the alias chain must terminate at an existing entry; cycles and missing final targets are rejected.

An ID alias and a payload migration solve different problems. The former renames a type; the latter changes the structure of its saved data. Renaming examplemod:old_filter to examplemod:filter does not automatically turn the old threshold field into a new data structure.

Do not derive stable IDs from enum ordinals, Java class names, random per-launch UUIDs, or collection iteration order. IDs are part of saved data and relationships between extensions. Use separate, meaningful IDs for distinct entities: a type, component, recipe, handler, and synchronization channel may all belong to one addon but serve different purposes.

Services and External Modules

BuildCraftServices defines typed service keys. Public ApiRuntime supports looking them up, but not installing or replacing them. A ServiceKey<T> does not create a backend on its own. Passing services into your components through constructors or parameters makes dependencies explicit and simplifies testing.

An addon that is physically loaded can declare a module via ModuleService.registerExternal. In the current backend, the ID namespace must match RegistrationContext.owner(), the version must be nonblank, and loaded must be true. Freeze checks required-module existence and dependency cycles. This declaration complements loader dependency metadata but does not replace it: it cannot load a missing JAR.

Not every method called register belongs to ApiRegistry. In particular, PermissionServiceRegistry manages its own ordered collection and does not check the shared freeze boundary in the current implementation. By contrast, registration in WorldEventService, ModuleService, and DefinitionReloadService explicitly closes. Follow each service's actual contract rather than assuming that all registries behave alike.

Source Code

Sources: ApiRuntime.javaS069; ApiLifecycle.javaS068; ApiRegistry.javaS405; ModuleService.javaS285; BuildCraftApiRuntime.javaS536; ApiRegistryImpl.javaS563; ModuleServiceImpl.javaS552; BCLibRegistries.javaS532.

I. Integration Fundamentals

05Resources and Transfers Between Storages

A port describes the available operations on a resource: insertion, extraction, or both. It need not represent a separate block and does not expose loader-native storage. A single port may represent one inventory side, an allowed part of a machine, or an adapter for another mod.

Resources are divided into items, fluids, MJ, and external energy. They share the concepts of operation modes and amounts actually transferred, but use different value and result types. Do not flatten them into an untyped number with no unit or resource identity.

Simulation and Execution

OperationMode.SIMULATE checks an operation without modifying storage state. EXECUTE performs the actual operation. ItemPort and FluidPort implementations must honor this contract: simulation must not consume supplies, occupy capacity, create items, or mark storage dirty.

Simulation does not create a reservation. Free capacity, resource identity, or permissions can change between simulation and execution. The EXECUTE result is therefore authoritative and must be used for actual debiting and accounting.

For example, a port may accept 1,000 mB during simulation but only 600 mB during execution. The source must lose only the 600 mB actually transferred; the remaining 400 mB stays with the operation's owner. A successful simulation does not authorize removing an entire bucket.

SIMULATE is not a universal world transaction. It does not combine arbitrary extraction from one port with insertion into another. Higher-level services may also perform housekeeping: for example, checking request-logistics delivery can update timeouts. Read the specific operation contract rather than assuming all high-level operations follow low-level port semantics.

Items

ItemPort.insert(ItemStack, mode) returns ItemTransferResult, where transferredCount() is the accepted item count and remainderCount() is the difference between offered and accepted counts. For extraction, the result contains the actual extracted stack. transferred() returns a copy; modifying it does not alter the result.

Item counts use int. Negative requests and results exceeding the request are rejected. A zero transfer is an ordinary response from a full, incompatible, or unavailable port, not necessarily an error. completed() means the quantity request was fulfilled; for a zero request it may be true while movedAnything() == false.

ItemMatcher tests a stack. Built-in matchers any() and none() compose with and, or, and negate. examples() supplies optional representative stacks for interfaces; an empty list does not mean the matcher can never succeed. More complex rules, tags, and exact component matching are covered in the lists and recipes chapters.

When slot positions matter, a port may additionally implement SlottedItemPort, exposing slots(), stackInSlot(slot), and the effective filter(slot). The filter is an admission rule and may be more complex than a single item example. Slot access is optional: ordinary ItemPort need not expose slots. A stack returned for inspection must not be used to bypass transfer methods; the slot-view contract does not make it a mutation mechanism.

Insertion with ItemTransferPolicy.PARTIAL permits partial acceptance. The default ALL_OR_NOTHING implementation has a crucial behavior: successful simulation does not lead to execution. In EXECUTE, it conservatively transfers nothing unless the specific port overrides it with a genuine atomic guarantee. This prevents a false all-or-nothing promise on a port that supports only partial insertion.

The default extraction overload with minCount and maxCount works similarly. With a positive minimum, the generic method does not execute extraction without a special implementation. Simulating the minimum and hoping execution behaves the same is insufficient. A zero minimum permits ordinary partial extraction.

Item Ownership During Transfers

A practical way to connect two arbitrary storages is an intermediate machine-owned buffer. The machine first takes the extracted resource into its own buffer. It then offers the contents to the destination and deducts only what was actually transferred. Unaccepted items remain in the buffer and are persisted with the machine.

The following snippet assumes a buffer exclusively owned by the caller for the duration of the operation:

java
ItemStack offered = ownedBuffer.copy();
ItemTransferResult result = destination.insert(
    offered, OperationMode.EXECUTE
);
ownedBuffer.shrink(result.transferredCount());

There is no extraction from an external source in this snippet. ownedBuffer already belongs to the sender; a copy is offered, and the sender deducts according to the destination's result. The caller must prevent re-entry and concurrent modification of the buffer. If the buffer changes, its owner must also mark it dirty and persist it.

The pattern “extract everything, insert whatever fits, discard the remainder” is wrong. Re-inserting the remainder into the source is not a universal rollback either: the source may have stopped accepting it. Reliable integrations define an owner for every item at every stage.

Fluid Identity

Fluids are represented by several small concepts:

ConceptMeaning
FluidVariantFluid ID and immutable component data
FluidComponentPayloadFormat ID and canonical bytes of opaque components
FluidAmountNonnegative quantity in mB, stored as long
FluidVolumeA variant and quantity, or a special empty volume
FluidMatcherRule selecting acceptable variants
FluidTransferResultRequested quantity, transferred volume, and remainder

FluidVariant.sameFluid compares IDs only. sameVariant and equals also compare the component format and bytes. Two fluids with the same ID may be different variants. If components encode composition, temperature, or other resource properties, matching by ID alone loses that identity.

The public layer does not interpret opaque bytes as NBT or JSON. Their format belongs to the adapter or addon that produces them. FluidComponentPayload copies byte arrays both on construction and on reading. The producer must guarantee byte canonicalization: semantically identical data must serialize identically when it needs to compare as the same variant.

stableHash64() computes a stable hash from the ID, format, and bytes. It is useful for indexes and diagnostics, but cannot replace equality checks: matching hashes alone do not prove two resources are identical.

Creating FluidVariant.of(id) does not check whether a fluid exists in Minecraft. The FluidService contract provides fluid registration checks, a matcher context, and a codec, but BCCE 8.0.24 has no standard BuildCraftServices.FLUIDS key or universal backend installed for it. Use your domain's service or your own platform adapter; do not reference a nonexistent key.

Volumes, Filters, and Results

One bucket equals 1,000 mB. FluidAmount.plus and minus check overflow, and a negative result is invalid. FluidVolume.of(variant, 0) normalizes to an empty volume without a variant. Do not call requireVariant() on an empty result, and do not turn an empty volume into a nonempty one without specifying the variant.

FluidMatcher.exact matches the precise variant, fluid(id) matches all variants with an ID, and tag(id) matches tagged fluids using the supplied FluidMatchContext. Tags are resolved for each operation rather than captured as a fixed list when the matcher is constructed. Custom matchers must respect the actual context and must not modify the world.

On insertion, FluidTransferResult.ofInsertion(offered, accepted) checks that acceptance never exceeds the offer. On extraction, ofExtraction(requested, extracted) checks the upper bound. The result contains fluid actually transferred, not what remains in the source. remainderAmount() returns the numerical difference. FluidPort has no generic all-or-nothing overload.

FluidVariant.CODEC converts a variant to its FluidVariantData representation. It is a value codec, not a ready-made file loader or a converter for arbitrary loader-native FluidStack values. Conversion between the API and the loader belongs at the platform boundary.

Platform Units and Containers

FluidUnitConverter calculates an exact rational conversion between mB and a platform's units per bucket. UnitConversionResult expresses the result as whole + remainder / divisor; exact() checks whether a remainder exists. If the platform uses finer units, dropping the remainder on every transfer creates systematic resource loss. Preserve it in an owner-controlled buffer or reject inexact conversions.

PlatformServices supplies optional ItemTransfer, FluidTransfer, and EnergyTransfer adapters. Their find(level, pos, side) methods locate a port on a block side. A missing platform adapter and a missing port at a position are different reasons for an empty Optional. The direction refers to the queried or receiving block's side; when querying a neighbor, this is normally the side facing the source.

Directly locating a platform port does not automatically supply an actor or check permissions. If an operation runs on behalf of a machine and must honor permissions, use the appropriate world-access context or an explicit permission check. Nor should a block port be cached indefinitely across chunk unloads or BlockEntity replacement.

FluidItemAdapter lets you declare which fluid an item represents. FluidItemService takes the first applicable nonempty result in adapter registration order. These contracts describe contents; they do not provide a universal action for emptying buckets or capsules. Consuming the item and returning its empty container require separate logic, or the same item could yield its contents repeatedly.

Source Code

Sources: OperationMode.javaS075; ItemPort.javaS217; ItemTransferResult.javaS219; FluidPort.javaS181; FluidVariant.javaS185; FluidVolume.javaS187; FluidUnitConverter.javaS184; PlatformServices.javaS388; FluidItemServiceImpl.javaS546.

I. Integration Fundamentals

06Operation Context and Permissions

Automation must identify on whose behalf it operates and what object it changes. The API uses AutomationActor, WorldOperationContext, and PermissionService for this purpose. One integration can then serve a quarry, robot, or other machine without depending on BCCE's internal FakePlayer.

Actor Identity

AutomationActor distinguishes players (PLAYER), machine owners (MACHINE_OWNER), the system (SYSTEM), and unknown owners (UNKNOWN). The first two require a player UUID. Machines and systems have a sourceId that identifies the automation source using a stable ID rather than a Java class name. ActorService provides factories for these identities.

The player name is supplemental data. An actor's equality with a UUID does not change when a player changes their name; kind, UUID, and source are considered. This is useful for validating ownership after loading saved data. OwnedView only exposes an owner's UUID for reading; it is not itself a permission store.

An actor describes the request's provenance. Constructing AutomationActor.system(...) does not grant elevated permissions, and creating machineOwner(...) does not authenticate the supplied UUID. These values must come from trusted placed-machine state or a verified player action. Some current backend domain callbacks pass unknown; such a context must not automatically be treated as the machine owner.

The Complete Permission Request

WorldOperationContext contains the actor, level and dimension, origin position, target, operation kind, mode, and optional reason ID. WorldOperationTarget may identify a block, an entity UUID, both, or neither. Choose the exact WorldOperationKind: breaking, placing, interacting, inventory access, draining fluids, and force-loading chunks have different implications for world protection.

Coordinates in this context are copied as immutable BlockPos values. However, a Level reference still points to a live world. An immutable wrapper stabilizes the request description, not the world itself; it does not make arbitrary level.setBlock calls safe.

ExtensionContext serves another purpose: it publishes explicitly available typed views through ContextKey<T>. For example, a callback can obtain PIPE, MACHINE, ITEM_PORT, or ROBOT from BuildCraftContextKeys. An empty Optional means that view is not published for this operation. Do not cast a view into an internal BlockEntity to circumvent its boundaries.

How Decisions Are Combined

PermissionService.decide(context) returns PermissionDecision with ALLOW, DENY, or PASS. Allow and deny decisions require an authority ID; a reason explains the outcome. PASS means the provider offers no decision, not explicit permission.

PermissionServiceRegistry sorts handlers by descending priority and then string ID. The first DENY stops evaluation. The first ALLOW is remembered, but later handlers may still deny the action. A high-priority allowing provider therefore cannot bypass another provider's protection. If everyone returns PASS, the aggregate remains PASS, and the calling domain applies its policy.

PermissionServices.compose combines supplied services in collection order, with the same terminal DENY rule, but does not sort them. Both forms require non-null decisions. Checks must be observational in either mode: a permission decision must not itself break a block or consume an item.

Repeat necessary checks against current state when executing. A decision obtained during preview is not a long-lived authorization. An implementation accepting an actor does not necessarily call a permission service itself, as documented for schematic placement, gate programs, and some attachment operations.

World Events

WorldEventService allows registering handlers before freeze for BLOCK_INTERACT, BEFORE_BLOCK_PLACE, BLOCK_PLACED, and PIPE_PLACED. The first two are cancellable; the latter two notify about completed actions. Returning DENY after placement does not undo the block.

Events contain typed WorldEventContext snapshots. Handlers run by priority and ID on the server thread. In the current shared implementation, cancellable events are denied when requested on the client, off the server thread, or for an unloaded position. Handler exceptions also deny cancellable events; for notifications they cannot undo already completed operations. When porting an integration, verify the selected family backend because the public contract and precise exception-isolation behavior can differ among implementations.

This is a BCCE event bus with specific invocation points, not a subscription to every vanilla or loader event. If another mod directly changes a block, merely installing WorldEventService does not intercept that change.

Source Code

Sources: AutomationActor.javaS289; WorldOperationContext.javaS297; PermissionServices.javaS295; ExtensionContext.javaS119; BuildCraftContextKeys.javaS117; PermissionServiceRegistryImpl.javaS553; WorldEventServiceImpl.javaS557.

I. Integration Fundamentals

07State Persistence and Schema Evolution

Extension state should survive world reloads, Java class renames, and temporary absence of an addon. The API separates type identity from implementation: persisted data holds a stable ID and schema version, while a codec transforms the payload.

Three Layers of Data

PersistentType<T, P> describes a type: its ID, current schema version, ApiCodec<T, P>, aliases, and migration steps. T is an addon's runtime value, while P is its serialized representation. The public codec is not tied to NBT: concrete domains may use NBT, JSON, OpaqueData, or another supported representation.

EncodedPayload<P> is the saved envelope containing typeId, schemaVersion, and payload. PayloadResolution<T, P> resolves it to a KnownPayload with a runtime value or an UnknownPayload containing original data. Constructing a PersistentType does not register it in some universal global persistence service. A domain retrieves its persistent type through its own contract, such as a component or robot task type.

ApiCodec.decode and encode return CodecResult. Represent expected format errors with failure rather than returning null. valueOrThrow() is convenient when an error should truly abort an operation; while loading saved data, you usually need to handle successful(), errors, and unknown payloads.

Schema Versions

A schema version belongs to one persistent type. It need not match API 2.1.0, BCCE 8.0.24, or the Minecraft version. Increment it when old payloads cannot be read correctly using the new format without conversion.

SchemaMigration<P> defines a deterministic fromVersion() → toVersion() transition. The builder requires nonnegative starting versions, forward movement, no transition beyond the current schema, and at most one transition from each source version. These checks do not prove that every historical version has a path; the developer must supply the necessary migrations.

If schema 1 stored a single limit, while schema 2 stores a limit and mode, the migration must explicitly choose the new mode's value. Never silently interpret old bytes as a new structure just because the payload length happens to match. Validate numeric ranges, lengths, allowed IDs, and trailing data where relevant.

Aliases in PersistentType and a domain's ApiRegistry are not interchangeable. Whether they are used depends on the real host. For example, robot task saves use registry resolution of persistent types, whereas snapshot-element loading starts with SNAPSHOT_ELEMENT_TYPES.get(storedTypeId). To rename such an element, register the alias specifically in BuildCraftRegistries.SNAPSHOT_ELEMENT_TYPES using registerAlias(oldId, newId, registrationContext). That alias participates in the initial lookup; PersistentType.alias(...) alone is insufficient. The new type's codec must return an element with the correct current typeId().

Unknown Data

UnknownPayloadReason distinguishes missing types, newer schemas, missing migrations, migration failures, and decode failures. None of these unknown values may execute as ordinary components. Their purpose is to preserve the original data until it can be understood again.

When saving again, retain the original storedTypeId, storedSchemaVersion, and raw payload. Replacing an unknown component with an empty instance of your own type destroys the possibility of recovery. UnknownPayload.toEncodedPayload(copier) helps reconstruct an envelope with copied data.

Safe storage of generic payloads depends on copying. UnknownPayload and EncodedPayload cannot universally deep-copy an arbitrary P. PayloadCopier.immutable() is suitable only for truly immutable representations. Mutable NBT or collections require a copier that prevents shared mutable references. OpaqueData already copies its bytes on construction and access, making it useful for small component states.

This model supports data recovery but does not make every save a game-wide transaction. Coordinated changes to two worlds or BlockEntities can still be written at different times. Validate state after loading, and explicitly define what happens to incomplete operations.

Practical Implementation Order

First identify the minimal state you really need to save. Do not store Level, BlockEntity, services, callbacks, or views in the payload. Recreate them from IDs and context after loading. Then implement the codec and known migrations, attach the definition to the domain type, and verify the host's behavior.

Useful tests include a normal round trip, an older schema, an unknown ID, a newer schema, and saving again while the addon is absent. Also check that modifying a returned copy of an array or NBT value cannot change stored state. Stay within any payload-size limits enforced by the host.

Source Code

Sources: ApiCodec.javaS300; CodecResult.javaS301; PersistentType.javaS307; SchemaMigration.javaS308; UnknownPayload.javaS309; PayloadCopier.javaS305; OpaqueData.javaS304; PersistenceRegistrySnapshot.javaS562.

I. Integration Fundamentals

08Reloadable Definitions

Extension types are normally frozen at startup, but configuration values and rules may change during execution. DefinitionReloadService defines a separate model for this: a registered data domain, incoming definitions, a validated candidate, and an atomically published snapshot.

Reload is not another ApiLifecycle phase. It can happen repeatedly after freeze. On failure, the previous snapshot remains active and the caller receives diagnostics.

Domain, Decoder, and Sources

ScriptableDefinitionType<T> specifies a domain ID, Java result type, and default decoder. Register it before freeze with an owner matching the ID's namespace. Additional named decoders are selected through the input data's string type field. If absent, the default decoder is used. If present but not registered, the definition is invalid.

Definitions are supplied as ScriptedDefinition, in three forms: VALUE, ALIAS, and DISABLED. A value has a structured map, an alias has a target ID, and a disabled record must have a reason. A record cannot be both a value and an alias.

Nested data is restricted to JSON-like values: strings, numbers, booleans, null, lists, and maps with string keys. It is defensively copied, and nesting deeper than the limit of 48 is rejected. A decoded T must also be immutable or manage its own state correctly: an immutable outer snapshot cannot freeze an arbitrary user-provided object.

ReloadContributor resupplies code-owned definitions for each attempt; its output does not accumulate across generations. DefinitionProvenance contains owner, source, and priority, explaining where a value came from and why it prevailed.

A Minimal Rules Domain

This example describes string-valued addon rules. It is a complete shared class for registering a domain; the reload itself happens separately after freeze.

java
package example.bcce;

import buildcraft.api.v2.reload.DefinitionReloadService;
import buildcraft.api.v2.reload.ScriptableDefinitionType;
import java.util.Objects;
import net.minecraft.resources.ResourceLocation;

public final class RuleDefinitions {
    public static final ScriptableDefinitionType<String> RULES =
        new ScriptableDefinitionType<>(
            Objects.requireNonNull(
                ResourceLocation.tryParse("examplemod:rules")
            ),
            String.class,
            data -> {
                Object value = data.get("value");
                if (!(value instanceof String text) || text.isBlank()) {
                    throw new IllegalArgumentException("value is required");
                }
                return text;
            }
        );

    private RuleDefinitions() { }

    public static void register(DefinitionReloadService service) {
        service.registerType(RULES, () -> "examplemod");
    }
}

After freeze, the data adapter supplies the complete input set for another reload attempt:

java
ReloadPublishResult result = service.reload(
    RuleDefinitions.RULES,
    List.of(ScriptedDefinition.value(
        Objects.requireNonNull(
            ResourceLocation.tryParse("examplemod:delivery_policy")
        ),
        Map.of("value", "prefer_existing_route"),
        new DefinitionProvenance("examplemod", "config:delivery", 10)
    ))
);

This snippet assumes an existing DefinitionReloadService service and imports from reload and java.util. The config:delivery path is source metadata, not an automatically loaded filename. Reading files, integrating with /reload, and choosing a thread are responsibilities of the calling adapter. BCCE 8.0.24 does not provide a universal JSON directory that automatically feeds arbitrary domains to this service.

Conflict Resolution

Definitions are grouped by ID. The highest-priority entry wins. If two top candidates have the same priority, it is a conflict and publication is rejected. Sorting by owner and source additionally makes diagnostics deterministic, but does not resolve equal-priority ties.

The rule applies equally to values, aliases, and disabled records. A higher-priority DISABLED can explicitly disable a value, for example. For a normal value, ResolvedDefinition records the winner and lower-priority values it superseded and successfully decoded. An error in a fully shadowed lower-priority value need not invalidate the result; an error in the winner does.

An alias may point through other aliases, but its chain must terminate at an enabled value. Cycles, missing IDs, and disabled final targets are errors. ScriptedDefinitionSnapshot.get(id) resolves aliases and respects disabled records.

Publishing and Observers

After decoding and validating references, the service creates a candidate snapshot. All ReloadObserver.beforePublish callbacks run in registration order. An exception from one rejects the candidate. Only once validation succeeds does the service replace the current domain snapshot in a single assignment. afterPublish callbacks then run; their failures are recorded as warnings and cannot roll back already published data.

This guarantee applies to one ScriptableDefinitionType domain. Two independent reload calls do not form a transaction covering all BCCE registries. The service also cannot undo arbitrary external changes made by callbacks. Thus beforePublish should validate the candidate rather than mutate the world in advance. Side effects after commit must assume the data is already published.

ReloadGeneration identifies an attempt. Failed attempts consume numbers too, so successfully published generations may have gaps. ReloadPublishResult.generation() describes the latest attempt, whereas snapshot(...).generation() describes the actually published data. Compare both with published() and diagnostics.

Do not recursively reload the same domain from its callback: the current implementation rejects recursive publication. Service synchronization protects the publishing process; it does not allow observers to access the Minecraft world from arbitrary background threads.

Interaction with Recipes and Fuel

EnergyFluidService and MachineRecipeService also provide definition snapshots but are not automatically connected to DefinitionReloadService. Their implementations contain internal data-replacement mechanisms and codecs; no public automatic JSON/datapack path to these domains is available. Add definitions through the corresponding public registration methods.

This matters for config and scripting-mod integrations: having a data format is not the same as having a loader for it. An adapter must explicitly translate incoming data into the chosen supported contract.

Source Code

Sources: DefinitionReloadService.javaS412; ScriptedDefinition.javaS422; ScriptedDefinitionSnapshot.javaS423; ReloadObserver.javaS418; ResolvedDefinition.javaS420; DefinitionReloadServiceImpl.javaS564; FoundationReloadFixtureAddon.javaS012.

II. Machines and Energy

09Machines: Your Own Block and Runtime Rules

Use the machine API when your addon creates its own machine and needs to integrate with BCCE energy, pipes, gates, and monitoring tools. It is based on composition: a machine definition specifies components and settings, while a separate executable object manages component lifecycles. The addon remains the owner of the block, inventories, and world interactions.

Three objects serve three different purposes. MachineType describes a kind of machine: its stable ID, components, and properties. MachineHost represents one running machine in a world. MachineHostOwner connects the host to the addon's block entity: it supplies location and logical side, exposes ports, persists state, and sends network packets. Creating a host does not register a Minecraft block, create a block entity, menu, or model, or start an independent ticker.

This lets you use your mod's normal block entity. You do not need to subclass a BCCE Quarry, Distiller, or other implementation. The custom resource-processing algorithm must still live somewhere: it belongs in the addon's executable components.

Define the Type Before Creating an Instance

A component has a stable typeId(). To perform work each tick, it additionally implements MachineTickComponent; to report pending work, MachineWorkComponent; and to handle menu actions, MachineActionComponent. MachineComponentLifecycle adds load, activation, suspension, and removal callbacks. These interfaces can coexist on one class. They describe the component's capabilities, not separate objects that the host automatically creates for it.

MachineComponentType<C> associates an ID with a factory, optional persistence codec, and optional client-presentation codec. The MachineComponentFactory<C> receives MachineExecutionContext; a component restored from saved state receives the same context through attached. Both codec IDs must match the component ID; a mismatch is rejected when creating the type definition.

A practical integration sequence is:

  1. Define the ID, component type, factory, and necessary codecs. Register the component type before freezing registries.

  2. Build a MachineType, add the component ID, and register the resulting definition.

  3. From the block entity, once its logical side is known, create the host through BuildCraftServices.MACHINES.

  4. Connect ordinary block-entity callbacks to the host: state loading, ticking, unloading, removal, and synchronization.

  5. Implement MachineHostProvider so other BCCE systems discover this same host instance.

The following snippet connects definitions from CustomMachineFixtureAddon. Here, owner is the block entity's implementation of MachineHostOwner; registration and instance creation occur at their respective phases, not together on every tick.

java
ContentRegistrar content = BuildCraftContent.addon("fixture");
content.machineComponent(CustomMachineFixtureAddon.PRESS);
MachineType definition = content.machine(CustomMachineFixtureAddon.MACHINE);

// В block entity, после регистрации и определения логической стороны:
MachineHost host = BuildCraftApi.service(BuildCraftServices.MACHINES)
    .createHost(definition, owner);

The host checks more than the ID: in the current implementation, definition must be the exact MachineType object stored in the registry. Reconstructing an equal definition with the same ID is insufficient. Keep the registration result or retrieve it from MACHINE_TYPES.

A component without a factory is a valid descriptor, but the host does not create a running instance from it. An unknown component ID does not magically acquire default behavior either. After integration, check that host.component(id, ExpectedClass.class) returns the expected server-side object.

Machine Settings and Variants

MachineType.builder(id) assembles an immutable definition. component, removeComponent, and clearComponents manage composition; property and removeProperty manage settings. MachineType.variant(newId, base) and base.copyAs(newId) copy a definition under a new ID. copyFrom replaces the builder's current contents with the specified base.

MachineProperty<T> contains an ID, a Java type, and optionally a value validator. Properties can describe range, speed, capacity, or operating costs. Actual behavior belongs to a component that reads the property. Adding an unused property changes nothing.

The shared keys in BuiltInMachineProperties define the following common settings:

PropertyMeaning and Validation
WORK_SPEED_MULTIPLIERFinite speed multiplier strictly greater than zero.
ENERGY_COST_MULTIPLIERFinite cost multiplier greater than or equal to zero.
MAX_MJ_INPUT_PER_TICKMaximum MJ input per tick as MjAmount.
MJ_CAPACITYMJ capacity as MjAmount.
INVENTORY_SLOTSNonnegative slot count.
RANGENonnegative operating range.
CHUNK_LOADINGDescriptor-level permission to keep chunks loaded.

Two MachineProperty keys are equal by ID and Java type. Reuse the published key for common settings: creating another key with a different validator does not establish a separate namespace. Standard nested collections and arrays in definitions are copied; arbitrary mutable user values are not universally deep-copied. Prefer immutable values.

Boundary of Built-In Functionality. In BCCE 8.0.24, built-in ENERGY, MINING, PUMPING, DISTILLATION, and other machine component descriptors are registered without factories. They describe existing machine composition. You can register a Quarry variant under a new ID, but createHost does not transfer the quarry's mining algorithm. The built-in Quarry reads some properties of its own base definition through an internal bridge; that does not grant the same implementation to arbitrary new IDs.

Accordingly, machineVariant("quarry_mk2", ...) in EasyContentFixtureAddon illustrates creating a definition. To place that variant in the game and have it mine blocks, an addon must register its own block and provide executable components. A speed multiplier or the ID buildcraft:mining is not enough.

Instance Lifecycle

A host is bound to the thread on which it was created and to its owner's fixed logical side. Do not create one in an arbitrary background task. owner.level() may return null during deserialization; owner.clientSide() must still return the correct answer.

EventOwner ActionComponent Behavior
First initialization of a new server instancerestoreState(new byte[0]) or the next onLoad()The factory creates the component, then calls attached(context).
Loading a saved instancerestoreState(bytes) before the first onLoad / tickThe codec restores the component; attached(context) and restored() follow.
Entering a loaded worldonLoad()activated() prepares components for work; the host becomes active once activation finishes.
Server ticktick()The host calls onLoad if needed, then tick(context) on running components unless mode is OFF.
Chunk unloadonChunkUnloaded()Active components receive suspended().
Return after unloadonLoad()activated() runs again without recreating the object.
Actual block removaladdDrops(list), then removed(), then invalidate()Components contribute drops, receive removed(), and finally invalidated().

removed() and invalidated() are delivered at most once. Do not call addDrops as a probe: it requests real drops, not simulation. The host copies nonempty contributed stacks, but the component and owner must coordinate inventory ownership to prevent the same items being dropped twice.

During attached, restored, and activation itself, the machine is not ready for ordinary runtime mutations. In particular, markChanged and setWorkStatus require an already active server host. Use these callbacks to bind context and prepare local state; perform runtime changes after activation, usually in tick.

OFF stops MachineTickComponent execution but does not mean unloading: suspended() is a lifecycle callback, not an off-button callback. ON and LOOP are distinct MachineControlMode values passed to components; the shared host does not implement repetitive production automatically for LOOP. Components should handle context.mode() if needed.

Controls are available through MachineView.control() when the machine exposes them. Check supportedModes() before setMode(mode, operationMode). SIMULATE validates a transition without changing it; EXECUTE applies it. setEnabled is shorthand for ON / OFF, not a distinct state. A host in this model always provides controls; a monitored native machine may return an empty control().

Resources, Status, and World Interaction

The inventory, tank, and MJ buffer belong to MachineHostOwner. Its itemPort(side), fluidPort(side), mjPort(side), and externalEnergyPort(side) expose sides of those same resources. The host only makes them available to components through MachineExecutionContext. It neither persists port contents automatically nor creates a second energy reserve.

A component checks for required ports and resources, then operates under their contracts. SIMULATE can check transfer feasibility but does not jointly reserve inventory, tank, and energy. For recipes involving several storages, the addon must coordinate its own mutations or provide explicit rollback. “Simulate everything, deduct energy, insert output” is not a global transaction. CustomMachineFixtureAddon.Press demonstrates minimal port coordination and explicitly notes this limitation.

setWorkStatus(new WorkStatus(...)) publishes a human-readable machine state: IDLE, RUNNING, BLOCKED, PAUSED, or ERROR. progress is a finite number from 0 to 1. detail is a short explanation, not a log; host serialization limits it to 256 characters. After changing component counters or other state, call markChanged() to mark persistence and client presentation as dirty and notify the owner.

hasWork() asks a separate question: is unfinished work pending? A machine without energy may be BLOCKED yet still have work for a gate to detect. The host uses a pure query from any MachineWorkComponent, combining multiple answers with logical OR. Otherwise it falls back to RUNNING / BLOCKED status. On the server, an off, inactive, or removed host reports no work. On the client, it uses synchronized RUNNING / BLOCKED status when mode is not OFF; isActive() refers to server-side component execution.

blockState(pos) and isBlockLoaded(pos) do not load chunks. setBlock(pos, state) requires an active, enabled server machine and a loaded position. Additional authorization is provided by the owner's MachineWorldAccess implementation, which must enforce claims and other world restrictions. Missing world access means denial. The host does not replace the server's protection policy.

MachineAreaView extends AreaProvider with validFrom(machinePosition) to examine whether an area is suitable for a machine. It neither creates nor keeps chunks loaded.

Persistence, Menus, and Monitoring

The server stores host.saveState() as a byte array in ordinary block-entity data. The owner's inventories, fluids, and energy are saved separately alongside it. restoreState must be called before initialization; attempting to restore an already created running machine is rejected.

A component codec may support aliases and schema migrations. If its type is missing or a payload cannot be decoded, unknown data is retained for later recovery rather than silently replaced with an empty new component. If the whole host's outer format is damaged or its machine ID mismatches, the original bytes are retained, the machine enters ERROR, and execution stops. The addon must not overwrite that record with empty state.

The client receives a presentation, not the server component instance. MachineComponentPresentation<C, S> defines a snapshot S and its separate codec. Client menus read host.presentation(componentId, View.class) and workStatus(). component(...) is for the server; presentation(...) is for the client. machineComponents() is empty on clients.

Send fullSync() to new observers. Normal updates use changedSync(), where an empty array means nothing changed. The owner sends nonempty data in its own block-entity packet and calls applySync on the client. Before applying, the host verifies target ID, format, sizes, revision, and complete decoding. Stale revisions are ignored; malformed packets must not partially modify the presentation. Send a full snapshot after reconnection.

Host safety limits in BCCE 8.0.24 are 256 component records, 64 KiB per component payload, and 1,200,000 bytes per state or packet. These protect the format; they are not recommended sizes for routine UI updates.

Register menus using normal Minecraft mechanisms. The server handler passes requests to performAction(actor, actionId, payload). The host checks activity, owner.canPlayerUse(actor), and a 4,096-byte size limit before trying components in order until one handles the action. The owner denies actions by default. Components must still validate action IDs, payload structure, and the legitimacy of the state change. MachineActionComponent does not create a network channel or menu.

Other mods discover machines with MachineService.machine(level, pos). The query returns a host published by MachineHostProvider or a direct MachineView, without loading chunks. MachineService.hasWork also accounts for native endpoints of existing BCCE machines. MachineService.heat searches for a HeatPortProvider, host, or MachineView. Passing a side preserves the ability to expose different behavior on different block faces.

Chapter Sources

II. Machines and Energy

10MJ: Energy, Ports, and Conservation

MJ remains BuildCraft's own energy system. The BCCE API separates quantities, endpoint state, connection rules, and conversion to external energy. An addon can therefore connect its block through a public port without importing BCCE's internal battery or loader-specific capability tokens.

Units and Operation Results

MjAmount holds a nonnegative integer quantity of micro-MJ in a long: 1 MJ equals 1,000,000 micro-MJ. MjAmount.ofMj(4) is 4 MJ; MjAmount.ofMicro(4) is four millionths of an MJ. ofMj, plus, and multiplication-based FE conversion guard against overflow. minus forbids negative results.

Fields such as requiredMicroJoules in recipes are legacy names for that same micro-MJ unit. Passing 20 does not mean 20 MJ. For a 20 MJ cost, use MjAmount.ofMj(20).microMj().

MjTransferResult reports requested, transferred, and remainder. Its constructor enforces the equation:

requested = transferred + remainder.

completed() means that the remainder is zero. This is also true for a zero-quantity request, so it does not prove a machine performed useful work. For nonzero flow, check transferred().isZero().

MjPort.insert(offered, mode) and extract(requested, mode) can partially fulfill a request. With SIMULATE, the port must estimate the result without changing energy, limits, inventories, or other state. With EXECUTE, account for the amount actually transferred. stored / capacity and canInsert / canExtract describe a port but do not replace the result of an actual operation.

Connecting Your Block to a Network

For an ordinary addon, implementing MjPortProvider on the block entity is sufficient. mjPort(side) exposes that side's endpoint, and optional mjPortDescriptor(side) exposes its network properties. EnergyService.port checks a direct API provider first, then a machine view/host, and finally a platform bridge to native endpoints.

MjPortDescriptor contains networkId, a set of roles, and per-tick maximum flows. Roles distinguish consumers (CONSUMER), providers (PROVIDER), passive providers with a minimum request (PASSIVE_PROVIDER), connectors (CONNECTOR), pulse receivers (REDSTONE_RECEIVER), and readable state (READABLE). These are topology metadata. They may be queried on either logical side and must not simulate or execute resource transfers.

Without an explicit descriptor, the service may derive one from the public port. Standard MjStorage contributes its configured limits; for arbitrary ports with unknown stable rates, the limit is represented as Long.MAX_VALUE micro-MJ. This is not permission to accept that amount: actual port operations remain authoritative.

EnergyService.requested is a separate live demand query when MjDemandPort is supported. Call it during server operations, not during client connection discovery. Exposing a capability and actually requesting energy are different things.

Before adding a network edge, an API consumer may call canConnect(MjConnectionContext). The context contains the level, local block position, side, and two descriptors. Every registered MJ_CONNECTION_RULES rule must allow the connection, after which the platform bridge is considered. This method does not itself transfer energy. Addons implementing their own transport must additionally check role, network, and flow-limit compatibility; EngineHost.emit performs such checks for adjacent engine connections.

Standard Buffer and Tick Limits

Create standard storage through the service, not by importing an internal class:

java
EnergyService energy = BuildCraftApi.service(BuildCraftServices.ENERGY);
MjStorage buffer = energy.createStorage(
    new MjStoragePolicy(
        MjAmount.ofMj(100),     // номинальная ёмкость
        MjAmount.ofMj(100),     // физический верхний предел
        MjAmount.ofMj(8),       // приём за тик
        MjAmount.ofMj(4),       // отдача за тик
        true,                  // пассивная отдача
        false                  // обычный, не импульсный получатель
    ),
    MjAmount.ZERO
);

This snippet creates a buffer. For unrestricted ordinary storage, there is a createStorage(capacity) overload and one accepting an initial quantity. The block entity must own the buffer and call energy.tickStorage(buffer, level, pos) once per server tick when using tick limits or overcharge, preferably before its own transfers. This does not load chunks. Repeated calls for the same gameTime do not reset budgets. Without advancing to the next logical tick, already-used transfer allowances do not refresh.

Input and output limits are tracked independently and consumed only in EXECUTE. requested() considers both available space up to maximumStored and the remaining input budget. capacity() returns nominal capacity: overcharge may permit stored energy above that value.

maximumStored must be at least capacity. When it allows more than twice nominal capacity, tick reduces only the portion above 2 × capacity, losing one thirty-second of that excess per tick, rounded up. tickStorage sends POWER_LOSS_EFFECTS a PowerLossContext with the level, position, and energy actually lost. The current standard effect service draws nothing; do not assume particles or sound happen automatically.

For persistence, store buffer.snapshot().microMj() as a long alongside owner state. Restore with buffer.restore(MjStorageState.fromMicroMj(saved)). Physical energy is preserved; tick budgets begin anew. Values exceeding the current maximumStored are rejected. If capacity shrinks across versions, define a migration or explicit overflow policy.

Atomicity: What a Port Actually Promises

Simulation does not reserve capacity. A receiver can accept less than expected, or reject an operation, between preliminary checks and execution. Thus an arbitrary MjPort cannot be treated as a transactional storage system.

For MjTransferPolicy.ALL_OR_NOTHING, the default implementation first simulates full acceptance. With EXECUTE, it does not insert energy even if the simulation succeeded: without a dedicated atomic mechanism, returning no transfer is safer. A port that guarantees atomic insertion may override the overload. Standard MjStorageImpl in BCCE 8.0.24 does not. Do not use ALL_OR_NOTHING as a universal atomic insertion operation on standard buffers.

Similarly, the generic MjPort overload extract(minimum, maximum, mode) does not execute positive-minimum extraction based on simulation alone. A real “nothing or at least minimum” operation needs MjPassivePort or another suitable override. Standard MjStorage does implement this atomically: when less than the minimum is available, it changes no state.

MjStorage.refundExtracted(amount) returns energy that the buffer already extracted but the receiver did not accept. It rolls back an extraction, rather than inserting new energy or generating it. The standard implementation limits refunds to energy extracted during the current tick and to available space; it restores output budget without consuming input budget. Custom storage need not support refunds: its default implementation returns zero. Transfer code must handle that possibility.

FE Conversion and Display

EnergyService.conversion() returns the active microMjPerFe ratio. EnergyConversionStatus also reports whether automatic conversion is enabled, preference for external-energy display, and rate units. Do not hardcode a fixed MJ/FE ratio: it depends on configuration.

feToMicroMj performs exact multiplication. microMjToWholeFe rounds down to whole FE, and conversionRemainder preserves the indivisible remainder. feToMicroMjClamped saturates on overflow and is intended for capacity snapshots and display, not actual energy transfer.

automaticFeConversionEnabled describes BCCE's compatibility policy. It does not turn every long field, machine, or custom port into a converter. External energy uses separate public platform ports, and conversion must go through the appropriate adapter while retaining remainders.

MJ_FORMATTER supports consistent output: formatNumber returns a number, formatAmount includes MJ, and formatRate shows MJ/t or MJ/s according to the explicit EnergyRateUnit. Currently, per-second rates multiply by 20 and the formatter always displays MJ regardless of the FE display flag. If your UI must respect the user's FE preference, inspect status() and implement the corresponding display yourself.

Chapter Sources

II. Machines and Energy

11Engines, Heat, and Laser Targets

These features can be used independently. EngineHost helps implement a custom MJ generator with thermal state. HeatPort exposes temperature readings and, optionally, adjustments. LaserTarget makes a block able to receive laser energy. A simple laser target needs neither an engine host nor a machine host.

Creating a Custom Engine

EngineType associates an ID with an EngineProfile: maximum generation/output per tick, capacity, and a descriptive flag for accepting external energy. After registration, the ENGINES service creates a host with:

java
EngineHost host = BuildCraftApi.service(BuildCraftServices.ENGINES)
    .createHost(engineType, owner, engineFactory, persistence, heatSettings);

This is a contract-level snippet: owner implements EngineHostOwner, the factory creates an EngineComponent, and PersistentType persists it. CustomEngineFixtureAddon provides a compact full example with an engine definition, fuel counters, and codec.

The registered engine type must be the exact object passed to createHost, and the persistence ID must match EngineType.id(). Its executable component returns the same ID from typeId() and implements tickEngine(EngineExecutionContext). The ordinary machine lifecycle and persistence rules also apply. EngineComponent is designed for an engine host: its default tick casts the context to EngineExecutionContext, so placing it in a plain machine host does not create an engine.

EngineHostOwner.outputBuffer() returns the owner's existing MJ buffer. It must support insertion and extraction and have at least the profile's capacity. The host does not create another energy reserve. The owner persists the buffer together with host.saveState() and restores both before operation. If the buffer has per-tick limits, service it separately using tickStorage; the engine host does not do that automatically.

By default, the engine host's public mjPort(side) outputs energy and forbids external insertion, even though the engine itself can access the internal outputBuffer() for generation. If the owner supplies its own owner.mjPort(side) or implements MjPortProvider, that explicit choice defines additional sides and roles.

EngineProfile.acceptsExternalEnergy does not automatically enable input in the current runtime. It is descriptive metadata; actual FE, MJ, or other input requires an appropriate port and algorithm. Registering an EngineType also does not copy a built-in engine's implementation based on similar parameters.

An Engine Tick

At the start of a server tick, the host resets generation and emission counters and applies passive cooling. Then, unless control mode is OFF, it calls tickEngine. The component decides whether fuel is present, which animation stage to show, how much heat to add, and how much energy to generate. Custom engine hosts do not automatically burn fuel using EnergyFluidService; the component must consult fuel profiles as needed.

generate(requested, mode) limits the result by remaining maxOutputPerTick budget, available space under profile capacity, and the owner's buffer response. Generation stops at critical temperature. Multiple calls within one tick cannot bypass the limit; simulation consumes no budget. Actual generation is reported in MjTransferResult and reflected in generatedLastTick.

emit(facing, maximum, mode) addresses an adjacent loaded block. It locates the receiver's opposite-side port, checks provider/consumer roles, network compatibility, and EnergyService.canConnect. The amount is limited by stored energy, remaining engine output budget, and both descriptors' limits. The host simulates extraction and acceptance, executes the transfer, and refunds any unaccepted portion to its source buffer.

If the destination behaves differently during execution than during simulation, refunding depends on a correct refundExtracted or ordinary reverse insertion into the source. Failure to recover extracted energy is an error, not acceptable unaccounted loss. The recipient must accurately report how much it accepted; reporting more than offered violates the contract.

generate and emit are allowed only during an active engine tick. Do not call them from the GUI, constructor, attached, or arbitrary background tasks. For atomic consumption of custom fuel, first determine how much work is possible, then update fuel and energy consistently. Reducing fuel before every failed generation attempt wastes fuel.

setAnimation(phase, soundId) publishes a phase within [0, 1] and a sound ID. For a REDSTONE_RECEIVER, the host permits transfer only at phases of at least 0.5, with at most one successful transfer per stroke. Dropping below 0.5 resets the pulse flag. This applies to pulse receivers; ordinary endpoints do not wait for a piston stroke.

Temperature and Visual State

EngineHeatSettings defines HeatProfile(minimum, ideal, maximum), ambient temperature, critical threshold, and passive cooling per server tick. Temperatures are BuildCraft degrees, not a universal physical scale. All values must be finite and ordered; cooling cannot be negative, ambient cannot be below minimum, and critical cannot be below maximum.

The component calls setTemperature or addHeat. Values below minimum are rejected; reaching critical blocks generation. Host-controlled passive cooling approaches ambient even when machine controls are OFF. Engine-host overheating does not intrinsically cause an explosion, block destruction, or coolant consumption. Such behaviors must be implemented separately if needed and integrated with world-access rules.

EngineStage is an ID and a nonnegative severity level. It is an extensible descriptor: it neither forces external renderers to use a particular color nor triggers effects by itself. EngineVisualState contains the stage, HeatState, MJ generated in the last tick, animation phase, and optional sound ID. The host persists and synchronizes this state through its internal component. An addon reads visualState() for presentation and is responsible for the model, renderer, and playback.

Use EngineService.engine(level, pos) to find an existing engine. It returns an EngineView: an ordinary machine view plus stage and current energy output. A custom engine's block entity should implement EngineHostProvider, ensuring that engine lookups, machine controls, and gates all refer to the same host.

Blocks without a host may expose HeatPort via HeatPortProvider. heatState() reports current and critical temperatures; profile() reports the working range. canSetTemperature() distinguishes a controllable temperature from read-only measurement. setTemperature(value, SIMULATE) must predict a result without modifying the block; EXECUTE applies an allowed operation. Some built-in engines expose read-only heat ports, so having HeatPort does not guarantee an adjustable thermostat.

Receiving Energy from Lasers

To make a BCCE laser recognize a block, its block entity implements LaserTarget directly or LaserTargetProvider. The direct form exposes one laserPort(). A provider receives incomingSide and can return a target or Optional.empty() per side, for example for a machine accepting beams only from its front face.

LaserTargetService.target(level, pos, side) does not load chunks and only accepts targets with non-null ports whose canInsert() is true. Discovery does not transfer energy. The target port must subsequently honor SIMULATE and EXECUTE, and energy received must enter the machine's one real storage.

LaserTableType and optional LaserTarget.typeId() identify target kinds with namespaced IDs instead of a closed set of table classes. Registering such an ID does not make a block visible to lasers: it still needs a provider or direct LaserTarget. Current runtime discovery does not require first finding the target ID in LASER_TABLE_TYPES.

Built-in TileLaser uses this service: it searches its cone for visible targets, simulates insertion to determine demand, and transfers only the amount accepted. Its current range parameter is 6 and maximum power is 6.4 MJ/t. For side lookup it passes the direction opposite its own orientation; do not assume this is always the geometric surface normal at the exact impact position on an external block.

CustomLaserTargetFixtureAddon demonstrates a target that accepts beams only from NORTH and has a minimal MJ port without external output. It is an endpoint example: charge persistence, Minecraft content registration, models, and machine actions are not implemented.

Chapter Sources

II. Machines and Energy

12Machine Recipes: Requirements, Results, and Definition Updates

MachineRecipeService is the public entry point for registering and looking up Assembly Table, Integration Table, Distiller, and Heat Exchanger recipes. A recipe definition describes required inputs and produced outputs. The service does not itself deduct items, fluids, or MJ; the machine consuming the definition does that.

This allows addons to add recipes to existing BCCE machines without subclassing their implementations. Assembly Table and Integration Table support programmatic recipes whose outputs depend on input Data Components. Distillation and heat exchange offer ready-made immutable definitions and data codecs.

Choosing a Recipe Form

TaskPublic FormWhat the Author Defines
Fixed-output item recipe for Assembly TableSimpleAssemblyRecipeRequirements, one output, and a positive cost in micro-MJ.
Multiple outputs or input-dependent resultsAssemblyRecipeDefinitionAvailable outputs, previews, requirements, and cost for each chosen result.
Modifying the central item in Integration TableIntegrationRecipeDefinitionCentral ingredient, additional requirements, output calculation, and cost.
Lazy selection of integration recipes based on item dataIntegrationRecipeProviderConstructing a suitable definition from current inputs; null if none matches.
Fluid separation in DistillerDistillationRecipeDefinitionInput, gaseous and liquid outputs, and cost.
Heating or cooling in Heat ExchangerHeatExchangeRecipeDefinitionInput, output, and change in heat level.

Every form has RecipeDefinition.kind(), which is used for dispatch. An arbitrary marker-interface implementation claiming Kind.DISTILLATION cannot replace the DistillationRecipeDefinition expected by the service. Built-in lookups check both the kind and the concrete supported definition type.

Item Requirements Without Losing Data

CountedIngredient combines an item predicate with a required count. It can be constructed from a vanilla Ingredient, ItemLike, TagKey<Item>, or an example ItemStack. Counts must be positive. matchesItem(stack) checks type and data without requiring a sufficient count; test(stack) also checks the given stack's quantity.

An ItemStack example means an exact match of the item and its NBT/Data Components for the target version, not merely Ingredient.of(item). ingredient() is useful for UI and vanilla-ingredient integrations but cannot always express the additional restrictions of an exact example. When actually consuming resources, use CountedIngredient rather than reconstructing an IngredientStack from ingredient() and count().

In 1.21.11 and 26.X, tag-based ingredients retain the TagKey and resolve it at query time. Definitions may thus be created before datapack tags are bound and automatically respect updated bindings after reload. Item examples retain the isSameItemSameComponents comparison. Older source families use the corresponding NBT or component comparison.

AssemblyIngredients.remaining(requirements, slots) solves a harder problem than taking the first matching slot for each requirement. It determines whether all requirements, including overlapping tags and exact examples, can be satisfied without consuming the same item twice. On success it returns remaining stacks without modifying the source inventory; on failure it returns Optional.empty().

For example, one requirement may accept any tagged ingot while another requires a specific component-bearing ingot. The assembly algorithm need not waste the only exact ingot on the generic requirement. This guarantee applies to AssemblyIngredients and its Assembly Table execution path. The current Integration Table processes additional requirements sequentially and does not offer the same general allocation guarantee for arbitrary overlapping requirements.

Assembly Table

For a fixed result, the following registration is sufficient. It is a standalone recipe-definition fragment; content is a previously created ContentRegistrar for your namespace.

java
content.assembly("laser_compass", new SimpleAssemblyRecipe(
    List.of(
        CountedIngredient.of(Items.IRON_INGOT, 4),
        CountedIngredient.of(Items.REDSTONE, 1)
    ),
    new ItemStack(Items.COMPASS),
    MjAmount.ofMj(20).microMj()
));

SimpleAssemblyRecipe stores a copy of the output and rejects empty requirements, an empty output, or a nonpositive cost. outputs(available) returns the output only when resources can be allocated successfully. outputPreviews() shows it independently of actual resource availability. A request for a different output count or data is not accepted by this recipe.

When implementing AssemblyRecipeDefinition yourself, keep these methods consistent. Every output from outputs needs valid requirements(output) and positive requiredMicroJoules(output). Previews are for UI and JEI, not permission to create an item. Do not mutate input stacks while computing an output; return independent output copies.

The built-in table connects API recipes to its existing crafting cycle through an adapter. Before consuming resources, it revalidates the output, its count, its cost, and the item allocation, then applies the calculated remainders to its own inventory. Accordingly, recipe evaluation must be deterministic for the same input and free of side effects. Do not decrement counters, mutate items, or consume resources inside outputs.

assemblyRecipes() returns all published assembly definitions, findAssembly(available) returns all applicable definitions, and assemblyById(id) returns the current one for an ID. Priority sorts candidates but does not hide other recipe IDs from the results. This differs significantly from finding a single fuel or distillation match.

API assembly recipes do not become vanilla RecipeManager entries. Separate consumers exist for the table and JEI, so checking only the vanilla recipe manager is wrong. Use MachineRecipeService to look up API recipes.

Integration Table

Integration Table processes a central item and supplemental inputs. centerIngredient() defines consumption from the central slot; requirements(output) defines the other ingredients. output(target, toIntegrate) computes the result, including transfer or modification of relevant Data Components. Copy the source item before modifying it; changing the supplied target during a query violates the contract.

Use IntegrationRecipeProvider.recipeFor if a single prebuilt definition is insufficient. The provider receives current inputs and returns a matching recipe or null. The service checks that the recipe yields a nonempty result. No energy is consumed at this stage.

In the native table, the recipe's requirements must account for every supplemental item: unrelated leftovers in extra slots invalidate consumption. The central stack is reduced by centerIngredient().count(). Existing machine execution requires a positive cost; zero or negative cost is not interpreted as a free integration recipe.

The lookup handles exceptions from some user callbacks, but this is not universal protection against malformed recipes. The native table also accesses the selected definition outside that lookup. Implementations must return consistent nonempty requirements and valid costs and avoid throwing for ordinary nonmatching inputs.

Distillation and Heat Exchange

FluidIngredient contains a FluidMatcher and positive volume in millibuckets. exact(variant, amount) requires a precise FluidVariant; matching(matcher, amount) supports programmatic matchers, such as tag predicates. Recipe search checks fluid variants but not tank quantity; the machine verifies the full amount before execution.

DistillationRecipeDefinition specifies independent gasOutput and liquidOutput destinations. Either may be empty, but not both. These names identify output tanks; the recipe does not create fluids, gas physics, or textures. Output variants must correspond to existing game content.

java
content.distillation("crude_separation", recipe -> recipe
    .input(FluidIngredient.exact(crudeVariant, 100))
    .gas(gasVariant, 35)
    .liquid(liquidVariant, 60)
    .powerMj(20)
);

Here, the addon has defined three FluidVariant values. Each operation takes 100 mB input, and the two output tanks must accommodate 35 and 60 mB respectively. The API does not require output volumes to sum to input volume; the recipe defines that behavior. Distillation allows a nonnegative cost, unlike fixed assembly recipes, which require a positive cost.

Native Distiller finds a definition from the current input and checks complete extraction and capacity in both output tanks. API volumes are long, but bridges to native FluidStack and tanks may impose smaller limits. In particular, inputs above Integer.MAX_VALUE mB do not become valid single operations in the existing machine. Size batches for real tank capacity.

HeatExchangeRecipeDefinition accepts only HEATING or COOLING. Heating requires heatTo > heatFrom; cooling requires the opposite. These values describe the recipe's heat-level direction, not MJ/t or universal degrees for HeatPort. The output may be empty, allowing a process with no returned fluid. Heating and cooling definitions are registered separately and paired by the Heat Exchanger for their respective flows.

The native heat exchanger also requires the incoming cooling stream to be hotter than the incoming heating stream. It selects integer mB quantities satisfying coolingInput × coolingDelta = heatingInput × heatingDelta, scaling each output according to its recipe's input/output volume ratio. Recipes with different volumes can work together but must support an integer transfer within the machine's throughput.

The FluidIngredient, DistillationRecipeDefinition, and HeatExchangeRecipeDefinition codecs use FluidIngredientData, DistillationRecipeData, and HeatExchangeRecipeData structures. They validate quantities and variant/volume consistency. Serialized FluidIngredientData describes an exact variant. An arbitrary matching(...) predicate cannot be serialized by this codec, even though it is valid for gameplay lookup.

Priorities, Snapshots, and Reload

Use register(id, definition, provenance) or ContentRegistrar methods for registration. RecipeMatch returns the recipe, its ID, and DefinitionProvenance, making it possible to identify which definition was selected and who supplied it.

The service reads the current set from snapshot(). Candidate lookup sorts definitions by descending priority and then string ID. Registering the same ID twice in code is rejected even with a higher priority. Give programmatic extensions their own IDs; priority controls selection where a lookup returns one match. Casting to internal implementations to remove or replace another addon's code-owned definition is not a public contract.

A snapshot protects its collection of entries but cannot deep-freeze an arbitrary AssemblyRecipeDefinition or IntegrationRecipeDefinition. Registered recipes should behave as stable definitions. Do not secretly modify their settings without advancing the generation: consumers will not know to invalidate caches.

If an addon caches definitions, track snapshot().generation(). Built-in Integration Table clears its selected recipe on generation changes; Assembly Table refreshes API instructions and the adapter resolves definitions again by ID. Custom machines should likewise confirm that a recipe still exists and can finish after definitions change. Do not keep crafting forever using a stale removed definition.

What Reload Means Here. Internal registries can atomically build a new generation, retaining the previous one on failure. However, recipe reloadData is not connected to JSON/datapack loading in BCCE 8.0.24. Public *Data types and CODEC do not establish a supported data/<namespace>/... path or JSON syntax. DefinitionReloadService is separate; no automatic bridge to MACHINE_RECIPES exists.

The documented working path for adding these API recipes is therefore code registration. Addons can use the shared definitions mechanism for their own reloadable data, but must not promise users built-in datapack syntax for machine recipes simply because a codec exists.

Chapter Sources

II. Machines and Energy

13Fuels and Coolants: Properties of Registered Resources

EnergyFluidService defines how existing fluids and items are used in BuildCraft's energy systems. Registering a FuelProfile does not create the fluid, bucket, texture, or world-flow behavior. It tells an engine which resource can be burned, how much power and duration it provides, and whether residue is produced.

The service includes three EnergyFluidDefinition forms: liquid fuel (FUEL), liquid coolant (COOLANT), and solid-coolant conversion (SOLID_COOLANT). ProfileMatch<T> records the selected definition ID and provenance along with the profile. The native combustion engine in BCCE 8.0.24 uses these profiles to choose fuel, cool the engine, and load solid coolants.

Choosing a Fluid

A profile accepts FluidMatcher. FluidSelector is convenient for most integrations and has three distinct matching semantics:

FormMatches
FluidSelector.fluid(fluidId)Any variant with the given fluid ID, regardless of additional data.
FluidSelector.tag(tagId)A fluid that the supplied FluidMatchContext assigns to the specified tag.
FluidSelector.exact(variant)An exactly matching FluidVariant, including all component data.

Choose according to resource semantics. If composition, quality, or temperature are encoded in variant data and affect fuel properties, broad fluid(id) matching may incorrectly combine distinct fuels. Tag matching requires a real tag context; a context that always returns false disables those matches.

representativeVariant() provides a concrete fluid for UI display. ID and exact-variant selectors have representatives, but tag selectors do not: a tag need not identify one fluid. Missing a representative does not mean a profile fails. Programmatic matchers may also work without an icon known in advance.

FluidSelector.CODEC uses FluidSelectorData and serializes exactly these three forms. An arbitrary Java matcher lambda does not automatically become a serializable condition.

Fuel Power, Duration, and Residue

FuelProfile stores positive powerPerTickMicroMj, positive burnTicksPerBucket, and residuePerBucket. These represent power per tick and burn duration per bucket, not the price of one millibucket. For this calculation, an API bucket is 1,000 mB.

At constant output, total energy per bucket is powerPerTickMicroMj × burnTicksPerBucket; guard against long overflow. Each mB pays for burnTicksPerBucket / 1000 ticks, including the fractional portion. Rounding every millibucket up to a whole tick would generate extra energy for short burn times.

Example of code-based registration for fuel without residue:

java
content.fuel("biofuel", FuelProfile.clean(
    FluidSelector.fluid(content.id("biofuel")),
    MjAmount.ofMj(6).microMj(),
    12_000
));

This profile provides 6 MJ/t for 12,000 ticks per 1,000 mB. The definition ID and fluid ID may share a path but belong to different registries; the addon must register the biofuel fluid itself separately.

FuelProfile.dirty adds a nonempty FluidVolume of residue per bucket. The native combustion engine accounts for fractional residue and free tank capacity before burning each new mB of fuel. A full residue tank can stop burning. Do not remove a residue entry just because its amount per mB is fractional: the profile specifies a per-bucket volume, and the consumer must handle fractions correctly.

When a fuel profile changes, already paid-for work must not be retroactively repriced. The native engine retains the power associated with its remaining burnTime and chooses the new profile for subsequent fuel. Custom engines using these profiles need equivalent explicit accounting; EngineHost does not provide it for the component.

FuelProfile.CODEC and FuelProfileData encode the selector, power, duration, and optional residue. A residue variant and positive volume must be consistent. Profiles with arbitrary matchers cannot be serialized by the standard codec.

Cooling

CoolantProfile.constant(matcher, degreesPerMilliBucket) models constant efficiency; its coefficient must be finite and strictly positive. For behavior depending on current temperature or fluid data, use new CoolantProfile(matcher, coolingRule). The CoolingRule receives a FluidVariant and heat and returns degrees per mB.

CoolantProfile.degreesPerMilliBucket converts nonpositive or nonnumeric rule results into zero cooling. Nevertheless, the rule must be pure, fast, and must not throw exceptions on ordinary queries. It reports efficiency but does not drain a tank or modify machine temperature.

The native combustion engine applies the profile through its own thermal algorithm, tick consumption limits, and engine state. Thus an equal coolant coefficient does not imply that every HeatPort block cools itself with that fluid. The consumer controls when and how much coolant is used.

Serializable CoolantProfileData supports constant coefficients expressed with ConstantCoolingRule and FluidSelector. Arbitrary temperature curves remain programmatic. The standard codec correctly rejects attempts to serialize such curves instead of saving incomplete approximations.

Solid Coolants

SolidCoolantProfile pairs an ItemMatcher with a SolidCoolantConversion. The conversion receives a copy of a matching stack and returns a fluid volume; neither checking nor consuming code should mutate the original stack during the query. Nonmatching stacks or null results become an empty FluidVolume.

The profile describes conversion. The receiving tank or machine decides whether to consume an item after checking capacity. The native tank-loading path passes a one-item copy, checks complete acceptance, and only then consumes the item. A custom consumer passing multiple items at once must account for converted volume and consumption consistently; the API does not define an implicit universal batch size.

There is no content.solidCoolant(...) convenience method in ContentRegistrar. Register it through the service:

java
BuildCraftApi.service(BuildCraftServices.ENERGY_FLUIDS).register(
    content.id("packed_coolant"),
    solidCoolantProfile,
    content.provenance()
);

SolidCoolantProfile has no standard data codec in BCCE API 2.1; it remains a programmatic definition.

Lookup, Priority, and Profile Lifetime

findFuel, findCoolant, and findSolidCoolant select the first match sorted by descending priority and then ID. fuels, coolants, and solidCoolants expose ordered lists for inspection. Code-owned definitions have unique IDs; duplicate registration is rejected. Priorities for overlapping addon rules should be deliberate, and provenance makes the chosen profile's origin traceable.

Registry snapshots have generations. Invalidate custom caches when the generation changes, or look up profiles again when beginning another unit of work. Previously paid-for fuel and newly accepted inputs must be treated separately: data updates do not justify doubling energy or losing accumulated residue.

The reload limitation is the same as for machine recipes: an internal atomic mechanism exists, but BCCE 8.0.24 does not automatically load these *Data types from datapack JSON. Code registration is the supported public path for adding fuels and coolants. Casting a service to its internal implementation to call reloadData is not part of the API contract.

Chapter Sources

II. Machines and Energy

14ContentRegistrar: A Shortcut to the Same Public Contracts

BuildCraftContent.addon(namespace) creates an addon's ContentRegistrar; an overload with priority sets definition precedence. This is a convenience facade over BuildCraftApi, not an alternative runtime. It eliminates repeated construction of RegistrationContext and DefinitionProvenance while preserving each registry or service's rules.

content.id(path) creates a valid namespaced ID. registrationContext() exposes the registration owner; provenance() provides owner, addon-code source, and priority. register(key, id, value) is a general entry point for any public registry and returns the registered value.

Choosing the Right Method Family

What the Addon AddsFacade MethodsWhat the Addon Still Must Do
Machines and enginesmachineProperty, machineComponent, machine, machineVariant, engineRegister blocks and block entities, owner/host, component algorithms, menus, and presentation.
PipespipeComponent, pipeAttachment, pipeFlow, pipeBehaviour, pipe, pipeVariantProvide appropriate runtime definitions and register the pipe items being created.
Recipes and energy profilesassembly, integration, integrationProvider, distillation, heatExchange, fuel, coolantProvide existing resources, correct matchers, and recipe functions.
Gates, signals, and automationparameter, statementContributor, trigger, action, signal, automationAction, stripesHandlerDefine evaluation and execution semantics, permissions, and resource consistency.
Robots and constructionrobotTask, robotResource, dockPort, robotBoard, robotBoardProgram, fillerPattern, and schematic methodsImplement the actual program/task, codecs, and supported ways to interact with the world.
Integration datawrench, mapLocationAdapter, namedItemListImplement adapters and correct matching for your mod's objects.
Guidebook and generationguideSection, guideEntry, worldgen, oilInDimensionProvide translations, page assets, and an appropriate target world.
External modules and custom reloadable datamodule, definitionType, definitionDecoder, reloadContributor, reloadObserverProvide actual dependencies and a consumer of published definitions.

Detailed contracts for each domain appear in their chapters. The facade does not change their meaning. For example, pipeFlow and pipeBehaviour additionally register associated component types, but cannot resolve conflicting definitions with the same ID. robotBoardProgram does not replace registration of its RobotBoardType. blockSchematic and entitySchematic connect snapshot-element types with their adapters under expected IDs.

machineVariant and pipeVariant require an already registered base and copy it under a new ID. A missing base is an error, not a request to load a missing module automatically. A variant holds new settings but does not alter the original base.

BuildCraftContentIds publishes stable IDs for built-in machines, engines, stages, machine components, laser tables, pipes, and the default oil-generation profile. These help with lookup and references without repeating string literals. A constant does not guarantee that the required game module, executable factory, or block exists in the target installation; check registry entries and runtime capabilities.

Where the Facade Ends

Register static types before FROZEN: component definitions normally belong to TYPE_REGISTRATION, while content using them belongs to CONTENT_REGISTRATION. The facade cannot reopen frozen registries or replace someone else's type. Code-owned recipe and fuel definitions use their own services and generation models; do not confuse them with reopening type registries.

It is especially important to distinguish a descriptor, a running object, and Minecraft content. The API permits a machine descriptor without a block entity, a fluid port without a canister item, or a laser-table ID without a laser endpoint. These may be useful integration pieces but are not complete game objects.

EasyContentFixtureAddon demonstrates convenience facade calls and creating a pipe item separately through PIPES.createItem. Its machine variant is only a registration example until the addon supplies executable components. BuildCraftAdditionsParityAddon demonstrates a fluid-port contract but keeps contents in an instance field: it does not by itself persist independent contents for each canister item stack. Treat fixtures as verified examples of API signatures and integration boundaries, not promises of finished in-game behavior for every mentioned object.

Chapter Sources

III. Transport and Logistics

15Pipes: Types, Components, and Executable Flows

The transport API lets you add a standalone pipe, change routing for an existing type, or implement a completely different resource-transfer mechanism. These are three levels of extension. First decide who owns the resources in transit: BCCE's standard flow or your addon. That decision determines required work, available events, and responsibility for resource safety.

Choosing the Extension Level

For a filter, distributor, limiter, or item teleporter, the standard flow plus a few components is usually enough. BCCE continues to own travelling items, perform ordinary transfers between neighbors, persist pipe state, and send standard client updates. Your component makes decisions at the available extension points.

PipeType describes a registered pipe kind, while PipeView represents a specific pipe in the world. The immutable type contains an ID, transported media, profiles, component IDs, and optional custom flow and behavior references. A runtime pipe has a position, connected sides, color, attachments, component instances, and accessible ports. Registering a PipeType does not mean a pipe of that type has been placed anywhere.

GoalApproachAddon Responsibilities
New filter or routerPipeType.builder(id) with standard medium and componentsAdmission rules, directions, and small amounts of state
Variant of an existing pipePipeType.variant(id, base) or base.copyAs(id)Profile and component-composition changes
New behavior over a compatible flowRegistered PipeBehaviourDefinitionBehavior, persistence, necessary ports, and callbacks
Entirely custom transport or combined mediaRegistered PipeFlowDefinitionCustom buffers, movement, resource ports, persistence, and presentation

PipeMedium defines standard media: items, fluids, MJ, external energy, and structure. Without a custom flow, a type must declare exactly one medium for the standard carrier. Combining media or using customMedium(namespacedId) requires a custom flow when calling build(). STRUCTURE means a structural pipe, not unrestricted universal transport.

A standalone type gets BCCE's standard carrier for its medium. A variant inherits a base type and explicitly marks it as its archetype. This distinction matters: a custom filtering pipe need not inherit the behavior of a particular metal or special pipe. A variant explicitly expresses the intention to keep the behavior of a chosen carrier. Even so, an arbitrary archetype must be compatible with the selected standard flow.

After type registration, BuildCraftServices.PIPES exposes createItem(pipeTypeId). It creates or returns an item that places the type in a standard pipe block. The addon must register that returned Item in Minecraft's item registry. This does not produce a complete content package: localization, recipes, and special visuals remain separate. Unknown IDs cause IllegalArgumentException.

This snippet follows CustomPipeFixtureAddon. Run it during bootstrap after BCCE has registered the transport carrier but before the API registries freeze. FILTER_ID and LOGIC_ID are pre-created addon IDs and registration is its RegistrationContext; the IronFilter appears in the following chapter.

java
BuildCraftApi.registry(BuildCraftRegistries.PIPE_COMPONENT_TYPES)
    .register(LOGIC_ID,
        new PipeComponentType<>(LOGIC_ID, pipe -> new IronFilter(), null),
        registration);

PipeType type = PipeType.builder(FILTER_ID)
    .itemProfile(new ItemTransportProfile(16, 1))
    .component(LOGIC_ID)
    .build();

BuildCraftApi.registry(BuildCraftRegistries.PIPE_TYPES)
    .register(FILTER_ID, type, registration);

Item item = BuildCraftApi.service(BuildCraftServices.PIPES)
    .createItem(FILTER_ID);
// Передать item в обычную регистрацию предметов своего загрузчика.

A Component Is State for One Placed Pipe

PipeComponentType<C> associates an ID, PipeComponentFactory<C>, optional PersistentType<C, OpaqueData>, and synchronization channels. The factory creates a new instance for an individual pipe. Its PipeComponent.typeId() must match the type ID. Using a shared mutable singleton instead of per-pipe instances would mix state between pipes.

Components implement only the roles they need: item-admission, routing, transit observation, tick, connection, or activation handlers. Basic PipeComponent provides identity but does not automatically subscribe the object to every event. PipeTickComponent.tick(PipeMutationContext) receives the current tick context; a normal tick component may run on the client, so check the logical side before server work. Custom PipeRuntimeComponent instances are ticked by the runtime only on the server. PipeActivationComponent.activate(PipeActivationContext) handles interactions and returns PASS, SUCCESS, DENIED, or FAILED. The activation context supplies the side, a copy of the held item, an actor, and operation mode. Modifying heldStack() does not change the player's inventory.

PipeView permits inspection of a pipe but can also provide resource ports. Thus a read-only view does not make an obtained ItemPort or FluidPort read-only: mutations are governed by that port's explicit operation-mode contract. Do not cast the view to an internal holder or use it to bypass thread and flow rules. Dedicated contexts exist for neighbor queries and modifications to the current pipe.

Lifecycle, Persistence, and Synchronization

PipeComponentLifecycle separates creation/restoration from activity in a world. attached(pipe) associates the component with its pipe. Either restored() or placed() follows, depending on whether it is loaded or newly placed; activated() marks an active loaded pipe. suspended() is used on chunk unload, after which the same instance may become active again. Permanent destruction invokes removed(), then invalidated(). connectionsChanged(change) reports a committed transition on one side among NONE, PIPE, and BLOCK.

Two rules follow. Unregister temporary external-network memberships on suspend and restore them on activation; permanently clean resources up on removal. addDrops should only calculate drops: loot evaluation may be repeated or occur before or after removal. Do not decrement buffers while calculating drops.

Use PersistentType to encode any state that must survive a world restart. Simply having a Java field does not persist it. Call markChanged() after server-side changes. The distributor fixture stores the next-output index; restoration creates a component with that index and attached binds it to the current pipe. Without a codec, an ordinary component is reconstructed by its factory with initial state.

Custom flows and behaviors require codecs: PipeFlowDefinition and PipeBehaviourDefinition constructors reject components without persistence. The component's PersistentType ID must match its component ID. Register the flow component in PIPE_COMPONENT_TYPES, then the flow definition in PIPE_FLOW_TYPES, and reference it with PipeType.flow(id). Behavior similarly uses PIPE_BEHAVIOUR_TYPES and behaviour(id).

Network presentation is separate from world persistence. PipeSyncChannel<T> specifies an ID, codec, and maximum size. PipeComponentSync<C,T> specifies the server snapshot, client application, and policy. Under ON_REQUEST, components call requestSync(channelId). Under WHEN_CHANGED, the runtime marks the channel for sending after markChanged(); it does not observe arbitrary Java field assignments. Register channels in PIPE_SYNC_CHANNELS in advance, and do not let one channel ID serve two components of the same pipe.

Client snapshot application must tolerate repeats: if another application during the same update fails, a revision may be accepted again. For rendering, PipeView.presentation(channelId, valueClass) gives the latest decoded client value. It is not a means of inspecting server state.

In BCCE 8.0.24, each persisted/synchronized component payload is limited to 64 KiB, total pipe state to 1,200,000 bytes, and the number of component records to 256. Sync also obeys the lesser of 64 KiB and PipeSyncChannel.maxBytes. Unknown or unreadable component state is preserved for future recovery; it must not simply be replaced with defaults and treated as a brand-new component.

When to Implement Your Own Flow

PipeFlowComponent and PipeBehaviourComponent extend PipeRuntimeComponent. They receive lifecycle, tick, connection, and activation hooks, plus postAttachmentTick after attachments run and server-side entityCollided. Custom ports are exposed via itemPipePort(side), fluidPort(side), mjPort(side), and externalEnergyPort(side).

A custom flow does not automatically acquire the standard travelling-item queues or internal fluid sections. For items, a bridge connects the standard transport flow to a custom flow's public ItemPipePort. However, the item teleportation and direct fluid/energy transfer API facades discussed later operate on specific standard flows. Declaring PipeMedium.FLUID or PipeMedium.MJ does not make a custom buffer a BCCE-native buffer.

PipeMutationContext.worldAccess() is a borrowed world context available during an active server callback; it must not be retained until the next tick. PipeExecutionContext also exposes activity checks, loaded-neighbor checks, color changes, and deferred connection refresh. refreshConnections() schedules reevaluation rather than recursively rebuilding the graph from inside a callback.

Public PipeEventListener<E> defines the shape of a generic handler, but BCCE 8.0.24 has no corresponding registration or dispatcher wired up. Implement concrete component interfaces from this chapter rather than relying on a nonexistent universal pipe event bus.

Source Code

Sources: PipeType.javaS376, PipeComponentType.javaS350, PipeComponentLifecycle.javaS348, PipeComponentSync.javaS349, PipeRuntimeComponent.javaS372, PipeFlowDefinition.javaS363, PipeBehaviourDefinition.javaS345, PipeMutationContext.javaS367 and PipeExecutionContext.javaS359 in the same package; PipeTypeBridge.javaS586, PipeComponentRuntime.javaS578, PipeFlowBridge.javaS580, ApiPipeFlow.javaS575, PipeServiceImpl.javaS585; ApiItemPipeFlow.javaS065; CustomPipeFixtureAddon.javaS010.

III. Transport and Logistics

16Item Movement: Admission, Routing, and Ownership

In a standard item pipe, BCCE continues to own the travelling stack. Components may reject input, select an exit, modify permitted metadata, transfer an item into a public port, or forward it to another pipe. They do not receive unrestricted mutable access to the internal stack. This separation enables filters and logistics without copying the transport's internal queue into the addon.

Admission Filters and Exit Routes Have Different Purposes

ItemAdmissionComponent.accepts is called before insertion from both adjacent pipes and public injection. It receives ItemAdmissionContext with the pipe, incoming side, a stack copy, and mode. The predicate must be pure and return the same decision in SIMULATE and EXECUTE for identical state. Returning false rejects the whole offer, leaving it with the current owner; handler exceptions also count as rejection.

ItemRouteComponent.route runs when choosing an exit. It sees the incoming side, stack, and set of eligible candidates. This callback is suitable for selecting routes, but cannot replace an admission filter: the rejected item might already be inside the pipe by then.

The following is the functional part of a filter from the fixture, with simplified formatting. LOGIC_ID must be the same ID used to register the PipeComponentType.

java
final class IronFilter implements ItemAdmissionComponent,
        ItemRouteComponent, PipeConnectionComponent {
    public ResourceLocation typeId() { return LOGIC_ID; }

    public boolean accepts(ItemAdmissionContext context) {
        return context.stack().is(Items.IRON_INGOT);
    }

    public RouteDecision route(ItemRouteContext context) {
        EnumMap<Direction, Integer> weights =
            new EnumMap<>(Direction.class);
        for (Direction side : context.candidates()) {
            weights.put(side, side == Direction.EAST ? 1 : 0);
        }
        return new RouteDecision(weights);
    }

    public PipeConnectionDecision connection(PipeConnectionContext context) {
        return context.side() == Direction.WEST
                || context.side() == Direction.EAST
            ? PipeConnectionDecision.PASS
            : PipeConnectionDecision.DENY;
    }
}

Each of these three mechanisms has a distinct role: admission restricts contents; routing selects the east exit; and connection rules prevent physical connections on other sides. A single direction comparison inside routing cannot express all three restrictions.

How Weights Actually Work

RouteDecision specifies nonnegative weights for directions. At runtime, these multiply existing route weights. Zero disables a candidate; positive values boost its relative weight; directions absent from the map keep their original weights. A direction not already present among candidates is not added. A route blocked by an earlier component is not reenabled by a later positive multiplier.

Therefore new RouteDecision(Map.of(Direction.EAST, 1)) does not mean “east only.” To exclude other exits, explicitly assign them zero weight, as in the example. An empty map changes nothing. RouteDecision.blocked() examines only its own map; its true result for an empty map must not be interpreted as a final ban on all runtime candidates.

For items, weights establish weighted route ordering. They do not promise perfectly uniform round-robin selection or reserve neighboring inventory space. For strict cyclic scheduling, maintain your own index and apply redirects in a transit callback. That is how the second component in CustomPipeFixtureAddon works.

ItemTransportProfile.maxItemsPerCycle caps the size of one travelling stack subject to the item's maximum stack size. It is not a global items-per-tick budget. In BCCE 8.0.24's standard item flow, routingWeight is the initial neighbor-pipe weight during component-based routing, not unconditional priority across the entire network.

Injection While Preserving Metadata

An ordinary ItemPort suffices for transferring items as resources. ItemPipePort.inject(ItemInjectionRequest, mode) additionally carries ItemTransitData: optional color and speed in blocks per game tick. ItemInjectionRequest.from is the receiving pipe face through which the item enters. Inserting from the west means Direction.WEST, not the item's onward travel direction east.

ItemTransitData.DEFAULT has no color and speed 0.0. Zero is valid as a request for standard behavior: native injection normalizes missing or excessively small speeds. It does not guarantee that the item will stop. Speeds must be finite and nonnegative. The transit runtime additionally rejects speed changes above one block per tick.

You may also inject through PipeService.injectItem(level, position, request, mode). The facade does not load chunks. An absent pipe or item port produces zero transfer. The external sender deducts from its own stack only the amount actually accepted in EXECUTE; simulation transfers nothing.

Events Along an Item's Path

ItemTransitComponent.onTransit receives a temporary ItemTransitContext. Do not retain or use it after returning from the callback: the runtime closes it, and subsequent access fails. Inspection methods return stack copies. transitId identifies a travelling-item record; splitting off a distinct remainder may assign it a new ID.

Phases correspond to observable portions of the native algorithm, not an independent addon scheduler:

PhaseMeaning for an Extension
ENTERThe item has been accepted; custom payload may be attached
CENTERThe item reached the center, the main decision point
SIDE_CHECK, SPLIT, FIND_DEST, MODIFY_SPEEDNotifications of intermediate native routing stages
ROUTINGA route has been determined; permitted changes may be applied
EXITThe item is about to leave the pipe
TRANSFERRED, REJECTED, CANCELLED, EJECTEDNotifications of processing outcomes

General mutations are allowed on the server thread during CENTER, ROUTING, and EXIT. redirect(side) is allowed only in the first two and requires a genuinely connected exit. replace(stack, transit) requires a nonempty stack of the same count; it neither creates more items nor silently destroys any. Changing item type at the same count is deliberate addon logic, not additional BCCE recipe validation.

delay(ticks) holds an item and retries from the center after 1–1,200 ticks. reject() currently delays it by 20 ticks. eject(side) drops remaining items by a pipe face. Once a delay, ejection, or complete transfer is set, conflicting later context mutations are disallowed. Always check returned booleans.

Exceptions from transit handlers are logged, after which other handlers may continue. This is callback isolation, not transactional rollback of changes the handler may already have made to the external world.

Transfers and Partial Results

claim(ItemPort, mode) offers the travelling stack to a public port. In EXECUTE, BCCE deducts only the amount actually accepted. Partial acceptance leaves a remainder, delayed by one tick. transfer(targetWorld, targetPos, enteringFrom, mode) forwards similarly to an already loaded standard item pipe on the same server, preserving color, speed, and payload. Self-transfer to the same pipe and invalid destinations are rejected.

The following example comes from AdditionalPipesConformanceAddon. It runs inside a component whose target fields are configured by the addon:

java
public void onTransit(ItemTransitContext context) {
    if (context.phase() != ItemTransitPhase.CENTER
            || destinationWorld == null) return;

    ItemTransitTransfer result = context.transfer(
        destinationWorld, destination, enteringFrom,
        OperationMode.EXECUTE);

    if (result.status() == ItemTransitStatus.UNLOADED) {
        // Runtime сохранил предмет и отложил новую попытку.
        return;
    }
    // Не уменьшать context.stack() и не создавать второй предмет:
    // фактическое списание accepted уже выполнил runtime.
}

ItemTransitTransfer reports offered, accepted, remainder(), and status. MOVED and PARTIAL are actual transfers; SIMULATED is predicted acceptance without consumption. BLOCKED, UNLOADED, INVALID_TARGET, SERVER_ONLY, and FAILED explain why an ordinary transfer did not occur. succeeded() checks only accepted > 0, so it can also be true for simulation. Count completed deliveries with awareness of the mode.

Current transfer targets must be native PipeFlowItems. A custom full flow may instead accept an item through its public ItemPipePort / ItemPort, which is a different entry point. claim cannot undo an external port that changed inventory and then threw an exception. Public ports must honor their own transfer contracts.

Route Data Across Pipes and Saves

ItemTransitPayload stores opaque addon data separately from the item and its Data Components. Keys are namespace:path values at most 128 characters long. Each entry is limited to 4,096 bytes, and the total budget is 16,384 bytes including key lengths. Arrays are copied on input and output; with and without return new values.

setPayload is available on the server in ENTER, CENTER, ROUTING, and EXIT; the runtime carries this payload across pipe boundaries and world saves. Good uses include a destination ID, logistics-task number, or version of a custom route format. Do not store a duplicate of the whole item stack or an unbounded history of every pipe traversed.

In LogisticsPipesParityAddon, a string address is encoded as UTF-8 under lp_fixture:destination. The payload itself does not resolve the address into the world, load chunks, or guarantee reachability: that is the addon's responsibility. Transfer IDs and native receipts are likewise not an unlimited distributed transaction; the item pipe's receipt queue is limited to 512 IDs.

Source Code

Sources: ItemAdmissionComponent.javaS320, ItemRouteComponent.javaS324, RouteDecision.javaS383, ItemTransitContext.javaS327, ItemTransitData.javaS328, ItemTransitPayload.javaS329, ItemTransitTransfer.javaS332, ItemTransportProfile.javaS333 and related contexts and enums in the same package; PipeItemTransitRuntime.javaS582, PipeServiceImpl.javaS585; Pipe.javaS047; PipeFlowItems.javaS050; CustomPipeFixtureAddon.javaS010, AdditionalPipesConformanceAddon.javaS004, LogisticsPipesParityAddon.javaS013.

III. Transport and Logistics

17Fluids and Energy in Pipes

Fluid and energy transport extends the shared resource model in two ways: controlling standard native flow and directly transferring between two loaded pipes. In both cases, BCCE continues to manage the standard pipe's internal sections. Components never receive mutable arrays of tanks or energy buffers.

Profiles and Units

FluidTransportProfile defines FluidAmount maxPerTick and a delay in game ticks. The MJ profile, PowerTransportProfile, defines maximum transfer, loss at full transfer, resistance, and an extractor flag. Maximum must be positive, loss cannot exceed maximum, and resistance ranges from 0 to 1,000,000 micro-MJ per MJ. fromLoss and fromResistance derive the other parameter with integer arithmetic; extreme values may fail checked multiplication.

ExternalEnergyTransportProfile specifies separate integer units of external energy and an extractor flag. It does not define the FE/MJ conversion rate. Although public quantities use long, native FE flow has narrower integer limits.

ContextUnit
FluidAmount, FluidVolume, offeredMb, and centreCapacityMb fieldsmB
MjAmount and MJ in PowerMovementContext.offeredmicro-MJ; 1 MJ = 1,000,000 micro-MJ
ExternalEnergyRouteContext.amount and FE in PowerMovementContext.offeredinteger FE units
PowerFlowSettings.resistancePpmmillionths of a unit
Transfer profiles and delaysper game tick; delay is an integer number of ticks

A profile is a registered descriptor. Actual native pipe performance may also depend on configuration and behavior hooks. Do not present a profile value to players as guaranteed real network throughput without verifying it.

Routing and Movement Limits

Fluid may enter the center from multiple sides simultaneously, so FluidRouteContext includes inputs instead of a fabricated single input. FluidRouteComponent sees the combined central volume and eligible outputs. MjRouteComponent and ExternalEnergyRouteComponent see one input and an offered amount in their medium. They return RouteDecision: zero blocks routes, positive values multiply weights, and omitted directions remain unchanged.

To limit quantities rather than select exits, implement FluidMovementComponent.limitFluidMovement. It receives a phase, side, fluid variant/volume, offered mB, center capacity, and mode. The callback can only reduce the amount. The runtime clamps its result between zero and the current offer. It cannot replace the fluid, add volume, or override a rejection by an earlier component.

FluidMovementPhase distinguishes external insertion (INSERT), evaluation of input sections (BEFORE_CENTRE), entry into the center (TO_CENTRE), exit from the center (FROM_CENTRE), delivery to a neighbor (OUTPUT), and service-initiated transfer (PIPE_TRANSFER). Do not add these events as independent delivered volumes: the same fluid may pass through several phases.

For MJ/FE, PowerFlowComponent.configure modifies PowerFlowSettings: speed, receiver, disabled flag, and supported loss parameters. For MJ, -1 in loss/resistance retains the native-derived value; for FE, both loss fields must be -1. Components cannot change the medium or re-enable a flow already disabled by an earlier rule. Configuration callback errors preserve the prior working settings.

limitPower reduces offered energy during REQUEST, EXTRACT, SEND, PIPE_TRANSFER, and RECEIVE. REQUEST propagates demand, not already transferred energy; SEND occurs before applying loss. An amount-limiter error blocks that specific movement. This differs from configure failures, which retain previous settings. Neither fluid nor energy limiters may mutate state during SIMULATE.

Direct Pipe-to-Pipe Transfers

BuildCraftServices.PIPE_RESOURCES provides PipeResourceService. transferFluid, transferMj, and transferExternalEnergy take the source level/position/side, destination level/position/side, limit, actor, and mode. Fluid transfers additionally take a FluidMatcher.

Both worlds must belong to the same server, and the operation runs on its thread. Both pipes must already be loaded. The service validates both sides before modifying buffers, never force-loads chunks, checks permissions, and deducts from the source only what the destination actually accepts. Both sides receive PIPE_TRANSFER phases, so local limiters remain effective. Repeated calls within a tick respect separate native budgets for direct transfer.

This snippet shows a PipeResourceTickComponent callback: the addon defines targetWorld, targetPos, actor, and the relevant sides. For a real machine, the actor must identify its actual origin.

java
public void onResourceTick(PipeResourceTickContext context) {
    if (context.medium() != PipeMedium.MJ) return;

    PipeMjTransferResult result = context.transfers().transferMj(
        context.world(), context.pipe().position(), Direction.WEST,
        targetWorld, targetPos, Direction.EAST,
        MjAmount.ofMj(4), actor, OperationMode.EXECUTE);

    // result.transferred() уже списано с источника и принято целью.
    // result.remainder() продолжает принадлежать источнику.
}

PipeResourceTickComponent runs after the native server fluid/energy tick. Its PipeResourceTickContext is valid only inside the callback and is closed afterward. Ordinary item pipes and custom full flows do not generate this native resource callback in the current runtime.

PipeFluidTransferResult, PipeMjTransferResult, and PipeFeTransferResult distinguish requested and transferred quantities. Fluid results retain the variant in FluidVolume, while remainder() indicates what was not transferred. In SIMULATED results, transferred amounts are estimates rather than actual deductions. MOVED / PARTIAL report successful execution; BLOCKED means a zero offer or no available supply/capacity, and UNLOADED means a missing loaded endpoint. INVALID_SOURCE, INVALID_TARGET, SERVER_ONLY, DENIED, and FAILED distinguish bad sources, bad targets/media, wrong thread/side, permissions, and failures.

The current implementation accepts only native PipeFlowFluids, PipeFlowPower, and PipeFlowForgeEnergy. It is not a universal bridge between any two FluidPort or MjPort objects. For custom buffers, use their own port contracts and addon-managed logic.

A successful native transfer is atomic in memory during server execution. Writing two chunks or worlds to disk is not a global transaction. If an addon needs crash-safe cross-world delivery, it must define a durable journal and reconciliation protocol. SIMULATE estimates immediate transfer capacity, but does not reserve it for a future tick.

Source Code

Sources: FluidTransportProfile.javaS319, PowerTransportProfile.javaS382, ExternalEnergyTransportProfile.javaS313, FluidRouteContext.javaS318, FluidMovementContext.javaS315, FluidMovementPhase.javaS316, PowerFlowComponent.javaS378, PowerFlowSettings.javaS379, PowerMovementContext.javaS380, PowerMovementPhase.javaS381, PipeResourceService.javaS368 and result/status types in the same package; PipeFlowEventRuntime.javaS581, PipeResourceRuntime.javaS583, PipeResourceServiceImpl.javaS584; PipeFlowFluids.javaS048, PipeFlowPower.javaS051, PipeFlowForgeEnergy.javaS049.

III. Transport and Logistics

18Connecting External Blocks and Pipe Attachments

A physical connection, resource port, and attachment are different things. A port indicates whether a resource can transfer; a connection rule decides whether the link is allowed; and an attachment is something installed on a pipe face that interacts with the pipe. This distinction is especially helpful when integrating other mods' blocks, which should not need to subclass an internal BCCE pipe class.

External Endpoints

Register PipeEndpointProvider in PIPE_ENDPOINT_PROVIDERS. When inspecting a particular neighboring block, it receives PipeConnectionContext: level, source pipe, side, and neighbor state. Return Optional.empty() for blocks your provider does not own. Lookups must not mutate the world or load chunks. The provider registry freezes with other API registrations, while found endpoints refer to current neighboring runtime objects.

PipeExternalEndpoint exposes canConnect, optional resource and signal ports, and a geometric extension distance in blocks. The runtime clamps extension to −0.25…0.5 and substitutes zero for nonfinite values. This defines connection geometry, not transfer range.

Registering an endpoint provider does not automatically connect a neighboring block. Standard connection logic requires canConnect to allow it and the endpoint and pipe to share at least one resource medium. Signal capability alone does not satisfy this transport-connection check. Local connection rules and blocking attachments must also permit the link.

PipeConnectionComponent operates on one pipe's component, while registered PipeConnectionRule is a global rule. DENY takes precedence over ALLOW. For pipe-to-pipe checks, colors and flow compatibility are validated separately: a positive API rule cannot connect arbitrary incompatible media. The current runtime treats a connection callback exception as denial.

PipeView.connectedSides() and endpoint(side) describe established topology. They do not replace checking whether the receiver can actually accept a particular item or fluid. A neighbor might have changed after connection establishment, so real transfers must handle rejection or partial acceptance.

Attachments as Separate Extensions

A PipeAttachment lives on one face and returns its typeId() and side(). It can implement a blocking plug, specialized port, sensor, or other side logic. It defines connection blocking, solid-face status, redstone connectivity, bounding box, pick stack, drops, lifecycle, and activation. Item, fluid, MJ, FE, and signal ports are optional.

PipeAttachmentType<A> associates an ID, PipeAttachmentFactory<A>, and optional persistence. The factory receives PipeAttachmentPlacementContext with a read-only pipe view, side, copy of the placement stack, actor, and mode. Its result must report the requested type ID and side.

PipeService.placeAttachment(...) supports validating placement with SIMULATE before performing it with EXECUTE. An occupied side, unknown type, missing pipe, or factory error is reported in PipeAttachmentResult.reason(). Simulation may still invoke the factory, so object creation must not have external side effects. Server execution also verifies that a stateful attachment can initially serialize before installation.

With removeAttachment(...), simulation checks presence and execution removes the object and calls its removal hook. These facades are not complete player actions: they do not deduct placement items from inventory or issue compensation automatically. In BCCE 8.0.24, passing an actor does not imply an automatic permission-service call for placement/removal. The caller is responsible for authorizing the action and accounting for items.

State, Ticks, and Client Behavior

placed(PipeExecutionContext) runs after installation, restored after loading, tick on an active pipe, and removed() on removal. activate receives SIMULATE on the client and EXECUTE on the server. Use isServerSide() and isActive(), safe neighbor lookups, changeColor, refreshConnections, and requestAttachmentSync(side) for inspection and changes.

An attachment without a codec is reconstructed after loading by calling its factory with an empty stack and unknown actor. Such an attachment must derive all state from its type and side, or be stateless. A stateful attachment codec must also restore the side; the runtime constructor checks that it matches its installed position. State is serialized for initial client creation and updates as well. The current total envelope limit is 64 KiB.

drops(fortune) runs during loot evaluation and may be called repeatedly. It reads state; it is not a request to empty the attachment. The runtime isolates many attachment callback exceptions, and a tick exception stops ticking that attachment instance. This protects the main loop but neither fixes faulty domain logic nor supplies missing persistence.

Source Code

Sources: PipeExternalEndpoint.javaS360, PipeEndpointProvider.javaS357, PipeConnectionContext.javaS353, PipeConnectionRule.javaS355, PipeAttachment.javaS339, PipeAttachmentType.javaS343, PipeAttachmentPlacementContext.javaS341, PipeAttachmentResult.javaS342, PipeExecutionContext.javaS359; PipeEndpointRuntime.javaS579, PipeServiceImpl.javaS585, ApiPipePluggable.javaS576; Pipe.javaS047.

III. Transport and Logistics

19Statements and Gate Programs

The Statement API describes conditions and actions installed in gates. A condition asks about current state; an action performs work when the gate activates it. Both have stable IDs and parameter schemas. A gate's program stores selected conditions and actions, their parameters, sides, and connections between rows.

Definition, Availability, and Execution

TriggerType binds an ID, ParameterSchema, and TriggerEvaluator. ActionType similarly binds an ActionExecutor. Register them in TRIGGER_TYPES and ACTION_TYPES, and register parameter types in PARAMETER_TYPES.

A separate StatementContributor in STATEMENT_CONTRIBUTORS exposes these IDs for the current context through StatementCollector. The distinction is fundamental: registration says that a statement exists; contribution says where it can be offered to users. Nonapplicable contexts should contribute nothing rather than publishing a universally available command.

StatementContributor.scope(id) specifies a statement's location:

ScopeContext
INTERNALGate-local state without a selected face
INTERNAL_SIDEDGate/pipe state on the selected face
EXTERNALNeighboring block-entity state on the selected face

The default scope is internal. For someone else's IDs, contributors must also return INTERNAL: the runtime queries metadata from multiple contributors and should not encounter conflicting claims about other statements. Sided or external slots require an explicit side.

StatementService.availableTriggers/availableActions performs discovery, with an overload that restricts scope. These calls must be read-only. Even if an action contribution context contains EXECUTE, discovering available options is not permission to run them. Separate evaluateTrigger and executeAction methods handle execution.

Parameters as Contracts

ParameterType<T> specifies an ID, ApiCodec<T, OpaqueData>, and optional StatementSuggestionProvider<T>. Suggestions help populate UI choices but do not replace semantic validation. ParameterSpec defines a slot ID, type ID, and required flag; ParameterSchema stores their immutable list.

Values are passed as StatementParameters: a map from slot IDs to ParameterValue<T>. get(slotId, expectedType) returns Optional<T> only when type IDs match. Equal IDs do not prove that incompatible Java implementations of a parameter type are interchangeable; addons must use the canonical registered type.

StatementValidation.valid rejects duplicate schema slot IDs, missing required values, wrong type IDs, and unknown extra slots. Semantic validation of T remains with the codec and addon callback: the schema cannot know whether a destination address or temperature threshold makes sense.

This snippet registers a minimal statement pair based on ApiV2FixtureAddon. It runs during bootstrap; ALWAYS_ID, NOOP_ID, and CONTRIBUTOR_ID are the addon's namespaced IDs.

java
BuildCraftApi.registry(BuildCraftRegistries.TRIGGER_TYPES).register(
    ALWAYS_ID,
    new TriggerType(ALWAYS_ID, ParameterSchema.EMPTY,
        (context, parameters) -> true),
    registration);

BuildCraftApi.registry(BuildCraftRegistries.ACTION_TYPES).register(
    NOOP_ID,
    new ActionType(NOOP_ID, ParameterSchema.EMPTY,
        (context, parameters) -> StatementResult.success()),
    registration);

BuildCraftApi.registry(BuildCraftRegistries.STATEMENT_CONTRIBUTORS)
    .register(CONTRIBUTOR_ID, (context, collector) -> {
        collector.addTrigger(ALWAYS_ID);
        collector.addAction(NOOP_ID);
    }, registration);

In a real addon, an action uses only available views and services, while the contributor limits where the action may be installed. This minimal example demonstrates the chain “definition → discovery → execution,” not useful automation by itself.

Context and Simulation Mode

StatementContext contains ExtensionContext views, actor, mode, and optional side. Real gate callbacks may expose StatementContexts.GATE, GATE_POSITION, and EXTERNAL_TARGET; the last is available only with an external neighbor. Some discovery calls lack particular views, so use Optional rather than assuming all are present.

The native bridge evaluates triggers with SIMULATE and activates actions with EXECUTE. Triggers must only observe, never consume items or change the world. A key detail for actions: StatementService.executeAction(..., SIMULATE) checks that the type exists and its schema is valid, then returns success without calling the executor. This checks structural validity, not whether the action could truly succeed in the world.

During execution, unknown actions, invalid parameters, null results, or callback exceptions become StatementResult.Status.FAILED with diagnostics. Equivalent trigger failures evaluate to false. StatementResult also supports PASS, SUCCESS, and DENIED. The native gate activates an action through its bridge; it does not turn the returned result into a retry scheduler.

The current bridge constructs the real gate actor as AutomationActor.unknown(). Merely receiving an actor does not imply that callbacks have the machine owner's identity or a player's permission context. World-changing actions must respect actually available information and the contracts of services they call.

Reading and Replacing Programs

GateService.gate(level, pos, side) finds a real gate on the face of a loaded pipe. GateView.program() returns GateProgram, and control() provides an optional GateControl.

Each GateRule corresponds to one physical gate row; its trigger and action may be independently absent during editing. A nonempty StatementSlot has kind, ID, optional side, and parameters. Do not discard empty rows when serializing your own representation: that would change their positions and connection meanings.

GateProgram.connections[i] links row i with row i+1. AND/OR behavior is defined by the gate variant itself. These flags do not store separate operators for each pair. The connections list must not exceed the number of gaps; missing entries are treated as disabled.

setProgram(program, SIMULATE) validates physical capacity, slot kinds, statement availability, and parameters. EXECUTE repeats validation, replaces the whole program, and sends updates. Supplying fewer rows clears the remaining rows. To edit a single row safely, start with a full current snapshot and preserve all other rows.

In BCCE 8.0.24's GateLogic, exposing control() alone does not enforce server-side access, and setProgram performs no universal permission check. Modify programs only from an authorized server handler. Successful SIMULATE does not reserve gate state for later execution.

Source Code

Sources: statement/S483 — all schema, value, callback, contribution, and context types; GateProgram.javaS204, GateRule.javaS205, GateView.javaS207, GateControl.javaS203, GateService.javaS206; StatementServiceImpl.javaS554; StatementApi2Bridge.javaS567; GateLogic.javaS574; GateServiceImpl.javaS573; ApiV2FixtureAddon.javaS005.

III. Transport and Logistics

20Signals and Wire Networks

The Signal API separates a transmitted value's type, a local signal source, and the resulting network value. For a sensor, this means publishing its own measurement, observing the combined network state, and not treating received network values as a new independent local source.

Channel Types and Ports

SignalChannelType<T> specifies a namespaced ID, default value, codec, and SignalCombiner<T>. The combiner describes how values are merged; it does not create a physical network. A SignalPort<T> is bound to one channel type and provides connected(), value(), publish(value, mode), publishedValue(), and receive(value, mode).

value() is the observed network value. publishedValue() is this endpoint's local contribution. Its default implementation returns the channel default, so actual sources must override it. receive delivers the combined result to an external endpoint; the default does nothing. Separating source and received values prevents accidental self-sustaining signal loops.

publish and receive return SignalUpdateResult<T> with changed, previousValue, and currentValue. In simulation, changed may describe a potential transition; a true result does not prove the server has already published anything. External port implementations must honor SIMULATE themselves.

Channels Actually Connected to the Runtime

BuildCraftSignalChannels defines 16 colored channels, buildcraft:white through buildcraft:black, with conversions to/from DyeColor. Their built-in types use Boolean, default false, and logical OR. These are digital colored-wire signals, not analog redstone levels from 0 to 15.

The public SignalChannelType<T> model supports custom signal types. However, SignalServiceImpl.port in BCCE 8.0.24 supports only classic colored-channel IDs. Registering an arbitrary SignalChannelType<Integer> does not connect it to the wire network; custom types require a corresponding executable backend.

SignalService.port(level, pos, side, channelId) finds a port on a loaded block, attachment, or neighboring endpoint. For classic pipes, the returned Optional may contain a port even without an installed wire: check connected() to verify an actual connection. Service availability, channel registration, and the physical network are separate concerns.

An external block entity can implement SignalPortProvider.signalPort(side, channelId). The side is the face of that block entity where the wire connects. The returned port must use the exact registered channel-type object: the current bridge compares object identity, not just IDs. Do not construct a new SignalChannelType<Boolean> on every lookup.

Alternatively, expose signals through PipeExternalEndpoint.signalPort(channelId) or an attachment's signal port. These provide endpoints in an existing network and do not bypass the classic-channel backend limitation.

Network Observation and Updates

SignalService.networks(level) returns server-side SignalNetworkView<?> objects describing channel type, combined value, and endpoints. SignalEndpoint<T> contains a position, side, and channel type. The current client implementation returns an empty list. This is an inspection API, not a mutable graph for laying wires.

Publishing a local server value schedules a wire-runtime update. The combined signal propagates according to its lifecycle; do not assume every connected device has already finished processing during the same Java call to publish. Custom sources must persist their published local value separately if it should survive block-entity loading. A channel codec describes value encoding, not automatic persistence of arbitrary external block fields.

Source Code

Sources: SignalChannelType.javaS475, SignalCombiner.javaS476, SignalPort.javaS479, SignalPortProvider.javaS480, SignalEndpoint.javaS477, SignalNetworkView.javaS478, SignalService.javaS481, SignalUpdateResult.javaS482, BuildCraftSignalChannels.javaS474; ClassicSignalChannels.javaS577, SignalServiceImpl.javaS587; ApiV2FixtureAddon.javaS005.

III. Transport and Logistics

21Automation Actions and Stripes

The Automation API defines a standard request to an executor, including action provenance and mode. It is not a universal engine that can perform any AutomationRequest on its own: an executor must be registered for the request's kind().

Requests and Executors

AutomationRequest defines operation-kind ID, actor, mode, and origin. AutomationActionType<R> associates the ID with a Java request class and AutomationHandler<R>. AutomationService.execute finds the type in AUTOMATION_ACTION_TYPES, checks class compatibility, and calls the handler. A missing handler returns PASS; the wrong request class returns FAILED.

Standard DTOs describe five intents. BreakBlockRequest identifies a block to break; PlaceBlockRequest supplies the desired block state; UseItemRequest supplies an item, target, and use face; MoveItemRequest supplies a matcher and maximum count; and MoveFluidRequest supplies a matcher and maximum volume. Origin identifies the automation source, while target identifies the affected object.

Robotics bootstrap currently registers built-in handlers for break/place/use. MOVE_ITEM and MOVE_FLUID have public DTOs and IDs but no built-in executor in BCCE 8.0.24. Without an additional handler, they return PASS. A DTO existing in the API does not prove the operation is available in the installed modules.

AutomationResult contains status, nonnegative amount, and detail. PASS means unhandled, SUCCESS means successful, DENIED means forbidden, RETRY suggests trying later, and FAILED reports failure. The service does not turn RETRY into a job queue; scheduling belongs to the caller. The unit of amount depends on the operation. Built-in block/use handlers report 1 per action; custom item/fluid transfers must define their amounts according to the request.

Simulation and Real Side Effects

The dispatcher passes mode to the handler. Custom handlers must enforce permission checks themselves and avoid modifying the world, resources, or their own state during SIMULATE. An actor in the DTO does not mean the generic dispatcher already checked permissions.

The built-in break handler checks permissions, non-air state, and the ability to break through the platform path before simulation success. The place handler simulates by returning success after permission checks, without performing the full future placement. The use handler checks permissions and a nonempty stack but does not actually use it during simulation. Their simulated success therefore cannot guarantee eventual success if the environment changes.

UseItemRequest stores and returns a copy of ItemStack. The built-in handler uses a working copy, and AutomationResult does not include the replacement item or leftover after use. This DTO is not a ready-made transaction for “use an item in a machine inventory slot, then correctly replace it with an empty bucket or damaged tool.” A machine needing that behavior must explicitly manage inventory via a suitable resource contract or custom handler.

The placement DTO also lacks a source inventory for the construction item. If placement is part of a machine or robot task, resource availability and consumption must be defined by that system's lifecycle. Do not add extra deductions on the assumption that a generic dispatcher has already consumed resources.

Exception behavior depends on the runtime layer: the shared AutomationService implementation isolates RuntimeException and null, while the 26.X-family override invokes handlers directly. Write custom handlers that represent expected denials via AutomationResult rather than exceptions. The API documentation cannot promise identical exception isolation for every callback on every target.

Stripes: Handler Chains and Working Stacks

Extend Stripes using a dedicated StripesHandler registered in STRIPES_HANDLERS. Handlers run in descending priority() order, default zero. Each first checks whether the activation applies to it; return PASS for unrelated cases. The first different status stops the chain.

StripesContext contains the world, pipe position, outgoing side, actor, mode, optional player, and StripesOutput. targetPos() is the adjacent position in that direction. When activated without an item, hasItem() is false. stack() returns a copy of the current working stack; mutating the copy does not save anything. Use replaceStack and consume(count) for changes.

Each handler gets a fresh working stack. If one mutates its context but returns PASS, the next does not see those changes. External world side effects, however, are not rolled back, so a handler must not place a block and then return PASS. The current Stripes dispatcher uses EXECUTE; handlers invoked elsewhere with SIMULATE must still honor that mode.

StripesOutput.sendItem(stack, from) reports whether sending succeeded, while dropItem ejects an item. A StripesContext constructor without player/output uses discard(), a stub that neither sends nor drops resources. This is useful for isolated calls but is not the complete Stripes runtime.

DENIED, RETRY, and FAILED also terminate the chain because it distinguishes only PASS from other statuses. Modify the working stack only for outcomes that really should be committed. Useful handlers must coordinate input consumption, output sending, and result status; merely returning success(1) does not perform those actions automatically.

Source Code

Sources: automation/S081 — requests, action kinds, results, services, and Stripes contracts; AutomationServiceImpl.javaS534; AutomationServiceImpl.javaS039; RoboticsApi2Bootstrap.javaS572; StripesRegistry.javaS589; StripesApi2Bridge.javaS588; ApiV2FixtureAddon.javaS005.

III. Transport and Logistics

22Item Lists and Specialized Matching Rules

The API supports three independent item-list extension mechanisms: a named filter, an item that contains its own filter, and custom matching semantics for an individual list entry. Choose based on where configuration is stored and who needs it.

Named Definitions

ItemList contains an immutable list of ItemListEntry records, each pairing an ItemMatcher with an enabled flag. ListMatchMode controls evaluation: ANY requires at least one enabled match, ALL requires all enabled entries to match, and NONE requires none to match. With no enabled entries, ANY is false, while ALL and NONE are true. This is important when editing filters: disabling all entries does not always reject all items.

Register named definitions in NAMED_ITEM_LISTS before freeze. ItemListService.register(id, definition) performs that registration; it is not a way to rewrite registries dynamically during game ticks. list(id), ids(), and matches(id, stack) read or evaluate definitions. Unknown IDs produce an empty Optional or false, not an allow-all filter.

Named lists suit shared addon policies, such as materials accepted by several machines. They do not store quantities or reserve matching items. A definition containing ItemMatcher functions also does not become arbitrarily serializable merely because it has an ID.

Items Containing Their Own Filter Configuration

ItemListAdapter checks whether an ItemStack contains a supported list (supports) and whether a target stack matches it (matches). The current ItemListServiceImpl finds such an adapter directly on listStack.getItem() when that item implements the interface. It does not iterate a separate registry of stack-backed list adapters.

This allows custom filter items to interact with BCCE list consumers without subclassing internal ItemList_BC8. The item owns the configuration, so matching must read listStack without modifying it. ItemListService.isList(stack) and matches(listStack, target) use this path, unlike the overloads accepting a namespaced ID.

Extending Entry Matching Semantics

To make a custom item participate in BCCE's existing list modes, implement ListMatchAdapter registered in LIST_MATCH_ADAPTERS. It receives ListMatchContext containing ListMatchType (TYPE, MATERIAL, CLASS), copies of source/target, and a precise flag. supports specifies which comparisons it handles; matches determines the result. An integration could, for example, match tools by its own categories or fluid containers by contents without coupling consumers to concrete item classes.

Current ListHandler OR-combines applicable adapters: any successful match is enough. An adapter that does not apply must return false from supports, rather than claiming everything and returning false from matches. For TYPE, when no adapter handles a comparison, there is a fallback for the same item without exact variant matching; falsely universal supports can disable that fallback.

examples(context) returns Optional<List<ItemStack>>. Optional.empty() asks the runtime to find examples by enumerating candidates and calling matches, whereas Optional.of(emptyList()) explicitly supplies no examples. Accurate examples can reduce expensive UI work for specialized rules. precise is an input to each adapter's semantics, not an automatic universal exactness mode for every custom comparison.

Source Code

Sources: ItemList.javaS226, ItemListEntry.javaS228, ListMatchMode.javaS232, ItemListAdapter.javaS227, ItemListService.javaS229, ListMatchAdapter.javaS230, ListMatchContext.javaS231, ListMatchType.javaS233; ItemListServiceImpl.javaS548; ListHandler.javaS041; ApiV2FixtureAddon.javaS005.

III. Transport and Logistics

23Request Logistics: Demand, Promises, and Confirmed Delivery

The Request API lets blocks and docking stations advertise item demand and accept deliveries. Its advanced form tracks promised and received amounts persistently. It does not plan routes, choose robots, or create a second inventory: those responsibilities remain with robot runtime or addon logistics.

Simple Request Providers

ItemRequest describes a stable ID, matcher, minimum, maximum, and priority. Counts refer to items: minimum is nonnegative and maximum must be at least minimum. RequestProvider.requests() reports current requests; offer(requestId, stack, mode) tries to accept an item for one and returns an actual ItemTransferResult.

Stable IDs avoid relying on slot indexes. Consumers must not assume the first entry always represents the same request: the order and set may change. Providers are responsible for reducing demand after real insertion, correctly implementing SIMULATE, and matching the actual request criteria.

RequestService.provider(level, pos, side) finds a provider on a loaded block entity or docking station. The current providers(level) enumerates providers at registered robot docks, deduplicating objects. It does not scan every block entity in the world or maintain a complete index of addon requesters.

For discovery by classic delivery robots, a matcher must provide at least one truly matching ItemStack through ItemMatcher.examples(). A predicate that correctly implements matches but supplies no examples may be insufficient for that integration path.

When You Need RequestLedger

ManagedRequestProvider combines a provider with RequestLedger, requestTime(), and insertRequested(requestId, offered, mode). The last method only performs real insertion into an inventory/port and reports actual acceptance; it must not update the ledger itself. The ledger accounts for each successful result once.

RequestLedger is created on its owner's thread and enforces that affinity for every operation, including saving. For a block entity, this is the server thread. Its constructor callback should only mark the host dirty, not recursively mutate the ledger. Open individual requests with open(definition, owner, now, durationTicks).

Once opened, the ledger tracks three quantities: confirmed accepted items, actively promised items, and items still available for promises. RequestLedger.RequestView shows request state, PromiseView shows one promise, and snapshots() returns current request views. For each request snapshot:

available = max(0, maximum − confirmed − reserved).

outstanding = max(0, maximum − confirmed) includes quantities already promised to other couriers. available prevents assigning the same demand to multiple senders. requests() exposes only open matcher-bound requests with positive availability, sorted by descending priority and then ID.

In the current ledger, reaching minimum does not complete a request: COMPLETE occurs when confirmed == maximum. The minimum remains part of the demand descriptor. For a requirement of “exactly 32 items,” use minimum = maximum = 32, as in the fixture.

Calling open again with the same ID, compatible numeric parameters, and the same owner rebinds the definition; it does not create a new batch or extend the deadline. Do not reuse a completed ID to start a new delivery. After reconciliation, remove the old request using forget and assign a new stable ID for the next batch.

Reserving a Promise

reserve(requestId, token, count, actor, now, durationTicks) creates a promise for a particular courier. The token is a UUID kept by the sender. Despite “up to” wording in a short comment, the implementation requires the entire count to fit within available; otherwise it returns an empty Optional. It does not reduce requested reservations automatically.

Reusing a token is idempotent only for the same request ID, actor, and count. Inconsistent reuse causes an error. A reservation deadline cannot exceed its parent request's deadline. Duration zero means no ordinary expiry; times are game ticks, not system milliseconds.

release(token, actor) frees the unfulfilled remainder and may be invoked by the courier or request owner. cancel(requestId, owner, reason) closes demand and active promises and is limited to the creator. Request states are OPEN, COMPLETE, CANCELLED, TIMED_OUT; promise states are ACTIVE, COMPLETE, RELEASED, CANCELLED, TIMED_OUT.

For remote loaded providers, the RequestService.reserve/cancel/deliver facade additionally checks inventory-access permissions. Direct ledger calls enforce internal owner/courier identity but do not replace world-access policy.

Executing Deliveries and Preventing Duplicates

Delivery uses two different UUIDs: a reservation token binds a promise, while a delivery ID identifies one insertion attempt. RequestLedger.deliver limits the offered stack to the promise remainder, checks actor, matcher, deadlines, and phase, then calls insertRequested.

DeliveryResult deliberately distinguishes accepted from acknowledged:

StatusacceptedacknowledgedMeaning
MOVED / PARTIALNew quantity actually inserted0The sender deducts only accepted
SIMULATED0Potential acceptanceDeduct nothing
DUPLICATE0Previously acknowledged quantityThe provider was not called again; nothing is deducted twice
Other rejection0Usually 0Inspect status and diagnostics

The remaining field in a calculated delivery result refers to that particular offer, not the entire request. Some early RequestService failures are constructed without the offered count and return zero here. Do not treat it as an instruction to delete or replace the source stack after an error. Use request/promise snapshots for remaining demand and only new accepted quantities for deductions. BLOCKED can mean a mismatched item, full destination, or ledger limits; UNAVAILABLE can mean unknown/unbound promises or requesters; DENIED, EXPIRED, CLOSED, and FAILED refine the failure cause.

This snippet shows a sender that already owns a stack, a reservationId, and a unique deliveryId. requests refers to RequestService. It demonstrates handling a result, not a complete universal extraction transaction.

java
RequestLedger.DeliveryResult result = requests.deliver(
    level, origin, destination, side,
    reservationId, deliveryId, offered,
    actor, OperationMode.EXECUTE);

if (result.status() == RequestLedger.DeliveryStatus.MOVED
        || result.status() == RequestLedger.DeliveryStatus.PARTIAL) {
    int newlyDelivered = result.accepted();
    // Зафиксировать расход только newlyDelivered в собственном
    // протоколе владения отправленным стеком.
}

A real sender must define when the item moves out of its inventory into transit. Do not repeatedly submit a copy of the same item under new delivery IDs. Use the previous delivery ID when retrying acknowledgement of an already performed attempt, or a new ID for an actual new insertion of the remainder. LogisticsPipesParityAddon illustrates managed-provider and routing-payload structures, but its stock counter is a fixture, not a production inventory.

If a provider throws after possibly inserting items or returns an invalid transfer result during EXECUTE, the ledger closes the indeterminate request and its promises. This prevents blindly repeating unknown outcomes forever; it does not roll back an external inventory that may already have changed. Senders need a reconciliation path, not an automatic retry using a new ID.

Persistence, Timeouts, and Guarantee Limits

Hosts must save ledger.save() alongside inventory and restore it before opening requests. Matchers are not serialized: after loading, the addon rebinds definitions through open using the same stable IDs and numeric parameters. Unbound requests and promises are retained, but do not serve new deliveries.

load(bytes) rejects corrupt or unrecognized formats without replacing current state with partially decoded data. loadPreserving(bytes) retains a bounded unsupported payload unchanged and forbids further writes until successfully decoded. This preserves incompatible data; it does not justify silently starting a fresh empty ledger.

advance(now) moves server game time forward monotonically and closes expired records. Hosts must call it with current world time, including after chunk load. Default ManagedRequestProvider methods and the delivery path also call it. Thus SIMULATE delivery does not insert items but may still update timeout accounting. Do not promise that every SIMULATE call leaves all ledger fields byte-for-byte unchanged.

The current ledger is limited to 1 MiB, 512 requests, 2,048 promises, and 4,096 delivery receipts. When the receipt window fills, old acknowledgements are removed; deleting old terminal promises also removes associated receipts. Duplicate prevention holds within this bounded window, not for every UUID forever. Use forget after the owner reconciles possible outstanding deliveries.

The ledger supplies persistent local accounting but does not make two worlds' inventory saves transactional. Crash-resilient logistics require a separate protocol for parcel ownership, shipment commit, and acknowledgement reconciliation. These limits matter especially for teleporting pipes and LogisticsPipes-style addons.

Source Code

Sources: ItemRequest.javaS424, RequestProvider.javaS427, ManagedRequestProvider.javaS425, RequestLedger.javaS426, RequestService.javaS428; RequestServiceImpl.javaS570, RequestSupport.javaS571; LogisticsPipesParityAddon.javaS013.

IV. World, Robots, and Presentation

24Areas, Maps, and World Access

BCCE separates geometry from world interaction. An area defines where work should happen; an access context defines what can be read or obtained at that location; an actor-bound operation determines what changes are allowed. The same geometry can therefore serve a robot, machine, construction template, and map without passing BuildCraft internals between them.

Choosing Appropriate Geometry

BlockBox defines a rectangular volume in block coordinates, including both boundaries. Its constructor normalizes the corners, taking the smaller coordinate as minimum on each axis. A box from (0, 0, 0) to (1, 1, 1) therefore contains eight blocks. contains(BlockPos) tests membership, while volume() returns long and detects arithmetic overflow through Math.multiplyExact.

Zone supports arbitrary shapes. Its only required method is boolean contains(BlockPos pos). By default, bounds() is empty, meaning no known bounding box—not an empty zone. Enumerating such a zone requires your own search limits or traversal budget.

Path exposes an ordered set of points through List<BlockPos> points(). It has no pathfinding, movement speed, obstacle handling, or interpolation. empty() only checks the list. Custom implementations should return stable lists and immutable coordinates, because the interface does not copy implementation data.

AreaProvider combines optional box(), zone(), and path() views. Having a box does not imply having a path. Implementing this interface also does not guarantee every native machine discovers your block: BCCE 8.0.24 retains internal discovery points for classic area providers. Custom consumers should use AreaProvider and connect it to their own host/context as needed. Machines also expose the related public MachineAreaView contract, described in the machines chapter.

java
BlockBox bounds = new BlockBox(
    new BlockPos(10, 64, 10),
    new BlockPos(14, 66, 14)
);

Zone shell = new Zone() {
    @Override public boolean contains(BlockPos pos) {
        if (!bounds.contains(pos)) return false;
        return pos.getX() == bounds.min().getX()
            || pos.getX() == bounds.max().getX()
            || pos.getY() == bounds.min().getY()
            || pos.getY() == bounds.max().getY()
            || pos.getZ() == bounds.min().getZ()
            || pos.getZ() == bounds.max().getZ();
    }

    @Override public Optional<BlockBox> bounds() {
        return Optional.of(bounds);
    }
};

In this model, contains receives world coordinates. If the shape algorithm uses local coordinates, explicitly subtract bounds.min(). Mixing these coordinate systems is especially noticeable in Filler: the mask works near world origin but shifts incorrectly when moved.

Reading the World Through a Restricted Context

WorldAccessService.open(Level, BlockPos origin, AutomationActor) creates a world view tied to an origin and actor. scoped(Level, BlockPos, AutomationActor, BooleanSupplier valid) adds a lifetime condition. The resulting WorldAccessContext neither exposes Level nor provides direct methods for modifying blocks.

It provides world time, server-side status, loaded-position checks, block state, pipe ID and connections, and item, fluid, and external-energy ports. Pipe metadata are not mutable pipe components. The context deliberately returns Optional.empty() for absent objects, unloaded chunks, or invalid access; an empty result alone does not identify which condition occurred.

The current implementation runs operations only on the server thread, never loads chunks, and rechecks the context's validity when previously obtained ports are used. Once valid becomes false, storing the port in a field will not make later access work. Expired or otherwise invalid contexts return -1 from gameTime(), deny permission checks, and perform no transfers.

java
WorldAccessContext world = BuildCraftApi
    .service(BuildCraftServices.WORLD_ACCESS)
    .open(level, machinePos, actor);

BlockPos target = machinePos.relative(Direction.EAST);
Optional<BlockState> state = world.blockState(target);
Optional<ItemPort> input = world.itemPort(target, Direction.WEST);

Block-state reads are restricted by lifetime, thread, and chunk availability. Permission checks apply to permission(...) and port operations. Item and external-energy ports use INVENTORY_ACCESS; fluid ports use FLUID_PLACE for insertion and FLUID_DRAIN for extraction. This is not general world sandboxing: code separately given a Level still retains normal Java and Minecraft capabilities.

Do not make a callback-scoped context long-lived by wrapping it with your own () -> true predicate. Store positions and IDs, and obtain a fresh context on the next permitted call. Even successful simulation does not lock a neighbor's contents until the next tick.

Distinguishing Properties from Permissions

WorldPropertyService holds named WorldProperty predicates, useful for rules such as “this block counts as my ore” or “this position is suitable for work.” It provides register, get, properties, and test; unknown IDs test false. It does not build a cache, scan for matching blocks, or grant permission. If a predicate caches results, its owner must invalidate them. The mere existence of WorldProperty.clear() does not prove automatic cache management on every runtime path.

WorldRuleService answers other questions through isSoft, isReplaceable, mayBreak, and mayPlace. A soft block is not necessarily authorized for breaking, and allowed placement does not mean construction resources have been reserved. mayBreak and mayPlace are checks; actual changes should go through an appropriate actor-bound automation mechanism. On the server, mayPlace also simulates the pre-placement event.

Source Code

Chapter sources: area/S076; world/S515; WorldAccessServiceImpl.javaS556; WorldRuleServiceImpl.javaS559; WorldPropertyServiceImpl.javaS558; IAreaProvider.javaS566; TileQuarry.javaS058.

IV. World, Robots, and Presentation

25Schematics: Capturing State and Building

A schematic must preserve a block's meaning, resource requirements, and restoration method. For addons, this means more than serializing NBT: distinguish configuration from actual items and fluids, define rotation behavior, and ensure objects can recover after an addon is temporarily removed.

BCCE's public model centers on a snapshot element and a behavior adapter. SnapshotElement holds addon-owned data and returns typeId(). SnapshotElementType<E> associates the ID with PersistentType<E, OpaqueData>. A SchematicAdapter tells Builder how to capture, evaluate, and place a block. Entities have a separate EntitySchematicAdapter.

Connecting a Block to Architect and Builder

An integration has four parts:

  1. Create a snapshot element containing the minimal data needed to restore the object.

  2. Define its codec and persistence schema through PersistentType.

  3. Implement an adapter that recognizes both your block and your saved elements.

  4. Register the type and adapter before content registration closes.

ContentRegistrar.blockSchematic(String path, SnapshotElementType<E> type, SchematicAdapter adapter) registers both. The type ID must match registrar.id(path) and its persistence ID. You may register with separate snapshotElement(...) and schematicAdapter(...) calls if your architecture needs it, but must not register the type twice.

java
ContentRegistrar bc = BuildCraftContent.addon("example");
bc.blockSchematic("relay_snapshot", relaySnapshotType, relayAdapter);

Here relaySnapshotType and relayAdapter are addon-created instances of the corresponding public types. SchematicFixtureAddon shows a full PersistentType definition and joint registration; its example placement body returns success without changing the world. Do not copy that stub into a production addon as a working builder implementation.

During capture, the runtime processes adapters by descending priority(), then registration ID. supports(SchematicCaptureContext) identifies which blocks an adapter handles. capture(...) returns an element or an empty result to continue searching. The returned type must be registered. If no custom adapter applies, the current implementation falls back to BuildCraft's native snapshot handling.

Capture context contains level, snapshot origin, current position, state, and actor. Do not store the context or world object in the snapshot element. Use position - origin for relative location; a type ID and state data suffice to identify the element.

supportsElement(SnapshotElement) determines ownership of a saved element and defaults to false. An adapter implementing only supports(context) may capture blocks but then fail to be found during blueprint placement. Usually check both type ID and the expected Java class. Avoid “any non-air block” predicates in production; they steal other mods' machines and lose their special behavior.

Describing Resources and Build Order

A minimal adapter compiles with capture and place, but an inventory-bearing machine needs more. Builder asks several distinct questions that must describe one consistent operation.

Adapter ContractPurpose
requirements(element, level)Full requirements for display and resource calculation.
placementRequirements(element, level)Resources for initially placing the housing; defaults to full requirements.
requiredFluids(element, level)Fluids required for construction; defaults to requirements().fluids().
deferredItems(element, level)Items inserted after the block appears.
missingDeferredItems(element, level, position)Items still missing from an already placed block.
insertDeferredItem(..., stack, mode)Inserts a deferred item and returns its unaccepted remainder.
requiredBlockOffsets(element)Dependencies on adjacent blocks in relative coordinates.
canPlace(element, context)Whether placement at the specified position is allowed.
readyToPlace(element, context)Whether dependencies are ready for placement now.
isPlaced(element, level, position)Whether the world matches the expected result.

SchematicRequirements stores lists of items and FluidVolume values; items are copied on construction and reading. Empty requirements mean no cost, so do not leave them as the default for an expensive machine assuming Builder will discover the recipe itself.

Imagine a cabinet consisting of a housing and three installed modules. Initial requirements should include the housing, while modules can be deferred. missingDeferredItems must account for modules already installed. Otherwise, retrying after a load may request them twice or treat an unfinished cabinet as complete. In SIMULATE, insertDeferredItem computes a remainder without modifying inventory; in execution it returns the actual remainder.

rotate(element, Rotation) and requiredBlockOffsets must transform orientations and dependencies consistently. Returning the original element is correct only for state truly independent of direction. isAir defines the role of an empty element in a plan. The default placeUnchecked calls place; “Unchecked” does not absolve the addon of validating world changes.

Placing an Element and Handling Results

SchematicService offers three public operations: capture a block, capture an entity, and place one element. place(SnapshotElement, SchematicPlacementContext) receives snapshot origin, position, actor, and OperationMode. It returns SchematicResult with PASS, SUCCESS, MISSING_RESOURCE, DENIED, or FAILED and diagnostic detail.

Status must reflect the real operation. SUCCESS during simulation means the handler believes placement is feasible without performing it. PASS is not successful construction. The bridge to existing Builder treats only SUCCESS as success.

A key limitation: directly calling SchematicService.place does not consume requirements, run the full canPlace → readyToPlace → place sequence, or create a transaction. For custom elements, it locates and invokes the adapter's place. Ordinary Builder manages its own planning and supply cycle, but an external caller must implement those steps itself. Likewise, an actor in context does not mean any arbitrary level.setBlock call automatically checks permissions.

Public automation can be called inside place. This snippet maps results, with desiredState obtained from validated snapshot data. Confirm support for the relevant automation action separately.

java
AutomationResult action = BuildCraftApi
    .service(BuildCraftServices.AUTOMATION)
    .execute(new PlaceBlockRequest(
        context.level(), context.origin(), context.position(),
        desiredState, context.actor(), context.mode()
    ));

SchematicResult.Status status = switch (action.status()) {
    case SUCCESS -> SchematicResult.Status.SUCCESS;
    case DENIED -> SchematicResult.Status.DENIED;
    case PASS -> SchematicResult.Status.PASS;
    case RETRY, FAILED -> SchematicResult.Status.FAILED;
};
return new SchematicResult(status, action.detail());

This snippet does not replace resource checks, block-entity handling, or a custom isPlaced test. Builder predicate contexts may also use an unknown actor: Api2SchematicBlock creates one when no player was provided. Therefore enforce permission at execution rather than reusing a preview decision.

Preserving Contents Without Duplication

InventoryCopyPolicy exists for hidden item lists not exposed through normal platform storage. A policy contains an ID, a set of blocks, and allowed/denied DataPath values. An empty block set means it applies globally. Explicitly restrict addon policies to your own blocks whenever possible.

java
bc.inventoryCopyPolicy(InventoryCopyPolicy
    .forBlock(bc.id("relay_inventory"), bc.id("relay"))
    .allow("Items")
    .deny("InternalBuffer", "TransientItems")
    .build());

DataPath.of("InternalBuffer", "TransientItems") defines a sequence of keys, not a dotted string or a universal JSONPath language. Empty and whitespace-only segments are invalid. API policies match exact paths, and deny takes precedence over allow among applicable policies. Builder also has its own configuration rules; API policies do not automatically override them.

The native mechanism separates item contents from a block's housing NBT: known inventories are removed from restore data and their items transferred later. Otherwise Builder could place a filled container from NBT and then insert the same items again through resource requirements. A custom SchematicAdapter storing opaque payload must independently follow the same principle; registering a policy does not validate an arbitrary snapshot format.

Entities, Unknown Elements, and Model Boundaries

EntitySchematicAdapter receives SchematicEntityCaptureContext(Level, origin, entity, actor). It defines priority, matching, capture, and placement, but supplies relative position, resource requirements, and rotation rather than block dependencies. Do not interpret an entity UUID as an instruction to resurrect that same world object: the element should describe a new entity and permitted data. Requirements and codecs must preserve inventories and equipment without spawning free items.

If an element type is absent, the schema is newer than supported, or migration fails, persistence retains UnknownSnapshotElement(typeId, schemaVersion, payload), preserving original bytes for later recovery. The built-in wrapper for an unavailable block skips its position without modifying the world: it reports isPlaced/isBuilt == true to the scheduler but refuses actual placement. This is protective skipping of unknown content, not confirmation that the required block exists. Saved data remains available if the addon returns.

BlueprintMetadata(name, author, bounds) and SnapshotKind.TEMPLATE/BLUEPRINT provide metadata and snapshot-kind types, but not a universal complete blueprint object. The current API has no public builder that produces an arbitrary full blueprint in one call from those two objects. Use schematic services for elements and the separate library contracts for document formats.

Source Code

Chapter sources: schematic/S457; ContentRegistrar.javaS116; SchematicServiceImpl.javaS529; Api2SnapshotPersistence.javaS526; Api2SchematicBlock.javaS055; Api2SchematicEntity.javaS056; InventoryContentPolicy.javaS057; UnavailableSchematicBlock.javaS530; SchematicFixtureAddon.javaS018.

IV. World, Robots, and Presentation

26Templates, Filler, and the Electronic Library

A construction template stores shape; a blueprint stores specific contents. This gives two extension points: selecting the item that fills a template location, and defining the positions included in a Filler mask. File import/export is a separate concern: exchanging documents between tools and the player's local electronic library.

Placing Addon Items Through Templates

TemplateHandler defines boolean handle(Level level, BlockPos pos, Player actor, ItemStack stack) and optional acceptsItem(ItemStack). Register handlers with TemplateService.register(id, priority, handler); they run by descending priority, then ID. The first true stops processing.

acceptsItem matters for non-BlockItem items: without a positive result, native Builder will not select the item as template material from inventory. handle must still recheck that the item applies to it. The dispatcher does not promise to call only the same handler whose predicate originally matched the item.

In the native Builder path, it first selects and reserves one item, checks placement permission, and obtains an automation player. Only then does it call handle. Do not predicate behavior on stacks of three or five items: TemplatePlacementFixtureAddon uses a stack size of three as a test marker, but a real template step passes one reserved item. Check IDs and required components.

TemplateHandler has no OperationMode. Its handle is an executing callback; acceptsItem is an observational test. Do not invoke handle for previews. If it returns false or throws, the service can try another handler, so it must not modify the world before declining. Native Builder returns the reserved material after a failed task, but does not automatically roll back arbitrary world side effects.

Adding a Filler Pattern

FillerPattern converts FillerPatternContext into FillerMask. The context contains the box, facing, and a map of named string options. The mask exposes bounds() and tests world positions through includes(BlockPos).

java
ResourceLocation id = bc.id("checker_floor");
bc.fillerPattern(new FillerPatternType(id, context -> {
    BlockBox box = context.bounds();
    return new FillerMask() {
        @Override public BlockBox bounds() { return box; }

        @Override public boolean includes(BlockPos pos) {
            if (!box.contains(pos) || pos.getY() != box.min().getY()) {
                return false;
            }
            int x = pos.getX() - box.min().getX();
            int z = pos.getZ() - box.min().getZ();
            return ((x + z) & 1) == 0;
        }
    };
}, false));

The final FillerPatternType argument, internalOnly, hides the type from normal user selection in the Filler bridge when true, while retaining registration for programmatic consumers. FillerPatternService.type(id) and types() look up registered patterns.

The working bridge does apply addon masks through the existing Filler executor. However, the current standard wrapper calls the mask with Direction.NORTH and empty options, reports zero parameters, and provides no custom rotation. These context fields are not a promise of a complete pattern-parameter editor. External parameterized tools may call createMask with their own values; the native UI exposes only what it actually passes.

The mask is checked for every position in the volume. Do not scan the whole world inside includes, rebuild a large graph per call, or load chunks. Precompute geometry once in createMask and make membership tests cheap and deterministic.

Defining an Electronic Library Format

LibraryFormatHandler converts bytes of an external format to/from a snapshot document's CompoundTag:

java
CompoundTag decode(byte[] bytes) throws IOException;
byte[] encode(CompoundTag snapshot) throws IOException;

Register using LibraryService.register(id, extension, priority, handler). Extensions exclude the dot, are lowercase, and follow [a-z0-9][a-z0-9_-]{0,15}. bcnbt is reserved. The runtime rejects duplicate IDs or file extensions. formatForFilename chooses by filename suffix case-insensitively.

java
LibraryService library = BuildCraftApi.service(BuildCraftServices.LIBRARY);
library.register(
    bc.id("portable_blueprint"), "xblueprint", 100, portableFormat
);

Optional<CompoundTag> document = library.decode(filename, bytes);

Here, portableFormat implements LibraryFormatHandler. decode returns an empty result for an unknown extension and throws IOException for malformed content in a known format. encode(formatId, document) requires a registered ID. The service supplies defensive copies of documents and arrays and limits encoded data to 8 MiB in the current runtime. These safeguards do not establish trust: validate lengths, required fields, and format structure yourself.

An electronic-library document contains serialized BuildCraft blueprint state. Arbitrary custom NBT fields alone are insufficient for import. BlueprintLibraryFixtureAddon demonstrates conversion and validation of BLUEPRINT / TEMPLATE but does not implement full conversion of every blueprint field.

Distinguish File Conversion from Import

decode and encode only convert representations. importClient(filename, bytes) also validates the document, computes its content identity, and stores it in the local electronic library. clientEntries() returns LibraryEntry(contentId, name, kind), clientDocument(contentId) returns a document copy, and exportClient(contentId, formatId) returns encoded bytes.

These methods operate on the player's local library, not on the server world's storage. Importing a document does not put a blueprint into inventory, build anything, or bypass server-side blueprint upload rules. contentId identifies content within the library, rather than the filename. Editing a document may change its content identity.

Before importing, show the user the name, format, and expected scope of the operation. A custom integration needs access to the client environment and its local data; the mere availability of a Java interface on a dedicated server does not supply that environment.

Source Code

Chapter sources: template/S504; filler/S169; library/S221; TemplateBuilder.javaS028; TemplateServiceImpl.javaS555; FillerApi2Bridge.javaS525; LibraryServiceImpl.javaS528; TemplatePlacementFixtureAddon.javaS019; BlueprintLibraryFixtureAddon.javaS006.

IV. World, Robots, and Presentation

27Robots: Programs, Tasks, and Work Reservations

Robotics extensions begin with a task. An addon describes a small, persistent operation, and BCCE connects it to an existing robot executor. Access goes through RobotHandle and RobotControl; extending the internal AIRobot is unnecessary.

Keep three identities separate: a task type defines its format and behavior, a board type identifies tasks available on that board, and a board program connects the board to the Programming Table and a task factory. Registering a board descriptor alone does not create this whole chain.

Finding and Commanding a Robot

RobotService.robot(Level, long id) looks up a loaded robot. robots(Level) returns loaded instances, ordered by ID in the current implementation. Neither call loads an absent entity, and the service is not a search across every robot recorded in world saves.

RobotHandle provides an ID, optional owner, position, summary RobotStatus, current task type, and optional RobotControl. Do not serialize the handle itself; store its ID and look the robot up again. RobotStatus is an operational summary: the enum contains CHARGING, but the standard BCCE 8.0.24 implementation mainly returns ERROR, DOCKED, TRAVELLING, IDLE, and WORKING. Do not make your logic depend on every enum value being emitted.

RobotControl provides moveTo(target, mode), assign(task, mode), cancelTask(mode), and taskPhase(). In SIMULATE, assignment checks whether a persistent task can be accepted; it does not run the task or simulate the eventual route. Ordinary assignment must run on the server. Your command interface must verify that the player may control the chosen robot: these methods do not receive a separate identity for the player issuing the command.

In the current runtime, moveTo installs a separate navigation AI as the primary task. It is a robot-control command, not a guaranteed substep of the current tick. If you invoke it from your own task, account for replacement of the current executor; do not call it on every tick and expect the task stack to remain unchanged.

Implementing a Persistent Task

RobotTask requires two methods: ResourceLocation typeId() and RobotTaskResult tick(RobotTaskContext context). Results are RUNNING, COMPLETE, RETRY, or FAILED. Keep task state small and explicitly persistent: a work stage, target position, request ID, or progress counter.

java
final class InspectTask implements RobotTask {
    static final ResourceLocation ID =
        ResourceLocation.tryParse("example:inspect_area");
    private int progress;

    InspectTask(int progress) { this.progress = progress; }

    @Override public ResourceLocation typeId() { return ID; }

    @Override public RobotTaskResult tick(RobotTaskContext context) {
        // Здесь выполняется один ограниченный шаг вашей проверки.
        progress++;
        return progress >= 3
            ? RobotTaskResult.complete()
            : RobotTaskResult.running();
    }
}

This is a tick-driven state machine, not a ready-made world-scanning task. Do not scan thousands of blocks in one tick; retain the continuation in task fields. RobotTaskContext supplies the world, handle, owner's AutomationActor, and public methods for robotics reservations and requests.

For a task to be assignable, register RobotTaskType<T>(id, taskClass, persistence) through ContentRegistrar.robotTask. Its id must match the PersistentType ID. At assignment, the runtime checks the instance's Java type and whether it can be encoded. Simply implementing RobotTask without a correctly registered codec is insufficient.

RobotTaskConformanceAddon provides a complete verifiable example: progress is stored in four bytes, the format has its own ID, and the decoder validates length and range. Such checks matter: saved data must not become a negative index, an enormous loop, or an immediate resource payout.

In the current executor, RETRY keeps the task active just like RUNNING. It does not schedule a delay automatically. If you need to retry in 20 ticks, store a counter or next eligible tick in the task. FAILED ends the task unsuccessfully; an exception from tick also fails it.

Connecting a Board to In-Game Programming

java
ResourceLocation boardId = bc.id("inspection_board");

bc.robotTask(inspectTaskType);
bc.robotBoard(new RobotBoardType(
    boardId, 2, Set.of(InspectTask.ID)
));
bc.robotBoardProgram(new RobotBoardProgram(
    boardId,
    3_200,
    "blue",
    bc.id("textures/entity/inspection_robot.png"),
    robot -> new InspectTask(0)
));

inspectTaskType is a previously created RobotTaskType<InspectTask>; the shown fragment registers it once. If the type was already registered earlier in your integration, do not call bc.robotTask(inspectTaskType) again: that would cause an ID conflict. A board type contains a nonnegative tier and a set of permitted task types. RobotBoardProgram holds a positive programming cost in MJ, color, robot texture, and a Function<RobotHandle, ? extends RobotTask> factory.

Allowed colors are fixed: clean, green, blue, red, and yellow. The program ID must match the board type. The current bridge skips programs without a board type or with no tasks; when creating work, it verifies that the factory returned an allowed, persistent task type. A factory failure does not assign new work; the runtime retries later.

BuildCraftRobotBoards provides stable IDs for built-in boards, including carrier, miner, lumberjack, builder, and other types. This allows references to known content, but does not mean every internal AI task on a classic board is exposed as a registrable RobotTaskType. Define an explicit board-to-task connection for your custom program.

Handling Suspension and Recovery

RobotTaskLifecycle offers optional callbacks: onAssigned, onResumed, onSuspended, onCompleted, onFailed, and onCancelled. Phases are QUEUED, ASSIGNED, RUNNING, SUSPENDED, COMPLETED, FAILED, and CANCELLED. A terminal callback runs no more than once per task instance.

When a robot unloads, its task is suspended and runtime reservations are released. Persisted data retains the task and descriptors of required reservations. Upon recovery, those reservations must first be reacquired; until that succeeds, the task remains SUSPENDED and performs no work step. onResumed indicates a return to execution after this process, not the creation of a new logical task.

The current executor limits task payloads to 64 KiB, each resource payload to 8 KiB, tracked reservations to 16, and the total frame to 128 KiB. These implementation limits encourage compact state. For unknown types or malformed persisted state, the runtime retains the original frame without executing unknown code; recovery requires the appropriate type and another decode attempt.

A completion callback should not be the only place where a long-running operation issues an irreversible reward. The server can stop between an external side effect and persistence of the task. Use operation IDs and the request ledger for deliveries; for custom operations, persist an explicit completion stage and make recovery idempotent.

Reserving Work Targets

A robotics resource is not necessarily an item. RobotResource may represent a target block, station, work area, or external quota. RobotResourceRequest(resource, amount) requires a positive amount; RobotResourceLease exposes its owner, resource, granted amount, active(), and close().

java
Optional<RobotResourceLease> claim = context.acquire(
    new RobotResourceRequest(new BlockRobotResource(target), 1)
);
if (claim.isEmpty()) {
    return new RobotTaskResult(
        RobotTaskResult.Status.RETRY, "target_busy"
    );
}

BlockRobotResource stores a position and optional side. The built-in block reservation type accepts only an amount of 1 and uses the existing robotics registry to prevent two robots from claiming the same target at once. This reserves work; it does not prohibit a player from modifying the block or authorize a robot to break it.

Do not reacquire the same reservation needlessly on every tick: the executor tracks issued leases, so that could exhaust its limit. Keep the active lease in temporary runtime state or acquire it at a phase transition. After recovery, follow the reacquisition contract rather than serializing the Java lease object.

For a custom resource, register RobotResourceType<R>(id, resourceClass, persistence, acquirer). RobotResourceAcquirer.acquire(Level, robotId, resource, amount) must return an active lease or an empty result. Repeated close calls must be safe, and an old lease must not clear a reservation subsequently acquired by another robot.

Leases issued during task execution are associated with the executor and released at completion. External code calling RobotService.acquire outside a task callback must manage lease lifetimes itself. Tracking also requires correctly serializing the registered resource; an unserializable resource does not become a durable reservation just because active() exists.

Docks and Requests

RobotService.dock(level, pos) finds a dock without a side, while the overload with Direction finds a side-specific dock. For a pipe plug, the side forms part of its address, so do not always substitute null. RobotDock exposes position, side, occupancy, and typed ports.

BuildCraftDockPorts provides ITEMS, FLUIDS, MJ, and EXTERNAL_ENERGY keys. dock.port(type) returns an available capability, not a promise that every dock supports everything. Built-in item and fluid wrappers respect dock settings. A custom DockPortType<T> has an ID, Class<T>, and DockPortResolver<T> receiving a RobotDockContext with world, position, side, and occupancy. Register the type through ContentRegistrar.dockPort; a locally created key with an unknown ID does not connect a port.

For logistics, RobotTaskContext.reserveRequest, cancelRequest, and deliverRequest pass the task owner to the request service. Delivery uses a reservation ID and delivery ID, preserving duplicate-delivery suppression. RequestLedger defines the full lifecycle of those identifiers; moving the robot and retaining ownership of the carried stack are still responsibilities of your program.

Events: Use Verified Hook Points

Register RobotEventListener in ROBOT_EVENT_LISTENERS to receive RobotEventContext: event kind, robot, world, optional actor, and a defensive copy of the held item. Decisions combine as DENY > ALLOW > PASS; denial stops iteration. The current implementation orders listeners by registration ID.

RobotEventKind includes spawn, place, interact, dismantle, docking, and task events. In the standard BCCE 8.0.24 implementation, dispatch calls are wired for PLACE, INTERACT, and DISMANTLE. The presence of TASK_STARTED, TASK_FINISHED, TASK_FAILED, SPAWN, DOCK, and UNDOCK in the enum does not guarantee their automatic dispatch. Use RobotTaskLifecycle for custom tasks; do not make critical behavior depend on unwired events.

Source Code

Chapter sources: robot/S429; RobotServiceImpl.javaS063; ApiTaskAI.javaS569; RoboticsApi2Bootstrap.javaS572; BCRoboticsBoards.javaS030; ItemRobot.javaS033; EntityRobot.javaS043; RobotTaskConformanceAddon.javaS017; RobotBoardFixtureAddon.javaS016.

IV. World, Robots, and Presentation

28Plants and Resource Generation

A robot should understand a crop through its behavior, while a resource generator should select worlds through an explicit rule. These extensions solve different problems: CropAdapter participates in existing robot actions, whereas ResourceDepositRule controls how an existing world-generation profile applies.

Making a Crop Available to Robots

CropAdapter defines seed recognition, planting checks, planting itself, maturity checks, and harvesting. Register it through CropService.register(id, priority, adapter); adapters() lists CropRegistration entries. Higher-priority adapters are checked first; equal priorities are ordered by ID.

Answer five questions when designing an adapter:

MethodExpected Behavior
isSeed(ItemStack)Pure check whether an item can be used as a seed.
canSustainPlant(Level, seed, pos)Check the planting location without consuming a seed or modifying the world.
plant(Level, Player, seed, pos)Perform planting on behalf of the supplied player.
isMature(BlockGetter, state, pos)Pure check whether the block is ready for harvest.
harvest(Level, pos, NonNullList<ItemStack> drops, Player)Perform harvesting and return drops through the supplied list.

In the built-in planting path, pos identifies the supporting block where the robot uses the seed; the plant usually occupies pos.above(). During harvesting, pos identifies the plant itself. Confusing these coordinates makes the robot recognize seeds correctly while continually rejecting suitable farmland.

java
BuildCraftApi.service(BuildCraftServices.CROPS)
    .register(bc.id("orchard_crop"), 200, orchardAdapter);

orchardAdapter implements all five methods. Both the service and built-in planter/harvester use this contract. Set priority so your crop-specific logic runs before a generic handler while recognizing only crops it owns.

For planting, the service checks isSeed, then canSustainPlant, and finally calls plant. For harvesting, it first checks maturity and passes a separate copy of the drops list. Only a successful harvest commits changes to the list. If a harvester throws, the runtime stops harvesting instead of handing a potentially modified block to another adapter. Returning false may continue the search, so reject a crop before modifying it.

The drops list is the sole harvest result for an ordinary robot; the robot itself ejects the returned items. If an adapter both adds a stack to drops and spawns it in the world, items are duplicated. Likewise, decide who consumes the seed exactly once. The built-in planter additionally consumes one item if the successful handler left the stack count unchanged. This safeguard applies to the robot path, not every external call to CropService.plant.

CropAdapter has no simulation mode. isSeed, canSustainPlant, and isMature must be pure; do not call plant or harvest just to check feasibility. Built-in robots check permissions before changing the world, but a direct external service call does not inherit a robot's authority. Use the supplied Player when interacting with protection rules and the crop's own semantics.

Enabling a Built-In Resource in a Custom Dimension

ResourceDepositRule contains a rule ID, generation-profile ID, WorldTargetSelector, frequency multiplier, priority, and enabled flag. Register it through WorldgenService.register(rule, RegistrationContext) or ContentRegistrar.worldgen(rule).

java
bc.worldgen(ResourceDepositRule.builder(
        bc.id("moon_oil"), BuildCraftContentIds.Worldgen.STANDARD_OIL)
    .target(WorldTargetSelector.builder()
        .dimension(bc.id("moon"))
        .biomeTag(bc.id("oil_bearing"))
        .excludeBiome(bc.id("protected_crater"))
        .build())
    .frequencyMultiplier(0.5)
    .priority(100)
    .build());

For simple cases, use bc.oilInDimension(path, dimension) or its multiplier overload. The rule format is suitable for data-driven definitions, but this Java registration does not by itself install a datapack format or create a new generator.

WorldTargetSelector separately specifies included dimensions, dimension-type tags, excluded dimensions, biomes, biome tags, and excluded biomes. An empty inclusion set means all. Within each group, an ID or tag match is sufficient; between the dimension and biome groups, both must match. Exclusions always win. The oil-generation runtime checks dimension tags against DIMENSION_TYPE, not an arbitrary collection of dimension keys.

The service stores rules sorted by descending priority and then ID. Its working consumer in BCCE 8.0.24 is OilGenFeature, which selects the first enabled matching rule for the BuildCraftContentIds.Worldgen.STANDARD_OIL profile. This is connected to the platform world-generation path, not just a registered descriptor. The API does not, however, expose a public registry of arbitrary generators for any new profile.

Account for the following behavior of the current implementation:

Registering a rule changes future generation attempts. It does not request retrogen in already generated chunks or modify existing deposits. For diagnosis, check that Energy is loaded, dimension and biome IDs, the selected rule, and configuration before investigating the deposit's shape.

Source Code

Chapter sources: crops/S120; worldgen/S521; CropServiceImpl.javaS539; WorldgenServiceImpl.javaS560; AIRobotPlant.javaS032; AIRobotHarvest.javaS031; CropHandlerPlantable.javaS061; OilGenFeature.javaS531; OilGenFeature.javaS036; BCEnergyBiomeModifiers.javaS059; ContentRegistrar.javaS116.

IV. World, Robots, and Presentation

29Integration Items: Tools, Maps, Facades, and Drops

This part of the API connects an addon's existing content to BuildCraft's gameplay interactions. Usually, you only need to register an adapter with a precise applicability check. There is no need to extend BCCE's internal map item, wrench, or facade classes.

Rotating and Painting Blocks

BlockInteractionService accepts a RotationContext or PaintContext and returns a structured result. Contexts include world, position, block state, owner, and OperationMode. Rotation also receives a face; painting receives a color, hit position, and optional side.

PaintContext.color() == null has a specific meaning: remove the paint. It is not invalid input. Hit position and side matter for composite objects, notably pipes with attachments. The compatibility shorthand constructor uses the block center and no side, so pass the full context when the hit position is known.

Register RotationHandler in ROTATION_HANDLERS and PaintHandler in PAINT_HANDLERS. A handler returns PASS for an unrelated block, ROTATED / PAINTED on success, DENIED when prohibited, or FAILED on failure. The first non-PASS result ends dispatch. Do not use FAILED to mean “this is not my block”: that would suppress the standard fallback.

On the server, the dispatcher first checks chunk availability, BLOCK_INTERACT permission, and the interaction event. The custom callback must still honor SIMULATE. The legacy built-in rotation and painting handlers run only for EXECUTE; if their behavior cannot be safely predicted, simulation returns PASS rather than falsely claiming that the operation is impossible.

Supporting a Custom Wrench

WrenchService.isWrench(ItemStack) only recognizes wrench items. Integrate your own item through WrenchAdapter.recognizes(stack) and optional onUsed(WrenchUseContext) notification.

java
bc.wrench("service_wrench", new WrenchAdapter() {
    @Override public boolean recognizes(ItemStack stack) {
        return stack.is(serviceWrenchItem);
    }

    @Override public void onUsed(WrenchUseContext context) {
        // Начислите износ или обновите состояние вашего инструмента.
        // Callback относится к уже успешно обработанному действию.
    }
});

serviceWrenchItem is an item registered by the addon. WrenchUseContext supplies the player, hand, actual stack, and hit result. The stack is deliberately not a defensive copy, so the adapter can account for tool usage. The built-in bridge notifies all adapters that recognize the stack, so two overlapping handlers can both apply durability loss. Keep recognition conditions narrow.

The runtime also recognizes internal BuildCraft wrenches and, when enabled in configuration, the c:tools/wrench tag. The tag is suitable for simple recognition; use an adapter for special post-use behavior.

WrenchContext is a separate public container for side, tool, owner, and mode. However, the current WrenchService has no use(WrenchContext) method or universal wrench-based rotation command. Use BlockInteractionService for the actual operation; do not design an integration around a nonexistent method.

Item Labels

ItemLabelAdapter defines supports(stack), label(stack), and setLabel(stack, label, mode). Implement it directly in the item class or register it in ITEM_LABEL_ADAPTERS. The current service checks direct item implementations first, followed by registered adapters.

java
ItemLabelService labels = BuildCraftApi.service(BuildCraftServices.ITEM_LABELS);
Optional<String> oldLabel = labels.label(stack);
boolean accepted = labels.setLabel(stack, "Северный склад", OperationMode.EXECUTE);

A label should reflect the item's own adapter-managed content, not an arbitrary field in someone else's NBT. requireLabel checks support and a non-null string reference, but permits the empty string as a value. Unlike the map service, ItemLabelService does not substitute a stack copy for simulation: the setLabel(..., SIMULATE) implementation itself must be pure. Global registration does not authorize an addon to rename another mod's server-side stack based on an unchecked client request.

Custom Map-Location Carriers

MapLocationAdapter converts an item stack to and from a semantic MapLocationView. Its methods are supports, read, write, and clear. Implement it in the item or register it in MAP_LOCATION_ADAPTERS, including through ContentRegistrar.mapLocationAdapter.

MapLocationKind distinguishes CLEAN, SPOT, AREA, PATH, ZONE, and PATH_REPEATING. MapLocationView contains a label and optional geometry views; common cases have point, box, zone, path, repeatingPath, and clean factories.

java
MapLocationView destination = MapLocationView.point(
    "Разгрузка", stationPos, Direction.NORTH
);
MapLocationService maps = BuildCraftApi.service(BuildCraftServices.MAP_LOCATIONS);
if (maps.write(stack, destination, OperationMode.SIMULATE)) {
    boolean written = maps.write(stack, destination, OperationMode.EXECUTE);
    // Обработайте written: симуляция не резервирует состояние предмета.
}

The service reads a copy of the stack and, for simulated writes or clearing, passes a copy to the adapter. An adapter exception becomes an absent result or false. Actual execution mutates the supplied stack, so the caller must own it and synchronize it through the normal mechanisms.

CLEAN must have no geometry. For other kinds, use appropriate factories and validate consistency of persisted fields in your codec. MapLocationView is a stable semantic representation, but arbitrary Path and Zone implementations remain responsible for immutability and serialization. Zone does not supply a universal codec for any Java predicate.

This bridge is used by markers, the Zone Planner, map visualization, and robot actions. A custom item can thus carry a point, region, or route without impersonating a specific built-in item class. It does not create movement automatically: the map supplies data, while the consumer chooses the action.

Facade Materials and Phases

FacadeService.resolve(BlockState) and resolve(ItemStack) identify facade materials through FacadeMaterialAdapter. FacadeStateResult contains FacadeMaterial(state, transparent) and the source item, copied when read. source() is empty when no representative item exists.

Create a phased facade through createPhased(List<FacadePhase>, boolean hollow). Each phase defines a block state and active color. activeColour == null means an uncolored phase. The current service allows between one and seventeen phases; a color may appear only once, and an uncolored phase is allowed once.

java
Optional<ItemStack> facade = BuildCraftApi
    .service(BuildCraftServices.FACADES)
    .createPhased(List.of(
        new FacadePhase(Blocks.STONE.defaultBlockState(), null),
        new FacadePhase(Blocks.GLASS.defaultBlockState(), DyeColor.BLUE)
    ), false);

Success depends on whether the materials exist in the built-in facade collection. The normal backend uses its existing phased-item codec. An addon can provide its own FacadeMaterialAdapter, but returning an unrelated item, as the minimal stub in MiscParityFixtureAddon does, is useful only for testing the call, not for working gameplay.

FacadeRuleService operates at the material-discovery level: disable(ruleId, Block, provenance) excludes a block, and mapState(ruleId, BlockState, ItemStack, provenance) provides an item for a particular state. disabledBy and mappedStack reveal the chosen result. Rule IDs must be unique; conflicting mappings resolve by provenance priority and, on ties, deterministically by ID. An empty stack is invalid for mapState.

An item mapping does not make arbitrary geometry a valid facade: the built-in discoverer separately checks fluids, block entities, shape, and model type. Do not change rules after the cache has been built and expect all items to be rebuilt automatically: the public interface promises no such refresh operation.

Converting Stored Fluids into Item Drops

FluidDropProvider.createDrops(FluidDropContext) creates item representations of fluid volume without relying on loader-specific FluidStack types. The context contains FluidVolume and optional Level; use FluidDropContext.of(volume) when no world is available.

The FLUID_DROPS service aggregates results from all registered providers. This differs from a “first applicable handler wins” chain. If two providers both return a full volume of the same fluid, the result is doubled. Providers must restrict their applicability clearly and coordinate with other fluid-representation rules.

Return nonempty stacks; the service filters null and empty entries and makes defensive copies. The call only creates a drop description: it does not pour fluid into the world, reduce the source tank, or spawn items. The invoking game mechanism must connect the result to one-time removal of the fluid. Because the context may not include a world, never use context.level().get() without checking it.

Source Code

Chapter sources: block/S096; tool/S510; ItemLabelAdapter.javaS214; ItemLabelService.javaS215; map/S278; facade/S162; drop/S130; BlockInteractionServiceImpl.javaS535; WrenchUtil.javaS568; MapLocationServiceImpl.javaS551; FacadeServiceImpl.javaS544; FacadeRuleRegistryImpl.javaS543; FacadeStateManager.javaS064; FluidDropServiceImpl.javaS545; FluidDropRuntime.javaS062; MiscParityFixtureAddon.javaS014.

IV. World, Robots, and Presentation

30Content Presentation and the In-Game Guide

BCCE's public presentation layer defines resource identifiers, labels, and synchronized visual values. An addon can tell the client how to display its content without sending server components into the renderer or extending an internal renderer.

Registering a model or menu does not automatically create a Minecraft block entity renderer, screen, or network protocol. Built-in pipes already consume presentation metadata. For a custom block, the addon still implements its loader's normal client integration and uses the public presentation data as input.

Describing Static Appearance

ContentPresentation contains the content ID, title key, optional description, icon ID, and model ID. PipePresentation adds texture layers. StatementPresentation describes a condition/action label and icon. ParameterPresentation adds an ID for the parameter-editor type.

These records do not import net.minecraft.client. They may be constructed in common code, provided your own classes do not reference client-only types either. The package is called client because the data is intended for presentation, not because it is an instruction to load a screen on a dedicated server.

java
ResourceLocation typeId = bc.id("visual_pipe");
ContentPresentation base = new ContentPresentation(
    typeId, "example.pipe.visual", "example.pipe.visual.description",
    bc.id("gui/visual_pipe"), bc.id("block/visual_pipe")
);
BuildCraftApi.registry(BuildCraftRegistries.PIPE_PRESENTATIONS)
    .register(typeId, new PipePresentation(base, List.of()), () -> "example");

Identifiers must correspond to actual addon resources. A model ID refers to a model resource; pipe layers refer to sprites that must enter the relevant atlas through normal asset registration. A string in presentation metadata does not create a file or guarantee that it will load.

ClientPresentationService provides presentation(contentId), pipe(typeId), statement(id), parameter(id), and menu(contentId) lookups. Missing metadata is returned as an Optional. Use an intelligible fallback in your UI rather than allowing a NullPointerException.

Selecting Models from Synchronized State

DynamicPresentation has optional pipe(PipeView, partialTick), machine(MachineExecutionContext, partialTick), and engine(EngineHost, partialTick) selectors. They operate on client representations. Read required values from typed presentation channels, not mutable server components.

Every model a selector might return must be declared ahead of time in models(). The current runtime collects these during model setup, before baking. Returning a newly invented model ID from a render callback is too late: models should not be baked on a render or chunk-mesh thread.

java
final class PipeLook implements DynamicPresentation {
    private final ResourceLocation idle =
        ResourceLocation.tryParse("example:block/pipe_idle");
    private final ResourceLocation active =
        ResourceLocation.tryParse("example:block/pipe_active");
    private final ResourceLocation strength =
        ResourceLocation.tryParse("example:strength");

    @Override public Set<ResourceLocation> models() {
        return Set.of(idle, active);
    }

    @Override public Optional<PresentationFrame> pipe(
            PipeView view, float partialTick) {
        int value = view.presentation(strength, Integer.class).orElse(0);
        return Optional.of(new PresentationFrame(
            value > 0 ? active : idle,
            List.of(), Map.of(), null, null
        ));
    }
}

BuildCraftApi.registry(BuildCraftRegistries.DYNAMIC_PRESENTATIONS)
    .register(typeId, new PipeLook(), () -> "example");

The example assumes that the example:strength channel is already defined and its value delivered by a component through its synchronization contract. The selector itself does not perform synchronization. PresentationConformanceAddon illustrates reading such a channel for pipes and machines; the example above also explicitly declares dynamic models, which is necessary for rendering them.

PresentationFrame contains a model, up to 32 texture layers, up to 64 animation channels, an optional sound, and a menu. Each animation value must be finite and within [0, 1]. The service also validates partialTick against that range. Normalize progress and handle division by a zero-duration operation before building a frame.

frame.over(base) inherits missing model, sound, and menu, uses the base layers when the new list is empty, and merges animations with the new value taking precedence. Thus an empty layer list means inheritance; null is not an instruction to forcibly remove the base sound.

The built-in service adds buildcraft:work_progress for machines and buildcraft:engine_piston, buildcraft:engine_heat, and synchronized sound for engines. Selector errors fall back to an available static frame with diagnostics rather than breaking the entire renderer.

Understanding Which Channels Actually Animate

An arbitrary animation ID is data that a consumer must interpret. Existing shared model rendering supports buildcraft:offset_x, offset_y, offset_z and rotation_x, rotation_y, rotation_z under the same namespace. Offsets are interpreted as value - 0.5; rotations as value × 360° about the origin of the model's coordinate system.

A channel named example:movement will not move a model merely because its value exists in a map. A custom renderer can consume it, or a selector can convert it to the supported buildcraft:rotation_y. work_progress, temperature, and piston phase likewise have meaning for their respective consumers; they do not define universal skeletal animation.

For addon machines, public frame(machine, partialTick) and frame(engine, partialTick) methods are available. Use their result in the addon's normal renderer. Do not import buildcraft.lib.client.render.ApiPresentationModels for this purpose: it is a BCCE implementation class, outside the supported api.v2 surface.

Menus and Actions

MenuPresentation(menuId, titleKey, actions) describes a menu and at most 64 ActionPresentation entries. An action has an ID, title key, and optional icon. These metadata help label buttons consistently and describe which actions a UI exposes.

They contain no arbitrary executable callbacks and do not authorize the client to execute actions. Actual menus/screens are registered through the Minecraft loader; the server handler must revalidate the target, actor, distance, and permitted action. If you use the machine-menu bridge, consult the machine chapter. An actionId appearing in a client frame is not sufficient authorization.

Adding Guide Book Content

The Guide Book accepts sections and entries as common data. GuideSection has an ID, title key, optional icon and parent, and sort order. A GuideEntry references a section and has its own title, icon, order, and nonempty list of pages.

java
ResourceLocation sectionId = bc.id("logistics");
ResourceLocation entryId = bc.id("inspection_robot");

bc.guideSection(GuideSection.builder(
        sectionId, "example.guide.logistics")
    .order(100)
    .build());

bc.guideEntry(GuideEntry.builder(
        entryId, sectionId, "example.guide.inspection_robot")
    .order(10)
    .page(GuidePages.textKey("example.guide.inspection_robot.overview"))
    .page(GuidePages.recipe(bc.id("inspection_board")))
    .page(GuidePages.image(
        bc.id("textures/gui/guide/inspection_robot.png"), 128, 96,
        "example.guide.inspection_robot.diagram"
    ))
    .build());

Five standard GuidePage variants are available directly and through GuidePages:

The built-in implementation inserts a page break between supplied pages. Do not duplicate an entry's title at the beginning of its first text page: the guide structure already renders it. Sections and entries sort by ascending order, then ID.

GuideService supports fetching individual sections and entries, listing them all, and listing entries in a given section. Java registration supplies an owner; duplicate IDs are rejected with the previous owner reported. Empty titles and entries without pages are invalid. When nesting sections or linking entries, check that targets exist: a parent or targetEntry field does not guarantee the target has been registered.

The built-in renderer supports exactly these GuidePage.Text, Recipe, Image, Link, and Item variants. The fact that GuidePage is an interface does not imply an extension point for an arbitrary sixth renderer. For portable content, use the standard variants and addon assets.

Source Code

Chapter sources: client/S104; guide/S208; ClientPresentationServiceImpl.javaS538; GuideServiceImpl.javaS547; ApiPresentationModels.javaS533; GuideContent.javaS037; MessageGuideRecipeDisplays.javaS029; PresentationConformanceAddon.javaS015.

V. Maintenance

31Diagnosing and Validating Integrations

Validate an addon by starting with dependencies and data, then testing actual gameplay behavior. These levels prove different things: compilation establishes that signatures exist and match, but cannot create a backend where only a descriptor is registered.

Diagnostic Data

ApiDiagnostics exposes INFO, WARNING, and ERROR messages. The current runtime installs a mutable backend, but the common interface also allows read-only views: its default report throws UnsupportedOperationException. Treat messages as API information; do not assume every returned view permits writing.

DebugService collects sections from registered DebugContributor instances. DebugContext provides the world, position, optional side, and client mode. DebugSection contains a title and lines of technical information; it is a developer-tool format, not a localized in-game GUI. A contributor should collect state quickly without changing it or retaining the supplied context after the call.

For registration failures, inspect ID, owner, and lifecycle. For data failures, inspect generation, provenance, the winning definition, and rejection reason. For resource transfers, record offered amount, actual result, remainder, port side, and mode. For permission failures, record actor, operation, authority, and reason. Such a report helps reproduce the cause rather than just saying “the API does not work.”

Testkit

TestApiRuntime is a small in-memory runtime. You can add the registries and services needed by a test, set its version and features, and transition test registries to FROZEN. It does not install itself globally or initialize BCCE modules. Production code accepting ApiRuntime or specific services as parameters is straightforward to test in this environment.

TestExtensionContext holds values under ContextKey entries. put checks that the Java value matches the key's type, and get returns a typed Optional. This supports testing callbacks with and without a published PIPE, MACHINE, or other view.

The testkit does not emulate the entire game bootstrap. For example, setting lifecycle in a test runtime does not create a world, freeze arbitrary addon services, or initialize native capabilities. A passing test confirms only behavior that was actually modeled.

Checking Addon Compatibility

Compile addon code against BCCE's public classes without putting internal implementation packages on its compile classpath. Then test the assembled JAR with the actual dependencies for the target Minecraft version and loader. Successful compilation confirms correct signatures, not working gameplay.

Choose game tests based on the extension's responsibilities. Storage needs partial transfers and no mutation during simulation. A persistent component needs load, unload, and unknown-payload tests. A new renderer needs client and dedicated-server tests. World operations need permission-denial and resource-ownership tests. A runtime host needs tick, save, and unregister-on-unload tests. Testing unrelated scenarios does not replace testing your own contract.

Common Failure Causes

SymptomWhat to Check
service(...) is emptyIs the corresponding module loaded and its backend installed?
Registry reports duplicate IDIs registration called twice, or is the ID/alias already owned by another extension?
Registry is already frozenWas registration deferred until load-complete, first tick, or reload?
Type exists, but block does nothingDoes it have an executable factory/handler wired to its host?
Simulation succeeds, execution transfers zeroAre you using the default ALL_OR_NOTHING, or did the storage change?
Items are lost after a failed transferWhere is the unaccepted remainder stored, and who owns it between operations?
Data did not updateCheck published(), attempt diagnostics, and the active snapshot's generation.
Addon works only in the IDEDoes it import internal implementations, or does the production JAR not match its target environment?
Server crashes during startupWere client-only classes loaded by the addon's common bootstrap?
Source Code

Sources: ApiDiagnostics.javaS129; DebugService.javaS127; TestApiRuntime.javaS508; TestExtensionContext.javaS509; forge-target.gradleS022; neoforge-target.gradleS023.

V. Maintenance

32Migrating an Addon from the Original API

In BCCE API 2.1, the old public buildcraft.api.* packages outside buildcraft.api.v2 are no longer supported. Some historical classes exist in internal packages for BCCE's own operation. Importing those implementations is not a supported compatibility strategy for the old API.

BCCE API 2.1 is not binary-compatible with addons compiled against the original API. Even if a similar implementation exists inside BCCE, the old import or method call may fail. Port addons to the public buildcraft.api.v2 contracts.

Migration Map

Original Integration ApproachNew Extension PointWhat Must Be Reconsidered
BuildcraftFuelRegistryEnergyFluidService, fuel and coolant profilesUnits, matchers, definition IDs, provenance
BuildcraftRecipeRegistry and domain recipe managersMachineRecipeService, typed definitionsCounted ingredients, item components, outputs, recipe sources
MjAPI capabilities, IMjReceiver, MjBatteryEnergyService, MjPort, MjStorage, platform portsResult semantics, requested/accepted/remainder, port lifecycle
PipeDefinition, PipeBehaviour subclassPipeType, routing/connection components, or PipeBehaviourDefinitionInstance factory, scoped context, state persistence
PipeFlow subclass and direct holder accessPipeFlowDefinition, PipeView, domain-specific transport servicesAdmission, traveling-item ownership, synchronization and persistence
PipePluggablePipeAttachmentType and attachment instancesSide, ports, model, installation/removal, unknown state
StatementManager, IStatementParameterTriggerType, ActionType, ParameterTypeSchema, codec, ordered contributors, client presentation
Direct wire APISignalService, SignalChannelType, Boolean channelsActual channel backend, signal source, lifetime
AIRobot, boards, and ResourceIdRobotTaskType, RobotTask, board programs, typed resourcesTask lifecycle, leases, docking, state migration
Schematic subclasses and handlersSchematicAdapter, snapshot elements, placement contextSeparate capture and placement, resource consumption, permissions, inventory copy
Global template/filler registriesTemplateService, FillerPatternTypeReturned result, orientation, and actual caller parameters
IToolWrench, rotation/paint hooksWrench and block-interaction services/adaptersItem support separate from action and resource consumption
Mutable item lists and facade registriesItem-list and facade APIMatching mode, components, materials/phases, rule sources
FakePlayer providerAutomationActor and permission contextOwner provenance, target, operation kind, handling PASS / DENY
Arbitrary API network callbacksDomain-specific typed synchronization and presentationPermitted payload, direction, and lifecycle

This table maps the direction of migration, not mechanical name substitution. An old class may split into a type descriptor, component, codec, and view. Conversely, several former access points may converge into one service.

Migration Procedure

First, separate the addon's shared gameplay logic from loader code, client rendering, and BCCE internal references. Record what the addon actually does: filters items, creates an energy endpoint, adds robot work, or changes appearance. For each operation, choose the narrowest suitable contract from the migration map.

Next, migrate stable IDs and data. Map old names to new ones, define codecs and migrations, and test unknown payloads. Do not rename persisted IDs and change their format simultaneously without an explicit transition. Preserving old saves and maintaining binary compatibility with the old Java API are different problems.

Then connect the runtime: register the type, factories, and callbacks; create or locate a host; establish tick/unload order; and identify the owner of resources. Confirm that the chosen backend actually invokes your contract. If an execution hook is missing, a descriptor or feature flag is no substitute.

Add client models, GUI, and synchronization through the appropriate presentation contracts. Test a dedicated server to ensure common addon bootstrap does not load client-only classes. Finally, build and test gameplay on each target platform, including partial operations, denied permissions, and loading old saves. Preserve gameplay semantics during migration—including balance, transport intervals, and robot behavior; changing the API contract does not automatically require changing game mechanics.

Source Code

Primary contracts: package-info.javaS286; BuildCraftApi.javaS071; BuildCraftApiRuntime.javaS536.

Appendices

Reference Appendix: API Map

This map connects 43 API packages to the relevant chapters. It covers 438 Java API files, 44 service keys, 49 registry keys, and 33 feature identifiers. The public API version is 2.1.0.

Packages and Chapters

For nested packages, the common buildcraft.api.v2. prefix is omitted in the first column. Counts refer to Java files, not methods or nested types. The root package includes one package-info.java.

PackageFilesGuide ChapterPurpose
buildcraft.api.v211API Purpose and Boundaries; Extension Lifecycle and Registration; Resource Storage and TransfersAPI entry point, version, features, lifecycle phases, and standard keys.
area4Areas, Maps, and World AccessArea geometry: bounds, position sets, and volume views.
automation14Automation Actions and StripesWorld action requests, executors, and Stripes handlers.
block7Integration Items: Tools, Maps, Facades, and DropsExtensible block interaction, rotation, and painting.
client9Content Presentation and the In-Game GuideStatic and dynamic content presentation, menus, and parameters.
content3ContentRegistrar: A Shortcut to the Same Public ContractsConvenience facade for content registration built on public domain contracts.
context3Operation Context and PermissionsTyped views published for a particular callback.
crops3Plants and Resource GenerationDiscovery, planting, and harvesting of supported crops.
debug4Diagnosing and Validating IntegrationsCollecting structured debug information about a world object.
diagnostics2Diagnosing and Validating IntegrationsCommon API diagnostic messages with severity levels.
drop3Integration Items: Tools, Maps, Facades, and DropsConverting stored fluid into item drops.
energy21MJ: Energy, Ports, and ConservationMJ energy, ports and buffers, connections, FE conversion, and formatting.
event6Operation Context and PermissionsNamed BCCE world-event listeners and cancellation decisions.
facade6Integration Items: Tools, Maps, Facades, and DropsFacade materials, phased variants, and validity rules.
filler5Templates, Filler, and the Electronic LibraryParameterized shape templates for the Filler.
fluid14Resource Storage and TransfersFluid identity and volume, filters, transfers, containers, and platform units.
fuels13Fuel and Coolants: Properties of Registered ResourcesProfiles for fluid fuels, liquid coolants, and solid coolants.
gate5Statements and Gate ProgramsReading and replacing gate programs, slot addressing, and program limits.
guide5Content Presentation and the In-Game GuidePages and linked assets of the built-in guide.
item7Resource Storage and Transfers; Integration Items: Tools, Maps, Facades, and DropsItem filters and ports, transfer results, and custom labels.
library4Templates, Filler, and the Electronic LibraryDescribing electronic-library file formats and converting data.
list8Item Lists and Specialized Matching RulesNamed item lists, filter configurations in ItemStack, and matching rules.
machine44Machines: Custom Blocks and Execution Rules; Engines, Heat, and Laser TargetsMachine components and hosts, engines, heat, laser targets, and state views.
map4Integration Items: Tools, Maps, Facades, and DropsMap-location carriers and their data read/write adapters.
module3Extension Lifecycle and RegistrationModule availability and registration of external modules with dependencies.
permission13Operation Context and PermissionsAutomation identity, ownership, and composition of protection decisions.
persistence11State Persistence and Schema UpgradesStable persistent types, codecs, migrations, and preservation of unknown data.
pipe73Pipes: Types, Components, and Executable Flows; Item Movement: Admission, Routing, and Ownership; Fluids and Energy in Pipes; Connecting External Blocks and Pipe AttachmentsPipe types, executable components, flows, routing, endpoints, and attachments.
platform5Resource Storage and TransfersLoader bridges for external inventories, fluids, and energy.
recipe15Machine Recipes: Requirements, Outputs, and Definition UpdatesProduction-machine recipes, resource requirements, and definition snapshots.
registry4Extension Lifecycle and RegistrationTyped registration, entry provenance, aliases, and freeze.
reload15Reloadable DefinitionsReloading typed data with priorities, diagnostics, and snapshot publication.
request5Request Logistics: Demand, Promises, and Confirmed DeliveryDemand, delivery promises, reservations, and accounting for confirmed delivery.
robot27Robots: Programs, Tasks, and Work ReservationsBoard programs, persistent tasks, work resources, events, and docks.
schematic15Blueprints: Capturing State and BuildingBlueprint capture, element and entity adapters, resources, and placement.
service1Extension Lifecycle and RegistrationTyped key for retrieving a runtime service.
signal9Signals and Wire NetworksSignal channels, ports, and monitoring of network state.
statement20Statements and Gate ProgramsActions, triggers, their parameters, and availability to gates.
template3Templates, Filler, and the Electronic LibraryRecognizing and reading construction-template items.
testkit2Diagnosing and Validating IntegrationsIsolated runtime and context for small integration tests.
tool4Integration Items: Tools, Maps, Facades, and DropsWrench adapters and checks of wrench usability.
world5Areas, Maps, and World AccessRestricted world reads, world properties, and rules for applying mechanics.
worldgen3Plants and Resource GenerationEnabling built-in BCCE resource generation in dimensions.

Standard Services

Each constant is a ServiceKey<T>; the table shows the precise T. A key retrieves a service. ApiRuntime.service(...) returns an Optional, while BuildCraftApi.service(...) requires the service and throws if absent. Declaring a key does not guarantee that the backend is available in every runtime.

BuildCraftServicesT in ServiceKey<T>ID
ENERGYEnergyServicebuildcraft:energy
PERMISSIONSPermissionServiceRegistrybuildcraft:permissions
ENERGY_FLUIDSEnergyFluidServicebuildcraft:energy_fluids
MACHINE_RECIPESMachineRecipeServicebuildcraft:machine_recipes
CROPSCropServicebuildcraft:crops
TEMPLATESTemplateServicebuildcraft:templates
LIBRARYLibraryServicebuildcraft:library
FACADE_RULESFacadeRuleServicebuildcraft:facade_rules
WORLD_PROPERTIESWorldPropertyServicebuildcraft:world_properties
WORLD_RULESWorldRuleServicebuildcraft:world_rules
WORLD_ACCESSWorldAccessServicebuildcraft:world_access
WORLD_EVENTSWorldEventServicebuildcraft:world_events
GUIDEGuideServicebuildcraft:guide
WORLDGENWorldgenServicebuildcraft:worldgen
MODULESModuleServicebuildcraft:modules
DEFINITION_RELOADDefinitionReloadServicebuildcraft:definition_reload
ACTORSActorServicebuildcraft:actors
DIAGNOSTICSApiDiagnosticsbuildcraft:diagnostics
BLOCK_INTERACTIONSBlockInteractionServicebuildcraft:block_interactions
WRENCHESWrenchServicebuildcraft:wrenches
ITEM_LISTSItemListServicebuildcraft:item_lists
ITEM_LABELSItemLabelServicebuildcraft:item_labels
FLUID_ITEMSFluidItemServicebuildcraft:fluid_items
MAP_LOCATIONSMapLocationServicebuildcraft:map_locations
REQUESTSRequestServicebuildcraft:requests
FLUID_DROPSFluidDropServicebuildcraft:fluid_drops
DEBUG_VIEWSDebugServicebuildcraft:debug_views
FACADESFacadeServicebuildcraft:facades
FILLER_PATTERNSFillerPatternServicebuildcraft:filler_patterns
STATEMENTSStatementServicebuildcraft:statements
GATESGateServicebuildcraft:gates
SIGNALSSignalServicebuildcraft:signals
AUTOMATIONAutomationServicebuildcraft:automation
PIPESPipeServicebuildcraft:pipes
PIPE_RESOURCESPipeResourceServicebuildcraft:pipe_resources
MACHINESMachineServicebuildcraft:machines
ENGINESEngineServicebuildcraft:engines
LASER_TARGETSLaserTargetServicebuildcraft:laser_targets
ROBOTSRobotServicebuildcraft:robots
SCHEMATICSSchematicServicebuildcraft:schematics
CLIENT_PRESENTATIONSClientPresentationServicebuildcraft:client_presentations
PLATFORMPlatformServicesbuildcraft:platform
MJ_FORMATTERMjFormatterbuildcraft:mj_formatter
POWER_LOSS_EFFECTSPowerLossEffectServicebuildcraft:power_loss_effects

The fluid package also declares FluidService. There is no separate BuildCraftServices.FLUIDS key in this version. Use PLATFORM for external fluid ports and FLUID_ITEMS for fluids represented by items.

Source: BuildCraftServices.javaS074 S074.

Standard Extension Registries

Each constant is a RegistryKey<T>; T specifies the values accepted by its ApiRegistry<T>. Registries store extension descriptors or adapters. Registration-phase requirements and actual consumers are explained in the corresponding chapters; registering an entry alone does not create a working object in the world.

BuildCraftRegistriesT in RegistryKey<T>ID
ENGINE_TYPESEngineTypebuildcraft:engine_types
MACHINE_TYPESMachineTypebuildcraft:machine_types
MACHINE_COMPONENT_TYPESMachineComponentType<?>buildcraft:machine_component_types
MACHINE_PROPERTIESMachineProperty<?>buildcraft:machine_properties
LASER_TABLE_TYPESLaserTableTypebuildcraft:laser_table_types
MJ_CONNECTION_RULESMjConnectionRulebuildcraft:mj_connection_rules
PIPE_TYPESPipeTypebuildcraft:pipe_types
PIPE_FLOW_TYPESPipeFlowDefinition<?>buildcraft:pipe_flow_types
PIPE_BEHAVIOUR_TYPESPipeBehaviourDefinition<?>buildcraft:pipe_behaviour_types
PIPE_COMPONENT_TYPESPipeComponentType<?>buildcraft:pipe_component_types
PIPE_ATTACHMENT_TYPESPipeAttachmentType<?>buildcraft:pipe_attachment_types
PIPE_CONNECTION_RULESPipeConnectionRulebuildcraft:pipe_connection_rules
PIPE_ENDPOINT_PROVIDERSPipeEndpointProviderbuildcraft:pipe_endpoint_providers
PIPE_SYNC_CHANNELSPipeSyncChannel<?>buildcraft:pipe_sync_channels
PARAMETER_TYPESParameterType<?>buildcraft:parameter_types
ACTION_TYPESActionTypebuildcraft:action_types
TRIGGER_TYPESTriggerTypebuildcraft:trigger_types
STATEMENT_CONTRIBUTORSStatementContributorbuildcraft:statement_contributors
FILLER_PATTERN_TYPESFillerPatternTypebuildcraft:filler_pattern_types
SIGNAL_CHANNEL_TYPESSignalChannelType<?>buildcraft:signal_channel_types
AUTOMATION_ACTION_TYPESAutomationActionType<?>buildcraft:automation_action_types
STRIPES_HANDLERSStripesHandlerbuildcraft:stripes_handlers
ROBOT_TASK_TYPESRobotTaskType<?>buildcraft:robot_task_types
ROBOT_RESOURCE_TYPESRobotResourceType<?>buildcraft:robot_resource_types
ROBOT_DOCK_PORT_TYPESDockPortType<?>buildcraft:robot_dock_port_types
ROBOT_BOARD_TYPESRobotBoardTypebuildcraft:robot_board_types
ROBOT_BOARD_PROGRAMSRobotBoardProgrambuildcraft:robot_board_programs
ROBOT_EVENT_LISTENERSRobotEventListenerbuildcraft:robot_event_listeners
SCHEMATIC_ADAPTERSSchematicAdapterbuildcraft:schematic_adapters
SCHEMATIC_ENTITY_ADAPTERSEntitySchematicAdapterbuildcraft:schematic_entity_adapters
SNAPSHOT_ELEMENT_TYPESSnapshotElementType<?>buildcraft:snapshot_element_types
INVENTORY_COPY_POLICIESInventoryCopyPolicybuildcraft:inventory_copy_policies
FACADE_MATERIAL_ADAPTERSFacadeMaterialAdapterbuildcraft:facade_material_adapters
NAMED_ITEM_LISTSItemListbuildcraft:named_item_lists
LIST_MATCH_ADAPTERSListMatchAdapterbuildcraft:list_match_adapters
ITEM_LABEL_ADAPTERSItemLabelAdapterbuildcraft:item_label_adapters
FLUID_ITEM_ADAPTERSFluidItemAdapterbuildcraft:fluid_item_adapters
MAP_LOCATION_ADAPTERSMapLocationAdapterbuildcraft:map_location_adapters
FLUID_DROP_PROVIDERSFluidDropProviderbuildcraft:fluid_drop_providers
DEBUG_CONTRIBUTORSDebugContributorbuildcraft:debug_contributors
ROTATION_HANDLERSRotationHandlerbuildcraft:rotation_handlers
PAINT_HANDLERSPaintHandlerbuildcraft:paint_handlers
WRENCH_ADAPTERSWrenchAdapterbuildcraft:wrench_adapters
CLIENT_PRESENTATIONSContentPresentationbuildcraft:client_presentations
DYNAMIC_PRESENTATIONSDynamicPresentationbuildcraft:dynamic_presentations
MENU_PRESENTATIONSMenuPresentationbuildcraft:menu_presentations
PIPE_PRESENTATIONSPipePresentationbuildcraft:pipe_presentations
STATEMENT_PRESENTATIONSStatementPresentationbuildcraft:statement_presentations
PARAMETER_PRESENTATIONSParameterPresentationbuildcraft:parameter_presentations

Source: BuildCraftRegistries.javaS073 S073.

Features and Contract Levels

All BuildCraftFeatures constants are ResourceLocation values. The runtime assigns a level when creating each ApiFeature. The table gives levels from the shared runtime, matching the effective 26.X/NeoForge implementation. A level does not indicate Minecraft version or completeness of gameplay behavior. Supplement feature checks by obtaining the required service and testing the supported object.

BuildCraftFeaturesIDRuntime Level
REGISTRIESbuildcraft:registries1
PERSISTENCEbuildcraft:persistence1
RELOADbuildcraft:reload2
TRANSFERbuildcraft:transfer1
PERMISSIONSbuildcraft:permissions1
WORLD_CONTEXTbuildcraft:world_context1
WORLD_EVENTSbuildcraft:world_events1
ENERGYbuildcraft:energy1
DATA_DOMAINSbuildcraft:data_domains1
PIPESbuildcraft:pipes2
PIPE_FLUID_MOVEMENTbuildcraft:pipe_fluid_movement1
PIPE_POWER_MOVEMENTbuildcraft:pipe_power_movement1
PIPE_ENDPOINTSbuildcraft:pipe_endpoints1
PIPE_ATTACHMENT_PORTSbuildcraft:pipe_attachment_ports1
STATEMENTSbuildcraft:statements1
SIGNALSbuildcraft:signals1
AUTOMATIONbuildcraft:automation1
ROBOTSbuildcraft:robots2
ROBOT_BOARD_PROGRAMSbuildcraft:robot_board_programs1
LOGISTICSbuildcraft:logistics1
SCHEMATICSbuildcraft:schematics1
LIBRARY_FORMATSbuildcraft:library_formats1
MACHINESbuildcraft:machines2
CLIENT_PRESENTATIONbuildcraft:client_presentation2
CONTENT_EXTENSIONbuildcraft:content_extension1
GUIDEbuildcraft:guide1
WORLDGENbuildcraft:worldgen1
FACADE_PHASESbuildcraft:facade_phases1
NAMED_ITEM_LISTSbuildcraft:named_item_lists1
MAP_LOCATIONSbuildcraft:map_locations1
WRENCH_ADAPTERSbuildcraft:wrench_adapters1
CROPSbuildcraft:crops1
PAINTbuildcraft:paint1

Level 2 is declared for RELOAD, PIPES, ROBOTS, MACHINES, and CLIENT_PRESENTATION. The other 28 features are declared at level 1. For another runtime implementation, inspect its ApiFeatureSet instead of assuming a fixed capability set.

Source Code

Sources: BuildCraftFeatures.javaS072; BuildCraftApiRuntime.javaS536; BuildCraftApiRuntime.javaS045.

Full Reference

Find a Contract

All 438 Java API files: purpose, related chapter, and precise declarations. Click a contract name to open its source code here.

438 of 438 files

ContractPurposeExplanation
ApiFeaturebuildcraft.api.v2Feature identifier and a positive level for its public contract.Purpose and Scope of the API
ApiFeatureSetbuildcraft.api.v2Immutable feature set with ID lookup and minimum-level checks.Purpose and Scope of the API
ApiLifecyclebuildcraft.api.v2Lists lifecycle phases from discovery and registration to execution and shutdown.Lifecycle and Extension Registration
ApiRuntimebuildcraft.api.v2Provides version, phase, features, and typed access to registries and services; permits an empty implementation.Lifecycle and Extension Registration
ApiVersionbuildcraft.api.v2Three-part API version with value validation and lexicographic comparison.Purpose and Scope of the API
BuildCraftApibuildcraft.api.v2Public entry point: contract version, runtime discovery, registry and service access.Lifecycle and Extension Registration
BuildCraftFeaturesbuildcraft.api.v2Standard feature IDs used to negotiate the available contract with the runtime.Purpose and Scope of the API
BuildCraftRegistriesbuildcraft.api.v2Standard typed keys for extension registries.Lifecycle and Extension Registration
BuildCraftServicesbuildcraft.api.v2Standard typed runtime service keys; declaring a key is separate from installing a backend.Lifecycle and Extension Registration
OperationModebuildcraft.api.v2Distinguishes observational simulation from actual execution.Resources and Transfers Between Storages
package-infobuildcraft.api.v2Defines the public API v2 namespace and its boundary with internal implementations.Purpose and Scope of the API
AreaProviderbuildcraft.api.v2.areaOptional area views; implementing the interface does not guarantee discovery by a built-in machine.Areas, Maps, and World Access
BlockBoxbuildcraft.api.v2.areaNormalized, inclusive block bounds, with contains and overflow-checked volume.Areas, Maps, and World Access
Pathbuildcraft.api.v2.areaOrdered positions without pathfinding; the implementation must preserve list stability.Areas, Maps, and World Access
Zonebuildcraft.api.v2.areaArbitrary-area predicate and optional bounds.Areas, Maps, and World Access
AutomationActionTypebuildcraft.api.v2.automationAssociates a kind ID, Java request class, and handler.Automation Actions and Stripes
AutomationHandlerbuildcraft.api.v2.automationExecutor responsible for checks, mode, and results.Automation Actions and Stripes
AutomationKindsbuildcraft.api.v2.automationFive standard IDs; separates IDs/DTOs from installed handlers.Automation Actions and Stripes
AutomationRequestbuildcraft.api.v2.automationShared kind/actor/mode/origin for a user action request.Automation Actions and Stripes
AutomationResultbuildcraft.api.v2.automationStatus/amount/detail, domain-specific units, and no automatic retry scheduling.Automation Actions and Stripes
AutomationServicebuildcraft.api.v2.automationRegistry dispatch, PASS when no implementation exists, and runtime exception caveats.Automation Actions and Stripes
BreakBlockRequestbuildcraft.api.v2.automationIntent to break a target block from an origin, with built-in checks and SIMULATE.Automation Actions and Stripes
MoveFluidRequestbuildcraft.api.v2.automationMatcher/FluidAmount DTO; no built-in handler found in this runtime.Automation Actions and Stripes
MoveItemRequestbuildcraft.api.v2.automationMatcher/maxCount DTO; no built-in handler found in this runtime.Automation Actions and Stripes
PlaceBlockRequestbuildcraft.api.v2.automationIntent to place a BlockState; does not include the source of the consumed item.Automation Actions and Stripes
StripesContextbuildcraft.api.v2.automationFresh working stack, replace/consume, actor/mode/player/targetPos/output.Automation Actions and Stripes
StripesHandlerbuildcraft.api.v2.automationPriority-ordered chain; PASS for unrelated cases, stopping at the first other result.Automation Actions and Stripes
StripesOutputbuildcraft.api.v2.automationDispatching or dropping output; discard is not a real resource sink.Automation Actions and Stripes
UseItemRequestbuildcraft.api.v2.automationIntent to use a copied ItemStack against a target side; result does not return a replacement stack.Automation Actions and Stripes
BlockInteractionServicebuildcraft.api.v2.blockRotation/painting dispatcher with server permission/event checks and legacy fallback.Integration Items: Tools, Maps, Facades, and Drops
PaintContextbuildcraft.api.v2.blocknull color removes paint; hit position and side preserve multipart semantics.Integration Items: Tools, Maps, Facades, and Drops
PaintHandlerbuildcraft.api.v2.blockNarrow painting callback that must honor SIMULATE.Integration Items: Tools, Maps, Facades, and Drops
PaintResultbuildcraft.api.v2.blockPASS/PAINTED/DENIED/FAILED; reject unrelated blocks with PASS.Integration Items: Tools, Maps, Facades, and Drops
RotationContextbuildcraft.api.v2.blockWorld, block, side, actor, and rotation mode.Integration Items: Tools, Maps, Facades, and Drops
RotationHandlerbuildcraft.api.v2.blockRotation callback, dispatch chain, and pure simulation.Integration Items: Tools, Maps, Facades, and Drops
RotationResultbuildcraft.api.v2.blockPASS/ROTATED/DENIED/FAILED for dispatch results.Integration Items: Tools, Maps, Facades, and Drops
ActionPresentationbuildcraft.api.v2.clientAction ID, label, and icon without granting client authority.Content Presentation and the In-Game Guide
ClientPresentationServicebuildcraft.api.v2.clientStatic presentation lookup and client frames for pipes, machines, and engines.Content Presentation and the In-Game Guide
ContentPresentationbuildcraft.api.v2.clientContent metadata without a net.minecraft.client dependency.Content Presentation and the In-Game Guide
DynamicPresentationbuildcraft.api.v2.clientSelectors using synchronized client views and models() declarations before baking.Content Presentation and the In-Game Guide
MenuPresentationbuildcraft.api.v2.clientMenu description with up to 64 actions; actual screen/menu and authorization belong to the implementation.Content Presentation and the In-Game Guide
ParameterPresentationbuildcraft.api.v2.clientParameter-editor description and editorTypeId.Content Presentation and the In-Game Guide
PipePresentationbuildcraft.api.v2.clientBase presentation and predeclared atlas layers.Content Presentation and the In-Game Guide
PresentationFramebuildcraft.api.v2.clientModel, layers, normalized channels, sound, menu, over, and limits.Content Presentation and the In-Game Guide
StatementPresentationbuildcraft.api.v2.clientTrigger/action labels and icons.Content Presentation and the In-Game Guide
BuildCraftContentbuildcraft.api.v2.contentaddon(namespace[,priority]) factory for the facade over common public contracts.ContentRegistrar: A Shortcut to the Same Public Contracts
BuildCraftContentIdsbuildcraft.api.v2.contentStable namespaces for built-in machine/engine/stage/component/laser/pipe/worldgen IDs, without promises of factories or modules.ContentRegistrar: A Shortcut to the Same Public Contracts
ContentRegistrarbuildcraft.api.v2.contentConvenience content-registration facade, context/provenance, coverage of method families, and registration/runtime boundaries.ContentRegistrar: A Shortcut to the Same Public Contracts
BuildCraftContextKeysbuildcraft.api.v2.contextStandard keys for published views of pipes, gates, robots, machines, areas, and ports.Operation Context and Permissions
ContextKeybuildcraft.api.v2.contextAssociates a context ID with a checked Java value type.Operation Context and Permissions
ExtensionContextbuildcraft.api.v2.contextProvides optional typed views published for a particular callback.Operation Context and Permissions
CropAdapterbuildcraft.api.v2.cropsRecognition, pure checks, planting, and harvesting; distinguish soil position from crop position.Plants and Resource Generation
CropRegistrationbuildcraft.api.v2.cropsNamed crop adapter with priority.Plants and Resource Generation
CropServicebuildcraft.api.v2.cropsActive planter/harvester chain, staged drops, and no general simulation.Plants and Resource Generation
DebugContextbuildcraft.api.v2.debugPosition, world, optional side, and client mode of a debug request.Diagnosing and Validating Integrations
DebugContributorbuildcraft.api.v2.debugAdds debug-information sections for the supplied context.Diagnosing and Validating Integrations
DebugSectionbuildcraft.api.v2.debugImmutable title and line list for one debug-output section.Diagnosing and Validating Integrations
DebugServicebuildcraft.api.v2.debugCollects debug sections from registered contributors.Diagnosing and Validating Integrations
ApiDiagnosticbuildcraft.api.v2.diagnosticsStructured API message with INFO, WARNING, or ERROR severity.Diagnosing and Validating Integrations
ApiDiagnosticsbuildcraft.api.v2.diagnosticsProvides accumulated messages; the concrete implementation determines whether adding messages is supported.Diagnosing and Validating Integrations
FluidDropContextbuildcraft.api.v2.dropFluidVolume and optional Level; may be used without a world.Integration Items: Tools, Maps, Facades, and Drops
FluidDropProviderbuildcraft.api.v2.dropCreates item drops; results from all providers are aggregated.Integration Items: Tools, Maps, Facades, and Drops
FluidDropServicebuildcraft.api.v2.dropCopies and combines results without automatically draining the tank.Integration Items: Tools, Maps, Facades, and Drops
EnergyConversionbuildcraft.api.v2.energyExact MJ/FE ratio, floor rounding to whole FE, conversion remainder, and clamping for snapshots only.MJ: Energy, Ports, and Conservation
EnergyConversionStatusbuildcraft.api.v2.energySnapshot of conversion ratio, automatic conversion/display external energy, and display-rate unit.MJ: Energy, Ports, and Conservation
EnergyRateUnitbuildcraft.api.v2.energyPER_TICK/PER_SECOND; the formatter treats one second as 20 ticks.MJ: Energy, Ports, and Conservation
EnergyServicebuildcraft.api.v2.energyLookup, descriptors, demand, connection policy, storage creation/ticking, and live conversion policy.MJ: Energy, Ports, and Conservation
MjAmountbuildcraft.api.v2.energyNonnegative long of micro-MJ, 1 MJ = 1e6 units, exact arithmetic and overflow checks.MJ: Energy, Ports, and Conservation
MjConnectionContextbuildcraft.api.v2.energyWorld, local position, side, and local/remote descriptors for a connection rule.MJ: Energy, Ports, and Conservation
MjConnectionRulebuildcraft.api.v2.energyRegistered MJ connection rules; every rule must permit the connection.MJ: Energy, Ports, and Conservation
MjDemandPortbuildcraft.api.v2.energySeparate live server-side demand query instead of simulated insertion for topology.MJ: Energy, Ports, and Conservation
MjFormatterbuildcraft.api.v2.energyAmount/number/rate, expressed in MJ regardless of the FE display flag.MJ: Energy, Ports, and Conservation
MjPassivePortbuildcraft.api.v2.energyAtomic minimum/maximum extraction; zero transfer without changing state when insufficient.MJ: Energy, Ports, and Conservation
MjPortbuildcraft.api.v2.energyPartial insertion/extraction and read-only queries, proper SIMULATE; extra guarantees require overrides.MJ: Energy, Ports, and Conservation
MjPortDescriptorbuildcraft.api.v2.energyNetwork ID, roles, and stable per-tick limits; discovery without simulation side effects.MJ: Energy, Ports, and Conservation
MjPortProviderbuildcraft.api.v2.energyLoader-neutral side-dependent port and optional descriptor from a block entity.MJ: Energy, Ports, and Conservation
MjPortRolebuildcraft.api.v2.energyCONSUMER/PROVIDER/PASSIVE_PROVIDER/CONNECTOR/REDSTONE_RECEIVER/READABLE.MJ: Energy, Ports, and Conservation
MjStoragebuildcraft.api.v2.energyMutable buffer, snapshot/restore/refund/tick; the owner manages lifecycle and persistence.MJ: Energy, Ports, and Conservation
MjStoragePolicybuildcraft.api.v2.energyNominal/maximumStored capacity, insertion/extraction limits, passive and redstone roles; overcharge decay.MJ: Energy, Ports, and Conservation
MjStorageStatebuildcraft.api.v2.energyOnly the physical MJ amount; tick budgets reset after restoring.MJ: Energy, Ports, and Conservation
MjTransferPolicybuildcraft.api.v2.energyPARTIAL and ALL_OR_NOTHING; the default ALL_OR_NOTHING execution refuses without an atomic override.MJ: Energy, Ports, and Conservation
MjTransferResultbuildcraft.api.v2.energyrequested = transferred + remainder invariant and the meaning of completed, including zero requests.MJ: Energy, Ports, and Conservation
PowerLossContextbuildcraft.api.v2.energyLevel/position/actual MJ lost for a power-loss effect.MJ: Energy, Ports, and Conservation
PowerLossEffectServicebuildcraft.api.v2.energyCallback for losses from tickStorage; the current default backend is a no-op.MJ: Energy, Ports, and Conservation
WorldEventContextbuildcraft.api.v2.eventImmutable world-event envelope: kind, world, position, actor, state, content ID, and mode.Operation Context and Permissions
WorldEventDecisionbuildcraft.api.v2.eventPASS, ALLOW, or DENY decision; denial takes precedence when combining results.Operation Context and Permissions
WorldEventKindbuildcraft.api.v2.eventBCCE event kinds and whether their operation may be canceled.Operation Context and Permissions
WorldEventListenerbuildcraft.api.v2.eventWorld-event handler returning a structured decision.Operation Context and Permissions
WorldEventServicebuildcraft.api.v2.eventRegisters prioritized named listeners and dispatches BCCE events to them.Operation Context and Permissions
WorldEventSubscriptionbuildcraft.api.v2.eventSubscription data: stable ID, priority, and listener.Operation Context and Permissions
FacadeMaterialbuildcraft.api.v2.facadeFacade material state and transparency.Integration Items: Tools, Maps, Facades, and Drops
FacadeMaterialAdapterbuildcraft.api.v2.facadeState/stack conversion and factory for phased facades.Integration Items: Tools, Maps, Facades, and Drops
FacadePhasebuildcraft.api.v2.facadeMaterial by color; null represents an uncolored phase.Integration Items: Tools, Maps, Facades, and Drops
FacadeRuleServicebuildcraft.api.v2.facadedisable/mapState with provenance; does not bypass geometry checks or rebuild caches automatically.Integration Items: Tools, Maps, Facades, and Drops
FacadeServicebuildcraft.api.v2.facadeResolve materials and construct a phased ItemStack.Integration Items: Tools, Maps, Facades, and Drops
FacadeStateResultbuildcraft.api.v2.facadeMaterial and a defensive copy of the source item.Integration Items: Tools, Maps, Facades, and Drops
FillerMaskbuildcraft.api.v2.fillerPredicate for inclusion of a world position in a shape, plus bounds.Templates, Filler, and the Electronic Library
FillerPatternbuildcraft.api.v2.fillerMask factory using a shape context.Templates, Filler, and the Electronic Library
FillerPatternContextbuildcraft.api.v2.fillerBounds/facing/options; the current native bridge passes NORTH and empty options.Templates, Filler, and the Electronic Library
FillerPatternServicebuildcraft.api.v2.fillerLookup a registered shape type and enumerate shapes.Templates, Filler, and the Electronic Library
FillerPatternTypebuildcraft.api.v2.fillerShape with ID and internalOnly flag; Filler integration.Templates, Filler, and the Electronic Library
FluidAmountbuildcraft.api.v2.fluidNonnegative fluid amount in mB with 64-bit arithmetic and overflow checking.Resources and Transfers Between Storages
FluidComponentPayloadbuildcraft.api.v2.fluidImmutable canonical bytes for additional fluid data with a format ID.Resources and Transfers Between Storages
FluidItemAdapterbuildcraft.api.v2.fluidReads fluid represented by an item without requiring a loader-specific capability.Resources and Transfers Between Storages
FluidItemServicebuildcraft.api.v2.fluidResolves an ItemStack's represented fluid through available adapters.Resources and Transfers Between Storages
FluidMatchContextbuildcraft.api.v2.fluidResolves fluid-tag membership when evaluating a matcher.Resources and Transfers Between Storages
FluidMatcherbuildcraft.api.v2.fluidComposable fluid-selection rule by variant, ID, tag, or custom predicate.Resources and Transfers Between Storages
FluidPortbuildcraft.api.v2.fluidFluid insertion/extraction port with explicit mode and partial transfer result.Resources and Transfers Between Storages
FluidServicebuildcraft.api.v2.fluidContract for fluid registration and tag checks; no separate service key in the standard catalog.Resources and Transfers Between Storages
FluidTransferResultbuildcraft.api.v2.fluidRequested and transferred fluid amount, remainder, and operation-completion status.Resources and Transfers Between Storages
FluidUnitConverterbuildcraft.api.v2.fluidExactly converts between mB and platform units, retaining division remainders.Resources and Transfers Between Storages
FluidVariantbuildcraft.api.v2.fluidFluid identity by registry ID and components; comparison, stable hash, and representation codec.Resources and Transfers Between Storages
FluidVariantDatabuildcraft.api.v2.fluidStorage-format-independent data representation of fluid ID and components for codecs.Resources and Transfers Between Storages
FluidVolumebuildcraft.api.v2.fluidImmutable pair of fluid variant and amount with an explicit empty value.Resources and Transfers Between Storages
UnitConversionResultbuildcraft.api.v2.fluidConversion result with whole part, remainder, and divisor; reports exactness.Resources and Transfers Between Storages
ConstantCoolingRulebuildcraft.api.v2.fuelsPositive finite constant cooling factor, compatible with the codec.Fuels and Coolants: Properties of Registered Resources
CoolantProfilebuildcraft.api.v2.fuelsMatcher and cooling rule; cooling per mB; invalid numeric output becomes zero.Fuels and Coolants: Properties of Registered Resources
CoolantProfileDatabuildcraft.api.v2.fuelsSerializable selector and constant degreesPerMilliBucket.Fuels and Coolants: Properties of Registered Resources
CoolingRulebuildcraft.api.v2.fuelsPure programmatic efficiency curve based on FluidVariant and temperature.Fuels and Coolants: Properties of Registered Resources
EnergyFluidDefinitionbuildcraft.api.v2.fuelsFUEL/COOLANT/SOLID_COOLANT in one atomically published set.Fuels and Coolants: Properties of Registered Resources
EnergyFluidServicebuildcraft.api.v2.fuelsSnapshot, registration, and priority-ordered fuel/coolant/solid-coolant lookups and lists; consumed by native combustion engines.Fuels and Coolants: Properties of Registered Resources
FluidSelectorbuildcraft.api.v2.fuelsFluid ID, tag, or exact variant as a serializable subset of FluidMatcher, with representative preview.Fuels and Coolants: Properties of Registered Resources
FluidSelectorDatabuildcraft.api.v2.fuelsThree selector variants with ID/variant and codec validation.Fuels and Coolants: Properties of Registered Resources
FuelProfilebuildcraft.api.v2.fuelsMicro-MJ/t, ticks per 1000 mB, and residue per 1000 mB; code-based matchers versus serializable selectors; accounted runtime usage.Fuels and Coolants: Properties of Registered Resources
FuelProfileDatabuildcraft.api.v2.fuelsSerializable selector/power/burnTicks/residue, without an automatic JSON loader.Fuels and Coolants: Properties of Registered Resources
ProfileMatchbuildcraft.api.v2.fuelsStable profile ID, value, and provenance including owner and priority.Fuels and Coolants: Properties of Registered Resources
SolidCoolantConversionbuildcraft.api.v2.fuelsPure conversion of a stack into FluidVolume; item consumption is the consumer's responsibility.Fuels and Coolants: Properties of Registered Resources
SolidCoolantProfilebuildcraft.api.v2.fuelsItemMatcher and conversion, copied input and empty fallback; registered in code through the service.Fuels and Coolants: Properties of Registered Resources
GateControlbuildcraft.api.v2.gateValidated replacement of an entire program with SIMULATE/EXECUTE; the caller must authorize it.Statements and Gate Programs
GateProgrambuildcraft.api.v2.gatePhysical row order, connections between adjacent rows, and gate-variant logic.Statements and Gate Programs
GateRulebuildcraft.api.v2.gatePair of nullable trigger/action values while preserving empty physical rows.Statements and Gate Programs
GateServicebuildcraft.api.v2.gateFind a gate on the side of an already loaded pipe.Statements and Gate Programs
GateViewbuildcraft.api.v2.gatePosition/side, program snapshot, and optional control.Statements and Gate Programs
GuideEntrybuildcraft.api.v2.guideSection entry with nonempty pages, order, and ID.Content Presentation and the In-Game Guide
GuidePagebuildcraft.api.v2.guideFive standard page types; an open interface does not imply a pluggable sixth renderer.Content Presentation and the In-Game Guide
GuidePagesbuildcraft.api.v2.guideFactories for literal/translatable text, recipes, images, links, and items.Content Presentation and the In-Game Guide
GuideSectionbuildcraft.api.v2.guideSection with optional parent/icon and ordering.Content Presentation and the In-Game Guide
GuideServicebuildcraft.api.v2.guideOwner-aware registration, duplicate rejection, and ordered content lookups.Content Presentation and the In-Game Guide
ItemLabelAdapterbuildcraft.api.v2.itemSupport, reading, and changing labels; the adapter itself must honor SIMULATE.Integration Items: Tools, Maps, Facades, and Drops
ItemLabelServicebuildcraft.api.v2.itemLookup of direct/registered adapters and modification of a caller-owned stack.Integration Items: Tools, Maps, Facades, and Drops
ItemMatcherbuildcraft.api.v2.itemComposable ItemStack matcher with optional examples for UI and diagnostics.Resources and Transfers Between Storages
ItemPortbuildcraft.api.v2.itemItem insertion/extraction port; safe defaults for minimum amounts and full transfers.Resources and Transfers Between Storages
ItemTransferPolicybuildcraft.api.v2.itemChoose between partial transfers and requiring the entire requested amount.Resources and Transfers Between Storages
ItemTransferResultbuildcraft.api.v2.itemRequested and transferred item count; defensive copy of the moved stack and remaining count.Resources and Transfers Between Storages
SlottedItemPortbuildcraft.api.v2.itemOptional view of slots, their contents, and effective filters.Resources and Transfers Between Storages
LibraryEntrybuildcraft.api.v2.libraryLocal document with content ID, name, and kind.Templates, Filler, and the Electronic Library
LibraryFormatHandlerbuildcraft.api.v2.libraryConverts user bytes to a snapshot CompoundTag document and back.Templates, Filler, and the Electronic Library
LibraryFormatRegistrationbuildcraft.api.v2.libraryUnique ID/extension/priority; extension limits and reserved bcnbt.Templates, Filler, and the Electronic Library
LibraryServicebuildcraft.api.v2.libraryDecode/encode separate from local import/export; 8 MiB limit in current runtime.Templates, Filler, and the Electronic Library
ItemListbuildcraft.api.v2.listNamed immutable collection of matcher entries with list-level logic.Item Lists and Specialized Matching Rules
ItemListAdapterbuildcraft.api.v2.listItem carrying its own list configuration by implementing the interface directly.Item Lists and Specialized Matching Rules
ItemListEntrybuildcraft.api.v2.listMatcher and enabled state of each entry.Item Lists and Specialized Matching Rules
ItemListServicebuildcraft.api.v2.listNamed definitions before freeze and separate stack-backed lookups.Item Lists and Specialized Matching Rules
ListMatchAdapterbuildcraft.api.v2.listRegistered special supports/matches/examples semantics.Item Lists and Specialized Matching Rules
ListMatchContextbuildcraft.api.v2.listCopies of source/target with precise and semantic matching types.Item Lists and Specialized Matching Rules
ListMatchModebuildcraft.api.v2.listANY/ALL/NONE, including the exact outcome for an empty enabled set.Item Lists and Specialized Matching Rules
ListMatchTypebuildcraft.api.v2.listTYPE/MATERIAL/CLASS in special entry rules.Item Lists and Specialized Matching Rules
BuiltInMachinePropertiesbuildcraft.api.v2.machineCommon speed, cost, MJ limits, capacity, slots, range, and chunk loading; only effective when a consumer reads them.Machines: Your Own Block and Runtime Rules
EngineComponentbuildcraft.api.v2.machineExecutable tickEngine logic; base tick casts the context, so an engine host is required.Engines, Heat, and Laser Targets
EngineExecutionContextbuildcraft.api.v2.machinegenerate/emit, limits, simulation/execution, heat/stage/animation; MJ operations are allowed only during an active tick.Engines, Heat, and Laser Targets
EngineFactorybuildcraft.api.v2.machineEngineComponent factory using EngineExecutionContext.Engines, Heat, and Laser Targets
EngineHeatSettingsbuildcraft.api.v2.machineWorking range, ambient, critical, passive cooling, and finite/bounds validation.Engines, Heat, and Laser Targets
EngineHostbuildcraft.api.v2.machineMachine host with EngineExecutionContext, unified persistence, and separate engine component.Engines, Heat, and Laser Targets
EngineHostOwnerbuildcraft.api.v2.machineOwner-provided readable/writable outputBuffer; energy is persisted and ticked independently of the host.Engines, Heat, and Laser Targets
EngineHostProviderbuildcraft.api.v2.machineOne shared host for engine discovery, machine discovery, and gates.Engines, Heat, and Laser Targets
EngineProfilebuildcraft.api.v2.machinePer-tick output, capacity, and acceptsExternalEnergy metadata without automatic external input.Engines, Heat, and Laser Targets
EngineServicebuildcraft.api.v2.machineCreating engine hosts and locating native/third-party EngineView without loading chunks.Engines, Heat, and Laser Targets
EngineStagebuildcraft.api.v2.machineExtensible namespaced stage and nonnegative severity; not itself a renderer or effect.Engines, Heat, and Laser Targets
EngineTypebuildcraft.api.v2.machineRegistered ID and engine profile; does not create Minecraft content or clone built-in engine logic.Engines, Heat, and Laser Targets
EngineViewbuildcraft.api.v2.machineEngine observation: MachineView plus stage and current generation.Engines, Heat, and Laser Targets
EngineVisualStatebuildcraft.api.v2.machineServer stage/heat/generatedLastTick/animationPhase/soundId for custom presentation.Engines, Heat, and Laser Targets
HeatPortbuildcraft.api.v2.machineCurrent thermal state, profile, and optional temperature control with SIMULATE/EXECUTE.Engines, Heat, and Laser Targets
HeatPortProviderbuildcraft.api.v2.machineThird-party sided thermal endpoint without a machine host.Engines, Heat, and Laser Targets
HeatProfilebuildcraft.api.v2.machineFinite ordered minimum/ideal/maximum in BuildCraft degrees.Engines, Heat, and Laser Targets
HeatStatebuildcraft.api.v2.machineTemperature, critical temperature, and critical() once the threshold is reached.Engines, Heat, and Laser Targets
LaserTableTypebuildcraft.api.v2.machineNamespaced table-type metadata; registration does not provide an endpoint.Engines, Heat, and Laser Targets
LaserTargetbuildcraft.api.v2.machineDedicated MJ port for lasers and optional type ID, backed by one real storage.Engines, Heat, and Laser Targets
LaserTargetProviderbuildcraft.api.v2.machineSide-dependent target discovery; an empty result rejects a side, and lookup does not transfer energy.Engines, Heat, and Laser Targets
LaserTargetServicebuildcraft.api.v2.machineFind a loaded provider/direct target with an insert-capable port; consumed by TileLaser.Engines, Heat, and Laser Targets
MachineActionComponentbuildcraft.api.v2.machineServer-side action handling after owner.canPlayerUse and payload-size checks; authors validate IDs and payloads.Machines: Your Own Block and Runtime Rules
MachineAreaViewbuildcraft.api.v2.machineAreaProvider with validFrom check; an area is not chunk loading.Machines: Your Own Block and Runtime Rules
MachineComponentbuildcraft.api.v2.machineMinimal component identity via typeId; additional responsibilities come from separate interfaces.Machines: Your Own Block and Runtime Rules
MachineComponentFactorybuildcraft.api.v2.machineCreate a new component in a specific host context; restored components attach through lifecycle callbacks.Machines: Your Own Block and Runtime Rules
MachineComponentLifecyclebuildcraft.api.v2.machineattached/restored/activated/suspended/removed/invalidated/addDrops and their call order.Machines: Your Own Block and Runtime Rules
MachineComponentPresentationbuildcraft.api.v2.machineSeparate schema-versioned client snapshot with codec and capture function, without a server component on the client.Machines: Your Own Block and Runtime Rules
MachineComponentTypebuildcraft.api.v2.machineType ID, optional persistence, factory, and client presentation; codec IDs must match the type, and missing factories provide no behavior.Machines: Your Own Block and Runtime Rules
MachineControlbuildcraft.api.v2.machineSupported control modes, SIMULATE/EXECUTE mode transitions, and setEnabled.Machines: Your Own Block and Runtime Rules
MachineControlModebuildcraft.api.v2.machineON, OFF, and LOOP; LOOP does not implement a universal automatic repetition algorithm.Machines: Your Own Block and Runtime Rules
MachineExecutionContextbuildcraft.api.v2.machineCurrent properties, resource ports, recipe/fuel services, markChanged, status, and access to loaded world data.Machines: Your Own Block and Runtime Rules
MachineHostbuildcraft.api.v2.machineHost instance with lifecycle, persistence, packets, components, and GUI dispatch; tied to a logical side and thread.Machines: Your Own Block and Runtime Rules
MachineHostOwnerbuildcraft.api.v2.machinePlatform boundary: position, world, side, resources, dirty/synchronization, events, and permissions.Machines: Your Own Block and Runtime Rules
MachineHostProviderbuildcraft.api.v2.machineDiscovers the same active host from its own block entity.Machines: Your Own Block and Runtime Rules
MachinePropertybuildcraft.api.v2.machineTyped property ID, validator, value checking, and equality by ID and Java type.Machines: Your Own Block and Runtime Rules
MachineServicebuildcraft.api.v2.machineHost creation and discovery of loaded MachineView, work, and heat endpoints.Machines: Your Own Block and Runtime Rules
MachineTickComponentbuildcraft.api.v2.machineServer-side component work; OFF stops ticking but does not represent unloading.Machines: Your Own Block and Runtime Rules
MachineTypebuildcraft.api.v2.machineImmutable composition, properties, builder, and variants; registering a descriptor does not copy a native machine implementation.Machines: Your Own Block and Runtime Rules
MachineViewbuildcraft.api.v2.machineObserves type, position, state, control, and available ports without creating storage.Machines: Your Own Block and Runtime Rules
MachineWorkComponentbuildcraft.api.v2.machinePure query for outstanding work independent of progress; aggregation across components.Machines: Your Own Block and Runtime Rules
MachineWorldAccessbuildcraft.api.v2.machineDelegated block mutation; the owner is responsible for claims and permissions.Machines: Your Own Block and Runtime Rules
WorkStatebuildcraft.api.v2.machineIDLE/RUNNING/BLOCKED/PAUSED/ERROR in common controls and GUI.Machines: Your Own Block and Runtime Rules
WorkStatusbuildcraft.api.v2.machineState, finite progress from 0 to 1, and a short detail string.Machines: Your Own Block and Runtime Rules
MapLocationAdapterbuildcraft.api.v2.mapReading, writing, and clearing a semantic map location independently of item inheritance.Integration Items: Tools, Maps, Facades, and Drops
MapLocationKindbuildcraft.api.v2.mapCLEAN/SPOT/AREA/PATH/ZONE/PATH_REPEATING.Integration Items: Tools, Maps, Facades, and Drops
MapLocationServicebuildcraft.api.v2.mapDirect or registered adapter lookup, copies on read and SIMULATE, and actual gameplay consumers.Integration Items: Tools, Maps, Facades, and Drops
MapLocationViewbuildcraft.api.v2.mapLabel and geometry, kind factories, and the CLEAN invariant.Integration Items: Tools, Maps, Facades, and Drops
BuildCraftModulesbuildcraft.api.v2.moduleStandard public IDs for BuildCraft modules.Lifecycle and Extension Registration
ModuleInfobuildcraft.api.v2.moduleModule ID, version, and whether it is loaded.Lifecycle and Extension Registration
ModuleServicebuildcraft.api.v2.moduleChecks loaded modules and declares external modules with required dependencies.Lifecycle and Extension Registration
ActorServicebuildcraft.api.v2.permissionCreates identities for a player, machine owner, system, or unknown actor.Operation Context and Permissions
ActorTypebuildcraft.api.v2.permissionClassifies the authority source of an automated operation.Operation Context and Permissions
AutomationActorbuildcraft.api.v2.permissionStable automation identity with player UUID, optional name, and source ID.Operation Context and Permissions
OwnedViewbuildcraft.api.v2.permissionPublishes an optional owner UUID for an automated object.Operation Context and Permissions
PermissionDecisionbuildcraft.api.v2.permissionPermission decision with an authority ID and optional reason.Operation Context and Permissions
PermissionProviderbuildcraft.api.v2.permissionNamed permission provider with deterministic priority.Operation Context and Permissions
PermissionServicebuildcraft.api.v2.permissionObservational permission check for a world action; must not mutate the world even in EXECUTE.Operation Context and Permissions
PermissionServiceRegistrybuildcraft.api.v2.permissionRegisters protection providers and combines their decisions; DENY stops evaluation.Operation Context and Permissions
PermissionServicesbuildcraft.api.v2.permissionComposes multiple checks in supplied order, with any denial taking priority.Operation Context and Permissions
PermissionVerdictbuildcraft.api.v2.permissionThree-valued permission outcome: ALLOW, DENY, or PASS.Operation Context and Permissions
WorldOperationContextbuildcraft.api.v2.permissionFull permission context: actor, dimension, origin, target, operation kind, mode, and reason.Operation Context and Permissions
WorldOperationKindbuildcraft.api.v2.permissionStable world-operation categories for protection systems and auditing.Operation Context and Permissions
WorldOperationTargetbuildcraft.api.v2.permissionPermission target: block, entity UUID, both, or no specific target.Operation Context and Permissions
ApiCodecbuildcraft.api.v2.persistenceBidirectional conversion between a working value and its chosen serialized representation.State Persistence and Schema Evolution
CodecResultbuildcraft.api.v2.persistenceSuccessful value or list of codec errors; supports mapping a successful result.State Persistence and Schema Evolution
EncodedPayloadbuildcraft.api.v2.persistencePersisted extension envelope: type ID, schema version, and payload.State Persistence and Schema Evolution
KnownPayloadbuildcraft.api.v2.persistenceSuccessfully decoded value with original and canonical IDs and stored/current schema versions.State Persistence and Schema Evolution
OpaqueDatabuildcraft.api.v2.persistenceImmutable byte-state envelope with a namespaced format ID and defensive copying.State Persistence and Schema Evolution
PayloadCopierbuildcraft.api.v2.persistenceExplicit defensive copying of serialized representations, needed to preserve unknown data.State Persistence and Schema Evolution
PayloadResolutionbuildcraft.api.v2.persistenceGeneral result of resolving a saved envelope: known value or preserved unknown data.State Persistence and Schema Evolution
PersistentTypebuildcraft.api.v2.persistencePersistent-type descriptor: stable ID, current schema, codec, aliases, and forward migrations.State Persistence and Schema Evolution
SchemaMigrationbuildcraft.api.v2.persistenceOne deterministic transition of serialized data from an older to a newer schema version.State Persistence and Schema Evolution
UnknownPayloadbuildcraft.api.v2.persistenceRetains original ID, version, data, and decode-failure reason for later recovery.State Persistence and Schema Evolution
UnknownPayloadReasonbuildcraft.api.v2.persistenceUnknown-payload reasons: missing type, newer schema, absent/failed migration, or decode failure.State Persistence and Schema Evolution
ExternalEnergyRouteComponentbuildcraft.api.v2.pipeRoutes external energy separately from MJ.Fluids and Energy in Pipes
ExternalEnergyRouteContextbuildcraft.api.v2.pipeInput, integer FE offer, and candidates.Fluids and Energy in Pipes
ExternalEnergyTransportProfilebuildcraft.api.v2.pipeSeparate FE profile without automatic MJ conversion.Fluids and Energy in Pipes
FluidMovementComponentbuildcraft.api.v2.pipeLimits the amount of native fluid movement without changing ownership or variant.Fluids and Energy in Pipes
FluidMovementContextbuildcraft.api.v2.pipeStage, side, volume, offered mB, center capacity, and mode.Fluids and Energy in Pipes
FluidMovementPhasebuildcraft.api.v2.pipeINSERT/BEFORE_CENTRE/TO_CENTRE/FROM_CENTRE/OUTPUT/PIPE_TRANSFER.Fluids and Energy in Pipes
FluidRouteComponentbuildcraft.api.v2.pipeRoutes fluid volume combined at the center.Fluids and Energy in Pipes
FluidRouteContextbuildcraft.api.v2.pipeAll input sides of a merged flow, volume, and candidates.Fluids and Energy in Pipes
FluidTransportProfilebuildcraft.api.v2.pipeFluid throughput in mB per tick and transfer delay.Fluids and Energy in Pipes
ItemAdmissionComponentbuildcraft.api.v2.pipePure admission predicate for neighboring pipes and external injections; same decision for SIMULATE/EXECUTE.Item Movement: Admission, Routing, and Ownership
ItemAdmissionContextbuildcraft.api.v2.pipeCopy of incoming stack, receiving pipe side, and admission mode.Item Movement: Admission, Routing, and Ownership
ItemInjectionRequestbuildcraft.api.v2.pipeCopy of offered stack, receiving face, and ItemTransitData.Item Movement: Admission, Routing, and Ownership
ItemPipePortbuildcraft.api.v2.pipeItem insertion with pipe-specific metadata atop the general ItemPort.Item Movement: Admission, Routing, and Ownership
ItemRouteComponentbuildcraft.api.v2.pipeSelects available directions after item admission.Item Movement: Admission, Routing, and Ownership
ItemRouteContextbuildcraft.api.v2.pipeInput, stack copy, and the set of permitted routing candidates.Item Movement: Admission, Routing, and Ownership
ItemTransitComponentbuildcraft.api.v2.pipeCallback for observing and permitted control of a traveling item.Item Movement: Admission, Routing, and Ownership
ItemTransitContextbuildcraft.api.v2.pipeShort-lived context for replace/redirect/delay/claim/transfer/reject/eject with runtime ownership.Item Movement: Admission, Routing, and Ownership
ItemTransitDatabuildcraft.api.v2.pipeColor and speed in blocks per tick, default/no-color, finite values, and nonnegativity.Item Movement: Admission, Routing, and Ownership
ItemTransitPayloadbuildcraft.api.v2.pipeImmutable, bounded namespaced bytes traveling between pipes and across save/load.Item Movement: Admission, Routing, and Ownership
ItemTransitPhasebuildcraft.api.v2.pipeENTER/CENTER/intermediate/ROUTING/EXIT stages and terminal notices; mutation boundaries.Item Movement: Admission, Routing, and Ownership
ItemTransitStatusbuildcraft.api.v2.pipeDistinguishes real, partial, and simulated transfers and failure reasons.Item Movement: Admission, Routing, and Ownership
ItemTransitTransferbuildcraft.api.v2.pipeOffered/accepted/remainder; simulated acceptance and succeeded do not prove resource removal.Item Movement: Admission, Routing, and Ownership
ItemTransportProfilebuildcraft.api.v2.pipeLimit on one traveling stack and initial routing weight; not an items-per-tick budget.Item Movement: Admission, Routing, and Ownership
MjRouteComponentbuildcraft.api.v2.pipeRoutes MJ among existing candidates.Fluids and Energy in Pipes
MjRouteContextbuildcraft.api.v2.pipeInput, MjAmount, and candidates without exposing native buffers.Fluids and Energy in Pipes
PipeActivationComponentbuildcraft.api.v2.pipeComponent reaction to pipe interaction.Pipes: Types, Components, and Executable Flows
PipeActivationContextbuildcraft.api.v2.pipeActivation context: scoped pipe, side, copied held stack, actor, and mode.Pipes: Types, Components, and Executable Flows
PipeActivationResultbuildcraft.api.v2.pipeDistinguishes PASS, SUCCESS, DENIED, and FAILED on activation.Pipes: Types, Components, and Executable Flows
PipeAttachmentbuildcraft.api.v2.pipeLive attachment: side, blocking/ports, shape/loot, lifecycle, and activation.Connecting External Blocks and Pipe Attachments
PipeAttachmentFactorybuildcraft.api.v2.pipeCreates an instance, including pure SIMULATE checks.Connecting External Blocks and Pipe Attachments
PipeAttachmentPlacementContextbuildcraft.api.v2.pipeRead-only pipe, side, copied placement item, actor, and mode.Connecting External Blocks and Pipe Attachments
PipeAttachmentResultbuildcraft.api.v2.pipeSuccessful attachment or installation-failure reason.Connecting External Blocks and Pipe Attachments
PipeAttachmentTypebuildcraft.api.v2.pipeFactory and optional persistence; consequences of missing codecs.Connecting External Blocks and Pipe Attachments
PipeBehaviourComponentbuildcraft.api.v2.pipeCustom behavior separate from the transport flow.Pipes: Types, Components, and Executable Flows
PipeBehaviourDefinitionbuildcraft.api.v2.pipeRegisters behavior with mandatory persistence.Pipes: Types, Components, and Executable Flows
PipeComponentbuildcraft.api.v2.pipeIdentity of an individual pipe-component instance.Pipes: Types, Components, and Executable Flows
PipeComponentFactorybuildcraft.api.v2.pipeCreates a separate component for a specific PipeView.Pipes: Types, Components, and Executable Flows
PipeComponentLifecyclebuildcraft.api.v2.pipeSeparates attached/restored/placed/active/suspended/removed/invalidated, topology notifications, and read-only drops.Pipes: Types, Components, and Executable Flows
PipeComponentSyncbuildcraft.api.v2.pipeSnapshot/apply and ON_REQUEST/WHEN_CHANGED policies; replay of client-side application.Pipes: Types, Components, and Executable Flows
PipeComponentTypebuildcraft.api.v2.pipeRegisters component factory, persistence, and typed synchronization.Pipes: Types, Components, and Executable Flows
PipeConnectionChangebuildcraft.api.v2.pipeEvent reporting a committed topology change on one side.Connecting External Blocks and Pipe Attachments
PipeConnectionComponentbuildcraft.api.v2.pipeComponent-local pipe connection rule.Connecting External Blocks and Pipe Attachments
PipeConnectionContextbuildcraft.api.v2.pipeWorld, pipe, side, and neighbor state for connection checks.Connecting External Blocks and Pipe Attachments
PipeConnectionDecisionbuildcraft.api.v2.pipePASS/ALLOW/DENY, with DENY taking precedence over ALLOW.Connecting External Blocks and Pipe Attachments
PipeConnectionRulebuildcraft.api.v2.pipeRegistered global connection rule.Connecting External Blocks and Pipe Attachments
PipeEndpointKindbuildcraft.api.v2.pipeClassifies an installed connection as NONE/PIPE/BLOCK.Connecting External Blocks and Pipe Attachments
PipeEndpointProviderbuildcraft.api.v2.pipeRegisters lookups for neighboring loaded endpoints without modifying the world.Connecting External Blocks and Pipe Attachments
PipeEventListenerbuildcraft.api.v2.pipeGeneric callback shape; this runtime has no connected registration/dispatcher.Pipes: Types, Components, and Executable Flows
PipeExecutionContextbuildcraft.api.v2.pipeActivity, loaded neighbors, color changes, deferred topology refresh, and attachment sync.Pipes: Types, Components, and Executable Flows
PipeExternalEndpointbuildcraft.api.v2.pipeRuntime resource/signal ports, canConnect, and bounded extension; registration does not create a connection.Connecting External Blocks and Pipe Attachments
PipeFeTransferResultbuildcraft.api.v2.pipeInteger FE requested/transferred/remainder.Fluids and Energy in Pipes
PipeFlowComponentbuildcraft.api.v2.pipeCustom flow with its own resources and transfer algorithm instead of the standard native flow.Pipes: Types, Components, and Executable Flows
PipeFlowDefinitionbuildcraft.api.v2.pipeRegisters a full flow with mandatory persistence and matching component ID.Pipes: Types, Components, and Executable Flows
PipeFluidTransferResultbuildcraft.api.v2.pipeRequested fluid volume, actually transferred variant/volume, and remainder.Fluids and Energy in Pipes
PipeMediumbuildcraft.api.v2.pipeFive standard transport media plus structural pipe; distinct from namespaced custom media.Pipes: Types, Components, and Executable Flows
PipeMjTransferResultbuildcraft.api.v2.pipeRequested/transferred micro-MJ and remainder, without conversion.Fluids and Energy in Pipes
PipeMutationContextbuildcraft.api.v2.pipeScoped markChanged/requestSync and borrowed worldAccess in callbacks.Pipes: Types, Components, and Executable Flows
PipeResourceServicebuildcraft.api.v2.pipeDirect native transfers on the same loaded server, with permissions, budgets, and in-memory atomicity.Fluids and Energy in Pipes
PipeResourceStatusbuildcraft.api.v2.pipeMOVED/PARTIAL/SIMULATED/BLOCKED and precise failure categories.Fluids and Energy in Pipes
PipeResourceTickComponentbuildcraft.api.v2.pipeServer callback after standard fluid/energy ticking.Fluids and Energy in Pipes
PipeResourceTickContextbuildcraft.api.v2.pipeTime-scoped callback access to pipe, world, medium, and transfer service.Fluids and Energy in Pipes
PipeRuntimeComponentbuildcraft.api.v2.pipeCommon base for custom flow/behavior: server lifecycle/ticking, collision, post-attachment callback, and optional ports.Pipes: Types, Components, and Executable Flows
PipeServicebuildcraft.api.v2.pipeSeparates createItem/loader registration from runtime lookup, injection, and attachments.Pipes: Types, Components, and Executable Flows
PipeSyncChannelbuildcraft.api.v2.pipeTyped synchronization schema, codec, ID, and byte limits.Pipes: Types, Components, and Executable Flows
PipeTickComponentbuildcraft.api.v2.pipeTick behavior of an ordinary component, accounting for server/client side.Pipes: Types, Components, and Executable Flows
PipeTypebuildcraft.api.v2.pipeImmutable pipe type, standalone/variant, medium, components, archetype, flow, and behavior; nested builder.Pipes: Types, Components, and Executable Flows
PipeViewbuildcraft.api.v2.pipeLook up live pipes, topology, components, attachments, resource ports, and client presentation.Pipes: Types, Components, and Executable Flows
PowerFlowComponentbuildcraft.api.v2.pipeDistinguishes changes in effective configuration from limits on an individual power offer.Fluids and Energy in Pipes
PowerFlowSettingsbuildcraft.api.v2.pipeMedium/throughput/loss/resistance/receiver/disabled rules and the -1 sentinel.Fluids and Energy in Pipes
PowerMovementContextbuildcraft.api.v2.pipeMJ in micro-MJ and FE in native units; side, phase, and mode.Fluids and Energy in Pipes
PowerMovementPhasebuildcraft.api.v2.pipeREQUEST/EXTRACT/SEND/PIPE_TRANSFER/RECEIVE; separates demand from actual delivery.Fluids and Energy in Pipes
PowerTransportProfilebuildcraft.api.v2.pipeMaximum MJ, losses/resistance, extractor, and checked helpers.Fluids and Energy in Pipes
RouteDecisionbuildcraft.api.v2.pipeNonnegative weight multipliers, explicit zero veto, absent-side no-op, and limits of the blocked() helper.Item Movement: Admission, Routing, and Ownership
EnergyTransferbuildcraft.api.v2.platformFinds an external integer-energy port through the loader bridge.Resources and Transfers Between Storages
ExternalEnergyPortbuildcraft.api.v2.platformPort for an external energy system such as FE, with transfers and capacity queries.Resources and Transfers Between Storages
FluidTransferbuildcraft.api.v2.platformFinds a loader-native fluid handler and exposes it as FluidPort.Resources and Transfers Between Storages
ItemTransferbuildcraft.api.v2.platformFinds a loader-native inventory and exposes it as ItemPort.Resources and Transfers Between Storages
PlatformServicesbuildcraft.api.v2.platformProvides optional item, fluid, and external-energy transfer bridges.Resources and Transfers Between Storages
AssemblyIngredientsbuildcraft.api.v2.recipeNon-mutating simulation of ingredient allocation, supporting overlapping exact/tag matching.Machine Recipes: Requirements, Results, and Definition Updates
AssemblyRecipeDefinitionbuildcraft.api.v2.recipePure outputs/previews/requirements/cost for multi-output and component-dependent recipes.Machine Recipes: Requirements, Results, and Definition Updates
CountedIngredientbuildcraft.api.v2.recipeItem count and item/tag/exact-data matcher; distinguishes matchesItem from test across target families.Machine Recipes: Requirements, Results, and Definition Updates
DistillationRecipeDatabuildcraft.api.v2.recipeData form for input and optional gas/liquid variants, their mB, and micro-MJ.Machine Recipes: Requirements, Results, and Definition Updates
DistillationRecipeDefinitionbuildcraft.api.v2.recipeInput, two outputs (at least one nonempty), nonnegative power, builder, and codec.Machine Recipes: Requirements, Results, and Definition Updates
FluidIngredientbuildcraft.api.v2.recipeFluidMatcher with positive amount, exact/matching forms; separates variant matching from actual tank volume.Machine Recipes: Requirements, Results, and Definition Updates
FluidIngredientDatabuildcraft.api.v2.recipeSerializable exact FluidVariantData plus mB; arbitrary matchers are not encoded.Machine Recipes: Requirements, Results, and Definition Updates
HeatExchangeRecipeDatabuildcraft.api.v2.recipeSerializable kind/input/output/heat values with codec validation.Machine Recipes: Requirements, Results, and Definition Updates
HeatExchangeRecipeDefinitionbuildcraft.api.v2.recipeHEATING/COOLING and ordered heatFrom/heatTo, outputs, and actual exchanger ratios.Machine Recipes: Requirements, Results, and Definition Updates
IntegrationRecipeDefinitionbuildcraft.api.v2.recipeTarget and extra items, center requirement, cost/output, and consumption of all extra ingredients.Machine Recipes: Requirements, Results, and Definition Updates
IntegrationRecipeProviderbuildcraft.api.v2.recipeLazy recipeFor using copies; null means no match.Machine Recipes: Requirements, Results, and Definition Updates
MachineRecipeServicebuildcraft.api.v2.recipeAuthoritative code registration, snapshot+generation, typed find/query ordering and native consumers.Machine Recipes: Requirements, Results, and Definition Updates
RecipeDefinitionbuildcraft.api.v2.recipeFive Kind values and the required concrete implementation; a marker alone does not execute a recipe.Machine Recipes: Requirements, Results, and Definition Updates
RecipeMatchbuildcraft.api.v2.recipeID, definition, and provenance of a chosen recipe, for traceable origin.Machine Recipes: Requirements, Results, and Definition Updates
SimpleAssemblyRecipebuildcraft.api.v2.recipeFixed output with defensive copying, positive cost, and validated requirements.Machine Recipes: Requirements, Results, and Definition Updates
ApiRegistrybuildcraft.api.v2.registryRegisters and locates extensions, owners, and aliases; reports freeze state.Lifecycle and Extension Registration
RegistrationContextbuildcraft.api.v2.registrySupplies the registration owner for extension provenance.Lifecycle and Extension Registration
RegistryEntrybuildcraft.api.v2.registryRegistry entry containing ID, value, and nonempty owner name.Lifecycle and Extension Registration
RegistryKeybuildcraft.api.v2.registryTyped registry key identified by namespaced ID.Lifecycle and Extension Registration
DefinitionDecoderbuildcraft.api.v2.reloadDecodes a structured Map into an addon-owned typed definition.Reloadable Definitions
DefinitionEntrybuildcraft.api.v2.reloadOne typed definition with ID, value, and provenance.Reloadable Definitions
DefinitionProvenancebuildcraft.api.v2.reloadDefinition owner, source, and priority for conflict resolution.Reloadable Definitions
DefinitionReloadServicebuildcraft.api.v2.reloadRegisters domains, decoders, contributors, and observers; publishes new definition snapshots.Reloadable Definitions
DefinitionSnapshotbuildcraft.api.v2.reloadImmutable snapshot of resolved definitions with generation and ID lookup.Reloadable Definitions
ReloadChangebuildcraft.api.v2.reloadPair of previous and proposed snapshots for one domain's publication callbacks.Reloadable Definitions
ReloadContributorbuildcraft.api.v2.reloadAdds code-owned definitions before decoding resource-supplied definitions.Reloadable Definitions
ReloadDiagnosticbuildcraft.api.v2.reloadReload warning or error with optional definition ID.Reloadable Definitions
ReloadGenerationbuildcraft.api.v2.reloadNonnegative monotonic reload-attempt number.Reloadable Definitions
ReloadObserverbuildcraft.api.v2.reloadCallbacks before and after publication; pre-publication rejection prevents snapshot replacement.Reloadable Definitions
ReloadPublishResultbuildcraft.api.v2.reloadPublication outcome: whether a snapshot was replaced, attempt generation, and diagnostic messages.Reloadable Definitions
ResolvedDefinitionbuildcraft.api.v2.reloadWinning definition and overridden alternatives for explaining a reload outcome.Reloadable Definitions
ScriptableDefinitionTypebuildcraft.api.v2.reloadReloadable-domain ID, Java value type, and default decoder.Reloadable Definitions
ScriptedDefinitionbuildcraft.api.v2.reloadInput definition of kind VALUE, ALIAS, or DISABLED, with provenance and immutable structured data.Reloadable Definitions
ScriptedDefinitionSnapshotbuildcraft.api.v2.reloadSnapshot of values, aliases, and disabled entries with canonical ID resolution.Reloadable Definitions
ItemRequestbuildcraft.api.v2.requestStable request ID, matcher, minimum, maximum, and priority.Request Logistics: Demand, Promises, and Confirmed Delivery
ManagedRequestProviderbuildcraft.api.v2.requestConnects host inventory insertion, game time, and persisted RequestLedger without double accounting.Request Logistics: Demand, Promises, and Confirmed Delivery
RequestLedgerbuildcraft.api.v2.requestLocal persisted accounting, phases/views/results, all-or-nothing reservations, replay window, timeouts, rebinding, and preservation on load.Request Logistics: Demand, Promises, and Confirmed Delivery
RequestProviderbuildcraft.api.v2.requestSlot-independent discovery and offers with actual ItemTransferResult.Request Logistics: Demand, Promises, and Confirmed Delivery
RequestServicebuildcraft.api.v2.requestLookup of loaded providers, provider-enumeration limitations, permission-aware reserve/cancel/deliver.Request Logistics: Demand, Promises, and Confirmed Delivery
BlockRobotResourcebuildcraft.api.v2.robotBuilt-in reservation of a block/side with amount one.Robots: Programs, Tasks, and Work Reservations
BuildCraftDockPortsbuildcraft.api.v2.robotTyped ITEMS/FLUIDS/MJ/EXTERNAL_ENERGY ports and conditional availability.Robots: Programs, Tasks, and Work Reservations
BuildCraftRobotBoardsbuildcraft.api.v2.robotStable built-in board IDs; not a registry of every classic AI task.Robots: Programs, Tasks, and Work Reservations
DockPortResolverbuildcraft.api.v2.robotResolve a custom port from dock context.Robots: Programs, Tasks, and Work Reservations
DockPortTypebuildcraft.api.v2.robotRegistrable ID, class, and resolver for a custom port type.Robots: Programs, Tasks, and Work Reservations
RobotBoardProgrambuildcraft.api.v2.robotConnects the Programming Table, MJ cost, color, texture, and persistent-task factory.Robots: Programs, Tasks, and Work Reservations
RobotBoardTypebuildcraft.api.v2.robotID, tier, and permitted task-type set; a descriptor alone is insufficient for a program.Robots: Programs, Tasks, and Work Reservations
RobotControlbuildcraft.api.v2.robotMove/assign/cancel with mode, plus taskPhase; caller must authorize the player's command.Robots: Programs, Tasks, and Work Reservations
RobotDockbuildcraft.api.v2.robotRead-only dock address and occupancy, with typed port resolution.Robots: Programs, Tasks, and Work Reservations
RobotDockContextbuildcraft.api.v2.robotWorld, position, side, and occupancy for addon resolvers.Robots: Programs, Tasks, and Work Reservations
RobotEventContextbuildcraft.api.v2.robotKind, handle, world, actor, and copy of heldItem.Robots: Programs, Tasks, and Work Reservations
RobotEventDecisionbuildcraft.api.v2.robotDeterministic composition of DENY > ALLOW > PASS.Robots: Programs, Tasks, and Work Reservations
RobotEventKindbuildcraft.api.v2.robotDeclared events versus actual PLACE/INTERACT/DISMANTLE dispatch calls.Robots: Programs, Tasks, and Work Reservations
RobotEventListenerbuildcraft.api.v2.robotRobot-event registry; use RobotTaskLifecycle for task lifecycle hooks.Robots: Programs, Tasks, and Work Reservations
RobotHandlebuildcraft.api.v2.robotObserves a loaded robot; persist its ID rather than the Java handle.Robots: Programs, Tasks, and Work Reservations
RobotResourcebuildcraft.api.v2.robotTyped description of reservable work.Robots: Programs, Tasks, and Work Reservations
RobotResourceAcquirerbuildcraft.api.v2.robotStrategy for acquiring a custom resource and issuing a lease.Robots: Programs, Tasks, and Work Reservations
RobotResourceLeasebuildcraft.api.v2.robotGranted amount, owner, active status, and release; repeated close must be safe.Robots: Programs, Tasks, and Work Reservations
RobotResourceRequestbuildcraft.api.v2.robotPositive amount of requested resource.Robots: Programs, Tasks, and Work Reservations
RobotResourceTypebuildcraft.api.v2.robotAssociates resource type, persistence, and runtime acquirer.Robots: Programs, Tasks, and Work Reservations
RobotServicebuildcraft.api.v2.robotLoaded robot/dock lookup, reservation acquisition, and events.Robots: Programs, Tasks, and Work Reservations
RobotStatusbuildcraft.api.v2.robotSummary status; not every enum value is returned by the current implementation.Robots: Programs, Tasks, and Work Reservations
RobotTaskbuildcraft.api.v2.robotPersistent tick-driven state machine with bounded work per step.Robots: Programs, Tasks, and Work Reservations
RobotTaskContextbuildcraft.api.v2.robotWorld, robot, actor, and helpers for reservations and requests.Robots: Programs, Tasks, and Work Reservations
RobotTaskLifecyclebuildcraft.api.v2.robotAssignment, suspension/resumption, and one terminal callback per instance.Robots: Programs, Tasks, and Work Reservations
RobotTaskResultbuildcraft.api.v2.robotRUNNING/COMPLETE/RETRY/FAILED; RETRY does not introduce a delay automatically.Robots: Programs, Tasks, and Work Reservations
RobotTaskTypebuildcraft.api.v2.robotExplicit registration of a task's Java type and serialization before assignment.Robots: Programs, Tasks, and Work Reservations
BlueprintMetadatabuildcraft.api.v2.schematicMetadata for name/author/bounds; not a factory for a complete blueprint.Schematics: Capturing State and Building
DataPathbuildcraft.api.v2.schematicExact path of nonempty segments to hidden NBT content.Schematics: Capturing State and Building
EntitySchematicAdapterbuildcraft.api.v2.schematicEntity capture, restoration, requirements, and relative position.Schematics: Capturing State and Building
InventoryCopyPolicybuildcraft.api.v2.schematicTargeted allow/deny policy for copying concealed item contents without duplication.Schematics: Capturing State and Building
SchematicAdapterbuildcraft.api.v2.schematicCapture/placement, ownership, dependencies, requirements, deferred items, rotation, and criteria for a built block.Schematics: Capturing State and Building
SchematicCaptureContextbuildcraft.api.v2.schematicWorld, origin, position, state, and actor for block capture.Schematics: Capturing State and Building
SchematicEntityCaptureContextbuildcraft.api.v2.schematicWorld, origin, entity, and actor for entity capture.Schematics: Capturing State and Building
SchematicPlacementContextbuildcraft.api.v2.schematicWorld, origin, position, actor, and mode for element restoration.Schematics: Capturing State and Building
SchematicRequirementsbuildcraft.api.v2.schematicImmutable item/fluid requirements and defensive ItemStack copies.Schematics: Capturing State and Building
SchematicResultbuildcraft.api.v2.schematicPASS/SUCCESS/MISSING_RESOURCE/DENIED/FAILED with diagnostic text.Schematics: Capturing State and Building
SchematicServicebuildcraft.api.v2.schematicCapture and placement of individual elements, not a transaction over the entire build.Schematics: Capturing State and Building
SnapshotElementbuildcraft.api.v2.schematicTyped snapshot-element data with stable typeId.Schematics: Capturing State and Building
SnapshotElementTypebuildcraft.api.v2.schematicAssociation of element-type ID, schema version, and persistence.Schematics: Capturing State and Building
SnapshotKindbuildcraft.api.v2.schematicTEMPLATE and BLUEPRINT as semantic kinds, without a universal full-blueprint builder.Schematics: Capturing State and Building
UnknownSnapshotElementbuildcraft.api.v2.schematicPreserves original type, schema, and payload when an implementation is temporarily unavailable.Schematics: Capturing State and Building
ServiceKeybuildcraft.api.v2.serviceTyped key for retrieving a service by namespaced ID.Lifecycle and Extension Registration
BuildCraftSignalChannelsbuildcraft.api.v2.signalCanonical IDs for 16 colors and mapping to DyeColor.Signals and Wire Networks
SignalChannelTypebuildcraft.api.v2.signalGeneric value type, default, codec, and combiner; does not itself create a network backend.Signals and Wire Networks
SignalCombinerbuildcraft.api.v2.signalCombining rule, with OR for built-in Boolean channels.Signals and Wire Networks
SignalEndpointbuildcraft.api.v2.signalDescriptor for position, side, and channel type.Signals and Wire Networks
SignalNetworkViewbuildcraft.api.v2.signalServer-side inspection of resultant values and endpoints.Signals and Wire Networks
SignalPortbuildcraft.api.v2.signalSeparates observed value, locally published value, and receive/publish operations.Signals and Wire Networks
SignalPortProviderbuildcraft.api.v2.signalPort on a block entity's face with a canonical channel object.Signals and Wire Networks
SignalServicebuildcraft.api.v2.signalRuntime lookup, connection checks, and networks; classic-only backend in this version.Signals and Wire Networks
SignalUpdateResultbuildcraft.api.v2.signalChanged/previous/current as applied or proposed change, according to mode.Signals and Wire Networks
ActionExecutorbuildcraft.api.v2.statementCallback for actual action execution with context and parameters.Statements and Gate Programs
ActionTypebuildcraft.api.v2.statementID, parameter schema, and action callback.Statements and Gate Programs
ParameterSchemabuildcraft.api.v2.statementImmutable description of statement parameters.Statements and Gate Programs
ParameterSpecbuildcraft.api.v2.statementParameter slot ID, parameter-type ID, and required flag.Statements and Gate Programs
ParameterTypebuildcraft.api.v2.statementCanonical parameter type, codec, and suggestions.Statements and Gate Programs
ParameterValuebuildcraft.api.v2.statementAssociation of parameter type and actual value.Statements and Gate Programs
StatementCollectorbuildcraft.api.v2.statementDeclaration of available IDs, distinct from registering types.Statements and Gate Programs
StatementContextbuildcraft.api.v2.statementViews, actor, mode, optional side; having a context does not guarantee the actor's permissions.Statements and Gate Programs
StatementContextsbuildcraft.api.v2.statementPublic GATE/GATE_POSITION/EXTERNAL_TARGET keys and optional availability.Statements and Gate Programs
StatementContributorbuildcraft.api.v2.statementContext-dependent discovery and scope membership.Statements and Gate Programs
StatementKindbuildcraft.api.v2.statementDistinguishes trigger and action within a physical slot.Statements and Gate Programs
StatementParametersbuildcraft.api.v2.statementNamed map of values and typed Optional getter.Statements and Gate Programs
StatementResultbuildcraft.api.v2.statementPASS/SUCCESS/DENIED/FAILED and diagnostics from action calls.Statements and Gate Programs
StatementScopebuildcraft.api.v2.statementINTERNAL/INTERNAL_SIDED/EXTERNAL and selected side.Statements and Gate Programs
StatementServicebuildcraft.api.v2.statementLookup/discovery, validation, trigger evaluate, special SIMULATE action shortcut.Statements and Gate Programs
StatementSlotbuildcraft.api.v2.statementSelected statement ID, kind, side, and parameters.Statements and Gate Programs
StatementSuggestionProviderbuildcraft.api.v2.statementParameter-value suggestions for UI, not a replacement for validation.Statements and Gate Programs
StatementValidationbuildcraft.api.v2.statementSchema checking: required/type/unknown/duplicate slot.Statements and Gate Programs
TriggerEvaluatorbuildcraft.api.v2.statementRead-only trigger callback.Statements and Gate Programs
TriggerTypebuildcraft.api.v2.statementID, parameter schema, and trigger callback.Statements and Gate Programs
TemplateHandlerbuildcraft.api.v2.templateNon-BlockItem predicate and executable callback; no OperationMode.Templates, Filler, and the Electronic Library
TemplateRegistrationbuildcraft.api.v2.templateIdentified template handler with priority.Templates, Filler, and the Electronic Library
TemplateServicebuildcraft.api.v2.templateOrdered handler chain and rollback boundaries.Templates, Filler, and the Electronic Library
TestApiRuntimebuildcraft.api.v2.testkitLocal in-memory runtime for addon tests: services, registries, features, and explicit phase control.Diagnosing and Validating Integrations
TestExtensionContextbuildcraft.api.v2.testkitMutable test context that checks Java value types on insertion.Diagnosing and Validating Integrations
WrenchAdapterbuildcraft.api.v2.toolRecognition and notification of successful wrench usage.Integration Items: Tools, Maps, Facades, and Drops
WrenchContextbuildcraft.api.v2.toolPublic tool/actor/mode container without a nonexistent WrenchService.use.Integration Items: Tools, Maps, Facades, and Drops
WrenchServicebuildcraft.api.v2.toolRecognition only, including adapters and the configured c:tools/wrench tag.Integration Items: Tools, Maps, Facades, and Drops
WrenchUseContextbuildcraft.api.v2.toolPlayer, hand, live stack, and hit result after a successful interaction.Integration Items: Tools, Maps, Facades, and Drops
WorldAccessContextbuildcraft.api.v2.worldRestricted world view and checked ports; lifetime, thread, chunk loading, and permissions.Areas, Maps, and World Access
WorldAccessServicebuildcraft.api.v2.worldCreation of ordinary and callback-scoped world views.Areas, Maps, and World Access
WorldPropertybuildcraft.api.v2.worldNamed world predicate; caller manages caching and clearing.Areas, Maps, and World Access
WorldPropertyServicebuildcraft.api.v2.worldRegistration and lookup of named predicates; unknown properties test false.Areas, Maps, and World Access
WorldRuleServicebuildcraft.api.v2.worldSeparates softness, replaceability, and advance permission to break/place.Areas, Maps, and World Access
ResourceDepositRulebuildcraft.api.v2.worldgenProfile/targets/frequency/priority/enabled, with a real STANDARD_OIL consumer.Plants and Resource Generation
WorldTargetSelectorbuildcraft.api.v2.worldgenInclusions by ID/tag, AND between groups, exclusions, and dimension-type tags.Plants and Resource Generation
WorldgenServicebuildcraft.api.v2.worldgenRegisters and queries rules; not a general generator registry or retrogen mechanism.Plants and Resource Generation