Skip to content

Development Guide

This guide covers everything you need to set up a development environment, build the mod, run tests, and work with the multi-version build system.

See also: Contributing for the contribution workflow, Guidelines for code style and conventions.


Prerequisites

Requirement Version Notes
Java 17 Required — Minecraft 1.20.1 targets Java 17 (Mojang moved to Java 17 as the minimum starting with 1.18), and the project uses Java 17 language features (records, sealed classes)
Git 2.x+ For version control and conventional commits
IDE Any IntelliJ IDEA recommended (free Community edition works)
git-cliff Latest Changelog generation for releases — winget install git-cliff or git-cliff.org

Note: You do not need to install Gradle — the project includes the Gradle Wrapper (gradlew / gradlew.bat).


Getting Started

Clone and Build

# Clone the repository
git clone https://gitlab.com/starshadow/games/minecraft/minecraft-ansible-crafting.git
cd minecraft-ansible-crafting

# Build the mod (includes git SHA in version for dev builds)
# Linux/macOS
./gradlew build

# Windows
gradlew.bat build

Run the Development Client

# Linux/macOS
./gradlew runClient

# Windows
gradlew.bat runClient

This launches a Minecraft client with the mod loaded. Hot-swap is supported for minor code changes if your IDE is configured for it.

Run the Development Server

# Linux/macOS
./gradlew runServer

# Windows
gradlew.bat runServer

Build Types

flowchart LR
    DEV["./gradlew build"] -->|"includes git SHA"| DEV_JAR["ansiblecrafting-0.1.0+abc1234.jar"]
    REL["./gradlew build -Prelease"] -->|"clean version"| REL_JAR["ansiblecrafting-0.1.0.jar"]
Build Type Command Version Format Use Case
Development ./gradlew build 0.1.0+abc1234 Local testing, CI builds
Release ./gradlew build -Prelease 0.1.0 Distribution, mod platforms

Gradle Task Reference

Task Description Example
build Compile and create the mod JAR ./gradlew build
test Run JUnit 5 unit tests ./gradlew test
spotlessCheck Check code formatting (fails on violations) ./gradlew spotlessCheck
spotlessApply Auto-fix code formatting ./gradlew spotlessApply
runClient Launch Minecraft client with the mod ./gradlew runClient
runServer Launch Minecraft server with the mod ./gradlew runServer
clean Delete all build artifacts ./gradlew clean
buildAllVersions Build for all Stonecutter-configured MC versions ./gradlew buildAllVersions
# Format code, then build and test in one command
./gradlew spotlessApply build --no-configuration-cache

The --no-configuration-cache flag is needed because Stonecutter and configuration caching can conflict.

The project includes git hooks in .githooks/ that automatically enforce formatting and commit message conventions:

Hook What It Does
pre-commit Runs spotlessCheck on staged Java files — blocks commits with formatting violations
commit-msg Validates that commit messages follow Conventional Commits format

To enable the hooks:

# From the ansible-crafting/ directory
git config core.hooksPath .githooks

To disable them later:

git config --unset core.hooksPath

Note: The hooks are opt-in. CI will still catch formatting and commit message issues even without local hooks.


Code Formatting

The project uses Spotless with Palantir Java Format for consistent code style.

Key rules: - Tabs for Java indentation, 4 spaces for Gradle - 120-character line length - Import order: java.*javax.*net.minecraft.*net.fabricmc.*com.ansiblecrafting.* → everything else

# Check formatting (CI will fail if this fails)
./gradlew spotlessCheck

# Auto-fix formatting
./gradlew spotlessApply

Always run spotlessApply before committing. The CI pipeline runs spotlessCheck and will reject improperly formatted code.


Testing

The project uses JUnit 5 for unit tests. Tests live in src/test/java/ and mirror the main source structure.

Running Tests

# Run all tests
./gradlew test

# Run a specific test class
./gradlew test --tests "com.ansiblecrafting.config.ModConfigTest"

# Run with verbose output
./gradlew test --info

Test Structure

src/test/java/com/ansiblecrafting/
├── config/
│   └── ModConfigTest.java          # 46 tests — config loading, validation, defaults
├── crafting/
│   └── CraftingSessionManagerTest.java  # 12 tests — session lifecycle
└── inventory/
    └── ...                          # Inventory scanning tests

Writing Tests

Follow the AAA pattern (Arrange, Act, Assert) and descriptive naming:

@Test
@DisplayName("scanRange clamps to minimum of 1 when set to 0")
void scanRange_setToZero_clampsToOne() {
    // Arrange
    ModConfig config = new ModConfig();

    // Act
    config.setScanRange(0);

    // Assert
    assertEquals(1, config.getScanRange());
}

See Guidelines — Testing for full conventions.


Multi-Version Support (Stonecutter)

The project uses Stonecutter to support multiple Minecraft versions from a single codebase.

How It Works

flowchart TD
    SRC["src/main/java/<br/>Shared source code"] --> SC["Stonecutter<br/>Preprocessor"]
    SC -->|"MC 1.20.1"| V1["versions/1.20.1/<br/>Version-specific overrides"]
    SC -->|"MC 1.20.2+"| V2["versions/1.20.2/<br/>Version-specific overrides"]
    V1 --> JAR1["ansiblecrafting-*-mc1.20.1.jar"]
    V2 --> JAR2["ansiblecrafting-*-mc1.20.2.jar"]

Conditional Compilation

Use Stonecutter comment markers for version-specific code:

/*? if >=1.20.2 {*/
// Code for Minecraft 1.20.2 and above
/*?} else {*/
// Code for Minecraft 1.20.1 and below
/*?}*/

Version-Specific Resources

Version-specific resource overrides go in versions/<mc-version>/src/main/resources/. These override files in the main src/main/resources/ directory.

Building All Versions

./gradlew buildAllVersions

This produces JARs for every configured Minecraft version.


Project Structure

ansible-crafting/
├── src/main/java/com/ansiblecrafting/
│   ├── AnsibleCraftingMod.java        # Main mod entry point
│   ├── client/ui/                      # UI components (panels, widgets)
│   ├── config/                         # ModConfig, Cloth Config integration
│   ├── crafting/                       # CraftingSession, CraftingSessionManager
│   ├── integration/                    # Recipe viewer integrations
│   │   ├── emi/                        # EMI plugin
│   │   └── rei/                        # REI plugin
│   ├── inventory/                      # InventoryScanner, AggregatedInventory
│   ├── mixin/                          # Server-side mixins
│   │   └── client/                     # Client-side mixins
│   └── network/                        # Client-server packet definitions
├── src/main/resources/
│   ├── assets/ansiblecrafting/         # Textures, lang files
│   ├── fabric.mod.json                 # Fabric mod metadata
│   └── ansiblecrafting.mixins.json     # Mixin configuration
├── src/test/java/com/ansiblecrafting/  # Unit tests
├── versions/                           # Stonecutter version overrides
├── scripts/                            # Release scripts, splash taglines
├── docs/                               # Technical documentation
├── build.gradle                        # Build configuration
├── gradle.properties                   # Mod metadata and dependency versions
├── stonecutter.gradle                  # Multi-version build config
└── cliff.toml                          # git-cliff changelog config

For a deeper dive into the architecture, see Architecture Overview.


Recipe Viewer Development

The mod integrates with both EMI and REI. You can switch which recipe viewer loads in the dev environment by editing gradle.properties:

# Which recipe viewer to load in dev environment (emi, rei, or none)
recipe_viewer = emi

Set to emi, rei, or none and re-run ./gradlew runClient.


Debug Logging

For detailed runtime logging during development, see the Debug Logging Guide.