diff --git a/src/api/java/WayofTime/bloodmagic/api/BloodMagicPlugin.java b/src/api/java/WayofTime/bloodmagic/api/BloodMagicPlugin.java
index 0d2bf397..3cbeb4b9 100644
--- a/src/api/java/WayofTime/bloodmagic/api/BloodMagicPlugin.java
+++ b/src/api/java/WayofTime/bloodmagic/api/BloodMagicPlugin.java
@@ -13,4 +13,17 @@ import java.lang.annotation.Target;
@Target(ElementType.TYPE)
public @interface BloodMagicPlugin {
+ /**
+ * This annotation will inject the active {@link IBloodMagicAPI} into a {@code static} field of the same
+ * type. Fields with invalid types will be ignored and an error will be logged.
+ *
+ * These fields are populated during {@link net.minecraftforge.fml.common.event.FMLPreInitializationEvent}.
+ *
+ * {@code public static @BloodMagicPlugin.Inject IBloodMagicAPI API_INSTANCE = null;}
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ @interface Inject {
+
+ }
}
diff --git a/src/api/java/WayofTime/bloodmagic/api/IBloodMagicAPI.java b/src/api/java/WayofTime/bloodmagic/api/IBloodMagicAPI.java
index 776bee6d..11ce4bce 100644
--- a/src/api/java/WayofTime/bloodmagic/api/IBloodMagicAPI.java
+++ b/src/api/java/WayofTime/bloodmagic/api/IBloodMagicAPI.java
@@ -6,6 +6,10 @@ import javax.annotation.Nonnull;
/**
* The main interface between a plugin and Blood Magic's internals.
+ *
+ * This API is intended for compatibility between other mods and Blood Magic. More advanced integration is out of the scope of this API and are considered "addons".
+ *
+ * To get an instance of this without actually creating an {@link IBloodMagicPlugin}, use {@link BloodMagicPlugin.Inject}.
*/
public interface IBloodMagicAPI {
diff --git a/src/api/java/WayofTime/bloodmagic/api/IBloodMagicPlugin.java b/src/api/java/WayofTime/bloodmagic/api/IBloodMagicPlugin.java
index 78ac7036..c18d3c1d 100644
--- a/src/api/java/WayofTime/bloodmagic/api/IBloodMagicPlugin.java
+++ b/src/api/java/WayofTime/bloodmagic/api/IBloodMagicPlugin.java
@@ -7,9 +7,20 @@ package WayofTime.bloodmagic.api;
public interface IBloodMagicPlugin {
/**
- * Register mod content with the API
+ * Register mod content with the API. Called during {@link net.minecraftforge.fml.common.event.FMLInitializationEvent}.
*
* @param api The active instance of the {@link IBloodMagicAPI}
*/
- void register(IBloodMagicAPI api);
+ default void register(IBloodMagicAPI api) {
+ // No-op
+ }
+
+ /**
+ * Register recipes with the API. Called during {@link net.minecraftforge.event.RegistryEvent.Register}.
+ *
+ * @param recipeRegistrar The active instance of the {@link IBloodMagicRecipeRegistrar}
+ */
+ default void registerRecipes(IBloodMagicRecipeRegistrar recipeRegistrar) {
+ // No-op
+ }
}
diff --git a/src/main/java/WayofTime/bloodmagic/BloodMagic.java b/src/main/java/WayofTime/bloodmagic/BloodMagic.java
index 0b1122f4..ce2f8153 100644
--- a/src/main/java/WayofTime/bloodmagic/BloodMagic.java
+++ b/src/main/java/WayofTime/bloodmagic/BloodMagic.java
@@ -73,6 +73,7 @@ public class BloodMagic {
configDir = new File(event.getModConfigurationDirectory(), "bloodmagic");
PLUGINS.addAll(PluginUtil.gatherPlugins(event.getAsmData()));
+ PluginUtil.injectAPIInstances(PluginUtil.gatherInjections(event.getAsmData()));
ModTranquilityHandlers.init();
ModDungeons.init();
@@ -84,7 +85,7 @@ public class BloodMagic {
public void init(FMLInitializationEvent event) {
BloodMagicPacketHandler.init();
- PluginUtil.registerPlugins(PLUGINS);
+ PluginUtil.handlePluginStep(PluginUtil.RegistrationStep.PLUGIN_REGISTER);
ModRecipes.init();
ModRituals.initRituals();
diff --git a/src/main/java/WayofTime/bloodmagic/api/impl/BloodMagicCorePlugin.java b/src/main/java/WayofTime/bloodmagic/api/impl/BloodMagicCorePlugin.java
index 9f98f857..453d5174 100644
--- a/src/main/java/WayofTime/bloodmagic/api/impl/BloodMagicCorePlugin.java
+++ b/src/main/java/WayofTime/bloodmagic/api/impl/BloodMagicCorePlugin.java
@@ -6,6 +6,7 @@ import WayofTime.bloodmagic.api.BloodMagicPlugin;
import WayofTime.bloodmagic.api.IBloodMagicAPI;
import WayofTime.bloodmagic.api.IBloodMagicPlugin;
import WayofTime.bloodmagic.altar.EnumAltarComponent;
+import WayofTime.bloodmagic.api.IBloodMagicRecipeRegistrar;
import WayofTime.bloodmagic.block.BlockBloodRune;
import WayofTime.bloodmagic.block.BlockDecorative;
import WayofTime.bloodmagic.block.enums.BloodRuneType;
@@ -78,11 +79,14 @@ public class BloodMagicCorePlugin implements IBloodMagicPlugin {
BlockBloodRune bloodRune = (BlockBloodRune) RegistrarBloodMagicBlocks.BLOOD_RUNE;
for (BloodRuneType runeType : BloodRuneType.values())
api.registerAltarComponent(bloodRune.getDefaultState().withProperty(bloodRune.getProperty(), runeType), EnumAltarComponent.BLOODRUNE.name());
+ }
- RegistrarBloodMagicRecipes.registerAltarRecipes(api.getRecipeRegistrar());
- RegistrarBloodMagicRecipes.registerAlchemyTableRecipes(api.getRecipeRegistrar());
- RegistrarBloodMagicRecipes.registerTartaricForgeRecipes(api.getRecipeRegistrar());
- RegistrarBloodMagicRecipes.registerAlchemyArrayRecipes(api.getRecipeRegistrar());
+ @Override
+ public void registerRecipes(IBloodMagicRecipeRegistrar recipeRegistrar) {
+ RegistrarBloodMagicRecipes.registerAltarRecipes((BloodMagicRecipeRegistrar) recipeRegistrar);
+ RegistrarBloodMagicRecipes.registerAlchemyTableRecipes((BloodMagicRecipeRegistrar) recipeRegistrar);
+ RegistrarBloodMagicRecipes.registerTartaricForgeRecipes((BloodMagicRecipeRegistrar) recipeRegistrar);
+ RegistrarBloodMagicRecipes.registerAlchemyArrayRecipes((BloodMagicRecipeRegistrar) recipeRegistrar);
}
private static void handleConfigValues(BloodMagicAPI api) {
diff --git a/src/main/java/WayofTime/bloodmagic/core/RegistrarBloodMagicRecipes.java b/src/main/java/WayofTime/bloodmagic/core/RegistrarBloodMagicRecipes.java
index 298b7480..85ceebe4 100644
--- a/src/main/java/WayofTime/bloodmagic/core/RegistrarBloodMagicRecipes.java
+++ b/src/main/java/WayofTime/bloodmagic/core/RegistrarBloodMagicRecipes.java
@@ -12,6 +12,7 @@ import WayofTime.bloodmagic.item.alchemy.ItemCuttingFluid;
import WayofTime.bloodmagic.item.alchemy.ItemLivingArmourPointsUpgrade;
import WayofTime.bloodmagic.item.soul.ItemSoulGem;
import WayofTime.bloodmagic.item.types.ComponentTypes;
+import WayofTime.bloodmagic.util.PluginUtil;
import com.google.common.collect.Sets;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
@@ -51,6 +52,8 @@ public class RegistrarBloodMagicRecipes {
OreDictionary.registerOre("dustIron", ComponentTypes.SAND_IRON.getStack());
OreDictionary.registerOre("dustGold", ComponentTypes.SAND_GOLD.getStack());
OreDictionary.registerOre("dustCoal", ComponentTypes.SAND_COAL.getStack());
+
+ PluginUtil.handlePluginStep(PluginUtil.RegistrationStep.RECIPE_REGISTER);
}
public static void registerAltarRecipes(BloodMagicRecipeRegistrar registrar) {
diff --git a/src/main/java/WayofTime/bloodmagic/util/PluginUtil.java b/src/main/java/WayofTime/bloodmagic/util/PluginUtil.java
index 2faf5d38..aeed6037 100644
--- a/src/main/java/WayofTime/bloodmagic/util/PluginUtil.java
+++ b/src/main/java/WayofTime/bloodmagic/util/PluginUtil.java
@@ -1,17 +1,23 @@
package WayofTime.bloodmagic.util;
+import WayofTime.bloodmagic.BloodMagic;
import WayofTime.bloodmagic.api.BloodMagicPlugin;
+import WayofTime.bloodmagic.api.IBloodMagicAPI;
import WayofTime.bloodmagic.api.IBloodMagicPlugin;
import WayofTime.bloodmagic.api.impl.BloodMagicAPI;
import WayofTime.bloodmagic.api.impl.BloodMagicCorePlugin;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
+import net.minecraftforge.common.util.EnumHelper;
import net.minecraftforge.fml.common.discovery.ASMDataTable;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
public class PluginUtil {
@@ -20,7 +26,7 @@ public class PluginUtil {
public static List> gatherPlugins(ASMDataTable dataTable) {
Stopwatch stopwatch = Stopwatch.createStarted();
List> discoveredAnnotations = Lists.newArrayList();
- Set discoveredPlugins = dataTable.getAll(BloodMagicPlugin.class.getCanonicalName());
+ Set discoveredPlugins = dataTable.getAll(BloodMagicPlugin.class.getName());
for (ASMDataTable.ASMData data : discoveredPlugins) {
try {
@@ -47,20 +53,85 @@ public class PluginUtil {
return discoveredAnnotations;
}
- public static void registerPlugins(List> plugins) {
- Stopwatch total = Stopwatch.createStarted();
- int errors = 0;
- for (Pair plugin : plugins) {
- Stopwatch per = Stopwatch.createStarted();
+ @Nonnull
+ public static List gatherInjections(ASMDataTable dataTable) {
+ Stopwatch stopwatch = Stopwatch.createStarted();
+ List injectees = Lists.newArrayList();
+ Set discoveredInjectees = dataTable.getAll(BloodMagicPlugin.Inject.class.getName());
+
+ for (ASMDataTable.ASMData data : discoveredInjectees) {
try {
- plugin.getLeft().register(BloodMagicAPI.INSTANCE);
+ Class> asmClass = Class.forName(data.getClassName());
+ Field toInject = asmClass.getDeclaredField(data.getObjectName());
+ if (toInject.getType() != IBloodMagicAPI.class) {
+ BMLog.API.error("Mod requested API injection on field {}.{} which is an invalid type.", data.getClassName(), data.getObjectName());
+ continue;
+ }
+
+ BMLog.API.info("Discovered injection request at {}.{}", data.getClassName(), data.getObjectName());
+ injectees.add(toInject);
} catch (Exception e) {
- errors++;
- BMLog.DEFAULT.error("Error loading plugin at {}: {}: {}", plugin.getLeft().getClass(), e.getClass().getSimpleName(), e.getMessage());
+ e.printStackTrace();
}
- BMLog.API.info("Registered plugin at {} in {}", plugin.getLeft().getClass(), per.stop());
}
- BMLog.API.info("Registered {} plugins with {} errors in {}", plugins.size() - errors, errors, total.stop());
+ BMLog.API.info("Discovered {} potential API injection(s) in {}", injectees.size(), stopwatch.stop());
+ return injectees;
+ }
+
+ public static void handlePluginStep(RegistrationStep step) {
+ Stopwatch total = Stopwatch.createStarted();
+ int errors = 0;
+ for (Pair plugin : BloodMagic.PLUGINS) {
+ Stopwatch per = Stopwatch.createStarted();
+ try {
+ step.getConsumer().accept(plugin);
+ } catch (Exception e) {
+ errors++;
+ BMLog.DEFAULT.error("Error handling plugin step {} at {}: {}: {}", step, plugin.getLeft().getClass(), e.getClass().getSimpleName(), e.getMessage());
+ }
+ BMLog.API.info("Handled plugin step {} at {} in {}", step, plugin.getLeft().getClass(), per.stop());
+ }
+
+ BMLog.API.info("Handled {} plugin(s) at step {} with {} errors in {}", BloodMagic.PLUGINS.size() - errors, step, errors, total.stop());
+ }
+
+ public static void injectAPIInstances(List injectees) {
+ Stopwatch total = Stopwatch.createStarted();
+ int errors = 0;
+
+ for (Field injectee : injectees) {
+ Stopwatch per = Stopwatch.createStarted();
+ if (!Modifier.isStatic(injectee.getModifiers()))
+ continue;
+
+ try {
+ EnumHelper.setFailsafeFieldValue(injectee, null, BloodMagicAPI.INSTANCE);
+ } catch (Exception e) {
+ errors++;
+ BMLog.DEFAULT.error("Error injecting API instance at {}.{}", injectee.getDeclaringClass().getCanonicalName(), injectee.getName());
+ }
+ BMLog.API.info("Injected API instance at {}.{} in {}", injectee.getDeclaringClass().getCanonicalName(), injectee.getName(), per.stop());
+ }
+
+ BMLog.API.info("Injected API {} times with {} errors in {}", injectees.size() - errors, errors, total.stop());
+ }
+
+ public enum RegistrationStep {
+
+ PLUGIN_REGISTER(p -> p.getLeft().register(BloodMagicAPI.INSTANCE)),
+ RECIPE_REGISTER(p -> p.getLeft().registerRecipes(BloodMagicAPI.INSTANCE.getRecipeRegistrar()))
+ ;
+
+ private final Consumer> consumer;
+
+ RegistrationStep(Consumer> consumer) {
+ this.consumer = consumer;
+ }
+
+ @Nonnull
+ public Consumer> getConsumer() {
+ return consumer;
+ }
}
}