-
Notifications
You must be signed in to change notification settings - Fork 0
3.6 Implementing the Java Code Generator
The goal of this task is to generate complete, compilable Java registration code for each item in the blueprint, supporting the basic, food, fuel, and tool types.
The code has been corrected according to the Minecraft 26.1+ API changes, including:
- Tool classes now uniformly use
Item.Propertiesmethods such as.sword()and.axe(), instead of dedicated classes likeSwordItem. -
ApplyStatusEffectsConsumeEffecthas been moved to thenet.minecraft.world.item.consume_effectspackage. - Static field declaration order adjusted to avoid "illegal forward reference".
- Fabric API package paths updated (
CreativeModeTabEvents, etc.). - Fuel registration uses
FuelValueEvents.
- Completed Task 5:
modsmith/generator/java.pyand the skeleton templates are ready. - Understood the blueprint structure: the
itemsarray, where each item has fields likeid,type,display_name_en, etc. - The project already uses Minecraft 26.1+ dependencies and Java 25.
package {{ package_name }};
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.Identifier;
import net.minecraft.world.item.Item;
public class ModItemIds {
{% for item in items %}
public static final ResourceKey<Item> {{ item.id | upper }} = create("{{ item.id }}");
{% endfor %}
private static ResourceKey<Item> create(String name) {
return ResourceKey.create(Registries.ITEM, Identifier.fromNamespaceAndPath("{{ mod_id }}", name));
}
}package {{ package_name }};
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.component.Consumable;
import net.minecraft.world.item.component.Consumables;
import net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect;
import net.minecraft.world.item.ToolMaterial;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.ItemTags;
import java.util.List;
import java.util.function.Function;
public class ModItems {
{% for item in items %}
{% if item.type == "food" %}
public static final FoodProperties {{ item.id | upper }}_FOOD = new FoodProperties.Builder()
.nutrition({{ item.nutrition }})
.saturationModifier({{ item.saturation }}f)
{% if item.always_edible %}
.alwaysEdible()
{% endif %}
.build();
public static final Consumable {{ item.id | upper }}_CONSUMABLE = Consumables.defaultFood()
{% if item.effects %}
.onConsume(new ApplyStatusEffectsConsumeEffect(List.of(
{% for effect in item.effects %}
new MobEffectInstance(MobEffects.{{ effect.effect | upper }}, {{ effect.duration_ticks }}, {{ effect.amplifier }}){% if not loop.last %},{% endif %}
{% endfor %}
), 1.0f))
{% endif %}
.build();
{% endif %}
{% if item.type == "tool" %}
public static final ToolMaterial {{ item.id | upper }}_MATERIAL = new ToolMaterial(
BlockTags.INCORRECT_FOR_IRON_TOOL,
{{ item.durability }},
{{ item.mining_speed }}f,
{{ item.attack_damage }}f,
22,
ItemTags.IRON_TOOL_MATERIALS
);
{% endif %}
{% endfor %}
{% for item in items %}
public static final Item {{ item.id | upper }} = register(
ModItemIds.{{ item.id | upper }},
{% if item.type == "basic" %}
Item::new,
new Item.Properties()
{% elif item.type == "food" %}
Item::new,
new Item.Properties().food({{ item.id | upper }}_FOOD, {{ item.id | upper }}_CONSUMABLE)
{% elif item.type == "fuel" %}
Item::new,
new Item.Properties()
{% elif item.type == "tool" %}
{% if item.tool_type == "sword" %}
Item::new,
new Item.Properties().sword({{ item.id | upper }}_MATERIAL, {{ item.attack_damage }}f, 1f)
{% elif item.tool_type == "axe" %}
Item::new,
new Item.Properties().axe({{ item.id | upper }}_MATERIAL, {{ item.attack_damage }}f, -3.0f)
{% elif item.tool_type == "pickaxe" %}
Item::new,
new Item.Properties().pickaxe({{ item.id | upper }}_MATERIAL, {{ item.attack_damage }}f, -2.8f)
{% elif item.tool_type == "shovel" %}
Item::new,
new Item.Properties().shovel({{ item.id | upper }}_MATERIAL, {{ item.attack_damage }}f, -3.0f)
{% elif item.tool_type == "hoe" %}
Item::new,
new Item.Properties().hoe({{ item.id | upper }}_MATERIAL, {{ item.attack_damage }}f, -2.0f)
{% endif %}
{% endif %}
);
{% endfor %}
public static Item register(ResourceKey<Item> itemKey, Function<Item.Properties, Item> itemFactory, Item.Properties settings) {
Item item = itemFactory.apply(settings.setId(itemKey));
return Registry.register(BuiltInRegistries.ITEM, itemKey, item);
}
public static void initialize() {
}
}package {{ package_name }};
import net.fabricmc.fabric.api.creativetab.v1.CreativeModeTabEvents;
import net.fabricmc.fabric.api.registry.FuelValueEvents;
import net.minecraft.world.item.CreativeModeTabs;
public class ModItemsGenerated {
public static void initialize() {
// Trigger ModItems class loading
ModItems.initialize();
{% if items %}
// Add items to the creative mode item group
CreativeModeTabEvents.modifyOutputEvent(CreativeModeTabs.INGREDIENTS)
.register(entries -> {
{% for item in items %}
entries.accept(ModItems.{{ item.id | upper }});
{% endfor %}
});
{% endif %}
}
}In modsmith/templates/fabric-project/src/main/java/com/example/ExampleMod.java, ensure onInitialize() calls ModItemsGenerated.initialize():
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 }}!");
ModItemsGenerated.initialize();
}
}"""Generate Java code related to item registration."""
from pathlib import Path
from jinja2 import Environment, FileSystemLoader
TEMPLATE_DIR = Path(__file__).parent.parent / "templates" / "java"
def generate_java_items(blueprint: dict, project_dir: Path) -> None:
"""Generate Java code for the items in the blueprint.
Args:
blueprint: Blueprint dictionary.
project_dir: Root directory of the generated project.
"""
env = Environment(
loader=FileSystemLoader(str(TEMPLATE_DIR)),
keep_trailing_newline=True,
)
context = {
"package_name": blueprint["package_name"],
"items": blueprint["items"],
"mod_id": blueprint["mod_id"],
}
package_path = blueprint["package_name"].replace(".", "/")
java_dir = project_dir / "src" / "main" / "java" / package_path
java_dir.mkdir(parents=True, exist_ok=True)
files_to_generate = {
"ModItemIds.java.jinja": "ModItemIds.java",
"ModItems.java.jinja": "ModItems.java",
"ModItemsGenerated.java.jinja": "ModItemsGenerated.java",
}
for template_name, output_name in files_to_generate.items():
template = env.get_template(template_name)
content = template.render(**context)
(java_dir / output_name).write_text(content, encoding="utf-8")Create or reuse test_generator.py:
"""Test the project generator, including full Java code generation."""
from pathlib import Path
from modsmith.blueprint.validator import generate_validated_blueprint
from modsmith.generator.project import generate_project
# Use a blueprint containing multiple types for testing
blueprint = {
"mod_id": "example-mod",
"package_name": "com.example",
"minecraft_version": "26.2",
"fabric_loader_version": "0.19.5",
"items": [
{
"id": "ruby",
"type": "basic",
"display_name_en": "Ruby",
"display_name_zh": "红宝石",
"texture": "auto"
},
{
"id": "healing_apple",
"type": "food",
"display_name_en": "Healing Apple",
"display_name_zh": "治愈苹果",
"nutrition": 4,
"saturation": 0.3,
"always_edible": True,
"effects": [
{"effect": "regeneration", "duration_ticks": 200, "amplifier": 0}
],
"texture": "auto"
},
{
"id": "quark_gluon_plasma",
"type": "fuel",
"display_name_en": "Quark-Gluon Plasma",
"display_name_zh": "夸克-胶子等离子体",
"burn_time": 2400,
"texture": "auto"
},
{
"id": "guidite_sword",
"type": "tool",
"display_name_en": "Guidite Sword",
"display_name_zh": "基迪特剑",
"tool_type": "sword",
"durability": 455,
"mining_speed": 5.0,
"attack_damage": 1.5,
"texture": "auto"
}
]
}
output_dir = Path("./generated_project")
generate_project(blueprint, output_dir)Run:
python test_generator.pyExpected result: Under generated_project/src/main/java/com/example/:
-
ModItemIds.java: 4ResourceKeyconstants. -
ModItems.java: 4 item registrations; food hasFoodPropertiesandConsumable; tool hasToolMaterial; all tools use the correspondingItem.Propertiesmethods. -
ModItemsGenerated.java: creative tab registration and fuel registration.
cd generated_project
./gradlew buildIf compilation passes, the Java code generator is working correctly. If there are still errors, adjust the imports or method calls in the templates according to the error messages.
-
ModItemIds.java.jinjagenerates aResourceKeyfor each item. -
ModItems.java.jinjasupports the four types and generates correct, compilable registration code. -
ModItemsGenerated.java.jinjagenerates creative tab registration and fuel registration. -
ExampleMod.javacallsModItemsGenerated.initialize(). -
java.pycan render all templates and write them to the correct paths. - The
generated_projectproduced bytest_generator.pycompiles via./gradlew build.
After completing this task, your ModSmith will be able to generate fully functional Java item code. Next, you can proceed to Task 7 (refine the resource file generator), Task 8 (texture generator), and Task 9 (compile verification module).