1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-13 19:50:31 +00:00

Allow peripherals to have multiple types (#963)

Peripherals can now have multiple types:
 - A single primary type. This is the same as the current idea of a
   type - some identifier which (mostly) uniquely identifies this kind
   of peripheral. For instance, "speaker" or "minecraft:chest".

 - 0 or more "additional" types. These are more like traits, and
   describe what other behaviour the peripheral has - is it an
   inventory? Does it supply additional peripherals (like a wired
   modem)?.

This is mostly intended for the generic peripheral system, but it might
prove useful elsewhere too - we'll have to see!

 - peripheral.getType (and modem.getTypeRemote) now returns 1 or more
   values, rather than exactly one.
 - Add a new peripheral.hasType (and modem.hasTypeRemote) function which
   determines if a peripheral has the given type (primary or
   additional).
 - Change peripheral.find and all internal peripheral methods to use
   peripheral.hasType instead.
 - Update the peripherals program to show all types

This effectively allows you to do things like
`peripheral.find("inventory")` to find all inventories.

This also rewrites the introduction to the peripheral API, hopefully
making it a little more useful.
This commit is contained in:
Jonathan Coates 2021-11-29 17:37:30 +00:00 committed by GitHub
parent 298f339376
commit 53811f8169
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 405 additions and 53 deletions

View File

@ -2,7 +2,7 @@
module: [kind=event] redstone
---
The @{redstone} event is fired whenever any redstone inputs on the computer change.
The @{event!redstone} event is fired whenever any redstone inputs on the computer change.
## Example
Prints a message when a redstone input changes:

View File

@ -10,6 +10,8 @@ import net.minecraftforge.common.capabilities.Capability;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Set;
/**
* The interface that defines a peripheral.
@ -31,6 +33,18 @@ public interface IPeripheral
@Nonnull
String getType();
/**
* Return additional types/traits associated with this object.
*
* @return A collection of additional object traits.
* @see PeripheralType#getAdditionalTypes()
*/
@Nonnull
default Set<String> getAdditionalTypes()
{
return Collections.emptySet();
}
/**
* Is called when when a computer is attaching to the peripheral.
*

View File

@ -6,9 +6,13 @@
package dan200.computercraft.api.peripheral;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
/**
* The type of a {@link GenericPeripheral}.
@ -18,13 +22,19 @@ import javax.annotation.Nullable;
*/
public final class PeripheralType
{
private static final PeripheralType UNTYPED = new PeripheralType( null );
private static final PeripheralType UNTYPED = new PeripheralType( null, Collections.emptySet() );
private final String type;
private final Set<String> additionalTypes;
public PeripheralType( String type )
public PeripheralType( String type, Set<String> additionalTypes )
{
this.type = type;
this.additionalTypes = additionalTypes;
if( additionalTypes.contains( null ) )
{
throw new IllegalArgumentException( "All additional types must be non-null" );
}
}
/**
@ -46,7 +56,55 @@ public final class PeripheralType
public static PeripheralType ofType( @Nonnull String type )
{
if( Strings.isNullOrEmpty( type ) ) throw new IllegalArgumentException( "type cannot be null or empty" );
return new PeripheralType( type );
return new PeripheralType( type, Collections.emptySet() );
}
/**
* Create a new non-empty peripheral type with additional traits.
*
* @param type The name of the type.
* @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}.
* @return The constructed peripheral type.
*/
public static PeripheralType ofType( @Nonnull String type, Collection<String> additionalTypes )
{
if( Strings.isNullOrEmpty( type ) ) throw new IllegalArgumentException( "type cannot be null or empty" );
return new PeripheralType( type, ImmutableSet.copyOf( additionalTypes ) );
}
/**
* Create a new non-empty peripheral type with additional traits.
*
* @param type The name of the type.
* @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}.
* @return The constructed peripheral type.
*/
public static PeripheralType ofType( @Nonnull String type, @Nonnull String... additionalTypes )
{
if( Strings.isNullOrEmpty( type ) ) throw new IllegalArgumentException( "type cannot be null or empty" );
return new PeripheralType( type, ImmutableSet.copyOf( additionalTypes ) );
}
/**
* Create a new peripheral type with no primary type but additional traits.
*
* @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}.
* @return The constructed peripheral type.
*/
public static PeripheralType ofAdditional( Collection<String> additionalTypes )
{
return new PeripheralType( null, ImmutableSet.copyOf( additionalTypes ) );
}
/**
* Create a new peripheral type with no primary type but additional traits.
*
* @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}.
* @return The constructed peripheral type.
*/
public static PeripheralType ofAdditional( @Nonnull String... additionalTypes )
{
return new PeripheralType( null, ImmutableSet.copyOf( additionalTypes ) );
}
/**
@ -59,4 +117,15 @@ public final class PeripheralType
{
return type;
}
/**
* Get any additional types or "traits" of this peripheral. These effectively act as a standard set of interfaces
* a peripheral might have.
*
* @return All additional types.
*/
public Set<String> getAdditionalTypes()
{
return additionalTypes;
}
}

View File

@ -17,6 +17,7 @@ import dan200.computercraft.core.asm.NamedMethod;
import dan200.computercraft.core.asm.PeripheralMethod;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.core.tracking.TrackingField;
import dan200.computercraft.shared.util.LuaUtil;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -36,6 +37,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
private final IPeripheral peripheral;
private final String type;
private final Set<String> additionalTypes;
private final Map<String, PeripheralMethod> methodMap;
private boolean attached;
@ -47,6 +49,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
attached = false;
type = Objects.requireNonNull( peripheral.getType(), "Peripheral type cannot be null" );
additionalTypes = peripheral.getAdditionalTypes();
methodMap = PeripheralAPI.getMethods( peripheral );
}
@ -61,6 +64,11 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
return type;
}
public Set<String> getAdditionalTypes()
{
return additionalTypes;
}
public Collection<String> getMethods()
{
return methodMap.keySet();
@ -298,7 +306,23 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
synchronized( peripherals )
{
PeripheralWrapper p = peripherals[side.ordinal()];
if( p != null ) return new Object[] { p.getType() };
return p == null ? null : LuaUtil.consArray( p.getType(), p.getAdditionalTypes() );
}
}
@LuaFunction
public final Object[] hasType( String sideName, String type )
{
ComputerSide side = ComputerSide.valueOfInsensitive( sideName );
if( side == null ) return null;
synchronized( peripherals )
{
PeripheralWrapper p = peripherals[side.ordinal()];
if( p != null )
{
return new Object[] { p.getType().equals( type ) || p.getAdditionalTypes().contains( type ) };
}
}
return null;
}

