Restored the old PluginLoader as AddonLoader
Introduce a new `IAEAddon` interface, which has to be implemented and annotated wit `@AEAddon` to obtain access to our public API.
This commit is contained in:
@@ -26,12 +26,16 @@ import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Use this annotation on a class in your Mod to have it instantiated during the
|
||||
* initialization phase of Applied Energistics. AE expects your class to have a
|
||||
* single constructor and can supply certain arguments to your constructor using
|
||||
* dependency injection.
|
||||
* initialization phase of Applied Energistics.
|
||||
*
|
||||
* The class also needs to implement {@link IAEAddon}.
|
||||
*
|
||||
* AE expects your class to have a single constructor without any parameters.
|
||||
*
|
||||
* This is the only way to get access to the public {@link IAppEngApi} instance.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Documented
|
||||
public @interface AEPlugin {
|
||||
public @interface AEAddon {
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks interfaces that can be used as injectable constructor arguments for an
|
||||
* {@link AEPlugin}.
|
||||
* {@link AEAddon}.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 TeamAppliedEnergistics
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package appeng.api;
|
||||
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
|
||||
/**
|
||||
* Every AE2 addon requiring access to {@link IAppEngApi}, needs to provide at
|
||||
* least one class implementing this interface.
|
||||
*
|
||||
* Further it requires the class to be annotated with {@link AEAddon}.
|
||||
*
|
||||
*/
|
||||
public interface IAEAddon {
|
||||
|
||||
/**
|
||||
* This gets called once the API is successfully constructed and ready to be
|
||||
* used.
|
||||
*
|
||||
* For now this happens during {@link FMLCommonSetupEvent}.
|
||||
*
|
||||
* Each addon is responsible to maintain a reference to {@link IAppEngApi} for
|
||||
* future use. Otherwise there is no alternative to access it later.
|
||||
*
|
||||
* @param api The API instance when ready.
|
||||
*/
|
||||
void onAPIAvailable(IAppEngApi api);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of Applied Energistics 2.
|
||||
* Copyright (c) 2013 - 2020, AlgorithmX2, All rights reserved.
|
||||
*
|
||||
* Applied Energistics 2 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Applied Energistics 2 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 Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
|
||||
*/
|
||||
|
||||
package appeng.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData.AnnotationData;
|
||||
|
||||
import appeng.api.AEAddon;
|
||||
import appeng.api.IAEAddon;
|
||||
import appeng.api.IAppEngApi;
|
||||
|
||||
/**
|
||||
* Loads AE addons on startup and provides them with an {@link IAppEngApi}
|
||||
* instance.
|
||||
*/
|
||||
class AddonLoader {
|
||||
|
||||
public static void loadAddons(IAppEngApi api) {
|
||||
final Type annotationType = Type.getType(AEAddon.class);
|
||||
final Collection<ModFileScanData> allScanData = ModList.get().getAllScanData();
|
||||
|
||||
allScanData.stream().map(ModFileScanData::getAnnotations).flatMap(Set::stream)
|
||||
.filter(a -> Objects.equals(a.getAnnotationType(), annotationType)).map(AnnotationData::getMemberName)
|
||||
.forEach(className -> {
|
||||
try {
|
||||
final Class<?> clazz = Class.forName(className);
|
||||
final Class<? extends IAEAddon> instanceClass = clazz.asSubclass(IAEAddon.class);
|
||||
final IAEAddon instance = instanceClass.newInstance();
|
||||
|
||||
instance.onAPIAvailable(api);
|
||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
|
||||
| LinkageError e) {
|
||||
AELog.error("Failed to load: %s", className, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -71,14 +71,24 @@ import appeng.client.render.cablebus.CableBusModelLoader;
|
||||
import appeng.client.render.cablebus.P2PTunnelFrequencyModel;
|
||||
import appeng.client.render.crafting.CraftingCubeModelLoader;
|
||||
import appeng.client.render.crafting.EncodedPatternModelLoader;
|
||||
import appeng.client.render.model.*;
|
||||
import appeng.client.render.model.BiometricCardModel;
|
||||
import appeng.client.render.model.ColorApplicatorModel;
|
||||
import appeng.client.render.model.DriveModel;
|
||||
import appeng.client.render.model.GlassModel;
|
||||
import appeng.client.render.model.MemoryCardModel;
|
||||
import appeng.client.render.model.SkyCompassModel;
|
||||
import appeng.client.render.model.UVLModelLoader;
|
||||
import appeng.client.render.spatial.SpatialPylonModel;
|
||||
import appeng.core.crash.ModCrashEnhancement;
|
||||
import appeng.core.features.registries.PartModels;
|
||||
import appeng.core.stats.AdvancementTriggers;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.worlddata.WorldData;
|
||||
import appeng.entity.*;
|
||||
import appeng.entity.ChargedQuartzEntity;
|
||||
import appeng.entity.GrowingCrystalEntity;
|
||||
import appeng.entity.SingularityEntity;
|
||||
import appeng.entity.TinyTNTPrimedEntity;
|
||||
import appeng.entity.TinyTNTPrimedRenderer;
|
||||
import appeng.hooks.TickHandler;
|
||||
import appeng.integration.Integrations;
|
||||
import appeng.parts.PartPlacement;
|
||||
@@ -157,6 +167,7 @@ public final class AppEng {
|
||||
|
||||
registerNetworkHandler();
|
||||
|
||||
AddonLoader.loadAddons(Api.INSTANCE);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
@@ -253,10 +264,6 @@ public final class AppEng {
|
||||
//
|
||||
// AELog.info( "Pre Initialization ( ended after " + watch.elapsed( TimeUnit.MILLISECONDS ) + "ms )" );
|
||||
//
|
||||
// // Instantiate all Plugins
|
||||
// List<Object> injectables = Lists.newArrayList(
|
||||
// AEApi.instance() );
|
||||
// new PluginLoader().loadPlugins( injectables, event.getAsmData() );
|
||||
// }
|
||||
|
||||
private void startService(final String serviceName, final Thread thread) {
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* This file is part of Applied Energistics 2.
|
||||
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
|
||||
*
|
||||
* Applied Energistics 2 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Applied Energistics 2 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 Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
|
||||
*/
|
||||
|
||||
package appeng.core;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
|
||||
import appeng.api.AEInjectable;
|
||||
import appeng.api.AEPlugin;
|
||||
|
||||
/**
|
||||
* Loads AE plugins on startup and provides them with access to various
|
||||
* components of the AE API.
|
||||
*/
|
||||
class PluginLoader {
|
||||
|
||||
public void loadPlugins(Collection<Object> injectables) {
|
||||
Map<Class<?>, Object> injectableMap = mapInjectables(injectables);
|
||||
findAndInstantiatePlugins(injectableMap);
|
||||
}
|
||||
|
||||
private static void findAndInstantiatePlugins(Map<Class<?>, Object> injectableMap) {
|
||||
Type aType = Type.getType(AEPlugin.class);
|
||||
Set<ModFileScanData.AnnotationData> allAnnotated = ModList.get().getAllScanData().stream()
|
||||
.map(ModFileScanData::getAnnotations).flatMap(Collection::stream)
|
||||
.filter(a -> a.getAnnotationType().equals(aType)).filter(a -> a.getTargetType() == ElementType.TYPE)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (ModFileScanData.AnnotationData candidate : allAnnotated) {
|
||||
|
||||
String cName = candidate.getMemberName();
|
||||
Class<?> aClass;
|
||||
try {
|
||||
aClass = Class.forName(cName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
AELog.error(e, "Couldn't find annotated AE plugin class " + cName);
|
||||
throw new RuntimeException("Couldn't find annotated AE plugin class " + cName, e);
|
||||
}
|
||||
|
||||
// Try instantiating the plugin
|
||||
try {
|
||||
Object plugin = instantiatePlugin(aClass, injectableMap);
|
||||
AELog.info("Loaded AE2 Plugin {}", plugin.getClass());
|
||||
} catch (Exception e) {
|
||||
AELog.error(e, "Unable to instantiate AE plugin " + cName);
|
||||
throw new RuntimeException("Unable to instantiate AE plugin " + cName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Object instantiatePlugin(Class<?> aClass, Map<Class<?>, Object> injectableMap) throws Exception {
|
||||
|
||||
Constructor<?>[] constructors = aClass.getDeclaredConstructors();
|
||||
|
||||
if (constructors.length == 0) {
|
||||
// This is the default no-arg constructor, although it seems pointless to
|
||||
// instantiate anything but not take
|
||||
// any AE dependencies as parameters
|
||||
return aClass.newInstance();
|
||||
} else if (constructors.length != 1) {
|
||||
throw new IllegalArgumentException("Expected a single constructor, but found: " + constructors.length);
|
||||
}
|
||||
|
||||
Constructor<?> constructor = constructors[0];
|
||||
constructor.setAccessible(true);
|
||||
|
||||
Object[] args = findInjectables(constructor, injectableMap);
|
||||
|
||||
return constructor.newInstance(args);
|
||||
}
|
||||
|
||||
private static Object[] findInjectables(Constructor<?> constructor, Map<Class<?>, Object> injectableMap) {
|
||||
|
||||
Class<?>[] types = constructor.getParameterTypes();
|
||||
Object[] args = new Object[types.length];
|
||||
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
args[i] = injectableMap.get(types[i]);
|
||||
if (args[i] == null) {
|
||||
throw new IllegalArgumentException("Constructor has parameter of type " + types[i]
|
||||
+ " which is not an injectable type." + " Please see the documentation for @AEPlugin.");
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
private static Map<Class<?>, Object> mapInjectables(Collection<Object> injectables) {
|
||||
ImmutableMap.Builder<Class<?>, Object> builder = ImmutableMap.builder();
|
||||
|
||||
for (Object injectable : injectables) {
|
||||
// Get all super-interfaces that were annotated with @AEInjectable
|
||||
Set<Class<?>> injectableIfs = getInjectableInterfaces(injectable.getClass());
|
||||
for (Class<?> injectableIf : injectableIfs) {
|
||||
builder.put(injectableIf, injectable);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static Set<Class<?>> getInjectableInterfaces(Class<?> aClass) {
|
||||
Set<Class<?>> hierarchy = new HashSet<>();
|
||||
getFullHierarchy(aClass, hierarchy);
|
||||
|
||||
return hierarchy.stream().filter(c -> c.getAnnotation(AEInjectable.class) != null).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
// Recursively gather all superclasses and superinterfaces of the given class
|
||||
// and put them into the given collection
|
||||
private static void getFullHierarchy(Class<?> aClass, Set<Class<?>> classes) {
|
||||
classes.add(aClass);
|
||||
for (Class<?> anIf : aClass.getInterfaces()) {
|
||||
getFullHierarchy(anIf, classes);
|
||||
}
|
||||
if (aClass.getSuperclass() != null) {
|
||||
getFullHierarchy(aClass.getSuperclass(), classes);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user