Skip to content

3.3 Preparing the Fabric Project Template

Zhoumy303 edited this page Oct 3, 2026 · 3 revisions

This task transforms the official Fabric example mod into a parameterized template that supports generating projects targeting Minecraft 26.2 or later. Key changes include: removing Yarn mappings, using the non-remapping Loom plugin, upgrading to Java 25, Gradle 9.5.1, replacing modImplementation with implementation, and removing all Mixin and client entrypoints (the current MVP only generates items, so these are not needed).

Note: This task only prepares the template. It does not generate item code, so the template does not call ModItemsGenerated, nor does it verify game execution. Full compilation and runtime verification will be done after Tasks 6, 7, and 8 are complete.


Version Baseline

Item Value
Minecraft 26.2
Fabric Loader 0.19.5
Loom 1.17-SNAPSHOT
Fabric API 0.161.0+26.2
Gradle 9.5.1
Java 25

Step 1: Obtain the Fabric example mod

cd ~/modsmith
git clone https://github.com/FabricMC/fabric-example-mod.git \
    modsmith/templates/fabric-project
rm -rf modsmith/templates/fabric-project/.git

Verify:

ls modsmith/templates/fabric-project/

You should see build.gradle, gradle.properties, src/, etc.


Step 2: Clean up unneeded directories and files

Since the current ModSmith MVP only generates items, Mixin and client entrypoints are not needed. Delete the following:

# Delete the client source directory
rm -rf modsmith/templates/fabric-project/src/client

