2016-06-23 03:49:47 +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 ;
2014-05-25 02:32:24 +00:00
import java.lang.reflect.Field ;
2017-06-19 22:02:18 +00:00
import java.lang.reflect.Method ;
import java.lang.reflect.Modifier ;
2016-10-09 20:34:38 +00:00
import java.util.Collections ;
import java.util.LinkedList ;
import java.util.Queue ;
2014-09-23 05:01:24 +00:00
2015-11-28 08:01:31 +00:00
import net.minecraft.util.ResourceLocation ;
2014-05-25 02:32:24 +00:00
import org.apache.logging.log4j.Level ;
2014-09-23 05:01:24 +00:00
2014-05-25 02:32:24 +00:00
import com.google.common.base.Throwables ;
2014-09-23 05:01:24 +00:00
import net.minecraftforge.fml.common.FMLLog ;
2017-06-19 22:02:18 +00:00
import net.minecraftforge.fml.common.registry.GameRegistry ;
2014-09-23 05:01:24 +00:00
import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder ;
2014-05-25 02:32:24 +00:00
2017-01-11 23:17:56 +00:00
import javax.annotation.Nullable ;
2014-05-25 02:32:24 +00:00
/ * *
* Internal class used in tracking { @link ObjectHolder } references
* /
2016-10-09 22:57:18 +00:00
@SuppressWarnings ( " rawtypes " )
2017-06-19 22:02:18 +00:00
class ObjectHolderRef
{
2014-05-25 02:32:24 +00:00
private Field field ;
2015-11-28 08:01:31 +00:00
private ResourceLocation injectedObject ;
2016-10-09 20:34:38 +00:00
private boolean isValid ;
2017-06-19 22:02:18 +00:00
private ForgeRegistry < ? > registry ;
2014-05-25 02:32:24 +00:00
2016-10-09 22:57:18 +00:00
@SuppressWarnings ( " unchecked " )
2017-06-19 22:02:18 +00:00
ObjectHolderRef ( Field field , ResourceLocation injectedObject , boolean extractFromExistingValues )
2014-05-25 02:32:24 +00:00
{
2016-10-09 20:34:38 +00:00
registry = getRegistryForType ( field ) ;
2014-05-25 02:32:24 +00:00
this . field = field ;
2016-10-09 20:34:38 +00:00
this . isValid = registry ! = null ;
2014-05-25 02:32:24 +00:00
if ( extractFromExistingValues )
{
try
{
Object existing = field . get ( null ) ;
2014-05-26 14:58:13 +00:00
// nothing is ever allowed to replace AIR
2017-06-19 22:02:18 +00:00
if ( existing = = null | | existing = = registry . getDefault ( ) )
2014-05-26 14:58:13 +00:00
{
this . injectedObject = null ;
this . field = null ;
2016-10-09 20:34:38 +00:00
this . isValid = false ;
2014-05-26 14:58:13 +00:00
return ;
}
else
{
2017-06-19 22:02:18 +00:00
this . injectedObject = ( ( IForgeRegistryEntry ) existing ) . getRegistryName ( ) ;
2014-05-26 14:58:13 +00:00
}
2017-06-19 22:02:18 +00:00
}
catch ( Exception e )
2014-05-25 02:32:24 +00:00
{
2017-06-19 22:02:18 +00:00
Throwables . throwIfUnchecked ( e ) ;
throw new RuntimeException ( e ) ;
2014-05-25 02:32:24 +00:00
}
}
else
{
this . injectedObject = injectedObject ;
}
if ( this . injectedObject = = null | | ! isValid ( ) )
{
2016-10-09 20:34:38 +00:00
throw new IllegalStateException ( String . format ( " The ObjectHolder annotation cannot apply to a field that does not map to a registry. Ensure the registry was created during the RegistryEvent.NewRegistry event. (found : %s at %s.%s) " , field . getType ( ) . getName ( ) , field . getClass ( ) . getName ( ) , field . getName ( ) ) ) ;
2014-05-25 02:32:24 +00:00
}
try
{
2016-07-29 21:08:52 +00:00
FinalFieldHelper . makeWritable ( field ) ;
}
catch ( Exception e )
2014-05-25 02:32:24 +00:00
{
2017-06-19 22:02:18 +00:00
Throwables . throwIfUnchecked ( e ) ;
throw new RuntimeException ( e ) ;
2014-05-25 02:32:24 +00:00
}
}
2017-06-19 22:02:18 +00:00
@SuppressWarnings ( " unchecked " )
@Nullable
private ForgeRegistry < ? > getRegistryForType ( Field field )
2016-10-09 20:34:38 +00:00
{
Queue < Class < ? > > typesToExamine = new LinkedList < Class < ? > > ( ) ;
typesToExamine . add ( field . getType ( ) ) ;
2017-06-19 22:02:18 +00:00
ForgeRegistry < ? > registry = null ;
2016-10-09 22:57:18 +00:00
while ( ! typesToExamine . isEmpty ( ) & & registry = = null )
{
2016-10-09 20:34:38 +00:00
Class < ? > type = typesToExamine . remove ( ) ;
Collections . addAll ( typesToExamine , type . getInterfaces ( ) ) ;
if ( IForgeRegistryEntry . class . isAssignableFrom ( type ) )
{
2017-06-19 22:02:18 +00:00
registry = ( ForgeRegistry < ? > ) GameRegistry . findRegistry ( ( Class < IForgeRegistryEntry > ) type ) ;
2016-10-09 20:34:38 +00:00
final Class < ? > parentType = type . getSuperclass ( ) ;
if ( parentType ! = null )
{
typesToExamine . add ( parentType ) ;
}
}
}
return registry ;
}
2014-05-25 02:32:24 +00:00
public boolean isValid ( )
{
2016-10-09 20:34:38 +00:00
return isValid ;
2014-05-25 02:32:24 +00:00
}
2017-06-19 22:02:18 +00:00
2014-05-25 02:32:24 +00:00
public void apply ( )
{
Object thing ;
2016-10-09 22:57:18 +00:00
if ( isValid & & registry . containsKey ( injectedObject ) & & ! registry . isDummied ( injectedObject ) )
2014-05-26 14:58:13 +00:00
{
2016-10-09 20:34:38 +00:00
thing = registry . getValue ( injectedObject ) ;
2014-05-26 14:58:13 +00:00
}
else
{
thing = null ;
}
if ( thing = = null )
{
2017-06-23 05:33:11 +00:00
FMLLog . log . debug ( " Unable to lookup {} for {}. This means the object wasn't registered. It's likely just mod options. " , injectedObject , field ) ;
2014-05-26 14:58:13 +00:00
return ;
}
2014-05-25 02:32:24 +00:00
try
{
2016-07-29 21:08:52 +00:00
FinalFieldHelper . setField ( field , null , thing ) ;
2014-05-25 02:32:24 +00:00
}
catch ( Exception e )
{
2017-06-23 05:33:11 +00:00
FMLLog . log . warn ( " Unable to set {} with value {} ({}) " , this . field , thing , this . injectedObject , e ) ;
2014-05-25 02:32:24 +00:00
}
}
2017-06-19 22:02:18 +00:00
private static class FinalFieldHelper
{
private static Field modifiersField ;
private static Object reflectionFactory ;
private static Method newFieldAccessor ;
private static Method fieldAccessorSet ;
static Field makeWritable ( Field f ) throws Exception
{
if ( modifiersField = = null )
{
Method getReflectionFactory = Class . forName ( " sun.reflect.ReflectionFactory " ) . getDeclaredMethod ( " getReflectionFactory " ) ;
reflectionFactory = getReflectionFactory . invoke ( null ) ;
newFieldAccessor = Class . forName ( " sun.reflect.ReflectionFactory " ) . getDeclaredMethod ( " newFieldAccessor " , Field . class , boolean . class ) ;
fieldAccessorSet = Class . forName ( " sun.reflect.FieldAccessor " ) . getDeclaredMethod ( " set " , Object . class , Object . class ) ;
modifiersField = Field . class . getDeclaredField ( " modifiers " ) ;
modifiersField . setAccessible ( true ) ;
}
modifiersField . setInt ( f , f . getModifiers ( ) & ~ Modifier . FINAL ) ;
return f ;
}
static void setField ( Field field , @Nullable Object instance , Object thing ) throws Exception
{
Object fieldAccessor = newFieldAccessor . invoke ( reflectionFactory , field , false ) ;
fieldAccessorSet . invoke ( fieldAccessor , instance , thing ) ;
}
}
2014-05-25 02:32:24 +00:00
}