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

Merge pull request #1 from dan200/master

update 7/8
This commit is contained in:
KingofGamesYami 2017-07-08 16:17:24 -05:00 committed by GitHub
commit 592c0c9341
61 changed files with 1667 additions and 1245 deletions

View File

@ -36,7 +36,7 @@ This mod is provided 'as is' with no warranties, implied or otherwise. The owner
of this mod takes no responsibility for any damages incurred from the use of of this mod takes no responsibility for any damages incurred from the use of
this mod. This mod alters fundamental parts of the Minecraft game, parts of this mod. This mod alters fundamental parts of the Minecraft game, parts of
Minecraft may not work with this mod installed. All damages caused from the use Minecraft may not work with this mod installed. All damages caused from the use
or misuse of this mad fall on the user. or misuse of this mod fall on the user.
3. Play rights 3. Play rights
-------------- --------------

View File

@ -78,21 +78,23 @@ processResources {
inputs.property "mcversion", project.minecraft.version inputs.property "mcversion", project.minecraft.version
def grgit = Grgit.open(dir: '.') def grgit = Grgit.open(dir: '.')
inputs.property "commithash", grgit.log(maxCommits: 1)[0].id inputs.property "commithash", grgit.head().id
def blacklist = ['GitHub', 'dan200', 'Daniel Ratcliffe'] def blacklist = ['GitHub', 'dan200', 'Daniel Ratcliffe']
Set<String> contributors = [] Set<String> contributors = []
grgit.log().each { grgit.log().each {
if (!blacklist.contains(it.committer.name)) if (!blacklist.contains(it.author.name)) contributors.add(it.author.name)
contributors.add(it.committer.name) if (!blacklist.contains(it.committer.name)) contributors.add(it.committer.name)
} }
from(sourceSets.main.resources.srcDirs) { from(sourceSets.main.resources.srcDirs) {
include 'mcmod.info' include 'mcmod.info'
include 'assets/computercraft/lua/rom/help/credits.txt' include 'assets/computercraft/lua/rom/help/credits.txt'
expand 'version':project.version, 'mcversion':project.minecraft.version, 'gitcontributors':contributors.sort().join('\n') expand 'version':project.version,
'mcversion':project.minecraft.version,
'gitcontributors':contributors.sort(false, String.CASE_INSENSITIVE_ORDER).join('\n')
} }
from(sourceSets.main.resources.srcDirs) { from(sourceSets.main.resources.srcDirs) {

View File

@ -18,6 +18,7 @@ import dan200.computercraft.api.permissions.ITurtlePermissionProvider;
import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider; import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.core.apis.AddressPredicate;
import dan200.computercraft.core.filesystem.ComboMount; import dan200.computercraft.core.filesystem.ComboMount;
import dan200.computercraft.core.filesystem.FileMount; import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.JarMount; import dan200.computercraft.core.filesystem.JarMount;
@ -60,6 +61,7 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.SoundEvent; import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property; import net.minecraftforge.common.config.Property;
import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.FMLCommonHandler;
@ -105,8 +107,18 @@ public class ComputerCraft
public static final int pocketComputerGUIID = 106; public static final int pocketComputerGUIID = 106;
// Configuration options // Configuration options
private static final String[] DEFAULT_HTTP_WHITELIST = new String[] { "*" };
private static final String[] DEFAULT_HTTP_BLACKLIST = new String[] {
"127.0.0.0/8",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"fd00::/8",
};
public static boolean http_enable = true; public static boolean http_enable = true;
public static String http_whitelist = "*"; public static AddressPredicate http_whitelist = new AddressPredicate( DEFAULT_HTTP_WHITELIST );
public static AddressPredicate http_blacklist = new AddressPredicate( DEFAULT_HTTP_BLACKLIST );
public static boolean disable_lua51_features = false; public static boolean disable_lua51_features = false;
public static String default_computer_settings = ""; public static String default_computer_settings = "";
public static boolean logPeripheralErrors = false; public static boolean logPeripheralErrors = false;
@ -185,6 +197,7 @@ public class ComputerCraft
public static Property http_enable; public static Property http_enable;
public static Property http_whitelist; public static Property http_whitelist;
public static Property http_blacklist;
public static Property disable_lua51_features; public static Property disable_lua51_features;
public static Property default_computer_settings; public static Property default_computer_settings;
public static Property logPeripheralErrors; public static Property logPeripheralErrors;
@ -252,10 +265,28 @@ public class ComputerCraft
Config.config.load(); Config.config.load();
Config.http_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "http_enable", http_enable ); Config.http_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "http_enable", http_enable );
Config.http_enable.setComment( "Enable the \"http\" API on Computers (see \"http_whitelist\" for more fine grained control than this)" ); Config.http_enable.setComment( "Enable the \"http\" API on Computers (see \"http_whitelist\" and \"http_blacklist\" for more fine grained control than this)" );
Config.http_whitelist = Config.config.get( Configuration.CATEGORY_GENERAL, "http_whitelist", http_whitelist ); {
Config.http_whitelist.setComment( "A semicolon limited list of wildcards for domains that can be accessed through the \"http\" API on Computers. Set this to \"*\" to access to the entire internet. Example: \"*.pastebin.com;*.github.com;*.computercraft.info\" will restrict access to just those 3 domains." ); ConfigCategory category = Config.config.getCategory( Configuration.CATEGORY_GENERAL );
Property currentProperty = category.get( "http_whitelist" );
if( currentProperty != null && !currentProperty.isList() ) category.remove( "http_whitelist" );
Config.http_whitelist = Config.config.get( Configuration.CATEGORY_GENERAL, "http_whitelist", DEFAULT_HTTP_WHITELIST );
if( currentProperty != null && !currentProperty.isList() )
{
Config.http_whitelist.setValues( currentProperty.getString().split( ";" ) );
}
}
Config.http_whitelist.setComment( "A list of wildcards for domains or IP ranges that can be accessed through the \"http\" API on Computers.\n" +
"Set this to \"*\" to access to the entire internet. Example: \"*.pastebin.com\" will restrict access to just subdomains of pastebin.com.\n" +
"You can use domain names (\"pastebin.com\"), wilcards (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\")." );
Config.http_blacklist = Config.config.get( Configuration.CATEGORY_GENERAL, "http_blacklist", DEFAULT_HTTP_BLACKLIST );
Config.http_blacklist.setComment( "A list of wildcards for domains or IP ranges that cannot be accessed through the \"http\" API on Computers.\n" +
"If this is empty then all whitelisted domains will be accessible. Example: \"*.github.com\" will block access to all subdomains of github.com.\n" +
"You can use domain names (\"pastebin.com\"), wilcards (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\")." );
Config.disable_lua51_features = Config.config.get( Configuration.CATEGORY_GENERAL, "disable_lua51_features", disable_lua51_features ); Config.disable_lua51_features = Config.config.get( Configuration.CATEGORY_GENERAL, "disable_lua51_features", disable_lua51_features );
Config.disable_lua51_features.setComment( "Set this to true to disable Lua 5.1 functions that will be removed in a future update. Useful for ensuring forward compatibility of your programs now." ); Config.disable_lua51_features.setComment( "Set this to true to disable Lua 5.1 functions that will be removed in a future update. Useful for ensuring forward compatibility of your programs now." );
@ -327,7 +358,8 @@ public class ComputerCraft
public static void syncConfig() { public static void syncConfig() {
http_enable = Config.http_enable.getBoolean(); http_enable = Config.http_enable.getBoolean();
http_whitelist = Config.http_whitelist.getString(); http_whitelist = new AddressPredicate( Config.http_whitelist.getStringList() );
http_blacklist = new AddressPredicate( Config.http_blacklist.getStringList() );
disable_lua51_features = Config.disable_lua51_features.getBoolean(); disable_lua51_features = Config.disable_lua51_features.getBoolean();
default_computer_settings = Config.default_computer_settings.getString(); default_computer_settings = Config.default_computer_settings.getString();

View File

@ -0,0 +1,168 @@
package dan200.computercraft.core.apis;
import com.google.common.net.InetAddresses;
import dan200.computercraft.ComputerCraft;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* Used to determine whether a domain or IP address matches a series of patterns.
*/
public class AddressPredicate
{
private static class HostRange
{
private final byte[] min;
private final byte[] max;
private HostRange( byte[] min, byte[] max )
{
this.min = min;
this.max = max;
}
public boolean contains( InetAddress address )
{
byte[] entry = address.getAddress();
if( entry.length != min.length ) return false;
for( int i = 0; i < entry.length; i++ )
{
int value = 0xFF & entry[ i ];
if( value < (0xFF & min[ i ]) || value > (0xFF & max[ i ]) ) return false;
}
return true;
}
}
private final List<Pattern> wildcards;
private final List<HostRange> ranges;
public AddressPredicate( String... filters )
{
List<Pattern> wildcards = this.wildcards = new ArrayList<Pattern>();
List<HostRange> ranges = this.ranges = new ArrayList<HostRange>();
for( String filter : filters )
{
int cidr = filter.indexOf( '/' );
if( cidr >= 0 )
{
String addressStr = filter.substring( 0, cidr );
String prefixSizeStr = filter.substring( cidr + 1 );
int prefixSize;
try
{
prefixSize = Integer.parseInt( prefixSizeStr );
}
catch( NumberFormatException e )
{
ComputerCraft.log.warn( "Cannot parse CIDR size from {} ({})", filter, prefixSizeStr );
continue;
}
InetAddress address;
try
{
address = InetAddresses.forString( addressStr );
}
catch( IllegalArgumentException e )
{
ComputerCraft.log.warn( "Cannot parse IP address from {} ({})", filter, addressStr );
continue;
}
// Mask the bytes of the IP address.
byte[] minBytes = address.getAddress(), maxBytes = address.getAddress();
int size = prefixSize;
for( int i = 0; i < minBytes.length; i++ )
{
if( size <= 0 )
{
minBytes[ i ] &= 0;
maxBytes[ i ] |= 0xFF;
}
else if( size < 8 )
{
minBytes[ i ] &= 0xFF << (8 - size);
maxBytes[ i ] |= ~(0xFF << (8 - size));
}
size -= 8;
}
ranges.add( new HostRange( minBytes, maxBytes ) );
}
else
{
wildcards.add( Pattern.compile( "^\\Q" + filter.replaceAll( "\\*", "\\\\E.*\\\\Q" ) + "\\E$" ) );
}
}
}
/**
* Determine whether a host name matches a series of patterns.
*
* This is intended to allow early exiting, before one has to look up the IP address. You should use
* {@link #matches(InetAddress)} instead of/in addition to this one.
*
* @param domain The domain to match.
* @return Whether the patterns were matched.
*/
public boolean matches( String domain )
{
for( Pattern domainPattern : wildcards )
{
if( domainPattern.matcher( domain ).matches() ) return true;
}
return false;
}
private boolean matchesAddress( InetAddress address )
{
String addressString = address.getHostAddress();
for( Pattern domainPattern : wildcards )
{
if( domainPattern.matcher( addressString ).matches() ) return true;
}
for( HostRange range : ranges )
{
if( range.contains( address ) ) return true;
}
return false;
}
/**
* Determine whether the given address matches a series of patterns
*
* @param address The address to check.
* @return Whether it matches any of these patterns.
*/
public boolean matches( InetAddress address )
{
// Match the host name
String host = address.getHostName();
if( host != null && matches( host ) ) return true;
// Match the normal address
if( matchesAddress( address ) ) return true;
// If we're an IPv4 address in disguise then let's check that.
if( address instanceof Inet6Address && InetAddresses.is6to4Address( (Inet6Address) address )
&& matchesAddress( InetAddresses.get6to4IPv4Address( (Inet6Address) address ) ) )
{
return true;
}
return false;
}
}

View File

@ -0,0 +1,246 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map;
/**
* Various helpers for arguments
*/
public final class ArgumentHelper
{
private ArgumentHelper()
{
throw new IllegalStateException( "Cannot instantiate singleton " + getClass().getName() );
}
@Nonnull
public static String getType( @Nullable Object type )
{
if( type == null ) return "nil";
if( type instanceof String ) return "string";
if( type instanceof Boolean ) return "boolean";
if( type instanceof Number ) return "number";
if( type instanceof Map ) return "table";
Class<?> klass = type.getClass();
if( klass.isArray() )
{
StringBuilder name = new StringBuilder();
while( klass.isArray() )
{
name.append( "[]" );
klass = klass.getComponentType();
}
name.insert( 0, klass.getName() );
return name.toString();
}
else
{
return klass.getName();
}
}
@Nonnull
public static LuaException badArgument( int index, @Nonnull String expected, @Nullable Object actual )
{
return badArgument( index, expected, getType( actual ) );
}
@Nonnull
public static LuaException badArgument( int index, @Nonnull String expected, @Nonnull String actual )
{
return new LuaException( "bad argument #" + (index + 1) + " (" + expected + " expected, got " + actual + ")" );
}
public static double getNumber( @Nonnull Object[] args, int index ) throws LuaException
{
if( index >= args.length ) throw badArgument( index, "number", "nil" );
Object value = args[ index ];
if( value instanceof Number )
{
return ((Number) value).doubleValue();
}
else
{
throw badArgument( index, "number", value );
}
}
public static int getInt( @Nonnull Object[] args, int index ) throws LuaException
{
if( index >= args.length ) throw badArgument( index, "number", "nil" );
Object value = args[ index ];
if( value instanceof Number )
{
return (int) ((Number) value).longValue();
}
else
{
throw badArgument( index, "number", value );
}
}
public static double getReal( @Nonnull Object[] args, int index ) throws LuaException
{
return checkReal( index, getNumber( args, index ) );
}
public static boolean getBoolean( @Nonnull Object[] args, int index ) throws LuaException
{
if( index >= args.length ) throw badArgument( index, "boolean", "nil" );
Object value = args[ index ];
if( value instanceof Boolean )
{
return (Boolean) value;
}
else
{
throw badArgument( index, "boolean", value );
}
}
@Nonnull
public static String getString( @Nonnull Object[] args, int index ) throws LuaException
{
if( index >= args.length ) throw badArgument( index, "string", "nil" );
Object value = args[ index ];
if( value instanceof String )
{
return (String) value;
}
else
{
throw badArgument( index, "string", value );
}
}
@SuppressWarnings("unchecked")
@Nonnull
public static Map<Object, Object> getTable( @Nonnull Object[] args, int index ) throws LuaException
{
if( index >= args.length ) throw badArgument( index, "table", "nil" );
Object value = args[ index ];
if( value instanceof Map )
{
return (Map<Object, Object>) value;
}
else
{
throw badArgument( index, "table", value );
}
}
public static double optNumber( @Nonnull Object[] args, int index, double def ) throws LuaException
{
Object value = index < args.length ? args[ index ] : null;
if( value == null )
{
return def;
}
else if( value instanceof Number )
{
return ((Number) value).doubleValue();
}
else
{
throw badArgument( index, "number", value );
}
}
public static int optInt( @Nonnull Object[] args, int index, int def ) throws LuaException
{
Object value = index < args.length ? args[ index ] : null;
if( value == null )
{
return def;
}
else if( value instanceof Number )
{
return (int) ((Number) value).longValue();
}
else
{
throw badArgument( index, "number", value );
}
}
public static double optReal( @Nonnull Object[] args, int index, double def ) throws LuaException
{
return checkReal( index, optNumber( args, index, def ) );
}
public static boolean optBoolean( @Nonnull Object[] args, int index, boolean def ) throws LuaException
{
Object value = index < args.length ? args[ index ] : null;
if( value == null )
{
return def;
}
else if( value instanceof Boolean )
{
return (Boolean) value;
}
else
{
throw badArgument( index, "boolean", value );
}
}
public static String optString( @Nonnull Object[] args, int index, String def ) throws LuaException
{
Object value = index < args.length ? args[ index ] : null;
if( value == null )
{
return def;
}
else if( value instanceof String )
{
return (String) value;
}
else
{
throw badArgument( index, "string", value );
}
}
@SuppressWarnings("unchecked")
public static Map<Object, Object> optTable( @Nonnull Object[] args, int index, Map<Object, Object> def ) throws LuaException
{
Object value = index < args.length ? args[ index ] : null;
if( value == null )
{
return def;
}
else if( value instanceof Map )
{
return (Map<Object, Object>) value;
}
else
{
throw badArgument( index, "table", value );
}
}
private static double checkReal( int index, double value ) throws LuaException
{
if( Double.isNaN( value ) )
{
throw badArgument( index, "number", "nan" );
}
else if( value == Double.POSITIVE_INFINITY )
{
throw badArgument( index, "number", "inf" );
}
else if( value == Double.NEGATIVE_INFINITY )
{
throw badArgument( index, "number", "-inf" );
}
else
{
return value;
}
}
}

View File

@ -11,6 +11,8 @@ import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
// Contributed by Nia // Contributed by Nia
// Based on LuaBit (http://luaforge.net/projects/bit) // Based on LuaBit (http://luaforge.net/projects/bit)
@ -24,25 +26,6 @@ public class BitAPI implements ILuaAPI
private static final int BLSHIFT = 5; private static final int BLSHIFT = 5;
private static final int BLOGIC_RSHIFT = 6; private static final int BLOGIC_RSHIFT = 6;
private static int checkInt( Object o, int count ) throws LuaException
{
if( o instanceof Number )
{
return (int)(((Number)o).longValue());
}
else
{
if( count == 2 )
{
throw new LuaException( "Expected number, number" );
}
else
{
throw new LuaException( "Expected number" );
}
}
}
public BitAPI( IAPIEnvironment _environment ) public BitAPI( IAPIEnvironment _environment )
{ {
} }
@ -82,31 +65,28 @@ public class BitAPI implements ILuaAPI
@Override @Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException
{ {
Object a = args.length>0?args[0]:null;
Object b = args.length>1?args[1]:null;
int ret = 0; int ret = 0;
switch(method) { switch(method) {
case BNOT: case BNOT:
ret = ~checkInt(a, 1); ret = ~getInt( args, 0 );
break; break;
case BAND: case BAND:
ret = checkInt(a, 2) & checkInt(b, 2); ret = getInt( args, 0 ) & getInt( args, 1 );
break; break;
case BOR: case BOR:
ret = checkInt(a, 2) | checkInt(b, 2); ret = getInt( args, 0 ) | getInt( args, 1 );
break; break;
case BXOR: case BXOR:
ret = checkInt(a, 2) ^ checkInt(b, 2); ret = getInt( args, 0 ) ^ getInt( args, 1 );
break; break;
case BRSHIFT: case BRSHIFT:
ret = checkInt(a, 2) >> checkInt(b, 2); ret = getInt( args, 0 ) >> getInt( args, 1 );
break; break;
case BLSHIFT: case BLSHIFT:
ret = checkInt(a, 2) << checkInt(b, 2); ret = getInt( args, 0 ) << getInt( args, 1 );
break; break;
case BLOGIC_RSHIFT: case BLOGIC_RSHIFT:
ret = checkInt(a, 2) >>> checkInt(b, 2); ret = getInt( args, 0 ) >>> getInt( args, 1 );
break; break;
} }

View File

@ -13,6 +13,9 @@ import dan200.computercraft.core.terminal.TextBuffer;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
import static dan200.computercraft.core.apis.ArgumentHelper.optInt;
public class BufferAPI implements ILuaAPI public class BufferAPI implements ILuaAPI
{ {
private static class BufferLuaObject implements ILuaObject private static class BufferLuaObject implements ILuaObject
@ -55,81 +58,25 @@ public class BufferAPI implements ILuaAPI
case 2: case 2:
{ {
// read // read
int start = 0; int start = optInt( arguments, 0, 0 );
if( arguments.length >= 1 && (arguments[0] != null) ) int end = optInt( arguments, 1, m_buffer.length() );
{
if( !(arguments[0] instanceof Number) )
{
throw new LuaException( "Expected number" );
}
start = ((Number)arguments[1]).intValue() - 1;
}
int end = m_buffer.length();
if( arguments.length >= 2 && (arguments[1] != null) )
{
if( !(arguments[1] instanceof Number) )
{
throw new LuaException( "Expected number, number" );
}
end = ((Number)arguments[1]).intValue();
}
return new Object[] { m_buffer.read( start, end ) }; return new Object[] { m_buffer.read( start, end ) };
} }
case 3: case 3:
{ {
// write // write
if( arguments.length < 1 || !(arguments[0] instanceof String) ) String text = getString( arguments, 0 );
{ int start = optInt( arguments, 1, 0 );
throw new LuaException( "Expected string" ); int end = optInt( arguments, 2, start + text.length() );
}
String text = (String)(arguments[0]);
int start = 0;
if( arguments.length >= 2 && (arguments[1] != null) )
{
if( !(arguments[1] instanceof Number) )
{
throw new LuaException( "Expected string, number" );
}
start = ((Number)arguments[1]).intValue() - 1;
}
int end = start + text.length();
if( arguments.length >= 3 && (arguments[2] != null) )
{
if( !(arguments[2] instanceof Number) )
{
throw new LuaException( "Expected string, number, number" );
}
end = ((Number)arguments[2]).intValue();
}
m_buffer.write( text, start, end ); m_buffer.write( text, start, end );
return null; return null;
} }
case 4: case 4:
{ {
// fill // fill
if( arguments.length < 1 || !(arguments[0] instanceof String) ) String text = getString( arguments, 0 );
{ int start = optInt( arguments, 1, 0 );
throw new LuaException( "Expected string" ); int end = optInt( arguments, 2, m_buffer.length() );
}
String text = (String)(arguments[0]);
int start = 0;
if( arguments.length >= 2 && (arguments[1] != null) )
{
if( !(arguments[1] instanceof Number) )
{
throw new LuaException( "Expected string, number" );
}
start = ((Number)arguments[1]).intValue() - 1;
}
int end = m_buffer.length();
if( arguments.length >= 3 && (arguments[2] != null) )
{
if( !(arguments[2] instanceof Number) )
{
throw new LuaException( "Expected string, number, number" );
}
end = ((Number)arguments[2]).intValue();
}
m_buffer.fill( text, start, end ); m_buffer.fill( text, start, end );
return null; return null;
} }
@ -184,23 +131,11 @@ public class BufferAPI implements ILuaAPI
{ {
case 0: case 0:
{ {
if( arguments.length < 1 || !(arguments[0] instanceof String) ) String text = getString( arguments, 0 );
{ int repetitions = optInt( arguments, 1, 1 );
throw new LuaException( "Expected string" );
}
String text = (String)(arguments[0]);
int repetitions = 1;
if( arguments.length >= 2 && arguments[1] != null )
{
if( !(arguments[1] instanceof Number) )
{
throw new LuaException( "Expected string, number" );
}
repetitions = ((Number)arguments[1]).intValue();
if( repetitions < 0 ) if( repetitions < 0 )
{ {
throw new LuaException( "Expected positive number" ); throw ArgumentHelper.badArgument( 1, "positive number", Integer.toString( repetitions ) );
}
} }
TextBuffer buffer = new TextBuffer( text, repetitions ); TextBuffer buffer = new TextBuffer( text, repetitions );
return new Object[] { new BufferLuaObject( buffer ) }; return new Object[] { new BufferLuaObject( buffer ) };

View File

@ -21,6 +21,8 @@ import java.io.OutputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
public class FSAPI implements ILuaAPI public class FSAPI implements ILuaAPI
{ {
private IAPIEnvironment m_env; private IAPIEnvironment m_env;
@ -89,11 +91,7 @@ public class FSAPI implements ILuaAPI
case 0: case 0:
{ {
// list // list
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
try { try {
String[] results = m_fileSystem.list( path ); String[] results = m_fileSystem.list( path );
Map<Object,Object> table = new HashMap<Object,Object>(); Map<Object,Object> table = new HashMap<Object,Object>();
@ -110,32 +108,20 @@ public class FSAPI implements ILuaAPI
case 1: case 1:
{ {
// combine // combine
if( args.length != 2 || args[0] == null || !(args[0] instanceof String) || args[1] == null || !(args[1] instanceof String) ) String pathA = getString( args, 0 );
{ String pathB = getString( args, 1 );
throw new LuaException( "Expected string, string" );
}
String pathA = (String)args[0];
String pathB = (String)args[1];
return new Object[] { m_fileSystem.combine( pathA, pathB ) }; return new Object[] { m_fileSystem.combine( pathA, pathB ) };
} }
case 2: case 2:
{ {
// getName // getName
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
return new Object[]{ FileSystem.getName( path ) }; return new Object[]{ FileSystem.getName( path ) };
} }
case 3: case 3:
{ {
// getSize // getSize
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
try try
{ {
return new Object[]{ m_fileSystem.getSize( path ) }; return new Object[]{ m_fileSystem.getSize( path ) };
@ -148,11 +134,7 @@ public class FSAPI implements ILuaAPI
case 4: case 4:
{ {
// exists // exists
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
try { try {
return new Object[]{ m_fileSystem.exists( path ) }; return new Object[]{ m_fileSystem.exists( path ) };
} catch( FileSystemException e ) { } catch( FileSystemException e ) {
@ -162,11 +144,7 @@ public class FSAPI implements ILuaAPI
case 5: case 5:
{ {
// isDir // isDir
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
try { try {
return new Object[]{ m_fileSystem.isDir( path ) }; return new Object[]{ m_fileSystem.isDir( path ) };
} catch( FileSystemException e ) { } catch( FileSystemException e ) {
@ -176,11 +154,7 @@ public class FSAPI implements ILuaAPI
case 6: case 6:
{ {
// isReadOnly // isReadOnly
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
try { try {
return new Object[]{ m_fileSystem.isReadOnly( path ) }; return new Object[]{ m_fileSystem.isReadOnly( path ) };
} catch( FileSystemException e ) { } catch( FileSystemException e ) {
@ -190,11 +164,7 @@ public class FSAPI implements ILuaAPI
case 7: case 7:
{ {
// makeDir // makeDir
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
try { try {
m_fileSystem.makeDir( path ); m_fileSystem.makeDir( path );
return null; return null;
@ -205,12 +175,8 @@ public class FSAPI implements ILuaAPI
case 8: case 8:
{ {
// move // move
if( args.length != 2 || args[0] == null || !(args[0] instanceof String) || args[1] == null || !(args[1] instanceof String) ) String path = getString( args, 0 );
{ String dest = getString( args, 1 );
throw new LuaException( "Expected string, string" );
}
String path = (String)args[0];
String dest = (String)args[1];
try { try {
m_fileSystem.move( path, dest ); m_fileSystem.move( path, dest );
return null; return null;
@ -221,12 +187,8 @@ public class FSAPI implements ILuaAPI
case 9: case 9:
{ {
// copy // copy
if( args.length != 2 || args[0] == null || !(args[0] instanceof String) || args[1] == null || !(args[1] instanceof String) ) String path = getString( args, 0 );
{ String dest = getString( args, 1 );
throw new LuaException( "Expected string, string" );
}
String path = (String)args[0];
String dest = (String)args[1];
try { try {
m_fileSystem.copy( path, dest ); m_fileSystem.copy( path, dest );
return null; return null;
@ -237,11 +199,7 @@ public class FSAPI implements ILuaAPI
case 10: case 10:
{ {
// delete // delete
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
try { try {
m_fileSystem.delete( path ); m_fileSystem.delete( path );
return null; return null;
@ -252,12 +210,8 @@ public class FSAPI implements ILuaAPI
case 11: case 11:
{ {
// open // open
if( args.length < 2 || args[0] == null || !(args[0] instanceof String) || args[1] == null || !(args[1] instanceof String) ) String path = getString( args, 0 );
{ String mode = getString( args, 1 );
throw new LuaException( "Expected string, string" );
}
String path = (String)args[0];
String mode = (String)args[1];
try { try {
if( mode.equals( "r" ) ) { if( mode.equals( "r" ) ) {
// Open the file for reading, then create a wrapper around the reader // Open the file for reading, then create a wrapper around the reader
@ -300,11 +254,7 @@ public class FSAPI implements ILuaAPI
case 12: case 12:
{ {
// getDrive // getDrive
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
try { try {
if( !m_fileSystem.exists( path ) ) if( !m_fileSystem.exists( path ) )
{ {
@ -318,11 +268,7 @@ public class FSAPI implements ILuaAPI
case 13: case 13:
{ {
// getFreeSpace // getFreeSpace
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
try { try {
long freeSpace = m_fileSystem.getFreeSpace( path ); long freeSpace = m_fileSystem.getFreeSpace( path );
if( freeSpace >= 0 ) if( freeSpace >= 0 )
@ -337,11 +283,7 @@ public class FSAPI implements ILuaAPI
case 14: case 14:
{ {
// find // find
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
try { try {
String[] results = m_fileSystem.find( path ); String[] results = m_fileSystem.find( path );
Map<Object,Object> table = new HashMap<Object,Object>(); Map<Object,Object> table = new HashMap<Object,Object>();
@ -356,11 +298,7 @@ public class FSAPI implements ILuaAPI
case 15: case 15:
{ {
// getDir // getDir
if( args.length != 1 || args[0] == null || !(args[0] instanceof String) ) String path = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String path = (String)args[0];
return new Object[]{ FileSystem.getDirectory( path ) }; return new Object[]{ FileSystem.getDirectory( path ) };
} }
default: default:

View File

@ -7,24 +7,26 @@
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.handles.BinaryInputHandle; import dan200.computercraft.core.apis.http.HTTPCheck;
import dan200.computercraft.core.apis.handles.EncodedInputHandle; import dan200.computercraft.core.apis.http.HTTPRequest;
import dan200.computercraft.core.apis.http.HTTPTask;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.InputStream; import java.net.URL;
import java.util.*; import java.util.*;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class HTTPAPI implements ILuaAPI public class HTTPAPI implements ILuaAPI
{ {
private final IAPIEnvironment m_apiEnvironment; private final IAPIEnvironment m_apiEnvironment;
private final List<HTTPRequest> m_httpRequests; private final List<HTTPTask> m_httpTasks;
public HTTPAPI( IAPIEnvironment environment ) public HTTPAPI( IAPIEnvironment environment )
{ {
m_apiEnvironment = environment; m_apiEnvironment = environment;
m_httpRequests = new ArrayList<HTTPRequest>(); m_httpTasks = new ArrayList<HTTPTask>();
} }
@Override @Override
@ -44,95 +46,31 @@ public class HTTPAPI implements ILuaAPI
public void advance( double _dt ) public void advance( double _dt )
{ {
// Wait for all of our http requests // Wait for all of our http requests
synchronized( m_httpRequests ) synchronized( m_httpTasks )
{ {
Iterator<HTTPRequest> it = m_httpRequests.iterator(); Iterator<HTTPTask> it = m_httpTasks.iterator();
while( it.hasNext() ) { while( it.hasNext() )
final HTTPRequest h = it.next(); {
if( h.isComplete() ) { final HTTPTask h = it.next();
final String url = h.getURL(); if( h.isFinished() )
if( h.wasSuccessful() ) { {
// Queue the "http_success" event h.whenFinished( m_apiEnvironment );
InputStream contents = h.getContents();
Object result = wrapStream(
h.isBinary() ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, h.getEncoding() ),
h.getResponseCode(), h.getResponseHeaders()
);
m_apiEnvironment.queueEvent( "http_success", new Object[] { url, result } );
} else {
// Queue the "http_failure" event
InputStream contents = h.getContents();
Object result = null;
if( contents != null ) {
result = wrapStream(
h.isBinary() ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, h.getEncoding() ),
h.getResponseCode(), h.getResponseHeaders()
);
}
m_apiEnvironment.queueEvent( "http_failure", new Object[]{ url, "Could not connect", result } );
}
it.remove(); it.remove();
} }
} }
} }
} }
private static ILuaObject wrapStream( final ILuaObject reader, final int responseCode, final Map<String, String> responseHeaders )
{
String[] oldMethods = reader.getMethodNames();
final int methodOffset = oldMethods.length;
final String[] newMethods = Arrays.copyOf( oldMethods, oldMethods.length + 2 );
newMethods[ methodOffset + 0 ] = "getResponseCode";
newMethods[ methodOffset + 1 ] = "getResponseHeaders";
return new ILuaObject()
{
@Nonnull
@Override
public String[] getMethodNames()
{
return newMethods;
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException
{
if( method < methodOffset )
{
return reader.callMethod( context, method, args );
}
switch( method - methodOffset )
{
case 0:
{
// getResponseCode
return new Object[] { responseCode };
}
case 1:
{
// getResponseHeaders
return new Object[] { responseHeaders };
}
default:
{
return null;
}
}
}
};
}
@Override @Override
public void shutdown( ) public void shutdown( )
{ {
synchronized( m_httpRequests ) synchronized( m_httpTasks )
{ {
for( HTTPRequest r : m_httpRequests ) for( HTTPTask r : m_httpTasks )
{ {
r.cancel(); r.cancel();
} }
m_httpRequests.clear(); m_httpTasks.clear();
} }
} }
@ -155,24 +93,16 @@ public class HTTPAPI implements ILuaAPI
{ {
// request // request
// Get URL // Get URL
if( args.length < 1 || !(args[0] instanceof String) ) String urlString = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String urlString = args[0].toString();
// Get POST // Get POST
String postString = null; String postString = optString( args, 1, null );
if( args.length >= 2 && args[1] instanceof String )
{
postString = args[1].toString();
}
// Get Headers // Get Headers
Map<String, String> headers = null; Map<String, String> headers = null;
if( args.length >= 3 && args[2] instanceof Map ) Map<Object, Object> table = optTable( args, 2, null );
if( table != null )
{ {
Map<?, ?> table = (Map<?, ?>)args[2];
headers = new HashMap<String, String>( table.size() ); headers = new HashMap<String, String>( table.size() );
for( Object key : table.keySet() ) for( Object key : table.keySet() )
{ {
@ -194,10 +124,11 @@ public class HTTPAPI implements ILuaAPI
// Make the request // Make the request
try try
{ {
HTTPRequest request = new HTTPRequest( urlString, postString, headers, binary ); URL url = HTTPRequest.checkURL( urlString );
synchronized( m_httpRequests ) HTTPRequest request = new HTTPRequest( urlString, url, postString, headers, binary );
synchronized( m_httpTasks )
{ {
m_httpRequests.add( request ); m_httpTasks.add( HTTPTask.submit( request ) );
} }
return new Object[] { true }; return new Object[] { true };
} }
@ -210,16 +141,16 @@ public class HTTPAPI implements ILuaAPI
{ {
// checkURL // checkURL
// Get URL // Get URL
if( args.length < 1 || !(args[0] instanceof String) ) String urlString = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String urlString = args[0].toString();
// Check URL // Check URL
try try
{ {
HTTPRequest.checkURL( urlString ); URL url = HTTPRequest.checkURL( urlString );
HTTPCheck check = new HTTPCheck( urlString, url );
synchronized( m_httpTasks ) {
m_httpTasks.add( HTTPTask.submit( check ) );
}
return new Object[] { true }; return new Object[] { true };
} }
catch( HTTPRequestException e ) catch( HTTPRequestException e )

View File

@ -1,261 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis;
import com.google.common.base.Joiner;
import com.google.common.io.ByteStreams;
import dan200.computercraft.ComputerCraft;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class HTTPRequest
{
public static URL checkURL( String urlString ) throws HTTPRequestException
{
URL url;
try
{
url = new URL( urlString );
}
catch( MalformedURLException e )
{
throw new HTTPRequestException( "URL malformed" );
}
// Validate the URL
String protocol = url.getProtocol().toLowerCase();
if( !protocol.equals("http") && !protocol.equals("https") )
{
throw new HTTPRequestException( "URL not http" );
}
// Compare the URL to the whitelist
boolean allowed = false;
String whitelistString = ComputerCraft.http_whitelist;
String[] allowedURLs = whitelistString.split( ";" );
for( String allowedURL : allowedURLs )
{
Pattern allowedURLPattern = Pattern.compile( "^\\Q" + allowedURL.replaceAll( "\\*", "\\\\E.*\\\\Q" ) + "\\E$" );
if( allowedURLPattern.matcher( url.getHost() ).matches() )
{
allowed = true;
break;
}
}
if( !allowed )
{
throw new HTTPRequestException( "Domain not permitted" );
}
return url;
}
public HTTPRequest( String url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException
{
// Parse the URL
m_urlString = url;
m_url = checkURL( m_urlString );
m_binary = binary;
// Start the thread
m_cancelled = false;
m_complete = false;
m_success = false;
m_result = null;
m_responseCode = -1;
Thread thread = new Thread( new Runnable() {
@Override
public void run()
{
try
{
// Connect to the URL
HttpURLConnection connection = (HttpURLConnection)m_url.openConnection();
if( postText != null )
{
connection.setRequestMethod( "POST" );
connection.setDoOutput( true );
}
else
{
connection.setRequestMethod( "GET" );
}
// Set headers
connection.setRequestProperty( "accept-charset", "UTF-8" );
if( postText != null )
{
connection.setRequestProperty( "content-type", "application/x-www-form-urlencoded; charset=utf-8" );
connection.setRequestProperty( "content-encoding", "UTF-8" );
}
if( headers != null )
{
for( Map.Entry<String, String> header : headers.entrySet() )
{
connection.setRequestProperty( header.getKey(), header.getValue() );
}
}
// Send POST text
if( postText != null )
{
OutputStream os = connection.getOutputStream();
OutputStreamWriter osw;
try
{
osw = new OutputStreamWriter( os, "UTF-8" );
}
catch( UnsupportedEncodingException e )
{
osw = new OutputStreamWriter( os );
}
BufferedWriter writer = new BufferedWriter( osw );
writer.write( postText, 0, postText.length() );
writer.close();
}
// Read response
InputStream is;
int code = connection.getResponseCode();
boolean responseSuccess;
if (code >= 200 && code < 400) {
is = connection.getInputStream();
responseSuccess = true;
} else {
is = connection.getErrorStream();
responseSuccess = false;
}
byte[] result = ByteStreams.toByteArray( is );
is.close();
synchronized( m_lock )
{
if( m_cancelled )
{
// We cancelled
m_complete = true;
m_success = false;
m_result = null;
}
else
{
// We completed
m_complete = true;
m_success = responseSuccess;
m_result = result;
m_responseCode = connection.getResponseCode();
m_encoding = connection.getContentEncoding();
Joiner joiner = Joiner.on( ',' );
Map<String, String> headers = m_responseHeaders = new HashMap<String, String>();
for (Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
headers.put(header.getKey(), joiner.join( header.getValue() ));
}
}
}
connection.disconnect(); // disconnect
}
catch( IOException e )
{
synchronized( m_lock )
{
// There was an error
m_complete = true;
m_success = false;
m_result = null;
}
}
}
} );
thread.setDaemon(true);
thread.start();
}
public String getURL() {
return m_urlString;
}
public void cancel()
{
synchronized(m_lock) {
m_cancelled = true;
}
}
public boolean isComplete()
{
synchronized(m_lock) {
return m_complete;
}
}
public int getResponseCode() {
synchronized(m_lock) {
return m_responseCode;
}
}
public Map<String, String> getResponseHeaders() {
synchronized (m_lock) {
return m_responseHeaders;
}
}
public boolean wasSuccessful()
{
synchronized(m_lock) {
return m_success;
}
}
public boolean isBinary()
{
return m_binary;
}
public InputStream getContents()
{
byte[] result;
synchronized(m_lock) {
result = m_result;
}
if( result != null ) {
return new ByteArrayInputStream( result );
}
return null;
}
public String getEncoding() {
return m_encoding;
}
private final Object m_lock = new Object();
private final URL m_url;
private final String m_urlString;
private boolean m_complete;
private boolean m_cancelled;
private boolean m_success;
private String m_encoding;
private byte[] m_result;
private boolean m_binary;
private int m_responseCode;
private Map<String, String> m_responseHeaders;
}

View File

@ -13,6 +13,8 @@ import dan200.computercraft.shared.util.StringUtil;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.*; import java.util.*;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class OSAPI implements ILuaAPI public class OSAPI implements ILuaAPI
{ {
private IAPIEnvironment m_apiEnvironment; private IAPIEnvironment m_apiEnvironment;
@ -225,21 +227,13 @@ public class OSAPI implements ILuaAPI
case 0: case 0:
{ {
// queueEvent // queueEvent
if( args.length == 0 || args[0] == null || !(args[0] instanceof String) ) queueLuaEvent( getString( args, 0 ), trimArray( args, 1 ) );
{
throw new LuaException( "Expected string" );
}
queueLuaEvent( (String)args[0], trimArray( args, 1 ) );
return null; return null;
} }
case 1: case 1:
{ {
// startTimer // startTimer
if( args.length < 1 || args[0] == null || !(args[0] instanceof Number) ) double timer = getReal( args, 0 );
{
throw new LuaException( "Expected number" );
}
double timer = ((Number)args[0]).doubleValue();
synchronized( m_timers ) synchronized( m_timers )
{ {
m_timers.put( m_nextTimerToken, new Timer( (int)Math.round( timer / 0.05 ) ) ); m_timers.put( m_nextTimerToken, new Timer( (int)Math.round( timer / 0.05 ) ) );
@ -249,11 +243,7 @@ public class OSAPI implements ILuaAPI
case 2: case 2:
{ {
// setAlarm // setAlarm
if( args.length < 1 || args[0] == null || !(args[0] instanceof Number) ) double time = getReal( args, 0 );
{
throw new LuaException( "Expected number" );
}
double time = ((Number)args[0]).doubleValue();
if( time < 0.0 || time >= 24.0 ) if( time < 0.0 || time >= 24.0 )
{ {
throw new LuaException( "Number out of range" ); throw new LuaException( "Number out of range" );
@ -286,16 +276,8 @@ public class OSAPI implements ILuaAPI
case 7: case 7:
{ {
// setComputerLabel // setComputerLabel
String label = null; String label = optString( args, 0, null );
if( args.length > 0 && args[0] != null ) m_apiEnvironment.setLabel( StringUtil.normaliseLabel( label ) );
{
if(!(args[0] instanceof String))
{
throw new LuaException( "Expected string or nil" );
}
label = StringUtil.normaliseLabel( (String) args[0] );
}
m_apiEnvironment.setLabel( label );
return null; return null;
} }
case 8: case 8:
@ -320,13 +302,7 @@ public class OSAPI implements ILuaAPI
case 11: case 11:
{ {
// time // time
String param = "ingame"; String param = optString( args, 0, "ingame" );
if (args.length > 0 && args[0] != null) {
if (!(args[0] instanceof String)) {
throw new LuaException("Expected string");
}
param = (String)args[0];
}
if (param.equals("utc")) { if (param.equals("utc")) {
// Get Hour of day (UTC) // Get Hour of day (UTC)
@ -351,13 +327,7 @@ public class OSAPI implements ILuaAPI
case 12: case 12:
{ {
// day // day
String param = "ingame"; String param = optString( args, 0, "ingame" );
if (args.length > 0 && args[0] != null) {
if (!(args[0] instanceof String)) {
throw new LuaException("Expected string");
}
param = (String)args[0];
}
if (param.equals("utc")) { if (param.equals("utc")) {
// Get numbers of days since 1970-01-01 (utc) // Get numbers of days since 1970-01-01 (utc)
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
@ -381,11 +351,7 @@ public class OSAPI implements ILuaAPI
case 13: case 13:
{ {
// cancelTimer // cancelTimer
if( args.length < 1 || args[0] == null || !(args[0] instanceof Number) ) int token = getInt( args, 0 );
{
throw new LuaException( "Expected number" );
}
int token = ((Number)args[0]).intValue();
synchronized( m_timers ) synchronized( m_timers )
{ {
if( m_timers.containsKey( token ) ) if( m_timers.containsKey( token ) )
@ -398,11 +364,7 @@ public class OSAPI implements ILuaAPI
case 14: case 14:
{ {
// cancelAlarm // cancelAlarm
if( args.length < 1 || args[0] == null || !(args[0] instanceof Number) ) int token = getInt( args, 0 );
{
throw new LuaException( "Expected number" );
}
int token = ((Number)args[0]).intValue();
synchronized( m_alarms ) synchronized( m_alarms )
{ {
if( m_alarms.containsKey( token ) ) if( m_alarms.containsKey( token ) )
@ -415,13 +377,7 @@ public class OSAPI implements ILuaAPI
case 15: case 15:
{ {
// epoch // epoch
String param = "ingame"; String param = optString( args, 0, "ingame" );
if (args.length > 0 && args[0] != null) {
if (!(args[0] instanceof String)) {
throw new LuaException("Expected string");
}
param = (String)args[0];
}
if (param.equals("utc")) { if (param.equals("utc")) {
// Get utc epoch // Get utc epoch
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

View File

@ -21,6 +21,8 @@ import dan200.computercraft.core.filesystem.FileSystemException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.*; import java.util.*;
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChangeListener public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChangeListener
{ {
private class PeripheralWrapper implements IComputerAccess private class PeripheralWrapper implements IComputerAccess
@ -460,14 +462,10 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
case 3: case 3:
{ {
// call // call
if( args.length < 2 || args[1] == null || !(args[1] instanceof String) ) int side = parseSide( args );
{ String methodName = getString( args, 1 );
throw new LuaException( "Expected string, string" );
}
String methodName = (String)args[1];
Object[] methodArgs = trimArray( args, 2 ); Object[] methodArgs = trimArray( args, 2 );
int side = parseSide( args );
if( side >= 0 ) if( side >= 0 )
{ {
PeripheralWrapper p; PeripheralWrapper p;
@ -498,11 +496,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
private int parseSide( Object[] args ) throws LuaException private int parseSide( Object[] args ) throws LuaException
{ {
if( args.length < 1 || args[0] == null || !(args[0] instanceof String) ) String side = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String side = (String)args[0];
for( int n=0; n<Computer.s_sideNames.length; ++n ) for( int n=0; n<Computer.s_sideNames.length; ++n )
{ {
if( side.equals( Computer.s_sideNames[n] ) ) if( side.equals( Computer.s_sideNames[n] ) )

View File

@ -14,6 +14,8 @@ import javax.annotation.Nonnull;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class RedstoneAPI implements ILuaAPI public class RedstoneAPI implements ILuaAPI
{ {
private IAPIEnvironment m_environment; private IAPIEnvironment m_environment;
@ -86,12 +88,8 @@ public class RedstoneAPI implements ILuaAPI
case 1: case 1:
{ {
// setOutput // setOutput
if( args.length < 2 || args[0] == null || !(args[0] instanceof String) || args[1] == null || !(args[1] instanceof Boolean) )
{
throw new LuaException( "Expected string, boolean" );
}
int side = parseSide( args ); int side = parseSide( args );
boolean output = (Boolean) args[ 1 ]; boolean output = getBoolean( args, 1 );
m_environment.setOutput( side, output ? 15 : 0 ); m_environment.setOutput( side, output ? 15 : 0 );
return null; return null;
} }
@ -110,12 +108,8 @@ public class RedstoneAPI implements ILuaAPI
case 4: case 4:
{ {
// setBundledOutput // setBundledOutput
if( args.length < 2 || args[0] == null || !(args[0] instanceof String) || args[1] == null || !(args[1] instanceof Double) )
{
throw new LuaException( "Expected string, number" );
}
int side = parseSide( args ); int side = parseSide( args );
int output = ((Double)args[1]).intValue(); int output = getInt( args, 1 );
m_environment.setBundledOutput( side, output ); m_environment.setBundledOutput( side, output );
return null; return null;
} }
@ -134,12 +128,8 @@ public class RedstoneAPI implements ILuaAPI
case 7: case 7:
{ {
// testBundledInput // testBundledInput
if( args.length < 2 || args[0] == null || !(args[0] instanceof String) || args[1] == null || !(args[1] instanceof Double) )
{
throw new LuaException( "Expected string, number" );
}
int side = parseSide( args ); int side = parseSide( args );
int mask = ((Double)args[1]).intValue(); int mask = getInt( args, 1 );
int input = m_environment.getBundledInput( side ); int input = m_environment.getBundledInput( side );
return new Object[] { ((input & mask) == mask) }; return new Object[] { ((input & mask) == mask) };
} }
@ -147,12 +137,8 @@ public class RedstoneAPI implements ILuaAPI
case 9: case 9:
{ {
// setAnalogOutput/setAnalogueOutput // setAnalogOutput/setAnalogueOutput
if( args.length < 2 || args[0] == null || !(args[0] instanceof String) || args[1] == null || !(args[1] instanceof Double) )
{
throw new LuaException( "Expected string, number" );
}
int side = parseSide( args ); int side = parseSide( args );
int output = ((Double)args[1]).intValue(); int output = getInt( args, 1 );
if( output < 0 || output > 15 ) if( output < 0 || output > 15 )
{ {
throw new LuaException( "Expected number in range 0-15" ); throw new LuaException( "Expected number in range 0-15" );
@ -183,11 +169,7 @@ public class RedstoneAPI implements ILuaAPI
private int parseSide( Object[] args ) throws LuaException private int parseSide( Object[] args ) throws LuaException
{ {
if( args.length < 1 || args[0] == null || !(args[0] instanceof String) ) String side = getString( args, 0 );
{
throw new LuaException( "Expected string" );
}
String side = (String)args[0];
for( int n=0; n<Computer.s_sideNames.length; ++n ) for( int n=0; n<Computer.s_sideNames.length; ++n )
{ {
if( side.equals( Computer.s_sideNames[n] ) ) if( side.equals( Computer.s_sideNames[n] ) )

View File

@ -13,12 +13,10 @@ import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.util.Palette; import dan200.computercraft.shared.util.Palette;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class TermAPI implements ILuaAPI public class TermAPI implements ILuaAPI
{ {
private final Terminal m_terminal; private final Terminal m_terminal;
@ -86,11 +84,7 @@ public class TermAPI implements ILuaAPI
public static int parseColour( Object[] args ) throws LuaException public static int parseColour( Object[] args ) throws LuaException
{ {
if( args.length < 1 || args[0] == null || !(args[0] instanceof Double) ) int colour = getInt( args, 0 );
{
throw new LuaException( "Expected number" );
}
int colour = (int)((Double)args[0]).doubleValue();
if( colour <= 0 ) if( colour <= 0 )
{ {
throw new LuaException( "Colour out of range" ); throw new LuaException( "Colour out of range" );
@ -144,12 +138,7 @@ public class TermAPI implements ILuaAPI
case 1: case 1:
{ {
// scroll // scroll
if( args.length != 1 || args[0] == null || !(args[0] instanceof Double) ) int y = getInt( args, 0 );
{
throw new LuaException( "Expected number" );
}
int y = (int)((Double)args[0]).doubleValue();
synchronized( m_terminal ) synchronized( m_terminal )
{ {
m_terminal.scroll(y); m_terminal.scroll(y);
@ -159,12 +148,8 @@ public class TermAPI implements ILuaAPI
case 2: case 2:
{ {
// setCursorPos // setCursorPos
if( args.length != 2 || args[0] == null || !(args[0] instanceof Double) || args[1] == null || !(args[1] instanceof Double) ) int x = getInt( args, 0 ) - 1;
{ int y = getInt( args, 1 ) - 1;
throw new LuaException( "Expected number, number" );
}
int x = (int)((Double)args[0]).doubleValue() - 1;
int y = (int)((Double)args[1]).doubleValue() - 1;
synchronized( m_terminal ) synchronized( m_terminal )
{ {
m_terminal.setCursorPos( x, y ); m_terminal.setCursorPos( x, y );
@ -174,11 +159,7 @@ public class TermAPI implements ILuaAPI
case 3: case 3:
{ {
// setCursorBlink // setCursorBlink
if( args.length != 1 || args[0] == null || !(args[0] instanceof Boolean) ) boolean b = getBoolean( args, 0 );
{
throw new LuaException( "Expected boolean" );
}
boolean b = (Boolean) args[ 0 ];
synchronized( m_terminal ) synchronized( m_terminal )
{ {
m_terminal.setCursorBlink( b ); m_terminal.setCursorBlink( b );
@ -268,14 +249,9 @@ public class TermAPI implements ILuaAPI
case 18: case 18:
{ {
// blit // blit
if( args.length < 3 || !(args[0] instanceof String) || !(args[1] instanceof String) || !(args[2] instanceof String) ) String text = getString( args, 0 );
{ String textColour = getString( args, 1 );
throw new LuaException( "Expected string, string, string" ); String backgroundColour = getString( args, 2 );
}
String text = (String)args[0];
String textColour = (String)args[1];
String backgroundColour = (String)args[2];
if( textColour.length() != text.length() || backgroundColour.length() != text.length() ) if( textColour.length() != text.length() || backgroundColour.length() != text.length() )
{ {
throw new LuaException( "Arguments must be the same length" ); throw new LuaException( "Arguments must be the same length" );
@ -292,36 +268,26 @@ public class TermAPI implements ILuaAPI
case 20: case 20:
{ {
// setPaletteColour/setPaletteColor // setPaletteColour/setPaletteColor
if(args.length == 2 && args[0] instanceof Double && args[1] instanceof Double)
{
int colour = 15 - parseColour( args ); int colour = 15 - parseColour( args );
int hex = ((Double)args[1]).intValue(); if( args.length == 2 )
{
int hex = getInt( args, 1 );
double[] rgb = Palette.decodeRGB8( hex ); double[] rgb = Palette.decodeRGB8( hex );
setColour( m_terminal, colour, rgb[0], rgb[1], rgb[2] ); setColour( m_terminal, colour, rgb[ 0 ], rgb[ 1 ], rgb[ 2 ] );
return null;
} }
else
if(args.length >= 4 && args[0] instanceof Double && args[1] instanceof Double && args[2] instanceof Double && args[3] instanceof Double)
{ {
int colour = 15 - parseColour( args ); double r = getReal( args, 1 );
double r = (Double)args[1]; double g = getReal( args, 2 );
double g = (Double)args[2]; double b = getReal( args, 3 );
double b = (Double)args[3];
setColour( m_terminal, colour, r, g, b ); setColour( m_terminal, colour, r, g, b );
return null;
} }
return null;
throw new LuaException( "Expected number, number or number, number, number, number" );
} }
case 21: case 21:
case 22: case 22:
{ {
// getPaletteColour/getPaletteColor // getPaletteColour/getPaletteColor
if(args.length < 1 || !(args[0] instanceof Double))
{
throw new LuaException( "Expected number" );
}
int colour = 15 - parseColour( args ); int colour = 15 - parseColour( args );
synchronized( m_terminal ) synchronized( m_terminal )
{ {

View File

@ -9,6 +9,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
public class BinaryInputHandle extends HandleGeneric public class BinaryInputHandle extends HandleGeneric
{ {
private final InputStream m_stream; private final InputStream m_stream;
@ -42,13 +44,7 @@ public class BinaryInputHandle extends HandleGeneric
{ {
if( args.length > 0 && args[ 0 ] != null ) if( args.length > 0 && args[ 0 ] != null )
{ {
if( !(args[ 0 ] instanceof Number) ) int count = getInt( args, 0 );
{
throw new LuaException( "Expected number" );
}
int count = ((Number) args[ 0 ]).intValue();
if( count <= 0 || count >= 1024 * 16 ) if( count <= 0 || count >= 1024 * 16 )
{ {
throw new LuaException( "Count out of range" ); throw new LuaException( "Count out of range" );

View File

@ -2,6 +2,7 @@ package dan200.computercraft.core.apis.handles;
import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.ArgumentHelper;
import dan200.computercraft.shared.util.StringUtil; import dan200.computercraft.shared.util.StringUtil;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -51,7 +52,7 @@ public class BinaryOutputHandle extends HandleGeneric
} }
else else
{ {
throw new LuaException( "Expected number" ); throw ArgumentHelper.badArgument( 0, "string or number", args.length > 0 ? args[ 0 ] : null );
} }
return null; return null;
} }

View File

@ -0,0 +1,45 @@
package dan200.computercraft.core.apis.http;
import dan200.computercraft.core.apis.HTTPRequestException;
import dan200.computercraft.core.apis.IAPIEnvironment;
import java.net.URL;
public class HTTPCheck implements HTTPTask.IHTTPTask
{
private final String urlString;
private final URL url;
private String error;
public HTTPCheck( String urlString, URL url )
{
this.urlString = urlString;
this.url = url;
}
@Override
public void run()
{
try
{
HTTPRequest.checkHost( url );
}
catch( HTTPRequestException e )
{
error = e.getMessage();
}
}
@Override
public void whenFinished( IAPIEnvironment environment )
{
if( error == null )
{
environment.queueEvent( "http_check", new Object[] { urlString, true } );
}
else
{
environment.queueEvent( "http_check", new Object[] { urlString, false, error } );
}
}
}

View File

@ -0,0 +1,290 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http;
import com.google.common.base.Joiner;
import com.google.common.io.ByteStreams;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.HTTPRequestException;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
import javax.annotation.Nonnull;
import java.io.*;
import java.net.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HTTPRequest implements HTTPTask.IHTTPTask
{
public static URL checkURL( String urlString ) throws HTTPRequestException
{
URL url;
try
{
url = new URL( urlString );
}
catch( MalformedURLException e )
{
throw new HTTPRequestException( "URL malformed" );
}
// Validate the URL
String protocol = url.getProtocol().toLowerCase();
if( !protocol.equals( "http" ) && !protocol.equals( "https" ) )
{
throw new HTTPRequestException( "URL not http" );
}
// Compare the URL to the whitelist
if( !ComputerCraft.http_whitelist.matches( url.getHost() ) || ComputerCraft.http_blacklist.matches( url.getHost() ) )
{
throw new HTTPRequestException( "Domain not permitted" );
}
return url;
}
public static InetAddress checkHost( URL url ) throws HTTPRequestException
{
try
{
InetAddress resolved = InetAddress.getByName( url.getHost() );
if( !ComputerCraft.http_whitelist.matches( resolved ) || ComputerCraft.http_blacklist.matches( resolved ) )
{
throw new HTTPRequestException( "Domain not permitted" );
}
return resolved;
}
catch( UnknownHostException e )
{
throw new HTTPRequestException( "Unknown host" );
}
}
private final URL m_url;
private final String m_urlString;
private final String m_postText;
private final Map<String, String> m_headers;
private boolean m_success = false;
private String m_encoding;
private byte[] m_result;
private boolean m_binary;
private int m_responseCode = -1;
private Map<String, String> m_responseHeaders;
private String m_errorMessage;
public HTTPRequest( String urlString, URL url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException
{
// Parse the URL
m_urlString = urlString;
m_url = url;
m_binary = binary;
m_postText = postText;
m_headers = headers;
}
public InputStream getContents()
{
byte[] result = m_result;
if( result != null )
{
return new ByteArrayInputStream( result );
}
return null;
}
@Override
public void run()
{
// First verify the address is allowed.
try
{
checkHost( m_url );
}
catch( HTTPRequestException e )
{
m_success = false;
m_errorMessage = e.getMessage();
return;
}
try
{
// Connect to the URL
HttpURLConnection connection = (HttpURLConnection) m_url.openConnection();
if( m_postText != null )
{
connection.setRequestMethod( "POST" );
connection.setDoOutput( true );
}
else
{
connection.setRequestMethod( "GET" );
}
// Set headers
connection.setRequestProperty( "accept-charset", "UTF-8" );
if( m_postText != null )
{
connection.setRequestProperty( "content-type", "application/x-www-form-urlencoded; charset=utf-8" );
connection.setRequestProperty( "content-encoding", "UTF-8" );
}
if( m_postText != null )
{
for( Map.Entry<String, String> header : m_headers.entrySet() )
{
connection.setRequestProperty( header.getKey(), header.getValue() );
}
}
// Send POST text
if( m_postText != null )
{
OutputStream os = connection.getOutputStream();
OutputStreamWriter osw;
try
{
osw = new OutputStreamWriter( os, "UTF-8" );
}
catch( UnsupportedEncodingException e )
{
osw = new OutputStreamWriter( os );
}
BufferedWriter writer = new BufferedWriter( osw );
writer.write( m_postText, 0, m_postText.length() );
writer.close();
}
// Read response
InputStream is;
int code = connection.getResponseCode();
boolean responseSuccess;
if( code >= 200 && code < 400 )
{
is = connection.getInputStream();
responseSuccess = true;
}
else
{
is = connection.getErrorStream();
responseSuccess = false;
}
byte[] result = ByteStreams.toByteArray( is );
is.close();
// We completed
m_success = responseSuccess;
m_result = result;
m_responseCode = connection.getResponseCode();
m_encoding = connection.getContentEncoding();
Joiner joiner = Joiner.on( ',' );
Map<String, String> headers = m_responseHeaders = new HashMap<String, String>();
for( Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet() )
{
headers.put( header.getKey(), joiner.join( header.getValue() ) );
}
connection.disconnect(); // disconnect
}
catch( IOException e )
{
// There was an error
m_success = false;
}
}
@Override
public void whenFinished( IAPIEnvironment environment )
{
final String url = m_urlString;
if( m_success )
{
// Queue the "http_success" event
InputStream contents = getContents();
Object result = wrapStream(
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, m_encoding ),
m_responseCode, m_responseHeaders
);
environment.queueEvent( "http_success", new Object[] { url, result } );
}
else
{
// Queue the "http_failure" event
String error = "Could not connect";
if( m_errorMessage != null ) error = m_errorMessage;
InputStream contents = getContents();
Object result = null;
if( contents != null )
{
result = wrapStream(
m_binary ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, m_encoding ),
m_responseCode, m_responseHeaders
);
}
environment.queueEvent( "http_failure", new Object[] { url, error, result } );
}
}
private static ILuaObject wrapStream( final ILuaObject reader, final int responseCode, final Map<String, String> responseHeaders )
{
String[] oldMethods = reader.getMethodNames();
final int methodOffset = oldMethods.length;
final String[] newMethods = Arrays.copyOf( oldMethods, oldMethods.length + 2 );
newMethods[ methodOffset + 0 ] = "getResponseCode";
newMethods[ methodOffset + 1 ] = "getResponseHeaders";
return new ILuaObject()
{
@Nonnull
@Override
public String[] getMethodNames()
{
return newMethods;
}
@Override
public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException
{
if( method < methodOffset )
{
return reader.callMethod( context, method, args );
}
switch( method - methodOffset )
{
case 0:
{
// getResponseCode
return new Object[] { responseCode };
}
case 1:
{
// getResponseHeaders
return new Object[] { responseHeaders };
}
default:
{
return null;
}
}
}
};
}
}

View File

@ -0,0 +1,61 @@
package dan200.computercraft.core.apis.http;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dan200.computercraft.core.apis.IAPIEnvironment;
import java.util.concurrent.*;
/**
* A task which executes asynchronously on a new thread.
*
* This functions very similarly to a {@link Future}, but with an additional
* method which is called on the main thread when the task is completed.
*/
public class HTTPTask
{
public interface IHTTPTask extends Runnable
{
void whenFinished( IAPIEnvironment environment );
}
private static final ExecutorService httpThreads = new ThreadPoolExecutor(
4, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setDaemon( true )
.setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 )
.setNameFormat( "ComputerCraft-HTTP-%d" )
.build()
);
private final Future<?> future;
private final IHTTPTask task;
private HTTPTask( Future<?> future, IHTTPTask task )
{
this.future = future;
this.task = task;
}
public static HTTPTask submit( IHTTPTask task )
{
Future<?> future = httpThreads.submit( task );
return new HTTPTask( future, task );
}
public void cancel()
{
future.cancel( false );
}
public boolean isFinished()
{
return future.isDone();
}
public void whenFinished( IAPIEnvironment environment )
{
task.whenFinished( environment );
}
}

View File

@ -172,7 +172,7 @@ public class FileSystem
{ {
if( m_writableMount == null ) if( m_writableMount == null )
{ {
throw new FileSystemException( "Access Denied" ); throw new FileSystemException( "Access denied" );
} }
try try
{ {
@ -199,7 +199,7 @@ public class FileSystem
{ {
if( m_writableMount == null ) if( m_writableMount == null )
{ {
throw new FileSystemException( "Access Denied" ); throw new FileSystemException( "Access denied" );
} }
try try
{ {
@ -219,7 +219,7 @@ public class FileSystem
{ {
if( m_writableMount == null ) if( m_writableMount == null )
{ {
throw new FileSystemException( "Access Denied" ); throw new FileSystemException( "Access denied" );
} }
try try
{ {
@ -251,7 +251,7 @@ public class FileSystem
{ {
if( m_writableMount == null ) if( m_writableMount == null )
{ {
throw new FileSystemException( "Access Denied" ); throw new FileSystemException( "Access denied" );
} }
try try
{ {

View File

@ -28,6 +28,9 @@ import javax.annotation.Nonnull;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
public class CommandAPI implements ILuaAPI public class CommandAPI implements ILuaAPI
{ {
private TileCommandComputer m_computer; private TileCommandComputer m_computer;
@ -151,11 +154,7 @@ public class CommandAPI implements ILuaAPI
case 0: case 0:
{ {
// exec // exec
if( arguments.length < 1 || !(arguments[0] instanceof String) ) final String command = getString( arguments, 0 );
{
throw new LuaException( "Expected string" );
}
final String command = (String)arguments[0];
return context.executeMainThreadTask( new ILuaTask() return context.executeMainThreadTask( new ILuaTask()
{ {
@Override @Override
@ -168,11 +167,7 @@ public class CommandAPI implements ILuaAPI
case 1: case 1:
{ {
// execAsync // execAsync
if( arguments.length < 1 || !(arguments[0] instanceof String) ) final String command = getString( arguments, 0 );
{
throw new LuaException( "Expected string" );
}
final String command = (String)arguments[0];
long taskID = context.issueMainThreadTask( new ILuaTask() long taskID = context.issueMainThreadTask( new ILuaTask()
{ {
@Override @Override
@ -234,22 +229,12 @@ public class CommandAPI implements ILuaAPI
case 4: case 4:
{ {
// getBlockInfos // getBlockInfos
if( arguments.length < 6 || final int minx = getInt( arguments, 0 );
!(arguments[0] instanceof Number) || final int miny = getInt( arguments, 1 );
!(arguments[1] instanceof Number) || final int minz = getInt( arguments, 2 );
!(arguments[2] instanceof Number) || final int maxx = getInt( arguments, 3 );
!(arguments[3] instanceof Number) || final int maxy = getInt( arguments, 4 );
!(arguments[4] instanceof Number) || final int maxz = getInt( arguments, 5 );
!(arguments[5] instanceof Number) )
{
throw new LuaException( "Expected number, number, number, number, number, number" );
}
final int minx = ((Number)arguments[0]).intValue();
final int miny = ((Number)arguments[1]).intValue();
final int minz = ((Number)arguments[2]).intValue();
final int maxx = ((Number)arguments[3]).intValue();
final int maxy = ((Number)arguments[4]).intValue();
final int maxz = ((Number)arguments[5]).intValue();
return context.executeMainThreadTask( new ILuaTask() return context.executeMainThreadTask( new ILuaTask()
{ {
@Override @Override
@ -295,16 +280,9 @@ public class CommandAPI implements ILuaAPI
case 5: case 5:
{ {
// getBlockInfo // getBlockInfo
if( arguments.length < 3 || final int x = getInt( arguments, 0 );
!(arguments[0] instanceof Number) || final int y = getInt( arguments, 1 );
!(arguments[1] instanceof Number) || final int z = getInt( arguments, 2 );
!(arguments[2] instanceof Number) )
{
throw new LuaException( "Expected number, number, number" );
}
final int x = ((Number)arguments[0]).intValue();
final int y = ((Number)arguments[1]).intValue();
final int z = ((Number)arguments[2]).intValue();
return context.executeMainThreadTask( new ILuaTask() return context.executeMainThreadTask( new ILuaTask()
{ {
@Override @Override

View File

@ -16,6 +16,8 @@ import net.minecraft.util.math.BlockPos;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
public class CommandBlockPeripheral implements IPeripheral public class CommandBlockPeripheral implements IPeripheral
{ {
private final TileEntityCommandBlock m_commandBlock; private final TileEntityCommandBlock m_commandBlock;
@ -67,12 +69,7 @@ public class CommandBlockPeripheral implements IPeripheral
case 1: case 1:
{ {
// setCommand // setCommand
if( arguments.length < 1 || !(arguments[0] instanceof String) ) final String command = getString( arguments, 0 );
{
throw new LuaException( "Expected string" );
}
final String command = (String) arguments[ 0 ];
context.issueMainThreadTask( new ILuaTask() context.issueMainThreadTask( new ILuaTask()
{ {
@Override @Override

View File

@ -18,6 +18,8 @@ import net.minecraft.item.ItemStack;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.optString;
public class DiskDrivePeripheral implements IPeripheral public class DiskDrivePeripheral implements IPeripheral
{ {
private final TileDiskDrive m_diskDrive; private final TileDiskDrive m_diskDrive;
@ -78,15 +80,7 @@ public class DiskDrivePeripheral implements IPeripheral
case 2: case 2:
{ {
// setDiskLabel // setDiskLabel
String label = null; String label = optString( arguments, 0, null );
if( arguments.length > 0 )
{
if( arguments[0] != null && !(arguments[0] instanceof String) )
{
throw new LuaException( "Expected string" );
}
label = (String)arguments[0];
}
IMedia media = m_diskDrive.getDiskMedia(); IMedia media = m_diskDrive.getDiskMedia();
if( media != null ) if( media != null )

View File

@ -21,6 +21,8 @@ import net.minecraft.world.World;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
public abstract class ModemPeripheral public abstract class ModemPeripheral
implements IPeripheral, IPacketSender, IPacketReceiver implements IPeripheral, IPacketSender, IPacketReceiver
{ {
@ -147,11 +149,7 @@ public abstract class ModemPeripheral
private static int parseChannel( Object[] arguments, int index ) throws LuaException private static int parseChannel( Object[] arguments, int index ) throws LuaException
{ {
if( arguments.length <= index || !(arguments[index] instanceof Double) ) int channel = getInt( arguments, index );
{
throw new LuaException( "Expected number" );
}
int channel = (int)((Double)arguments[index]).doubleValue();
if( channel < 0 || channel > 65535 ) if( channel < 0 || channel > 65535 )
{ {
throw new LuaException( "Expected number in range 0-65535" ); throw new LuaException( "Expected number in range 0-65535" );

View File

@ -40,6 +40,8 @@ import javax.annotation.Nonnull;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
public class TileCable extends TileModemBase public class TileCable extends TileModemBase
implements IPacketNetwork implements IPacketNetwork
{ {
@ -116,15 +118,6 @@ public class TileCable extends TileModemBase
return newMethods; return newMethods;
} }
private String parseString( Object[] arguments, int index ) throws LuaException
{
if( arguments.length < (index + 1) || !(arguments[index] instanceof String) )
{
throw new LuaException( "Expected string" );
}
return (String)arguments[index];
}
@Override @Override
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
{ {
@ -148,13 +141,13 @@ public class TileCable extends TileModemBase
case 1: case 1:
{ {
// isPresentRemote // isPresentRemote
String type = m_entity.getTypeRemote( parseString( arguments, 0 ) ); String type = m_entity.getTypeRemote( getString( arguments, 0 ) );
return new Object[] { type != null }; return new Object[] { type != null };
} }
case 2: case 2:
{ {
// getTypeRemote // getTypeRemote
String type = m_entity.getTypeRemote( parseString( arguments, 0 ) ); String type = m_entity.getTypeRemote( getString( arguments, 0 ) );
if( type != null ) if( type != null )
{ {
return new Object[] { type }; return new Object[] { type };
@ -164,7 +157,7 @@ public class TileCable extends TileModemBase
case 3: case 3:
{ {
// getMethodsRemote // getMethodsRemote
String[] methodNames = m_entity.getMethodNamesRemote( parseString( arguments, 0 ) ); String[] methodNames = m_entity.getMethodNamesRemote( getString( arguments, 0 ) );
if( methodNames != null ) if( methodNames != null )
{ {
Map<Object,Object> table = new HashMap<Object,Object>(); Map<Object,Object> table = new HashMap<Object,Object>();
@ -178,8 +171,8 @@ public class TileCable extends TileModemBase
case 4: case 4:
{ {
// callRemote // callRemote
String remoteName = parseString( arguments, 0 ); String remoteName = getString( arguments, 0 );
String methodName = parseString( arguments, 1 ); String methodName = getString( arguments, 1 );
Object[] methodArgs = new Object[ arguments.length - 2 ]; Object[] methodArgs = new Object[ arguments.length - 2 ];
System.arraycopy( arguments, 2, methodArgs, 0, arguments.length - 2 ); System.arraycopy( arguments, 2, methodArgs, 0, arguments.length - 2 );
return m_entity.callMethodRemote( remoteName, context, methodName, methodArgs ); return m_entity.callMethodRemote( remoteName, context, methodName, methodArgs );

View File

@ -10,14 +10,15 @@ import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.apis.TermAPI;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.util.Palette; import dan200.computercraft.shared.util.Palette;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import java.util.HashMap;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class MonitorPeripheral implements IPeripheral public class MonitorPeripheral implements IPeripheral
{ {
private final TileMonitor m_monitor; private final TileMonitor m_monitor;
@ -90,23 +91,16 @@ public class MonitorPeripheral implements IPeripheral
case 1: case 1:
{ {
// scroll // scroll
if( args.length < 1 || !(args[0] instanceof Number) ) int value = getInt( args, 0 );
{
throw new LuaException( "Expected number" );
}
Terminal terminal = m_monitor.getTerminal().getTerminal(); Terminal terminal = m_monitor.getTerminal().getTerminal();
terminal.scroll( ((Number)(args[0])).intValue() ); terminal.scroll( value );
return null; return null;
} }
case 2: case 2:
{ {
// setCursorPos // setCursorPos
if( args.length < 2 || !(args[0] instanceof Number) || !(args[1] instanceof Number) ) int x = getInt( args, 0 ) - 1;
{ int y = getInt( args, 1 ) - 1;
throw new LuaException( "Expected number, number" );
}
int x = ((Number)args[0]).intValue() - 1;
int y = ((Number)args[1]).intValue() - 1;
Terminal terminal = m_monitor.getTerminal().getTerminal(); Terminal terminal = m_monitor.getTerminal().getTerminal();
terminal.setCursorPos( x, y ); terminal.setCursorPos( x, y );
return null; return null;
@ -114,12 +108,9 @@ public class MonitorPeripheral implements IPeripheral
case 3: case 3:
{ {
// setCursorBlink // setCursorBlink
if( args.length < 1 || !(args[0] instanceof Boolean) ) boolean blink = getBoolean( args, 0 );
{
throw new LuaException( "Expected boolean" );
}
Terminal terminal = m_monitor.getTerminal().getTerminal(); Terminal terminal = m_monitor.getTerminal().getTerminal();
terminal.setCursorBlink( (Boolean) args[ 0 ] ); terminal.setCursorBlink( blink );
return null; return null;
} }
case 4: case 4:
@ -157,11 +148,7 @@ public class MonitorPeripheral implements IPeripheral
case 8: case 8:
{ {
// setTextScale // setTextScale
if( args.length < 1 || !(args[0] instanceof Number) ) int scale = (int) (getReal( args, 0 ) * 2.0);
{
throw new LuaException( "Expected number" );
}
int scale = (int)(((Number)args[0]).doubleValue() * 2.0);
if( scale < 1 || scale > 10 ) if( scale < 1 || scale > 10 )
{ {
throw new LuaException( "Expected number in range 0.5-5" ); throw new LuaException( "Expected number in range 0.5-5" );
@ -173,7 +160,7 @@ public class MonitorPeripheral implements IPeripheral
case 10: case 10:
{ {
// setTextColour/setTextColor // setTextColour/setTextColor
int colour = dan200.computercraft.core.apis.TermAPI.parseColour( args ); int colour = TermAPI.parseColour( args );
Terminal terminal = m_monitor.getTerminal().getTerminal(); Terminal terminal = m_monitor.getTerminal().getTerminal();
terminal.setTextColour( colour ); terminal.setTextColour( colour );
return null; return null;
@ -182,7 +169,7 @@ public class MonitorPeripheral implements IPeripheral
case 12: case 12:
{ {
// setBackgroundColour/setBackgroundColor // setBackgroundColour/setBackgroundColor
int colour = dan200.computercraft.core.apis.TermAPI.parseColour( args ); int colour = TermAPI.parseColour( args );
Terminal terminal = m_monitor.getTerminal().getTerminal(); Terminal terminal = m_monitor.getTerminal().getTerminal();
terminal.setBackgroundColour( colour ); terminal.setBackgroundColour( colour );
return null; return null;
@ -200,26 +187,21 @@ public class MonitorPeripheral implements IPeripheral
{ {
// getTextColour/getTextColor // getTextColour/getTextColor
Terminal terminal = m_monitor.getTerminal().getTerminal(); Terminal terminal = m_monitor.getTerminal().getTerminal();
return dan200.computercraft.core.apis.TermAPI.encodeColour( terminal.getTextColour() ); return TermAPI.encodeColour( terminal.getTextColour() );
} }
case 17: case 17:
case 18: case 18:
{ {
// getBackgroundColour/getBackgroundColor // getBackgroundColour/getBackgroundColor
Terminal terminal = m_monitor.getTerminal().getTerminal(); Terminal terminal = m_monitor.getTerminal().getTerminal();
return dan200.computercraft.core.apis.TermAPI.encodeColour( terminal.getBackgroundColour() ); return TermAPI.encodeColour( terminal.getBackgroundColour() );
} }
case 19: case 19:
{ {
// blit // blit
if( args.length < 3 || !(args[0] instanceof String) || !(args[1] instanceof String) || !(args[2] instanceof String) ) String text = getString( args, 0 );
{ String textColour = getString( args, 1 );
throw new LuaException( "Expected string, string, string" ); String backgroundColour = getString( args, 2 );
}
String text = (String)args[0];
String textColour = (String)args[1];
String backgroundColour = (String)args[2];
if( textColour.length() != text.length() || backgroundColour.length() != text.length() ) if( textColour.length() != text.length() || backgroundColour.length() != text.length() )
{ {
throw new LuaException( "Arguments must be the same length" ); throw new LuaException( "Arguments must be the same length" );
@ -236,26 +218,21 @@ public class MonitorPeripheral implements IPeripheral
// setPaletteColour/setPaletteColor // setPaletteColour/setPaletteColor
Terminal terminal = m_monitor.getTerminal().getTerminal(); Terminal terminal = m_monitor.getTerminal().getTerminal();
if(args.length == 2 && args[0] instanceof Double && args[1] instanceof Double) int colour = 15 - TermAPI.parseColour( args );
if( args.length == 2 )
{ {
int colour = 15 - dan200.computercraft.core.apis.TermAPI.parseColour( args ); int hex = getInt( args, 1 );
int hex = ((Double)args[1]).intValue();
double[] rgb = Palette.decodeRGB8( hex ); double[] rgb = Palette.decodeRGB8( hex );
dan200.computercraft.core.apis.TermAPI.setColour( terminal, colour, rgb[0], rgb[1], rgb[2] ); TermAPI.setColour( terminal, colour, rgb[ 0 ], rgb[ 1 ], rgb[ 2 ] );
return null;
} }
else
if (args.length >= 4 && args[0] instanceof Double && args[1] instanceof Double && args[2] instanceof Double && args[3] instanceof Double)
{ {
int colour = 15 - dan200.computercraft.core.apis.TermAPI.parseColour( args ); double r = getReal( args, 1 );
double r = (Double)args[1]; double g = getReal( args, 2 );
double g = (Double)args[2]; double b = getReal( args, 3 );
double b = (Double)args[3]; TermAPI.setColour( terminal, colour, r, g, b );
dan200.computercraft.core.apis.TermAPI.setColour( terminal, colour, r, g, b );
return null;
} }
return null;
throw new LuaException( "Expected number, number, number, number" );
} }
case 22: case 22:
case 23: case 23:
@ -264,7 +241,7 @@ public class MonitorPeripheral implements IPeripheral
Terminal terminal = m_monitor.getTerminal().getTerminal(); Terminal terminal = m_monitor.getTerminal().getTerminal();
Palette palette = terminal.getPalette(); Palette palette = terminal.getPalette();
int colour = 15 - dan200.computercraft.core.apis.TermAPI.parseColour( args ); int colour = 15 - TermAPI.parseColour( args );
if( palette != null ) if( palette != null )
{ {

View File

@ -14,6 +14,9 @@ import dan200.computercraft.core.terminal.Terminal;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
import static dan200.computercraft.core.apis.ArgumentHelper.optString;
public class PrinterPeripheral implements IPeripheral public class PrinterPeripheral implements IPeripheral
{ {
private final TilePrinter m_printer; private final TilePrinter m_printer;
@ -70,13 +73,8 @@ public class PrinterPeripheral implements IPeripheral
case 1: case 1:
{ {
// setCursorPos // setCursorPos
if( args.length != 2 || args[0] == null || !(args[0] instanceof Number) || args[1] == null || !(args[1] instanceof Number) ) int x = getInt( args, 0 ) - 1;
{ int y = getInt( args, 1 ) - 1;
throw new LuaException( "Expected number, number" );
}
int x = ((Number)args[0]).intValue() - 1;
int y = ((Number)args[1]).intValue() - 1;
Terminal page = getCurrentPage(); Terminal page = getCurrentPage();
page.setCursorPos( x, y ); page.setCursorPos( x, y );
return null; return null;
@ -116,16 +114,7 @@ public class PrinterPeripheral implements IPeripheral
case 7: case 7:
{ {
// setPageTitle // setPageTitle
String title = ""; String title = optString( args, 0, "" );
if( args.length > 0 && args[0] != null )
{
if( !(args[0] instanceof String) )
{
throw new LuaException( "Expected string" );
}
title = (String)args[0];
}
getCurrentPage(); getCurrentPage();
m_printer.setPageTitle( title ); m_printer.setPageTitle( title );
return null; return null;

View File

@ -17,9 +17,13 @@ import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent; import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static dan200.computercraft.core.apis.ArgumentHelper.getString;
import static dan200.computercraft.core.apis.ArgumentHelper.optReal;
public class SpeakerPeripheral implements IPeripheral { public class SpeakerPeripheral implements IPeripheral {
private TileSpeaker m_speaker; private TileSpeaker m_speaker;
private long m_clock; private long m_clock;
@ -127,47 +131,20 @@ public class SpeakerPeripheral implements IPeripheral {
@Nonnull @Nonnull
private synchronized Object[] playNote( Object[] arguments, ILuaContext context ) throws LuaException private synchronized Object[] playNote( Object[] arguments, ILuaContext context ) throws LuaException
{ {
float volume = 1.0f; String name = getString(arguments, 0);
float pitch = 1.0f; float volume = (float) optReal( arguments, 1, 1.0 );
float pitch = (float) optReal( arguments, 2, 1.0 );
// Check if arguments are correct // Check if sound exists
if( arguments.length == 0 ) // Too few args if ( !SoundEvent.REGISTRY.containsKey( new ResourceLocation( "block.note." + name ) ) )
{
throw new LuaException( "Expected string, number (optional), number (optional)" );
}
if( !(arguments[0] instanceof String) ) // Arg wrong type
{
throw new LuaException("Expected string, number (optional), number (optional)");
}
if ( !SoundEvent.REGISTRY.containsKey( new ResourceLocation( "block.note." + arguments[0] ) ) )
{ {
throw new LuaException("Invalid instrument, \"" + arguments[0] + "\"!"); throw new LuaException("Invalid instrument, \"" + arguments[0] + "\"!");
} }
if ( arguments.length > 1 )
{
if ( arguments[1] != null && !(arguments[1] instanceof Double) ) // Arg wrong type
{
throw new LuaException( "Expected string, number (optional), number (optional)" );
}
volume = arguments[1] != null ? ((Double) arguments[1]).floatValue() : 1f;
}
if( arguments.length > 2 )
{
if( arguments[2] != null && !(arguments[2] instanceof Double) ) // Arg wrong type
{
throw new LuaException("Expected string, number (optional), number (optional)");
}
pitch = arguments[2] != null ? ((Double) arguments[2]).floatValue() : 1f;
}
// If the resource location for note block notes changes, this method call will need to be updated // If the resource location for note block notes changes, this method call will need to be updated
Object[] returnValue = playSound( Object[] returnValue = playSound(
new Object[] { new Object[] {
"block.note." + arguments[0], "block.note." + name,
(double)Math.min( volume, 3f ), (double)Math.min( volume, 3f ),
Math.pow( 2.0f, ( pitch - 12.0f ) / 12.0f) Math.pow( 2.0f, ( pitch - 12.0f ) / 12.0f)
}, context, true }, context, true
@ -184,42 +161,11 @@ public class SpeakerPeripheral implements IPeripheral {
@Nonnull @Nonnull
private synchronized Object[] playSound( Object[] arguments, ILuaContext context, boolean isNote ) throws LuaException private synchronized Object[] playSound( Object[] arguments, ILuaContext context, boolean isNote ) throws LuaException
{ {
String name = getString(arguments, 0);
float volume = (float) optReal( arguments, 1, 1.0 );
float pitch = (float) optReal( arguments, 2, 1.0 );
float volume = 1.0f; ResourceLocation resourceName = new ResourceLocation( name );
float pitch = 1.0f;
// Check if arguments are correct
if( arguments.length == 0 ) // Too few args
{
throw new LuaException( "Expected string, number (optional), number (optional)" );
}
if( !(arguments[0] instanceof String) ) // Arg wrong type
{
throw new LuaException( "Expected string, number (optional), number (optional)" );
}
if( arguments.length > 1 )
{
if( arguments[1] != null && !(arguments[1] instanceof Double) ) // Arg wrong type
{
throw new LuaException( "Expected string, number (optional), number (optional)" );
}
volume = arguments[1] != null ? ((Double) arguments[1]).floatValue() : 1f;
}
if( arguments.length > 2 )
{
if( arguments[2] != null && !(arguments[2] instanceof Double) ) // Arg wrong type
{
throw new LuaException( "Expected string, number (optional), number (optional)" );
}
pitch = arguments[2] != null ? ((Double) arguments[2]).floatValue() : 1f;
}
ResourceLocation resourceName = new ResourceLocation( (String) arguments[0] );
if( m_clock - m_lastPlayTime >= TileSpeaker.MIN_TICKS_BETWEEN_SOUNDS || ( ( m_clock - m_lastPlayTime == 0 ) && ( m_notesThisTick < ComputerCraft.maxNotesPerTick ) && isNote ) ) if( m_clock - m_lastPlayTime >= TileSpeaker.MIN_TICKS_BETWEEN_SOUNDS || ( ( m_clock - m_lastPlayTime == 0 ) && ( m_notesThisTick < ComputerCraft.maxNotesPerTick ) && isNote ) )
{ {

View File

@ -22,6 +22,8 @@ import javax.annotation.Nonnull;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class TurtleAPI implements ILuaAPI public class TurtleAPI implements ILuaAPI
{ {
private IAPIEnvironment m_environment; private IAPIEnvironment m_environment;
@ -115,38 +117,23 @@ public class TurtleAPI implements ILuaAPI
private int parseSlotNumber( Object[] arguments, int index ) throws LuaException private int parseSlotNumber( Object[] arguments, int index ) throws LuaException
{ {
int slot = parseOptionalSlotNumber( arguments, index, 99 ); int slot = getInt( arguments, index );
if( slot == 99 ) if( slot < 1 || slot > 16 ) throw new LuaException( "Slot number " + slot + " out of range" );
{ return slot - 1;
throw new LuaException( "Expected number" );
}
return slot;
} }
private int parseOptionalSlotNumber( Object[] arguments, int index, int fallback ) throws LuaException private int parseOptionalSlotNumber( Object[] arguments, int index, int fallback ) throws LuaException
{ {
if( arguments.length <= index || !(arguments[index] instanceof Number) ) if( index >= arguments.length || arguments[ index ] == null ) return fallback;
{
return fallback; int slot = getInt( arguments, index );
} if( slot < 1 || slot > 16 ) throw new LuaException( "Slot number " + slot + " out of range" );
int slot = ((Number)arguments[index]).intValue();
if( slot >= 1 && slot <= 16 )
{
return slot - 1; return slot - 1;
} }
else
{
throw new LuaException( "Slot number " + slot + " out of range" );
}
}
private int parseCount( Object[] arguments, int index ) throws LuaException private int parseCount( Object[] arguments, int index ) throws LuaException
{ {
if( arguments.length <= index || !(arguments[index] instanceof Number) ) int count = optInt( arguments, index, 64 );
{
throw new LuaException( "Expected number" );
}
int count = ((Number)arguments[index]).intValue();
if( count >= 0 && count <= 64 ) if( count >= 0 && count <= 64 )
{ {
return count; return count;
@ -159,19 +146,16 @@ public class TurtleAPI implements ILuaAPI
private Optional<TurtleSide> parseSide( Object[] arguments, int index ) throws LuaException private Optional<TurtleSide> parseSide( Object[] arguments, int index ) throws LuaException
{ {
if( arguments.length <= index || arguments[index] == null ) String side = optString( arguments, index, null );
if( side == null )
{ {
return Optional.absent(); return Optional.absent();
} }
if( !(arguments[ index ] instanceof String) ) else if( side.equalsIgnoreCase( "left" ) )
{
throw new LuaException( "Expected string" );
}
if( arguments[ index ].equals( "left" ) )
{ {
return Optional.of( TurtleSide.Left ); return Optional.of( TurtleSide.Left );
} }
else if( arguments[ index ].equals( "right" ) ) else if( side.equalsIgnoreCase( "right" ) )
{ {
return Optional.of( TurtleSide.Right ); return Optional.of( TurtleSide.Right );
} }
@ -252,11 +236,7 @@ public class TurtleAPI implements ILuaAPI
case 12: case 12:
{ {
// drop // drop
int count = 64; int count = parseCount( args, 0 );
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
return tryCommand( context, new TurtleDropCommand( InteractDirection.Forward, count ) ); return tryCommand( context, new TurtleDropCommand( InteractDirection.Forward, count ) );
} }
case 13: case 13:
@ -343,51 +323,31 @@ public class TurtleAPI implements ILuaAPI
case 25: case 25:
{ {
// dropUp // dropUp
int count = 64; int count = parseCount( args, 0 );
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
return tryCommand( context, new TurtleDropCommand( InteractDirection.Up, count ) ); return tryCommand( context, new TurtleDropCommand( InteractDirection.Up, count ) );
} }
case 26: case 26:
{ {
// dropDown // dropDown
int count = 64; int count = parseCount( args, 0 );
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
return tryCommand( context, new TurtleDropCommand( InteractDirection.Down, count ) ); return tryCommand( context, new TurtleDropCommand( InteractDirection.Down, count ) );
} }
case 27: case 27:
{ {
// suck // suck
int count = 64; int count = parseCount( args, 0 );
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
return tryCommand( context, new TurtleSuckCommand( InteractDirection.Forward, count ) ); return tryCommand( context, new TurtleSuckCommand( InteractDirection.Forward, count ) );
} }
case 28: case 28:
{ {
// suckUp // suckUp
int count = 64; int count = parseCount( args, 0 );
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
return tryCommand( context, new TurtleSuckCommand( InteractDirection.Up, count ) ); return tryCommand( context, new TurtleSuckCommand( InteractDirection.Up, count ) );
} }
case 29: case 29:
{ {
// suckDown // suckDown
int count = 64; int count = parseCount( args, 0 );
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
return tryCommand( context, new TurtleSuckCommand( InteractDirection.Down, count ) ); return tryCommand( context, new TurtleSuckCommand( InteractDirection.Down, count ) );
} }
case 30: case 30:
@ -405,11 +365,7 @@ public class TurtleAPI implements ILuaAPI
case 31: case 31:
{ {
// refuel // refuel
int count = 64; int count = parseCount( args, 0 );
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
return tryCommand( context, new TurtleRefuelCommand( count ) ); return tryCommand( context, new TurtleRefuelCommand( count ) );
} }
case 32: case 32:
@ -422,11 +378,7 @@ public class TurtleAPI implements ILuaAPI
{ {
// transferTo // transferTo
int slot = parseSlotNumber( args, 0 ); int slot = parseSlotNumber( args, 0 );
int count = 64; int count = parseCount( args, 1 );
if( args.length > 1 )
{
count = parseCount( args, 1 );
}
return tryCommand( context, new TurtleTransferToCommand( slot, count ) ); return tryCommand( context, new TurtleTransferToCommand( slot, count ) );
} }
case 34: case 34:

View File

@ -24,13 +24,16 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntitySign; import net.minecraft.tileentity.TileEntitySign;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldServer; import net.minecraft.world.WorldServer;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.fml.common.eventhandler.Event;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -215,7 +218,6 @@ public class TurtlePlaceCommand implements ITurtleCommand
} }
// Load up the turtle's inventory // Load up the turtle's inventory
Item item = stack.getItem();
ItemStack stackCopy = stack.copy(); ItemStack stackCopy = stack.copy();
turtlePlayer.loadInventory( stackCopy ); turtlePlayer.loadInventory( stackCopy );
@ -237,23 +239,28 @@ public class TurtlePlaceCommand implements ITurtleCommand
// Place on the entity // Place on the entity
boolean placed = false; boolean placed = false;
if( hitEntity.applyPlayerInteraction( turtlePlayer, hitPos, stackCopy, EnumHand.MAIN_HAND ) == EnumActionResult.SUCCESS ) if( !ForgeHooks.onInteractEntityAt( turtlePlayer, hitEntity, hitPos, stack, EnumHand.MAIN_HAND ) &&
hitEntity.applyPlayerInteraction( turtlePlayer, hitPos, stackCopy, EnumHand.MAIN_HAND ) == EnumActionResult.SUCCESS )
{ {
placed = true; placed = true;
turtlePlayer.loadInventory( stackCopy ); turtlePlayer.loadInventory( stackCopy );
} }
else if( hitEntity.processInitialInteract( turtlePlayer, stackCopy, EnumHand.MAIN_HAND ) ) else if( !ForgeHooks.onInteractEntity( turtlePlayer, hitEntity, stack, EnumHand.MAIN_HAND ) )
{
// See EntityPlayer.interact
if( hitEntity.processInitialInteract( turtlePlayer, stackCopy, EnumHand.MAIN_HAND ) )
{ {
placed = true; placed = true;
} }
else if( hitEntity instanceof EntityLivingBase ) else if( hitEntity instanceof EntityLivingBase )
{ {
placed = item.itemInteractionForEntity( stackCopy, turtlePlayer, (EntityLivingBase)hitEntity, EnumHand.MAIN_HAND ); placed = stackCopy.interactWithEntity( turtlePlayer, (EntityLivingBase) hitEntity, EnumHand.MAIN_HAND );
if( placed ) if( placed )
{ {
turtlePlayer.loadInventory( stackCopy ); turtlePlayer.loadInventory( stackCopy );
} }
} }
}
// Stop claiming drops // Stop claiming drops
ComputerCraft.clearEntityDropConsumer( hitEntity ); ComputerCraft.clearEntityDropConsumer( hitEntity );
@ -352,19 +359,29 @@ public class TurtlePlaceCommand implements ITurtleCommand
// Do the deploying (put everything in the players inventory) // Do the deploying (put everything in the players inventory)
boolean placed = false; boolean placed = false;
// See PlayerInteractionManager.processRightClickBlock
PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock( turtlePlayer, EnumHand.MAIN_HAND, stackCopy, position, side, new Vec3d( hitX, hitY, hitZ ) );
if( !event.isCanceled() )
{
if( item.onItemUseFirst( stackCopy, turtlePlayer, turtle.getWorld(), position, side, hitX, hitY, hitZ, EnumHand.MAIN_HAND ) == EnumActionResult.SUCCESS ) if( item.onItemUseFirst( stackCopy, turtlePlayer, turtle.getWorld(), position, side, hitX, hitY, hitZ, EnumHand.MAIN_HAND ) == EnumActionResult.SUCCESS )
{ {
placed = true; placed = true;
turtlePlayer.loadInventory( stackCopy ); turtlePlayer.loadInventory( stackCopy );
} }
else if( item.onItemUse( stackCopy, turtlePlayer, turtle.getWorld(), position, EnumHand.MAIN_HAND, side, hitX, hitY, hitZ ) == EnumActionResult.SUCCESS ) else if( event.getUseItem() != Event.Result.DENY &&
stackCopy.onItemUse( turtlePlayer, turtle.getWorld(), position, EnumHand.MAIN_HAND, side, hitX, hitY, hitZ ) == EnumActionResult.SUCCESS )
{ {
placed = true; placed = true;
turtlePlayer.loadInventory( stackCopy ); turtlePlayer.loadInventory( stackCopy );
} }
else if( item instanceof ItemBucket || item instanceof ItemBoat || item instanceof ItemLilyPad || item instanceof ItemGlassBottle ) }
if( !placed && (item instanceof ItemBucket || item instanceof ItemBoat || item instanceof ItemLilyPad || item instanceof ItemGlassBottle)
&& ForgeHooks.onItemRightClick( turtlePlayer, EnumHand.MAIN_HAND, stackCopy ) )
{ {
ActionResult<ItemStack> result = item.onItemRightClick( stackCopy, turtle.getWorld(), turtlePlayer, EnumHand.MAIN_HAND ); ActionResult<ItemStack> result = stackCopy.useItemRightClick( turtle.getWorld(), turtlePlayer, EnumHand.MAIN_HAND );
if( result.getType() == EnumActionResult.SUCCESS && !ItemStack.areItemStacksEqual( stack, result.getResult() ) ) if( result.getType() == EnumActionResult.SUCCESS && !ItemStack.areItemStacksEqual( stack, result.getResult() ) )
{ {
placed = true; placed = true;

View File

@ -15,6 +15,8 @@ import dan200.computercraft.shared.turtle.core.TurtleCraftCommand;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.optInt;
public class CraftingTablePeripheral public class CraftingTablePeripheral
implements IPeripheral implements IPeripheral
{ {
@ -45,16 +47,7 @@ public class CraftingTablePeripheral
private int parseCount( Object[] arguments ) throws LuaException private int parseCount( Object[] arguments ) throws LuaException
{ {
if( arguments.length < 1 ) int count = optInt( arguments, 0, 64 );
{
return 64;
}
if( !(arguments[0] instanceof Number) )
{
throw new LuaException( "Expected number" );
}
int count = ((Number)arguments[0]).intValue();
if( count < 0 || count > 64 ) if( count < 0 || count > 64 )
{ {
throw new LuaException( "Crafting count " + count + " out of range" ); throw new LuaException( "Crafting count " + count + " out of range" );

View File

@ -22,14 +22,22 @@ import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.item.EntityArmorStand; import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.*; import net.minecraft.util.DamageSource;
import net.minecraft.util.math.*; import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldServer; import net.minecraft.world.WorldServer;
import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.fml.relauncher.SideOnly;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
@ -189,9 +197,10 @@ public class TurtleTool implements ITurtleUpgrade
} }
} ); } );
// Place on the entity // Attack the entity
boolean placed = false; boolean attacked = false;
if( hitEntity.canBeAttackedWithItem() && !hitEntity.hitByEntity( turtlePlayer ) ) if( hitEntity.canBeAttackedWithItem() && !hitEntity.hitByEntity( turtlePlayer )
&& !MinecraftForge.EVENT_BUS.post( new AttackEntityEvent( turtlePlayer, hitEntity ) ) )
{ {
float damage = (float)turtlePlayer.getEntityAttribute( SharedMonsterAttributes.ATTACK_DAMAGE ).getAttributeValue(); float damage = (float)turtlePlayer.getEntityAttribute( SharedMonsterAttributes.ATTACK_DAMAGE ).getAttributeValue();
damage *= getDamageMultiplier(); damage *= getDamageMultiplier();
@ -206,13 +215,13 @@ public class TurtleTool implements ITurtleUpgrade
{ {
hitEntity.attackEntityFrom( source, damage ); hitEntity.attackEntityFrom( source, damage );
} }
placed = true; attacked = true;
} }
else else
{ {
if( hitEntity.attackEntityFrom( source, damage ) ) if( hitEntity.attackEntityFrom( source, damage ) )
{ {
placed = true; attacked = true;
} }
} }
} }
@ -222,7 +231,7 @@ public class TurtleTool implements ITurtleUpgrade
ComputerCraft.clearEntityDropConsumer( hitEntity ); ComputerCraft.clearEntityDropConsumer( hitEntity );
// Put everything we collected into the turtles inventory, then return // Put everything we collected into the turtles inventory, then return
if( placed ) if( attacked )
{ {
turtlePlayer.unloadInventory( turtle ); turtlePlayer.unloadInventory( turtle );
return TurtleCommandResult.success(); return TurtleCommandResult.success();
@ -243,10 +252,16 @@ public class TurtleTool implements ITurtleUpgrade
!world.isAirBlock( newPosition ) && !world.isAirBlock( newPosition ) &&
!WorldUtil.isLiquidBlock( world, newPosition ) ) !WorldUtil.isLiquidBlock( world, newPosition ) )
{ {
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, position, direction );
if( ComputerCraft.turtlesObeyBlockProtection ) if( ComputerCraft.turtlesObeyBlockProtection )
{ {
// Check spawn protection // Check spawn protection
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, position, direction );
if( MinecraftForge.EVENT_BUS.post( new BlockEvent.BreakEvent( world, newPosition, world.getBlockState( newPosition ), turtlePlayer ) ) )
{
return TurtleCommandResult.failure( "Cannot break protected block" );
}
if( !ComputerCraft.isBlockEditable( world, newPosition, turtlePlayer ) ) if( !ComputerCraft.isBlockEditable( world, newPosition, turtlePlayer ) )
{ {
return TurtleCommandResult.failure( "Cannot break protected block" ); return TurtleCommandResult.failure( "Cannot break protected block" );
@ -262,7 +277,7 @@ public class TurtleTool implements ITurtleUpgrade
// Consume the items the block drops // Consume the items the block drops
if( canHarvestBlock( world, newPosition ) ) if( canHarvestBlock( world, newPosition ) )
{ {
List<ItemStack> items = getBlockDropped( world, newPosition ); List<ItemStack> items = getBlockDropped( world, newPosition, turtlePlayer );
if( items != null && items.size() > 0 ) if( items != null && items.size() > 0 )
{ {
for( ItemStack stack : items ) for( ItemStack stack : items )
@ -295,9 +310,20 @@ public class TurtleTool implements ITurtleUpgrade
return TurtleCommandResult.failure( "Nothing to dig here" ); return TurtleCommandResult.failure( "Nothing to dig here" );
} }
private java.util.List<ItemStack> getBlockDropped( World world, BlockPos pos ) private List<ItemStack> getBlockDropped( World world, BlockPos pos, EntityPlayer player )
{ {
Block block = world.getBlockState( pos ).getBlock(); IBlockState state = world.getBlockState( pos );
return block.getDrops( world, pos, world.getBlockState( pos ), 0 ); Block block = state.getBlock();
List<ItemStack> drops = block.getDrops( world, pos, world.getBlockState( pos ), 0 );
double chance = ForgeEventFactory.fireBlockHarvesting( drops, world, pos, state, 0, 1, false, player );
for( int i = drops.size() - 1; i >= 0; i-- )
{
if( world.rand.nextFloat() > chance )
{
drops.remove( i );
}
}
return drops;
} }
} }

View File

@ -163,13 +163,13 @@ public class InventoryUtil
} }
// Inspect the slots in order and try to find empty or stackable slots // Inspect the slots in order and try to find empty or stackable slots
ItemStack remainder = stack; ItemStack remainder = stack.copy();
for( int slot : slots ) for( int slot : slots )
{ {
if( remainder == null ) break; if( remainder == null ) break;
remainder = inventory.insertItem( slot, remainder, false ); remainder = inventory.insertItem( slot, remainder, false );
} }
return remainder; return areItemsEqual( stack, remainder ) ? stack : remainder;
} }
private static ItemStack takeItems( int count, IItemHandler inventory, int[] slots ) private static ItemStack takeItems( int count, IItemHandler inventory, int[] slots )

View File

@ -44,6 +44,7 @@ gui.computercraft:wired_modem.peripheral_disconnected=Peripheral "%s" disconnect
gui.computercraft:config.http_enable=Enable HTTP API gui.computercraft:config.http_enable=Enable HTTP API
gui.computercraft:config.http_whitelist=HTTP whitelist gui.computercraft:config.http_whitelist=HTTP whitelist
gui.computercraft:config.http_blacklist=HTTP blacklist
gui.computercraft:config.disable_lua51_features=Disable Lua 5.1 features gui.computercraft:config.disable_lua51_features=Disable Lua 5.1 features
gui.computercraft:config.default_computer_settings=Default Computer settings gui.computercraft:config.default_computer_settings=Default Computer settings
gui.computercraft:config.log_peripheral_errors=Log peripheral errors gui.computercraft:config.log_peripheral_errors=Log peripheral errors

View File

@ -184,6 +184,9 @@ end
-- Install globals -- Install globals
function sleep( nTime ) function sleep( nTime )
if nTime ~= nil and type( nTime ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( nTime ) .. ")", 2 )
end
local timer = os.startTimer( nTime or 0 ) local timer = os.startTimer( nTime or 0 )
repeat repeat
local sEvent, param = os.pullEvent( "timer" ) local sEvent, param = os.pullEvent( "timer" )
@ -191,6 +194,10 @@ function sleep( nTime )
end end
function write( sText ) function write( sText )
if sText ~= nil and type( sText ) ~= "string" and type( sText ) ~= "number" then
error( "bad argument #1 (expected string, got " .. type( sText ) .. ")", 2 )
end
local w,h = term.getSize() local w,h = term.getSize()
local x,y = term.getCursorPos() local x,y = term.getCursorPos()
@ -276,12 +283,29 @@ function printError( ... )
end end
end end
function read( _sReplaceChar, _tHistory, _fnComplete ) function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
if _sReplaceChar ~= nil and type( _sReplaceChar ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sReplaceChar ) .. ")", 2 )
end
if _tHistory ~= nil and type( _tHistory ) ~= "table" then
error( "bad argument #2 (expected table, got " .. type( _tHistory ) .. ")", 2 )
end
if _fnComplete ~= nil and type( _fnComplete ) ~= "function" then
error( "bad argument #3 (expected function, got " .. type( _fnComplete ) .. ")", 2 )
end
if _sDefault ~= nil and type( _sDefault ) ~= "string" then
error( "bad argument #4 (expected string, got " .. type( _sDefault ) .. ")", 2 )
end
term.setCursorBlink( true ) term.setCursorBlink( true )
local sLine = "" local sLine
if type( _sDefault ) == "string" then
sLine = _sDefault
else
sLine = ""
end
local nHistoryPos local nHistoryPos
local nPos = 0 local nPos = #sLine
if _sReplaceChar then if _sReplaceChar then
_sReplaceChar = string.sub( _sReplaceChar, 1, 1 ) _sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
end end
@ -530,6 +554,12 @@ function read( _sReplaceChar, _tHistory, _fnComplete )
end end
loadfile = function( _sFile, _tEnv ) loadfile = function( _sFile, _tEnv )
if type( _sFile ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sFile ) .. ")", 2 )
end
if _tEnv ~= nil and type( _tEnv ) ~= "table" then
error( "bad argument #2 (expected table, got " .. type( _tEnv ) .. ")", 2 )
end
local file = fs.open( _sFile, "r" ) local file = fs.open( _sFile, "r" )
if file then if file then
local func, err = load( file.readAll(), fs.getName( _sFile ), "t", _tEnv ) local func, err = load( file.readAll(), fs.getName( _sFile ), "t", _tEnv )
@ -540,6 +570,9 @@ loadfile = function( _sFile, _tEnv )
end end
dofile = function( _sFile ) dofile = function( _sFile )
if type( _sFile ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sFile ) .. ")", 2 )
end
local fnFile, e = loadfile( _sFile, _G ) local fnFile, e = loadfile( _sFile, _G )
if fnFile then if fnFile then
return fnFile() return fnFile()
@ -550,6 +583,12 @@ end
-- Install the rest of the OS api -- Install the rest of the OS api
function os.run( _tEnv, _sPath, ... ) function os.run( _tEnv, _sPath, ... )
if type( _tEnv ) ~= "table" then
error( "bad argument #1 (expected table, got " .. type( _tEnv ) .. ")", 2 )
end
if type( _sPath ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( _sPath ) .. ")", 2 )
end
local tArgs = table.pack( ... ) local tArgs = table.pack( ... )
local tEnv = _tEnv local tEnv = _tEnv
setmetatable( tEnv, { __index = _G } ) setmetatable( tEnv, { __index = _G } )
@ -574,6 +613,9 @@ end
local tAPIsLoading = {} local tAPIsLoading = {}
function os.loadAPI( _sPath ) function os.loadAPI( _sPath )
if type( _sPath ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sPath ) .. ")", 2 )
end
local sName = fs.getName( _sPath ) local sName = fs.getName( _sPath )
if sName:sub(-4) == ".lua" then if sName:sub(-4) == ".lua" then
sName = sName:sub(1,-5) sName = sName:sub(1,-5)
@ -613,6 +655,9 @@ function os.loadAPI( _sPath )
end end
function os.unloadAPI( _sName ) function os.unloadAPI( _sName )
if type( _sName ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sName ) .. ")", 2 )
end
if _sName ~= "_G" and type(_G[_sName]) == "table" then if _sName ~= "_G" and type(_G[_sName]) == "table" then
_G[_sName] = nil _G[_sName] = nil
end end
@ -658,25 +703,73 @@ if http then
end end
http.get = function( _url, _headers, _binary) http.get = function( _url, _headers, _binary)
if type( _url ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
end
if _headers ~= nil and type( _headers ) ~= "table" then
error( "bad argument #2 (expected table, got " .. type( _headers ) .. ")", 2 )
end
return wrapRequest( _url, nil, _headers, _binary) return wrapRequest( _url, nil, _headers, _binary)
end end
http.post = function( _url, _post, _headers, _binary) http.post = function( _url, _post, _headers, _binary)
if type( _url ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
end
if type( _post ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( _post ) .. ")", 2 )
end
if _headers ~= nil and type( _headers ) ~= "table" then
error( "bad argument #3 (expected table, got " .. type( _headers ) .. ")", 2 )
end
return wrapRequest( _url, _post or "", _headers, _binary) return wrapRequest( _url, _post or "", _headers, _binary)
end end
http.request = function( _url, _post, _headers, _binary ) http.request = function( _url, _post, _headers, _binary )
if type( _url ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
end
if _post ~= nil and type( _post ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( _post ) .. ")", 2 )
end
if _headers ~= nil and type( _headers ) ~= "table" then
error( "bad argument #3 (expected table, got " .. type( _headers ) .. ")", 2 )
end
local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary ) local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary )
if not ok then if not ok then
os.queueEvent( "http_failure", _url, err ) os.queueEvent( "http_failure", _url, err )
end end
return ok, err return ok, err
end end
local nativeCheckURL = http.checkURL
http.checkURLAsync = nativeCheckURL
http.checkURL = function( _url )
local ok, err = nativeCheckURL( _url )
if not ok then return ok, err end
while true do
local event, url, ok, err = os.pullEvent( "http_check" )
if url == _url then return ok, err end
end
end
end end
-- Install the lua part of the FS api -- Install the lua part of the FS api
local tEmpty = {} local tEmpty = {}
function fs.complete( sPath, sLocation, bIncludeFiles, bIncludeDirs ) function fs.complete( sPath, sLocation, bIncludeFiles, bIncludeDirs )
if type( sPath ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( sPath ) .. ")", 2 )
end
if type( sLocation ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( sLocation ) .. ")", 2 )
end
if bIncludeFiles ~= nil and type( bIncludeFiles ) ~= "boolean" then
error( "bad argument #3 (expected boolean, got " .. type( bIncludeFiles ) .. ")", 2 )
end
if bIncludeDirs ~= nil and type( bIncludeDirs ) ~= "boolean" then
error( "bad argument #4 (expected boolean, got " .. type( bIncludeDirs ) .. ")", 2 )
end
bIncludeFiles = (bIncludeFiles ~= false) bIncludeFiles = (bIncludeFiles ~= false)
bIncludeDirs = (bIncludeDirs ~= false) bIncludeDirs = (bIncludeDirs ~= false)
local sDir = sLocation local sDir = sLocation
@ -747,7 +840,7 @@ for n,sFile in ipairs( tApis ) do
end end
end end
if turtle then if turtle and fs.isDir( "rom/apis/turtle" ) then
-- Load turtle APIs -- Load turtle APIs
local tApis = fs.list( "rom/apis/turtle" ) local tApis = fs.list( "rom/apis/turtle" )
for n,sFile in ipairs( tApis ) do for n,sFile in ipairs( tApis ) do

View File

@ -19,32 +19,51 @@ black = 32768
function combine( ... ) function combine( ... )
local r = 0 local r = 0
for n,c in ipairs( { ... } ) do for n,c in ipairs( { ... } ) do
if type( c ) ~= "number" then
error( "bad argument #"..n.." (expected number, got " .. type( c ) .. ")", 2 )
end
r = bit32.bor(r,c) r = bit32.bor(r,c)
end end
return r return r
end end
function subtract( colors, ... ) function subtract( colors, ... )
if type( colors ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( colors ) .. ")", 2 )
end
local r = colors local r = colors
for n,c in ipairs( { ... } ) do for n,c in ipairs( { ... } ) do
if type( c ) ~= "number" then
error( "bad argument #"..tostring( n+1 ).." (expected number, got " .. type( c ) .. ")", 2 )
end
r = bit32.band(r, bit32.bnot(c)) r = bit32.band(r, bit32.bnot(c))
end end
return r return r
end end
function test( colors, color ) function test( colors, color )
if type( colors ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( colors ) .. ")", 2 )
end
if type( color ) ~= "number" then
error( "bad argument #2 (expected number, got " .. type( color ) .. ")", 2 )
end
return ((bit32.band(colors, color)) == color) return ((bit32.band(colors, color)) == color)
end end
function rgb8( r, g, b ) function rgb8( r, g, b )
if type(r) == "number" and g == nil and b == nil then if type( r ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( r ) .. ")", 2 )
elseif type(r) == "number" and g == nil and b == nil then
return bit32.band( bit32.rshift( r, 16 ), 0xFF ) / 255, bit32.band( bit32.rshift( r, 8 ), 0xFF ) / 255, bit32.band( r, 0xFF ) / 255 return bit32.band( bit32.rshift( r, 16 ), 0xFF ) / 255, bit32.band( bit32.rshift( r, 8 ), 0xFF ) / 255, bit32.band( r, 0xFF ) / 255
elseif type(r) == "number" and type(g) == "number" and type(b) == "number" then elseif type(r) == "number" and type(g) == "number" and type(b) == "number" then
return return
bit32.lshift( bit32.band(r * 255, 0xFF), 16 ) + bit32.lshift( bit32.band(r * 255, 0xFF), 16 ) +
bit32.lshift( bit32.band(g * 255, 0xFF), 8 ) + bit32.lshift( bit32.band(g * 255, 0xFF), 8 ) +
bit32.band(b * 255, 0xFF) bit32.band(b * 255, 0xFF)
else elseif type( g ) ~= "number" then
error( "Expected 1 or 3 numbers" ) error( "bad argument #2 (expected number, got " .. type( g ) .. ")", 2 )
elseif type( b ) ~= "number" then
error( "bad argument #3 (expected number, got " .. type( b ) .. ")", 2 )
end end
end end

View File

@ -1,5 +1,8 @@
local function isDrive( name ) local function isDrive( name )
if type( name ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( name ) .. ")", 3 )
end
return peripheral.getType( name ) == "drive" return peripheral.getType( name ) == "drive"
end end

View File

@ -56,6 +56,12 @@ local function narrow( p1, p2, fix )
end end
function locate( _nTimeout, _bDebug ) function locate( _nTimeout, _bDebug )
if _nTimeout ~= nil and type( _nTimeout ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( _nTimeout ) .. ")", 2 )
end
if _bDebug ~= nil and type( _bDebug ) ~= "boolean" then
error( "bad argument #2 (expected boolean, got " .. type( _bDebug) .. ")", 2 )
end
-- Let command computers use their magic fourth-wall-breaking special abilities -- Let command computers use their magic fourth-wall-breaking special abilities
if commands then if commands then
return commands.getBlockPosition() return commands.getBlockPosition()

View File

@ -6,10 +6,16 @@ function path()
end end
function setPath( _sPath ) function setPath( _sPath )
if type( _sPath ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sPath ) .. ")", 2 )
end
sPath = _sPath sPath = _sPath
end end
function lookup( _sTopic ) function lookup( _sTopic )
if type( _sTopic ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sTopic ) .. ")", 2 )
end
-- Look on the path variable -- Look on the path variable
for sPath in string.gmatch(sPath, "[^:]+") do for sPath in string.gmatch(sPath, "[^:]+") do
sPath = fs.combine( sPath, _sTopic ) sPath = fs.combine( sPath, _sTopic )
@ -57,6 +63,9 @@ function topics()
end end
function completeTopic( sText ) function completeTopic( sText )
if type( sText ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( sText ) .. ")", 2 )
end
local tTopics = topics() local tTopics = topics()
local tResults = {} local tResults = {}
for n=1,#tTopics do for n=1,#tTopics do

View File

@ -55,5 +55,8 @@ end
keys["return"] = keys.enter keys["return"] = keys.enter
function getName( _nKey ) function getName( _nKey )
if type( _nKey ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( _nKey ) .. ")", 2 )
end
return tKeys[ _nKey ] return tKeys[ _nKey ]
end end

View File

@ -11,7 +11,7 @@ end
function loadImage( sPath ) function loadImage( sPath )
if type( sPath ) ~= "string" then if type( sPath ) ~= "string" then
error( "Expected path", 2 ) error( "bad argument #1 (expected string, got " .. type( sPath ) .. ")", 2 )
end end
local tImage = {} local tImage = {}
@ -33,9 +33,9 @@ function loadImage( sPath )
end end
function drawPixel( xPos, yPos, nColour ) function drawPixel( xPos, yPos, nColour )
if type( xPos ) ~= "number" or type( yPos ) ~= "number" or (nColour ~= nil and type( nColour ) ~= "number") then if type( xPos ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( xPos ) .. ")", 2 ) end
error( "Expected x, y, colour", 2 ) if type( yPos ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( yPos ) .. ")", 2 ) end
end if nColour ~= nil and type( nColour ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( nColour ) .. ")", 2 ) end
if nColour then if nColour then
term.setBackgroundColor( nColour ) term.setBackgroundColor( nColour )
end end
@ -43,11 +43,11 @@ function drawPixel( xPos, yPos, nColour )
end end
function drawLine( startX, startY, endX, endY, nColour ) function drawLine( startX, startY, endX, endY, nColour )
if type( startX ) ~= "number" or type( startX ) ~= "number" or if type( startX ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( startX ) .. ")", 2 ) end
type( endX ) ~= "number" or type( endY ) ~= "number" or if type( startY ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( startY ) .. ")", 2 ) end
(nColour ~= nil and type( nColour ) ~= "number") then if type( endX ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( endX ) .. ")", 2 ) end
error( "Expected startX, startY, endX, endY, colour", 2 ) if type( endY ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( endY ) .. ")", 2 ) end
end if nColour ~= nil and type( nColour ) ~= "number" then error( "bad argument #5 (expected number, got " .. type( nColour ) .. ")", 2 ) end
startX = math.floor(startX) startX = math.floor(startX)
startY = math.floor(startY) startY = math.floor(startY)
@ -103,11 +103,11 @@ function drawLine( startX, startY, endX, endY, nColour )
end end
function drawBox( startX, startY, endX, endY, nColour ) function drawBox( startX, startY, endX, endY, nColour )
if type( startX ) ~= "number" or type( startX ) ~= "number" or if type( startX ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( startX ) .. ")", 2 ) end
type( endX ) ~= "number" or type( endY ) ~= "number" or if type( startY ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( startY ) .. ")", 2 ) end
(nColour ~= nil and type( nColour ) ~= "number") then if type( endX ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( endX ) .. ")", 2 ) end
error( "Expected startX, startY, endX, endY, colour", 2 ) if type( endY ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( endY ) .. ")", 2 ) end
end if nColour ~= nil and type( nColour ) ~= "number" then error( "bad argument #5 (expected number, got " .. type( nColour ) .. ")", 2 ) end
startX = math.floor(startX) startX = math.floor(startX)
startY = math.floor(startY) startY = math.floor(startY)
@ -147,11 +147,11 @@ function drawBox( startX, startY, endX, endY, nColour )
end end
function drawFilledBox( startX, startY, endX, endY, nColour ) function drawFilledBox( startX, startY, endX, endY, nColour )
if type( startX ) ~= "number" or type( startX ) ~= "number" or if type( startX ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( startX ) .. ")", 2 ) end
type( endX ) ~= "number" or type( endY ) ~= "number" or if type( startY ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( startY ) .. ")", 2 ) end
(nColour ~= nil and type( nColour ) ~= "number") then if type( endX ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( endX ) .. ")", 2 ) end
error( "Expected startX, startY, endX, endY, colour", 2 ) if type( endY ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( endY ) .. ")", 2 ) end
end if nColour ~= nil and type( nColour ) ~= "number" then error( "bad argument #5 (expected number, got " .. type( nColour ) .. ")", 2 ) end
startX = math.floor(startX) startX = math.floor(startX)
startY = math.floor(startY) startY = math.floor(startY)
@ -185,9 +185,9 @@ function drawFilledBox( startX, startY, endX, endY, nColour )
end end
function drawImage( tImage, xPos, yPos ) function drawImage( tImage, xPos, yPos )
if type( tImage ) ~= "table" or type( xPos ) ~= "number" or type( yPos ) ~= "number" then if type( tImage ) ~= "table" then error( "bad argument #1 (expected table, got " .. type( tImage ) .. ")", 2 ) end
error( "Expected image, x, y", 2 ) if type( xPos ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( xPos ) .. ")", 2 ) end
end if type( yPos ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( yPos ) .. ")", 2 ) end
for y=1,#tImage do for y=1,#tImage do
local tLine = tImage[y] local tLine = tImage[y]
for x=1,#tLine do for x=1,#tLine do

View File

@ -1,12 +1,17 @@
local function create( first, ... ) local function create( ... )
if first ~= nil then local tFns = table.pack(...)
if type( first ) ~= "function" then local tCos = {}
error( "Expected function, got "..type( first ), 3 ) for i = 1, tFns.n, 1 do
local fn = tFns[i]
if type( fn ) ~= "function" then
error( "bad argument #" .. i .. " (expected function, got " .. type( fn ) .. ")", 3 )
end end
return coroutine.create(first), create( ... )
tCos[i] = coroutine.create(fn)
end end
return nil
return tCos
end end
local function runUntilLimit( _routines, _limit ) local function runUntilLimit( _routines, _limit )
@ -51,11 +56,11 @@ local function runUntilLimit( _routines, _limit )
end end
function waitForAny( ... ) function waitForAny( ... )
local routines = { create( ... ) } local routines = create( ... )
return runUntilLimit( routines, #routines - 1 ) return runUntilLimit( routines, #routines - 1 )
end end
function waitForAll( ... ) function waitForAll( ... )
local routines = { create( ... ) } local routines = create( ... )
runUntilLimit( routines, 0 ) runUntilLimit( routines, 0 )
end end

View File

@ -18,7 +18,7 @@ end
function isPresent( _sSide ) function isPresent( _sSide )
if type( _sSide ) ~= "string" then if type( _sSide ) ~= "string" then
error( "Expected string", 2 ) error( "bad argument #1 (expected string, got " .. type( _sSide ) .. ")", 2 )
end end
if native.isPresent( _sSide ) then if native.isPresent( _sSide ) then
return true return true
@ -35,7 +35,7 @@ end
function getType( _sSide ) function getType( _sSide )
if type( _sSide ) ~= "string" then if type( _sSide ) ~= "string" then
error( "Expected string", 2 ) error( "bad argument #1 (expected string, got " .. type( _sSide ) .. ")", 2 )
end end
if native.isPresent( _sSide ) then if native.isPresent( _sSide ) then
return native.getType( _sSide ) return native.getType( _sSide )
@ -52,7 +52,7 @@ end
function getMethods( _sSide ) function getMethods( _sSide )
if type( _sSide ) ~= "string" then if type( _sSide ) ~= "string" then
error( "Expected string", 2 ) error( "bad argument #1 (expected string, got " .. type( _sSide ) .. ")", 2 )
end end
if native.isPresent( _sSide ) then if native.isPresent( _sSide ) then
return native.getMethods( _sSide ) return native.getMethods( _sSide )
@ -68,8 +68,11 @@ function getMethods( _sSide )
end end
function call( _sSide, _sMethod, ... ) function call( _sSide, _sMethod, ... )
if type( _sSide ) ~= "string" or type( _sMethod ) ~= "string" then if type( _sSide ) ~= "string" then
error( "Expected string, string", 2 ) error( "bad argument #1 (expected string, got " .. type( _sSide ) .. ")", 2 )
end
if type( _sSide ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( _sMethod ) .. ")", 2 )
end end
if native.isPresent( _sSide ) then if native.isPresent( _sSide ) then
return native.call( _sSide, _sMethod, ... ) return native.call( _sSide, _sMethod, ... )
@ -86,7 +89,7 @@ end
function wrap( _sSide ) function wrap( _sSide )
if type( _sSide ) ~= "string" then if type( _sSide ) ~= "string" then
error( "Expected string", 2 ) error( "bad argument #1 (expected string, got " .. type( _sSide ) .. ")", 2 )
end end
if peripheral.isPresent( _sSide ) then if peripheral.isPresent( _sSide ) then
local tMethods = peripheral.getMethods( _sSide ) local tMethods = peripheral.getMethods( _sSide )
@ -102,8 +105,11 @@ function wrap( _sSide )
end end
function find( sType, fnFilter ) function find( sType, fnFilter )
if type( sType ) ~= "string" or (fnFilter ~= nil and type( fnFilter ) ~= "function") then if type( sType ) ~= "string" then
error( "Expected string, [function]", 2 ) error( "bad argument #1 (expected string, got " .. type( sType ) .. ")", 2 )
end
if fnFilter ~= nil and type( fnFilter ) ~= "function" then
error( "bad argument #2 (expected function, got " .. type( fnFilter ) .. ")", 2 )
end end
local tResults = {} local tResults = {}
for n,sName in ipairs( peripheral.getNames() ) do for n,sName in ipairs( peripheral.getNames() ) do

View File

@ -233,7 +233,7 @@ function run()
if type( tMessage ) == "table" and tMessage.nMessageID then if type( tMessage ) == "table" and tMessage.nMessageID then
if not tReceivedMessages[ tMessage.nMessageID ] then if not tReceivedMessages[ tMessage.nMessageID ] then
tReceivedMessages[ tMessage.nMessageID ] = true tReceivedMessages[ tMessage.nMessageID ] = true
tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = nMessageID tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = tMessage.nMessageID
os.queueEvent( "rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol ) os.queueEvent( "rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol )
end end
end end

View File

@ -2,11 +2,13 @@
local tSettings = {} local tSettings = {}
function set( sName, value ) function set( sName, value )
if type(sName) ~= "string" or if type( sName ) ~= "string" then error( "bad argument #1 (expected string, got " .. type( sName ) .. ")", 2 ) end
(type(value) ~= "string" and type(value) ~= "number" and type(value) ~= "boolean" and type(value) ~= "table") then
error( "Expected string, value", 2 ) local sValueTy = type(value)
if sValueTy ~= "number" and sValueTy ~= "string" and sValueTy ~= "boolean" and sValueTy ~= "table" then
error( "bad argument #2 (expected value, got " .. sValueTy .. ")", 2 )
end end
if type(value) == "table" then if sValueTy == "table" then
-- Ensure value is serializeable -- Ensure value is serializeable
value = textutils.unserialize( textutils.serialize(value) ) value = textutils.unserialize( textutils.serialize(value) )
end end
@ -28,7 +30,7 @@ end
function get( sName, default ) function get( sName, default )
if type(sName) ~= "string" then if type(sName) ~= "string" then
error( "Expected string", 2 ) error( "bad argument #1 (expected string, got " .. type( sName ) .. ")", 2 )
end end
local result = tSettings[ sName ] local result = tSettings[ sName ]
if result ~= nil then if result ~= nil then
@ -40,7 +42,7 @@ end
function unset( sName ) function unset( sName )
if type(sName) ~= "string" then if type(sName) ~= "string" then
error( "Expected string", 2 ) error( "bad argument #1 (expected string, got " .. type( sName ) .. ")", 2 )
end end
tSettings[ sName ] = nil tSettings[ sName ] = nil
end end
@ -54,12 +56,13 @@ function getNames()
for k,v in pairs( tSettings ) do for k,v in pairs( tSettings ) do
result[ #result + 1 ] = k result[ #result + 1 ] = k
end end
table.sort(result)
return result return result
end end
function load( sPath ) function load( sPath )
if type(sPath) ~= "string" then if type(sPath) ~= "string" then
error( "Expected string", 2 ) error( "bad argument #1 (expected string, got " .. type( sPath ) .. ")", 2 )
end end
local file = fs.open( sPath, "r" ) local file = fs.open( sPath, "r" )
if not file then if not file then
@ -86,7 +89,7 @@ end
function save( sPath ) function save( sPath )
if type(sPath) ~= "string" then if type(sPath) ~= "string" then
error( "Expected string", 2 ) error( "bad argument #1 (expected string, got " .. type( sPath ) .. ")", 2 )
end end
local file = fs.open( sPath, "w" ) local file = fs.open( sPath, "w" )
if not file then if not file then

View File

@ -7,7 +7,7 @@ function slowWrite( sText, nRate )
local nSleep = 1 / nRate local nSleep = 1 / nRate
sText = tostring( sText ) sText = tostring( sText )
local x,y = term.getCursorPos(x,y) local x,y = term.getCursorPos()
local len = string.len( sText ) local len = string.len( sText )
for n=1,len do for n=1,len do
@ -25,6 +25,12 @@ function slowPrint( sText, nRate )
end end
function formatTime( nTime, bTwentyFourHour ) function formatTime( nTime, bTwentyFourHour )
if type( nTime ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( nTime ) .. ")", 2 )
end
if bTwentyFourHour ~= nil and type( bTwentyFourHour ) ~= "boolean" then
error( "bad argument #2 (expected boolean, got " .. type( bTwentyFourHour ) .. ")", 2 )
end
local sTOD = nil local sTOD = nil
if not bTwentyFourHour then if not bTwentyFourHour then
if nTime >= 12 then if nTime >= 12 then
@ -68,6 +74,9 @@ local function makePagedScroll( _term, _nFreeLines )
end end
function pagedPrint( _sText, _nFreeLines ) function pagedPrint( _sText, _nFreeLines )
if _nFreeLines ~= nil and type( _nFreeLines ) ~= "number" then
error( "bad argument #2 (expected number, got " .. type( _nFreeLines ) .. ")", 2 )
end
-- Setup a redirector -- Setup a redirector
local oldTerm = term.current() local oldTerm = term.current()
local newTerm = {} local newTerm = {}
@ -99,6 +108,11 @@ end
local function tabulateCommon( bPaged, ... ) local function tabulateCommon( bPaged, ... )
local tAll = { ... } local tAll = { ... }
for k,v in ipairs( tAll ) do
if type( v ) ~= "number" and type( v ) ~= "table" then
error( "bad argument #"..k.." (expected number/table, got " .. type( v ) .. ")", 3 )
end
end
local w,h = term.getSize() local w,h = term.getSize()
local nMaxLen = w / 8 local nMaxLen = w / 8
@ -301,6 +315,9 @@ function serialize( t )
end end
function unserialize( s ) function unserialize( s )
if type( s ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( s ) .. ")", 2 )
end
local func = load( "return "..s, "unserialize", "t", {} ) local func = load( "return "..s, "unserialize", "t", {} )
if func then if func then
local ok, result = pcall( func ) local ok, result = pcall( func )
@ -312,11 +329,20 @@ function unserialize( s )
end end
function serializeJSON( t, bNBTStyle ) function serializeJSON( t, bNBTStyle )
if type( t ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( t ) .. ")", 2 )
end
if bNBTStyle ~= nil and type( bNBTStyle ) ~= "boolean" then
error( "bad argument #2 (expected boolean, got " .. type( bNBTStyle ) .. ")", 2 )
end
local tTracking = {} local tTracking = {}
return serializeJSONImpl( t, tTracking, bNBTStyle or false ) return serializeJSONImpl( t, tTracking, bNBTStyle or false )
end end
function urlEncode( str ) function urlEncode( str )
if type( str ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( str ) .. ")", 2 )
end
if str then if str then
str = string.gsub(str, "\n", "\r\n") str = string.gsub(str, "\n", "\r\n")
str = string.gsub(str, "([^A-Za-z0-9 %-%_%.])", function(c) str = string.gsub(str, "([^A-Za-z0-9 %-%_%.])", function(c)
@ -338,6 +364,14 @@ end
local tEmpty = {} local tEmpty = {}
function complete( sSearchText, tSearchTable ) function complete( sSearchText, tSearchTable )
if type( sSearchText ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( sSearchText ) .. ")", 2 )
end
if type( tSearchTable ) ~= "table" then
error( "bad argument #2 (expected table, got " .. type( tSearchTable ) .. ")", 2 )
end
if g_tLuaKeywords[sSearchText] then return tEmpty end
local nStart = 1 local nStart = 1
local nDot = string.find( sSearchText, ".", nStart, true ) local nDot = string.find( sSearchText, ".", nStart, true )
local tTable = tSearchTable or _ENV local tTable = tSearchTable or _ENV
@ -352,8 +386,19 @@ function complete( sSearchText, tSearchTable )
return tEmpty return tEmpty
end end
end end
local nColon = string.find( sSearchText, ":", nStart, true )
if nColon then
local sPart = string.sub( sSearchText, nStart, nColon - 1 )
local value = tTable[ sPart ]
if type( value ) == "table" then
tTable = value
nStart = nColon + 1
else
return tEmpty
end
end
local sPart = string.sub( sSearchText, nStart, nDot ) local sPart = string.sub( sSearchText, nStart )
local nPartLength = string.len( sPart ) local nPartLength = string.len( sPart )
local tResults = {} local tResults = {}

View File

@ -18,20 +18,18 @@ local tHex = {
[ colors.black ] = "f", [ colors.black ] = "f",
} }
local type = type
local string_rep = string.rep local string_rep = string.rep
local string_sub = string.sub local string_sub = string.sub
local table_unpack = table.unpack local table_unpack = table.unpack
function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
if type( parent ) ~= "table" then error( "bad argument #1 (expected table, got " .. type( parent ) .. ")", 2 ) end
if type( parent ) ~= "table" or if type( nX ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( nX ) .. ")", 2 ) end
type( nX ) ~= "number" or if type( nY ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( nY ) .. ")", 2 ) end
type( nY ) ~= "number" or if type( nWidth ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( nWidth ) .. ")", 2 ) end
type( nWidth ) ~= "number" or if type( nHeight ) ~= "number" then error( "bad argument #5 (expected number, got " .. type( nHeight ) .. ")", 2 ) end
type( nHeight ) ~= "number" or if bStartVisible ~= nil and type( bStartVisible ) ~= "boolean" then error( "bad argument #6 (expected boolean, got " .. type( bStartVisible ) .. ")", 2 ) end
(bStartVisible ~= nil and type( bStartVisible ) ~= "boolean") then
error( "Expected object, number, number, number, number, [boolean]", 2 )
end
if parent == term then if parent == term then
error( "term is not a recommended window parent, try term.current() instead", 2 ) error( "term is not a recommended window parent, try term.current() instead", 2 )
@ -193,9 +191,9 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end end
function window.blit( sText, sTextColor, sBackgroundColor ) function window.blit( sText, sTextColor, sBackgroundColor )
if type(sText) ~= "string" or type(sTextColor) ~= "string" or type(sBackgroundColor) ~= "string" then if type( sText ) ~= "string" then error( "bad argument #1 (expected string, got " .. type( sText ) .. ")", 2 ) end
error( "Expected string, string, string", 2 ) if type( sTextColor ) ~= "string" then error( "bad argument #2 (expected string, got " .. type( sTextColor ) .. ")", 2 ) end
end if type( sBackgroundColor ) ~= "string" then error( "bad argument #3 (expected string, got " .. type( sBackgroundColor ) .. ")", 2 ) end
if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then
error( "Arguments must be the same length", 2 ) error( "Arguments must be the same length", 2 )
end end
@ -243,9 +241,8 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end end
function window.setCursorPos( x, y ) function window.setCursorPos( x, y )
if type( x ) ~= "number" or type( y ) ~= "number" then if type( x ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( x ) .. ")", 2 ) end
error( "Expected number, number", 2 ) if type( y ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( y ) .. ")", 2 ) end
end
nCursorX = math.floor( x ) nCursorX = math.floor( x )
nCursorY = math.floor( y ) nCursorY = math.floor( y )
if bVisible then if bVisible then
@ -254,9 +251,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end end
function window.setCursorBlink( blink ) function window.setCursorBlink( blink )
if type( blink ) ~= "boolean" then if type( blink ) ~= "boolean" then error( "bad argument #1 (expected boolean, got " .. type( blink ) .. ")", 2 ) end
error( "Expected boolean", 2 )
end
bCursorBlink = blink bCursorBlink = blink
if bVisible then if bVisible then
updateCursorBlink() updateCursorBlink()
@ -276,10 +271,10 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end end
local function setTextColor( color ) local function setTextColor( color )
if type(color) ~= "number" then if type( color ) ~= "number" then
error( "Expected number", 3 ) error( "bad argument #1 (expected number, got " .. type( color ) .. ")", 2 )
elseif tHex[color] == nil then elseif tHex[color] == nil then
error( "Invalid color", 3 ) error( "Invalid color", 2 )
end end
nTextColor = color nTextColor = color
if bVisible then if bVisible then
@ -287,26 +282,25 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end end
end end
function window.setTextColor( color ) window.setTextColor = setTextColor
setTextColor( color ) window.setTextColour = setTextColor
end
function window.setTextColour( color )
setTextColor( color )
end
function window.setPaletteColour( colour, r, g, b ) function window.setPaletteColour( colour, r, g, b )
if type( colour ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( colour ) .. ")", 2 ) end
local tCol local tCol
if type(colour) == "number" and type(r) == "number" and g == nil and b == nil then if type(r) == "number" and g == nil and b == nil then
tCol = { colours.rgb8( r ) } tCol = { colours.rgb8( r ) }
tPalette[ colour ] = tCol tPalette[ colour ] = tCol
elseif type(colour) == "number" and type(r) == "number" and type(g) == "number" and type(b) == "number" then else
if type( r ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( r ) .. ")", 2 ) end
if type( g ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( g ) .. ")", 2 ) end
if type( b ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( b ) .. ")", 2 ) end
tCol = tPalette[ colour ] tCol = tPalette[ colour ]
tCol[1] = r tCol[1] = r
tCol[2] = g tCol[2] = g
tCol[3] = b tCol[3] = b
else
error( "Expected number, number, number, number", 2 )
end end
if bVisible then if bVisible then
@ -324,30 +318,23 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
window.getPaletteColor = window.getPaletteColour window.getPaletteColor = window.getPaletteColour
local function setBackgroundColor( color ) local function setBackgroundColor( color )
if type(color) ~= "number" then if type( color ) ~= "number" then
error( "Expected number", 3 ) error( "bad argument #1 (expected number, got " .. type( color ) .. ")", 2 )
elseif tHex[color] == nil then elseif tHex[color] == nil then
error( "Invalid color", 3 ) error( "Invalid color", 3 )
end end
nBackgroundColor = color nBackgroundColor = color
end end
function window.setBackgroundColor( color ) window.setBackgroundColor = setBackgroundColor
setBackgroundColor( color ) window.setBackgroundColour = setBackgroundColor
end
function window.setBackgroundColour( color )
setBackgroundColor( color )
end
function window.getSize() function window.getSize()
return nWidth, nHeight return nWidth, nHeight
end end
function window.scroll( n ) function window.scroll( n )
if type( n ) ~= "number" then if type( n ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( n ) .. ")", 2 ) end
error( "Expected number", 2 )
end
if n ~= 0 then if n ~= 0 then
local tNewLines = {} local tNewLines = {}
local sEmptyText = sEmptySpaceLine local sEmptyText = sEmptySpaceLine
@ -392,9 +379,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
-- Other functions -- Other functions
function window.setVisible( bVis ) function window.setVisible( bVis )
if type( bVis) ~= "boolean" then if type( bVis ) ~= "boolean" then error( "bad argument #1 (expected boolean, got " .. type( bVis ) .. ")", 2 ) end
error( "Expected boolean", 2 )
end
if bVisible ~= bVis then if bVisible ~= bVis then
bVisible = bVis bVisible = bVis
if bVisible then if bVisible then
@ -426,9 +411,11 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end end
function window.reposition( nNewX, nNewY, nNewWidth, nNewHeight ) function window.reposition( nNewX, nNewY, nNewWidth, nNewHeight )
if type( nNewX ) ~= "number" or type( nNewY ) ~= "number" or type( nNewWidth ) ~= "number" or type( nNewWidth ) ~= "number" then if type( nNewX ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( nNewX ) .. ")", 2 ) end
error( "Expected number, number, number, number", 2 ) if type( nNewY ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( nNewY ) .. ")", 2 ) end
end if type( nNewWidth ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( nNewWidth ) .. ")", 2 ) end
if type( nNewHeight ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( nNewHeight ) .. ")", 2 ) end
nX = nNewX nX = nNewX
nY = nNewY nY = nNewY
if nNewWidth and nNewHeight then if nNewWidth and nNewHeight then

View File

@ -1,4 +1,5 @@
equip is a program for Turtles. equip will equip an item from the Turtle's inventory for use as a tool of peripheral. equip is a program for Turtles and Pocket Computer. equip will equip an item from the Turtle's inventory for use as a tool of peripheral. On a Pocket Computer you don't need to write a side.
ex: ex:
"equip 5 left" will equip the item from slot 5 of the turtle onto the left side of the turtle "equip 5 left" will equip the item from slot 5 of the turtle onto the left side of the turtle
"equip" on a Pocket Computer will equip the first item from your inventory.

View File

@ -1,5 +1,6 @@
Functions in the HTTP API: Functions in the HTTP API:
http.checkURL( url ) http.checkURL( url )
http.checkURLAsync( url )
http.request( url, [postData], [headers] ) http.request( url, [postData], [headers] )
http.get( url, [headers] ) http.get( url, [headers] )
http.post( url, postData, [headers] ) http.post( url, postData, [headers] )

View File

@ -1,4 +1,5 @@
unequip is a program for Turtles. unequip will remove tools of peripherals from the specified side of the turtle. unequip is a program for Turtles and Pocket Computers. unequip will remove tools of peripherals from the specified side of the turtle. On a Pocket Computer you don't need to write a side.
ex: ex:
"unequip left" will remove the item on the left side of the turtle "unequip left" will remove the item on the left side of the turtle
"unequip" on a Pocket Computer will remove the item from the Pocket Computer

View File

@ -171,6 +171,9 @@ function multishell.getFocus()
end end
function multishell.setFocus( n ) function multishell.setFocus( n )
if type( n ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( n ) .. ")", 2 )
end
if n >= 1 and n <= #tProcesses then if n >= 1 and n <= #tProcesses then
selectProcess( n ) selectProcess( n )
redrawMenu() redrawMenu()
@ -180,6 +183,9 @@ function multishell.setFocus( n )
end end
function multishell.getTitle( n ) function multishell.getTitle( n )
if type( n ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( n ) .. ")", 2 )
end
if n >= 1 and n <= #tProcesses then if n >= 1 and n <= #tProcesses then
return tProcesses[n].sTitle return tProcesses[n].sTitle
end end
@ -187,6 +193,12 @@ function multishell.getTitle( n )
end end
function multishell.setTitle( n, sTitle ) function multishell.setTitle( n, sTitle )
if type( n ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( n ) .. ")", 2 )
end
if type( sTitle ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( sTitle ) .. ")", 2 )
end
if n >= 1 and n <= #tProcesses then if n >= 1 and n <= #tProcesses then
setProcessTitle( n, sTitle ) setProcessTitle( n, sTitle )
redrawMenu() redrawMenu()
@ -198,6 +210,12 @@ function multishell.getCurrent()
end end
function multishell.launch( tProgramEnv, sProgramPath, ... ) function multishell.launch( tProgramEnv, sProgramPath, ... )
if type( tProgramArgs ) ~= "table" then
error( "bad argument #1 (expected table, got " .. type( tProgramEnv ) .. ")", 2 )
end
if type( sProgramPath ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( sProgramPath ) .. ")", 2 )
end
local previousTerm = term.current() local previousTerm = term.current()
setMenuVisible( (#tProcesses + 1) >= 2 ) setMenuVisible( (#tProcesses + 1) >= 2 )
local nResult = launchProcess( tProgramEnv, sProgramPath, ... ) local nResult = launchProcess( tProgramEnv, sProgramPath, ... )

View File

@ -175,7 +175,7 @@ local nCompletion
local tCompleteEnv = _ENV local tCompleteEnv = _ENV
local function complete( sLine ) local function complete( sLine )
if settings.get( "edit.autocomplete" ) then if settings.get( "edit.autocomplete" ) then
local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.]+$" ) local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.:]+$" )
if nStartPos then if nStartPos then
sLine = string.sub( sLine, nStartPos ) sLine = string.sub( sLine, nStartPos )
end end
@ -709,7 +709,13 @@ while bRunning do
end end
elseif sEvent == "paste" then elseif sEvent == "paste" then
if not bMenu and not bReadOnly then if not bReadOnly then
-- Close menu if open
if bMenu then
bMenu = false
term.setCursorBlink( true )
redrawMenu()
end
-- Input text -- Input text
local sLine = tLines[y] local sLine = tLines[y]
tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x) tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)

View File

@ -310,7 +310,7 @@ local function accessMenu()
-- Select an option -- Select an option
if mChoices[selection]=="Save" then if mChoices[selection]=="Save" then
if bReadOnly then if bReadOnly then
fMessage = "Access Denied" fMessage = "Access denied"
return false return false
end end
local success = save(sPath) local success = save(sPath)

View File

@ -57,10 +57,6 @@ elseif sCommand == "host" then
x,y,z = gps.locate( 2, true ) x,y,z = gps.locate( 2, true )
if x == nil then if x == nil then
print( "Run \"gps host <x> <y> <z>\" to set position manually" ) print( "Run \"gps host <x> <y> <z>\" to set position manually" )
if bCloseChannel then
print( "Closing GPS channel" )
modem.close( gps.CHANNEL_GPS )
end
return return
end end
end end

View File

@ -34,7 +34,7 @@ while bRunning do
local s = read( nil, tCommandHistory, function( sLine ) local s = read( nil, tCommandHistory, function( sLine )
if settings.get( "lua.autocomplete" ) then if settings.get( "lua.autocomplete" ) then
local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.]+$" ) local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.:]+$" )
if nStartPos then if nStartPos then
sLine = string.sub( sLine, nStartPos ) sLine = string.sub( sLine, nStartPos )
end end
@ -64,10 +64,10 @@ while bRunning do
end end
if func then if func then
local tResults = { pcall( func ) } local tResults = table.pack( pcall( func ) )
if tResults[1] then if tResults[1] then
local n = 1 local n = 1
while (tResults[n + 1] ~= nil) or (n <= nForcePrint) do while n < tResults.n or (n <= nForcePrint) do
local value = tResults[ n + 1 ] local value = tResults[ n + 1 ]
if type( value ) == "table" then if type( value ) == "table" then
local metatable = getmetatable( value ) local metatable = getmetatable( value )

View File

@ -0,0 +1,6 @@
local ok, err = pcall( pocket.equipBack )
if not ok then
printError( "Nothing to equip" )
else
print( "Item equipped" )
end

View File

@ -0,0 +1,6 @@
local ok, err = pcall( pocket.unequipBack )
if not ok then
printError( "Nothing to unequip" )
else
print( "Item unequipped" )
end

View File

@ -310,14 +310,14 @@ elseif sCommand == "join" then
function printMessage( sMessage ) function printMessage( sMessage )
term.redirect( historyWindow ) term.redirect( historyWindow )
print() print()
if string.match( sMessage, "^\*" ) then if string.match( sMessage, "^%*" ) then
-- Information -- Information
term.setTextColour( highlightColour ) term.setTextColour( highlightColour )
write( sMessage ) write( sMessage )
term.setTextColour( textColour ) term.setTextColour( textColour )
else else
-- Chat -- Chat
local sUsernameBit = string.match( sMessage, "^\<[^\>]*\>" ) local sUsernameBit = string.match( sMessage, "^<[^>]*>" )
if sUsernameBit then if sUsernameBit then
term.setTextColour( highlightColour ) term.setTextColour( highlightColour )
write( sUsernameBit ) write( sUsernameBit )

View File

@ -2,9 +2,12 @@
local tArgs = { ... } local tArgs = { ... }
if #tArgs == 0 then if #tArgs == 0 then
-- "set" -- "set"
local x,y = term.getCursorPos()
local tSettings = {}
for n,sName in ipairs( settings.getNames() ) do for n,sName in ipairs( settings.getNames() ) do
print( textutils.serialize(sName) .. " is " .. textutils.serialize(settings.get(sName)) ) tSettings[n] = textutils.serialize(sName) .. " is " .. textutils.serialize(settings.get(sName))
end end
textutils.pagedPrint(table.concat(tSettings,"\n"),y-3)
elseif #tArgs == 1 then elseif #tArgs == 1 then
-- "set foo" -- "set foo"

View File

@ -31,6 +31,7 @@ local function createShellEnv( sDir )
table = table, table = table,
} }
package.path = "?;?.lua;?/init.lua" package.path = "?;?.lua;?/init.lua"
package.config = "/\n;\n?\n!\n-"
package.preload = {} package.preload = {}
package.loaders = { package.loaders = {
function( name ) function( name )
@ -68,6 +69,9 @@ local function createShellEnv( sDir )
local sentinel = {} local sentinel = {}
local function require( name ) local function require( name )
if type( name ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( name ) .. ")", 2 )
end
if package.loaded[name] == sentinel then if package.loaded[name] == sentinel then
error("Loop detected requiring '" .. name .. "'", 0) error("Loop detected requiring '" .. name .. "'", 0)
end end
@ -181,6 +185,12 @@ function shell.dir()
end end
function shell.setDir( _sDir ) function shell.setDir( _sDir )
if type( _sDir ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sDir ) .. ")", 2 )
end
if not fs.isDir( _sDir ) then
error( "Not a directory", 2 )
end
sDir = _sDir sDir = _sDir
end end
@ -189,10 +199,16 @@ function shell.path()
end end
function shell.setPath( _sPath ) function shell.setPath( _sPath )
if type( _sPath ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sPath ) .. ")", 2 )
end
sPath = _sPath sPath = _sPath
end end
function shell.resolve( _sPath ) function shell.resolve( _sPath )
if type( _sPath ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sPath ) .. ")", 2 )
end
local sStartChar = string.sub( _sPath, 1, 1 ) local sStartChar = string.sub( _sPath, 1, 1 )
if sStartChar == "/" or sStartChar == "\\" then if sStartChar == "/" or sStartChar == "\\" then
return fs.combine( "", _sPath ) return fs.combine( "", _sPath )
@ -212,6 +228,9 @@ local function pathWithExtension( _sPath, _sExt )
end end
function shell.resolveProgram( _sCommand ) function shell.resolveProgram( _sCommand )
if type( _sCommand ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sCommand ) .. ")", 2 )
end
-- Substitute aliases firsts -- Substitute aliases firsts
if tAliases[ _sCommand ] ~= nil then if tAliases[ _sCommand ] ~= nil then
_sCommand = tAliases[ _sCommand ] _sCommand = tAliases[ _sCommand ]
@ -327,6 +346,9 @@ local function completeProgramArgument( sProgram, nArgument, sPart, tPreviousPar
end end
function shell.complete( sLine ) function shell.complete( sLine )
if type( sLine ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( sLine ) .. ")", 2 )
end
if #sLine > 0 then if #sLine > 0 then
local tWords = tokenise( sLine ) local tWords = tokenise( sLine )
local nIndex = #tWords local nIndex = #tWords
@ -363,10 +385,19 @@ function shell.complete( sLine )
end end
function shell.completeProgram( sProgram ) function shell.completeProgram( sProgram )
if type( sProgram ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( sProgram ) .. ")", 2 )
end
return completeProgram( sProgram ) return completeProgram( sProgram )
end end
function shell.setCompletionFunction( sProgram, fnComplete ) function shell.setCompletionFunction( sProgram, fnComplete )
if type( sProgram ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( sProgram ) .. ")", 2 )
end
if type( fnComplete ) ~= "function" then
error( "bad argument #2 (expected function, got " .. type( fnComplete ) .. ")", 2 )
end
tCompletionInfo[ sProgram ] = { tCompletionInfo[ sProgram ] = {
fnComplete = fnComplete fnComplete = fnComplete
} }
@ -384,10 +415,19 @@ function shell.getRunningProgram()
end end
function shell.setAlias( _sCommand, _sProgram ) function shell.setAlias( _sCommand, _sProgram )
if type( _sCommand ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sCommand ) .. ")", 2 )
end
if type( _sProgram ) ~= "string" then
error( "bad argument #2 (expected string, got " .. type( _sProgram ) .. ")", 2 )
end
tAliases[ _sCommand ] = _sProgram tAliases[ _sCommand ] = _sProgram
end end
function shell.clearAlias( _sCommand ) function shell.clearAlias( _sCommand )
if type( _sCommand ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( _sCommand ) .. ")", 2 )
end
tAliases[ _sCommand ] = nil tAliases[ _sCommand ] = nil
end end
@ -417,6 +457,9 @@ if multishell then
end end
function shell.switchTab( nID ) function shell.switchTab( nID )
if type( nID ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( nID ) .. ")", 2 )
end
multishell.setFocus( nID ) multishell.setFocus( nID )
end end
end end

View File

@ -168,6 +168,15 @@ local function completeSet( shell, nIndex, sText, tPreviousText )
return completeMultipleChoice( sText, settings.getNames(), true ) return completeMultipleChoice( sText, settings.getNames(), true )
end end
end end
local tCommands
if commands then
tCommands = commands.list()
end
local function completeExec( shell, nIndex, sText, tPreviousText )
if nIndex == 1 and commands then
return completeMultipleChoice( sText, tCommands, true )
end
end
shell.setCompletionFunction( "rom/programs/alias.lua", completeAlias ) shell.setCompletionFunction( "rom/programs/alias.lua", completeAlias )
shell.setCompletionFunction( "rom/programs/cd.lua", completeDir ) shell.setCompletionFunction( "rom/programs/cd.lua", completeDir )
shell.setCompletionFunction( "rom/programs/copy.lua", completeEitherEither ) shell.setCompletionFunction( "rom/programs/copy.lua", completeEitherEither )
@ -194,6 +203,36 @@ shell.setCompletionFunction( "rom/programs/fun/dj.lua", completeDJ )
shell.setCompletionFunction( "rom/programs/fun/advanced/paint.lua", completeFile ) shell.setCompletionFunction( "rom/programs/fun/advanced/paint.lua", completeFile )
shell.setCompletionFunction( "rom/programs/http/pastebin.lua", completePastebin ) shell.setCompletionFunction( "rom/programs/http/pastebin.lua", completePastebin )
shell.setCompletionFunction( "rom/programs/rednet/chat.lua", completeChat ) shell.setCompletionFunction( "rom/programs/rednet/chat.lua", completeChat )
shell.setCompletionFunction( "rom/programs/command/exec.lua", completeExec )
if turtle then
local tGoOptions = { "left", "right", "forward", "back", "down", "up" }
local function completeGo( shell, nIndex, sText )
return completeMultipleChoice(sText,tGoOptions)
end
local tTurnOptions = { "left", "right" }
local function completeTurn( shell, nIndex, sText )
if nIndex == 1 then
return completeMultipleChoice( sText, tTurnOptions )
end
end
local tEquipOptions = { "left", "right" }
local function completeEquip( shell, nIndex, sText )
if nIndex == 2 then
return completeMultipleChoice( sText, tEquipOptions )
end
end
local function completeUnequip( shell, nIndex, sText )
if nIndex == 1 then
return completeMultipleChoice( sText, tEquipOptions )
end
end
shell.setCompletionFunction( "rom/programs/turtle/go.lua", completeGo )
shell.setCompletionFunction( "rom/programs/turtle/turn.lua", completeTurn )
shell.setCompletionFunction( "rom/programs/turtle/equip.lua", completeEquip )
shell.setCompletionFunction( "rom/programs/turtle/unequip.lua", completeUnequip )
end
-- Run autorun files -- Run autorun files
if fs.exists( "/rom/autorun" ) and fs.isDir( "/rom/autorun" ) then if fs.exists( "/rom/autorun" ) and fs.isDir( "/rom/autorun" ) then