diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java
index b73ba910e..1ed1ac999 100644
--- a/src/main/java/dan200/computercraft/ComputerCraft.java
+++ b/src/main/java/dan200/computercraft/ComputerCraft.java
@@ -21,7 +21,6 @@ import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.apis.http.options.AddressRule;
import dan200.computercraft.core.apis.http.websocket.Websocket;
-import dan200.computercraft.core.asm.GenericSource;
import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
@@ -39,7 +38,6 @@ import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
import dan200.computercraft.shared.util.Config;
import dan200.computercraft.shared.util.ImpostorRecipe;
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
-import dan200.computercraft.shared.util.ServiceUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -132,7 +130,6 @@ public final class ComputerCraft implements ModInitializer {
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier(ComputerCraft.MOD_ID, "player_creative"), PlayerCreativeLootCondition.TYPE);
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier(ComputerCraft.MOD_ID, "has_id"), HasComputerIdLootCondition.TYPE);
init();
- GenericSource.setup( () -> ServiceUtil.loadServices( GenericSource.class ));
FabricLoader.getInstance().getModContainer(MOD_ID).ifPresent(modContainer -> {
ResourceManagerHelper.registerBuiltinResourcePack(new Identifier(MOD_ID, "classic"), modContainer, ResourcePackActivationType.NORMAL);
ResourceManagerHelper.registerBuiltinResourcePack(new Identifier(MOD_ID, "overhaul"), modContainer, ResourcePackActivationType.NORMAL);
diff --git a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java
index 776fec7ca..4dcb559a9 100644
--- a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java
+++ b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java
@@ -16,6 +16,7 @@ import javax.annotation.Nullable;
import dan200.computercraft.api.ComputerCraftAPI.IComputerCraftAPI;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
+import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.ILuaAPIFactory;
import dan200.computercraft.api.media.IMediaProvider;
import dan200.computercraft.api.network.IPacketNetwork;
@@ -26,6 +27,7 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.core.apis.ApiFactories;
+import dan200.computercraft.core.asm.GenericMethod;
import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.ResourceMount;
import dan200.computercraft.mixin.MinecraftServerAccess;
@@ -143,6 +145,12 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI {
PocketUpgrades.register(upgrade);
}
+ @Override
+ public void registerGenericSource( @Nonnull GenericSource source )
+ {
+ GenericMethod.register( source );
+ }
+
@Nonnull
@Override
public IPacketNetwork getWirelessNetwork() {
diff --git a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java
index 4868de353..9699b6173 100644
--- a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java
+++ b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java
@@ -11,6 +11,7 @@ import javax.annotation.Nullable;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
+import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.ILuaAPIFactory;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.media.IMediaProvider;
@@ -136,6 +137,17 @@ public final class ComputerCraftAPI {
getInstance().registerPeripheralProvider(provider);
}
+ /**
+ * Registers a method source for generic peripherals.
+ *
+ * @param source The method source to register.
+ * @see GenericSource
+ */
+ public static void registerGenericSource( @Nonnull GenericSource source )
+ {
+ getInstance().registerGenericSource( source );
+ }
+
/**
* Registers a new turtle turtle for use in ComputerCraft. After calling this, users should be able to craft Turtles with your new turtle. It is
* recommended to call this during the load() method of your mod.
@@ -238,6 +250,8 @@ public final class ComputerCraftAPI {
void registerPeripheralProvider(@Nonnull IPeripheralProvider provider);
+ void registerGenericSource( @Nonnull GenericSource source );
+
void registerTurtleUpgrade(@Nonnull ITurtleUpgrade upgrade);
void registerBundledRedstoneProvider(@Nonnull IBundledRedstoneProvider provider);
diff --git a/src/main/java/dan200/computercraft/api/lua/GenericSource.java b/src/main/java/dan200/computercraft/api/lua/GenericSource.java
new file mode 100644
index 000000000..6b054ee9b
--- /dev/null
+++ b/src/main/java/dan200/computercraft/api/lua/GenericSource.java
@@ -0,0 +1,58 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.api.lua;
+
+import dan200.computercraft.api.ComputerCraftAPI;
+import dan200.computercraft.api.peripheral.IPeripheral;
+import dan200.computercraft.api.peripheral.IPeripheralProvider;
+import dan200.computercraft.core.asm.LuaMethod;
+import net.minecraft.util.Identifier;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A generic source of {@link LuaMethod} functions.
+ *
+ * Unlike normal objects ({@link IDynamicLuaObject} or {@link IPeripheral}), methods do not target this object but
+ * instead are defined as {@code static} and accept their target as the first parameter. This allows you to inject
+ * methods onto objects you do not own, as well as declaring methods for a specific "trait" (for instance, a
+ * {@link Capability}).
+ *
+ * Currently the "generic peripheral" system is incompatible with normal peripherals. Normal {@link IPeripheralProvider}
+ * or {@link IPeripheral} implementations take priority. Tile entities which use this system are given a peripheral name
+ * determined by their id, rather than any peripheral provider. This will hopefully change in the future, once a suitable
+ * design has been established.
+ *
+ * For example, the main CC: Tweaked mod defines a generic source for inventories, which works on {@link IItemHandler}s:
+ *
+ *
{@code
+ * public class InventoryMethods implements GenericSource {
+ * @LuaFunction( mainThread = true )
+ * public static int size(IItemHandler inventory) {
+ * return inventory.getSlots();
+ * }
+ *
+ * // ...
+ * }
+ * }
+ *
+ * @see ComputerCraftAPI#registerGenericSource(GenericSource)
+ * @see ComputerCraftAPI#registerGenericCapability(Capability) New capabilities (those not built into Forge) must be
+ * explicitly given to the generic peripheral system, as there is no way to enumerate all capabilities.
+ */
+public interface GenericSource
+{
+ /**
+ * A unique identifier for this generic source.
+ *
+ * This is currently unused, but may be used in the future to allow disabling specific sources. It is recommended
+ * to return an identifier using your mod's ID.
+ *
+ * @return This source's identifier.
+ */
+ @Nonnull
+ Identifier id();
+}
diff --git a/src/main/java/dan200/computercraft/core/asm/Generator.java b/src/main/java/dan200/computercraft/core/asm/Generator.java
index e600880c0..a3c028076 100644
--- a/src/main/java/dan200/computercraft/core/asm/Generator.java
+++ b/src/main/java/dan200/computercraft/core/asm/Generator.java
@@ -36,10 +36,7 @@ import com.google.common.cache.LoadingCache;
import com.google.common.primitives.Primitives;
import com.google.common.reflect.TypeToken;
import dan200.computercraft.ComputerCraft;
-import dan200.computercraft.api.lua.IArguments;
-import dan200.computercraft.api.lua.LuaException;
-import dan200.computercraft.api.lua.LuaFunction;
-import dan200.computercraft.api.lua.MethodResult;
+import dan200.computercraft.api.lua.*;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
@@ -120,10 +117,9 @@ public final class Generator {
this.addMethod(methods, method, annotation, instance);
}
- for (GenericSource.GenericMethod method : GenericSource.GenericMethod.all()) {
- if (!method.target.isAssignableFrom(klass)) {
- continue;
- }
+ for( GenericMethod method : GenericMethod.all() )
+ {
+ if( !method.target.isAssignableFrom( klass ) ) continue;
T instance = this.methodCache.getUnchecked(method.method)
.orElse(null);
diff --git a/src/main/java/dan200/computercraft/core/asm/GenericMethod.java b/src/main/java/dan200/computercraft/core/asm/GenericMethod.java
new file mode 100644
index 000000000..ebd589112
--- /dev/null
+++ b/src/main/java/dan200/computercraft/core/asm/GenericMethod.java
@@ -0,0 +1,85 @@
+package dan200.computercraft.core.asm;
+
+import dan200.computercraft.ComputerCraft;
+import dan200.computercraft.api.lua.GenericSource;
+import dan200.computercraft.api.lua.LuaFunction;
+
+import javax.annotation.Nonnull;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * A generic method is a method belonging to a {@link GenericSource} with a known target.
+ */
+public class GenericMethod
+{
+ final Method method;
+ final LuaFunction annotation;
+ final Class> target;
+
+ private static final List sources = new ArrayList<>();
+ private static List cache;
+
+ GenericMethod( Method method, LuaFunction annotation, Class> target )
+ {
+ this.method = method;
+ this.annotation = annotation;
+ this.target = target;
+ }
+
+ /**
+ * Find all public static methods annotated with {@link LuaFunction} which belong to a {@link GenericSource}.
+ *
+ * @return All available generic methods.
+ */
+ static List all()
+ {
+ if( cache != null ) return cache;
+ return cache = sources.stream()
+ .flatMap( x -> Arrays.stream( x.getClass().getDeclaredMethods() ) )
+ .map( method ->
+ {
+ LuaFunction annotation = method.getAnnotation( LuaFunction.class );
+ if( annotation == null ) return null;
+
+ if( !Modifier.isStatic( method.getModifiers() ) )
+ {
+ ComputerCraft.log.error( "GenericSource method {}.{} should be static.", method.getDeclaringClass(), method.getName() );
+ return null;
+ }
+
+ Type[] types = method.getGenericParameterTypes();
+ if( types.length == 0 )
+ {
+ ComputerCraft.log.error( "GenericSource method {}.{} has no parameters.", method.getDeclaringClass(), method.getName() );
+ return null;
+ }
+
+ Class> target = Reflect.getRawType( method, types[0], false );
+ if( target == null ) return null;
+
+ return new GenericMethod( method, annotation, target );
+ } )
+ .filter( Objects::nonNull )
+ .collect( Collectors.toList() );
+ }
+
+
+ public static synchronized void register( @Nonnull GenericSource source )
+ {
+ Objects.requireNonNull( source, "Source cannot be null" );
+
+ if( cache != null )
+ {
+ ComputerCraft.log.warn( "Registering a generic source {} after cache has been built. This source will be ignored.", cache );
+ }
+
+ sources.add( source );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/core/asm/GenericSource.java b/src/main/java/dan200/computercraft/core/asm/GenericSource.java
deleted file mode 100644
index 9c870c814..000000000
--- a/src/main/java/dan200/computercraft/core/asm/GenericSource.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * This file is part of ComputerCraft - http://www.computercraft.info
- * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
- * Send enquiries to dratcliffe@gmail.com
- */
-package dan200.computercraft.core.asm;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Type;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import javax.annotation.Nonnull;
-
-import dan200.computercraft.ComputerCraft;
-import dan200.computercraft.api.lua.LuaFunction;
-
-import net.minecraft.util.Identifier;
-
-/**
- * A generic source of {@link LuaMethod} functions. This allows for injecting methods onto objects you do not own.
- *
- * Unlike conventional Lua objects, the annotated methods should be {@code static}, with their target as the first parameter.
- */
-public interface GenericSource {
- /**
- * Register a stream of generic sources.
- *
- * @param sources The source of generic methods.
- */
- static void setup(Supplier> sources) {
- GenericMethod.sources = sources;
- }
-
- /**
- * A unique identifier for this generic source. This may be used in the future to allow disabling specific sources.
- *
- * @return This source's identifier.
- */
- @Nonnull
- Identifier id();
-
- /**
- * A generic method is a method belonging to a {@link GenericSource} with a known target.
- */
- class GenericMethod {
- static Supplier> sources;
- private static List cache;
- final Method method;
- final LuaFunction annotation;
- final Class> target;
-
- GenericMethod(Method method, LuaFunction annotation, Class> target) {
- this.method = method;
- this.annotation = annotation;
- this.target = target;
- }
-
- /**
- * Find all public static methods annotated with {@link LuaFunction} which belong to a {@link GenericSource}.
- *
- * @return All available generic methods.
- */
- static List all() {
- if (cache != null) {
- return cache;
- }
- if (sources == null) {
- ComputerCraft.log.warn("Getting GenericMethods without a provider");
- return cache = Collections.emptyList();
- }
-
- return cache = sources.get()
- .flatMap(x -> Arrays.stream(x.getClass()
- .getDeclaredMethods()))
- .map(method -> {
- LuaFunction annotation = method.getAnnotation(LuaFunction.class);
- if (annotation == null) {
- return null;
- }
-
- if (!Modifier.isStatic(method.getModifiers())) {
- ComputerCraft.log.error("GenericSource method {}.{} should be static.",
- method.getDeclaringClass(),
- method.getName());
- return null;
- }
-
- Type[] types = method.getGenericParameterTypes();
- if (types.length == 0) {
- ComputerCraft.log.error("GenericSource method {}.{} has no parameters.",
- method.getDeclaringClass(),
- method.getName());
- return null;
- }
-
- Class> target = Reflect.getRawType(method, types[0], false);
- if (target == null) {
- return null;
- }
-
- return new GenericMethod(method, annotation, target);
- })
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
- }
- }
-}
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java
index c03576ee6..86267a3ba 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java
@@ -5,13 +5,12 @@
*/
package dan200.computercraft.shared.peripheral.generic.methods;
-import com.google.auto.service.AutoService;
import dan200.computercraft.ComputerCraft;
+import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
-import dan200.computercraft.core.asm.GenericSource;
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.ItemStorage;
@@ -34,14 +33,13 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel
*
* @cc.module inventory
*/
-@AutoService( GenericSource.class )
public class InventoryMethods implements GenericSource
{
@Nonnull
@Override
public Identifier id()
{
- return new Identifier(ComputerCraft.MOD_ID, "inventory" );
+ return new Identifier( ComputerCraft.MOD_ID, "inventory" );
}
/**
diff --git a/src/main/java/dan200/computercraft/shared/util/ServiceUtil.java b/src/main/java/dan200/computercraft/shared/util/ServiceUtil.java
deleted file mode 100644
index 9e4772450..000000000
--- a/src/main/java/dan200/computercraft/shared/util/ServiceUtil.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * This file is part of ComputerCraft - http://www.computercraft.info
- * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
- * Send enquiries to dratcliffe@gmail.com
- */
-package dan200.computercraft.shared.util;
-
-import dan200.computercraft.ComputerCraft;
-import dan200.computercraft.api.ComputerCraftAPI;
-import org.objectweb.asm.Type;
-
-import java.util.List;
-import java.util.ServiceLoader;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-public final class ServiceUtil
-{
- private static final Type AUTO_SERVICE = Type.getType( "Lcom/google/auto/service/AutoService;" );
-
- private ServiceUtil()
- {
- }
-
- public static Stream loadServices( Class target )
- {
- return StreamSupport.stream( ServiceLoader.load( target, ServiceUtil.class.getClassLoader() ).spliterator(), false );
- }
-}