Repository navigation
Java Integration
Add the MMD Maven repository and the Ley Lines dependency to your NeoForge 1.21.1 build. Gradle fetches the published JAR on demand.
repositories {
maven {
url = 'https://maven.moddev.zone/releases/'
content { includeModule 'zone.moddev.mc.skysleylines', 'SkysLeyLines' }
}
}
configurations {
leyLinesMod {
canBeConsumed = false
canBeResolved = true
transitive = false
}
compileOnly.extendsFrom(leyLinesMod)
}
dependencies {
leyLinesMod 'zone.moddev.mc.skysleylines:SkysLeyLines:0.5.6.121012'
}For NeoGradle, stage the resolved JAR in each development run’s mods directory so NeoForge discovers it as a mod. This example assumes run/client and run/server are the run working directories. Adjust the destination to match your project. NeoGradle run configuration
['client', 'server'].each { runName ->
def suffix = runName.capitalize()
def stage = tasks.register("stageLeyLinesFor$suffix", Copy) {
from configurations.leyLinesMod
into layout.projectDirectory.dir("run/$runName/mods")
rename { 'skysleylines-dev.jar' }
}
tasks.configureEach {
if (name == "run$suffix" || name == "ideBeforeRun$suffix") dependsOn stage
}
}Install Sky’s Ley Lines alongside your mod in a regular game instance. For an optional integration, check ModList.get().isLoaded("skysleylines") before loading any class that imports its API. Declare a version range in META-INF/neoforge.mods.toml that covers the API versions you tested. Replace yourmod below with your mod ID. Use type="required" if your mod relies on Ley Lines to function.
[[dependencies.yourmod]]
modId="skysleylines"
type="optional"
versionRange="[0.5.6.121012]"
ordering="AFTER"
side="BOTH"Choose an entry point based on the job.
| Job | Entry point | When to call it |
|---|---|---|
| Read natural strength for ores, structures, or rituals |
LeyLinesApi.forLevel(level).field() and LeyFieldSampler.sampleColumn(x, z)
|
Acquire the sampler on the server thread during level load. Its retained sampler supports worker reads. |
| Find nexus sites in a structure callback |
LeyLinesApi.worldgenSampler(randomState) and LeyFieldSampler.nexusesInChunk(chunkX, chunkZ)
|
Query on the generation worker. See the worldgen guide. |
| Register a paradigm and currencies | LeyLinesApi.paradigms().register(...) |
Common setup via enqueueWork, before registration freezes at load complete. |
| Inspect balances or pay for a machine action |
NexusPowerService.quoteAt, convertAt, spendAt, or returnToRawAt
|
On the owning server thread. Use the position where the action happens. |
| Read saved rates and active flags | LeyLinesApi.currencyInfo(level) |
On the server thread. |
| Give players personal pools or move energy over time | LeyLinesApi.playerPower(server) |
On the server thread. See player energy. |
| Show Ley Vision to a player |
LeyVision.grant or revoke
|
On the connected player’s server thread. Register custom styles during mod setup. |
| Enforce protection rules or observe a completed transaction |
NexusTransactionEvent.Pre or .Post
|
Listen on NeoForge.EVENT_BUS. Include the player UUID in the request when one is responsible. |
Put this class in your mod, change the package and examplemagic IDs to your own, then call SurveyPower.register(modBus) in your mod constructor. This class is for a required dependency. For an optional integration, put the class behind the loader check described above.
package com.example.magic;
import java.util.List;
import java.util.Optional;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import zone.moddev.mc.skysleylines.api.LeyLinesApi;
import zone.moddev.mc.skysleylines.api.LeyVision;
import zone.moddev.mc.skysleylines.api.MagicAmount;
import zone.moddev.mc.skysleylines.api.Paradigm;
import zone.moddev.mc.skysleylines.api.PowerRequest;
import zone.moddev.mc.skysleylines.api.PowerResult;
public final class SurveyPower {
private static final ResourceLocation AETHER = ResourceLocation.parse("examplemagic:aether");
private static final ResourceLocation CONVERTER = ResourceLocation.parse("examplemagic:converter");
private static final ResourceLocation SURVEY = ResourceLocation.parse("examplemagic:survey");
public static void register(IEventBus modBus) {
modBus.addListener(SurveyPower::commonSetup);
}
private static void commonSetup(FMLCommonSetupEvent event) {
event.enqueueWork(() -> LeyLinesApi.paradigms().register(new Paradigm<>(
ResourceLocation.parse("examplemagic:survey_magic"), null,
List.of(new Paradigm.Currency(AETHER, () -> MagicAmount.of(1000))))));
}
public static PowerResult convert(ServerLevel level, BlockPos machine, ServerPlayer player) {
var consumer = new PowerRequest.Consumer(CONVERTER, Optional.of(player.getUUID()));
return LeyLinesApi.forLevel(level).power().convertAt(
machine.getX(), machine.getY(), machine.getZ(), AETHER, MagicAmount.of(5), consumer);
}
public static PowerResult castSurvey(ServerPlayer player) {
var pos = player.blockPosition();
var consumer = new PowerRequest.Consumer(SURVEY, Optional.of(player.getUUID()));
var result = LeyLinesApi.forLevel(player.serverLevel()).power().spendAt(
pos.getX(), pos.getY(), pos.getZ(), AETHER, 1000, consumer);
if (result.succeeded()) {
LeyVision.grant(player, SURVEY, 20 * 60);
}
return result;
}
private SurveyPower() {}
}Call the methods from server-side game events after checking the player's access and cooldown. Call convert with the actual machine location and its interacting player in that level. At the default saved rate it adds 5,000 aether. castSurvey spends 1,000 at the player’s feet and grants one minute of vision at 20 ticks per second. For the two actions to share the balance, both positions must resolve to the same nexus. An automated machine can use the one-argument PowerRequest.Consumer constructor when there is no player actor.
Use result.status() to tell the player what happened. For a converter, read the units credited from receipt.after().currency(AETHER) - receipt.before().currency(AETHER). This follows the saved rate in existing worlds. Use quoteAt to display current funds, then check the transaction result again when the player actually acts.
The full Technomancy Demo has block and item registration, recipes, a Creative tab, two currency definitions, and timed lamps. Its converters and lamps illustrate the same calls inside real block interactions.
For an optional ore feature, acquire the sampler in a server-side LevelEvent.Load handler before spawn preparation. Publish it in a concurrent map keyed by ServerLevel and remove the entry on LevelEvent.Unload. A worker can retrieve that sampler and reuse one column for several candidate heights. Keep the account and transaction service on the server thread. Decide how your feature behaves if Ley Lines is absent, the field is inactive, or strength is below your threshold. The optional worldgen probe shows how the loader check and sampler lifecycle fit together.
See the public API reference for complete signatures, result codes, thread rules, and examples. Pack scripts use the KubeJS guide.