Search is available after the production docs build.

Browse Docs
DocsDevelopersGetting Started

Developers

Getting Started

Set up the workspace, build core modules, and create a small ECHO-compatible module.

Purpose

Start here when you want to build an ECHO module or addon against the public SDK instead of copying internal implementation details from another module.

The current public developer path is concrete:

  • Use ECHO-SDK for schemas, templates, validation tooling, Gradle task names, and public dev.echo.api.* samples.
  • Use ECHO-Modules for first-party module source and META-INF/echo.mod.json descriptors.
  • Use ECHO-Release-Index for public release metadata, asset families, checksums, and launcher download truth.
  • Use ECHO Launcher as the normal player delivery path.

ECHO still supports the Minecraft/NeoForge lane while Native and Standalone lanes mature. Keep runtime-specific code behind AdapterCore or generated adapters, and keep public addon code on the SDK contracts.

Before You Build

Read these pages first:

You do not need to understand every module before writing a first addon. You do need to know which layer owns your feature, which runtime targets it supports, and which contract IDs it provides or consumes.

Install And Validate The SDK

Run SDK validation from ECHO-SDK:

python tools/echo_sdk.py validate templates --json
python tools/validate_echo_contracts.py --json

For a Gradle addon project, apply the SDK plugin and use the current task names:

plugins {
    id 'dev.echo.native.echo-sdk-gradle-plugin' version '1.0.0-RC'
}

dependencies {
    implementation 'dev.echo.native:echoaddonapi:1.0.0-RC'
    implementation 'dev.echo.native:echo-native-contracts:1.0.0-RC'
    testImplementation 'dev.echo.native:echo-native-testkit:1.0.0-RC'
}

Useful SDK tasks:

  • echoInitAddon
  • echoValidateManifest
  • echoValidateSchemas
  • echoValidateImports
  • echoValidatePermissions
  • echoValidateRuntimeTargets
  • echoValidate
  • echoGenerateAdapters
  • echoPackageNativeAddon
  • echoPackageNeoForge
  • echoPackageStandalone
  • echoParityTest
  • echoBuildReleaseBundle

Workspace To First Addon

Use this flow for the first pass:

  1. Validate ECHO-SDK templates and contracts.
  2. Pick a target from Native Addon Guide, NeoForge Module Guide, or Standalone Module Guide.
  3. Create an addon from a template or mirror the sample structure in ECHO-SDK/samples/hello-content-addon.
  4. Define META-INF/echo.mod.json.
  5. Define META-INF/echo-addon-package.json when producing .echo-addon packages.
  6. Implement the addon through dev.echo.api.* and JDK imports.
  7. Register content, services, events, or optional integrations through SDK and module contracts.
  8. Run echoValidate and focused tests.
  9. Package the selected runtime families.
  10. Publish release metadata through the Release Index path when the artifact is player-facing.

Current Addon Shape

The SDK sample addon uses dev.echo.api.* public types and a registry context. This is the public shape developers should copy before reaching for first-party internals.

package dev.echo.samples.hello;

import dev.echo.api.addon.EchoAddon;
import dev.echo.api.addon.EchoAddonDescriptor;
import dev.echo.api.addon.EchoAddonId;
import dev.echo.api.addon.EchoAddonKind;
import dev.echo.api.addon.EchoAddonRole;
import dev.echo.api.addon.EchoAddonRuntimeTarget;
import dev.echo.api.addon.EchoAddonVersion;
import dev.echo.api.context.EchoRegistryContext;
import java.util.Set;

public final class HelloContentAddon implements EchoAddon {
    private static final EchoAddonDescriptor DESCRIPTOR = new EchoAddonDescriptor(
            new EchoAddonId("hello_content_addon"),
            new EchoAddonVersion("0.1.0"),
            "Hello Content Add-on",
            EchoAddonKind.CONTENT,
            EchoAddonRole.SAMPLE,
            Set.of(
                    EchoAddonRuntimeTarget.ECHO_NATIVE,
                    EchoAddonRuntimeTarget.NEOFORGE,
                    EchoAddonRuntimeTarget.ECHO_RUNTIME_STANDALONE
            )
    );

    @Override
    public EchoAddonDescriptor descriptor() {
        return DESCRIPTOR;
    }

    @Override
    public void register(EchoRegistryContext context) {
        // Register items, blocks, recipes, services, or providers through SDK contexts.
    }
}

The matching module manifest is META-INF/echo.mod.json:

{
  "schema": "echo.mod.v1",
  "id": "hello_content_addon",
  "name": "Hello Content Add-on",
  "version": "0.1.0",
  "kind": "content",
  "role": "sample",
  "runtimeTargets": ["echo_native", "neoforge", "echo_runtime_standalone"],
  "domains": ["items", "blocks", "recipes"],
  "permissions": ["registry.items", "registry.blocks", "registry.recipes"],
  "entrypoint": "dev.echo.samples.hello.HelloContentAddon"
}

Good First Addons

A good first addon does one thing clearly:

  • Adds registry content through EchoRegistryContext.
  • Publishes data-driven Index, Lens, Terminal, or HoloMap resources.
  • Registers one service contract through EchoNativeAddonRuntime.
  • Adds a small NetCore action with explicit server validation.
  • Adds PackOS-aware metadata for an existing module or pack.

Avoid starting with an addon that owns networking, persistent saves, custom UI, and world generation at the same time. That hides the contract boundaries you are trying to learn.

Implementation Checklist

  • META-INF/echo.mod.json exists and uses schema: "echo.mod.v1".
  • The addon ID is lowercase and stable.
  • runtimeTargets match the artifacts you actually build.
  • permissions match the registries, networking, data, or UI surfaces the addon uses.
  • Public sample code imports dev.echo.api.* and JDK types only.
  • Optional integrations use service lookup or no-op behavior.
  • Runtime-specific code is isolated in AdapterCore, generated adapters, or runtime entrypoints.
  • Tests cover the service or registry behavior before release.
  • Docs and release notes are present before the addon is public.

Common Mistakes

  • Copying com.knoxhack.* internals into third-party SDK samples.
  • Declaring echo_native, neoforge, and echo_runtime_standalone when only one artifact builds.
  • Publishing a .jar without META-INF/echo.mod.json.
  • Hard-requiring an optional module instead of using EchoOptionalServices.
  • Treating GitHub release assets as the normal player install path instead of routing players through ECHO Launcher.

Next Step

Create the module descriptor first, then choose one integration surface. If the addon is mostly content or discovery, start with Index or Lens. If it is mission or route related, start with Terminal and MissionCore through service contracts.