Compatibility
Minecraft: Java Edition
Platforms
Supported environments
Links
Tags
Creators
Details
Integrated Energy Management System (IEMS)
An energy management framework mod designed for large modpacks: a unified energy abstraction, high-precision BigInteger arithmetic, laser topology visualization, and a web monitoring panel. Devices and cores are hooked up by external mods; IEMS's job is to weave them into one single power grid.
Development team: 等离子工作室 (DLZstudio)
Mod ID: iems
Development codename: IEMS0930
License: MIT License
📖 Table of Contents
- Introduction
- Blocks
- Gameplay Guide
- Connection System
- Protocol Capacity System
- Web Monitoring Panel
- Energy Exchange Rate Configuration
- Render Configuration
- Persistent Storage
- Advanced Gameplay
- Technical Details
- Device Discovery System
- Adapter Layer
- Developer API
- Building and Development
- Requirements
- FAQ
- Debug Commands
- Changelog
- Acknowledgments
Introduction
IEMS 0.8.0-beta is a complete rewrite of the old IEMS (with a total separation of logic layer and presentation layer):
- Energy management framework — this mod does not ship power generation/storage cores; cores and devices are hooked up by external mods through the API (official reference implementation: IEMS-TestDevices)
- Unified energy abstraction — units such as FE / AE are uniformly converted to SE (Standard Energy), with BigInteger high-precision arithmetic and no precision loss for extremely large values
- Laser connection visualization — laser beams between devices show the grid topology in real time: powered yellow / unpowered red / bridged cyan (configurable)
- Manual wire pulling + automatic networking — Shift + right-click to drag a wire and create a connection (HUD shows live distance), automatic scanning and connection within a broadcast tower's range, automatic bridging of external FE devices
- Protocol capacity management — a device-count-based load limit for the grid, with automatic shutdown protection when exceeded
- Web monitoring panel — view grid status, devices, topology, and historical metrics in a browser in real time (JDK built-in HttpServer, zero third-party dependencies)
- Simulation-style persistence — grid data is stored in
iems_grid.datand rebuilt from device factories after a restart; it keeps running even when chunks are not loaded - Open API — a stable
IEMSAPIstatic facade + theIIemsInteractablewire-pulling contract, for external mods to hook into
⚠️ Important: this mod is a framework and does not provide core blocks! You need to install a mod that provides a core (the official test mod IEMS-TestDevices, or wire one up yourself via the API) to form a complete grid.
Blocks
Core Block
📌 Provided by other mods through
CoreDevice(e.g. the test core from IEMS-TestDevices)
- The grid's identity anchor, the main inlet/outlet of the energy pool, and the protocol capacity manager
- The chunk it is in when registered is automatically set to force-loaded (released on unregister)
- Unique instance; the starting point for the grid's BFS reachability check
Energy Transfer Relay
- Long-distance signal relaying and power transfer; a manual wire-pulling device (does not take part in automatic scanning)
- Connection distance: 500 blocks
- Laser anchor: block center (0.5, 10.75, 0.5) (tower top)
- Laser color: yellow (powered) / red (unpowered)
- Protocol capacity used: 1
Energy Broadcast Tower
- Short-range wireless transfer, with automatic + manual dual modes
- Connection distance: 50 blocks
- Signal source position: tower top (0.5, 8.0, 0.5)
- After placement, automatically scans and connects devices in range (including relays and other broadcast towers)
- Must be connected to the core directly or indirectly to actually supply power (isolated connections are rendered with a red laser)
- Protocol capacity used: 1
- Comes with light level 7, visible in the dark
External FE Devices (automatic bridging)
- IEMS automatically discovers any NeoForge FE device in range (generators/consumers/energy storage from other tech mods)
- Rescans every 20 ticks (1 second) and automatically creates a bridge connection on discovery (cyan laser, configurable to a regular color)
- The other mod needs no adaptation at all
Gameplay Guide
Getting Started
1. Understand the energy unit
All energy uses SE (Standard Energy) as its unified unit and is stored precisely as a BigInteger. The energy of external FE devices is converted automatically by the bridge layer.
2. How the grid works
┌────────────────────────────────────────────┐
│ Core (provided by another mod) │
│ Energy pool · Protocol cap · BFS root │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ ↓ ↓ ↓ │
│ ┌────────┐ ┌────────┐ ┌──────────┐ │
│ │ Relay │───│ Tower │───│FE device │ │
│ │500 blk │ │ 50 blk │ │ auto-brg │ │
│ └────────┘ └────────┘ └──────────┘ │
└────────────────────────────────────────────┘
3. Networking workflow
- Install a mod that provides a core and place the core
- Place relays / broadcast towers (or make direct use of existing FE devices)
- Aim at a device and Shift + right-click to enter wire-pulling mode (the HUD shows the live distance)
- Aim at the target device and Shift + right-click to finish the connection
- Devices within a broadcast tower's range are networked automatically; FE devices are bridged automatically
- Open
http://127.0.0.1:28567/in a browser to monitor the grid
Device Power Status
- Devices connected to the core (directly or via relays/broadcast towers) are powered automatically
- When the grid is shut down (protocol capacity exceeded or manual shutdown) all devices lose power and lasers turn red
- Connections where both ends are registered but not connected to the core (islanded connections) are rendered as a red laser
Connection System
Manual Wire-Pulling Mode
How to enter:
- Aim at a device that implements the wire-pulling contract and press Shift + right-click
During wire pulling:
- The HUD shows the live pulling distance and the maximum distance
- Beyond the maximum distance (+10 blocks of buffer) the wire is retracted automatically with a notification
- A dimension change (going through a portal / cross-dimension TP) retracts the wire immediately — old coordinates are meaningless in the new dimension
- Leaving the world clears the wire-pulling state automatically
Completing the connection:
- Aim at the target device and press Shift + right-click again
- The client sends a connection request, and the server re-validates it (legal endpoints, same dimension, distance, duplicates, relay rules) before creating the connection
- Clicking the starting point itself or a non-device block → cancels wire pulling
Valid Connection Rules
| Connection type | Valid | Condition |
|---|---|---|
| Core → Relay | ✅ | Within distance, core force-loaded |
| Core → Broadcast tower | ✅ | Within distance |
| Relay → Relay | ✅ | Within 500 blocks |
| Relay → Broadcast tower | ✅ | Uses the smaller distance limit of the two ends |
| Broadcast tower → Broadcast tower | ✅ | Within 50 blocks, automatic or manual |
| Broadcast tower → FE device | ✅ | Automatic bridging (ADAPTER_BRIDGE) |
| Cross-dimension connection | ❌ | Rejected by both client and server (except dimension bridges) |
| Island-to-island interconnection not connected to the core | ⚠️ | Allowed to be created, rendered as a red laser, not powered |
Connection Distance Limits
⚠️ The distance limit is the smaller of the distances declared by the two devices at the ends. When nothing is declared, the framework default of 500 blocks is used.
| Device combination | Maximum distance |
|---|---|
| Relay ↔ Relay | 500 blocks |
| Broadcast tower ↔ Broadcast tower | 50 blocks |
| Broadcast tower ↔ Relay | 50 blocks |
| Bridge (FE device ↔ device) | Follows the anchoring device |
Laser Display
| State | Color | Description |
|---|---|---|
| Powered connection | Yellow | Animated with power flow |
| Unpowered / islanded connection | Red | Grid shut down or not connected to the core |
| FE bridge | Cyan (default) | Distinguished from regular connections when colorizeAutoConnect = true |
| FE bridge | Yellow/Red | Same color as regular connections when colorizeAutoConnect = false |
Rendering features:
- Beam width gets a screen-space minimum-width compensation based on distance (does not become too thin to see at a distance)
- Double-sided rendering, visible from any viewing angle
- Cross-dimension islanded connections are not rendered (direction vector cannot be computed)
- Drawn as long as either end is within render distance (long connections are not culled as a whole by the frustum)
Protocol Capacity System
What Is Protocol Capacity?
Protocol capacity is the upper limit on the number of device connections the grid can support.
| Device | Capacity used |
|---|---|
| Relay | 1 |
| Broadcast tower | 1 |
| Externally connected device | Declared by the device |
| Core | 0 (uses none) |
Limit: declared by the core at registration time (protocolLimit), adjustable at runtime.
Over-Limit Protection
When used capacity exceeds total capacity:
- The grid shuts down automatically
- All devices lose power and lasers turn red
/iems statusshows the reason for the shutdown
How to recover:
- Remove some devices (an automatic recovery is triggered after a rescan)
- Or have the core mod raise the capacity at runtime
Web Monitoring Panel
How to Access
- Launch the game and load a world (the panel starts/stops with the server)
- Visit in a browser:
http://127.0.0.1:28567/ - A random token is generated automatically on first launch (written to the config file)
Authentication
- All
/api/*endpoints require a token: request headerX-IEMS-Token: <token>or query parameter?token=<token> /healthand/(home page) need no authentication- The token lives in
config/DLZstudio/IEMS/APIserve.toml
Config File
# config/DLZstudio/IEMS/APIserve.toml
[APIserve]
enabled = true # Whether to enable the web panel
host = "127.0.0.1" # Bind address; for remote access change to "0.0.0.0" (make sure to change the token too and set up a TLS reverse proxy)
port = 28567 # Listening port
token = "a1B2c3D4..." # API token (randomly generated on first launch)
Endpoint Overview
| Method | Path | Description |
|---|---|---|
| GET | /health |
Liveness probe (no envelope) |
| GET | /api/status |
Grid overview (core position/energy/capacity/protocol/shutdown status) |
| GET | /api/devices |
Device list (including the core, per-device attributes) |
| GET | /api/connections |
Connection list (reachable main grid + boundary connections) |
| GET | /api/topology |
Topology snapshot (aggregated main grid/islands/boundary views) |
| GET | /api/metrics |
Time-series metrics (ring buffer, sampled energy/capacity/protocol/device count) |
| POST | /api/grid/active |
Manually enable/disable the grid (maps to IEMSAPI.setGridActive) |
Data Envelope
{
"schema": 1,
"ts": 1727073600000,
"data": { }
}
- BigInteger fields such as energy/capacity/rates are transmitted as decimal strings (to avoid precision loss)
- Dimensions use resource-key strings (e.g.
"minecraft:overworld") - With no core, energy returns
"0"andcorePosisnull
curl Examples
# Read-only endpoint
curl -H "X-IEMS-Token: <token>" http://127.0.0.1:28567/api/status
# Write endpoint (shut down the grid)
curl -X POST -H "X-IEMS-Token: <token>" \
-H "Content-Type: application/json" \
-d '{"active": false}' \
http://127.0.0.1:28567/api/grid/active
💡 In Windows PowerShell, inline JSON gets its quotes mangled by argument passing; write the request body to a file and submit it with
--data-binary @body.jsoninstead.
Energy Exchange Rate Configuration
The exchange rate between SE (the grid's internal standard unit) and FE determines "how large an energy shortfall justifies calling on FE-tier devices", and can be tuned to the energy scale of your modpack.
config/DLZstudio/IEMS/Energy.toml:
# IEMS energy exchange rate configuration
[energy]
# How many FE 1 SE converts to (i.e. SE:FE = 1:N).
# Default 10; range [1, 900000000000000000000000000] (the upper bound is the old fixed value, so old behavior can be restored in one step).
# After editing, run /iems reload in-game for it to take effect immediately (no need to restart the game or leave the world).
# Note: the change reinterprets the existing SE values in the grid (the FE equivalent of 1 SE is redefined), so adjust with care.
fePerSe = 10
- Default: 1 SE = 10 FE: matched to the scale of mainstream FE devices, so FE-bridged devices can take part in grid throughput normally
- Lower bound 1 (1 SE = 1 FE), upper bound 9×10²⁶ (the old hard-coded default; set it to this value to restore old behavior)
- Out-of-range values are clamped to the bounds automatically, and non-numeric values fall back to the default
- Manual reload: after editing the file, run
/iems reloadin-game (permission level 2) for it to take effect immediately — no need to restart the game, and no need to leave the world - If the whole key is deleted while editing, the currently effective value is kept rather than falling back to the default (partial edits won't cause accidental damage)
/iems reload
One command reloads all IEMS config files (manually triggered, no automatic polling):
| Config file | Reload effect |
|---|---|
Energy.toml |
Re-parses the exchange rate and injects it into the converter, effective immediately |
APIserve.toml |
Rebuilds the web panel, applying enabled / host / port / token |
Renderer.toml |
Invalidates the client render config cache (effective on the local client's next read; remote clients on a dedicated server need to re-enter the world) |
/iems reloadrequires permission level 2 (same as/iems shutdownand/iems restart).
⚠️ The exchange rate is the definition of SE versus FE; changing it immediately reinterprets all existing SE values in the grid (core energy storage, device capacity, etc.). The SE ratios between devices stay the same and settlement remains conservative, but the FE equivalents presented externally scale overall — set it once according to your modpack's energy scale and avoid adjusting it frequently.
⚠️ When the exchange rate is set high, the real rate of FE-scale devices may convert to less than 1 SE/tick; the bridge layer already adapts its declaration granularity to the "amount delivered in one tick", so throughput will never be crushed down to
fePerSeFE/tick no matter how high or low the rate is.
Render Configuration
config/DLZstudio/IEMS/Renderer.toml:
# IEMS render configuration
[renderer]
colorizeAutoConnect = true # true: automatic bridge connections use cyan; false: same color as regular connections (yellow/red)
- Zero-IO lazy cache: config is read on demand and cached, and invalidated automatically when leaving the world
- After changing the config, re-entering the world applies it, with no game restart needed
Persistent Storage
Grid Data Saving
Storage location:
world save/data/iems_grid.dat
Persisted content:
- Device list (indexed by factory ID, including position/type/parameters)
- Core information
- All connections (including islanded connections and bridge connections)
Simulation-Style Rebuild (M9)
- Devices are persisted as a factory ID + parameters, rather than relying on the BlockEntity staying alive
- On server startup,
iems_grid.datis traversed: factory rebuild →restoreState→ grid recovery - The grid keeps simulating even when the chunk a device is in is not loaded (the BlockEntity is only the appearance/view)
- The chunk the core is in is force-loaded automatically when registered
Automatic Save Points
- When a connection is created / removed
- When the world is saved
- When the server stops
Data Safety
- Breaking a device unregisters it automatically and cleans up its connections
- Chunk unloading no longer unregisters devices (fix in BUILD.00000110:
setRemovedmistakenly unregistered the core, causing connections to be lost after a restart) - Connections are fully preserved after a save restart
Advanced Gameplay
Networking Tips
- Height advantage: build broadcast towers high up to extend effective coverage
- Relay backbone: use relays (500 blocks) for long-distance backbone links and broadcast towers (50 blocks) for end-point coverage
- FE device reuse: existing tech-mod devices are bridged automatically, with no need to disassemble or modify anything
- Island pre-wiring: devices not yet connected to the core can be interconnected first (a red laser indicates no power), and the whole network powers up once the core is in place
- Core siting: the core's chunk is force-loaded, so put it in the center of your main base to shorten the backbone
Energy Management Strategies
- Peak/valley balancing: rely on energy storage devices to discharge during peak usage
- Capacity planning: keep a 20% protocol capacity margin; exceeding the limit shuts down the whole grid
- Monitoring and alerts:
/api/metricsprovides time-series samples that can feed an external dashboard - Zoned power: give important devices their own core/subnet
Technical Details
Architecture: Full Logic-Presentation Separation
External mod (device provider)
│
└── Block / BlockEntity (registered with Minecraft, appearance/view only)
│
├── new the logic instance in the constructor/loadAdditional() (pure POJO, no Level needed)
├── hook up in onLoad() via IEMSAPI.attachOrRegisterDevice() (server side only)
└── persist parameters in saveAdditional()
│
▼
┌─────────────────────────────────────────────┐
│ IEMS logic layer (com.iems) │
│ │
│ CoreDevice (unique instance, force-loaded) │
│ Device pool DeviceRegistry (N instances) │
│ │
│ GridTopology (BFS/topology snapshot) │
│ EnergyDispatcher (per-tick energy allocation + frame-sliced scheduling) │
│ ProtocolCalculator (capacity calculation/over-limit detection) │
│ FEDA (FE device discovery and bridge adaptation) │
└─────────────────────────────────────────────┘
Design philosophy: instance autonomy — every device instance is fully independent, holds no references to other instances, and interacts with the grid only through the dispatcher; it supports independent new / destroy / parameter changes and naturally supports multi-core parallelism.
Module Breakdown
| Package | Responsibility |
|---|---|
com.iems.api |
Public facade (IEMSAPI / IIemsInteractable / SubSeAccumulator) |
com.iems.core |
Grid kernel (grid / node / dispatch / protocol) |
com.iems.eds |
Device Discovery System EDS (scanner + identity axis, discovery only, no bridging) |
com.iems.adapter |
Adapter layer (DA: FEDA disguises external FE devices as nodes and hooks them into the grid) |
com.iems.network |
C2S/S2C payloads (connection requests, full sync, shutdown sync) |
com.iems.webpanel |
Web panel (JDK HttpServer, port 28567) |
com.iems.example |
Example devices (relay/broadcast tower, depending only on the api facade) |
Threading Model
- All grid state is owned exclusively by the server tick thread
- External threads (web panel HTTP threads, etc.) submit tasks through
IEMSAPI.runOnServerThread()/callOnServerThread(), eliminating data races - The web panel thread pool is stopped manually on world reload to prevent thread leaks
Network Sync
GridSyncPayload— full topology sync (three lists for main grid/islands/bridges, with size validation to prevent OOM)ConnectionRequestPayload— C2S wire-pulling connection request (server re-validation + proximity check + rate limiting)- Shutdown status / protocol capacity sync along with snapshots; the HUD shows three states
Laser Rendering Details
- Billboard width direction: width = beam direction × (camera→beam), so side views don't vanish
- Screen-space minimum width:
max(0.03, distance × 0.0015), so distant beams don't thin out and disappear - Degenerate triangles separate adjacent strips, eliminating "ghost diagonals"
- Client cache (
ClientGridCache) handles the three lists (main grid + islands + bridges), with quadrant statistics and EMA smoothing
Device Discovery System
The Device Discovery System (EDS) answers one question: which external energy devices are nearby, and which energy system does each of them belong to?
Its boundary is very clear — it only discovers and classifies; it does not register, create connections, or convert. All bridging decisions are left to the downstream adapter layer.
Scan Entry Points
IEMSAPI.EDS.scan(level, center, radius) // synchronous one-shot scan (server main thread)
IEMSAPI.EDS.addListener(listener) // register a consumer (can be unregistered)
IEMSAPI.EDS.registerFlavor(flavor) // declare your own energy system
- Radius is in blocks, clamped to [1, 128] (
EDSScanner.MAX_RADIUS = 128) - Global entry points:
IEMSAPI.EDSis a static facade (equivalent to global functions); the scanner's listener table and identity table are also static - Only loaded chunks are enumerated, then clipped to a spherical range
- Already registered IEMS devices (including the core) are skipped, and counted in the report's
skippedIemsDevices - Executed synchronously, no async, no rate limiting — call frequency is up to the caller (the built-in adapter does it once every 20 ticks)
Single-Device Probing
Each block entity is probed by taking the union of 7 directional contexts and reading the NeoForge IEnergyStorage capability:
| Reading | Purpose |
|---|---|
canExtract |
Classified as a producer |
canReceive |
Classified as a consumer |
stored / capacity |
Stored amount and capacity snapshot |
| Exposed sides | Records from which side energy can be drawn (exposedSides; null means no directional context) |
Identity Axis: EnergyFlavor
The device's energy system is resolved from the namespace of its block ID:
| Field | Meaning |
|---|---|
namespace |
The mod's namespace (e.g. appliedenergistics2) |
unit |
That system's energy unit (determines the conversion basis for bridging) |
displayName |
Display name of the identity (e.g. Applied Energistics) |
- External mods declare their own system via
IEMSAPI.EDS.registerFlavor - Unregistered namespaces are treated as FE by default, using the namespace itself as the display name
Discovery Report: EDSReport
One scan produces an immutable report (EDS, v1) that is pushed to all listeners:
| Field | Description |
|---|---|
origin / radius / scanTick |
Scan origin, clamped radius, scan moment (level.getGameTime()) |
devices |
List of discovered devices (EDSDevice, stable order) |
chunksScanned |
Number of loaded chunks actually enumerated |
blockEntitiesProbed |
Total number of block entities probed (including those without a matching capability) |
skippedIemsDevices |
Number of registered IEMS devices skipped (including the core) |
Convenience subset methods: producers() / consumers() / storage() / byNamespace(ns).
Consumer Contract: IEDSListener
void onDiscovery(EDSReport report);
- The callback runs on the server main thread
- Mental model: the discovery layer only says "this is an FE device, that is an AE device"; whether, with whom, and how to bridge is entirely up to the listener
- Registering the same listener twice means it receives two copies of every report
⚠️
EDSReportis a snapshot at scan time, not live state. Do not rely on it to decide whether an existing device is alive — the discovery layer classifies registered positions asskippedIemsand skips them, so a rescan will inevitably not see them (for the correct approach, seesyncin the adapter layer).
Adapter Layer
The adapter layer (denoted DA; also called EDA when referring to the set of all DAs) is a pluggable execution layer: it registers external devices found by the discovery layer into the grid disguised as IEMS nodes, and performs bidirectional SE ↔ target-unit read/write conversion every tick.
There is one DA implementation per energy system, named "system name + DA" — the only implementation in this version is FEDA (the DA for the FE system); AE / EU and others are future extension points.
Why an Adapter Layer Is Needed
The dispatcher only recognizes the node interfaces and does not distinguish between "native SE devices" and "adapter-disguised nodes":
IEnergyProducer / IEnergyConsumer / StorageDevice
A DA is merely an executor that translates SE instructions into an external protocol — adding a new energy system means adding one DeviceAdapter implementation, with no kernel changes required.
The DeviceAdapter Interface
Every adapter is bound to one auto-connectable TransferDevice (such as an energy broadcast tower):
| Method | When called | Responsibility |
|---|---|---|
unit() |
— | The energy system unit it adapts to (FE / AE…) |
scan(level, center, radius) |
Periodic | Requests a discovery-layer scan and returns the device list this adapter cares about |
sync(level) |
Every 20 ticks | Diffs the report against registered nodes: register new ones, unregister gone ones |
tick(level) |
Every tick | Performs actual reads/writes on the external capabilities of its nodes (extract / measure remaining receive capacity / deliver) |
detach() |
Host invalidated | Unregisters all of its nodes |
⚠️
syncdecides existence by probing the block capability of existing devices directly, not relying on the scan report — because the discovery layer classifies registered positions asskippedIemsand skips them; if only the report were consulted, a rescan would mistake existing devices for gone ones.
Dispatcher: FeBridgeTicker
| Period | Action |
|---|---|
| Every tick | Iterates auto-connected relays → DeviceAdapter.tick; also sweeps orphan nodes |
| Every 20 ticks | DeviceAdapter.sync rescan (diff sync) |
Order within the main loop (IEMSEvents): FeBridgeTicker.tick / tickRescan → EnergyDispatcher.dispatchAtTick, i.e. sync external devices first, then perform this tick's energy allocation.
Disguised Nodes and Orphan Sweeping
Nodes registered into the grid by adapters all implement the marker interface IAdapterNode:
owner()— ownership chain: host relay → per-device disguised nodeisOrphaned()— whether the host has been unregistered (the position in the registry is no longer the original instance)
Two uses:
- Auto-connect exclusion —
IemsAutoConnectorskipsRELAY_TO_DEVICEautomatic connections for disguised nodes; their access goes throughADAPTER_BRIDGEonly, avoiding double-connection conflicts - Orphan sweeping — after the host relay is unregistered, the disguised node becomes a connectionless orphan, which
FeBridgeTickerdetects and unregisters every tick
Built-in Implementation: FEDA
FEDA is the only DeviceAdapter implementation in this version; it binds to auto-connect relays (broadcast towers) and registers by classifying external device capabilities:
| External capability | Disguised node type | Scenario |
|---|---|---|
| Can extract | FeProducerAdapter |
External generator → feeds power into the grid |
| Can receive | FeConsumerAdapter |
External consumer → draws power from the grid |
| Has capacity | FeBufferAdapter |
External storage → bidirectional throughput |
- Registers external devices as independent disguised nodes via
silentRegister - Establishes bridge connections via
IEMSAPI.addConnection(..., ConnectionType.ADAPTER_BRIDGE)(rendered as a cyan laser; coloring can be disabled withcolorizeAutoConnect) - On host invalidation (relay broken / unregistered) it calls
detach()and cleans up its nodes
Conversion Buffer: FeConversionBuffer
Maintains per-device extraction-side and push-side buffers for FE ↔ SE; it is the core state machine of energy conversion in the DA layer:
- Computes the SE demand declaration from the external receive headroom and
maxFePerTick - SE allocated by the grid is converted to FE at the current
fePerSeinto the push pool, then delivered to the external device - The demand declaration granularity adapts to the exchange rate: when the amount delivered in one tick is less than 1 SE, the declaration is rounded up, so raising the exchange rate does not choke throughput (see Energy Exchange Rate Configuration for details)
Developer API
Quick Start
IEMS is integrated into external mods as a Jar-in-Jar / libs dependency:
Gradle dependency (Groovy DSL):
dependencies {
// After copying the IEMS build artifact into libs/:
implementation files('libs/IEMS-0.8.0-beta-BUILD.00000120.jar')
}
neoforge.mods.toml dependency declaration:
[[dependencies.yourmod]]
modId="iems"
type="required"
versionRange="[0.8.0-beta,)" # ⚠️ The -beta suffix is mandatory; "[0.8.0,)" compares as unsatisfied and crashes on load
ordering="AFTER"
side="BOTH"
Device Integration: Three Logical Base Classes
An external mod's BlockEntity holds a logic instance (a pure POJO):
// 1. Register the factory during mod loading (key to M9 persistence rebuild)
IEMSAPI.registerDeviceFactory("mymod:my_device", MyDevice::new);
// 2. Hook up in BlockEntity.onLoad() (server side only)
GlobalPos pos = new GlobalPos(level.dimension(), blockPos);
MyDevice device = new MyDevice("My Device");
IEMSAPI.attachOrRegisterDevice(pos, device, "mymod:my_device");
// Devices with a factoryId are persisted to iems_grid.dat and rebuilt by the factory after a restart, even without chunk loading
// 3. Unregister in BlockEntity.setRemoved()
IEMSAPI.unregisterDevice(pos);
Core integration:
CoreDevice core = new CoreDevice("My Core",
BigInteger.valueOf(1000), // protocolLimit protocol capacity limit
BigInteger.valueOf(100000), // energyCapacity total energy capacity (SE)
BigInteger.ZERO); // powerGenRate self-generated energy per tick
IEMSAPI.attachOrRegisterCore(pos, core, "mymod:my_core");
// The chunk it is in is force-loaded automatically on registration; released on unregister
Wire-Pulling Contract: IIemsInteractable
A BlockEntity can take part in the framework's built-in Shift+right-click wire-pulling interaction simply by implementing this interface:
public class MyDeviceBlockEntity extends BlockEntity implements IIemsInteractable {
@Override
public boolean canBeConnectionEndpoint() {
return true;
}
@Override
public int getMaxConnectionDistance() {
return 500; // framework default of 500 blocks when not declared
}
}
- A purely client-side discovery mechanism (
instanceofdetection); the server does not rely on this interface for security decisions - The wire-pulling HUD, over-distance retraction, and dimension-change retraction are all handled by the framework
- Server-side connection creation goes through
ConnectionRequestPayloadre-validation (endpoints / same dimension / distance / duplicates / relay rules)
IEMSAPI Static Facade (Method Overview)
| Category | Method | Description |
|---|---|---|
| Thread safety | runOnServerThread(Runnable) |
Submit a task asynchronously from an external thread |
| Thread safety | callOnServerThread(Supplier, timeoutMs) |
Wait synchronously for a result from an external thread |
| Factory | registerDeviceFactory(id, factory) |
Register a device factory (M9 rebuild) |
| Device | attachOrRegisterDevice(pos, node, factoryId) |
Hook up a device (persisted if it has a factory) |
| Device | unregisterDevice(pos) |
Unregister a device |
| Core | attachOrRegisterCore(pos, core, factoryId) |
Hook up a core (chunk force-loaded) |
| Core | unregisterCore(pos) |
Unregister a core |
| Query | getCurrentEnergy() / getTotalCapacity() |
Energy pool (BigInteger) |
| Query | getProtocolUsed() / getProtocolTotal() |
Protocol capacity |
| Query | getSnapshot() |
Topology snapshot (main grid/islands/boundary) |
| Query | isDeviceConnected(pos) |
Whether the device is connected to the main grid |
| Connection | addConnection(a, b, type) / removeConnection(...) |
Connection management |
| Control | setGridActive(boolean) / forceRescan() |
Grid on/off / force rescan |
| Power | registerPowerInput(id, rate) / unregisterPowerInput(id) |
Power source registration |
| EDS | EDS.scan(level, center, radius) |
Nearby device scan (radius clamped to [1,128]) |
| EDS | EDS.addListener(l) / EDS.removeListener(l) |
Register/unregister a discovery listener |
| EDS | EDS.registerFlavor(flavor) |
Register an energy system identity (namespace → unit/display name) |
For the full design of the discovery and adapter layers (scan boundaries, report structure,
DeviceAdapterlifecycle, disguised nodes and orphan sweeping), see Device Discovery System and Adapter Layer.
Web Panel HTTP API (Complete)
Key points:
- All
/api/*endpoints require a token (X-IEMS-Tokenheader or?token=parameter) - Uniform envelope
{schema, ts, data}, with BigIntegers stringified - Error codes:
400(parameter/JSON error),401(unauthorized),404(route not found),405(method not allowed),409(state conflict such as no core),500(internal error)
Reference Implementation: IEMS-TestDevices
The test mod IEMS-TestDevices (modid iemstest) demonstrates every integration pattern:
| Device | What it demonstrates |
|---|---|
test_core test core |
CoreDevice integration + protocol capacity declaration |
auto_relay / manual_relay |
Automatic/manual relay devices (TransferDevice) |
fe_battery |
FE storage block (automatically bridged by FEDA) |
arc_furnace arc furnace |
A complete example of a power-consuming device with GUI/Menu |
Building and Development
BUILDID Build System
This mod is built using the DLZ Studio BUILDID convention; the only legal build entry points are:
.DLZstudio/buildid/
├── BUILDID.txt # Iteration counter (8-digit zero-padded)
├── build.ps1 # Windows build entry point
└── build.sh # Unix/Linux/macOS build entry point
Windows:
# ⚠️ Set JAVA_HOME first (the script environment does not inherit it)
$env:JAVA_HOME="D:\Java\jdk-21"
$env:Path="D:\Java\jdk-21\bin;"+$env:Path
powershell -ExecutionPolicy Bypass -File .\.DLZstudio\buildid\build.ps1
Unix:
./.DLZstudio/buildid/build.sh
Release mode (version number synced to neoforge.mods.toml):
build.ps1 -Release/build.sh --release
Artifact naming: IEMS-0.8.0-beta-BUILD.00000120.jar (normal) / iems-1.21.1-neoforge-0.8.0-beta.120.jar (release, version number written back to neoforge.mods.toml)
Testing
.\gradlew test # webpanel + client unit tests (including RendererConfigTest, etc.)
.\gradlew compileJava # quick compile check
Environment Requirements
- JDK 21
- NeoForge 21.1.x (dependency range
[21.1.0,)) - The test runtime classpath has been synced to a pure ASCII directory (to work around GBK decoding issues with Chinese paths)
Requirements
Required Dependencies
| Mod | Version | Description |
|---|---|---|
| NeoForge | 21.1.0+ | Mod loader (dependency range [21.1.0,)) |
| Minecraft | 1.21.1 | — |
Bundled Components
| Component | Description |
|---|---|
| GeckoLib 4.8.4 | Bundled via Jar-in-Jar (META-INF/jarjar/); players do not need to install it separately |
Recommended Companions
| Mod | Description |
|---|---|
| IEMS-TestDevices | Official test mod: test core/relay/FE battery/arc furnace (the recommended reference implementation) |
| Any FE tech mod | Devices are discovered and bridged automatically by FEDA |
| ⚠️ No longer maintained: only compatible with the old IEMS and has not kept up with the new API |
⚠️ ZCSMSS is discontinued: it no longer adapts to updated versions of IEMS, and therefore does not support the new IEMS. On the new version, use the official test mod IEMS-TestDevices instead, or provide a core yourself following the Developer API.
Installation Steps
- Install NeoForge 21.1+
- Download the IEMS JAR and put it in the
modsfolder - (Optional) Add a mod that provides a core (the official test mod IEMS-TestDevices is recommended)
- Launch the game
FAQ
Q: Why is there no core block?
A: IEMS is an energy management framework; the core is provided by external mods to avoid duplicated functionality. You can install the official test mod IEMS-TestDevices.
Q: Does ZCSMSS still work?
A: Not recommended. ZCSMSS is no longer maintained and is only compatible with the old IEMS without keeping up with the new API — under the new IEMS it is no longer supported. Use the official test mod IEMS-TestDevices instead, or provide a core yourself following the Developer API.
Q: How do I know whether the core is working?
A: Use /iems status to check grid status, or visit the web panel at http://127.0.0.1:28567/ (token required).
Q: Connections are lost after restarting the save?
A: This was fixed in BUILD.00000110 (chunk unloading mistakenly unregistered the core). Grid data is stored in iems_grid.dat and fully preserved across restarts; if losses still occur, make sure BUILD ≥ 110.
Q: The lasers turned red?
A: Two situations — the grid is shut down (protocol capacity exceeded or manual /iems shutdown), or the connection is islanded (not connected to the core; always red and never powered).
Q: What happens if I teleport away halfway through pulling a wire?
A: A dimension change (portal/cross-dimension TP) retracts the wire immediately with a notification; leaving the world also clears the wire-pulling state.
Q: How do FE devices from other mods get connected?
A: No action needed — FEDA automatically scans and bridges FE devices in range every 20 ticks (cyan laser).
Q: I don't like the cyan bridge laser?
A: Set colorizeAutoConnect = false in config/DLZstudio/IEMS/Renderer.toml and re-enter the world for it to take effect.
Q: I want to adjust the SE-to-FE exchange rate?
A: Change fePerSe in config/DLZstudio/IEMS/Energy.toml (default 10, range 1 ~ 9×10²⁶), then run /iems reload in-game for it to take effect immediately, with no restart needed. Note that changing the rate immediately reinterprets the existing SE values in the grid.
Q: The web panel is unreachable?
A: Check: whether the game has a world loaded, whether enabled is true in APIserve.toml, whether port 28567 is occupied, and the firewall. Remote access requires changing host = "0.0.0.0" and must be accompanied by a new token and a TLS reverse proxy.
Q: How should an external mod write its version range when depending on IEMS?
A: "[0.8.0-beta,)". You must not write "[0.8.0,)" — in semantic comparison 0.8.0-beta < 0.8.0, so the required dependency would be judged unsatisfied and the game would crash on load.
Q: How do I develop my own device?
A: See the Developer API section of this document and refer to the IEMS-TestDevices source (the arc furnace is a complete example with a GUI).
Debug Commands
| Command | Permission | Description |
|---|---|---|
/iems status |
none | Grid status (core/energy/main grid/islands/connections/shutdown reason) |
/iems protocol |
none | Protocol capacity usage and over-limit state |
/iems scan |
none | Force a BFS rescan and report the result |
/iems shutdown |
2 | Manually shut down the grid (device add/remove does not auto-recover) |
/iems restart |
2 | Restore power |
/iems reload |
2 | Reload all IEMS config files (Energy / APIserve / Renderer) |
Changelog
Improvements over the Old IEMS (new 0.8.0-beta / IEMS0930 ← old v0.6.0 ~ v0.8.0-BUILD.00000160)
The new version is a complete rewrite of the old IEMS: from a monolithic implementation where "the block entity is the grid" to an energy management framework with "full logic-presentation separation + factory-based persistence". The main improvements and behavior differences are listed below by dimension (current build BUILD.00000120).
1. Architecture: Full Separation of Logic and Presentation
| Dimension | Old | New |
|---|---|---|
| Where grid logic lives | Written inside BlockEntity, tightly coupled to Level |
A separate logic layer (pure POJO) that does not depend on Level |
| Device identity | Hard-coded type enum | Factory ID + parameters, registrable by external mods |
| Relations between devices | Hold references to each other | Instance autonomy, interacting with the grid only through the dispatcher |
| Grid operation | Stops when chunks are unloaded | Keeps simulating even without chunk loading |
- ✅ External mods only need to
newa logic instance +IEMSAPI.attachOrRegisterDevice(), without extending framework base classes - ✅ Instances can be independently created / destroyed / re-parameterized, naturally suited to multi-core parallelism
- ✅ The device factory mechanism makes "devices that exist even without their block" possible
2. Persistence: From "connections only" to a "complete grid snapshot"
| Dimension | Old | New |
|---|---|---|
| Storage file | data/DLZstudio/IEMS/iems_connections.dat |
data/iems_grid.dat |
| Stored content | Connections only (coordinates + type enum) | Devices (factory ID + parameters) + core + all connections |
| Rebuild method | Relies on BEs re-registering one by one, order-sensitive | Rebuilt by factories on server startup → restoreState |
| Chunk unload | Unload means unregister + clean up connections | Unloading does not unregister devices; connections are fully preserved after a restart |
| Coverage | Main grid connections only | Full coverage of main grid + islands + bridges |
- ✅ Fixes the old order-sensitivity defect where "a device loads before the core →
onLoad()registration has no effect → the grid is forever empty" - ✅ Fixes "chunk unload mistakenly unregisters the core → core connections are lost after a restart" (BUILD.00000110)
3. Energy System: From "hard-coded conversion of three units" to "unified SE basis + configurable exchange rate"
| Dimension | Old | New |
|---|---|---|
| Unit system | SE / FE / GE (EnergyValue.EnergyUnit) |
FE / AE / SE, with SE as the internal settlement basis |
| Unit conversion | Hard-coded (1 SE = 1 FE, 1 SE = 10⁸ GE) | Configurable rate: fePerSe in Energy.toml (default 10) |
| Conversion carrier | Requires placing an extra "energy converter" block | FE devices are converted automatically by the bridge layer, no converter needed |
| Settlement tick | SE/2 ticks (once every 2 ticks) | SE/tick (settled every tick) |
| Legacy unit | GE threaded through the whole codebase | GE removed (along with its display-mode API) |
- ✅
fePerSesupports hot reload (/iems reload), clamps out-of-range values, and falls back to the default for non-numeric values - ✅ FE bridge declaration granularity adapts to the "amount delivered in one tick"; under a high exchange rate throughput is no longer crushed down to
fePerSeFE/tick
4. External Device Integration: From "requiring adaptation by the other side" to "zero-adaptation automatic bridging"
- Old: devices from other mods had to implement the IEMS API or extend framework blocks to join the grid
- New: responsibilities are split across two layers — the Device Discovery System (EDS) handles scanning and identity recognition (discovery only, no bridging), while the adapter layer (EDA) uses
FEDAto disguise each device as a node; it rescans any NeoForge FE device in range every 20 ticks (generators / consumers / storage) and establishes a bridge connection automatically on discovery (cyan laser, configurable), with zero changes required in the other mod - ✅ This finally unlocked the external FE device interconnection that had never worked before (since BUILD.00000091)
5. Web Panel: From "unauthenticated read-only page on 8080" to "token-authenticated REST API"
| Dimension | Old | New |
|---|---|---|
| Port / binding | 8080, local page only | 28567, default 127.0.0.1, remote access can be explicitly enabled |
| Authentication | None | All /api/* require a token (header or query parameter) |
| Form | Fixed page (status + device toggles) | REST API: status / devices / connections / topology / metrics / grid/active |
| Data format | Page reads memory directly | Uniform envelope {schema, ts, data}, BigIntegers stringified to prevent precision loss |
| Time-series data | None | Metrics ring buffer, can feed an external dashboard |
| Write operations | Device power toggles | POST /api/grid/active to toggle the grid |
- ✅ Thread safety: HTTP thread tasks are queued onto the tick thread, eliminating data races with
CoreDevice - ✅ The thread pool is explicitly stopped on world reload, and the metrics buffer is cleared per session, avoiding thread leaks and cross-save interference
6. Grid Topology: New "main grid / island / bridge" tri-state
- Old: anything not connected to the core was judged invalid (broadcast towers not connected to the core simply refused connections)
- New: devices not yet connected to the core are allowed to interconnect first (islands), rendered with a red laser to indicate no power, and powered automatically once the core is in place
- ✅ BFS reachability determination + topology snapshot (aggregated main grid / island / boundary views)
- ✅ Core↔device connections turn red rather than disappearing when the device leaves the main grid (core-side exemption filtering)
7. Connection Interaction: From "simple two-point lines" to a "wire-pulling contract with re-validation"
| Dimension | Old | New |
|---|---|---|
| Interaction detection | instanceof on concrete block classes |
The IIemsInteractable contract (decoupled from block type) |
| Distance feedback | Top text prompt, silently cancelled when too far | HUD live distance + maximum distance, automatic retraction when too far (+10 buffer) |
| Server validation | Weak (trusted the client) | C2S request + server re-validation (endpoints / same dimension / distance / duplicates / relay rules) |
| Security | None | Proximity check + rate limiting (prevents remote arbitrary connections via forged packets) |
| Leftover state | Could linger across dimension changes / world exit | Wire retracted immediately on dimension change, forcibly cleared on world exit |
8. Laser Rendering: Triple Robustness for Viewing Angle, Distance, and Topology
- ✅ Billboard width direction (width = beam direction × (camera→beam)) — side views are no longer compressed into a thin line
- ✅ Screen-space minimum width
max(0.03, distance × 0.0015)— distant beams do not thin out and vanish - ✅ Back-face culling disabled for double-sided rendering, visible from any angle; depth testing retained (still occluded by blocks)
- ✅ Degenerate triangles inserted between adjacent strips to eliminate "ghost diagonals"
- ✅ New
colorizeAutoConnectinRenderer.toml: the cyan bridge laser can be toggled off - ✅ The
ClientGridCacheclient cache handles all three lists (main grid / islands / bridges), with quadrant statistics and EMA smoothing
9. Concurrency Model: Explicit "tick thread ownership"
- Old: public APIs read/write grid state directly, with web threads and the game thread accessing concurrently
- New: grid state is owned exclusively by the server tick thread; external threads all submit through
IEMSAPI.runOnServerThread()/callOnServerThread() - ✅
GridSyncPayloadsize validation to prevent oversized packets from causing client OOM
10. Commands and Operations
| Old | New |
|---|---|
/iems_debug add_connection |
/iems status grid overview |
/iems_debug list_connections |
/iems protocol protocol capacity |
/iems_debug clear_connections |
/iems scan force a BFS rescan |
| — | /iems shutdown / /iems restart grid on/off (permission 2) |
| — | /iems reload hot reload all config (permission 2) |
11. Build and Dependencies
- ✅ Adopted the DLZstudio BUILDID convention: the only legal build entry points are
build.ps1/build.sh, with the build number embedded in the artifact (the old version used a hand-writtenbuild_v3.bat) - ✅ GeckoLib 4.8.4 is bundled via Jar-in-Jar, so players do not need to install it separately (the old version listed it as a required external dependency)
- ✅ The NeoForge dependency range was relaxed from a fixed
21.1.228+to[21.1.0,) - ✅ Config is centralized in
config/DLZstudio/IEMS/(Energy / APIserve / Renderer), auto-generated with comments on first launch
12. Framework Boundary Changes and Behavior Differences (Migration Reference)
The following are behavior changes, not all improvements; be aware of them when upgrading.
- Block consolidation: the old version shipped relays / broadcast towers / standard storage / general storage / converters / marker blocks; the new framework keeps only the example devices (relay / broadcast tower), leaving cores and storage to external mods (e.g. IEMS-TestDevices; ZCSMSS is no longer maintained and does not follow the new IEMS)
- Core connection distance: fixed at 1500 blocks in the old version → declared by the core at registration in the new version, with a framework default of 500 blocks when not declared
- Protocol capacity: defaulted to 1000 in the old version, increased/decreased by the device side via
requestProtocolCapacity/releaseProtocolCapacity→ declared by the core asprotocolLimitin the new version, with the framework performing unified calculation and over-limit detection - Line-of-sight occlusion check: old broadcast towers had a "cannot connect if blocked by ≥3 layers of full blocks" rule → the new version uses pure distance determination
- Time display mode API: old
standard/coordinated→ removed in the new version, unified as SE/tick - API shape: old
IEMSApi.getInstance()instance facade +EnergyValuewrapper → new static facadeIEMSAPI+ rawBigInteger
IEMS0930 (2026-09-30, BUILD.00000114)
Development codename IEMS0930 adopted; this entry corresponds to the latest build of the 0.8.0-beta rewrite mainline.
Fixes (connection persistence):
- ✅
setRemovedmistakenly unregistering the core device on chunk unload, causing core connections to be lost after a restart — the unload path now marks and skips unregistration, while a block that truly disappears is still unregistered normally - ✅ Core-side exemption for island filtering: after disconnecting from the core, the connection is rendered as a red laser instead of simply disappearing (aligned with old semantics)
Fixes (laser rendering):
- ✅ Degenerate triangles separate adjacent strips, eliminating "ghost diagonals"
- ✅ A series of rendering robustness fixes: billboard width direction (doesn't vanish from the side), screen-space minimum width (doesn't thin out at a distance), double-sided rendering (prevents a whole beam from disappearing at certain angles), and defense against abnormal cross-dimension connections
Fixes (lighting):
- ✅ Example devices let skylight pass straight through (
propagatesSkylightDownoverride + documented mechanism), so newly placed devices no longer appear as "completely black models"
New (render config):
- ✅
colorizeAutoConnecttoggle inRenderer.toml: the cyan FE bridge laser can be turned off - ✅ Zero-IO lazy cache for render config, effective on re-entering the world
New (energy exchange rate config):
- ✅
fePerSerate entry inEnergy.toml(default 1 SE = 10 FE, range 1 ~ 9×10²⁶) - ✅
/iems reloadcommand: manually reload all config (Energy / APIserve / Renderer) with no game restart needed - ✅ FE bridge demand declaration adapts to the "amount delivered in one tick"; under a low exchange rate throughput is no longer crushed down to
fePerSeFE/tick - ✅ Removed the legacy GE unit from the old version (zero references in the project; the new unit system is FE / AE / SE)
Fixes (WebPanel / thread safety):
- ✅ Data race between HTTP threads and the tick thread over
CoreDevice— external thread tasks are now queued (H-01) - ✅ Thread pool leak on world reload (M-01), metrics ring buffer cross-session interference (M-02)
- ✅ Proximity check + rate limiting for C2S connection requests (H-02),
GridSyncPayloadsize validation to prevent client OOM (M-03) - ✅ No more bridges created when FEDA silently skips registration under overlapping multi-host radii (M-04)
- ✅ Leftover wire-pulling state across dimensions / on world exit (M-06)
0.8.0-beta Rewrite Mainline (2026-08 ~ 2026-09)
- M1-M4 Grid kernel: device registration, BFS topology, protocol capacity, energy dispatch
- M5-M7 Network sync and laser rendering: full snapshot sync, island/bridge rendering, quadrant statistics
- M8/M8.1 Wire-pulling interaction: the
IIemsInteractablecontract, C2S requests, and server re-validation - M9 simulation-style device factory persistence; keeps running after a restart without chunk loading
- M10 WebPanel JDK HttpServer panel (port 28567, token authentication, metrics ring buffer)
- M11 review fixes minimized fixes for the H/M/L series of potential issue reports + render config
- External FE bridging (FEDA) working for the first time: IEMS and NeoForge FE devices successfully interconnected (since BUILD.00000091)
Acknowledgments
- The GeckoLib team — an excellent 3D model rendering library (distributed via Jar-in-Jar)
- The NeoForge team — a powerful mod loader
- 北山_Besson — provided some device models
- [EI] 终末地工业 (Endfield Industries) — source of reference for some models
License
This mod is open source; contributions and derivative works are welcome!
The bundled GeckoLib 4.8.4 is likewise distributed under the MIT License; its notice can be found in the LICENSE file inside the nested JAR.
等离子工作室 (DLZstudio) © 2026 · Development codename IEMS0930


