1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-07-07 04:14:26 +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
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
or misuse of this mad fall on the user.
or misuse of this mod fall on the user.
3. Play rights
--------------

View File

@ -31,7 +31,7 @@
version = "1.9.4-12.17.0.1959"
runDir = "run"
replace '${version}', project.version
// the mappings can be changed at any time, and must be in the following format.
// snapshot_YYYYMMDD snapshot are built nightly.
// stable_# stables are built at the discretion of the MCP team.
@ -46,7 +46,7 @@
// or you may define them like so..
//compile "some.group:artifact:version:classifier"
//compile "some.group:artifact:version"
// real examples
//compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
//compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
@ -78,23 +78,25 @@
inputs.property "mcversion", project.minecraft.version
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']
Set<String> contributors = []
grgit.log().each {
if (!blacklist.contains(it.committer.name))
contributors.add(it.committer.name)
if (!blacklist.contains(it.author.name)) contributors.add(it.author.name)
if (!blacklist.contains(it.committer.name)) contributors.add(it.committer.name)
}
from(sourceSets.main.resources.srcDirs) {
include 'mcmod.info'
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) {
exclude 'mcmod.info'
exclude 'assets/computercraft/lua/rom/help/credits.txt'

View File

@ -18,6 +18,7 @@
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.core.apis.AddressPredicate;
import dan200.computercraft.core.filesystem.ComboMount;
import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.JarMount;
@ -60,6 +61,7 @@
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import net.minecraftforge.fml.common.FMLCommonHandler;
@ -105,8 +107,18 @@ public class ComputerCraft
public static final int pocketComputerGUIID = 106;
// 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 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 String default_computer_settings = "";
public static boolean logPeripheralErrors = false;
@ -185,6 +197,7 @@ public static class Config {
public static Property http_enable;
public static Property http_whitelist;
public static Property http_blacklist;
public static Property disable_lua51_features;
public static Property default_computer_settings;
public static Property logPeripheralErrors;
@ -252,10 +265,28 @@ public void preInit( FMLPreInitializationEvent event )
Config.config.load();
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.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 void preInit( FMLPreInitializationEvent event )
public static void syncConfig() {
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();
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 javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.getInt;
// Contributed by Nia
// Based on LuaBit (http://luaforge.net/projects/bit)
@ -23,25 +25,6 @@ public class BitAPI implements ILuaAPI
private static final int BRSHIFT = 4;
private static final int BLSHIFT = 5;
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 )
{
@ -82,31 +65,28 @@ public String[] getMethodNames() {
@Override
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;
switch(method) {
case BNOT:
ret = ~checkInt(a, 1);
ret = ~getInt( args, 0 );
break;
case BAND:
ret = checkInt(a, 2) & checkInt(b, 2);
ret = getInt( args, 0 ) & getInt( args, 1 );
break;
case BOR:
ret = checkInt(a, 2) | checkInt(b, 2);
ret = getInt( args, 0 ) | getInt( args, 1 );
break;
case BXOR:
ret = checkInt(a, 2) ^ checkInt(b, 2);
ret = getInt( args, 0 ) ^ getInt( args, 1 );
break;
case BRSHIFT:
ret = checkInt(a, 2) >> checkInt(b, 2);
ret = getInt( args, 0 ) >> getInt( args, 1 );
break;
case BLSHIFT:
ret = checkInt(a, 2) << checkInt(b, 2);
ret = getInt( args, 0 ) << getInt( args, 1 );
break;
case BLOGIC_RSHIFT:
ret = checkInt(a, 2) >>> checkInt(b, 2);
ret = getInt( args, 0 ) >>> getInt( args, 1 );
break;
}

View File

@ -13,6 +13,9 @@
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
{
private static class BufferLuaObject implements ILuaObject
@ -55,81 +58,25 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 2:
{
// read
int start = 0;
if( arguments.length >= 1 && (arguments[0] != null) )
{
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();
}
int start = optInt( arguments, 0, 0 );
int end = optInt( arguments, 1, m_buffer.length() );
return new Object[] { m_buffer.read( start, end ) };
}
case 3:
{
// write
if( arguments.length < 1 || !(arguments[0] instanceof String) )
{
throw new LuaException( "Expected string" );
}
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();
}
String text = getString( arguments, 0 );
int start = optInt( arguments, 1, 0 );
int end = optInt( arguments, 2, start + text.length() );
m_buffer.write( text, start, end );
return null;
}
case 4:
{
// fill
if( arguments.length < 1 || !(arguments[0] instanceof String) )
{
throw new LuaException( "Expected string" );
}
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();
}
String text = getString( arguments, 0 );
int start = optInt( arguments, 1, 0 );
int end = optInt( arguments, 2, m_buffer.length() );
m_buffer.fill( text, start, end );
return null;
}
@ -184,23 +131,11 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
{
case 0:
{
if( arguments.length < 1 || !(arguments[0] instanceof String) )
String text = getString( arguments, 0 );
int repetitions = optInt( arguments, 1, 1 );
if( repetitions < 0 )
{
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 )
{
throw new LuaException( "Expected positive number" );
}
throw ArgumentHelper.badArgument( 1, "positive number", Integer.toString( repetitions ) );
}
TextBuffer buffer = new TextBuffer( text, repetitions );
return new Object[] { new BufferLuaObject( buffer ) };

View File

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

View File

@ -7,24 +7,26 @@
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.handles.BinaryInputHandle;
import dan200.computercraft.core.apis.handles.EncodedInputHandle;
import dan200.computercraft.core.apis.http.HTTPCheck;
import dan200.computercraft.core.apis.http.HTTPRequest;
import dan200.computercraft.core.apis.http.HTTPTask;
import javax.annotation.Nonnull;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class HTTPAPI implements ILuaAPI
{
private final IAPIEnvironment m_apiEnvironment;
private final List<HTTPRequest> m_httpRequests;
private final List<HTTPTask> m_httpTasks;
public HTTPAPI( IAPIEnvironment environment )
{
m_apiEnvironment = environment;
m_httpRequests = new ArrayList<HTTPRequest>();
m_httpTasks = new ArrayList<HTTPTask>();
}
@Override
@ -44,95 +46,31 @@ public void startup( )
public void advance( double _dt )
{
// Wait for all of our http requests
synchronized( m_httpRequests )
synchronized( m_httpTasks )
{
Iterator<HTTPRequest> it = m_httpRequests.iterator();
while( it.hasNext() ) {
final HTTPRequest h = it.next();
if( h.isComplete() ) {
final String url = h.getURL();
if( h.wasSuccessful() ) {
// Queue the "http_success" event
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 } );
}
Iterator<HTTPTask> it = m_httpTasks.iterator();
while( it.hasNext() )
{
final HTTPTask h = it.next();
if( h.isFinished() )
{
h.whenFinished( m_apiEnvironment );
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
public void shutdown( )
{
synchronized( m_httpRequests )
synchronized( m_httpTasks )
{
for( HTTPRequest r : m_httpRequests )
for( HTTPTask r : m_httpTasks )
{
r.cancel();
}
m_httpRequests.clear();
m_httpTasks.clear();
}
}
@ -155,24 +93,16 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
{
// request
// Get URL
if( args.length < 1 || !(args[0] instanceof String) )
{
throw new LuaException( "Expected string" );
}
String urlString = args[0].toString();
String urlString = getString( args, 0 );
// Get POST
String postString = null;
if( args.length >= 2 && args[1] instanceof String )
{
postString = args[1].toString();
}
String postString = optString( args, 1, null );
// Get Headers
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() );
for( Object key : table.keySet() )
{
@ -194,10 +124,11 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
// Make the request
try
{
HTTPRequest request = new HTTPRequest( urlString, postString, headers, binary );
synchronized( m_httpRequests )
URL url = HTTPRequest.checkURL( urlString );
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 };
}
@ -210,16 +141,16 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
{
// checkURL
// Get URL
if( args.length < 1 || !(args[0] instanceof String) )
{
throw new LuaException( "Expected string" );
}
String urlString = args[0].toString();
String urlString = getString( args, 0 );
// Check URL
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 };
}
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 javax.annotation.Nonnull;
import java.util.*;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class OSAPI implements ILuaAPI
{
private IAPIEnvironment m_apiEnvironment;
@ -225,21 +227,13 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 0:
{
// queueEvent
if( args.length == 0 || args[0] == null || !(args[0] instanceof String) )
{
throw new LuaException( "Expected string" );
}
queueLuaEvent( (String)args[0], trimArray( args, 1 ) );
queueLuaEvent( getString( args, 0 ), trimArray( args, 1 ) );
return null;
}
case 1:
{
// startTimer
if( args.length < 1 || args[0] == null || !(args[0] instanceof Number) )
{
throw new LuaException( "Expected number" );
}
double timer = ((Number)args[0]).doubleValue();
double timer = getReal( args, 0 );
synchronized( m_timers )
{
m_timers.put( m_nextTimerToken, new Timer( (int)Math.round( timer / 0.05 ) ) );
@ -249,11 +243,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 2:
{
// setAlarm
if( args.length < 1 || args[0] == null || !(args[0] instanceof Number) )
{
throw new LuaException( "Expected number" );
}
double time = ((Number)args[0]).doubleValue();
double time = getReal( args, 0 );
if( time < 0.0 || time >= 24.0 )
{
throw new LuaException( "Number out of range" );
@ -286,16 +276,8 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 7:
{
// setComputerLabel
String label = null;
if( args.length > 0 && args[0] != null )
{
if(!(args[0] instanceof String))
{
throw new LuaException( "Expected string or nil" );
}
label = StringUtil.normaliseLabel( (String) args[0] );
}
m_apiEnvironment.setLabel( label );
String label = optString( args, 0, null );
m_apiEnvironment.setLabel( StringUtil.normaliseLabel( label ) );
return null;
}
case 8:
@ -320,13 +302,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 11:
{
// time
String param = "ingame";
if (args.length > 0 && args[0] != null) {
if (!(args[0] instanceof String)) {
throw new LuaException("Expected string");
}
param = (String)args[0];
}
String param = optString( args, 0, "ingame" );
if (param.equals("utc")) {
// Get Hour of day (UTC)
@ -351,13 +327,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 12:
{
// day
String param = "ingame";
if (args.length > 0 && args[0] != null) {
if (!(args[0] instanceof String)) {
throw new LuaException("Expected string");
}
param = (String)args[0];
}
String param = optString( args, 0, "ingame" );
if (param.equals("utc")) {
// Get numbers of days since 1970-01-01 (utc)
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
@ -381,11 +351,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 13:
{
// cancelTimer
if( args.length < 1 || args[0] == null || !(args[0] instanceof Number) )
{
throw new LuaException( "Expected number" );
}
int token = ((Number)args[0]).intValue();
int token = getInt( args, 0 );
synchronized( m_timers )
{
if( m_timers.containsKey( token ) )
@ -398,11 +364,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 14:
{
// cancelAlarm
if( args.length < 1 || args[0] == null || !(args[0] instanceof Number) )
{
throw new LuaException( "Expected number" );
}
int token = ((Number)args[0]).intValue();
int token = getInt( args, 0 );
synchronized( m_alarms )
{
if( m_alarms.containsKey( token ) )
@ -415,13 +377,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 15:
{
// epoch
String param = "ingame";
if (args.length > 0 && args[0] != null) {
if (!(args[0] instanceof String)) {
throw new LuaException("Expected string");
}
param = (String)args[0];
}
String param = optString( args, 0, "ingame" );
if (param.equals("utc")) {
// Get utc epoch
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.apis.ArgumentHelper;
import dan200.computercraft.shared.util.StringUtil;
import javax.annotation.Nonnull;
@ -51,7 +52,7 @@ else if( args.length > 0 && args[ 0 ] instanceof String )
}
else
{
throw new LuaException( "Expected number" );
throw ArgumentHelper.badArgument( 0, "string or number", args.length > 0 ? args[ 0 ] : 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 void makeDirectory( String path ) throws FileSystemException
{
if( m_writableMount == null )
{
throw new FileSystemException( "Access Denied" );
throw new FileSystemException( "Access denied" );
}
try
{
@ -199,7 +199,7 @@ public void delete( String path ) throws FileSystemException
{
if( m_writableMount == null )
{
throw new FileSystemException( "Access Denied" );
throw new FileSystemException( "Access denied" );
}
try
{
@ -219,7 +219,7 @@ public OutputStream openForWrite( String path ) throws FileSystemException
{
if( m_writableMount == null )
{
throw new FileSystemException( "Access Denied" );
throw new FileSystemException( "Access denied" );
}
try
{
@ -251,7 +251,7 @@ public OutputStream openForAppend( String path ) throws FileSystemException
{
if( m_writableMount == null )
{
throw new FileSystemException( "Access Denied" );
throw new FileSystemException( "Access denied" );
}
try
{

View File

@ -28,6 +28,9 @@
import java.util.HashMap;
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
{
private TileCommandComputer m_computer;
@ -151,11 +154,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 0:
{
// exec
if( arguments.length < 1 || !(arguments[0] instanceof String) )
{
throw new LuaException( "Expected string" );
}
final String command = (String)arguments[0];
final String command = getString( arguments, 0 );
return context.executeMainThreadTask( new ILuaTask()
{
@Override
@ -168,11 +167,7 @@ public Object[] execute() throws LuaException
case 1:
{
// execAsync
if( arguments.length < 1 || !(arguments[0] instanceof String) )
{
throw new LuaException( "Expected string" );
}
final String command = (String)arguments[0];
final String command = getString( arguments, 0 );
long taskID = context.issueMainThreadTask( new ILuaTask()
{
@Override
@ -234,22 +229,12 @@ public Object[] execute() throws LuaException
case 4:
{
// getBlockInfos
if( arguments.length < 6 ||
!(arguments[0] instanceof Number) ||
!(arguments[1] instanceof Number) ||
!(arguments[2] instanceof Number) ||
!(arguments[3] instanceof Number) ||
!(arguments[4] instanceof Number) ||
!(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();
final int minx = getInt( arguments, 0 );
final int miny = getInt( arguments, 1 );
final int minz = getInt( arguments, 2 );
final int maxx = getInt( arguments, 3 );
final int maxy = getInt( arguments, 4 );
final int maxz = getInt( arguments, 5 );
return context.executeMainThreadTask( new ILuaTask()
{
@Override
@ -295,16 +280,9 @@ public Object[] execute() throws LuaException
case 5:
{
// getBlockInfo
if( arguments.length < 3 ||
!(arguments[0] instanceof Number) ||
!(arguments[1] instanceof Number) ||
!(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();
final int x = getInt( arguments, 0 );
final int y = getInt( arguments, 1 );
final int z = getInt( arguments, 2 );
return context.executeMainThreadTask( new ILuaTask()
{
@Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,6 +14,9 @@
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
{
private final TilePrinter m_printer;
@ -70,13 +73,8 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont
case 1:
{
// setCursorPos
if( args.length != 2 || args[0] == null || !(args[0] instanceof Number) || args[1] == null || !(args[1] instanceof Number) )
{
throw new LuaException( "Expected number, number" );
}
int x = ((Number)args[0]).intValue() - 1;
int y = ((Number)args[1]).intValue() - 1;
int x = getInt( args, 0 ) - 1;
int y = getInt( args, 1 ) - 1;
Terminal page = getCurrentPage();
page.setCursorPos( x, y );
return null;
@ -116,16 +114,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont
case 7:
{
// setPageTitle
String title = "";
if( args.length > 0 && args[0] != null )
{
if( !(args[0] instanceof String) )
{
throw new LuaException( "Expected string" );
}
title = (String)args[0];
}
String title = optString( args, 0, "" );
getCurrentPage();
m_printer.setPageTitle( title );
return null;

View File

@ -17,9 +17,13 @@
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
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 {
private TileSpeaker m_speaker;
private long m_clock;
@ -127,47 +131,20 @@ public Object[] callMethod( @Nonnull IComputerAccess computerAccess, @Nonnull IL
@Nonnull
private synchronized Object[] playNote( Object[] arguments, ILuaContext context ) throws LuaException
{
float volume = 1.0f;
float pitch = 1.0f;
String name = getString(arguments, 0);
float volume = (float) optReal( arguments, 1, 1.0 );
float pitch = (float) optReal( arguments, 2, 1.0 );
// 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 ( !SoundEvent.REGISTRY.containsKey( new ResourceLocation( "block.note." + arguments[0] ) ) )
// Check if sound exists
if ( !SoundEvent.REGISTRY.containsKey( new ResourceLocation( "block.note." + name ) ) )
{
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
Object[] returnValue = playSound(
new Object[] {
"block.note." + arguments[0],
"block.note." + name,
(double)Math.min( volume, 3f ),
Math.pow( 2.0f, ( pitch - 12.0f ) / 12.0f)
}, context, true
@ -184,42 +161,11 @@ private synchronized Object[] playNote( Object[] arguments, ILuaContext context
@Nonnull
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;
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] );
ResourceLocation resourceName = new ResourceLocation( name );
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 java.util.HashMap;
import java.util.Map;
import static dan200.computercraft.core.apis.ArgumentHelper.*;
public class TurtleAPI implements ILuaAPI
{
private IAPIEnvironment m_environment;
@ -115,38 +117,23 @@ private Object[] tryCommand( ILuaContext context, ITurtleCommand command ) throw
private int parseSlotNumber( Object[] arguments, int index ) throws LuaException
{
int slot = parseOptionalSlotNumber( arguments, index, 99 );
if( slot == 99 )
{
throw new LuaException( "Expected number" );
}
return slot;
int slot = getInt( arguments, index );
if( slot < 1 || slot > 16 ) throw new LuaException( "Slot number " + slot + " out of range" );
return slot - 1;
}
private int parseOptionalSlotNumber( Object[] arguments, int index, int fallback ) throws LuaException
{
if( arguments.length <= index || !(arguments[index] instanceof Number) )
{
return fallback;
}
int slot = ((Number)arguments[index]).intValue();
if( slot >= 1 && slot <= 16 )
{
return slot - 1;
}
else
{
throw new LuaException( "Slot number " + slot + " out of range" );
}
if( index >= arguments.length || arguments[ index ] == null ) return fallback;
int slot = getInt( arguments, index );
if( slot < 1 || slot > 16 ) throw new LuaException( "Slot number " + slot + " out of range" );
return slot - 1;
}
private int parseCount( Object[] arguments, int index ) throws LuaException
{
if( arguments.length <= index || !(arguments[index] instanceof Number) )
{
throw new LuaException( "Expected number" );
}
int count = ((Number)arguments[index]).intValue();
int count = optInt( arguments, index, 64 );
if( count >= 0 && count <= 64 )
{
return count;
@ -159,19 +146,16 @@ private int parseCount( 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();
}
if( !(arguments[ index ] instanceof String) )
{
throw new LuaException( "Expected string" );
}
if( arguments[ index ].equals( "left" ) )
else if( side.equalsIgnoreCase( "left" ) )
{
return Optional.of( TurtleSide.Left );
}
else if( arguments[ index ].equals( "right" ) )
else if( side.equalsIgnoreCase( "right" ) )
{
return Optional.of( TurtleSide.Right );
}
@ -252,11 +236,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 12:
{
// drop
int count = 64;
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
int count = parseCount( args, 0 );
return tryCommand( context, new TurtleDropCommand( InteractDirection.Forward, count ) );
}
case 13:
@ -343,51 +323,31 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 25:
{
// dropUp
int count = 64;
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
int count = parseCount( args, 0 );
return tryCommand( context, new TurtleDropCommand( InteractDirection.Up, count ) );
}
case 26:
{
// dropDown
int count = 64;
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
int count = parseCount( args, 0 );
return tryCommand( context, new TurtleDropCommand( InteractDirection.Down, count ) );
}
case 27:
{
// suck
int count = 64;
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
int count = parseCount( args, 0 );
return tryCommand( context, new TurtleSuckCommand( InteractDirection.Forward, count ) );
}
case 28:
{
// suckUp
int count = 64;
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
int count = parseCount( args, 0 );
return tryCommand( context, new TurtleSuckCommand( InteractDirection.Up, count ) );
}
case 29:
{
// suckDown
int count = 64;
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
int count = parseCount( args, 0 );
return tryCommand( context, new TurtleSuckCommand( InteractDirection.Down, count ) );
}
case 30:
@ -405,11 +365,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
case 31:
{
// refuel
int count = 64;
if( args.length > 0 )
{
count = parseCount( args, 0 );
}
int count = parseCount( args, 0 );
return tryCommand( context, new TurtleRefuelCommand( count ) );
}
case 32:
@ -422,11 +378,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
{
// transferTo
int slot = parseSlotNumber( args, 0 );
int count = 64;
if( args.length > 1 )
{
count = parseCount( args, 1 );
}
int count = parseCount( args, 1 );
return tryCommand( context, new TurtleTransferToCommand( slot, count ) );
}
case 34:

View File

@ -24,13 +24,16 @@
import net.minecraft.tileentity.TileEntitySign;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
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.text.TextComponentString;
import net.minecraft.world.World;
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 javax.annotation.Nonnull;
@ -215,7 +218,6 @@ private static ItemStack deployOnEntity( ItemStack stack, final ITurtleAccess tu
}
// Load up the turtle's inventory
Item item = stack.getItem();
ItemStack stackCopy = stack.copy();
turtlePlayer.loadInventory( stackCopy );
@ -237,21 +239,26 @@ public void consumeDrop( Entity entity, ItemStack drop )
// Place on the entity
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;
turtlePlayer.loadInventory( stackCopy );
}
else if( hitEntity.processInitialInteract( turtlePlayer, stackCopy, EnumHand.MAIN_HAND ) )
else if( !ForgeHooks.onInteractEntity( turtlePlayer, hitEntity, stack, EnumHand.MAIN_HAND ) )
{
placed = true;
}
else if( hitEntity instanceof EntityLivingBase )
{
placed = item.itemInteractionForEntity( stackCopy, turtlePlayer, (EntityLivingBase)hitEntity, EnumHand.MAIN_HAND );
if( placed )
// See EntityPlayer.interact
if( hitEntity.processInitialInteract( turtlePlayer, stackCopy, EnumHand.MAIN_HAND ) )
{
turtlePlayer.loadInventory( stackCopy );
placed = true;
}
else if( hitEntity instanceof EntityLivingBase )
{
placed = stackCopy.interactWithEntity( turtlePlayer, (EntityLivingBase) hitEntity, EnumHand.MAIN_HAND );
if( placed )
{
turtlePlayer.loadInventory( stackCopy );
}
}
}
@ -352,19 +359,29 @@ private static ItemStack deployOnBlock( ItemStack stack, ITurtleAccess turtle, T
// Do the deploying (put everything in the players inventory)
boolean placed = false;
if( item.onItemUseFirst( stackCopy, turtlePlayer, turtle.getWorld(), position, side, hitX, hitY, hitZ, EnumHand.MAIN_HAND ) == EnumActionResult.SUCCESS )
// See PlayerInteractionManager.processRightClickBlock
PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock( turtlePlayer, EnumHand.MAIN_HAND, stackCopy, position, side, new Vec3d( hitX, hitY, hitZ ) );
if( !event.isCanceled() )
{
placed = true;
turtlePlayer.loadInventory( stackCopy );
if( item.onItemUseFirst( stackCopy, turtlePlayer, turtle.getWorld(), position, side, hitX, hitY, hitZ, EnumHand.MAIN_HAND ) == EnumActionResult.SUCCESS )
{
placed = true;
turtlePlayer.loadInventory( stackCopy );
}
else if( event.getUseItem() != Event.Result.DENY &&
stackCopy.onItemUse( turtlePlayer, turtle.getWorld(), position, EnumHand.MAIN_HAND, side, hitX, hitY, hitZ ) == EnumActionResult.SUCCESS )
{
placed = true;
turtlePlayer.loadInventory( stackCopy );
}
}
else if( item.onItemUse( stackCopy, turtlePlayer, turtle.getWorld(), position, EnumHand.MAIN_HAND, side, hitX, hitY, hitZ ) == EnumActionResult.SUCCESS )
if( !placed && (item instanceof ItemBucket || item instanceof ItemBoat || item instanceof ItemLilyPad || item instanceof ItemGlassBottle)
&& ForgeHooks.onItemRightClick( turtlePlayer, EnumHand.MAIN_HAND, stackCopy ) )
{
placed = true;
turtlePlayer.loadInventory( stackCopy );
}
else if( item instanceof ItemBucket || item instanceof ItemBoat || item instanceof ItemLilyPad || item instanceof ItemGlassBottle )
{
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() ) )
{
placed = true;

View File

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

View File

@ -22,14 +22,22 @@
import net.minecraft.entity.Entity;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.util.DamageSource;
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.WorldServer;
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.SideOnly;
import org.apache.commons.lang3.tuple.Pair;
@ -189,9 +197,10 @@ public void consumeDrop( Entity entity, ItemStack drop )
}
} );
// Place on the entity
boolean placed = false;
if( hitEntity.canBeAttackedWithItem() && !hitEntity.hitByEntity( turtlePlayer ) )
// Attack the entity
boolean attacked = false;
if( hitEntity.canBeAttackedWithItem() && !hitEntity.hitByEntity( turtlePlayer )
&& !MinecraftForge.EVENT_BUS.post( new AttackEntityEvent( turtlePlayer, hitEntity ) ) )
{
float damage = (float)turtlePlayer.getEntityAttribute( SharedMonsterAttributes.ATTACK_DAMAGE ).getAttributeValue();
damage *= getDamageMultiplier();
@ -206,13 +215,13 @@ public void consumeDrop( Entity entity, ItemStack drop )
{
hitEntity.attackEntityFrom( source, damage );
}
placed = true;
attacked = true;
}
else
{
if( hitEntity.attackEntityFrom( source, damage ) )
{
placed = true;
attacked = true;
}
}
}
@ -222,7 +231,7 @@ public void consumeDrop( Entity entity, ItemStack drop )
ComputerCraft.clearEntityDropConsumer( hitEntity );
// Put everything we collected into the turtles inventory, then return
if( placed )
if( attacked )
{
turtlePlayer.unloadInventory( turtle );
return TurtleCommandResult.success();
@ -243,10 +252,16 @@ private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction )
!world.isAirBlock( newPosition ) &&
!WorldUtil.isLiquidBlock( world, newPosition ) )
{
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, position, direction );
if( ComputerCraft.turtlesObeyBlockProtection )
{
// 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 ) )
{
return TurtleCommandResult.failure( "Cannot break protected block" );
@ -262,7 +277,7 @@ private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction )
// Consume the items the block drops
if( canHarvestBlock( world, newPosition ) )
{
List<ItemStack> items = getBlockDropped( world, newPosition );
List<ItemStack> items = getBlockDropped( world, newPosition, turtlePlayer );
if( items != null && items.size() > 0 )
{
for( ItemStack stack : items )
@ -279,7 +294,7 @@ private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction )
// Destroy the block
IBlockState previousState = world.getBlockState( newPosition );
world.playEvent(2001, newPosition, Block.getStateId(previousState));
world.playEvent(2001, newPosition, Block.getStateId(previousState));
world.setBlockToAir( newPosition );
// Remember the previous block
@ -295,9 +310,20 @@ private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction )
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();
return block.getDrops( world, pos, world.getBlockState( pos ), 0 );
IBlockState state = world.getBlockState( pos );
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 @@ private static ItemStack storeItems( ItemStack stack, IItemHandler inventory, in
}
// Inspect the slots in order and try to find empty or stackable slots
ItemStack remainder = stack;
ItemStack remainder = stack.copy();
for( int slot : slots )
{
if( remainder == null ) break;
remainder = inventory.insertItem( slot, remainder, false );
}
return remainder;
return areItemsEqual( stack, remainder ) ? stack : remainder;
}
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_whitelist=HTTP whitelist
gui.computercraft:config.http_blacklist=HTTP blacklist
gui.computercraft:config.disable_lua51_features=Disable Lua 5.1 features
gui.computercraft:config.default_computer_settings=Default Computer settings
gui.computercraft:config.log_peripheral_errors=Log peripheral errors

View File

@ -184,6 +184,9 @@ end
-- Install globals
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 )
repeat
local sEvent, param = os.pullEvent( "timer" )
@ -191,6 +194,10 @@ function sleep( nTime )
end
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 x,y = term.getCursorPos()
@ -276,12 +283,29 @@ function printError( ... )
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 )
local sLine = ""
local sLine
if type( _sDefault ) == "string" then
sLine = _sDefault
else
sLine = ""
end
local nHistoryPos
local nPos = 0
local nPos = #sLine
if _sReplaceChar then
_sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
end
@ -530,6 +554,12 @@ function read( _sReplaceChar, _tHistory, _fnComplete )
end
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" )
if file then
local func, err = load( file.readAll(), fs.getName( _sFile ), "t", _tEnv )
@ -540,6 +570,9 @@ loadfile = function( _sFile, _tEnv )
end
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 )
if fnFile then
return fnFile()
@ -550,6 +583,12 @@ end
-- Install the rest of the OS api
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 tEnv = _tEnv
setmetatable( tEnv, { __index = _G } )
@ -574,6 +613,9 @@ end
local tAPIsLoading = {}
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 )
if sName:sub(-4) == ".lua" then
sName = sName:sub(1,-5)
@ -613,6 +655,9 @@ function os.loadAPI( _sPath )
end
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
_G[_sName] = nil
end
@ -658,25 +703,73 @@ if http then
end
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)
end
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)
end
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 )
if not ok then
os.queueEvent( "http_failure", _url, err )
end
return ok, err
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
-- Install the lua part of the FS api
local tEmpty = {}
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)
bIncludeDirs = (bIncludeDirs ~= false)
local sDir = sLocation
@ -747,7 +840,7 @@ for n,sFile in ipairs( tApis ) do
end
end
if turtle then
if turtle and fs.isDir( "rom/apis/turtle" ) then
-- Load turtle APIs
local tApis = fs.list( "rom/apis/turtle" )
for n,sFile in ipairs( tApis ) do

View File

@ -19,32 +19,51 @@ black = 32768
function combine( ... )
local r = 0
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)
end
return r
end
function subtract( colors, ... )
if type( colors ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( colors ) .. ")", 2 )
end
local r = colors
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))
end
return r
end
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)
end
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
elseif type(r) == "number" and type(g) == "number" and type(b) == "number" then
return
bit32.lshift( bit32.band(r * 255, 0xFF), 16 ) +
bit32.lshift( bit32.band(g * 255, 0xFF), 8 ) +
bit32.band(b * 255, 0xFF)
else
error( "Expected 1 or 3 numbers" )
elseif type( g ) ~= "number" then
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

View File

@ -1,5 +1,8 @@
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"
end

View File

@ -56,6 +56,12 @@ local function narrow( p1, p2, fix )
end
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
if commands then
return commands.getBlockPosition()

View File

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

View File

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

View File

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

View File

@ -1,12 +1,17 @@
local function create( first, ... )
if first ~= nil then
if type( first ) ~= "function" then
error( "Expected function, got "..type( first ), 3 )
end
return coroutine.create(first), create( ... )
local function create( ... )
local tFns = table.pack(...)
local tCos = {}
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
tCos[i] = coroutine.create(fn)
end
return nil
return tCos
end
local function runUntilLimit( _routines, _limit )
@ -51,11 +56,11 @@ local function runUntilLimit( _routines, _limit )
end
function waitForAny( ... )
local routines = { create( ... ) }
local routines = create( ... )
return runUntilLimit( routines, #routines - 1 )
end
function waitForAll( ... )
local routines = { create( ... ) }
local routines = create( ... )
runUntilLimit( routines, 0 )
end

View File

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

View File

@ -7,14 +7,14 @@ local tReceivedMessageTimeouts = {}
local tHostnames = {}
function open( sModem )
if type( sModem ) ~= "string" then
error( "expected string", 2 )
end
if peripheral.getType( sModem ) ~= "modem" then
error( "No such modem: "..sModem, 2 )
end
peripheral.call( sModem, "open", os.getComputerID() )
peripheral.call( sModem, "open", CHANNEL_BROADCAST )
if type( sModem ) ~= "string" then
error( "expected string", 2 )
end
if peripheral.getType( sModem ) ~= "modem" then
error( "No such modem: "..sModem, 2 )
end
peripheral.call( sModem, "open", os.getComputerID() )
peripheral.call( sModem, "open", CHANNEL_BROADCAST )
end
function close( sModem )
@ -55,7 +55,7 @@ function isOpen( sModem )
end
end
end
return false
return false
end
function send( nRecipient, message, sProtocol )
@ -93,7 +93,7 @@ function send( nRecipient, message, sProtocol )
end
function broadcast( message, sProtocol )
send( CHANNEL_BROADCAST, message, sProtocol )
send( CHANNEL_BROADCAST, message, sProtocol )
end
function receive( sProtocolFilter, nTimeout )
@ -103,31 +103,31 @@ function receive( sProtocolFilter, nTimeout )
end
-- Start the timer
local timer = nil
local sFilter = nil
if nTimeout then
timer = os.startTimer( nTimeout )
sFilter = nil
else
sFilter = "rednet_message"
end
local timer = nil
local sFilter = nil
if nTimeout then
timer = os.startTimer( nTimeout )
sFilter = nil
else
sFilter = "rednet_message"
end
-- Wait for events
while true do
local sEvent, p1, p2, p3 = os.pullEvent( sFilter )
if sEvent == "rednet_message" then
-- Return the first matching rednet_message
local nSenderID, message, sProtocol = p1, p2, p3
if sProtocolFilter == nil or sProtocol == sProtocolFilter then
return nSenderID, message, sProtocol
end
elseif sEvent == "timer" then
-- Return nil if we timeout
if p1 == timer then
return nil
end
end
end
-- Wait for events
while true do
local sEvent, p1, p2, p3 = os.pullEvent( sFilter )
if sEvent == "rednet_message" then
-- Return the first matching rednet_message
local nSenderID, message, sProtocol = p1, p2, p3
if sProtocolFilter == nil or sProtocol == sProtocolFilter then
return nSenderID, message, sProtocol
end
elseif sEvent == "timer" then
-- Return nil if we timeout
if p1 == timer then
return nil
end
end
end
end
function host( sProtocol, sHostname )
@ -219,41 +219,41 @@ end
local bRunning = false
function run()
if bRunning then
error( "rednet is already running", 2 )
end
bRunning = true
while bRunning do
local sEvent, p1, p2, p3, p4 = os.pullEventRaw()
if sEvent == "modem_message" then
-- Got a modem message, process it and add it to the rednet event queue
local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4
if isOpen( sModem ) and ( nChannel == os.getComputerID() or nChannel == CHANNEL_BROADCAST ) then
if type( tMessage ) == "table" and tMessage.nMessageID then
if not tReceivedMessages[ tMessage.nMessageID ] then
tReceivedMessages[ tMessage.nMessageID ] = true
tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = nMessageID
os.queueEvent( "rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol )
end
end
end
if bRunning then
error( "rednet is already running", 2 )
end
bRunning = true
while bRunning do
local sEvent, p1, p2, p3, p4 = os.pullEventRaw()
if sEvent == "modem_message" then
-- Got a modem message, process it and add it to the rednet event queue
local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4
if isOpen( sModem ) and ( nChannel == os.getComputerID() or nChannel == CHANNEL_BROADCAST ) then
if type( tMessage ) == "table" and tMessage.nMessageID then
if not tReceivedMessages[ tMessage.nMessageID ] then
tReceivedMessages[ tMessage.nMessageID ] = true
tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = tMessage.nMessageID
os.queueEvent( "rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol )
end
end
end
elseif sEvent == "rednet_message" then
-- Got a rednet message (queued from above), respond to dns lookup
local nSenderID, tMessage, sProtocol = p1, p2, p3
if sProtocol == "dns" and type(tMessage) == "table" and tMessage.sType == "lookup" then
local sHostname = tHostnames[ tMessage.sProtocol ]
if sHostname ~= nil and (tMessage.sHostname == nil or tMessage.sHostname == sHostname) then
rednet.send( nSenderID, {
sType = "lookup response",
sHostname = sHostname,
sProtocol = tMessage.sProtocol,
}, "dns" )
end
end
elseif sEvent == "rednet_message" then
-- Got a rednet message (queued from above), respond to dns lookup
local nSenderID, tMessage, sProtocol = p1, p2, p3
if sProtocol == "dns" and type(tMessage) == "table" and tMessage.sType == "lookup" then
local sHostname = tHostnames[ tMessage.sProtocol ]
if sHostname ~= nil and (tMessage.sHostname == nil or tMessage.sHostname == sHostname) then
rednet.send( nSenderID, {
sType = "lookup response",
sHostname = sHostname,
sProtocol = tMessage.sProtocol,
}, "dns" )
end
end
elseif sEvent == "timer" then
elseif sEvent == "timer" then
-- Got a timer event, use it to clear the event queue
local nTimer = p1
local nMessage = tReceivedMessageTimeouts[ nTimer ]
@ -261,6 +261,6 @@ function run()
tReceivedMessageTimeouts[ nTimer ] = nil
tReceivedMessages[ nMessage ] = nil
end
end
end
end
end
end

View File

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

View File

@ -7,7 +7,7 @@ function slowWrite( sText, nRate )
local nSleep = 1 / nRate
sText = tostring( sText )
local x,y = term.getCursorPos(x,y)
local x,y = term.getCursorPos()
local len = string.len( sText )
for n=1,len do
@ -25,6 +25,12 @@ function slowPrint( sText, nRate )
end
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
if not bTwentyFourHour then
if nTime >= 12 then
@ -68,6 +74,9 @@ local function makePagedScroll( _term, _nFreeLines )
end
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
local oldTerm = term.current()
local newTerm = {}
@ -99,6 +108,11 @@ end
local function tabulateCommon( bPaged, ... )
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 nMaxLen = w / 8
@ -301,6 +315,9 @@ function serialize( t )
end
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", {} )
if func then
local ok, result = pcall( func )
@ -312,11 +329,20 @@ function unserialize( s )
end
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 = {}
return serializeJSONImpl( t, tTracking, bNBTStyle or false )
end
function urlEncode( str )
if type( str ) ~= "string" then
error( "bad argument #1 (expected string, got " .. type( str ) .. ")", 2 )
end
if str then
str = string.gsub(str, "\n", "\r\n")
str = string.gsub(str, "([^A-Za-z0-9 %-%_%.])", function(c)
@ -338,6 +364,14 @@ end
local tEmpty = {}
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 nDot = string.find( sSearchText, ".", nStart, true )
local tTable = tSearchTable or _ENV
@ -352,8 +386,19 @@ function complete( sSearchText, tSearchTable )
return tEmpty
end
end
local sPart = string.sub( sSearchText, nStart, nDot )
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 )
local nPartLength = string.len( sPart )
local tResults = {}

View File

@ -18,20 +18,18 @@ local tHex = {
[ colors.black ] = "f",
}
local type = type
local string_rep = string.rep
local string_sub = string.sub
local table_unpack = table.unpack
function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
if type( parent ) ~= "table" or
type( nX ) ~= "number" or
type( nY ) ~= "number" or
type( nWidth ) ~= "number" or
type( nHeight ) ~= "number" or
(bStartVisible ~= nil and type( bStartVisible ) ~= "boolean") then
error( "Expected object, number, number, number, number, [boolean]", 2 )
end
if type( parent ) ~= "table" then error( "bad argument #1 (expected table, got " .. type( parent ) .. ")", 2 ) end
if type( nX ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( nX ) .. ")", 2 ) end
if type( nY ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( nY ) .. ")", 2 ) end
if type( nWidth ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( nWidth ) .. ")", 2 ) end
if type( nHeight ) ~= "number" then error( "bad argument #5 (expected number, got " .. type( nHeight ) .. ")", 2 ) end
if bStartVisible ~= nil and type( bStartVisible ) ~= "boolean" then error( "bad argument #6 (expected boolean, got " .. type( bStartVisible ) .. ")", 2 ) end
if parent == term then
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
function window.blit( sText, sTextColor, sBackgroundColor )
if type(sText) ~= "string" or type(sTextColor) ~= "string" or type(sBackgroundColor) ~= "string" then
error( "Expected string, string, string", 2 )
end
if type( sText ) ~= "string" then error( "bad argument #1 (expected string, got " .. type( sText ) .. ")", 2 ) end
if type( sTextColor ) ~= "string" then error( "bad argument #2 (expected string, got " .. type( sTextColor ) .. ")", 2 ) end
if type( sBackgroundColor ) ~= "string" then error( "bad argument #3 (expected string, got " .. type( sBackgroundColor ) .. ")", 2 ) end
if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then
error( "Arguments must be the same length", 2 )
end
@ -243,9 +241,8 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end
function window.setCursorPos( x, y )
if type( x ) ~= "number" or type( y ) ~= "number" then
error( "Expected number, number", 2 )
end
if type( x ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( x ) .. ")", 2 ) end
if type( y ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( y ) .. ")", 2 ) end
nCursorX = math.floor( x )
nCursorY = math.floor( y )
if bVisible then
@ -254,9 +251,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end
function window.setCursorBlink( blink )
if type( blink ) ~= "boolean" then
error( "Expected boolean", 2 )
end
if type( blink ) ~= "boolean" then error( "bad argument #1 (expected boolean, got " .. type( blink ) .. ")", 2 ) end
bCursorBlink = blink
if bVisible then
updateCursorBlink()
@ -276,10 +271,10 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end
local function setTextColor( color )
if type(color) ~= "number" then
error( "Expected number", 3 )
if type( color ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( color ) .. ")", 2 )
elseif tHex[color] == nil then
error( "Invalid color", 3 )
error( "Invalid color", 2 )
end
nTextColor = color
if bVisible then
@ -287,26 +282,25 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end
end
function window.setTextColor( color )
setTextColor( color )
end
function window.setTextColour( color )
setTextColor( color )
end
window.setTextColor = setTextColor
window.setTextColour = setTextColor
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
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 ) }
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[1] = r
tCol[2] = g
tCol[3] = b
else
error( "Expected number, number, number, number", 2 )
end
if bVisible then
@ -324,30 +318,23 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
window.getPaletteColor = window.getPaletteColour
local function setBackgroundColor( color )
if type(color) ~= "number" then
error( "Expected number", 3 )
if type( color ) ~= "number" then
error( "bad argument #1 (expected number, got " .. type( color ) .. ")", 2 )
elseif tHex[color] == nil then
error( "Invalid color", 3 )
end
nBackgroundColor = color
end
function window.setBackgroundColor( color )
setBackgroundColor( color )
end
function window.setBackgroundColour( color )
setBackgroundColor( color )
end
window.setBackgroundColor = setBackgroundColor
window.setBackgroundColour = setBackgroundColor
function window.getSize()
return nWidth, nHeight
end
function window.scroll( n )
if type( n ) ~= "number" then
error( "Expected number", 2 )
end
if type( n ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( n ) .. ")", 2 ) end
if n ~= 0 then
local tNewLines = {}
local sEmptyText = sEmptySpaceLine
@ -392,9 +379,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
-- Other functions
function window.setVisible( bVis )
if type( bVis) ~= "boolean" then
error( "Expected boolean", 2 )
end
if type( bVis ) ~= "boolean" then error( "bad argument #1 (expected boolean, got " .. type( bVis ) .. ")", 2 ) end
if bVisible ~= bVis then
bVisible = bVis
if bVisible then
@ -426,9 +411,11 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
end
function window.reposition( nNewX, nNewY, nNewWidth, nNewHeight )
if type( nNewX ) ~= "number" or type( nNewY ) ~= "number" or type( nNewWidth ) ~= "number" or type( nNewWidth ) ~= "number" then
error( "Expected number, number, number, number", 2 )
end
if type( nNewX ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( nNewX ) .. ")", 2 ) end
if type( nNewY ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( nNewY ) .. ")", 2 ) 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
nY = nNewY
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:
"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:
http.checkURL( url )
http.checkURLAsync( url )
http.request( url, [postData], [headers] )
http.get( url, [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:
"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
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
selectProcess( n )
redrawMenu()
@ -180,6 +183,9 @@ function multishell.setFocus( n )
end
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
return tProcesses[n].sTitle
end
@ -187,6 +193,12 @@ function multishell.getTitle( n )
end
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
setProcessTitle( n, sTitle )
redrawMenu()
@ -198,6 +210,12 @@ function multishell.getCurrent()
end
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()
setMenuVisible( (#tProcesses + 1) >= 2 )
local nResult = launchProcess( tProgramEnv, sProgramPath, ... )

View File

@ -175,7 +175,7 @@ local nCompletion
local tCompleteEnv = _ENV
local function complete( sLine )
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
sLine = string.sub( sLine, nStartPos )
end
@ -709,7 +709,13 @@ while bRunning do
end
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
local sLine = tLines[y]
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
if mChoices[selection]=="Save" then
if bReadOnly then
fMessage = "Access Denied"
fMessage = "Access denied"
return false
end
local success = save(sPath)

View File

@ -57,10 +57,6 @@ elseif sCommand == "host" then
x,y,z = gps.locate( 2, true )
if x == nil then
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
end
end

View File

@ -34,7 +34,7 @@ while bRunning do
local s = read( nil, tCommandHistory, function( sLine )
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
sLine = string.sub( sLine, nStartPos )
end
@ -64,10 +64,10 @@ while bRunning do
end
if func then
local tResults = { pcall( func ) }
local tResults = table.pack( pcall( func ) )
if tResults[1] then
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 ]
if type( value ) == "table" then
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 )
term.redirect( historyWindow )
print()
if string.match( sMessage, "^\*" ) then
if string.match( sMessage, "^%*" ) then
-- Information
term.setTextColour( highlightColour )
write( sMessage )
term.setTextColour( textColour )
else
-- Chat
local sUsernameBit = string.match( sMessage, "^\<[^\>]*\>" )
local sUsernameBit = string.match( sMessage, "^<[^>]*>" )
if sUsernameBit then
term.setTextColour( highlightColour )
write( sUsernameBit )

View File

@ -2,9 +2,12 @@
local tArgs = { ... }
if #tArgs == 0 then
-- "set"
local x,y = term.getCursorPos()
local tSettings = {}
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
textutils.pagedPrint(table.concat(tSettings,"\n"),y-3)
elseif #tArgs == 1 then
-- "set foo"

View File

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

View File

@ -168,6 +168,15 @@ local function completeSet( shell, nIndex, sText, tPreviousText )
return completeMultipleChoice( sText, settings.getNames(), true )
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/cd.lua", completeDir )
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/http/pastebin.lua", completePastebin )
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
if fs.exists( "/rom/autorun" ) and fs.isDir( "/rom/autorun" ) then