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