View File

@ -18,18 +18,21 @@ import net.minecraft.util.ResourceLocation;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;
class GenericPeripheral implements IDynamicPeripheral
{
private final String type;
private final Set<String> additionalTypes;
private final TileEntity tile;
private final List<SaturatedMethod> methods;
GenericPeripheral( TileEntity tile, String name, List<SaturatedMethod> methods )
GenericPeripheral( TileEntity tile, String name, Set<String> additionalTypes, List<SaturatedMethod> methods )
{
ResourceLocation type = tile.getType().getRegistryName();
this.tile = tile;
this.type = name != null ? name : (type != null ? type.toString() : "unknown");
this.additionalTypes = additionalTypes;
this.methods = methods;
}
@ -56,6 +59,13 @@ class GenericPeripheral implements IDynamicPeripheral
return type;
}
@Nonnull
@Override
public Set<String> getAdditionalTypes()
{
return additionalTypes;
}
@Nullable
@Override
public Object getTarget()

View File

@ -20,9 +20,7 @@ import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.*;
public class GenericPeripheralProvider
{
@ -62,15 +60,16 @@ public class GenericPeripheralProvider
private static class GenericPeripheralBuilder
{
String name;
final ArrayList<SaturatedMethod> methods = new ArrayList<>( 0 );
private String name;
private final Set<String> additionalTypes = new HashSet<>( 0 );
private final ArrayList<SaturatedMethod> methods = new ArrayList<>( 0 );
IPeripheral toPeripheral( TileEntity tile )
{
if( methods.isEmpty() ) return null;
methods.trimToSize();
return new GenericPeripheral( tile, name, methods );
return new GenericPeripheral( tile, name, additionalTypes, methods );
}
void addMethods( Object target, List<NamedMethod<PeripheralMethod>> methods )
@ -89,6 +88,7 @@ public class GenericPeripheralProvider
String name = type.getPrimaryType();
if( this.name == null || this.name.compareTo( name ) > 0 ) this.name = name;
}
if( type != null ) additionalTypes.addAll( type.getAdditionalTypes() );
}
}
}

