2017-06-26 03:45:48 +00:00
/ *
* Minecraft Forge
* Copyright ( c ) 2016 .
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2 . 1
* of the License .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
* /
2017-06-19 22:02:18 +00:00
package net.minecraftforge.registries ;
import java.util.BitSet ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.Iterator ;
import java.util.List ;
import java.util.Map ;
import java.util.Map.Entry ;
2018-01-18 20:46:05 +00:00
import javax.annotation.Nonnull ;
2017-06-19 22:02:18 +00:00
import javax.annotation.Nullable ;
import org.apache.commons.lang3.Validate ;
import java.util.Set ;
import com.google.common.base.Preconditions ;
import com.google.common.collect.ArrayListMultimap ;
import com.google.common.collect.BiMap ;
import com.google.common.collect.HashBiMap ;
import com.google.common.collect.ImmutableList ;
import com.google.common.collect.ImmutableSet ;
import com.google.common.collect.Lists ;
import com.google.common.collect.Maps ;
import com.google.common.collect.Multimap ;
import com.google.common.collect.Sets ;
2018-04-30 00:58:02 +00:00
import net.minecraft.nbt.NBTTagCompound ;
import net.minecraft.nbt.NBTTagList ;
import net.minecraft.nbt.NBTTagString ;
2017-06-19 22:02:18 +00:00
import net.minecraft.util.ResourceLocation ;
import net.minecraftforge.event.RegistryEvent ;
import net.minecraftforge.event.RegistryEvent.MissingMappings ;
2017-06-23 07:58:00 +00:00
import net.minecraftforge.fml.common.FMLContainer ;
2017-06-19 22:02:18 +00:00
import net.minecraftforge.fml.common.FMLLog ;
2017-06-23 07:58:00 +00:00
import net.minecraftforge.fml.common.InjectedModContainer ;
import net.minecraftforge.fml.common.Loader ;
import net.minecraftforge.fml.common.ModContainer ;
2017-06-19 22:02:18 +00:00
import net.minecraftforge.fml.relauncher.ReflectionHelper ;
public class ForgeRegistry < V extends IForgeRegistryEntry < V > > implements IForgeRegistryInternal < V > , IForgeRegistryModifiable < V >
{
public static final boolean DEBUG = Boolean . parseBoolean ( System . getProperty ( " forge.debugRegistryEntries " , " false " ) ) ;
private final RegistryManager stage ;
private final BiMap < Integer , V > ids = HashBiMap . create ( ) ;
private final BiMap < ResourceLocation , V > names = HashBiMap . create ( ) ;
private final Class < V > superType ;
private final Map < ResourceLocation , ResourceLocation > aliases = Maps . newHashMap ( ) ;
2017-06-21 01:39:55 +00:00
final Map < ResourceLocation , ? > slaves = Maps . newHashMap ( ) ;
2017-06-19 22:02:18 +00:00
private final ResourceLocation defaultKey ;
private final CreateCallback < V > create ;
private final AddCallback < V > add ;
private final ClearCallback < V > clear ;
2017-08-15 12:10:07 +00:00
private final MissingFactory < V > missing ;
2017-06-19 22:02:18 +00:00
private final BitSet availabilityMap ;
private final Set < ResourceLocation > dummies = Sets . newHashSet ( ) ;
private final Set < Integer > blocked = Sets . newHashSet ( ) ;
2017-06-23 07:58:00 +00:00
private final Multimap < ResourceLocation , V > overrides = ArrayListMultimap . create ( ) ;
2017-06-26 03:45:48 +00:00
private final BiMap < OverrideOwner , V > owners = HashBiMap . create ( ) ;
2017-06-19 22:02:18 +00:00
private final DummyFactory < V > dummyFactory ;
private final boolean isDelegated ;
private final int min ;
private final int max ;
private final boolean allowOverrides ;
private final boolean isModifiable ;
private V defaultValue = null ;
boolean isFrozen = false ;
2017-08-15 12:10:07 +00:00
ForgeRegistry ( Class < V > superType , ResourceLocation defaultKey , int min , int max , @Nullable CreateCallback < V > create , @Nullable AddCallback < V > add , @Nullable ClearCallback < V > clear , RegistryManager stage , boolean allowOverrides , boolean isModifiable , @Nullable DummyFactory < V > dummyFactory , @Nullable MissingFactory < V > missing )
2017-06-19 22:02:18 +00:00
{
this . stage = stage ;
this . superType = superType ;
this . defaultKey = defaultKey ;
this . min = min ;
this . max = max ;
this . availabilityMap = new BitSet ( Math . min ( max + 1 , 0x0FFF ) ) ;
this . create = create ;
this . add = add ;
this . clear = clear ;
2017-08-15 12:10:07 +00:00
this . missing = missing ;
2017-06-19 22:02:18 +00:00
this . isDelegated = IForgeRegistryEntry . Impl . class . isAssignableFrom ( superType ) ; //TODO: Make this IDelegatedRegistryEntry?
this . allowOverrides = allowOverrides ;
this . isModifiable = isModifiable ;
this . dummyFactory = dummyFactory ;
if ( this . create ! = null )
this . create . onCreate ( this , stage ) ;
}
@Override
public void register ( V value )
{
add ( - 1 , value ) ;
}
@Override
public Iterator < V > iterator ( ) {
return new Iterator < V > ( )
{
2017-06-24 04:52:27 +00:00
int cur = - 1 ;
V next = null ;
{ next ( ) ; }
2017-06-19 22:02:18 +00:00
@Override
public boolean hasNext ( )
{
2017-06-24 04:52:27 +00:00
return next ! = null ;
2017-06-19 22:02:18 +00:00
}
@Override
public V next ( )
{
2017-06-24 04:52:27 +00:00
V ret = next ;
do {
2017-06-26 03:45:48 +00:00
cur = availabilityMap . nextSetBit ( cur + 1 ) ;
next = ids . get ( cur ) ;
2017-06-24 04:52:27 +00:00
} while ( next = = null & & cur ! = - 1 ) ; // nextSetBit returns -1 when none is found
return ret ;
2017-06-19 22:02:18 +00:00
}
//TODO add remove support?
} ;
}
@Override
public Class < V > getRegistrySuperType ( )
{
return superType ;
}
@Override
public void registerAll ( @SuppressWarnings ( " unchecked " ) V . . . values )
{
for ( V value : values )
register ( value ) ;
}
@Override
public boolean containsKey ( ResourceLocation key )
{
while ( key ! = null )
{
if ( this . names . containsKey ( key ) )
return true ;
key = this . aliases . get ( key ) ;
}
return false ;
}
@Override
public boolean containsValue ( V value )
{
return this . names . containsValue ( value ) ;
}
@Override
public V getValue ( ResourceLocation key )
{
V ret = this . names . get ( key ) ;
key = this . aliases . get ( key ) ;
while ( ret = = null & & key ! = null )
{
ret = this . names . get ( key ) ;
key = this . aliases . get ( key ) ;
}
return ret = = null ? this . defaultValue : ret ;
}
@Override
public ResourceLocation getKey ( V value )
{
return this . names . inverse ( ) . get ( value ) ;
}
@Override
public Set < ResourceLocation > getKeys ( )
{
2018-01-18 20:46:05 +00:00
return Collections . unmodifiableSet ( this . names . keySet ( ) ) ;
2017-06-19 22:02:18 +00:00
}
2018-01-18 20:46:05 +00:00
/ * *
* @deprecated use { @link # getValuesCollection } to avoid copying
* /
@Deprecated
2017-06-19 22:02:18 +00:00
@Override
public List < V > getValues ( )
{
return ImmutableList . copyOf ( this . names . values ( ) ) ;
}
2018-01-18 20:46:05 +00:00
@Nonnull
@Override
public Collection < V > getValuesCollection ( )
{
return Collections . unmodifiableSet ( this . names . values ( ) ) ;
}
2017-06-19 22:02:18 +00:00
@Override
public Set < Entry < ResourceLocation , V > > getEntries ( )
{
2018-01-18 20:46:05 +00:00
return Collections . unmodifiableSet ( this . names . entrySet ( ) ) ;
2017-06-19 22:02:18 +00:00
}
@SuppressWarnings ( " unchecked " )
@Override
public < T > T getSlaveMap ( ResourceLocation name , Class < T > type )
{
return ( T ) this . slaves . get ( name ) ;
}
@SuppressWarnings ( " unchecked " )
@Override
public void setSlaveMap ( ResourceLocation name , Object obj )
{
( ( Map < ResourceLocation , Object > ) this . slaves ) . put ( name , obj ) ;
}
public int getID ( V value )
{
Integer ret = this . ids . inverse ( ) . get ( value ) ;
if ( ret = = null & & this . defaultValue ! = null )
ret = this . ids . inverse ( ) . get ( this . defaultValue ) ;
return ret = = null ? - 1 : ret . intValue ( ) ;
}
public int getID ( ResourceLocation name )
{
return getID ( this . names . get ( name ) ) ;
}
2017-06-21 06:45:32 +00:00
private int getIDRaw ( V value )
{
Integer ret = this . ids . inverse ( ) . get ( value ) ;
return ret = = null ? - 1 : ret . intValue ( ) ;
}
private int getIDRaw ( ResourceLocation name )
{
return getIDRaw ( this . names . get ( name ) ) ;
}
2017-06-19 22:02:18 +00:00
public V getValue ( int id )
{
V ret = this . ids . get ( id ) ;
return ret = = null ? this . defaultValue : ret ;
}
void validateKey ( )
{
if ( this . defaultKey ! = null )
Validate . notNull ( this . defaultValue , " Missing default of ForgeRegistry: " + this . defaultKey + " Type: " + this . superType ) ;
}
ForgeRegistry < V > copy ( RegistryManager stage )
{
2017-08-15 12:10:07 +00:00
return new ForgeRegistry < V > ( superType , defaultKey , min , max , create , add , clear , stage , allowOverrides , isModifiable , dummyFactory , missing ) ;
2017-06-19 22:02:18 +00:00
}
int add ( int id , V value )
2017-06-23 07:58:00 +00:00
{
ModContainer mc = Loader . instance ( ) . activeModContainer ( ) ;
String owner = mc = = null | | ( mc instanceof InjectedModContainer & & ( ( InjectedModContainer ) mc ) . wrappedContainer instanceof FMLContainer ) ? null : mc . getModId ( ) . toLowerCase ( ) ;
return add ( id , value , owner ) ;
}
int add ( int id , V value , String owner )
2017-06-19 22:02:18 +00:00
{
ResourceLocation key = value = = null ? null : value . getRegistryName ( ) ;
Preconditions . checkNotNull ( key , " Can't use a null-name for the registry, object %s. " , value ) ;
Preconditions . checkNotNull ( value , " Can't add null-object to the registry, name %s. " , key ) ;
int idToUse = id ;
if ( idToUse < 0 | | availabilityMap . get ( idToUse ) )
idToUse = availabilityMap . nextClearBit ( min ) ;
if ( idToUse > max )
throw new RuntimeException ( String . format ( " Invalid id %d - maximum id range exceeded. " , idToUse ) ) ;
V oldEntry = getRaw ( key ) ;
if ( oldEntry = = value ) // already registered, return prev registration's id
{
2017-06-26 03:45:48 +00:00
FMLLog . bigWarning ( " Registry {}: The object {} has been registered twice for the same name {}. " , this . superType . getSimpleName ( ) , value , key ) ;
2017-06-19 22:02:18 +00:00
return this . getID ( value ) ;
}
if ( oldEntry ! = null ) // duplicate name
{
if ( ! this . allowOverrides )
throw new IllegalArgumentException ( String . format ( " The name %s has been registered twice, for %s and %s. " , key , getRaw ( key ) , value ) ) ;
2017-06-23 07:58:00 +00:00
if ( owner = = null )
throw new IllegalStateException ( String . format ( " Could not determine owner for the override on %s. Value: %s " , key , value ) ) ;
2017-06-26 03:45:48 +00:00
if ( DEBUG )
FMLLog . log . debug ( " Registry {} Override: {} {} -> {} " , this . superType . getSimpleName ( ) , key , oldEntry , value ) ;
2017-06-19 22:02:18 +00:00
idToUse = this . getID ( oldEntry ) ;
}
Integer foundId = this . ids . inverse ( ) . get ( value ) ; //Is this ever possible to trigger with otherThing being different?
if ( foundId ! = null )
{
V otherThing = this . ids . get ( foundId ) ;
throw new IllegalArgumentException ( String . format ( " The object %s{%x} has been registered twice, using the names %s and %s. (Other object at this id is %s{%x}) " , value , System . identityHashCode ( value ) , getKey ( value ) , key , otherThing , System . identityHashCode ( otherThing ) ) ) ;
}
if ( isLocked ( ) )
throw new IllegalStateException ( String . format ( " The object %s (name %s) is being added too late. " , value , key ) ) ;
if ( defaultKey ! = null & & defaultKey . equals ( key ) )
{
if ( this . defaultValue ! = null )
throw new IllegalStateException ( String . format ( " Attemped to override already set default value. This is not allowed: The object %s (name %s) " , value , key ) ) ;
this . defaultValue = value ;
}
this . names . put ( key , value ) ;
this . ids . put ( idToUse , value ) ;
this . availabilityMap . set ( idToUse ) ;
2017-06-26 03:45:48 +00:00
this . owners . put ( new OverrideOwner ( owner = = null ? key . getResourceDomain ( ) : owner , key ) , value ) ;
2017-06-19 22:02:18 +00:00
if ( isDelegated )
{
getDelegate ( value ) . setName ( key ) ;
if ( oldEntry ! = null )
{
2017-06-26 03:45:48 +00:00
if ( ! this . overrides . get ( key ) . contains ( oldEntry ) )
this . overrides . put ( key , oldEntry ) ;
this . overrides . get ( key ) . remove ( value ) ;
2017-06-19 22:02:18 +00:00
if ( this . stage = = RegistryManager . ACTIVE )
getDelegate ( oldEntry ) . changeReference ( value ) ;
}
}
if ( this . add ! = null )
2017-06-26 03:45:48 +00:00
this . add . onAdd ( this , this . stage , idToUse , value , oldEntry ) ;
2017-06-19 22:02:18 +00:00
if ( this . dummies . remove ( key ) & & DEBUG )
2017-06-26 03:45:48 +00:00
FMLLog . log . debug ( " Registry {} Dummy Remove: {} " , this . superType . getSimpleName ( ) , key ) ;
2017-06-19 22:02:18 +00:00
if ( DEBUG )
2017-06-26 03:45:48 +00:00
FMLLog . log . trace ( " Registry {} add: {} {} {} (req. id {}) " , this . superType . getSimpleName ( ) , key , idToUse , value , id ) ;
2017-06-19 22:02:18 +00:00
return idToUse ;
}
private V getRaw ( ResourceLocation key )
{
V ret = this . names . get ( key ) ;
key = this . aliases . get ( key ) ;
while ( ret = = null & & key ! = null )
{
ret = this . names . get ( key ) ;
key = this . aliases . get ( key ) ;
}
return ret ;
}
@Deprecated //Public for ByteByfUtils only!
public V getRaw ( int id )
{
return this . ids . get ( id ) ;
}
void addAlias ( ResourceLocation from , ResourceLocation to )
{
if ( this . isLocked ( ) )
throw new IllegalStateException ( String . format ( " Attempted to register the alias %s -> %s to late " , from , to ) ) ;
this . aliases . put ( from , to ) ;
if ( DEBUG )
2017-06-26 03:45:48 +00:00
FMLLog . log . trace ( " Registry {} alias: {} -> {} " , this . superType . getSimpleName ( ) , from , to ) ;
2017-06-19 22:02:18 +00:00
}
void addDummy ( ResourceLocation key )
{
if ( this . isLocked ( ) )
throw new IllegalStateException ( String . format ( " Attempted to register the dummy %s to late " , key ) ) ;
this . dummies . add ( key ) ;
if ( DEBUG )
2017-06-26 03:45:48 +00:00
FMLLog . log . trace ( " Registry {} dummy: {} " , this . superType . getSimpleName ( ) , key ) ;
2017-06-19 22:02:18 +00:00
}
private RegistryDelegate < V > getDelegate ( V thing )
{
if ( isDelegated )
return ( RegistryDelegate < V > ) ( ( IForgeRegistryEntry . Impl < V > ) thing ) . delegate ;
else
throw new IllegalStateException ( " Tried to get existing delegate from registry that is not delegated. " ) ;
}
void resetDelegates ( )
{
if ( ! this . isDelegated )
return ;
for ( V value : this )
getDelegate ( value ) . changeReference ( value ) ;
2017-06-23 07:58:00 +00:00
for ( V value : this . overrides . values ( ) )
getDelegate ( value ) . changeReference ( value ) ;
2017-06-19 22:02:18 +00:00
}
V getDefault ( )
{
return this . defaultValue ;
}
boolean isDummied ( ResourceLocation key )
{
return this . dummies . contains ( key ) ;
}
void validateContent ( ResourceLocation registryName )
{
try
{
ReflectionHelper . findMethod ( BitSet . class , " trimToSize " , null ) . invoke ( this . availabilityMap ) ;
}
catch ( Exception e )
{
//We don't care... Just a micro-optimization
}
for ( V obj : this )
{
int id = getID ( obj ) ;
ResourceLocation name = getKey ( obj ) ;
// name lookup failed -> obj is not in the obj<->name map
if ( name = = null )
throw new IllegalStateException ( String . format ( " Registry entry for %s %s, id %d, doesn't yield a name. " , registryName , obj , id ) ) ;
// id is too high
if ( id > max )
throw new IllegalStateException ( String . format ( " Registry entry for %s %s, name %s uses the too large id %d. " , registryName , obj , name , id ) ) ;
// id -> obj lookup is inconsistent
if ( getValue ( id ) ! = obj )
throw new IllegalStateException ( String . format ( " Registry entry for id %d, name %s, doesn't yield the expected %s %s. " , id , name , registryName , obj ) ) ;
// name -> obj lookup is inconsistent
if ( getValue ( name ) ! = obj )
throw new IllegalStateException ( String . format ( " Registry entry for name %s, id %d, doesn't yield the expected %s %s. " , name , id , registryName , obj ) ) ;
// name -> id lookup is inconsistent
if ( getID ( name ) ! = id )
throw new IllegalStateException ( String . format ( " Registry entry for name %s doesn't yield the expected id %d. " , name , id ) ) ;
/ *
// entry is blocked, thus should be empty
if ( blockedIds . contains ( id ) )
throw new IllegalStateException ( String . format ( " Registry entry for %s %s, id %d, name %s, marked as dangling. " , registryName , obj , id , name ) ) ;
* /
}
}
void sync ( ResourceLocation name , ForgeRegistry < V > from )
{
2017-06-26 03:45:48 +00:00
if ( DEBUG )
FMLLog . log . debug ( " Registry {} Sync: {} -> {} " , this . superType . getSimpleName ( ) , this . stage . getName ( ) , from . stage . getName ( ) ) ;
if ( this = = from )
throw new IllegalArgumentException ( " WTF We are the same!?!?! " ) ;
2017-06-19 22:02:18 +00:00
if ( from . superType ! = this . superType )
throw new IllegalArgumentException ( " Attempted to copy to incompatible registry: " + name + " " + from . superType + " -> " + this . superType ) ;
2017-06-21 06:45:32 +00:00
this . isFrozen = false ;
2017-06-19 22:02:18 +00:00
if ( this . clear ! = null )
this . clear . onClear ( this , stage ) ;
/ * - - Should never need to be copied
this . defaultKey = from . defaultKey ;
this . max = from . max ;
this . min = from . min ;
* /
this . aliases . clear ( ) ;
2017-06-28 07:14:10 +00:00
from . aliases . forEach ( this : : addAlias ) ;
2017-06-19 22:02:18 +00:00
this . ids . clear ( ) ;
this . names . clear ( ) ;
this . availabilityMap . clear ( 0 , this . availabilityMap . length ( ) ) ;
2017-06-21 01:39:55 +00:00
this . defaultValue = null ;
2017-06-26 03:45:48 +00:00
this . overrides . clear ( ) ;
this . owners . clear ( ) ;
2017-06-19 22:02:18 +00:00
boolean errored = false ;
for ( Entry < ResourceLocation , V > entry : from . names . entrySet ( ) )
{
2017-06-23 07:58:00 +00:00
List < V > overrides = Lists . newArrayList ( from . overrides . get ( entry . getKey ( ) ) ) ;
2017-06-21 01:39:55 +00:00
int id = from . getID ( entry . getKey ( ) ) ;
2017-06-23 07:58:00 +00:00
if ( overrides . isEmpty ( ) )
2017-06-19 22:02:18 +00:00
{
2017-06-23 07:58:00 +00:00
int realId = add ( id , entry . getValue ( ) ) ;
if ( id ! = realId & & id ! = - 1 )
{
2017-06-26 03:45:48 +00:00
FMLLog . log . warn ( " Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {} " , this . superType . getSimpleName ( ) , entry . getKey ( ) , id , realId ) ;
2017-06-23 07:58:00 +00:00
errored = true ;
}
}
else
{
overrides . add ( entry . getValue ( ) ) ;
for ( V value : overrides )
{
2017-06-26 03:45:48 +00:00
OverrideOwner owner = from . owners . inverse ( ) . get ( value ) ;
2017-06-23 07:58:00 +00:00
if ( owner = = null )
{
2017-06-26 03:45:48 +00:00
FMLLog . log . warn ( " Registry {}: Override did not have an associated owner object. Name: {} Value: {} " , this . superType . getSimpleName ( ) , entry . getKey ( ) , value ) ;
2017-06-23 07:58:00 +00:00
errored = true ;
continue ;
}
int realId = add ( id , value , owner . owner ) ;
if ( id ! = realId & & id ! = - 1 )
{
2017-06-26 03:45:48 +00:00
FMLLog . log . warn ( " Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {} " , this . superType . getSimpleName ( ) , entry . getKey ( ) , id , realId ) ;
2017-06-23 07:58:00 +00:00
errored = true ;
}
}
2017-06-19 22:02:18 +00:00
}
}
2017-06-21 06:45:32 +00:00
//Needs to be below add so that dummies are persisted
this . dummies . clear ( ) ;
2017-06-28 07:14:10 +00:00
from . dummies . forEach ( this : : addDummy ) ;
2017-06-21 06:45:32 +00:00
2017-06-19 22:02:18 +00:00
if ( errored )
throw new RuntimeException ( " One of more entry values did not copy to the correct id. Check log for details! " ) ;
}
@Override
public void clear ( )
{
if ( ! this . isModifiable )
throw new UnsupportedOperationException ( " Attempted to clear a non-modifiable Forge Registry " ) ;
if ( this . isLocked ( ) )
throw new IllegalStateException ( " Attempted to clear the registry to late. " ) ;
if ( this . clear ! = null )
this . clear . onClear ( this , stage ) ;
this . aliases . clear ( ) ;
this . dummies . clear ( ) ;
this . ids . clear ( ) ;
this . names . clear ( ) ;
this . availabilityMap . clear ( 0 , this . availabilityMap . length ( ) ) ;
}
@Override
public V remove ( ResourceLocation key )
{
if ( ! this . isModifiable )
throw new UnsupportedOperationException ( " Attempted to remove from a non-modifiable Forge Registry " ) ;
if ( this . isLocked ( ) )
throw new IllegalStateException ( " Attempted to remove from the registry to late. " ) ;
V value = this . names . remove ( key ) ;
if ( value ! = null )
{
Integer id = this . ids . inverse ( ) . remove ( value ) ;
if ( id = = null )
throw new IllegalStateException ( " Removed a entry that did not have an associated id: " + key + " " + value . toString ( ) + " This should never happen unless hackery! " ) ;
2017-08-22 09:08:34 +00:00
if ( DEBUG )
FMLLog . log . trace ( " Registry {} remove: {} {} " , this . superType . getSimpleName ( ) , key , id ) ;
2017-06-19 22:02:18 +00:00
}
return value ;
}
void block ( int id )
{
this . blocked . add ( id ) ;
this . availabilityMap . set ( id ) ;
}
@Override
public boolean isLocked ( )
{
return this . isFrozen ;
}
2017-06-21 01:39:55 +00:00
/ * *
* Used to control the times where people can modify this registry .
* Users should only ever register things in the Register < ? > events !
* /
2017-06-19 22:02:18 +00:00
public void freeze ( )
{
this . isFrozen = true ;
}
2017-06-21 01:39:55 +00:00
public void unfreeze ( )
{
this . isFrozen = false ;
}
2017-06-19 22:02:18 +00:00
RegistryEvent . Register < V > getRegisterEvent ( ResourceLocation name )
{
return new RegistryEvent . Register < V > ( name , this ) ;
}
void dump ( ResourceLocation name )
{
List < Integer > ids = Lists . newArrayList ( ) ;
getKeys ( ) . forEach ( n - > ids . add ( getID ( n ) ) ) ;
Collections . sort ( ids ) ;
2017-06-24 08:46:05 +00:00
FMLLog . log . trace ( " Registry Name : {} " , name ) ;
2017-06-26 03:45:48 +00:00
ids . forEach ( id - > FMLLog . log . trace ( " Registry: {} {} {} " , id , getKey ( getValue ( id ) ) , getValue ( id ) ) ) ;
2017-06-19 22:02:18 +00:00
}
2017-06-23 07:58:00 +00:00
public void loadIds ( Map < ResourceLocation , Integer > ids , Map < ResourceLocation , String > overrides , Map < ResourceLocation , Integer > missing , Map < ResourceLocation , Integer [ ] > remapped , ForgeRegistry < V > old , ResourceLocation name )
2017-06-19 22:02:18 +00:00
{
2017-06-26 03:45:48 +00:00
Map < ResourceLocation , String > ovs = Maps . newHashMap ( overrides ) ;
2017-06-19 22:02:18 +00:00
for ( Map . Entry < ResourceLocation , Integer > entry : ids . entrySet ( ) )
{
ResourceLocation itemName = entry . getKey ( ) ;
int newId = entry . getValue ( ) ;
2017-06-21 06:45:32 +00:00
int currId = old . getIDRaw ( itemName ) ;
2017-06-19 22:02:18 +00:00
if ( currId = = - 1 )
{
2017-06-26 03:45:48 +00:00
FMLLog . log . info ( " Registry {}: Found a missing id from the world {} " , this . superType . getSimpleName ( ) , itemName ) ;
2017-06-19 22:02:18 +00:00
missing . put ( itemName , newId ) ;
continue ; // no block/item -> nothing to add
}
else if ( currId ! = newId )
{
2017-06-26 03:45:48 +00:00
FMLLog . log . debug ( " Registry {}: Fixed {} id mismatch {}: {} (init) -> {} (map). " , this . superType . getSimpleName ( ) , name , itemName , currId , newId ) ;
2017-06-19 22:02:18 +00:00
remapped . put ( itemName , new Integer [ ] { currId , newId } ) ;
}
2017-06-26 03:45:48 +00:00
2017-06-19 22:02:18 +00:00
V obj = old . getRaw ( itemName ) ;
Preconditions . checkState ( obj ! = null , " objectKey has an ID but no object. Reflection/ASM hackery? Registry bug? " ) ;
2017-06-26 03:45:48 +00:00
List < V > lst = Lists . newArrayList ( old . overrides . get ( itemName ) ) ;
2017-06-23 07:58:00 +00:00
String primaryName = null ;
2017-06-26 03:45:48 +00:00
if ( old . overrides . containsKey ( itemName ) )
2017-06-23 07:58:00 +00:00
{
2017-06-26 03:45:48 +00:00
if ( ! overrides . containsKey ( itemName ) )
{
lst . add ( obj ) ;
obj = old . overrides . get ( itemName ) . iterator ( ) . next ( ) ; //Get the first one in the list, Which should be the first one registered
primaryName = old . owners . inverse ( ) . get ( obj ) . owner ;
}
else
primaryName = overrides . get ( itemName ) ;
2017-06-23 07:58:00 +00:00
}
2017-06-26 03:45:48 +00:00
for ( V value : lst )
2017-06-23 07:58:00 +00:00
{
2017-06-26 03:45:48 +00:00
OverrideOwner owner = old . owners . inverse ( ) . get ( value ) ;
2017-06-23 07:58:00 +00:00
if ( owner = = null )
{
2017-06-26 03:45:48 +00:00
FMLLog . log . warn ( " Registry {}: Override did not have an associated owner object. Name: {} Value: {} " , this . superType . getSimpleName ( ) , entry . getKey ( ) , value ) ;
2017-06-23 07:58:00 +00:00
continue ;
}
2017-07-05 21:41:24 +00:00
if ( primaryName . equals ( owner . owner ) )
2017-06-23 07:58:00 +00:00
continue ;
int realId = add ( newId , value , owner . owner ) ;
if ( newId ! = realId )
2017-06-26 03:45:48 +00:00
FMLLog . log . warn ( " Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {} " , this . superType . getSimpleName ( ) , entry . getKey ( ) , newId , realId ) ;
2017-06-23 07:58:00 +00:00
}
2017-06-19 22:02:18 +00:00
2017-06-23 07:58:00 +00:00
int realId = add ( newId , obj , primaryName = = null ? itemName . getResourceDomain ( ) : primaryName ) ;
if ( realId ! = newId )
2017-06-26 03:45:48 +00:00
FMLLog . log . warn ( " Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {} " , this . superType . getSimpleName ( ) , entry . getKey ( ) , newId , realId ) ;
ovs . remove ( itemName ) ;
}
for ( Map . Entry < ResourceLocation , String > entry : ovs . entrySet ( ) )
{
ResourceLocation itemName = entry . getKey ( ) ;
String owner = entry . getValue ( ) ;
String current = this . owners . inverse ( ) . get ( this . getRaw ( itemName ) ) . owner ;
if ( ! owner . equals ( current ) )
{
V _new = this . owners . get ( new OverrideOwner ( owner , itemName ) ) ;
if ( _new = = null )
{
FMLLog . log . warn ( " Registry {}: Skipping override for {}, Unknown owner {} " , this . superType . getSimpleName ( ) , itemName , owner ) ;
continue ;
}
FMLLog . log . info ( " Registry {}: Activating override {} for {} " , this . superType . getSimpleName ( ) , owner , itemName ) ;
int newId = this . getID ( itemName ) ;
int realId = this . add ( newId , _new , owner ) ;
if ( newId ! = realId )
FMLLog . log . warn ( " Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {} " , this . superType . getSimpleName ( ) , entry . getKey ( ) , newId , realId ) ;
}
2017-06-19 22:02:18 +00:00
}
}
boolean markDummy ( ResourceLocation key , int id )
{
if ( this . dummyFactory = = null )
return false ;
V dummy = this . dummyFactory . createDummy ( key ) ;
if ( DEBUG )
2017-08-22 09:08:34 +00:00
FMLLog . log . debug ( " Registry Dummy Add: {} {} -> {} " , key , id , dummy ) ;
2017-06-19 22:02:18 +00:00
2017-06-21 06:45:32 +00:00
//It was blocked before so we need to unset the blocking map
this . availabilityMap . clear ( id ) ;
2017-08-22 09:08:34 +00:00
if ( this . containsKey ( key ) )
{
//If the entry already exists, we need to delete it so we can add a dummy...
V value = this . names . remove ( key ) ;
if ( value = = null )
throw new IllegalStateException ( " ContainsKey for " + key + " was true, but removing by name returned no value.. This should never happen unless hackery! " ) ;
Integer oldid = this . ids . inverse ( ) . remove ( value ) ;
if ( oldid = = null )
throw new IllegalStateException ( " Removed a entry that did not have an associated id: " + key + " " + value . toString ( ) + " This should never happen unless hackery! " ) ;
if ( oldid ! = id )
FMLLog . log . debug ( " Registry {}: Dummy ID mismatch {} {} -> {} " , this . superType . getSimpleName ( ) , key , oldid , id ) ;
if ( DEBUG )
FMLLog . log . debug ( " Registry {} remove: {} {} " , this . superType . getSimpleName ( ) , key , oldid ) ;
}
2017-06-21 06:45:32 +00:00
2017-06-19 22:02:18 +00:00
int realId = this . add ( id , dummy ) ;
if ( realId ! = id )
2017-06-26 03:45:48 +00:00
FMLLog . log . warn ( " Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {} " , this . superType . getSimpleName ( ) , key , id , realId ) ;
2017-06-19 22:02:18 +00:00
this . dummies . add ( key ) ;
return true ;
}
//Public for tests
public Snapshot makeSnapshot ( )
{
Snapshot ret = new Snapshot ( ) ;
this . ids . forEach ( ( id , value ) - > ret . ids . put ( getKey ( value ) , id ) ) ;
2017-06-28 07:14:10 +00:00
ret . aliases . putAll ( this . aliases ) ;
ret . blocked . addAll ( this . blocked ) ;
ret . dummied . addAll ( this . dummies ) ;
2017-06-23 07:58:00 +00:00
ret . overrides . putAll ( getOverrideOwners ( ) ) ;
return ret ;
}
Map < ResourceLocation , String > getOverrideOwners ( )
{
Map < ResourceLocation , String > ret = Maps . newHashMap ( ) ;
for ( ResourceLocation key : this . overrides . keySet ( ) )
{
V obj = this . names . get ( key ) ;
2017-06-26 03:45:48 +00:00
OverrideOwner owner = this . owners . inverse ( ) . get ( obj ) ;
if ( owner = = null & & DEBUG )
FMLLog . log . debug ( " Registry {} {}: Invalid override {} {} " , this . superType . getSimpleName ( ) , this . stage . getName ( ) , key , obj ) ;
ret . put ( key , owner . owner ) ;
2017-06-23 07:58:00 +00:00
}
2017-06-19 22:02:18 +00:00
return ret ;
}
public static class Snapshot
{
public final Map < ResourceLocation , Integer > ids = Maps . newHashMap ( ) ;
public final Map < ResourceLocation , ResourceLocation > aliases = Maps . newHashMap ( ) ;
public final Set < Integer > blocked = Sets . newHashSet ( ) ;
public final Set < ResourceLocation > dummied = Sets . newHashSet ( ) ;
2017-06-23 07:58:00 +00:00
public final Map < ResourceLocation , String > overrides = Maps . newHashMap ( ) ;
2018-04-30 00:58:02 +00:00
public NBTTagCompound write ( )
{
NBTTagCompound data = new NBTTagCompound ( ) ;
NBTTagList ids = new NBTTagList ( ) ;
this . ids . entrySet ( ) . stream ( ) . sorted ( ( o1 , o2 ) - > o1 . getKey ( ) . compareTo ( o2 . getKey ( ) ) ) . forEach ( e - >
{
NBTTagCompound tag = new NBTTagCompound ( ) ;
tag . setString ( " K " , e . getKey ( ) . toString ( ) ) ;
tag . setInteger ( " V " , e . getValue ( ) ) ;
ids . appendTag ( tag ) ;
} ) ;
data . setTag ( " ids " , ids ) ;
NBTTagList aliases = new NBTTagList ( ) ;
this . aliases . entrySet ( ) . stream ( ) . sorted ( ( o1 , o2 ) - > o1 . getKey ( ) . compareTo ( o2 . getKey ( ) ) ) . forEach ( e - >
{
NBTTagCompound tag = new NBTTagCompound ( ) ;
tag . setString ( " K " , e . getKey ( ) . toString ( ) ) ;
tag . setString ( " V " , e . getKey ( ) . toString ( ) ) ;
aliases . appendTag ( tag ) ;
} ) ;
data . setTag ( " aliases " , aliases ) ;
NBTTagList overrides = new NBTTagList ( ) ;
this . overrides . entrySet ( ) . stream ( ) . sorted ( ( o1 , o2 ) - > o1 . getKey ( ) . compareTo ( o2 . getKey ( ) ) ) . forEach ( e - >
{
NBTTagCompound tag = new NBTTagCompound ( ) ;
tag . setString ( " K " , e . getKey ( ) . toString ( ) ) ;
tag . setString ( " V " , e . getValue ( ) ) ;
overrides . appendTag ( tag ) ;
} ) ;
data . setTag ( " overrides " , overrides ) ;
int [ ] blocked = this . blocked . stream ( ) . mapToInt ( x - > x ) . sorted ( ) . toArray ( ) ;
data . setIntArray ( " blocked " , blocked ) ;
NBTTagList dummied = new NBTTagList ( ) ;
this . dummied . stream ( ) . sorted ( ) . forEach ( e - > dummied . appendTag ( new NBTTagString ( e . toString ( ) ) ) ) ;
data . setTag ( " dummied " , dummied ) ;
return data ;
}
public static Snapshot read ( NBTTagCompound nbt )
{
Snapshot ret = new Snapshot ( ) ;
if ( nbt = = null )
{
return ret ;
}
NBTTagList list = nbt . getTagList ( " ids " , 10 ) ;
list . forEach ( e - >
{
NBTTagCompound comp = ( NBTTagCompound ) e ;
ret . ids . put ( new ResourceLocation ( comp . getString ( " K " ) ) , comp . getInteger ( " V " ) ) ;
} ) ;
list = nbt . getTagList ( " aliases " , 10 ) ;
list . forEach ( e - >
{
NBTTagCompound comp = ( NBTTagCompound ) e ;
String v = comp . getString ( " V " ) ;
if ( v . indexOf ( ':' ) = = - 1 ) //Forge Bug: https://github.com/MinecraftForge/MinecraftForge/issues/4894 TODO: Remove in 1.13
{
ret . overrides . put ( new ResourceLocation ( comp . getString ( " K " ) ) , v ) ;
}
else
{
2018-05-01 04:05:37 +00:00
ResourceLocation aliask = new ResourceLocation ( comp . getString ( " K " ) ) ;
ResourceLocation aliasv = new ResourceLocation ( v ) ;
if ( aliasv . equals ( aliask ) )
{
FMLLog . log . warn ( " Found unrecoverable 4894 bugged alias/override: {} -> {}, skipping. " , aliask , aliasv ) ;
}
else
{
ret . aliases . put ( aliask , aliasv ) ;
}
2018-04-30 00:58:02 +00:00
}
} ) ;
list = nbt . getTagList ( " overrides " , 10 ) ;
list . forEach ( e - >
{
NBTTagCompound comp = ( NBTTagCompound ) e ;
ret . overrides . put ( new ResourceLocation ( comp . getString ( " K " ) ) , comp . getString ( " V " ) ) ;
} ) ;
int [ ] blocked = nbt . getIntArray ( " blocked " ) ;
for ( int i : blocked )
{
ret . blocked . add ( i ) ;
}
list = nbt . getTagList ( " dummied " , 10 ) ; //10 - NBTTagCompound, Old format. New format is String list. For now we will just merge the old and new. TODO: Remove in 1.13
list . forEach ( e - > ret . dummied . add ( new ResourceLocation ( ( ( NBTTagCompound ) e ) . getString ( " K " ) ) ) ) ;
list = nbt . getTagList ( " dummied " , 8 ) ; //8 - NBTTagString, New format, less redundant/verbose
list . forEach ( e - > ret . dummied . add ( new ResourceLocation ( ( ( NBTTagString ) e ) . getString ( ) ) ) ) ;
return ret ;
}
2017-06-19 22:02:18 +00:00
}
public MissingMappings < ? > getMissingEvent ( ResourceLocation name , Map < ResourceLocation , Integer > map )
{
List < MissingMappings . Mapping < V > > lst = Lists . newArrayList ( ) ;
2017-06-27 23:56:42 +00:00
ForgeRegistry < V > pool = RegistryManager . ACTIVE . getRegistry ( name ) ;
map . forEach ( ( rl , id ) - > lst . add ( new MissingMappings . Mapping < V > ( this , pool , rl , id ) ) ) ;
2017-06-19 22:02:18 +00:00
return new MissingMappings < V > ( name , this , lst ) ;
}
2017-08-15 12:10:07 +00:00
void processMissingEvent ( ResourceLocation name , ForgeRegistry < V > pool , List < MissingMappings . Mapping < V > > mappings , Map < ResourceLocation , Integer > missing , Map < ResourceLocation , Integer [ ] > remaps , Collection < ResourceLocation > defaulted , Collection < ResourceLocation > failed , boolean injectNetworkDummies )
2017-06-19 22:02:18 +00:00
{
2017-06-24 08:46:05 +00:00
FMLLog . log . debug ( " Processing missing event for {}: " , name ) ;
2017-06-19 22:02:18 +00:00
int ignored = 0 ;
for ( MissingMappings . Mapping < V > remap : mappings )
{
MissingMappings . Action action = remap . getAction ( ) ;
if ( action = = MissingMappings . Action . REMAP )
{
// entry re-mapped, finish the registration with the new name/object, but the old id
int currId = getID ( remap . getTarget ( ) ) ;
ResourceLocation newName = pool . getKey ( remap . getTarget ( ) ) ;
2017-06-24 08:46:05 +00:00
FMLLog . log . debug ( " Remapping {} -> {}. " , remap . key , newName ) ;
2017-06-19 22:02:18 +00:00
missing . remove ( remap . key ) ;
//I don't think this will work, but I dont think it ever worked.. the item is already in the map with a different id... we want to fix that..
int realId = this . add ( remap . id , remap . getTarget ( ) ) ;
if ( realId ! = remap . id )
2017-06-24 08:46:05 +00:00
FMLLog . log . warn ( " Registered object did not get ID it asked for. Name: {} Type: {} Expected: {} Got: {} " , newName , this . getRegistrySuperType ( ) , remap . id , realId ) ;
2017-06-19 22:02:18 +00:00
this . addAlias ( remap . key , newName ) ;
if ( currId ! = realId )
{
2017-06-24 08:46:05 +00:00
FMLLog . log . info ( " Fixed id mismatch {}: {} (init) -> {} (map). " , newName , currId , realId ) ;
2017-06-19 22:02:18 +00:00
remaps . put ( newName , new Integer [ ] { currId , realId } ) ;
}
}
else
{
// block item missing, warn as requested and block the id
if ( action = = MissingMappings . Action . DEFAULT )
{
2017-08-15 12:10:07 +00:00
V m = this . missing = = null ? null : this . missing . createMissing ( remap . key , injectNetworkDummies ) ;
if ( m = = null )
defaulted . add ( remap . key ) ;
else
this . add ( remap . id , m , remap . key . getResourceDomain ( ) ) ;
2017-06-19 22:02:18 +00:00
}
else if ( action = = MissingMappings . Action . IGNORE )
{
2017-06-24 08:46:05 +00:00
FMLLog . log . debug ( " Ignoring {} " , remap . key ) ;
2017-06-19 22:02:18 +00:00
ignored + + ;
}
else if ( action = = MissingMappings . Action . FAIL )
{
2017-06-24 08:46:05 +00:00
FMLLog . log . debug ( " Failing {}! " , remap . key ) ;
2017-06-19 22:02:18 +00:00
failed . add ( remap . key ) ;
}
else if ( action = = MissingMappings . Action . WARN )
{
2017-06-24 08:46:05 +00:00
FMLLog . log . warn ( " {} may cause world breakage! " , remap . key ) ;
2017-06-19 22:02:18 +00:00
}
this . block ( remap . id ) ;
}
}
if ( failed . isEmpty ( ) & & ignored > 0 )
2017-06-24 08:46:05 +00:00
FMLLog . log . debug ( " There were {} missing mappings that have been ignored " , ignored ) ;
2017-06-19 22:02:18 +00:00
}
2017-06-23 07:58:00 +00:00
private static class OverrideOwner
{
final String owner ;
final ResourceLocation key ;
private OverrideOwner ( String owner , ResourceLocation key )
{
this . owner = owner ;
this . key = key ;
}
public boolean equals ( Object o )
{
if ( this = = o )
return true ;
if ( ! ( o instanceof OverrideOwner ) )
return false ;
OverrideOwner oo = ( OverrideOwner ) o ;
return this . owner . equals ( oo . owner ) & & this . key . equals ( oo . key ) ;
}
public int hashCode ( )
{
return 31 * this . key . hashCode ( ) + this . owner . hashCode ( ) ;
}
}
2017-06-19 22:02:18 +00:00
}