/* * Minecraft Forge * Copyright (c) 2016-2018. * * 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 */ package net.minecraftforge.registries; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.LinkedList; import java.util.Queue; import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocationException; import javax.annotation.Nullable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @SuppressWarnings("rawtypes") public class ObjectHolderRef implements Runnable { private static final Logger LOGGER = LogManager.getLogger(); private Field field; private ResourceLocation injectedObject; private boolean isValid; private ForgeRegistry registry; public ObjectHolderRef(Field field, ResourceLocation injectedObject) { this(field, injectedObject.toString(), false); } ObjectHolderRef(Field field, String injectedObject, boolean extractFromExistingValues) { this.registry = getRegistryForType(field); this.field = field; this.isValid = registry != null; if (extractFromExistingValues) { try { Object existing = field.get(null); // nothing is ever allowed to replace AIR if (!isValid || (existing == null || existing == registry.getDefault())) { this.injectedObject = null; this.field = null; this.isValid = false; return; } else { this.injectedObject = ((IForgeRegistryEntry)existing).getRegistryName(); } } catch (IllegalAccessException e) { throw new RuntimeException(e); } } else { try { this.injectedObject = new ResourceLocation(injectedObject); } catch (ResourceLocationException e) { throw new IllegalArgumentException("Invalid @ObjectHolder annotation on \"" + field.toString() + "\"", e); } } if (this.injectedObject == null || !isValid()) { 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())); } field.setAccessible(true); if (Modifier.isFinal(field.getModifiers())) { throw new RuntimeException("@ObjectHolder on final field, our transformer did not run? " + field.getDeclaringClass().getName() + "/" + field.getName()); } } @SuppressWarnings("unchecked") @Nullable private ForgeRegistry getRegistryForType(Field field) { Queue> typesToExamine = new LinkedList>(); typesToExamine.add(field.getType()); ForgeRegistry registry = null; while (!typesToExamine.isEmpty() && registry == null) { Class type = typesToExamine.remove(); Collections.addAll(typesToExamine, type.getInterfaces()); if (IForgeRegistryEntry.class.isAssignableFrom(type)) { registry = (ForgeRegistry)RegistryManager.ACTIVE.getRegistry((Class)type); final Class parentType = type.getSuperclass(); if (parentType != null) { typesToExamine.add(parentType); } } } return registry; } public boolean isValid() { return isValid; } @Override public void run() { Object thing; if (isValid && registry.containsKey(injectedObject) && !registry.isDummied(injectedObject)) { thing = registry.getValue(injectedObject); } else { thing = null; } if (thing == null) { LOGGER.debug("Unable to lookup {} for {}. This means the object wasn't registered. It's likely just mod options.", injectedObject, field); return; } try { field.set(null, thing); } catch (IllegalArgumentException | ReflectiveOperationException e) { LOGGER.warn("Unable to set {} with value {} ({})", this.field, thing, this.injectedObject, e); } } @Override public int hashCode() { return field.hashCode(); } @Override public boolean equals(Object other) { if (!(other instanceof ObjectHolderRef)) return false; ObjectHolderRef o = (ObjectHolderRef)other; return this.field.equals(o.field); } }