# FTB Materials Unification System - Implementation Plan Ideas
## Problem Statement
Multiple mods add duplicate material items (lead ingot, copper wire, etc.) with matching tags (`c:ingots/lead`, `c:wires/copper`). Manually mapping these for each modpack is unsustainable since mod availability varies by pack and Minecraft version.
## Proposed Solution
An automated material unification system **built into FTB Materials** that:
1. **Discovers materials at runtime** by scanning unified tags (`c:ingots/{material}`, etc.)
2. **Prioritizes FTB Materials items first**, then falls back to configured priority
3. **Allows overrides** for materials not in FTB Materials or when another mod's version is needed
4. **Full unification scope:** recipes, loot tables, and world generation
5. **Supports common tech mods:** Mekanism, Thermal, Create, Immersive Engineering and new mods as needed
## Architecture Decision: Built Into FTB Materials
**Rationale:**
- Single mod installation for pack devs
- Guaranteed compatibility with material definitions
- Can be enabled/disabled via config
- Direct access to Resource enum and ResourceType definitions
---
## Core Components
### 1. UnificationManager (Orchestrator)
- Central singleton that coordinates all unification subsystems.
**Responsibilities:**
- Initialize config and subsystems during mod init (or deferred to some ftblibs implementation)
- Build material index when tags are populated (SERVER_STARTED event)
- Handle datapack reloads
- Provide unified API for querying preferred items
### 2. UnificationConfig
**Config Files Location:** `config/ftbmaterials/unification/`
```
config/ftbmaterials/unification/
├── settings.json # Global settings
├── priorities.json # Mod priority list
├── extra-materials.json # Materials not in Resource enum
├── disabled.json # Materials/types to skip
└── worldgen.json # Worldgen-specific settings
```
**settings.json:**
```json
{
"enabled": true,
"unifyRecipes": true,
"unifyLootTables": true,
"unifyWorldGen": true,
"convertInputsToTags": true,
"hideNonPreferredInJEI": true,
"removeDuplicateRecipes": true,
"removeTagsFromNonPreffered": true,
"debugLogging": false
}
```
**priorities.json:**
```json
{
"default": ["ftbmaterials", "mekanism", "thermal", "create", "immersiveengineering", "minecraft"],
"overrides": {
"osmium": ["mekanism"],
"refined_glowstone": ["mekanism"],
"andesite_alloy": ["create"]
}
}
```
### 3. MaterialLookup (Tag Scanner)
**Purpose:** Discover what items exist for each material type at runtime
**Algorithm:**
```
For each ResourceType (INGOT, DUST, PLATE, WIRE, etc.):
For each known material (from Resource enum + config):
Resolve tag: c:ingots/{material}, c:dusts/{material}, etc.
Query tag contents from registry
Build bidirectional index: item <-> tag
```
**Key Maps:**
- `Map<ResourceLocation, UnificationEntry>` - item ID to entry
- `Map<TagKey<Item>, UnificationEntry>` - tag to entry
- `Map<String, UnificationEntry>` - "material:type" to entry
### 4. TagResolver
**Purpose:** Handle tag namespace resolution (c:/forge:/fabric: conventions)
**Tag Namespaces in Priority Order:**
1. `c:` (modern NeoForge/Fabric convention)
2. `forge:` (legacy Forge)
3. `fabric:` (legacy Fabric)
**Special Cases:**
- ResourceType.SHARD has `"shards|mekanism:shards"` - need to handle pipe-separated alternatives
- RAW_BLOCK uses name mutator: `raw_{material}` pattern
### 5. PriorityResolver
**Purpose:** Determine which item version to prefer for each tag
**Default Priority Order:**
1. `ftbmaterials` (ALWAYS first - this is the mod's core value proposition)
2. Configured mod list (mekanism, thermal, create, etc.)
3. `minecraft` (fallback for vanilla items)
### 6. UnificationEntry
**Data Class containing:**
- Material name
- Resource type
- Unified tag
- All items in tag
- Preferred item (cached)
---
## Intial Foundation & Core Lookup
### Modifications to Existing Files
**FTBMaterials.java:**
```java
// Add to init():
UnificationManager.get().init();
// Add SERVER_STARTED event:
LifecycleEvent.SERVER_STARTED.register(server -> {
UnificationManager.get().onTagsPopulated();
});
// Add to registerCommands():
.then(UnificationCommands.register())
```
### Debug Commands
```
/ftbmaterials unify dump # List all unified materials
/ftbmaterials unify check <item> # Show unification info for item
/ftbmaterials unify stats # Show index statistics
/ftbmaterials unify material <name> # Show all types for a material
/ftbmaterials unify reload # Force rebuild caches
```
### Deliverable
System that logs discovered materials and preferred items on server start.
---
## Vanilla Recipe Transformation
### RecipeManager Mixin Strategy
- Create accessor mixin to get recipe map
- Inject at `apply()` method TAIL to transform after loading
### Recipe Types to Handle
**Vanilla:**
- ShapedRecipe (requires reflection for pattern access most likely)
- ShapelessRecipe
- SmeltingRecipe
- BlastingRecipe
- SmokingRecipe
- CampfireCookingRecipe
- StonecutterRecipe
- SmithingRecipe
### Data Component Preservation
```java
ItemStack result = new ItemStack(preferredItem, original.getCount());
if (!original.getComponents().isEmpty()) {
result.applyComponents(original.getComponents());
}
```
### Deliverable
All vanilla recipe outputs unified to FTB Materials items.
---
## Tech Mod Recipe Support
### Files to Create
```
common/src/main/java/dev/ftb/mods/ftbmaterials/unification/recipe/
└── modhandlers/
├── RecipeHandler.java # Interface
├── MekanismRecipeHandler.java
├── ThermalRecipeHandler.java
├── CreateRecipeHandler.java
└── IERecipeHandler.java
└── MiscMods.java
└── FutureMods.java
```
etc etc
### Soft Dependencies
```java
public class MekanismRecipeHandler implements RecipeHandler {
private static final boolean MEKANISM_LOADED =
ModList.get().isLoaded("mekanism");
@Override
public boolean canHandle(Recipe<?> recipe) {
if (!MEKANISM_LOADED) return false;
return recipe.getClass().getName().contains("mekanism");
}
}
```
### Tech Mod Recipe Types
**Mekanism:**
- Crushing, Enriching, Combining, Purifying
- Injecting, Infusing, Crystallizing
- Note: Many use `ItemStackToItemStackRecipe` base class
**Thermal:**
- Pulverizer, Smelter, Press
- Sawmill, Centrifuge, Refinery
- Note: Most extend `SingleItemRecipe` variants
**Create:**
- Crushing, Milling, Pressing
- Mixing, Compacting, Washing
- Note: All extend `ProcessingRecipe`
**Immersive Engineering:**
- Crusher, Arc Furnace
- Metal Press, Alloy Kiln
- Note: Multiblock recipes may need special handling
### Deliverable
Full tech mod recipe unification.
---
## Loot Table Unification
### Approach Options
- **Mixin into LootTable loading** - Intercept during load
### Loot Entry Types to Handle
- `LootItem` entries (direct item drops)
- Preserve counts, functions, conditions
- Skip entries that reference specific mod items (don't unify mod-specific loot)
### Deliverable
All loot drops use preferred items.
---
## World Generation Control
### Files to Create
```
common/src/main/java/dev/ftb/mods/ftbmaterials/
├── unification/worldgen/
│ └── OreFeatureDisabler.java
├── mixin/
│ └── BiomeGenerationMixin.java (or use platform API)
```
### Strategy Options
1. **Feature Removal:** Remove configured features from biome generation
2. **Ore Replacement:** Replace other mod ores with FTB Materials ores in world
3. **Config Generation:** Generate configs for other mods to disable their ores
### Considered Approach
Feature removal via mixin into `BiomeGenerationSettings`:
- Scan for ore features that place blocks matching unification tags
- Remove features that place non-preferred ore variants
- Keep FTB Materials ore generation active
### Config
**worldgen.json:**
```json
{
"disableOtherModOres": true,
"modsToDisable": ["mekanism", "thermal", "immersiveengineering"],
"keepModOres": {
"mekanism": ["osmium", "fluorite"],
"thermal": ["cinnabar", "niter"]
}
}
```
### Deliverable
Only FTB Materials ores generate (configurable).
---
## Integrations & Polish
### JEI/EMI Integration
- Hide non-preferred items from ingredient list
- Ensure recipes show correctly with unified outputs
- Implement via `IIngredientFilter` (JEI) or similar
### Performance Optimization
- Profile startup time impact
- Implement lazy caching where possible
- Use early termination in loops
## Data Flow
### Datapack Reload Flow
```
1. /reload command
└── Clear caches
└── Rebuild MaterialLookup index
└── Re-transform recipes
```
---
## Existing Codebase Integration Points
### Potential Unit Tests
- `PriorityResolverTest` - Verify FTB Materials always wins, overrides work
- `TagResolverTest` - Test c:/forge:/fabric: fallback logic
- `MaterialLookupTest` - Test bidirectional index building
---
## Risk Mitigation
| Risk | Mitigation |
|------|------------|
| Performance with 200+ mods | Lazy caching, batch operations, early termination |
| Recipe type not handled | Fallback to generic handler, log warning |
| Mod API changes | Use reflection with fallbacks, version checks |
| Circular ore processing chains | Track processed items to avoid infinite loops |
| Config conflicts | Validation on load, clear error messages |
| 1.21 API changes | Verify all mappings against Mojang/Yarn, use accessor mixins |
---