SDK
ECHO Native Mod Author Guide
my-addon/ build.gradle src/main/java/.../MyAddon.java src/main/resources/META-INF/echo.mod.json src/main/resources/data/myaddon/... src/test/java/.../MyAddonTest.java
ECHO Native Mod Author Guide
Getting Started
- Clone or download the ECHO Native SDK.
- Apply the
echo-sdk-gradle-pluginto your addon project. - Pick a template matching your addon type.
- Write your addon descriptor, service registrations, and content.
- Validate with
./gradlew checkand package with./gradlew packageEchoNativeAddon.
Project Structure
my-addon/
build.gradle
src/main/java/.../MyAddon.java
src/main/resources/META-INF/echo.mod.json
src/main/resources/data/myaddon/...
src/test/java/.../MyAddonTest.java
Descriptor
Every addon needs echo.mod.json:
{
"schema": "echo.mod.v1",
"id": "myaddon",
"name": "My Addon",
"version": "1.0.0-RC1",
"entrypoint": "com.example.myaddon.MyAddon",
"side": "common",
"provides": ["myaddon.registry"],
"access": {
"nativeClasspath": ["addon.jar"]
},
"apiStability": "beta"
}
| Field | Required | Description |
|---|---|---|
| id | Yes | Unique modid. Lowercase, no spaces. |
| name | Yes | Human-readable name. |
| version | Yes | Semver string. |
| entrypoint | Yes | Class implementing EchoNativeModuleEntrypoint. |
| side | Yes | common, client, or server. |
| provides | No | Feature IDs this addon provides. |
| access.nativeClasspath | Yes | Release-mode classpath entries, usually addon.jar. |
Service Registration
Use EchoNativeModuleLoadContext to register addon services, then call typed host services for runtime mutations. Do not mint MUTATED receipts yourself; record the receipt returned by the host service.
public class MyAddon implements EchoNativeModuleEntrypoint {
private static final String SERVICE_ID = "myaddon:registry_service";
@Override
public void registerServices(EchoNativeModuleLoadContext context) {
context.registerService(SERVICE_ID, new MyRegistryService(), "registry");
}
@Override
public void registerContent(EchoNativeModuleLoadContext context) {
EchoNativeServiceMutation mutation = EchoNativeServiceMutation.of(
"myaddon", "registry", "declare_content", "myaddon:example", EchoNativeRuntimeSide.COMMON);
context.serviceRegistry()
.service("echo.native.registry", EchoNativeRegistryService.class)
.map(registry -> registry.register(mutation))
.ifPresent(context::recordMutation);
}
}
Keep registrations idempotent and safe to call multiple times during reloads.
Optional Integration
Never hard-reference optional addons. Use service lookup:
Optional<IndexService> index = EchoOptionalServices.index();
index.ifPresent(i -> i.registerProvider(myDocsProvider));
Build & Package
# Compile and run tests
./gradlew build
# Produce .echo-addon distribution
./gradlew packageEchoNativeAddon
Output lands in build/echo-native/addons/<id>-<version>.echo-addon.
Testing
Use the echo-native-testkit dependency for in-memory loader tests:
testImplementation 'dev.echo.native:echo-native-testkit:1.0.0-RC1'
@Test
public void testBootstrap() {
EchoNativeSdkTestkit.Environment env = EchoNativeSdkTestkit.common("myaddon");
EchoNativeModuleLoadContext context = env.loadEntrypoint(new MyAddon());
assertTrue(context.serviceRegistry().hasService("myaddon:registry_service"));
env.goldenParity().requireOnlyTypedReceipts();
}
Publishing
See Release Packaging Guide for artifact naming, checksums, and metadata requirements.