# Delete all Mixin config files
rm -f modsmith/templates/fabric-project/src/main/resources/*.mixins.json

# Delete the client entrypoint class (if present)
rm -f modsmith/templates/fabric-project/src/main/java/com/example/client/ExampleModClient.java

# Delete the empty client package directory
rm -rf modsmith/templates/fabric-project/src/main/java/com/example/client

Step 3: Parameterize gradle.properties

Open modsmith/templates/fabric-project/gradle.properties and replace with:

# Gradle
org.gradle.jvmargs=-Xmx2G
org.gradle.parallel=true

# Fabric Properties
minecraft_version={{ minecraft_version }}
loader_version={{ fabric_loader_version }}
loom_version={{ loom_version }}

# Mod Properties
mod_version={{ mod_version }}
maven_group={{ package_name }}
archives_base_name={{ mod_id }}

# Dependencies
fabric_version={{ fabric_api_version }}

Notes:

  • yarn_mappings has been removed because 26.2 uses Mojang official mappings.
  • All version numbers use Jinja2 placeholders, filled by the render_project context.

Step 4: Replace build.gradle

Open modsmith/templates/fabric-project/build.gradle and replace the entire file with:

plugins {
    id 'net.fabricmc.fabric-loom' version "${loom_version}"
    id 'maven-publish'
}

version = project.mod_version
group = project.maven_group

base {
    archivesName = project.archives_base_name
}

repositories {
    // Add a domestic mirror here if needed
}

dependencies {
    minecraft "com.mojang:minecraft:${project.minecraft_version}"
    // 26.2+ no longer requires the mappings line

    implementation "net.fabricmc:fabric-loader:${project.loader_version}"
    implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
}

processResources {
    inputs.property "version", project.version
    filesMatching("fabric.mod.json") {
        expand "version": project.version
    }
}

tasks.withType(JavaCompile).configureEach {
    it.options.encoding = "UTF-8"
    it.options.release = 25
}

java {
    withSourcesJar()
    sourceCompatibility = JavaVersion.VERSION_25
    targetCompatibility = JavaVersion.VERSION_25
}

jar {
    from("LICENSE") {
        rename { "${it}_${project.base.archivesName.get()}" }
    }
}

Key changes:

  • Plugin ID: net.fabricmc.fabric-loom (non-remapping version).
  • Removed the mappings line.
  • modImplementation → implementation.
  • Java version upgraded to 25.

Step 5: Update the Gradle Wrapper

Open modsmith/templates/fabric-project/gradle/wrapper/gradle-wrapper.properties and change distributionUrl to:

distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip

Step 6: Parameterize fabric.mod.json

Open modsmith/templates/fabric-project/src/main/resources/fabric.mod.json and replace the entire file with:

{
  "schemaVersion": 1,
  "id": "{{ mod_id }}",
  "version": "{{ mod_version }}",
  "name": "{{ mod_name }}",
  "description": "{{ mod_description }}",
  "authors": [
    "{{ author }}"
  ],
  "contact": {},
  "license": "MIT",
  "icon": "assets/{{ mod_id }}/icon.png",
  "environment": "*",
  "entrypoints": {
    "main": [
      "{{ package_name }}.ExampleMod"
    ]
  },
  "depends": {
    "fabricloader": ">={{ fabric_loader_version }}",
    "minecraft": "~{{ minecraft_version }}",
    "java": ">=25",
    "fabric-api": "*"
  }
}

Key changes:

  • Removed the client entrypoint: the current MVP does not generate client classes.
  • Removed the mixins field: the current MVP does not need Mixin.
  • java requires >=25.
  • minecraft uses ~{{ minecraft_version }}.

Step 7: Parameterize the main entrypoint class (do not call ModItemsGenerated)

Open modsmith/templates/fabric-project/src/main/java/com/example/ExampleMod.java and replace with:

package {{ package_name }};

import net.fabricmc.api.ModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExampleMod implements ModInitializer {
    public static final String MOD_ID = "{{ mod_id }}";
    public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);

    @Override
    public void onInitialize() {
        LOGGER.info("Hello Fabric world from {{ mod_name }}!");
        // Note: ModItemsGenerated will be generated in a later task; do not call it here for now
        // ModItemsGenerated.initialize();
    }
}

Key changes:

  • ModItemsGenerated.initialize() is commented out because the class has not been generated yet.
  • After Task 7 is complete, uncomment it or let the generator add it automatically.

Step 8: Check and remove residual Mixin configs

Confirm that there are no Mixin-related files left in the template:

find modsmith/templates/fabric-project -name "*.mixins.json"

There should be no output. If there are any, delete them.

Also check whether fabric.mod.json still contains a mixins field:

grep -i "mixin" modsmith/templates/fabric-project/src/main/resources/fabric.mod.json

There should be no output.


Step 9: Create the Template Rendering Script

Once the template files are ready, you need to write a Python script to render them. Create modsmith/generator/project.py:

"""根据蓝图渲染 Fabric 项目模板。"""

import shutil
from pathlib import Path

from jinja2 import Environment, FileSystemLoader

TEMPLATE_DIR = Path(__file__).parent.parent / "templates" / "fabric-project"


def render_project(blueprint: dict, output_dir: Path) -> None:
    """将蓝图渲染到输出目录。

    Args:
        blueprint: 蓝图字典。
        output_dir: 输出项目目录。
    """
    # 准备 Jinja2 环境
    env = Environment(
        loader=FileSystemLoader(str(TEMPLATE_DIR)),
        keep_trailing_newline=True,
    )

    # 准备渲染上下文
    context = {
        "mod_id": blueprint["mod_id"],
        "mod_name": blueprint.get("mod_name", blueprint["mod_id"]),
        "mod_description": blueprint.get("description", "A ModSmith generated mod"),
        "mod_version": blueprint.get("version", "0.1.0"),
        "package_name": blueprint["package_name"],
        "package_path": blueprint["package_name"].replace(".", "/"),
        "minecraft_version": blueprint["minecraft_version"],
        "fabric_loader_version": blueprint["fabric_loader_version"],
        "yarn_mappings": blueprint.get("yarn_mappings", "1.21.4+build.1"),
        "loom_version": blueprint.get("loom_version", "1.10-SNAPSHOT"),
        "fabric_api_version": blueprint.get("fabric_api_version", "0.119.2+1.21.4"),
        "author": blueprint.get("author", "ModSmith User"),
    }

    # 如果输出目录已存在,先删除
    if output_dir.exists():
        shutil.rmtree(output_dir)

    # 遍历模板目录,渲染每个文件
    for template_path in TEMPLATE_DIR.rglob("*"):
        if template_path.is_dir():
            continue

        # 计算相对路径
        rel_path = template_path.relative_to(TEMPLATE_DIR)
        rel_str = str(rel_path)

        # 渲染文件路径(处理 {{ mod_id }} 等)
        rendered_rel_str = env.from_string(rel_str).render(**context)
        rendered_rel_path = Path(rendered_rel_str)

        # 处理包名路径替换
        if "src/main/java/com/example" in rendered_rel_str:
            rendered_rel_str = rendered_rel_str.replace(
                "src/main/java/com/example",
                f"src/main/java/{context['package_path']}"
            )
            rendered_rel_path = Path(rendered_rel_str)

        output_path = output_dir / rendered_rel_path
        output_path.parent.mkdir(parents=True, exist_ok=True)

        # 渲染文件内容
        content = template_path.read_text(encoding="utf-8")
        rendered_content = env.from_string(content).render(**context)

        output_path.write_text(rendered_content, encoding="utf-8")

    print(f"✅ 项目已生成到: {output_dir}")   

Step 10: Verify template rendering (compile-only, do not run the game)

Create or reuse test_template.py:

from pathlib import Path
from modsmith.generator.project import render_project

blueprint = {
    "mod_id": "test-mod",
    "mod_name": "Test Mod",
    "package_name": "com.testmod",
    "minecraft_version": "26.2",
    "fabric_loader_version": "0.19.5",
    "fabric_api_version": "0.161.0+26.2",
    "loom_version": "1.17-SNAPSHOT",
}

render_project(blueprint, Path("./test_output"))

Run:

python test_template.py

Verify the generated output:

# Check that version numbers were replaced correctly
cat test_output/gradle.properties

# Check that fabric.mod.json has no mixins and no client
cat test_output/src/main/resources/fabric.mod.json

# Check that there are no .mixins.json files
find test_output -name "*.mixins.json"

Compile verification:

cd test_output
./gradlew build

Expected: compilation passes (because ModItemsGenerated is commented out, no error).

Note: Do not run ./gradlew runClient at this point, because the project has no item registration code and lacks resource files; running the game is meaningless. Game runtime verification will be done after Tasks 6, 7, and 8 are complete.


Common Issues

Problem Cause Solution
ClassNotFoundException: ExampleModClient fabric.mod.json still declares the client entrypoint Remove the client entrypoint
MixinInitialisationError fabric.mod.json still declares the mixins field Remove the mixins field and delete all .mixins.json files
Cannot find symbol: ModItemsGenerated ExampleMod.java did not comment out the call Comment out ModItemsGenerated.initialize()
gradle.properties version not replaced Template has hardcoded values instead of placeholders Confirm the template uses {{ minecraft_version }} etc.
Minecraft downloaded again Cache cleared or version changed Keep ~/.gradle/caches/fabric-loom and keep versions consistent

Task 3 Acceptance Criteria

  • Template cloned and .git removed.
  • src/client/ directory and all .mixins.json files deleted.
  • gradle.properties version variables parameterized, no yarn_mappings.
  • build.gradle uses the net.fabricmc.fabric-loom plugin, no mappings line, implementation instead of modImplementation, Java 25.
  • gradle-wrapper.properties uses Gradle 9.5.1.
  • fabric.mod.json has no mixins field, no client entrypoint, java >=25, minecraft ~26.2.
  • ExampleMod.java package name and MOD_ID parameterized, ModItemsGenerated.initialize() commented out.
  • project.py's render_project() correctly renders the template and adds executable permission to gradlew.
  • test_output generated by test_template.py compiles via ./gradlew build (without running the game).

After completing these steps, your template can generate Fabric projects targeting Minecraft 26.2+, and will not fail to compile due to missing classes generated in later tasks. Next, you can proceed to Task 7 to complete the Java code generator.

Clone this wiki locally