2016-06-22 23:49:47 -04:00
|
|
|
/*
|
2022-02-28 13:41:23 -08:00
|
|
|
* Minecraft Forge - Forge Development LLC
|
|
|
|
|
* SPDX-License-Identifier: LGPL-2.1-only
|
2016-06-22 23:49:47 -04:00
|
|
|
*/
|
|
|
|
|
|
2017-06-19 15:02:18 -07:00
|
|
|
package net.minecraftforge.registries;
|
2014-05-24 22:32:24 -04:00
|
|
|
|
2019-01-09 20:57:01 -08:00
|
|
|
import java.lang.annotation.ElementType;
|
2014-05-24 22:32:24 -04:00
|
|
|
import java.lang.reflect.Field;
|
2019-01-09 20:57:01 -08:00
|
|
|
import java.util.Collection;
|
|
|
|
|
import java.util.HashSet;
|
2014-05-24 22:32:24 -04:00
|
|
|
import java.util.List;
|
2019-01-09 20:57:01 -08:00
|
|
|
import java.util.Locale;
|
2014-05-24 22:32:24 -04:00
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Set;
|
2019-01-16 12:28:13 -08:00
|
|
|
import java.util.function.Consumer;
|
|
|
|
|
import java.util.function.Predicate;
|
2019-01-09 20:57:01 -08:00
|
|
|
import java.util.stream.Collectors;
|
2014-09-22 22:01:24 -07:00
|
|
|
|
2021-07-17 00:08:49 -05:00
|
|
|
import net.minecraft.resources.ResourceLocation;
|
2019-01-09 20:57:01 -08:00
|
|
|
import net.minecraftforge.fml.ModList;
|
2017-06-20 18:39:55 -07:00
|
|
|
import net.minecraftforge.fml.common.Mod;
|
2019-01-13 22:51:36 -05:00
|
|
|
import net.minecraftforge.forgespi.language.ModFileScanData;
|
2014-09-22 22:01:24 -07:00
|
|
|
|
2014-05-24 22:32:24 -04:00
|
|
|
import com.google.common.collect.Maps;
|
|
|
|
|
|
2017-01-11 15:17:56 -08:00
|
|
|
import javax.annotation.Nullable;
|
|
|
|
|
|
2018-09-06 04:39:01 -07:00
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
|
|
|
import org.apache.logging.log4j.Logger;
|
|
|
|
|
import org.objectweb.asm.Opcodes;
|
2019-01-09 20:57:01 -08:00
|
|
|
import org.objectweb.asm.Type;
|
2018-09-06 04:39:01 -07:00
|
|
|
|
2019-01-27 00:06:57 -05:00
|
|
|
import static net.minecraftforge.registries.ForgeRegistry.REGISTRIES;
|
2019-01-26 22:18:28 -05:00
|
|
|
|
2014-05-24 22:32:24 -04:00
|
|
|
/**
|
|
|
|
|
* Internal registry for tracking {@link ObjectHolder} references
|
|
|
|
|
*/
|
2019-01-09 20:57:01 -08:00
|
|
|
public class ObjectHolderRegistry
|
2017-06-19 15:02:18 -07:00
|
|
|
{
|
2019-01-09 20:57:01 -08:00
|
|
|
/**
|
|
|
|
|
* Exposed to allow modders to register their own notification handlers.
|
|
|
|
|
* This runnable will be called after a registry snapshot has been injected and finalized.
|
|
|
|
|
* The internal list is backed by a HashSet so it is HIGHLY recommended you implement a proper equals
|
|
|
|
|
* and hashCode function to de-duplicate callers here.
|
|
|
|
|
* The default @ObjectHolder implementation uses the hashCode/equals for the field the annotation is on.
|
|
|
|
|
*/
|
2022-04-01 15:49:46 -05:00
|
|
|
public static synchronized void addHandler(Consumer<Predicate<ResourceLocation>> ref)
|
2019-01-09 20:57:01 -08:00
|
|
|
{
|
|
|
|
|
objectHolders.add(ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Removed the specified handler from the notification list.
|
|
|
|
|
*
|
|
|
|
|
* The internal list is backed by a hash set, and so proper hashCode and equals operations are required for success.
|
|
|
|
|
*
|
|
|
|
|
* The default @ObjectHolder implementation uses the hashCode/equals for the field the annotation is on.
|
|
|
|
|
*
|
|
|
|
|
* @return true if handler was matched and removed.
|
|
|
|
|
*/
|
2022-04-01 15:49:46 -05:00
|
|
|
public static synchronized boolean removeHandler(Consumer<Predicate<ResourceLocation>> ref)
|
2019-01-09 20:57:01 -08:00
|
|
|
{
|
|
|
|
|
return objectHolders.remove(ref);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//==============================================================
|
|
|
|
|
// Everything below is internal, do not use.
|
|
|
|
|
//==============================================================
|
|
|
|
|
|
2018-09-06 04:39:01 -07:00
|
|
|
private static final Logger LOGGER = LogManager.getLogger();
|
2019-01-16 12:28:13 -08:00
|
|
|
private static final Set<Consumer<Predicate<ResourceLocation>>> objectHolders = new HashSet<>();
|
2019-01-09 20:57:01 -08:00
|
|
|
private static final Type OBJECT_HOLDER = Type.getType(ObjectHolder.class);
|
|
|
|
|
private static final Type MOD = Type.getType(Mod.class);
|
|
|
|
|
|
|
|
|
|
public static void findObjectHolders()
|
2014-05-24 22:32:24 -04:00
|
|
|
{
|
2019-01-27 00:06:57 -05:00
|
|
|
LOGGER.debug(REGISTRIES,"Processing ObjectHolder annotations");
|
2019-01-09 20:57:01 -08:00
|
|
|
final List<ModFileScanData.AnnotationData> annotations = ModList.get().getAllScanData().stream()
|
|
|
|
|
.map(ModFileScanData::getAnnotations)
|
|
|
|
|
.flatMap(Collection::stream)
|
2021-07-17 00:08:49 -05:00
|
|
|
.filter(a -> OBJECT_HOLDER.equals(a.annotationType()) || MOD.equals(a.annotationType()))
|
2019-01-09 20:57:01 -08:00
|
|
|
.collect(Collectors.toList());
|
2018-09-06 04:39:01 -07:00
|
|
|
|
2019-01-09 20:57:01 -08:00
|
|
|
Map<Type, String> classModIds = Maps.newHashMap();
|
|
|
|
|
Map<Type, Class<?>> classCache = Maps.newHashMap();
|
|
|
|
|
|
|
|
|
|
// Gather all @Mod classes, so that @ObjectHolder's in those classes don't need to specify the mod id, Modder convince
|
2021-07-17 00:08:49 -05:00
|
|
|
annotations.stream().filter(a -> MOD.equals(a.annotationType())).forEach(data -> classModIds.put(data.clazz(), (String)data.annotationData().get("value")));
|
2018-09-06 04:39:01 -07:00
|
|
|
|
2014-05-26 10:58:13 -04:00
|
|
|
// double pass - get all the class level annotations first, then the field level annotations
|
2021-07-17 00:08:49 -05:00
|
|
|
annotations.stream().filter(a -> OBJECT_HOLDER.equals(a.annotationType())).filter(a -> a.targetType() == ElementType.TYPE)
|
|
|
|
|
.forEach(data -> scanTarget(classModIds, classCache, data.clazz(), null, (String)data.annotationData().get("value"), true, data.clazz().getClassName().startsWith("net.minecraft.")));
|
2019-01-09 20:57:01 -08:00
|
|
|
|
2021-07-17 00:08:49 -05:00
|
|
|
annotations.stream().filter(a -> OBJECT_HOLDER.equals(a.annotationType())).filter(a -> a.targetType() == ElementType.FIELD)
|
|
|
|
|
.forEach(data -> scanTarget(classModIds, classCache, data.clazz(), data.memberName(), (String)data.annotationData().get("value"), false, false));
|
2019-01-27 00:06:57 -05:00
|
|
|
LOGGER.debug(REGISTRIES,"Found {} ObjectHolder annotations", objectHolders.size());
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|
2019-01-09 20:57:01 -08:00
|
|
|
|
|
|
|
|
private static void scanTarget(Map<Type, String> classModIds, Map<Type, Class<?>> classCache, Type type, @Nullable String annotationTarget, String value, boolean isClass, boolean extractFromValue)
|
2014-05-24 22:32:24 -04:00
|
|
|
{
|
|
|
|
|
Class<?> clazz;
|
2019-01-09 20:57:01 -08:00
|
|
|
if (classCache.containsKey(type))
|
2014-05-24 22:32:24 -04:00
|
|
|
{
|
2019-01-09 20:57:01 -08:00
|
|
|
clazz = classCache.get(type);
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2019-01-09 20:57:01 -08:00
|
|
|
clazz = Class.forName(type.getClassName(), extractFromValue, ObjectHolderRegistry.class.getClassLoader());
|
|
|
|
|
classCache.put(type, clazz);
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|
2017-07-13 05:50:28 +01:00
|
|
|
catch (ClassNotFoundException ex)
|
2014-05-24 22:32:24 -04:00
|
|
|
{
|
|
|
|
|
// unpossible?
|
2017-06-19 15:02:18 -07:00
|
|
|
throw new RuntimeException(ex);
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (isClass)
|
|
|
|
|
{
|
2019-01-09 20:57:01 -08:00
|
|
|
scanClassForFields(classModIds, type, value, clazz, extractFromValue);
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (value.indexOf(':') == -1)
|
|
|
|
|
{
|
2019-01-09 20:57:01 -08:00
|
|
|
String prefix = classModIds.get(type);
|
2014-05-24 22:32:24 -04:00
|
|
|
if (prefix == null)
|
|
|
|
|
{
|
2019-01-27 00:06:57 -05:00
|
|
|
LOGGER.warn(REGISTRIES,"Found an unqualified ObjectHolder annotation ({}) without a modid context at {}.{}, ignoring", value, type, annotationTarget);
|
2014-05-24 22:32:24 -04:00
|
|
|
throw new IllegalStateException("Unqualified reference to ObjectHolder");
|
|
|
|
|
}
|
2019-01-09 20:57:01 -08:00
|
|
|
value = prefix + ':' + value;
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|
|
|
|
|
try
|
|
|
|
|
{
|
2017-06-20 18:39:55 -07:00
|
|
|
Field f = clazz.getDeclaredField(annotationTarget);
|
2019-01-09 20:57:01 -08:00
|
|
|
ObjectHolderRef ref = new ObjectHolderRef(f, value, extractFromValue);
|
|
|
|
|
if (ref.isValid())
|
|
|
|
|
addHandler(ref);
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|
2017-07-13 05:50:28 +01:00
|
|
|
catch (NoSuchFieldException ex)
|
2014-05-24 22:32:24 -04:00
|
|
|
{
|
|
|
|
|
// unpossible?
|
2017-06-19 15:02:18 -07:00
|
|
|
throw new RuntimeException(ex);
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-09 20:57:01 -08:00
|
|
|
private static void scanClassForFields(Map<Type, String> classModIds, Type targetClass, String value, Class<?> clazz, boolean extractFromExistingValues)
|
2014-05-24 22:32:24 -04:00
|
|
|
{
|
2019-01-09 20:57:01 -08:00
|
|
|
classModIds.put(targetClass, value);
|
2018-09-06 04:39:01 -07:00
|
|
|
final int flags = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
|
2014-05-24 22:32:24 -04:00
|
|
|
for (Field f : clazz.getFields())
|
|
|
|
|
{
|
2018-09-06 04:39:01 -07:00
|
|
|
if (((f.getModifiers() & flags) != flags) || f.isAnnotationPresent(ObjectHolder.class))
|
2014-05-24 22:32:24 -04:00
|
|
|
continue;
|
2019-01-09 20:57:01 -08:00
|
|
|
ObjectHolderRef ref = new ObjectHolderRef(f, value + ':' + f.getName().toLowerCase(Locale.ENGLISH), extractFromExistingValues);
|
|
|
|
|
if (ref.isValid())
|
|
|
|
|
addHandler(ref);
|
2014-05-26 10:58:13 -04:00
|
|
|
}
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|
|
|
|
|
|
2019-01-09 20:57:01 -08:00
|
|
|
public static void applyObjectHolders()
|
2014-05-24 22:32:24 -04:00
|
|
|
{
|
2019-01-27 00:06:57 -05:00
|
|
|
LOGGER.debug(REGISTRIES,"Applying holder lookups");
|
2019-01-16 12:28:13 -08:00
|
|
|
applyObjectHolders(key -> true);
|
2019-01-27 00:06:57 -05:00
|
|
|
LOGGER.debug(REGISTRIES,"Holder lookups applied");
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|
|
|
|
|
|
2019-01-16 12:28:13 -08:00
|
|
|
public static void applyObjectHolders(Predicate<ResourceLocation> filter)
|
|
|
|
|
{
|
2022-04-01 15:49:46 -05:00
|
|
|
RuntimeException aggregate = new RuntimeException("Failed to apply some object holders, see suppressed exceptions for details");
|
|
|
|
|
objectHolders.forEach(objectHolder -> {
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
objectHolder.accept(filter);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
aggregate.addSuppressed(e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (aggregate.getSuppressed().length > 0)
|
|
|
|
|
{
|
|
|
|
|
// Something had an exception, log and propagate with a throw for when FML eventually logs this too
|
|
|
|
|
LOGGER.error("", aggregate);
|
|
|
|
|
throw aggregate;
|
|
|
|
|
}
|
2019-01-16 12:28:13 -08:00
|
|
|
}
|
|
|
|
|
|
2014-05-24 22:32:24 -04:00
|
|
|
}
|