Skip to main content

Introduction

The Modifier System is the core of Lithostitched’s functionality, providing a powerful data-driven approach to modifying Minecraft’s worldgen without code. Modifiers are JSON files placed in data/<namespace>/lithostitched_worldgen_modifier/ that apply changes to existing worldgen elements.

How Modifiers Work

Modifiers are applied during server startup after all worldgen data is loaded. They allow you to:
  • Add or remove structures from structure sets
  • Inject elements into template pools
  • Modify surface rules and density functions
  • Add or remove biome features and spawns (Fabric)
  • Configure structure processors and more
All modifiers implement the Modifier interface:
public interface Modifier {
    void applyModifier(RegistryAccess registryAccess);
    void applyModifier();
    int priority();
    MapCodec<? extends Modifier> codec();
}

Priority System

Modifiers execute in order based on their priority field. Lower numbers run first:
priority
integer
default:"1000"
Determines execution order. Lower values execute first.Default priorities:
  • Default modifiers: 1000
  • Remove modifiers: 2000 (execute after additions)
  • NoOp modifier: 0 (highest priority)
{
  "type": "lithostitched:add_surface_rule",
  "priority": 500,
  "levels": ["minecraft:overworld"],
  "surface_rule": {
    "type": "minecraft:sequence",
    "sequence": []
  }
}

Modifier Categories

Lithostitched provides several categories of modifiers:

Structure Modifiers

Modify template pools, structure sets, and structure configurations

Surface Modifiers

Add and modify surface rules for terrain generation

Density Modifiers

Wrap and modify density functions and noise routers

Common Modifier Types

No-Op Modifier

The no_op modifier does nothing and is useful for overriding modifiers from other datapacks:
public record NoOpModifier() implements Modifier
{
  "type": "lithostitched:no_op"
}
Place this in data/<namespace>/lithostitched_worldgen_modifier/<path>.json to override another modifier at the same path.

Stack Feature Modifier

The stack_feature modifier wraps existing configured features with additional placed features, allowing layered generation:
public record StackFeatureModifier(
    int priority,
    HolderSet<ConfiguredFeature<?, ?>> baseFeatures,
    Holder<PlacedFeature> stackedFeature,
    CompositeConfig.Type placementType
) implements Modifier
base_features
HolderSet<ConfiguredFeature>
required
The configured features to wrap with additional generation
stacked_feature
PlacedFeature
required
The placed feature to add on top of base features
placement_type
CompositeConfig.Type
default:"CANCEL_ON_FAILURE"
How to handle placement failures: CANCEL_ON_FAILURE, CONTINUE_ON_FAILURE, or REQUIRE_ALL
{
  "type": "lithostitched:stack_feature",
  "priority": 1000,
  "base_features": "#minecraft:tree",
  "stacked_feature": {
    "feature": "minecraft:flower",
    "placement": []
  },
  "placement_type": "CONTINUE_ON_FAILURE"
}
This wraps all tree features with flower generation, adding flowers on top of generated trees.
See the Adding Modifiers guide for practical examples.

File Location

All modifiers must be placed in:
data/<namespace>/lithostitched_worldgen_modifier/<name>.json
Modifiers with the same file path override each other based on datapack priority. Higher priority datapacks override lower ones.

Execution Flow

The ModifierManager handles modifier application:
public static void applyModifiers(MinecraftServer server) {
    RegistryAccess registries = server.registryAccess();
    HolderLookup.RegistryLookup<Modifier> modifiers = registries.lookupOrThrow(LithostitchedRegistryKeys.WORLDGEN_MODIFIER);
    
    // Sort by priority and apply
    for (Holder.Reference<Modifier> reference : sortByPriority(modifiers.listElements())) {
        reference.value().applyModifier(registries);
    }
}
  1. Server starts and loads all worldgen data
  2. Lithostitched collects all modifiers from datapacks
  3. Modifiers are sorted by priority (lowest first)
  4. Each modifier applies its changes sequentially
  5. Worldgen is ready with all modifications applied

Best Practices

  • Use default priority (1000) for most additions
  • Use higher priority (2000+) for removals to ensure they run after additions
  • Use lower priority (500-999) for base modifications that should run first
  • Group related modifiers in subdirectories
  • Use descriptive file names
  • Consider using prefixes like 01_, 02_ for clear ordering
  • Multiple modifiers can target the same registry entry
  • Ensure modifiers work together correctly
  • Use debug logging to verify execution order
  • Add comments in adjacent .txt files if needed
  • Use clear, descriptive naming
  • Keep modifier files focused on single concerns

Registry Keys

Modifiers are registered under:
public static final ResourceKey<Registry<MapCodec<? extends Modifier>>> MODIFIER_TYPE = 
    ResourceKey.createRegistryKey(Identifier.fromNamespaceAndPath("lithostitched", "worldgen_modifier_type"));

public static final ResourceKey<Registry<Modifier>> WORLDGEN_MODIFIER = 
    ResourceKey.createRegistryKey(Identifier.fromNamespaceAndPath("lithostitched", "worldgen_modifier"));

Next Steps

Structure Modifiers

Learn about template pool and structure set modifiers

Surface Modifiers

Discover surface rule modification

Density Modifiers

Explore density function wrapping

Common Patterns

See practical modifier examples

Build docs developers (and LLMs) love