View File

@ -6,8 +6,9 @@
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.energy.IEnergyStorage;
@ -25,8 +26,15 @@ import javax.annotation.Nonnull;
*
* @cc.module energy_storage
*/
public class EnergyMethods implements GenericSource
public class EnergyMethods implements GenericPeripheral
{
@Nonnull
@Override
public PeripheralType getType()
{
return PeripheralType.ofAdditional( "energy_storage" );
}
@Nonnull
@Override
public ResourceLocation id()

View File

@ -6,11 +6,12 @@
package dan200.computercraft.shared.peripheral.generic.methods;
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.GenericPeripheral;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.generic.data.FluidData;
import net.minecraft.fluid.Fluid;
import net.minecraft.tileentity.TileEntity;
@ -35,8 +36,15 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel
*
* @cc.module fluid_storage
*/
public class FluidMethods implements GenericSource
public class FluidMethods implements GenericPeripheral
{
@Nonnull
@Override
public PeripheralType getType()
{
return PeripheralType.ofAdditional( "fluid_storage" );
}
@Nonnull
@Override
public ResourceLocation id()

View File

@ -6,11 +6,12 @@
package dan200.computercraft.shared.peripheral.generic.methods;
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.GenericPeripheral;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
@ -36,8 +37,15 @@ import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHel
*
* @cc.module inventory
*/
public class InventoryMethods implements GenericSource
public class InventoryMethods implements GenericPeripheral
{
@Nonnull
@Override
public PeripheralType getType()
{
return PeripheralType.ofAdditional( "inventory" );
}
@Nonnull
@Override
public ResourceLocation id()

View File

@ -21,6 +21,7 @@ import dan200.computercraft.core.apis.PeripheralAPI;
import dan200.computercraft.core.asm.PeripheralMethod;
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.util.LuaUtil;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
@ -118,13 +119,35 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
* @param name The peripheral's name.
* @return The peripheral's name.
* @cc.treturn string|nil The peripheral's type, or {@code nil} if it is not present.
* @cc.changed 1.99 Peripherals can have multiple types - this function returns multiple values.
* @see PeripheralAPI#getType
*/
@LuaFunction
public final Object[] getTypeRemote( IComputerAccess computer, String name )
{
RemotePeripheralWrapper wrapper = getWrapper( computer, name );
return wrapper != null ? new Object[] { wrapper.getType() } : null;
return wrapper == null ? null : LuaUtil.consArray( wrapper.getType(), wrapper.getAdditionalTypes() );
}
/**
* Check a peripheral is of a particular type.
*
* <blockquote><strong>Important:</strong> This function only appears on wired modems. Check {@link #isWireless}
* returns false before calling it.</blockquote>
*
* @param computer The calling computer.
* @param name The peripheral's name.
* @param type The type to check.
* @return The peripheral's name.
* @cc.treturn boolean|nil If a peripheral has a particular type, or {@literal nil} if it is not present.
* @cc.since 1.99
* @see PeripheralAPI#getType
*/
@LuaFunction
public final Object[] hasTypeRemote( IComputerAccess computer, String name, String type )
{
RemotePeripheralWrapper wrapper = getWrapper( computer, name );
return wrapper == null ? null : new Object[] { wrapper.getType().equals( type ) || wrapper.getAdditionalTypes().contains( getType() ) };
}
/**
@ -308,6 +331,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
private final String name;
private final String type;
private final Set<String> additionalTypes;
private final Map<String, PeripheralMethod> methodMap;
private volatile boolean attached;
@ -321,6 +345,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
this.name = name;
type = Objects.requireNonNull( peripheral.getType(), "Peripheral type cannot be null" );
additionalTypes = peripheral.getAdditionalTypes();
methodMap = PeripheralAPI.getMethods( peripheral );
}
@ -354,6 +379,11 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
return type;
}
public Set<String> getAdditionalTypes()
{
return additionalTypes;
}
public Collection<String> getMethodNames()
{
return methodMap.keySet();

View File

@ -0,0 +1,23 @@
/*
* 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 java.util.Collection;
public class LuaUtil
{
public static Object[] consArray( Object value, Collection<?> rest )
{
if( rest.isEmpty() ) return new Object[] { value };
// I'm not proud of this code.
Object[] out = new Object[rest.size() + 1];
out[0] = value;
int i = 1;
for( Object additionalType : rest ) out[i++] = additionalType;
return out;
}
}

View File

@ -1,18 +1,90 @@
--- The Peripheral API is for interacting with peripherals connected to the
-- computer, such as the Disk Drive, the Advanced Monitor and Monitor.
--
-- Each peripheral block has a name, either referring to the side the peripheral
-- can be found on, or a name on an adjacent wired network.
--
-- If the peripheral is next to the computer, its side is either `front`,
-- `back`, `left`, `right`, `top` or `bottom`. If the peripheral is attached by
-- a cable, its side will follow the format `type_id`, for example `printer_0`.
--
-- Peripheral functions are called *methods*, a term borrowed from Java.
--
-- @module peripheral
-- @since 1.3
-- @changed 1.51 Add support for wired modems.
--[[- Peripherals are blocks (or turtle and pocket computer upgrades) which can
be controlled by a computer. For instance, the @{speaker} peripheral allows a
computer to play music and the @{monitor} peripheral allows you to display text
in the world.
## Referencing peripherals
Computers can interact with adjacent peripherals. Each peripheral is given a
name based on which direction it is in. For instance, a disk drive below your
computer will be called `"bottom"` in your Lua code, one to the left called
`"left"` , and so on for all 6 directions (`"bottom"`, `"top"`, `"left"`,
`"right"`, `"front"`, `"back"`).
You can list the names of all peripherals with the `peripherals` program, or the
@{peripheral.getNames} function.
It's also possible to use peripherals which are further away from your computer
through the use of @{modem|Wired Modems}. Place one modem against your computer,
run Networking Cable to your peripheral, and then place another modem against
that block. You can then right click the modem to use (or *attach*) the
peripheral. This will print a peripheral name to chat, which can then be used
just like a direction name to access the peripheral. You can click on the message
to copy the name to your clipboard.
## Using peripherals
Once you have the name of a peripheral, you can call functions on it using the
@{peripheral.call} function. This takes the name of our peripheral, the name of
the function we want to call, and then its arguments.
> Some bits of the peripheral API call peripheral functions *methods* instead
> (for example, the @{peripheral.getMethods} function). Don't worry, they're the
> same thing!
Let's say we have a monitor above our computer (and so "top") and want to
@{monitor.write|write some text to it}. We'd write the following:
```lua
peripheral.call("top", "write", "This is displayed on a monitor!")
```
Once you start calling making a couple of peripheral calls this can get very
repetitive, and so we can @{peripheral.wrap|wrap} a peripheral. This builds a
table of all the peripheral's functions so you can use it like an API or module.
For instance, we could have written the above example as follows:
```lua
local my_monitor = peripheral.wrap("top")
my_monitor.write("This is displayed on a monitor!")
```
## Finding peripherals
Sometimes when you're writing a program you don't care what a peripheral is
called, you just need to know it's there. For instance, if you're writing a
music player, you just need a speaker - it doesn't matter if it's above or below
the computer.
Thankfully there's a quick way to do this: @{peripheral.find}. This takes a
*peripheral type* and returns all the attached peripherals which are of this
type.
What is a peripheral type though? This is a string which describes what a
peripheral is, and so what functions are available on it. For instance, speakers
are just called `"speaker"`, and monitors `"monitor"`. Some peripherals might
have more than one type; a Minecraft chest is both a `"minecraft:chest"` and
`"inventory"`.
You can get all the types a peripheral has with @{peripheral.getType}, and check
a peripheral is a specific type with @{peripheral.hasType}.
To return to our original example, let's use @{peripheral.find} to find an
attached speaker:
```lua
local speaker = peripheral.find("speaker")
speaker.playNote("harp")
```
@module peripheral
@see event!peripheral This event is fired whenever a new peripheral is attached.
@see event!peripheral_detach This event is fired whenever a peripheral is detached.
@since 1.3
@changed 1.51 Add support for wired modems.
@changed 1.99 Peripherals can have multiple types.
]]
local expect = dofile("rom/modules/main/cc/expect.lua").expect
@ -33,7 +105,7 @@ function getNames()
local side = sides[n]
if native.isPresent(side) then
table.insert(results, side)
if native.getType(side) == "modem" and not native.call(side, "isWireless") then
if native.hasType(side, "modem") and not native.call(side, "isWireless") then
local remote = native.call(side, "getNamesRemote")
for _, name in ipairs(remote) do
table.insert(results, name)
@ -58,7 +130,7 @@ function isPresent(name)
for n = 1, #sides do
local side = sides[n]
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
if native.hasType(side, "modem") and not native.call(side, "isWireless") and
native.call(side, "isPresentRemote", name)
then
return true
@ -67,12 +139,17 @@ function isPresent(name)
return false
end
--- Get the type of a wrapped peripheral, or a peripheral with the given name.
--
-- @tparam string|table peripheral The name of the peripheral to find, or a
-- wrapped peripheral instance.
-- @treturn string|nil The peripheral's type, or `nil` if it is not present.
-- @changed 1.88.0 Accepts a wrapped peripheral as an argument.
--[[- Get the types of a named or wrapped peripheral.
@tparam string|table peripheral The name of the peripheral to find, or a
wrapped peripheral instance.
@treturn string... The peripheral's types, or `nil` if it is not present.
@changed 1.88.0 Accepts a wrapped peripheral as an argument.
@changed 1.99 Now returns multiple types.
@usage Get the type of a peripheral above this computer.
peripheral.getType("top")
]]
function getType(peripheral)
expect(1, peripheral, "string", "table")
if type(peripheral) == "string" then -- Peripheral name passed
@ -81,7 +158,7 @@ function getType(peripheral)
end
for n = 1, #sides do
local side = sides[n]
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
if native.hasType(side, "modem") and not native.call(side, "isWireless") and
native.call(side, "isPresentRemote", peripheral)
then
return native.call(side, "getTypeRemote", peripheral)
@ -90,10 +167,43 @@ function getType(peripheral)
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.type) ~= "string" then
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.type
return table.unpack(mt.types)
end
end
--[[- Check if a peripheral is of a particular type.
@tparam string|table peripheral The name of the peripheral or a wrapped peripheral instance.
@tparam string peripheral_type The type to check.
@treturn boolean|nil If a peripheral has a particular type, or `nil` if it is not present.
@since 1.99
]]
function hasType(peripheral, peripheral_type)
expect(1, peripheral, "string", "table")
expect(2, peripheral_type, "string")
if type(peripheral) == "string" then -- Peripheral name passed
if native.isPresent(peripheral) then
return native.hasType(peripheral, peripheral_type)
end
for n = 1, #sides do
local side = sides[n]
if native.hasType(side, "modem") and not native.call(side, "isWireless") and
native.call(side, "isPresentRemote", peripheral)
then
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
end
end
return nil
else
local mt = getmetatable(peripheral)
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
error("bad argument #1 (table is not a peripheral)", 2)
end
return mt.types[peripheral_type] ~= nil
end
end
@ -109,7 +219,7 @@ function getMethods(name)
end
for n = 1, #sides do
local side = sides[n]
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
if native.hasType(side, "modem") and not native.call(side, "isWireless") and
native.call(side, "isPresentRemote", name)
then
return native.call(side, "getMethodsRemote", name)
@ -151,7 +261,7 @@ function call(name, method, ...)
for n = 1, #sides do
local side = sides[n]
if native.getType(side) == "modem" and not native.call(side, "isWireless") and
if native.hasType(side, "modem") and not native.call(side, "isWireless") and
native.call(side, "isPresentRemote", name)
then
return native.call(side, "callRemote", name, method, ...)
@ -160,15 +270,16 @@ function call(name, method, ...)
return nil
end
--- Get a table containing functions pointing to the peripheral's methods, which
-- can then be called as if using @{peripheral.call}.
--- Get a table containing all functions available on a peripheral. These can
-- then be called instead of using @{peripheral.call} every time.
--
-- @tparam string name The name of the peripheral to wrap.
-- @treturn table|nil The table containing the peripheral's methods, or `nil` if
-- there is no peripheral present with the given name.
-- @usage Open the modem on the top of this computer.
--
-- peripheral.wrap("top").open(1)
-- local modem = peripheral.wrap("top")
-- modem.open(1)
function wrap(name)
expect(1, name, "string")
@ -177,10 +288,14 @@ function wrap(name)
return nil
end
-- We store our types array as a list (for getType) and a lookup table (for hasType).
local types = { peripheral.getType(name) }
for i = 1, #types do types[types[i]] = true end
local result = setmetatable({}, {
__name = "peripheral",
name = name,
type = peripheral.getType(name),
type = types[1],
types = types,
})
for _, method in ipairs(methods) do
result[method] = function(...)
@ -222,7 +337,7 @@ function find(ty, filter)
local results = {}
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == ty then
if peripheral.hasType(name, ty) then
local wrapped = peripheral.wrap(name)
if filter == nil or filter(name, wrapped) then
table.insert(results, wrapped)

View File

@ -3,7 +3,7 @@ print("Attached Peripherals:")
if #tPeripherals > 0 then
for n = 1, #tPeripherals do
local sPeripheral = tPeripherals[n]
print(sPeripheral .. " (" .. peripheral.getType(sPeripheral) .. ")")
print(sPeripheral .. " (" .. table.concat({ peripheral.getType(sPeripheral) }, ", ") .. ")")
end
else
print("None")

View File

@ -1,6 +1,13 @@
describe("The peripheral library", function()
local it_modem = peripheral.getType("top") == "modem" and it or pending
local multitype_peripheral = setmetatable({}, {
__name = "peripheral",
name = "top",
type = "modem",
types = { "modem", "inventory", modem = true, inventory = true },
})
describe("peripheral.isPresent", function()
it("validates arguments", function()
peripheral.isPresent("")
@ -26,6 +33,10 @@ describe("The peripheral library", function()
expect.error(peripheral.getType, {}):eq("bad argument #1 (table is not a peripheral)")
end)
it("returns nil when no peripheral is present", function()
expect(peripheral.getType("bottom")):eq(nil)
end)
it_modem("can get the type of a peripheral by side", function()
expect(peripheral.getType("top")):eq("modem")
end)
@ -33,6 +44,38 @@ describe("The peripheral library", function()
it_modem("can get the type of a wrapped peripheral", function()
expect(peripheral.getType(peripheral.wrap("top"))):eq("modem")
end)
it("can return multiple types", function()
expect({ peripheral.getType(multitype_peripheral) }):same { "modem", "inventory" }
end)
end)
describe("peripheral.hasType", function()
it("validates arguments", function()
peripheral.getType("")
expect.error(peripheral.hasType, nil):eq("bad argument #1 (expected string or table, got nil)")
expect.error(peripheral.hasType, {}, ""):eq("bad argument #1 (table is not a peripheral)")
expect.error(peripheral.hasType, ""):eq("bad argument #2 (expected string, got nil)")
end)
it("returns nil when no peripherals are present", function()
expect(peripheral.hasType("bottom", "modem")):eq(nil)
end)
it_modem("can check type of a peripheral by side", function()
expect(peripheral.hasType("top", "modem")):eq(true)
expect(peripheral.hasType("top", "not_a_modem")):eq(false)
end)
it_modem("can check the type of a wrapped peripheral (true)", function()
expect(peripheral.hasType(peripheral.wrap("top"), "modem")):eq(true)
end)
it("can check the type of a wrapped peripheral (fake)", function()
expect(peripheral.hasType(multitype_peripheral, "modem")):eq(true)
expect(peripheral.hasType(multitype_peripheral, "inventory")):eq(true)
expect(peripheral.hasType(multitype_peripheral, "something else")):eq(false)
end)
end)
describe("peripheral.getMethods", function()