Skip to main content

Building & Contributing

This page is for working on Nebulon itself. Consumers only need Getting Started.


Environment

  • JDK 21 and the Gradle wrapper (./gradlew); no system Gradle needed.
  • ./gradlew runClient starts the dev client with Iris and Sodium pinned via modLocalRuntime (versions in gradle.properties). They are compatibility-test dependencies only and are never published as dependencies of Nebulon.
  • The dev run already sets -Dnebulon.showcase=true via loom.runs.client in build.gradle. Loom wipes hand-added IDE VM arguments on every Gradle sync, so keep dev flags in build.gradle.

Select the compatibility runtime explicitly per test pass:

./gradlew runClient -Pnebulon.compat=vanilla
./gradlew runClient -Pnebulon.compat=sodium
./gradlew runClient -Pnebulon.compat=iris

./gradlew compatibilityInfo -Pnebulon.compat=<mode> prints the selected mode without launching.

Manual test matrix for rendering changes

ConfigurationWhat must hold
Vanilla (no pack)Effects visible, occluded by blocks, hand drawn over effects
Iris installed, pack offSame as vanilla
Iris pack activeEffects visible after the pack's composite; block and hand occlusion intact

To test the shader-pack path, drop a pack zip into run/shaderpacks/ and enable it in the Iris screen. MakeUp-UltraFast is the validation pack. For fog changes, also walk into the showcase volume to exercise the inside-volume (exit-face, no depth test) path.


Architecture rules

  • Server safety: nothing under api/network, api/render (excluding client), or network may touch client-only classes. That separation lets dedicated servers sync effects they never render.
  • Iris isolation: all Iris access goes through the reflection-only client/compat/IrisBridge and degrades to vanilla behavior when anything is missing. No compile-time Iris import outside that class.
  • The deferred END_MAIN window must not move. With a pack active, Nebulon replays the stage inside GameRenderer.renderWorld immediately after WorldRenderer.render returns — after the pack's composite, before the hand depth clear. Earlier, Iris intercepts unmapped passes; later, world depth reads 1.0 and occlusion breaks.
  • GPU resources are created lazily on the render thread and released via EffectBatchRenderer.close(); the same mixin tears everything down when GameRenderer closes.

Building and releasing

./gradlew build   # remapped jar + sources jar in build/libs/

CI runs on Forgejo Actions (.forgejo/workflows/build.yml): every push and PR builds and uploads jars as artifacts. Direct pushes to main and tags starting with v additionally publish de.nexusrealms:nebulon to https://maven.riftrealms.de/releases.

Version format: <base>+mc<minecraft>.<Berlin yyyyMMdd>.<global build number>, e.g. 0.1.0-beta+mc1.21.11.20260715.123. Local builds use build number 0; override with -Pnebulon.buildDate=yyyyMMdd / -Pnebulon.buildNumber=N. ./gradlew versionInfo prints the coordinate (quote dotted -P arguments in PowerShell).

Release procedure:

  1. Bump the base mod_version in gradle.properties only when the API/release line changes.
  2. Commit and push to main for an automatic dated Maven build; tag milestones with v*.
  3. Maven credentials live as protected Forgejo secrets MAVEN_USERNAME and MAVEN_PASSWORD.

Workstation publishing:

MAVEN_USERNAME=<username> MAVEN_PASSWORD=<password> ./gradlew publish