Skip to content

Advancement Data Generation

Maksym Uimanov edited this page Jan 30, 2026 · 1 revision

Advancement Data Generation

The @GenerateAdvancement annotation enables automated advancement data generation during the gradle runData phase. This annotation eliminates the need for manual JSON file creation by allowing developers to define advancement logic through Java classes that implement the AdvancementDescription interface.

Responsibilities / Purpose

The @GenerateAdvancement annotation serves to:

  • Automate advancement file generation - Create JSON advancement files from Java class definitions
  • Type-safe advancement creation - Provide compile-time validation for advancement properties
  • Integrate with localization - Work seamlessly with the framework's translation system
  • Support custom strategies - Allow for specialized advancement generation logic
  • Simplify complex advancement trees - Manage parent-child relationships through code

How it works

The annotation system operates during data generation by scanning classes annotated with @GenerateAdvancement. Each annotated class must implement AdvancementDescription and provide advancement configuration through method implementations. The framework then generates corresponding JSON files in the correct resource locations.

Core Architecture

  • Type-level annotation - Applied to classes implementing AdvancementDescription
  • Strategy pattern support - Optional custom strategy for specialized generation
  • Interface-based configuration - All advancement properties defined through method implementations
  • Automatic registration - Classes are automatically discovered and processed during data generation

How to use / Configure

Basic Advancement Generation

@GenerateAdvancement
public class ExampleAdvancement implements AdvancementDescription {
    @TranslateAmericanEnglish("Example Advancement")
    private static final String TITLE_ID = "advancement.example.example.title";
    @TranslateAmericanEnglish("Example Description for Example Advancement")
    private static final String DESCRIPTION_ID = "advancement.example.example.description";

    @Override
    public String getId() {
        return "example:example";
    }

    @Override
    public String getParentRoot() {
        return "minecraft:story/root";
    }

    @Override
    public ItemLike icon() {
        return ExampleItems.EXAMPLE_ITEM;
    }

    @Override
    public AdvancementType getType() {
        return AdvancementType.TASK;
    }

    @Override
    public boolean showToast() {
        return true;
    }

    @Override
    public boolean isAnnouncedInChat() {
        return true;
    }

    @Override
    public boolean isHidden() {
        return false;
    }

    @Override
    public int getExperience() {
        return 10;
    }

    @Override
    public Map<String, Criterion<?>> getCriterions() {
        return Map.of("has_example_item", 
            InventoryChangeTrigger.TriggerInstance.hasItems(ExampleItems.EXAMPLE_ITEM));
    }

    @Override
    public AdvancementRequirements getRequirements(Set<String> criterions) {
        return AdvancementRequirements.allOf(criterions);
    }
}

Custom Strategy Implementation

@GenerateAdvancement(CustomAdvancementStrategy.class)
public class CustomAdvancement implements AdvancementDescription {
    // Implementation methods...
}

public class CustomAdvancementStrategy implements AdvancementStrategy {
    @Override
    public void generateAdvancement(AdvancementDescription advancement, 
                                   HolderLookup.Provider provider, 
                                   Consumer<AdvancementHolder> consumer) {
        Advancement.Builder builder = createBuilder();
        setParentRoot(builder, advancement.getParentRoot());
        
        // Custom generation logic
        builder.display(advancement.icon(),
            advancement.getTitleComponent(), 
            advancement.getDescriptionComponent(),
            advancement.getBackground(), 
            advancement.getType(),
            advancement.showToast(), 
            advancement.isAnnouncedInChat(), 
            advancement.isHidden());
        
        // Custom criteria handling
        Map<String, Criterion<?>> criterions = advancement.getCriterions();
        criterions.forEach(builder::addCriterion);
        builder.requirements(advancement.getRequirements(criterions.keySet()));
        
        saveAdvancement(builder, consumer, advancement.getId());
    }
}

When to use / When not to use

When to use

  • Standard advancement trees - Use for conventional advancement progressions
  • Type-safe development - Prefer when compile-time validation is important
  • Integrated localization - Use when working with the framework's translation system
  • Complex criteria logic - Use when advancement requirements need programmatic logic
  • Modular advancement design - Use when different team members work on different advancement branches

The @GenerateAdvancement annotation is designed for most advancement generation scenarios, providing type safety and integration with the framework's ecosystem. For highly specialized requirements, custom data providers offer more direct control.

Clone this wiki locally