private Set<ASMData> unpackInterfaces(Set<ASMData> packedInterfaces) { Set<ASMData> result = Sets.newHashSet(); for (ASMData data : packedInterfaces) { @SuppressWarnings("unchecked") List<Map<String,Object>> packedList = (List<Map<String,Object>>) data.getAnnotationInfo().get("value"); for (Map<String,Object> packed : packedList) { ASMData newData = data.copy(packed); result.add(newData); } } return result; }
public static void loadData(ASMDataTable data) { FMLLog.fine("Loading @Config anotation data"); for (ASMData target : data.getAll(Config.class.getName())) { String modid = (String)target.getAnnotationInfo().get("modid"); Multimap<Config.Type, ASMData> map = asm_data.get(modid); if (map == null) { map = ArrayListMultimap.create(); asm_data.put(modid, map); } EnumHolder tholder = (EnumHolder)target.getAnnotationInfo().get("type"); Config.Type type = tholder == null ? Config.Type.INSTANCE : Config.Type.valueOf(tholder.getValue()); map.put(type, target); } }
private static String getInjectClass(String test, ASMData data, ClassLoader classLoader) throws Exception { String className; String methodName; if (!test.equals("")) { int index = test.lastIndexOf('.'); className = test.substring(0, index); methodName = test.substring(index + 1); } else { className = data.getClassName(); methodName = "proxyCallback"; } Class<?> clazz = Class.forName(className, true, classLoader); for (Method method : clazz.getMethods()) { if (method.getName().equals(methodName) && method.getReturnType().equals(String.class) && ReflectionManager.isStatic(method)) { method.setAccessible(true); return (String) method.invoke(null); } } throw new RuntimeException(String.format("Unable to find static method with string return type! Method: %s.%s", className, methodName)); }
@Nonnull @Override public Set<Pair<Object, Map<Class, Set<Method>>>> findSubscribers() { SetMultimap<String, ASMData> allAnnotationsInContainer = asmTable.getAnnotationsFor( Loader.instance().activeModContainer()); if (!allAnnotationsInContainer.containsKey(ForgepathHandler.class.getCanonicalName())) return NO_SUBSCRIBERS; Set<ASMData> asmDataSet = allAnnotationsInContainer.get(ForgepathHandler.class.getName()); // Goddamnit Java and your stupidly long types ImmutableSet.Builder<Pair<Object, Map<Class, Set<Method>>>> mapBuilder = new ImmutableSet.Builder<Pair<Object, Map<Class, Set<Method>>>>(); for (ASMData asmData : asmDataSet) { String cname = asmData.getClassName(); Object obj; try { obj = Class.forName(cname).newInstance(); } catch (Exception ex) { continue; // SKIP! } Map<Class, Set<Method>> subscribers = innerLocator.findSubscribers(obj); mapBuilder.add(new Pair<Object, Map<Class, Set<Method>>>(obj, subscribers)); } return mapBuilder.build(); }
private static void findTargets(ASMData target, Map<Field, Class<?>> classTargetToSource, Map<Field, Class<?>> fieldTargetToSource) { final String targetClassName = target.getClassName(); final String targetObject = target.getObjectName(); final Type sourceClassName = (Type)target.getAnnotationInfo().get("value"); try { final Class<?> targetClass = Class.forName(targetClassName); final Class<?> sourceClass; if (sourceClassName == null || sourceClassName.equals(USE_DECLARING_TYPE_MARKER)) sourceClass = targetClass; else sourceClass = Class.forName(sourceClassName.getClassName()); if (targetClassName.equals(targetObject)) addClassFields(classTargetToSource, targetClass, sourceClass); else addField(fieldTargetToSource, targetClass, targetObject, sourceClass); } catch (Exception e) { Log.warn(e, "Failed to fill type variable holder at %s:%s", targetClassName, targetObject); } }
public static void inject(ModContainer mod, ASMDataTable data, Side side) { FMLLog.fine("Attempting to inject @EventBusSubscriber classes into the eventbus for %s", mod.getModId()); Set<ASMDataTable.ASMData> targets = data.getAnnotationsFor(mod).get(Mod.EventBusSubscriber.class.getName()); ClassLoader mcl = Loader.instance().getModClassLoader(); for (ASMDataTable.ASMData targ : targets) { try { //noinspection unchecked List<ModAnnotation.EnumHolder> sidesEnum = (List<ModAnnotation.EnumHolder>)targ.getAnnotationInfo().get("value"); EnumSet<Side> sides = DEFAULT; if (sidesEnum != null) { sides = EnumSet.noneOf(Side.class); for (ModAnnotation.EnumHolder h: sidesEnum) { sides.add(Side.valueOf(h.getValue())); } } if (sides == DEFAULT || sides.contains(side)) { FMLLog.fine("Found @EventBusSubscriber class %s", targ.getClassName()); Class<?> subscriptionTarget = Class.forName(targ.getClassName(), true, mcl); MinecraftForge.EVENT_BUS.register(subscriptionTarget); FMLLog.fine("Injected @EventBusSubscriber class %s", targ.getClassName()); } } catch (Exception e) { FMLLog.log(Level.ERROR, e, "An error occurred trying to load an EventBusSubscriber %s for modid %s", targ.getClassName(), mod.getModId()); throw new LoaderException(e); } } }
public void initTable(ASMDataTable dataTable) { optionals = ArrayListMultimap.create(); Set<ASMData> interfaceLists = dataTable.getAll("net.minecraftforge.fml.common.Optional$InterfaceList"); addData(unpackInterfaces(interfaceLists)); Set<ASMData> interfaces = dataTable.getAll("net.minecraftforge.fml.common.Optional$Interface"); addData(interfaces); Set<ASMData> methods = dataTable.getAll("net.minecraftforge.fml.common.Optional$Method"); addData(methods); }
private void addData(Set<ASMData> interfaces) { for (ASMData data : interfaces) { optionals.put(data.getClassName(),data); } }
public static void load(String modid, Config.Type type) { FMLLog.fine("Attempting to inject @Config classes into %s for type %s", modid, type); ClassLoader mcl = Loader.instance().getModClassLoader(); File configDir = Loader.instance().getConfigDir(); Multimap<Config.Type, ASMData> map = asm_data.get(modid); if (map == null) return; for (ASMData targ : map.get(type)) { try { Class<?> cls = Class.forName(targ.getClassName(), true, mcl); String name = (String)targ.getAnnotationInfo().get("name"); if (name == null) name = modid; File file = new File(configDir, name + ".cfg"); Configuration cfg = CONFIGS.get(file.getAbsolutePath()); if (cfg == null) { cfg = new Configuration(file); cfg.load(); CONFIGS.put(file.getAbsolutePath(), cfg); } createConfig(cfg, cls, modid, type == Config.Type.INSTANCE); cfg.save(); } catch (Exception e) { FMLLog.log(Level.ERROR, e, "An error occurred trying to load a config for %s into %s", modid, targ.getClassName()); throw new LoaderException(e); } } }
public static void registerCommands(@Nonnull ASMDataTable asmDataTable) { String annotationClassName = Command.class.getCanonicalName(); Set<ASMData> asmDatas = new HashSet<ASMData>(asmDataTable.getAll(annotationClassName)); for (ASMDataTable.ASMData asmData : asmDatas) { try { Class<?> asmClass = Class.forName(asmData.getClassName()); Class<? extends AbstractCommand> asmInstanceClass = asmClass.asSubclass(AbstractCommand.class); AbstractCommand instance = asmInstanceClass.newInstance(); CommandManager.INSTANCE.registerCommand(instance); } catch (Exception e) { e.printStackTrace(); } } }
public void fillAllHolders(ASMDataTable data) { final Map<Field, Class<?>> classTargetToSource = Maps.newHashMap(); final Map<Field, Class<?>> fieldTargetToSource = Maps.newHashMap(); for (ASMData target : data.getAll(TypeVariableHolder.class.getName())) findTargets(target, classTargetToSource, fieldTargetToSource); fillFields(classTargetToSource, fieldTargetToSource); }
private static <A> void fillTargetField(ClassInfoCache clsCache, ApiProviderRegistry<A> registry, ASMData data, Class<A> interfaceMarker) { final String targetClassName = data.getClassName(); final String targetObjectName = data.getObjectName(); final ClassInfo targetCls = clsCache.getOrCreate(targetClassName); final Setter setter = targetCls.get(targetObjectName); Preconditions.checkArgument(setter != null, "Entry '%s' in class '%s' is not valid target for API annotation", targetObjectName, targetClassName); final Class<?> acceptedType = setter.getType(); Preconditions.checkState(interfaceMarker.isAssignableFrom(acceptedType), "Failed to set API object on %s:%s - invalid type, expected %s", targetClassName, targetObjectName, interfaceMarker); final Class<? extends A> castAcceptedType = acceptedType.asSubclass(interfaceMarker); final A api = registry.getApi(castAcceptedType); if (api != null) { try { setter.set(api); } catch (Throwable t) { throw new RuntimeException(String.format("Failed to set entry '%s' in class '%s'", targetObjectName, targetClassName), t); } Log.trace("Injecting instance of %s from mod %s to field %s:%s from file %s", castAcceptedType, Loader.instance().activeModContainer().getModId(), targetClassName, targetObjectName, data.getCandidate().getModContainer()); } else { Log.info("Can't set API field %s:%s - no API for type %s", targetClassName, targetObjectName, castAcceptedType); } }
private static <A> void fillTargetFields(final ApiProviderRegistry<A> registry, ASMDataTable table, Class<? extends Annotation> fieldMarker, Class<A> interfaceMarker) { final ClassInfoCache clsCache = new ClassInfoCache(); final Set<ASMData> targets = table.getAll(fieldMarker.getName()); for (ASMData data : targets) fillTargetField(clsCache, registry, data, interfaceMarker); }
public static void inject(ModContainer mod, ASMDataTable data, Side side, ILanguageAdapter languageAdapter) { FMLLog.fine("Attempting to inject @SidedProxy classes into %s", mod.getModId()); Set<ASMData> targets = data.getAnnotationsFor(mod).get(SidedProxy.class.getName()); ClassLoader mcl = Loader.instance().getModClassLoader(); for (ASMData targ : targets) { try { Class<?> proxyTarget = Class.forName(targ.getClassName(), true, mcl); Field target = proxyTarget.getDeclaredField(targ.getObjectName()); if (target == null) { // Impossible? FMLLog.severe("Attempted to load a proxy type into %s.%s but the field was not found", targ.getClassName(), targ.getObjectName()); throw new LoaderException(String.format("Attempted to load a proxy type into %s.%s but the field was not found", targ.getClassName(), targ.getObjectName())); } target.setAccessible(true); SidedProxy annotation = target.getAnnotation(SidedProxy.class); if (!Strings.isNullOrEmpty(annotation.modId()) && !annotation.modId().equals(mod.getModId())) { FMLLog.fine("Skipping proxy injection for %s.%s since it is not for mod %s", targ.getClassName(), targ.getObjectName(), mod.getModId()); continue; } String targetType = side.isClient() ? annotation.clientSide() : annotation.serverSide(); if(targetType.equals("")) { targetType = targ.getClassName() + (side.isClient() ? "$ClientProxy" : "$ServerProxy"); } Object proxy=Class.forName(targetType, true, mcl).newInstance(); if (languageAdapter.supportsStatics() && (target.getModifiers() & Modifier.STATIC) == 0 ) { FMLLog.severe("Attempted to load a proxy type %s into %s.%s, but the field is not static", targetType, targ.getClassName(), targ.getObjectName()); throw new LoaderException(String.format("Attempted to load a proxy type %s into %s.%s, but the field is not static", targetType, targ.getClassName(), targ.getObjectName())); } if (!target.getType().isAssignableFrom(proxy.getClass())) { FMLLog.severe("Attempted to load a proxy type %s into %s.%s, but the types don't match", targetType, targ.getClassName(), targ.getObjectName()); throw new LoaderException(String.format("Attempted to load a proxy type %s into %s.%s, but the types don't match", targetType, targ.getClassName(), targ.getObjectName())); } languageAdapter.setProxy(target, proxyTarget, proxy); } catch (Exception e) { FMLLog.log(Level.ERROR, e, "An error occurred trying to load a proxy into %s.%s", targ.getAnnotationInfo(), targ.getClassName(), targ.getObjectName()); throw new LoaderException(e); } } // Allow language specific proxy injection. languageAdapter.setInternalProxies(mod, side, mcl); }
@Override public byte[] transform(String name, String transformedName, byte[] basicClass) { String lookupName = name; if(name.endsWith("$class")) { lookupName = name.substring(0, name.length() - 6); } if (optionals == null || !optionals.containsKey(lookupName)) { return basicClass; } ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(basicClass); classReader.accept(classNode, 0); if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - found optionals for class %s - processing", name); for (ASMData optional : optionals.get(lookupName)) { String modId = (String) optional.getAnnotationInfo().get("modid"); if (Loader.isModLoaded(modId) || ModAPIManager.INSTANCE.hasAPI(modId)) { if (logDebugInfo) FMLRelaunchLog.finer("Optional removal skipped - mod present %s", modId); continue; } if (logDebugInfo) FMLRelaunchLog.finer("Optional on %s triggered - mod missing %s", name, modId); if (optional.getAnnotationInfo().containsKey("iface")) { Boolean stripRefs = (Boolean)optional.getAnnotationInfo().get("striprefs"); if (stripRefs == null) stripRefs = Boolean.FALSE; stripInterface(classNode,(String)optional.getAnnotationInfo().get("iface"), stripRefs); } else { stripMethod(classNode, optional.getObjectName()); } } if (logDebugInfo) FMLRelaunchLog.finer("Optional removal - class %s processed", name); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); }
private void parseSimpleFieldAnnotation(SetMultimap<String, ASMData> annotations, String annotationClassName, Function<ModContainer, Object> retriever) throws IllegalAccessException { String[] annName = annotationClassName.split("\\."); String annotationName = annName[annName.length - 1]; for (ASMData targets : annotations.get(annotationClassName)) { String targetMod = (String)targets.getAnnotationInfo().get("value"); Field f = null; Object injectedMod = null; ModContainer mc = this; boolean isStatic = false; Class<?> clz = modInstance.getClass(); if (!Strings.isNullOrEmpty(targetMod)) { if (Loader.isModLoaded(targetMod)) { mc = Loader.instance().getIndexedModList().get(targetMod); } else { mc = null; } } if (mc != null) { try { clz = Class.forName(targets.getClassName(), true, Loader.instance().getModClassLoader()); f = clz.getDeclaredField(targets.getObjectName()); f.setAccessible(true); isStatic = Modifier.isStatic(f.getModifiers()); injectedMod = retriever.apply(mc); } catch (Exception e) { Throwables.propagateIfPossible(e); FMLLog.log(getModId(), Level.WARN, e, "Attempting to load @%s in class %s for %s and failing", annotationName, targets.getClassName(), mc.getModId()); } } if (f != null) { Object target = null; if (!isStatic) { target = modInstance; if (!modInstance.getClass().equals(clz)) { FMLLog.log(getModId(), Level.WARN, "Unable to inject @%s in non-static field %s.%s for %s as it is NOT the primary mod instance", annotationName, targets.getClassName(), targets.getObjectName(), mc.getModId()); continue; } } f.set(target, injectedMod); } } }
/** * Gets all classes annotated with the annotation class. * * @param table The ASMDataTable. This is generated from forge and can be accessed from the * initialization events. * @param annotation The annotation to search for. * @return A list of all classes with the passed annotation. */ public static <A extends Annotation> List<Tuple<Class<?>, A>> getAnnotatedClasses (ASMDataTable table, Class<A> annotation) { final List<Tuple<Class<?>, A>> classes = new ArrayList<>(); for (final ASMData data : getData(table, annotation)) { try { final Class clazz = Class.forName(data.getClassName()); if (clazz != null) { classes.add(new Tuple<Class<?>, A>(clazz, (A) clazz.getAnnotation(annotation))); } } catch (final ClassNotFoundException e) { Constants.LOG.warn(e, "Could not load class {} ", data.getClassName()); } } return classes; }
/** * Finds all classes annotated with the annotation class. These classes are then * instantiated, added to a list, and given to you. * * @param table The ASMDataTable created by Forge. You can get this from most of the main * mod loading stage events. * @param annotation The class of the annotation you're using to search for. * @param instance The class of the thing you're trying to construct. This should be a * shared interface, or parent class. * @return A list of all classes annotated with the annotation, as instances. */ public static <T, A extends Annotation> Map<T, A> getAnnotations (ASMDataTable table, Class<A> annotation, Class<T> instance) { final Map<T, A> map = new HashMap<>(); for (final ASMDataTable.ASMData asmData : getData(table, annotation)) { try { final Class<?> asmClass = Class.forName(asmData.getClassName()); final Class<? extends T> asmInstanceClass = asmClass.asSubclass(instance); map.put(asmInstanceClass.newInstance(), asmInstanceClass.getAnnotation(annotation)); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { Constants.LOG.warn(e, "Could not load class {}", asmData.getClassName()); } } return map; }
public static void registerRules(@Nonnull ASMDataTable asmDataTable) { Class annotationClass = ProgressionRule.class; String annotationClassName = annotationClass.getCanonicalName(); Set<ASMData> asmDatas = new HashSet<ASMData>(asmDataTable.getAll(annotationClassName)); topLoop: for (ASMDataTable.ASMData asmData : asmDatas) { try { Class<?> asmClass = Class.forName(asmData.getClassName()); Class<? extends IRule> asmInstanceClass = asmClass.asSubclass(IRule.class); IRule instance = asmInstanceClass.newInstance(); Map<String, Object> data = asmData.getAnnotationInfo(); String modData = (String) data.get("mod"); if (modData != null) { String[] mods = modData.replace(" ", "").split(","); for (String mod: mods) { if (mod != null && !Loader.isModLoaded(mod)) continue topLoop; } } String name = (String) data.get("name"); int color = 0xFFCCCCCC; if (data.get("color") != null) { color = (Integer) data.get("color"); } String icon = (String) data.get("icon"); String meta = (String) data.get("meta"); boolean isCancelable = false; if (data.get("cancelable") != null) { isCancelable = (Boolean) data.get("cancelable"); } ItemStack stack = StackHelper.getStackFromString(icon); if (stack == null) stack = new ItemStack(Progression.item); if (meta != null) { for (ItemMeta item: ItemMeta.values()) { if (item.name().equalsIgnoreCase(meta)) { stack.setItemDamage(item.ordinal()); break; } } } if (instance instanceof IReward) { APIHandler.registerRewardType(instance, name, color).setIcon(stack); } else if (instance instanceof ITrigger) { ITriggerProvider provider = APIHandler.registerTriggerType(instance, name, color).setIcon(stack); if (isCancelable) { provider.setCancelable(); } } else if (instance instanceof ICondition) { APIHandler.registerConditionType(instance, name).setIcon(stack); } else if (instance instanceof IFilter) { APIHandler.registerFilterType(instance, name, color); } } catch (Exception e) { e.printStackTrace(); } } }
/** * Gets the ASMData for all classes annotated with the annotation class. * * @param annotation The annotation to search for. * @return A set of ASMData for classes with the passed annotation. */ public static <A extends Annotation> Set<ASMData> getData (Class<A> annotation) { return getData(asmData, annotation); }
/** * Gets the ASMData for all classes annotated with the annotation class. * * @param table The ASMDataTable. This is generated from forge an can be accessed from the * initialization events. * @param annotation The annotation to search for. * @return A set of ASMData for classes with the passed annotation. */ public static <A extends Annotation> Set<ASMData> getData (ASMDataTable table, Class<A> annotation) { return table.getAll(annotation.getCanonicalName()); }