mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-09-12 07:15:59 +00:00
Expose GenericSource to the public API
- Remove the service provider code and require people to explicitly register these. This is definitely more ugly, but easier than people pulling in AutoService or similar! - Add an API for registering capabilities. - Expand the doc comments a little. Not sure how useful they'll be, but let's see! There's still so much work to be done on this, but it's a "good enough" first step.
This commit is contained in:
@@ -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);
|
||||
|
@@ -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() {
|
||||
|
@@ -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);
|
||||
|
@@ -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:
|
||||
*
|
||||
* <pre>{@code
|
||||
* public class InventoryMethods implements GenericSource {
|
||||
* @LuaFunction( mainThread = true )
|
||||
* public static int size(IItemHandler inventory) {
|
||||
* return inventory.getSlots();
|
||||
* }
|
||||
*
|
||||
* // ...
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @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();
|
||||
}
|
@@ -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<T> {
|
||||
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);
|
||||
|
@@ -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<GenericSource> sources = new ArrayList<>();
|
||||
private static List<GenericMethod> 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<GenericMethod> 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 );
|
||||
}
|
||||
}
|
@@ -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<Stream<GenericSource>> 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<Stream<GenericSource>> sources;
|
||||
private static List<GenericMethod> 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<GenericMethod> 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());
|
||||
}
|
||||
}
|
||||
}
|
@@ -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" );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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 <T> Stream<T> loadServices( Class<T> target )
|
||||
{
|
||||
return StreamSupport.stream( ServiceLoader.load( target, ServiceUtil.class.getClassLoader() ).spliterator(), false );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user