2017-06-19 15:02:18 -07:00
/*
2022-04-14 03:42:00 +00:00
* Copyright (c) Forge Development LLC and contributors
2022-02-28 13:41:23 -08:00
* SPDX-License-Identifier: LGPL-2.1-only
2017-06-19 15:02:18 -07:00
*/
package net.minecraftforge.registries ;
2022-06-07 15:31:26 -05:00
import com.google.common.collect.ArrayListMultimap ;
import com.google.common.collect.ImmutableMap ;
import com.google.common.collect.Multimap ;
2024-04-30 10:56:00 -07:00
import com.mojang.serialization.MapCodec ;
2022-11-22 13:32:47 -06:00
import java.util.Collection ;
import java.util.Comparator ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.LinkedHashMap ;
2022-06-08 11:08:55 -05:00
import java.util.LinkedHashSet ;
2022-11-22 13:32:47 -06:00
import java.util.List ;
import java.util.Locale ;
import java.util.Map ;
import java.util.Set ;
import java.util.function.BiConsumer ;
2024-04-30 10:56:00 -07:00
import java.util.function.BiFunction ;
2022-11-22 13:32:47 -06:00
import java.util.stream.Collectors ;
2023-10-14 18:14:03 +01:00
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap ;
import it.unimi.dsi.fastutil.objects.Object2IntMap ;
2024-04-30 10:56:00 -07:00
import net.minecraft.core.DefaultedRegistry ;
import net.minecraft.core.Holder ;
2022-06-07 15:31:26 -05:00
import net.minecraft.core.IdMapper ;
import net.minecraft.core.MappedRegistry ;
import net.minecraft.core.Registry ;
2024-04-30 10:56:00 -07:00
import net.minecraft.core.WritableRegistry ;
2022-11-22 13:32:47 -06:00
import net.minecraft.core.registries.BuiltInRegistries ;
2024-10-07 17:26:02 -07:00
import net.minecraft.core.registries.Registries ;
2022-06-07 15:31:26 -05:00
import net.minecraft.network.syncher.EntityDataSerializer ;
import net.minecraft.resources.ResourceKey ;
2025-12-09 18:18:19 -08:00
import net.minecraft.resources.Identifier ;
2021-07-17 00:08:49 -05:00
import net.minecraft.world.entity.EntityType ;
2022-10-14 12:39:14 -04:00
import net.minecraft.world.entity.SpawnPlacements ;
2021-07-17 00:08:49 -05:00
import net.minecraft.world.entity.ai.attributes.Attribute ;
import net.minecraft.world.entity.ai.attributes.DefaultAttributes ;
2022-06-07 15:31:26 -05:00
import net.minecraft.world.entity.ai.village.poi.PoiType ;
2021-07-17 00:08:49 -05:00
import net.minecraft.world.item.BlockItem ;
import net.minecraft.world.item.Item ;
2023-02-24 08:58:27 -06:00
import net.minecraft.world.item.ItemDisplayContext ;
2022-06-07 15:31:26 -05:00
import net.minecraft.world.level.block.Block ;
2023-09-07 20:23:27 -07:00
import net.minecraft.world.level.block.entity.BlockEntityType ;
2022-06-07 15:31:26 -05:00
import net.minecraft.world.level.block.state.BlockState ;
2021-07-17 00:08:49 -05:00
import net.minecraft.world.level.block.state.StateDefinition ;
import net.minecraft.world.level.levelgen.DebugLevelSource ;
2022-06-07 15:31:26 -05:00
import net.minecraft.world.level.material.Fluid ;
2022-11-22 13:32:47 -06:00
import net.minecraftforge.common.CreativeModeTabRegistry ;
2022-06-07 15:31:26 -05:00
import net.minecraftforge.common.ForgeHooks ;
2023-09-20 15:24:46 -07:00
import net.minecraftforge.common.loot.IGlobalLootModifier ;
2021-07-17 00:08:49 -05:00
import net.minecraftforge.common.util.LogMessageAdapter ;
2022-06-14 18:07:51 -04:00
import net.minecraftforge.fluids.FluidType ;
2022-04-01 15:49:46 -05:00
import net.minecraftforge.fml.ModLoader ;
2019-02-13 21:06:39 -05:00
import net.minecraftforge.fml.ModLoadingContext ;
2021-07-17 00:08:49 -05:00
import net.minecraftforge.fml.StartupMessageManager ;
import net.minecraftforge.fml.util.EnhancedRuntimeException ;
import net.minecraftforge.fml.util.thread.EffectiveSide ;
2023-07-17 17:26:28 -07:00
import net.minecraftforge.registries.ForgeRegistries.Keys ;
2024-04-30 10:56:00 -07:00
import net.minecraftforge.registries.IForgeRegistry.AddCallback ;
import net.minecraftforge.registries.IForgeRegistry.BakeCallback ;
import net.minecraftforge.registries.IForgeRegistry.ClearCallback ;
import net.minecraftforge.registries.IForgeRegistry.CreateCallback ;
import net.minecraftforge.registries.IForgeRegistry.SlaveKey ;
import net.minecraftforge.registries.IForgeRegistry.ValidateCallback ;
2019-01-11 23:52:30 +01:00
import org.apache.logging.log4j.LogManager ;
import org.apache.logging.log4j.Logger ;
2023-07-17 17:26:28 -07:00
import org.apache.logging.log4j.Marker ;
2022-10-22 04:22:18 +08:00
import org.jetbrains.annotations.ApiStatus ;
2022-06-07 15:31:26 -05:00
import org.jetbrains.annotations.Nullable ;
2017-06-19 15:02:18 -07:00
/**
* INTERNAL ONLY
* MODDERS SHOULD HAVE NO REASON TO USE THIS CLASS
2023-10-14 18:14:03 +01:00
* <p>Use the public {@link IForgeRegistry} and {@link ForgeRegistries} APIs to get the data</p>
2017-06-19 15:02:18 -07:00
*/
2022-10-22 04:22:18 +08:00
@ApiStatus.Internal
2024-04-30 10:56:00 -07:00
public class GameData {
2018-08-27 13:10:07 -04:00
private static final Logger LOGGER = LogManager . getLogger ();
2023-10-14 18:14:03 +01:00
private static final Marker REGISTRIES = ForgeRegistry . REGISTRIES ;
2019-05-23 16:02:15 -07:00
private static final int MAX_VARINT = Integer . MAX_VALUE - 1 ; //We were told it is their intention to have everything in a reg be unlimited, so assume that until we find cases where it isnt.
2017-06-19 15:02:18 -07:00
private static boolean hasInit = false ;
private static final boolean DISABLE_VANILLA_REGISTRIES = Boolean . parseBoolean ( System . getProperty ( "forge.disableVanillaGameData" , "false" )); // Use for unit tests/debugging
2026-06-16 11:18:58 -07:00
private static final BiConsumer < Identifier , ForgeRegistry <?>> LOCK_VANILLA = ( _ , reg ) -> reg . slaves . values (). stream (). filter ( o -> o instanceof ILockableRegistry ). forEach ( o -> (( ILockableRegistry ) o ). lock ());
2025-12-09 18:18:19 -08:00
private static Set < Identifier > vanillaRegistryOrder = null ;
2017-06-19 15:02:18 -07:00
static {
init ();
}
2023-09-20 15:24:46 -07:00
@SuppressWarnings ( "deprecation" )
2024-04-30 10:56:00 -07:00
public static void init () {
if ( DISABLE_VANILLA_REGISTRIES ) {
2019-01-27 00:06:57 -05:00
LOGGER . warn ( REGISTRIES , "DISABLING VANILLA REGISTRY CREATION AS PER SYSTEM VARIABLE SETTING! forge.disableVanillaGameData" );
2017-06-19 15:02:18 -07:00
return ;
}
2024-04-30 10:56:00 -07:00
2017-06-19 15:02:18 -07:00
if ( hasInit )
return ;
2024-04-30 10:56:00 -07:00
2017-06-19 15:02:18 -07:00
hasInit = true ;
2020-06-19 10:58:45 -04:00
2019-06-09 03:23:00 -04:00
// Game objects
2023-07-17 17:26:28 -07:00
makeRegistry ( Keys . BLOCKS , "air" ). addCallback ( BlockCallbacks . INSTANCE ). legacyName ( "blocks" ). intrusiveHolderCallback ( Block :: builtInRegistryHolder ). create ();
makeRegistry ( Keys . FLUIDS , "empty" ). intrusiveHolderCallback ( Fluid :: builtInRegistryHolder ). create ();
makeRegistry ( Keys . ITEMS , "air" ). addCallback ( ItemCallbacks . INSTANCE ). legacyName ( "items" ). intrusiveHolderCallback ( Item :: builtInRegistryHolder ). create ();
2024-04-30 10:56:00 -07:00
makeRegistry ( Keys . MOB_EFFECTS ). create ();
makeRegistry ( Keys . SOUND_EVENTS ). create ();
makeRegistry ( Keys . POTIONS ). create ();
2023-07-17 17:26:28 -07:00
makeRegistry ( Keys . ENTITY_TYPES , "pig" ). legacyName ( "entities" ). intrusiveHolderCallback ( EntityType :: builtInRegistryHolder ). create ();
2023-09-07 20:23:27 -07:00
makeRegistry ( Keys . BLOCK_ENTITY_TYPES ). disableSaving (). legacyName ( "blockentities" ). intrusiveHolderCallback ( BlockEntityType :: builtInRegistryHolder ). create ();
2023-07-17 17:26:28 -07:00
makeRegistry ( Keys . PARTICLE_TYPES ). disableSaving (). create ();
makeRegistry ( Keys . MENU_TYPES ). disableSaving (). create ();
makeRegistry ( Keys . PAINTING_VARIANTS , "kebab" ). create ();
makeRegistry ( Keys . RECIPE_TYPES ). disableSaving (). disableSync (). create ();
makeRegistry ( Keys . RECIPE_SERIALIZERS ). disableSaving (). create ();
makeRegistry ( Keys . ATTRIBUTES ). onValidate ( AttributeCallbacks . INSTANCE ). disableSaving (). disableSync (). create ();
makeRegistry ( Keys . STAT_TYPES ). create ();
makeRegistry ( Keys . COMMAND_ARGUMENT_TYPES ). disableSaving (). create ();
2024-10-07 17:26:02 -07:00
makeRegistry ( Registries . DATA_COMPONENT_TYPE ). disableSaving (). create ();
2020-06-19 10:58:45 -04:00
2019-06-09 03:23:00 -04:00
// Villagers
2023-07-17 17:26:28 -07:00
makeRegistry ( Keys . VILLAGER_PROFESSIONS , "none" ). create ();
2024-04-30 10:56:00 -07:00
makeRegistry ( Keys . POI_TYPES ). addCallback ( PoiTypeCallbacks . INSTANCE ). disableSync (). create ();
2023-07-17 17:26:28 -07:00
makeRegistry ( Keys . MEMORY_MODULE_TYPES , "dummy" ). disableSync (). create ();
makeRegistry ( Keys . SENSOR_TYPES , "dummy" ). disableSaving (). disableSync (). create ();
makeRegistry ( Keys . ACTIVITIES ). disableSaving (). disableSync (). create ();
2019-06-09 03:23:00 -04:00
// Worldgen
2023-07-17 17:26:28 -07:00
makeRegistry ( Keys . CHUNK_STATUS , "empty" ). disableSaving (). disableSync (). create ();
makeRegistry ( Keys . FOLIAGE_PLACER_TYPES ). disableSaving (). disableSync (). create ();
makeRegistry ( Keys . TREE_DECORATOR_TYPES ). disableSaving (). disableSync (). create ();
2020-05-23 13:42:56 -04:00
2020-08-26 09:49:34 -07:00
// Dynamic Worldgen
2023-07-17 17:26:28 -07:00
makeRegistry ( Keys . BIOMES ). disableSync (). create ();
2022-03-16 15:36:00 -05:00
}
2020-08-26 09:49:34 -07:00
2024-04-30 10:56:00 -07:00
static RegistryBuilder < EntityDataSerializer <?>> getDataSerializersRegistryBuilder () {
2023-07-17 17:26:28 -07:00
return makeRegistry ( Keys . ENTITY_DATA_SERIALIZERS , 256 /*vanilla space*/ , MAX_VARINT ). disableSaving (). disableOverrides ();
2022-03-16 15:36:00 -05:00
}
2024-04-30 10:56:00 -07:00
static RegistryBuilder < MapCodec <? extends IGlobalLootModifier >> getGLMSerializersRegistryBuilder () {
2023-07-17 17:26:28 -07:00
return makeRegistry ( Keys . GLOBAL_LOOT_MODIFIER_SERIALIZERS ). disableSaving (). disableSync ();
2019-05-23 16:02:15 -07:00
}
2022-03-16 15:36:00 -05:00
2024-04-30 10:56:00 -07:00
static RegistryBuilder < FluidType > getFluidTypeRegistryBuilder () {
2023-07-17 17:26:28 -07:00
return makeRegistry ( Keys . FLUID_TYPES ). disableSaving ();
2022-06-14 18:07:51 -04:00
}
2022-10-22 04:22:18 +08:00
2023-10-06 20:54:08 -07:00
static < T > RegistryBuilder < T > makeUnsavedAndUnsynced () {
return RegistryBuilder . < T > of (). disableSaving (). disableSync ();
2023-09-20 15:24:46 -07:00
}
2024-04-30 10:56:00 -07:00
static RegistryBuilder < ItemDisplayContext > getItemDisplayContextRegistryBuilder () {
2023-02-24 08:58:27 -06:00
return new RegistryBuilder < ItemDisplayContext > ()
. setMaxID ( 128 * 2 ) /* 0 -> 127 gets positive ID, 128 -> 256 gets negative ID */ . disableOverrides (). disableSaving ()
2025-12-09 18:18:19 -08:00
. setDefaultKey ( Identifier . withDefaultNamespace ( "none" ))
2023-02-24 08:58:27 -06:00
. onAdd ( ItemDisplayContext . ADD_CALLBACK );
}
2024-04-30 10:56:00 -07:00
private static < T > RegistryBuilder < T > makeRegistry ( ResourceKey <? extends Registry < T >> key ) {
2025-12-09 18:18:19 -08:00
return new RegistryBuilder < T > (). setName ( key . identifier ()). setMaxID ( MAX_VARINT ). hasWrapper ();
2017-06-19 15:02:18 -07:00
}
2024-04-30 10:56:00 -07:00
private static < T > RegistryBuilder < T > makeRegistry ( ResourceKey <? extends Registry < T >> key , int min , int max ) {
2025-12-09 18:18:19 -08:00
return new RegistryBuilder < T > (). setName ( key . identifier ()). setIDRange ( min , max ). hasWrapper ();
2019-04-11 13:33:12 +01:00
}
2024-04-30 10:56:00 -07:00
private static < T > RegistryBuilder < T > makeRegistry ( ResourceKey <? extends Registry < T >> key , String _default ) {
2025-12-09 18:18:19 -08:00
return new RegistryBuilder < T > (). setName ( key . identifier ()). setMaxID ( MAX_VARINT ). hasWrapper (). setDefaultKey ( Identifier . parse ( _default ));
2017-06-19 15:02:18 -07:00
}
2024-04-30 10:56:00 -07:00
@SuppressWarnings ( "unchecked" )
public static < T , R extends WritableRegistry < T >> R getWrapper ( ResourceKey <? extends Registry < T >> key , R vanilla ) {
2025-12-09 18:18:19 -08:00
var reg = RegistryManager . ACTIVE . getRegistry ( key . identifier ());
2024-04-30 10:56:00 -07:00
if ( reg == null )
return vanilla ;
2017-06-19 15:02:18 -07:00
2024-04-30 10:56:00 -07:00
var wrapper = reg . getSlaveMap ( WrapperFactory . WRAPPER );
if ( wrapper == null )
return vanilla ;
2017-06-19 15:02:18 -07:00
2024-04-30 10:56:00 -07:00
var vanillaD = vanilla instanceof DefaultedRegistry tmp ? tmp : null ;
var wrapperD = wrapper instanceof DefaultedRegistry tmp ? tmp : null ;
if ( vanillaD == null && wrapperD != null )
2025-12-09 18:18:19 -08:00
throw new IllegalStateException ( "Invalid wrapper " + key . identifier () + " was defaulted when not expected" );
2024-04-30 10:56:00 -07:00
if ( vanillaD != null && wrapperD == null )
2025-12-09 18:18:19 -08:00
throw new IllegalStateException ( "Invalid wrapper " + key . identifier () + " was normal when should be expected" );
2024-04-30 10:56:00 -07:00
if ( vanillaD != null && ! vanillaD . getDefaultKey (). equals ( wrapperD . getDefaultKey ()))
2025-12-09 18:18:19 -08:00
throw new IllegalStateException ( "Invalid wrapper " + key . identifier () + " mismatched default key " + vanillaD . getDefaultKey () + " != " + wrapperD . getDefaultKey ());
2024-04-30 10:56:00 -07:00
return ( R ) wrapper ;
2017-06-19 15:02:18 -07:00
}
2024-04-30 10:56:00 -07:00
static < V > WrapperFactory < V > createWrapperFactory ( boolean defaulted ) {
BiFunction < ForgeRegistry < V > , RegistryManager , WritableRegistry < V >> factory = defaulted
? ( reg , stage ) -> new NamespacedDefaultedWrapper <> ( reg , reg . getBuilder (). getIntrusiveHolderCallback (), stage )
: ( reg , stage ) -> new NamespacedWrapper < V > ( reg , reg . getBuilder (). getIntrusiveHolderCallback (), stage );
return new WrapperFactory <> ( factory );
2017-06-19 15:02:18 -07:00
}
2021-02-15 17:24:35 -08:00
2024-04-30 10:56:00 -07:00
static record WrapperFactory < V > ( BiFunction < ForgeRegistry < V > , RegistryManager , WritableRegistry < V >> factory ) implements CreateCallback < V > , AddCallback < V > {
static SlaveKey < WritableRegistry <?>> WRAPPER = SlaveKey . create ( "forge:vanilla_wrapper" );
@Override
public void onCreate ( IForgeRegistryInternal < V > owner , RegistryManager stage ) {
owner . setSlaveMap ( WRAPPER , factory . apply (( ForgeRegistry < V > ) owner , stage ));
}
@Override
public void onAdd ( IForgeRegistryInternal < V > owner , RegistryManager stage , int id , ResourceKey < V > key , V value , V oldValue ) {
@SuppressWarnings ( "unchecked" )
var wrapper = ( NamespacedWrapper < V > ) owner . getSlaveMap ( WRAPPER );
wrapper . onAdded ( stage , id , key , value , oldValue );
}
2020-12-28 15:17:51 -05:00
}
2017-06-19 15:02:18 -07:00
2024-11-20 19:32:28 -08:00
public static void vanillaSnapshot () {
2019-01-27 00:06:57 -05:00
LOGGER . debug ( REGISTRIES , "Creating vanilla freeze snapshot" );
2024-11-20 19:32:28 -08:00
for ( var r : RegistryManager . ACTIVE . registries . entrySet ())
2022-06-07 15:31:26 -05:00
loadRegistry ( r . getKey (), RegistryManager . ACTIVE , RegistryManager . VANILLA , true );
2024-11-20 19:32:28 -08:00
RegistryManager . VANILLA . registries . forEach (( name , reg ) -> {
2017-06-20 18:39:55 -07:00
reg . validateContent ( name );
reg . freeze ();
});
2024-11-20 19:32:28 -08:00
2017-06-20 18:39:55 -07:00
RegistryManager . VANILLA . registries . forEach ( LOCK_VANILLA );
RegistryManager . ACTIVE . registries . forEach ( LOCK_VANILLA );
2024-11-20 19:32:28 -08:00
// Capture the vanilla registry order.
vanillaRegistryOrder = new LinkedHashSet <> ( MappedRegistry . getKnownRegistries ());
LOGGER . debug ( REGISTRIES , "Vanilla registry order:" );
for ( var key : vanillaRegistryOrder )
LOGGER . info ( REGISTRIES , "\t" + key );
2019-01-27 00:06:57 -05:00
LOGGER . debug ( REGISTRIES , "Vanilla freeze snapshot created" );
2017-06-19 15:02:18 -07:00
}
2023-09-20 15:24:46 -07:00
@SuppressWarnings ( "deprecation" )
2024-11-14 14:23:00 -08:00
public static void unfreezeData () {
2022-03-01 14:59:27 -06:00
LOGGER . debug ( REGISTRIES , "Unfreezing vanilla registries" );
2024-11-20 19:32:28 -08:00
for ( var reg : BuiltInRegistries . REGISTRY ) {
if ( reg instanceof MappedRegistry mapped )
mapped . unfreeze ();
}
2022-03-01 14:59:27 -06:00
}
2024-11-14 14:23:00 -08:00
public static void freezeData () {
2019-01-27 00:06:57 -05:00
LOGGER . debug ( REGISTRIES , "Freezing registries" );
2024-11-14 14:23:00 -08:00
for ( var reg : BuiltInRegistries . REGISTRY ) {
if ( reg instanceof NamespacedWrapper <?> named ) {
2025-03-26 16:32:44 -07:00
// This is called after we fire our register events, but before the game loads any tags.
2024-11-14 14:23:00 -08:00
// So lets skip the validation for this pass and just make every unbound tag empty
// Tags will be bound later when tag data is reloaded/synced
2025-03-26 16:32:44 -07:00
if ( named . isFrozen ())
named . unfreeze ();
2024-11-14 14:23:00 -08:00
named . freeze ();
} else if ( reg instanceof MappedRegistry maped )
maped . freeze ();
}
for ( var r : RegistryManager . ACTIVE . registries . entrySet ())
2022-06-07 15:31:26 -05:00
loadRegistry ( r . getKey (), RegistryManager . ACTIVE , RegistryManager . FROZEN , true );
2024-11-14 14:23:00 -08:00
RegistryManager . FROZEN . registries . forEach (( name , reg ) -> {
2017-06-20 18:39:55 -07:00
reg . validateContent ( name );
reg . freeze ();
});
2024-11-14 14:23:00 -08:00
2019-01-11 23:52:30 +01:00
RegistryManager . ACTIVE . registries . forEach (( name , reg ) -> {
reg . freeze ();
reg . bake ();
2019-01-27 00:06:57 -05:00
reg . dump ( name );
2019-01-11 23:52:30 +01:00
});
2017-07-13 14:45:51 -07:00
2018-08-29 21:30:15 -04:00
// the id mapping is finalized, no ids actually changed but this is a good place to tell everyone to 'bake' their stuff.
2019-03-25 21:41:28 -04:00
fireRemapEvent ( ImmutableMap . of (), true );
2017-07-13 14:45:51 -07:00
2019-01-27 00:06:57 -05:00
LOGGER . debug ( REGISTRIES , "All registries frozen" );
2017-06-19 15:02:18 -07:00
}
2020-10-18 13:37:28 -04:00
public static void revertToFrozen () {
revertTo ( RegistryManager . FROZEN , true );
}
2023-09-20 15:24:46 -07:00
2024-11-20 19:32:28 -08:00
public static void revertTo ( final RegistryManager target , boolean fireEvents ) {
if ( target . registries . isEmpty ()) {
2020-10-18 13:37:28 -04:00
LOGGER . warn ( REGISTRIES , "Can't revert to {} GameData state without a valid snapshot." , target . getName ());
2017-06-19 15:02:18 -07:00
return ;
}
2026-06-16 11:18:58 -07:00
RegistryManager . ACTIVE . registries . forEach (( _ , reg ) -> reg . resetDelegates ());
2017-06-19 15:02:18 -07:00
2020-10-18 13:37:28 -04:00
LOGGER . debug ( REGISTRIES , "Reverting to {} data state." , target . getName ());
2024-11-20 19:32:28 -08:00
for ( var r : RegistryManager . ACTIVE . registries . entrySet ())
2022-06-07 15:31:26 -05:00
loadRegistry ( r . getKey (), target , RegistryManager . ACTIVE , true );
2024-11-20 19:32:28 -08:00
2026-06-16 11:18:58 -07:00
RegistryManager . ACTIVE . registries . forEach (( _ , reg ) -> reg . bake ());
2017-06-19 15:02:18 -07:00
// the id mapping has reverted, fire remap events for those that care about id changes
2020-10-18 13:37:28 -04:00
if ( fireEvents ) {
2022-06-07 15:31:26 -05:00
fireRemapEvent ( ImmutableMap . of (), true );
2025-10-12 10:49:47 -07:00
ObjectHolderRegistry . applyObjectHolders ();
2020-10-18 13:37:28 -04:00
}
2017-06-19 15:02:18 -07:00
// the id mapping has reverted, ensure we sync up the object holders
2020-10-18 13:37:28 -04:00
LOGGER . debug ( REGISTRIES , "{} state restored." , target . getName ());
2017-06-19 15:02:18 -07:00
}
2025-12-09 18:18:19 -08:00
public static void revert ( RegistryManager state , Identifier registry , boolean lock ) {
2019-01-27 00:06:57 -05:00
LOGGER . debug ( REGISTRIES , "Reverting {} to {}" , registry , state . getName ());
2022-06-07 15:31:26 -05:00
loadRegistry ( registry , state , RegistryManager . ACTIVE , lock );
2019-01-27 00:06:57 -05:00
LOGGER . debug ( REGISTRIES , "Reverting complete" );
2017-06-19 15:02:18 -07:00
}
2022-06-07 15:31:26 -05:00
@SuppressWarnings ( "deprecation" )
2024-10-25 17:52:16 -07:00
public static void postRegisterEvents () {
2025-12-09 18:18:19 -08:00
Set < Identifier > keySet = new HashSet <> ( RegistryManager . ACTIVE . registries . keySet ());
2022-06-07 15:31:26 -05:00
keySet . addAll ( RegistryManager . getVanillaRegistryKeys ());
2020-08-30 21:39:06 -04:00
2025-12-09 18:18:19 -08:00
Set < Identifier > ordered = new LinkedHashSet <> ( vanillaRegistryOrder );
2022-06-08 11:08:55 -05:00
ordered . retainAll ( keySet );
2025-12-09 18:18:19 -08:00
ordered . addAll ( keySet . stream (). sorted ( Identifier :: compareNamespaced ). toList ());
2020-08-30 21:39:06 -04:00
2022-06-07 15:31:26 -05:00
RuntimeException aggregate = new RuntimeException ();
2025-12-09 18:18:19 -08:00
for ( Identifier rootRegistryName : ordered ) {
2024-10-25 17:52:16 -07:00
try {
2022-06-07 15:31:26 -05:00
ResourceKey <? extends Registry <?>> registryKey = ResourceKey . createRegistryKey ( rootRegistryName );
ForgeRegistry <?> forgeRegistry = RegistryManager . ACTIVE . getRegistry ( rootRegistryName );
2024-10-25 17:52:16 -07:00
Registry <?> vanillaRegistry = BuiltInRegistries . REGISTRY . getValue ( rootRegistryName );
2022-06-07 15:31:26 -05:00
RegisterEvent registerEvent = new RegisterEvent ( registryKey , forgeRegistry , vanillaRegistry );
2025-12-09 18:18:19 -08:00
StartupMessageManager . modLoaderConsumer (). ifPresent ( s -> s . accept ( "REGISTERING " + registryKey . identifier ()));
2022-06-07 15:31:26 -05:00
if ( forgeRegistry != null )
forgeRegistry . unfreeze ();
2025-10-01 11:12:41 -07:00
ModLoader . postEventWrapContainerInModOrder ( registerEvent );
2022-06-07 15:31:26 -05:00
if ( forgeRegistry != null )
forgeRegistry . freeze ();
2025-12-09 18:18:19 -08:00
LOGGER . debug ( REGISTRIES , "Applying holder lookups: {}" , registryKey . identifier ());
ObjectHolderRegistry . applyObjectHolders ( registryKey . identifier ():: equals );
LOGGER . debug ( REGISTRIES , "Holder lookups applied: {}" , registryKey . identifier ());
2024-10-25 17:52:16 -07:00
} catch ( Throwable t ) {
2022-06-07 15:31:26 -05:00
aggregate . addSuppressed ( t );
2020-10-18 13:37:28 -04:00
}
2022-06-07 15:31:26 -05:00
}
2024-10-25 17:52:16 -07:00
2024-11-20 19:32:28 -08:00
if ( aggregate . getSuppressed (). length > 0 ) {
2022-06-07 15:31:26 -05:00
LOGGER . fatal ( "Failed to register some entries, see suppressed exceptions for details" , aggregate );
LOGGER . fatal ( "Detected errors during registry event dispatch, rolling back to VANILLA state" );
revertTo ( RegistryManager . VANILLA , false );
LOGGER . fatal ( "Detected errors during registry event dispatch, roll back to VANILLA complete" );
2022-11-09 04:17:00 +08:00
throw aggregate ;
2024-10-25 17:52:16 -07:00
} else {
2022-06-07 15:31:26 -05:00
ForgeHooks . modifyAttributes ();
2022-10-14 12:39:14 -04:00
SpawnPlacements . fireSpawnPlacementEvent ();
2023-04-01 20:45:29 -05:00
CreativeModeTabRegistry . sortTabs ();
2022-06-07 15:31:26 -05:00
}
2022-04-01 15:49:46 -05:00
}
2017-06-19 15:02:18 -07:00
//Lets us clear the map so we can rebuild it.
2024-11-20 19:32:28 -08:00
private static class ClearableObjectIntIdentityMap < I > extends IdMapper < I > {
void clear () {
2021-03-09 13:37:23 -08:00
this . tToId . clear ();
this . idToT . clear ();
2019-01-11 23:52:30 +01:00
this . nextId = 0 ;
2017-06-19 15:02:18 -07:00
}
2019-01-04 01:36:32 +00:00
2023-09-20 15:24:46 -07:00
@SuppressWarnings ( "unused" )
2024-11-20 19:32:28 -08:00
void remove ( I key ) {
2023-10-14 18:14:03 +01:00
boolean hadId = this . tToId . containsKey ( key );
int prev = this . tToId . removeInt ( key );
if ( hadId ) {
2021-03-09 13:37:23 -08:00
this . idToT . set ( prev , null );
2019-01-04 01:36:32 +00:00
}
}
2017-06-19 15:02:18 -07:00
}
2024-04-30 10:56:00 -07:00
public static class BlockCallbacks implements AddCallback < Block > , ClearCallback < Block > , BakeCallback < Block > , CreateCallback < Block > {
2017-06-19 15:02:18 -07:00
static final BlockCallbacks INSTANCE = new BlockCallbacks ();
2024-04-30 10:56:00 -07:00
private static final SlaveKey < ClearableObjectIntIdentityMap < BlockState >> STATE_TO_ID = SlaveKey . create ( "state_to_id" );
public static IdMapper < BlockState > getBlockStateIDMap () {
return RegistryManager . ACTIVE . getRegistry ( Keys . BLOCKS ). getSlaveMap ( STATE_TO_ID );
}
2017-06-19 15:02:18 -07:00
@Override
2024-04-30 10:56:00 -07:00
public void onAdd ( IForgeRegistryInternal < Block > owner , RegistryManager stage , int id , ResourceKey < Block > key , Block block , @Nullable Block oldBlock ) {
if ( oldBlock != null ) {
2021-07-17 00:08:49 -05:00
StateDefinition < Block , BlockState > oldContainer = oldBlock . getStateDefinition ();
StateDefinition < Block , BlockState > newContainer = block . getStateDefinition ();
2019-01-11 23:52:30 +01:00
// Test vanilla blockstates, if the number matches, make sure they also match in their string representations
2025-12-09 18:18:19 -08:00
if ( key . identifier (). getNamespace (). equals ( "minecraft" ) && ! oldContainer . getProperties (). equals ( newContainer . getProperties ())) {
2019-01-11 23:52:30 +01:00
String oldSequence = oldContainer . getProperties (). stream ()
2022-02-15 18:27:10 +01:00
. map ( s -> String . format ( Locale . ENGLISH , "%s={%s}" , s . getName (),
2021-03-09 13:37:23 -08:00
s . getPossibleValues (). stream (). map ( Object :: toString ). collect ( Collectors . joining ( "," ))))
2019-01-11 23:52:30 +01:00
. collect ( Collectors . joining ( ";" ));
String newSequence = newContainer . getProperties (). stream ()
2022-02-15 18:27:10 +01:00
. map ( s -> String . format ( Locale . ENGLISH , "%s={%s}" , s . getName (),
2021-03-09 13:37:23 -08:00
s . getPossibleValues (). stream (). map ( Object :: toString ). collect ( Collectors . joining ( "," ))))
2019-01-11 23:52:30 +01:00
. collect ( Collectors . joining ( ";" ));
2021-07-17 00:08:49 -05:00
LOGGER . error ( REGISTRIES ,() -> LogMessageAdapter . adapt ( sb -> {
2025-12-09 18:18:19 -08:00
sb . append ( "Registry replacements for vanilla block '" ). append ( key . identifier ())
2022-06-07 15:31:26 -05:00
. append ( "' must not change the number or order of blockstates.\n" );
2019-01-27 00:06:57 -05:00
sb . append ( "\tOld: " ). append ( oldSequence ). append ( '\n' );
sb . append ( "\tNew: " ). append ( newSequence );
}));
2019-01-11 23:52:30 +01:00
throw new RuntimeException ( "Invalid vanilla replacement. See log for details." );
}
}
2017-06-19 15:02:18 -07:00
}
@Override
2024-04-30 10:56:00 -07:00
public void onClear ( IForgeRegistryInternal < Block > owner , RegistryManager stage ) {
owner . getSlaveMap ( STATE_TO_ID ). clear ();
2017-06-19 15:02:18 -07:00
}
@Override
2024-04-30 10:56:00 -07:00
public void onCreate ( IForgeRegistryInternal < Block > owner , RegistryManager stage ) {
var idMap = new ClearableObjectIntIdentityMap < BlockState > () {
2017-06-19 15:02:18 -07:00
@Override
2024-04-30 10:56:00 -07:00
public int getId ( BlockState key ) {
2023-10-14 18:14:03 +01:00
return this . tToId . containsKey ( key ) ? this . tToId . getInt ( key ) : - 1 ;
2017-06-19 15:02:18 -07:00
}
};
2024-04-30 10:56:00 -07:00
owner . setSlaveMap ( STATE_TO_ID , idMap );
2017-06-19 15:02:18 -07:00
}
2019-01-11 23:52:30 +01:00
@Override
2024-04-30 10:56:00 -07:00
public void onBake ( IForgeRegistryInternal < Block > owner , RegistryManager stage ) {
var blockstateMap = owner . getSlaveMap ( STATE_TO_ID );
2019-01-11 23:52:30 +01:00
2024-04-30 10:56:00 -07:00
for ( Block block : owner ) {
for ( BlockState state : block . getStateDefinition (). getPossibleStates ()) {
2019-01-11 23:52:30 +01:00
blockstateMap . add ( state );
2021-03-09 13:37:23 -08:00
state . initCache ();
2019-01-11 23:52:30 +01:00
}
2019-05-23 16:02:15 -07:00
2019-06-08 10:45:28 -04:00
block . getLootTable ();
2019-01-11 23:52:30 +01:00
}
2021-07-17 00:08:49 -05:00
DebugLevelSource . initValidStates ();
2019-01-11 23:52:30 +01:00
}
2017-06-19 15:02:18 -07:00
}
2024-04-30 10:56:00 -07:00
public static class ItemCallbacks implements AddCallback < Item > , ClearCallback < Item > , CreateCallback < Item > {
2017-06-19 15:02:18 -07:00
static final ItemCallbacks INSTANCE = new ItemCallbacks ();
2024-04-30 10:56:00 -07:00
private static final SlaveKey < Map < Block , Item >> BLOCK_TO_ITEM = SlaveKey . create ( "block_to_item" );
public static Map < Block , Item > getBlockItemMap () {
return RegistryManager . ACTIVE . getRegistry ( Keys . ITEMS ). getSlaveMap ( BLOCK_TO_ITEM );
}
2017-06-19 15:02:18 -07:00
@Override
2024-04-30 10:56:00 -07:00
public void onAdd ( IForgeRegistryInternal < Item > owner , RegistryManager stage , int id , ResourceKey < Item > key , Item item , @Nullable Item oldItem ) {
2024-11-20 19:32:28 -08:00
if ( oldItem instanceof BlockItem block )
2024-04-30 10:56:00 -07:00
block . removeFromBlockToItemMap ( owner . getSlaveMap ( BLOCK_TO_ITEM ), item );
2024-11-20 19:32:28 -08:00
if ( item instanceof BlockItem block )
2024-04-30 10:56:00 -07:00
block . registerBlocks ( owner . getSlaveMap ( BLOCK_TO_ITEM ), item );
2017-06-19 15:02:18 -07:00
}
@Override
2024-04-30 10:56:00 -07:00
public void onClear ( IForgeRegistryInternal < Item > owner , RegistryManager stage ) {
owner . getSlaveMap ( BLOCK_TO_ITEM ). clear ();
2017-06-19 15:02:18 -07:00
}
@Override
2024-04-30 10:56:00 -07:00
public void onCreate ( IForgeRegistryInternal < Item > owner , RegistryManager stage ) {
owner . setSlaveMap ( BLOCK_TO_ITEM , new HashMap <> ());
2017-06-19 15:02:18 -07:00
}
}
2024-04-30 10:56:00 -07:00
private static class AttributeCallbacks implements ValidateCallback < Attribute > {
2020-06-23 20:56:24 -04:00
static final AttributeCallbacks INSTANCE = new AttributeCallbacks ();
2017-06-19 15:02:18 -07:00
@Override
2025-12-09 18:18:19 -08:00
public void onValidate ( IForgeRegistryInternal < Attribute > owner , RegistryManager stage , int id , Identifier key , Attribute obj ) {
2020-10-18 13:37:28 -04:00
// some stuff hard patched in can cause this to derp if it's JUST vanilla, so skip
2024-11-20 19:32:28 -08:00
if ( stage != RegistryManager . VANILLA )
DefaultAttributes . validate ();
2017-06-19 15:02:18 -07:00
}
}
2024-04-30 10:56:00 -07:00
public static class PoiTypeCallbacks implements AddCallback < PoiType > , ClearCallback < PoiType > , CreateCallback < PoiType > {
static final PoiTypeCallbacks INSTANCE = new PoiTypeCallbacks ();
private static final SlaveKey < Map < BlockState , Holder < PoiType >>> STATE_TO_POI = SlaveKey . create ( "state_to_poi" );
public static Map < BlockState , Holder < PoiType >> getStateToPoi () {
return RegistryManager . ACTIVE . getRegistry ( Keys . POI_TYPES ). getSlaveMap ( STATE_TO_POI );
}
2021-02-15 17:24:35 -08:00
2020-12-28 15:17:51 -05:00
@Override
2024-04-30 10:56:00 -07:00
public void onAdd ( IForgeRegistryInternal < PoiType > owner , RegistryManager stage , int id , ResourceKey < PoiType > key , PoiType obj , @Nullable PoiType oldObj ) {
var map = owner . getSlaveMap ( STATE_TO_POI );
2024-11-20 19:32:28 -08:00
if ( oldObj != null )
2022-06-07 15:31:26 -05:00
oldObj . matchingStates (). forEach ( map :: remove );
2024-04-30 10:56:00 -07:00
var holder = owner . getHolder ( obj ). orElse ( null );
2024-11-20 19:32:28 -08:00
if ( holder == null )
2024-04-30 10:56:00 -07:00
throw new IllegalStateException ( "Could not get holder for " + key + " " + obj );
2024-08-16 22:49:11 +01:00
for ( BlockState state : obj . matchingStates ()) {
2024-04-30 10:56:00 -07:00
var oldType = map . put ( state , holder );
2024-11-20 19:32:28 -08:00
if ( oldType != null )
2022-02-15 18:27:10 +01:00
throw new IllegalStateException ( String . format ( Locale . ENGLISH , "Point of interest types %s and %s both list %s in their blockstates, this is not allowed. Blockstates can only have one point of interest type each." , oldType , obj , state ));
2024-08-16 22:49:11 +01:00
}
2020-12-28 15:17:51 -05:00
}
@Override
2024-04-30 10:56:00 -07:00
public void onClear ( IForgeRegistryInternal < PoiType > owner , RegistryManager stage ) {
owner . getSlaveMap ( STATE_TO_POI ). clear ();
2020-12-28 15:17:51 -05:00
}
@Override
2024-04-30 10:56:00 -07:00
public void onCreate ( IForgeRegistryInternal < PoiType > owner , RegistryManager stage ) {
owner . setSlaveMap ( STATE_TO_POI , new HashMap <> ());
2020-12-28 15:17:51 -05:00
}
}
2021-02-15 17:24:35 -08:00
2025-12-09 18:18:19 -08:00
private static < T > void loadRegistry ( final Identifier registryName , final RegistryManager from , final RegistryManager to , boolean freeze ) {
2017-06-19 15:02:18 -07:00
ForgeRegistry < T > fromRegistry = from . getRegistry ( registryName );
2024-11-20 19:32:28 -08:00
if ( fromRegistry == null ) {
2017-06-19 15:02:18 -07:00
ForgeRegistry < T > toRegistry = to . getRegistry ( registryName );
2024-11-20 19:32:28 -08:00
if ( toRegistry == null ) {
2017-06-19 15:02:18 -07:00
throw new EnhancedRuntimeException ( "Could not find registry to load: " + registryName ){
private static final long serialVersionUID = 1L ;
@Override
2024-11-20 19:32:28 -08:00
protected void printStackTrace ( WrappedPrintStream stream ) {
2017-06-19 15:02:18 -07:00
stream . println ( "Looking For: " + registryName );
stream . println ( "Found From:" );
2025-12-09 18:18:19 -08:00
for ( Identifier name : from . registries . keySet ())
2017-06-19 15:02:18 -07:00
stream . println ( " " + name );
stream . println ( "Found To:" );
2025-12-09 18:18:19 -08:00
for ( Identifier name : to . registries . keySet ())
2017-06-19 15:02:18 -07:00
stream . println ( " " + name );
}
};
}
// We found it in to, so lets trust to's state...
// This happens when connecting to a server that doesn't have this registry.
// Such as a 1.8.0 Forge server with 1.8.8+ Forge.
// We must however, re-fire the callbacks as some internal data may be corrupted {potions}
//TODO: With my rework of how registries add callbacks are done.. I don't think this is necessary.
//fire addCallback for each entry
2024-11-20 19:32:28 -08:00
} else {
2017-06-19 15:02:18 -07:00
ForgeRegistry < T > toRegistry = to . getRegistry ( registryName , from );
toRegistry . sync ( registryName , fromRegistry );
if ( freeze )
toRegistry . isFrozen = true ;
}
}
2025-12-09 18:18:19 -08:00
public static Multimap < Identifier , Identifier > injectSnapshot ( Map < Identifier , ForgeRegistry . Snapshot > snapshot , boolean injectFrozenData , boolean isLocalWorld ) {
2019-01-27 00:06:57 -05:00
LOGGER . info ( REGISTRIES , "Injecting existing registry data into this {} instance" , EffectiveSide . get ());
2017-06-19 15:02:18 -07:00
RegistryManager . ACTIVE . registries . forEach (( name , reg ) -> reg . validateContent ( name ));
RegistryManager . ACTIVE . registries . forEach (( name , reg ) -> reg . dump ( name ));
2026-06-16 11:18:58 -07:00
RegistryManager . ACTIVE . registries . forEach (( _ , reg ) -> reg . resetDelegates ());
2020-06-19 10:58:45 -04:00
2019-06-09 03:23:00 -04:00
// Update legacy names
snapshot = snapshot . entrySet (). stream ()
. sorted ( Map . Entry . comparingByKey ()) // FIXME Registries need dependency ordering, this makes sure blocks are done before items (for ItemCallbacks) but it's lazy as hell
2026-06-16 11:18:58 -07:00
. collect ( Collectors . toMap ( e -> RegistryManager . ACTIVE . updateLegacyName ( e . getKey ()), Map . Entry :: getValue , ( k1 , _ ) -> k1 , LinkedHashMap :: new ));
2017-06-19 15:02:18 -07:00
2024-11-20 19:32:28 -08:00
if ( isLocalWorld ) {
2025-12-09 18:18:19 -08:00
Identifier [] missingRegs = snapshot . keySet (). stream (). filter ( name -> ! RegistryManager . ACTIVE . registries . containsKey ( name )). toArray ( Identifier [] :: new );
2023-10-14 18:14:03 +01:00
if ( missingRegs . length > 0 )
2019-03-12 04:34:29 +00:00
{
2020-05-23 16:00:22 -04:00
String header = "Forge Mod Loader detected missing/unknown registrie(s).\n\n" +
2023-10-14 18:14:03 +01:00
"There are " + missingRegs . length + " missing registries in this save.\n" +
2019-03-12 04:34:29 +00:00
"If you continue the missing registries will get removed.\n" +
2020-05-23 16:00:22 -04:00
"This may cause issues, it is advised that you create a world backup before continuing.\n\n" ;
StringBuilder text = new StringBuilder ( "Missing Registries:\n" );
2017-06-19 15:02:18 -07:00
2025-12-09 18:18:19 -08:00
for ( Identifier s : missingRegs )
2020-05-23 16:00:22 -04:00
text . append ( s ). append ( "\n" );
2020-11-10 11:13:38 -08:00
LOGGER . warn ( REGISTRIES , header );
LOGGER . warn ( REGISTRIES , text . toString ());
2019-03-12 04:34:29 +00:00
}
2017-06-19 15:02:18 -07:00
}
2022-04-13 23:04:04 -07:00
RegistryManager STAGING = new RegistryManager ();
2017-06-19 15:02:18 -07:00
2025-12-09 18:18:19 -08:00
final Map < Identifier , Map < Identifier , IdMappingEvent . IdRemapping >> remaps = new HashMap <> ();
final LinkedHashMap < Identifier , Object2IntMap < Identifier >> missing = new LinkedHashMap <> ();
2017-06-19 15:02:18 -07:00
// Load the snapshot into the "STAGING" registry
2024-11-20 19:32:28 -08:00
snapshot . forEach (( key , value ) -> {
2023-10-14 18:14:03 +01:00
remaps . put ( key , new LinkedHashMap <> ());
missing . put ( key , new Object2IntLinkedOpenHashMap <> ());
2022-06-07 15:31:26 -05:00
loadPersistentDataToStagingRegistry ( RegistryManager . ACTIVE , STAGING , remaps . get ( key ), missing . get ( key ), key , value );
2017-06-19 15:02:18 -07:00
});
2017-06-28 00:14:10 -07:00
int count = missing . values (). stream (). mapToInt ( Map :: size ). sum ();
2024-11-20 19:32:28 -08:00
if ( count > 0 ) {
2019-01-27 00:06:57 -05:00
LOGGER . debug ( REGISTRIES , "There are {} mappings missing - attempting a mod remap" , count );
2025-12-09 18:18:19 -08:00
Multimap < Identifier , Identifier > defaulted = ArrayListMultimap . create ();
Multimap < Identifier , Identifier > failed = ArrayListMultimap . create ();
2017-06-19 15:02:18 -07:00
2024-11-20 19:32:28 -08:00
missing . entrySet (). stream (). filter ( e -> ! e . getValue (). isEmpty ()). forEach ( m -> {
2025-12-09 18:18:19 -08:00
Identifier name = m . getKey ();
2017-06-19 15:02:18 -07:00
ForgeRegistry <?> reg = STAGING . getRegistry ( name );
2025-12-09 18:18:19 -08:00
Object2IntMap < Identifier > missingIds = m . getValue ();
2023-10-14 18:14:03 +01:00
MissingMappingsEvent event = reg . getMissingEvent ( name , missingIds );
2025-06-18 02:34:13 -07:00
MissingMappingsEvent . BUS . post ( event );
2017-06-19 15:02:18 -07:00
2022-06-07 15:31:26 -05:00
List < MissingMappingsEvent . Mapping <?>> lst = event . getAllMappings ( reg . getRegistryKey ()). stream ()
. filter ( e -> e . action == MissingMappingsEvent . Action . DEFAULT )
. sorted ( Comparator . comparing ( Object :: toString ))
. collect ( Collectors . toList ());
2024-11-20 19:32:28 -08:00
if ( ! lst . isEmpty ()) {
2022-06-07 15:31:26 -05:00
LOGGER . error ( REGISTRIES , () -> LogMessageAdapter . adapt ( sb -> {
2019-02-23 20:42:45 -05:00
sb . append ( "Unidentified mapping from registry " ). append ( name ). append ( '\n' );
2022-06-07 15:31:26 -05:00
lst . stream (). sorted (). forEach ( map -> sb . append ( '\t' ). append ( map . key ). append ( ": " ). append ( map . id ). append ( '\n' ));
2019-01-27 00:06:57 -05:00
}));
2017-06-19 15:02:18 -07:00
}
2022-06-07 15:31:26 -05:00
event . getAllMappings ( reg . getRegistryKey ()). stream ()
. filter ( e -> e . action == MissingMappingsEvent . Action . FAIL )
. forEach ( fail -> failed . put ( name , fail . key ));
2017-06-19 15:02:18 -07:00
2023-10-14 18:14:03 +01:00
processMissing ( name , STAGING , event , missingIds , remaps . get ( name ), defaulted . get ( name ), failed . get ( name ), ! isLocalWorld );
2017-06-19 15:02:18 -07:00
});
if ( ! defaulted . isEmpty () && ! isLocalWorld )
return defaulted ;
2024-11-20 19:32:28 -08:00
if ( ! defaulted . isEmpty ()) {
2020-05-23 16:00:22 -04:00
String header = "Forge Mod Loader detected missing registry entries.\n\n" +
"There are " + defaulted . size () + " missing entries in this save.\n" +
"If you continue the missing entries will get removed.\n" +
"A world backup will be automatically created in your saves directory.\n\n" ;
2017-06-19 15:02:18 -07:00
2020-05-23 16:00:22 -04:00
StringBuilder buf = new StringBuilder ();
2024-11-20 19:32:28 -08:00
defaulted . asMap (). forEach (( name , entries ) -> {
2017-06-19 15:02:18 -07:00
buf . append ( "Missing " ). append ( name ). append ( ":\n" );
2025-12-09 18:18:19 -08:00
entries . stream (). sorted ( Identifier :: compareNamespaced ). forEach ( rl -> buf . append ( " " ). append ( rl ). append ( "\n" ));
2020-05-23 16:00:22 -04:00
buf . append ( "\n" );
2017-06-19 15:02:18 -07:00
});
2020-11-10 11:13:38 -08:00
LOGGER . warn ( REGISTRIES , header );
LOGGER . warn ( REGISTRIES , buf . toString ());
2017-06-19 15:02:18 -07:00
}
2024-11-20 19:32:28 -08:00
if ( ! defaulted . isEmpty ()) {
2017-06-19 15:02:18 -07:00
if ( isLocalWorld )
2019-01-27 00:06:57 -05:00
LOGGER . error ( REGISTRIES , "There are unidentified mappings in this world - we are going to attempt to process anyway" );
2017-06-19 15:02:18 -07:00
}
}
2024-11-20 19:32:28 -08:00
if ( injectFrozenData ) {
2017-06-19 15:02:18 -07:00
// If we're loading up the world from disk, we want to add in the new data that might have been provisioned by mods
// So we load it from the frozen persistent registry
2026-06-16 11:18:58 -07:00
RegistryManager . ACTIVE . registries . forEach (( name , _ ) -> {
2022-06-07 15:31:26 -05:00
loadFrozenDataToStagingRegistry ( STAGING , name , remaps . get ( name ));
2017-06-19 15:02:18 -07:00
});
}
// Validate that all the STAGING data is good
2017-06-20 18:39:55 -07:00
STAGING . registries . forEach (( name , reg ) -> reg . validateContent ( name ));
2017-06-19 15:02:18 -07:00
// Load the STAGING registry into the ACTIVE registry
2025-12-09 18:18:19 -08:00
//for (Map.Entry<Identifier, IForgeRegistry<?>>> r : RegistryManager.ACTIVE.registries.entrySet())
2026-06-16 11:18:58 -07:00
RegistryManager . ACTIVE . registries . forEach (( key , _ ) -> {
2022-06-07 15:31:26 -05:00
loadRegistry ( key , STAGING , RegistryManager . ACTIVE , true );
2018-09-05 20:06:18 -07:00
});
2017-06-19 15:02:18 -07:00
2019-01-11 23:52:30 +01:00
RegistryManager . ACTIVE . registries . forEach (( name , reg ) -> {
reg . bake ();
// Dump the active registry
reg . dump ( name );
});
2017-06-19 15:02:18 -07:00
// Tell mods that the ids have changed
2019-03-25 21:41:28 -04:00
fireRemapEvent ( remaps , false );
2017-06-19 15:02:18 -07:00
2025-10-12 10:49:47 -07:00
// The id map changed, ensure we apply object holders
ObjectHolderRegistry . applyObjectHolders ();
2017-06-19 15:02:18 -07:00
// Return an empty list, because we're good
return ArrayListMultimap . create ();
}
2025-12-09 18:18:19 -08:00
private static void fireRemapEvent ( final Map < Identifier , Map < Identifier , IdMappingEvent . IdRemapping >> remaps , final boolean isFreezing ) {
2025-06-18 02:34:13 -07:00
IdMappingEvent . BUS . post ( new IdMappingEvent ( remaps , isFreezing ));
2019-03-25 21:41:28 -04:00
}
2017-06-19 15:02:18 -07:00
//Has to be split because of generics, Yay!
2025-12-09 18:18:19 -08:00
private static < T > void loadPersistentDataToStagingRegistry ( RegistryManager pool , RegistryManager to , Map < Identifier , IdMappingEvent . IdRemapping > remaps , Object2IntMap < Identifier > missing , Identifier name , ForgeRegistry . Snapshot snap ) {
2017-06-19 15:02:18 -07:00
ForgeRegistry < T > active = pool . getRegistry ( name );
if ( active == null )
return ; // We've already asked the user if they wish to continue. So if the reg isnt found just assume the user knows and accepted it.
ForgeRegistry < T > _new = to . getRegistry ( name , RegistryManager . ACTIVE );
2017-06-28 00:14:10 -07:00
snap . aliases . forEach ( _new :: addAlias );
snap . blocked . forEach ( _new :: block );
2017-06-23 00:58:00 -07:00
_new . loadIds ( snap . ids , snap . overrides , missing , remaps , active , name );
2017-06-19 15:02:18 -07:00
}
//Another bouncer for generic reasons
2025-12-09 18:18:19 -08:00
private static < T > void processMissing ( Identifier name , RegistryManager STAGING , MissingMappingsEvent e , Object2IntMap < Identifier > missing , Map < Identifier , IdMappingEvent . IdRemapping > remaps , Collection < Identifier > defaulted , Collection < Identifier > failed , boolean injectNetworkDummies ) {
2022-06-07 15:31:26 -05:00
List < MissingMappingsEvent . Mapping < T >> mappings = e . getAllMappings ( ResourceKey . createRegistryKey ( name ));
2017-06-19 15:02:18 -07:00
ForgeRegistry < T > active = RegistryManager . ACTIVE . getRegistry ( name );
ForgeRegistry < T > staging = STAGING . getRegistry ( name );
2017-08-15 05:10:07 -07:00
staging . processMissingEvent ( name , active , mappings , missing , remaps , defaulted , failed , injectNetworkDummies );
2017-06-19 15:02:18 -07:00
}
2025-12-09 18:18:19 -08:00
private static < T > void loadFrozenDataToStagingRegistry ( RegistryManager STAGING , Identifier name , Map < Identifier , IdMappingEvent . IdRemapping > remaps ) {
2017-06-19 15:02:18 -07:00
ForgeRegistry < T > frozen = RegistryManager . FROZEN . getRegistry ( name );
ForgeRegistry < T > newRegistry = STAGING . getRegistry ( name , RegistryManager . FROZEN );
2025-12-09 18:18:19 -08:00
Object2IntMap < Identifier > _new = new Object2IntLinkedOpenHashMap <> ();
2017-06-19 15:02:18 -07:00
frozen . getKeys (). stream (). filter ( key -> ! newRegistry . containsKey ( key )). forEach ( key -> _new . put ( key , frozen . getID ( key )));
2023-10-14 18:14:03 +01:00
newRegistry . loadIds ( _new , frozen . getOverrideOwners (), new Object2IntLinkedOpenHashMap <> (), remaps , frozen , name );
2017-06-19 15:02:18 -07:00
}
2018-12-02 00:46:19 +00:00
/**
* Check a name for a domain prefix, and if not present infer it from the
* current active mod container.
2019-05-23 16:02:15 -07:00
*
2018-12-02 00:46:19 +00:00
* @param name The name or resource location
* @param warnOverrides If true, logs a warning if domain differs from that of
* the currently currently active mod container
2019-05-23 16:02:15 -07:00
*
2025-12-09 18:18:19 -08:00
* @return The {@link Identifier} with given or inferred domain
2018-12-02 00:46:19 +00:00
*/
2025-12-09 18:18:19 -08:00
public static Identifier checkPrefix ( String name , boolean warnOverrides ) {
2018-05-17 06:52:32 +01:00
int index = name . lastIndexOf ( ':' );
String oldPrefix = index == - 1 ? "" : name . substring ( 0 , index ). toLowerCase ( Locale . ROOT );
name = index == - 1 ? name : name . substring ( index + 1 );
2024-11-14 14:23:00 -08:00
@SuppressWarnings ( "removal" )
2019-08-04 20:16:01 -04:00
String prefix = ModLoadingContext . get (). getActiveNamespace ();
2024-11-20 19:32:28 -08:00
if ( warnOverrides && ! oldPrefix . equals ( prefix ) && ! oldPrefix . isEmpty ()) {
2022-06-07 15:31:26 -05:00
LogManager . getLogger (). debug ( "Mod `{}` attempting to register `{}` to the namespace `{}`. This could be intended, but likely means an EventBusSubscriber without a modid." , prefix , name , oldPrefix );
2018-05-17 06:52:32 +01:00
prefix = oldPrefix ;
}
2025-12-09 18:18:19 -08:00
return Identifier . fromNamespaceAndPath ( prefix , name );
2018-05-17 06:52:32 +01:00
}
2017-06-19 15:02:18 -07:00
}