diff --git a/src/main/java/dan200/computercraft/api/lua/ICallContext.java b/src/main/java/dan200/computercraft/api/lua/ICallContext.java new file mode 100644 index 0000000000..a7ef6c546d --- /dev/null +++ b/src/main/java/dan200/computercraft/api/lua/ICallContext.java @@ -0,0 +1,31 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.lua; + +import javax.annotation.Nonnull; + +/** + * An interface passed to peripherals and {@link ILuaObject}s by computers or turtles, providing methods that allow the + * method to interact with the invoking computer. + */ +public interface ICallContext +{ + /** + * Queue a task to be executed on the main server thread at the beginning of next tick, but do not wait for it to + * complete. This should be used when you need to interact with the world in a thread-safe manner but do not care + * about the result or you wish to run asynchronously. + * + * When the task has finished, it will enqueue a {@code task_completed} event, which takes the task id, a success + * value and the return values, or an error message if it failed. If you need to wait on this event, it may be + * better to use {@link MethodResult#onMainThread(ILuaCallable)}. + * + * @param task The task to execute on the main thread. + * @return The "id" of the task. This will be the first argument to the {@code task_completed} event. + * @throws LuaException If the task could not be queued. + */ + long issueMainThreadTask( @Nonnull ILuaTask task ) throws LuaException; +} diff --git a/src/main/java/dan200/computercraft/api/lua/ILuaCallable.java b/src/main/java/dan200/computercraft/api/lua/ILuaCallable.java new file mode 100644 index 0000000000..81c21ae06e --- /dev/null +++ b/src/main/java/dan200/computercraft/api/lua/ILuaCallable.java @@ -0,0 +1,31 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.lua; + +import javax.annotation.Nonnull; + +/** + * A function which calls performs an action in a specific context (such as on the server thread) and returns a result. + * + * @see MethodResult#onMainThread(ILuaCallable) + * @see ILuaContext#executeMainThreadTask(ILuaTask) + */ +@FunctionalInterface +public interface ILuaCallable +{ + /** + * Run the code within the specified context and return the result to continue with. + * + * @return The result of executing this function. Note that this may not be evaluated within the same context as + * this call is. + * @throws LuaException If you throw any exception from this function, a lua error will be raised with the + * same message as your exception. Use this to throw appropriate errors if the wrong + * arguments are supplied to your method. + */ + @Nonnull + MethodResult execute() throws LuaException; +} diff --git a/src/main/java/dan200/computercraft/api/lua/ILuaContext.java b/src/main/java/dan200/computercraft/api/lua/ILuaContext.java index b3e49246e0..06fd18e193 100644 --- a/src/main/java/dan200/computercraft/api/lua/ILuaContext.java +++ b/src/main/java/dan200/computercraft/api/lua/ILuaContext.java @@ -1,6 +1,6 @@ /* * This file is part of the public ComputerCraft API - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only. + * Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only. * For help using the API, and posting your mods, visit the forums at computercraft.info. */ @@ -13,8 +13,11 @@ * An interface passed to peripherals and {@link ILuaObject}s by computers or turtles, providing methods * that allow the peripheral call to wait for events before returning, just like in lua. This is very useful if you need * to signal work to be performed on the main thread, and don't want to return until the work has been completed. + * + * This interface mostly exists for integrating with older code. One should use {@link MethodResult} instead, as this + * encourages an asynchronous way of interacting with Lua coroutines. */ -public interface ILuaContext +public interface ILuaContext extends ICallContext { /** * Wait for an event to occur on the computer, suspending the thread until it arises. This method is exactly @@ -30,8 +33,10 @@ public interface ILuaContext * @throws InterruptedException If the user shuts down or reboots the computer while pullEvent() is waiting for an * event, InterruptedException will be thrown. This exception must not be caught or * intercepted, or the computer will leak memory and end up in a broken state. + * @deprecated Use {@link MethodResult#pullEvent(String, ILuaFunction)} */ @Nonnull + @Deprecated Object[] pullEvent( @Nullable String filter ) throws LuaException, InterruptedException; /** @@ -45,8 +50,10 @@ public interface ILuaContext * an event, InterruptedException will be thrown. This exception must not be caught or * intercepted, or the computer will leak memory and end up in a broken state. * @see #pullEvent(String) + * @deprecated Use {@link MethodResult#pullEventRaw(String, ILuaFunction)} */ @Nonnull + @Deprecated Object[] pullEventRaw( @Nullable String filter ) throws InterruptedException; /** @@ -59,8 +66,10 @@ public interface ILuaContext * InterruptedException will be thrown. This exception must not be caught or * intercepted, or the computer will leak memory and end up in a broken state. * @see #pullEvent(String) + * @deprecated Use {@link MethodResult#pullEventRaw(ILuaFunction)} */ @Nonnull + @Deprecated Object[] yield( @Nullable Object[] arguments ) throws InterruptedException; /** @@ -76,22 +85,9 @@ public interface ILuaContext * @throws InterruptedException If the user shuts down or reboots the computer the coroutine is suspended, * InterruptedException will be thrown. This exception must not be caught or * intercepted, or the computer will leak memory and end up in a broken state. + * @deprecated Use {@link MethodResult#onMainThread(ILuaCallable)} */ @Nullable + @Deprecated Object[] executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException, InterruptedException; - - /** - * Queue a task to be executed on the main server thread at the beginning of next tick, but do not wait for it to - * complete. This should be used when you need to interact with the world in a thread-safe manner but do not care - * about the result or you wish to run asynchronously. - * - * When the task has finished, it will enqueue a {@code task_completed} event, which takes the task id, a success - * value and the return values, or an error message if it failed. If you need to wait on this event, it may be - * better to use {@link #executeMainThreadTask(ILuaTask)}. - * - * @param task The task to execute on the main thread. - * @return The "id" of the task. This will be the first argument to the {@code task_completed} event. - * @throws LuaException If the task could not be queued. - */ - long issueMainThreadTask( @Nonnull ILuaTask task ) throws LuaException; } diff --git a/src/main/java/dan200/computercraft/api/lua/ILuaContextTask.java b/src/main/java/dan200/computercraft/api/lua/ILuaContextTask.java new file mode 100644 index 0000000000..d5add44b07 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/lua/ILuaContextTask.java @@ -0,0 +1,24 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.lua; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A function which executes using a {@link ILuaContext}. + * + * Like {@link ILuaContext}, this is not intended for use in the future - it purely exists as an argument for + * {@link MethodResult#withLuaContext(ILuaContextTask)}. + */ +@FunctionalInterface +public interface ILuaContextTask +{ + @Nullable + @Deprecated + Object[] execute( @Nonnull ILuaContext context ) throws LuaException, InterruptedException; +} diff --git a/src/main/java/dan200/computercraft/api/lua/ILuaFunction.java b/src/main/java/dan200/computercraft/api/lua/ILuaFunction.java new file mode 100644 index 0000000000..5f2418849e --- /dev/null +++ b/src/main/java/dan200/computercraft/api/lua/ILuaFunction.java @@ -0,0 +1,33 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.lua; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A Lua function which consumes some values and returns a result. + * + * @see MethodResult#then(ILuaFunction) + * @see MethodResult#pullEvent(ILuaFunction) + * @see MethodResult#pullEventRaw(String) + */ +@FunctionalInterface +public interface ILuaFunction +{ + /** + * Accept the values and return another method result. + * + * @param values The inputs for this function. + * @return The result of executing this function. + * @throws LuaException If you throw any exception from this function, a lua error will be raised with the + * same message as your exception. Use this to throw appropriate errors if the wrong + * arguments are supplied to your method. + */ + @Nonnull + MethodResult call( @Nullable Object[] values ) throws LuaException; +} diff --git a/src/main/java/dan200/computercraft/api/lua/ILuaObject.java b/src/main/java/dan200/computercraft/api/lua/ILuaObject.java index fefc5e8a5d..2f986d9305 100644 --- a/src/main/java/dan200/computercraft/api/lua/ILuaObject.java +++ b/src/main/java/dan200/computercraft/api/lua/ILuaObject.java @@ -1,6 +1,6 @@ /* * This file is part of the public ComputerCraft API - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only. + * Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only. * For help using the API, and posting your mods, visit the forums at computercraft.info. */ @@ -41,16 +41,41 @@ public interface ILuaObject * wishes to call. The integer indicates the index into the getMethodNames() table * that corresponds to the string passed into peripheral.call() * @param arguments The arguments for this method. See {@link IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])} - * the possible values and conversion rules. - * @return An array of objects, representing the values you wish to return to the Lua program. - * See {@link IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])} for the valid values and - * conversion rules. + * for the possible values and conversion rules. + * @return An array of objects, representing the values you wish to return to the Lua program. See + * {@link MethodResult#of(Object...)} for the valid values and conversion rules. * @throws LuaException If the task could not be queued, or if the task threw an exception. * @throws InterruptedException If the user shuts down or reboots the computer the coroutine is suspended, * InterruptedException will be thrown. This exception must not be caught or * intercepted, or the computer will leak memory and end up in a broken state.w * @see IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[]) + * @deprecated Use {@link #callMethod(ICallContext, int, Object[])} instead. */ @Nullable + @Deprecated Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException; + + /** + * Called when a user calls one of the methods that this object implements. This works the same as + * {@link IPeripheral#callMethod(IComputerAccess, ICallContext, int, Object[])}}. See that method for detailed + * documentation. + * + * @param context The context of the current call. + * @param method An integer identifying which of the methods from getMethodNames() the computercraft + * wishes to call. The integer indicates the index into the getMethodNames() table + * that corresponds to the string passed into peripheral.call() + * @param arguments The arguments for this method. See {@link IPeripheral#callMethod(IComputerAccess, ICallContext, int, Object[])} + * for the possible values and conversion rules. + * @return The result of calling this method. Use {@link MethodResult#empty()} to return nothing or + * {@link MethodResult#of(Object...)} to return several values. + * @throws LuaException If the task could not be queued, or if the task threw an exception. + * @see IPeripheral#callMethod(IComputerAccess, ICallContext, int, Object[]) + * @see MethodResult + */ + @Nonnull + @SuppressWarnings({ "deprecation" }) + default MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException + { + return MethodResult.withLuaContext( lua -> callMethod( lua, method, arguments ) ); + } } diff --git a/src/main/java/dan200/computercraft/api/lua/LuaContextResultEvaluator.java b/src/main/java/dan200/computercraft/api/lua/LuaContextResultEvaluator.java new file mode 100644 index 0000000000..22f64f52ae --- /dev/null +++ b/src/main/java/dan200/computercraft/api/lua/LuaContextResultEvaluator.java @@ -0,0 +1,105 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.lua; + +import javax.annotation.Nonnull; +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * Evaluates {@link MethodResult}s within a {@link ILuaContext}. + * + * @see MethodResult#evaluate(ILuaContext) + * @see MethodResult#withLuaContext(ILuaContextTask) + * @deprecated This should not be used except to interface between the two call call systems. + */ +@Deprecated +class LuaContextResultEvaluator +{ + @Deprecated + public static Object[] evaluate( @Nonnull ILuaContext context, @Nonnull MethodResult future ) throws LuaException, InterruptedException + { + Deque callbacks = null; + while( true ) + { + if( future instanceof MethodResult.AndThen ) + { + MethodResult.AndThen then = ((MethodResult.AndThen) future); + + // Thens are "unwrapped", being pushed onto a stack + if( callbacks == null ) callbacks = new ArrayDeque<>(); + callbacks.addLast( then.getCallback() ); + + future = then.getPrevious(); + if( future == null ) throw new NullPointerException( "Null result from " + then.getCallback() ); + } + else if( future instanceof MethodResult.Immediate ) + { + Object[] values = ((MethodResult.Immediate) future).getResult(); + + // Immediate values values will attempt to call the previous "then", or return if nothing + // else needs to be done. + ILuaFunction callback = callbacks == null ? null : callbacks.pollLast(); + if( callback == null ) return values; + + future = callback.call( values ); + if( future == null ) throw new NullPointerException( "Null result from " + callback ); + } + else if( future instanceof MethodResult.OnEvent ) + { + MethodResult.OnEvent onEvent = (MethodResult.OnEvent) future; + + // Poll for an event, and then call the previous "then" or return if nothing else needs + // to be done. + Object[] values = onEvent.isRaw() ? context.pullEventRaw( onEvent.getFilter() ) : context.pullEvent( onEvent.getFilter() ); + + ILuaFunction callback = callbacks == null ? null : callbacks.pollLast(); + if( callback == null ) return values; + + future = callback.call( values ); + if( future == null ) throw new NullPointerException( "Null result from " + callback ); + } + else if( future instanceof MethodResult.OnMainThread ) + { + MethodResult.OnMainThread onMainThread = (MethodResult.OnMainThread) future; + + // Evaluate our task on the main thread and mark it as the next future to evaluate. + Reference temporary = new Reference(); + context.executeMainThreadTask( () -> { + temporary.value = onMainThread.getTask().execute(); + return null; + } ); + + future = temporary.value; + if( future == null ) throw new NullPointerException( "Null result from " + onMainThread.getTask() ); + } + else if( future instanceof MethodResult.WithLuaContext ) + { + MethodResult.WithLuaContext withContext = (MethodResult.WithLuaContext) future; + + // Run the task, and then call the previous "then" or return if nothing else + // needs to be done. + Object[] values = withContext.getConsumer().execute( context ); + + ILuaFunction callback = callbacks == null ? null : callbacks.pollLast(); + if( callback == null ) return values; + + future = callback.call( values ); + if( future == null ) throw new NullPointerException( "Null result from " + callback ); + } + else + { + throw new IllegalStateException( "Unknown MethodResult " + future ); + } + } + } + + private static class Reference + { + MethodResult value; + } +} diff --git a/src/main/java/dan200/computercraft/api/lua/MethodResult.java b/src/main/java/dan200/computercraft/api/lua/MethodResult.java new file mode 100644 index 0000000000..209c153648 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/lua/MethodResult.java @@ -0,0 +1,344 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ + +package dan200.computercraft.api.lua; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.ListenableFuture; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.peripheral.IPeripheral; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; + +/** + * The result of calling a method, such as {@link ILuaObject#callMethod(ICallContext, int, Object[])} or + * {@link IPeripheral#callMethod(IComputerAccess, ICallContext, int, Object[])}. + * + * This is non-dissimilar to a promise or {@link ListenableFuture}. One can either return an immediate value through + * {@link #of(Object...)}, wait for an external action with {@link #onMainThread(ILuaCallable)} or {@link #pullEvent()} + * and then act on the result of either of those by using {@link #then(ILuaFunction)}. + */ +public abstract class MethodResult +{ + private static MethodResult empty; + + MethodResult() + { + } + + /** + * A result which returns immediately with no value. + * + * Use {@link #of(Object...)} if you need to return one or more values. + * + * @return The empty method result. + * @see #of(Object...) + */ + @Nonnull + public static MethodResult empty() + { + if( empty == null ) empty = new Immediate( null ); + return empty; + } + + /** + * A result which returns several values. + * + * @param result The values to return, this may be {@code null}. {@link Number}s, {@link String}s, {@link Boolean}s, + * {@link Map}s, {@link ILuaObject}s, and {@code null} be converted to their corresponding lua type. + * All other types will be converted to nil. + * @return A result which will return these values when evaluated. + * @see #empty() + */ + @Nonnull + public static MethodResult of( Object... result ) + { + return result == null ? empty() : new Immediate( result ); + } + + /** + * Wait for an event to occur on the computer, suspending the coroutine until it arises. This method is equivalent + * to {@code os.pullEvent()} in Lua. + * + * Normally you'll wish to consume the event using {@link #then(ILuaFunction)}. This can be done slightly more + * easily with {@link #pullEvent(ILuaFunction)}. + * + * If you want to listen to a specific event, it's easier to use {@link #pullEvent(String)} rather than + * running until the desired event is found. + * + * @return The constructed method result. This evaluates to the name of the event that occurred, and any event + * parameters. + * @see #pullEvent(ILuaFunction) + * @see #pullEvent(String) + */ + @Nonnull + public static MethodResult pullEvent() + { + return new OnEvent( false, null ); + } + + /** + * Wait for the specified event to occur on the computer, suspending the coroutine until it arises. This method is + * equivalent to {@code os.pullEvent(event)} in Lua. + * + * Normally you'll wish to consume the event using {@link #then(ILuaFunction)}. This can be done slightly more + * easily with {@link #pullEvent(String, ILuaFunction)}. + * + * @return The constructed method result. This evaluates to the name of the event that occurred, and any event + * parameters. + * @see #pullEvent(String, ILuaFunction) + * @see #pullEvent() + */ + @Nonnull + public static MethodResult pullEvent( @Nonnull String event ) + { + Preconditions.checkNotNull( event, "event cannot be null" ); + return new OnEvent( false, event ); + } + + /** + * Wait for an event to occur on the computer, suspending the coroutine until it arises. This method to + * {@link #pullEvent()} and {@link #then(ILuaFunction)}. + * + * If you want to listen to a specific event, it's easier to use {@link #pullEvent(String, ILuaFunction)} rather + * than running until the desired event is found. + * + * @return The constructed method result. This evaluates to the result of the {@code callback}. + * @see #pullEvent() + * @see #pullEvent(String, ILuaFunction) + */ + @Nonnull + public static MethodResult pullEvent( @Nonnull ILuaFunction callback ) + { + Preconditions.checkNotNull( callback, "callback cannot be null" ); + return new OnEvent( false, null ).then( callback ); + } + + /** + * Wait for the specified event to occur on the computer, suspending the coroutine until it arises. This method to + * {@link #pullEvent(String)} and {@link #then(ILuaFunction)}. + * + * @return The constructed method result. This evaluates to the result of the {@code callback}. + * @see #pullEvent(String) + * @see #pullEvent(ILuaFunction) + */ + @Nonnull + public static MethodResult pullEvent( @Nullable String filter, @Nonnull ILuaFunction callback ) + { + Preconditions.checkNotNull( callback, "callback cannot be null" ); + return new OnEvent( false, filter ).then( callback ); + } + + /** + * The same as {@link #pullEvent()}, except {@code terminated} events are also passed to the callback, instead of + * throwing an error. Only use this if you want to prevent program termination, which is not recommended. + * + * @return The constructed method result. This evaluates to the name of the event that occurred, and any event + * parameters. + */ + @Nonnull + public static MethodResult pullEventRaw() + { + return new OnEvent( true, null ); + } + + /** + * The same as {@link #pullEvent(String)}, except {@code terminated} events are also passed to the callback, instead + * of throwing an error. Only use this if you want to prevent program termination, which is not recommended. + * + * @return The constructed method result. This evaluates to the name of the event that occurred, and any event + * parameters. + */ + @Nonnull + public static MethodResult pullEventRaw( @Nonnull String event ) + { + return new OnEvent( true, event ); + } + + /** + * The same as {@link #pullEvent(ILuaFunction)}, except {@code terminated} events are also passed to the callback, + * instead of throwing an error. Only use this if you want to prevent program termination, which is not recommended. + * + * @return The constructed method result. This evaluates to the result of the {@code callback}. + */ + @Nonnull + public static MethodResult pullEventRaw( @Nonnull ILuaFunction callback ) + { + Preconditions.checkNotNull( callback, "callback cannot be null" ); + return new OnEvent( true, null ).then( callback ); + } + + /** + * The same as {@link #pullEvent(String, ILuaFunction)}, except {@code terminated} events are also passed to the + * callback, instead of throwing an error. Only use this if you want to prevent program termination, which is not + * recommended. + * + * @return The constructed method result. This evaluates to the result of the {@code callback}. + */ + @Nonnull + public static MethodResult pullEventRaw( @Nullable String filter, @Nonnull ILuaFunction callback ) + { + Preconditions.checkNotNull( callback, "callback cannot be null" ); + return new OnEvent( true, filter ).then( callback ); + } + + /** + * Queue a task to be executed on the main server thread at the beginning of next tick, waiting for it to complete. + * This should be used when you need to interact with the world in a thread-safe manner. + * + * @param callback The task to execute on the server thread. + * @return The constructed method result, which evaluates to the result of the {@code callback}. + */ + @Nonnull + public static MethodResult onMainThread( @Nonnull ILuaCallable callback ) + { + Preconditions.checkNotNull( callback, "callback cannot be null" ); + return new OnMainThread( callback ); + } + + /** + * Consume the result of this {@link MethodResult} and return another result. + * + * Note this does NOT modify the current method result, rather returning a new (wrapped) one. You must return the + * result of this call if you wish to use it. + * + * @param callback The function which consumes the provided values. + * @return The constructed method result. + */ + @Nonnull + public final MethodResult then( @Nonnull ILuaFunction callback ) + { + Preconditions.checkNotNull( callback, "callback cannot be null" ); + return new AndThen( this, callback ); + } + + /** + * Execute a blocking task within a {@link ILuaContext} and return its result. + * + * @param consumer The task to execute with the provided Lua context. + * @return The constructed method result. + * @see #evaluate(ILuaContext) + * @deprecated This should not be used except to interface between the two call systems. + */ + @Deprecated + public static MethodResult withLuaContext( @Nonnull ILuaContextTask consumer ) + { + Preconditions.checkNotNull( consumer, "consumer cannot be null" ); + return new WithLuaContext( consumer ); + } + + /** + * Evaluate this result task using {@link ILuaContext} and return its result. + * + * @param context The context to execute with. + * @return The resulting values. + * @see #withLuaContext(ILuaContextTask) + * @deprecated This should not be used except to interface between the two call systems. + */ + @Deprecated + public final Object[] evaluate( @Nonnull ILuaContext context ) throws LuaException, InterruptedException + { + return LuaContextResultEvaluator.evaluate( context, this ); + } + + public static class Immediate extends MethodResult + { + @Nullable + private final Object[] values; + + @Nullable + private Immediate( Object[] values ) + { + this.values = values; + } + + public Object[] getResult() + { + return values; + } + } + + public static class OnEvent extends MethodResult + { + private final boolean raw; + private final String filter; + + private OnEvent( boolean raw, String filter ) + { + this.raw = raw; + this.filter = filter; + } + + public boolean isRaw() + { + return raw; + } + + @Nullable + public String getFilter() + { + return filter; + } + } + + public static class OnMainThread extends MethodResult + { + private final ILuaCallable task; + + public OnMainThread( ILuaCallable task ) + { + this.task = task; + } + + @Nonnull + public ILuaCallable getTask() + { + return task; + } + } + + public static class AndThen extends MethodResult + { + private final MethodResult previous; + private final ILuaFunction callback; + + private AndThen( MethodResult previous, ILuaFunction callback ) + { + this.previous = previous; + this.callback = callback; + } + + @Nonnull + public MethodResult getPrevious() + { + return previous; + } + + @Nonnull + public ILuaFunction getCallback() + { + return callback; + } + } + + public static class WithLuaContext extends MethodResult + { + private final ILuaContextTask consumer; + + private WithLuaContext( ILuaContextTask consumer ) + { + this.consumer = consumer; + } + + @Nonnull + public ILuaContextTask getConsumer() + { + return consumer; + } + } +} diff --git a/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java b/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java index af2ecc9648..042ace17ed 100644 --- a/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java +++ b/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java @@ -1,13 +1,15 @@ /* * This file is part of the public ComputerCraft API - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only. + * Copyright Daniel Ratcliffe, 2011-2018. This API may be redistributed unmodified and in full only. * For help using the API, and posting your mods, visit the forums at computercraft.info. */ package dan200.computercraft.api.peripheral; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -70,10 +72,46 @@ public interface IPeripheral * InterruptedException will be thrown. This exception must not be caught or * intercepted, or the computer will leak memory and end up in a broken state. * @see #getMethodNames + * @deprecated Use {@link #callMethod(IComputerAccess, ICallContext, int, Object[])} instead. */ @Nullable + @Deprecated Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException; + /** + * This is called when a lua program on an attached computer calls {@code peripheral.call()} with + * one of the methods exposed by {@link #getMethodNames()}. + * + * Be aware that this will be called from the ComputerCraft Lua thread, and must be thread-safe + * when interacting with Minecraft objects. + * + * @param computer The interface to the computer that is making the call. Remember that multiple + * computers can be attached to a peripheral at once. + * @param context The context of the current call. + * @param method An integer identifying which of the methods from getMethodNames() the computercraft + * wishes to call. The integer indicates the index into the getMethodNames() table + * that corresponds to the string passed into peripheral.call() + * @param arguments An array of objects, representing the arguments passed into {@code peripheral.call()}.
+ * Lua values of type "string" will be represented by Object type String.
+ * Lua values of type "number" will be represented by Object type Double.
+ * Lua values of type "boolean" will be represented by Object type Boolean.
+ * Lua values of type "table" will be represented by Object type Map.
+ * Lua values of any other type will be represented by a null object.
+ * This array will be empty if no arguments are passed. + * @return The result of calling this method. Use {@link MethodResult#empty()} to return nothing or + * {@link MethodResult#of(Object...)} to return several values. + * @throws LuaException If you throw any exception from this function, a lua error will be raised with the + * same message as your exception. Use this to throw appropriate errors if the wrong + * arguments are supplied to your method. + * @see #getMethodNames + */ + @Nonnull + @SuppressWarnings({ "deprecation" }) + default MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException + { + return MethodResult.withLuaContext( lua -> callMethod( computer, lua, method, arguments ) ); + } + /** * Is called when canAttachToSide has returned true, and a computer is attaching to the peripheral. * diff --git a/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java b/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java index 940adb89ad..d800872fbb 100644 --- a/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java +++ b/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java @@ -8,6 +8,7 @@ import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.peripheral.IPeripheral; import net.minecraft.inventory.IInventory; import net.minecraft.nbt.NBTTagCompound; @@ -148,7 +149,7 @@ public interface ITurtleAccess * Get the inventory of this turtle as an {@link IItemHandlerModifiable}. * * @return This turtle's inventory - * @see #getInventory() + * @see #getInventory() * @see IItemHandlerModifiable * @see net.minecraftforge.items.CapabilityItemHandler#ITEM_HANDLER_CAPABILITY */ @@ -229,10 +230,30 @@ public interface ITurtleAccess * intercepted, or the computer will leak memory and end up in a broken state. * @see ITurtleCommand * @see ILuaContext#pullEvent(String) + * @deprecated Use {@link #executeCommand(ITurtleCommand)} instead. */ @Nonnull + @Deprecated Object[] executeCommand( @Nonnull ILuaContext context, @Nonnull ITurtleCommand command ) throws LuaException, InterruptedException; + /** + * Adds a custom command to the turtles command queue. Unlike peripheral methods, these custom commands will be + * executed on the main thread, so are guaranteed to be able to access Minecraft objects safely, and will be queued + * up with the turtles standard movement and tool commands. + * + * An issued command will return an unique integer, which will be supplied as a parameter to a "turtle_response" + * event issued to the turtle after the command has completed. Look at the Lua source code for "rom/apis/turtle" for + * how to build a Lua wrapper around this functionality. + * + * @param command An object which will execute the custom command when its point in the queue is reached + * @return The constructed method result. This evaluates to the result of the provided {@code command}. + * @throws UnsupportedOperationException When attempting to execute a command on the client side. + * @see ITurtleCommand + * @see MethodResult#pullEvent(String) + */ + @Nonnull + MethodResult executeCommand( @Nonnull ITurtleCommand command ); + /** * Start playing a specific animation. This will prevent other turtle commands from executing until * it is finished. diff --git a/src/main/java/dan200/computercraft/core/apis/BitAPI.java b/src/main/java/dan200/computercraft/core/apis/BitAPI.java index 8d4021b2c4..f88a555e2e 100644 --- a/src/main/java/dan200/computercraft/core/apis/BitAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/BitAPI.java @@ -6,10 +6,13 @@ package dan200.computercraft.core.apis; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static dan200.computercraft.core.apis.ArgumentHelper.getInt; @@ -62,8 +65,9 @@ public String[] getMethodNames() { }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { int ret = 0; switch(method) { @@ -89,7 +93,15 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O ret = getInt( args, 0 ) >>> getInt( args, 1 ); break; } - - return new Object[]{ ret&0xFFFFFFFFL }; + + return MethodResult.of( ret & 0xFFFFFFFFL ); + } + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); } } diff --git a/src/main/java/dan200/computercraft/core/apis/BufferAPI.java b/src/main/java/dan200/computercraft/core/apis/BufferAPI.java index 3035ef763e..3046d09058 100644 --- a/src/main/java/dan200/computercraft/core/apis/BufferAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/BufferAPI.java @@ -6,12 +6,11 @@ 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.api.lua.*; import dan200.computercraft.core.terminal.TextBuffer; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static dan200.computercraft.core.apis.ArgumentHelper.getString; import static dan200.computercraft.core.apis.ArgumentHelper.optInt; @@ -40,27 +39,28 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException { switch( method ) { case 0: { // len - return new Object[] { m_buffer.length() }; + return MethodResult.of( m_buffer.length() ); } case 1: { // tostring - return new Object[] { m_buffer.toString() }; + return MethodResult.of( m_buffer.toString() ); } case 2: { // read int start = optInt( arguments, 0, 0 ); int end = optInt( arguments, 1, m_buffer.length() ); - return new Object[] { m_buffer.read( start, end ) }; + return MethodResult.of( m_buffer.read( start, end ) ); } case 3: { @@ -69,7 +69,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O int start = optInt( arguments, 1, 0 ); int end = optInt( arguments, 2, start + text.length() ); m_buffer.write( text, start, end ); - return null; + return MethodResult.empty(); } case 4: { @@ -78,14 +78,22 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O int start = optInt( arguments, 1, 0 ); int end = optInt( arguments, 2, m_buffer.length() ); m_buffer.fill( text, start, end ); - return null; + return MethodResult.empty(); } default: { - return null; + return MethodResult.empty(); } } } + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); + } } public BufferAPI( IAPIEnvironment _env ) @@ -124,8 +132,9 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException { switch( method ) { @@ -138,12 +147,20 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O throw ArgumentHelper.badArgument( 1, "positive number", Integer.toString( repetitions ) ); } TextBuffer buffer = new TextBuffer( text, repetitions ); - return new Object[] { new BufferLuaObject( buffer ) }; + return MethodResult.of( new BufferLuaObject( buffer ) ); } default: { - return null; + return MethodResult.empty(); } } } + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); + } } diff --git a/src/main/java/dan200/computercraft/core/apis/FSAPI.java b/src/main/java/dan200/computercraft/core/apis/FSAPI.java index 7cf8bdc309..6dd1a45632 100644 --- a/src/main/java/dan200/computercraft/core/apis/FSAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/FSAPI.java @@ -6,8 +6,10 @@ package dan200.computercraft.core.apis; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.core.apis.handles.BinaryInputHandle; import dan200.computercraft.core.apis.handles.BinaryOutputHandle; import dan200.computercraft.core.apis.handles.EncodedInputHandle; @@ -16,6 +18,7 @@ import dan200.computercraft.core.filesystem.FileSystemException; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; @@ -83,8 +86,9 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { switch( method ) { @@ -98,7 +102,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O for(int i=0; i= 0 ) { - return new Object[]{ freeSpace }; + return MethodResult.of( freeSpace ); } - return new Object[]{ "unlimited" }; + return MethodResult.of( "unlimited" ); } catch( FileSystemException e ) { throw new LuaException( e.getMessage() ); } @@ -297,7 +301,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O for(int i=0; i 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( () -> - { - 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 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 headers1 = m_responseHeaders = new HashMap<>(); - for (Map.Entry> header : connection.getHeaderFields().entrySet()) { - headers1.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 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 m_responseHeaders; -} diff --git a/src/main/java/dan200/computercraft/core/apis/OSAPI.java b/src/main/java/dan200/computercraft/core/apis/OSAPI.java index 7ec4b6878c..5e22877b52 100644 --- a/src/main/java/dan200/computercraft/core/apis/OSAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/OSAPI.java @@ -6,11 +6,14 @@ package dan200.computercraft.core.apis; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.shared.util.StringUtil; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.*; import static dan200.computercraft.core.apis.ArgumentHelper.*; @@ -220,7 +223,8 @@ private long getEpochForCalendar(Calendar c) } @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException + @Nonnull + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { switch( method ) { @@ -228,7 +232,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { // queueEvent queueLuaEvent( getString( args, 0 ), trimArray( args, 1 ) ); - return null; + return MethodResult.empty(); } case 1: { @@ -237,7 +241,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O synchronized( m_timers ) { m_timers.put( m_nextTimerToken, new Timer( (int)Math.round( timer / 0.05 ) ) ); - return new Object[] { m_nextTimerToken++ }; + return MethodResult.of( m_nextTimerToken++ ); } } case 2: @@ -252,33 +256,33 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { int day = (time > m_time) ? m_day : (m_day + 1); m_alarms.put( m_nextAlarmToken, new Alarm( time, day ) ); - return new Object[] { m_nextAlarmToken++ }; + return MethodResult.of( m_nextAlarmToken++ ); } } case 3: { // shutdown m_apiEnvironment.shutdown(); - return null; + return MethodResult.empty(); } case 4: { // reboot m_apiEnvironment.reboot(); - return null; + return MethodResult.empty(); } case 5: case 6: { // computerID/getComputerID - return new Object[] { getComputerID() }; + return MethodResult.of( getComputerID() ); } case 7: { // setComputerLabel String label = optString( args, 0, null ); m_apiEnvironment.setLabel( StringUtil.normaliseLabel( label ) ); - return null; + return MethodResult.empty(); } case 8: case 9: @@ -287,16 +291,16 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O String label = m_apiEnvironment.getLabel(); if( label != null ) { - return new Object[] { label }; + return MethodResult.of( label ); } - return null; + return MethodResult.empty(); } case 10: { // clock synchronized( m_timers ) { - return new Object[] { (double)m_clock * 0.05 }; + return MethodResult.of( (double)m_clock * 0.05 ); } } case 11: @@ -309,19 +313,19 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { // Get Hour of day (UTC) Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); - return new Object[] { getTimeForCalendar( c ) }; + return MethodResult.of( getTimeForCalendar( c ) ); } case "local": { // Get Hour of day (local time) Calendar c = Calendar.getInstance(); - return new Object[] { getTimeForCalendar( c ) }; + return MethodResult.of( getTimeForCalendar( c ) ); } case "ingame": // Get ingame hour synchronized( m_alarms ) { - return new Object[] { m_time }; + return MethodResult.of( m_time ); } default: throw new LuaException( "Unsupported operation" ); @@ -337,19 +341,19 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { // Get numbers of days since 1970-01-01 (utc) Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); - return new Object[] { getDayForCalendar( c ) }; + return MethodResult.of( getDayForCalendar( c ) ); } case "local": { // Get numbers of days since 1970-01-01 (local time) Calendar c = Calendar.getInstance(); - return new Object[] { getDayForCalendar( c ) }; + return MethodResult.of( getDayForCalendar( c ) ); } case "ingame": // Get game day synchronized( m_alarms ) { - return new Object[] { m_day }; + return MethodResult.of( m_day ); } default: throw new LuaException( "Unsupported operation" ); @@ -366,7 +370,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O m_timers.remove( token ); } } - return null; + return MethodResult.empty(); } case 14: { @@ -379,7 +383,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O m_alarms.remove( token ); } } - return null; + return MethodResult.empty(); } case 15: { @@ -391,21 +395,21 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { // Get utc epoch Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); - return new Object[] { getEpochForCalendar( c ) }; + return MethodResult.of( getEpochForCalendar( c ) ); } case "local": { // Get local epoch Calendar c = Calendar.getInstance(); - return new Object[] { getEpochForCalendar( c ) }; + return MethodResult.of( getEpochForCalendar( c ) ); } case "ingame": // Get in-game epoch synchronized( m_alarms ) { - return new Object[] { + return MethodResult.of( m_day * 86400000 + (int) (m_time * 3600000.0f) - }; + ); } default: throw new LuaException( "Unsupported operation" ); @@ -413,11 +417,18 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O } default: { - return null; + return MethodResult.empty(); } } } + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); + } // Private methods private void queueLuaEvent( String event, Object[] args ) diff --git a/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java b/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java index 390bd5563e..4d7bfc16d2 100644 --- a/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java @@ -8,8 +8,10 @@ import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IWritableMount; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.computer.Computer; @@ -19,6 +21,7 @@ import dan200.computercraft.core.filesystem.FileSystemException; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.*; import static dan200.computercraft.core.apis.ArgumentHelper.getString; @@ -98,7 +101,7 @@ public synchronized void detach() m_mounts.clear(); } - public Object[] call( ILuaContext context, String methodName, Object[] arguments ) throws LuaException, InterruptedException + public MethodResult call( ICallContext context, String methodName, Object[] arguments ) throws LuaException { int method = -1; synchronized( this ) @@ -388,8 +391,9 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { switch( method ) { @@ -409,7 +413,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O } } } - return new Object[] { present }; + return MethodResult.of( present ); } case 1: { @@ -428,10 +432,10 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O } if( type != null ) { - return new Object[] { type }; + return MethodResult.of( type ); } } - return null; + return MethodResult.empty(); } case 2: { @@ -455,9 +459,9 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O for(int i=0; i 0 }; + return MethodResult.of( m_environment.getOutput( side ) > 0 ); } case 3: { // getInput int side = parseSide( args ); - return new Object[] { m_environment.getInput( side ) > 0 }; + return MethodResult.of( m_environment.getInput( side ) > 0 ); } case 4: { @@ -111,19 +115,19 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O int side = parseSide( args ); int output = getInt( args, 1 ); m_environment.setBundledOutput( side, output ); - return null; + return MethodResult.empty(); } case 5: { // getBundledOutput int side = parseSide( args ); - return new Object[] { m_environment.getBundledOutput( side ) }; + return MethodResult.of( m_environment.getBundledOutput( side ) ); } case 6: { // getBundledInput int side = parseSide( args ); - return new Object[] { m_environment.getBundledInput( side ) }; + return MethodResult.of( m_environment.getBundledInput( side ) ); } case 7: { @@ -131,7 +135,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O int side = parseSide( args ); int mask = getInt( args, 1 ); int input = m_environment.getBundledInput( side ); - return new Object[] { ((input & mask) == mask) }; + return MethodResult.of( ((input & mask) == mask) ); } case 8: case 9: @@ -144,28 +148,36 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O throw new LuaException( "Expected number in range 0-15" ); } m_environment.setOutput( side, output ); - return null; + return MethodResult.empty(); } case 10: case 11: { // getAnalogOutput/getAnalogueOutput int side = parseSide( args ); - return new Object[] { m_environment.getOutput( side ) }; + return MethodResult.of( m_environment.getOutput( side ) ); } case 12: case 13: { // getAnalogInput/getAnalogueInput int side = parseSide( args ); - return new Object[] { m_environment.getInput( side ) }; + return MethodResult.of( m_environment.getInput( side ) ); } default: { - return null; + return MethodResult.empty(); } } } + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); + } private int parseSide( Object[] args ) throws LuaException { diff --git a/src/main/java/dan200/computercraft/core/apis/TermAPI.java b/src/main/java/dan200/computercraft/core/apis/TermAPI.java index 38a64b059d..3e596ea311 100644 --- a/src/main/java/dan200/computercraft/core/apis/TermAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/TermAPI.java @@ -6,14 +6,17 @@ package dan200.computercraft.core.apis; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.core.computer.IComputerEnvironment; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.util.Palette; import org.apache.commons.lang3.ArrayUtils; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static dan200.computercraft.core.apis.ArgumentHelper.*; @@ -97,11 +100,9 @@ public static int parseColour( Object[] args ) throws LuaException return colour; } - public static Object[] encodeColour( int colour ) throws LuaException + public static MethodResult encodeColour( int colour ) { - return new Object[] { - 1 << colour - }; + return MethodResult.of( 1 << colour ); } public static void setColour( Terminal terminal, int colour, double r, double g, double b ) @@ -113,8 +114,9 @@ public static void setColour( Terminal terminal, int colour, double r, double g, } } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { switch( method ) { @@ -133,7 +135,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O m_terminal.write( text ); m_terminal.setCursorPos( m_terminal.getCursorX() + text.length(), m_terminal.getCursorY() ); } - return null; + return MethodResult.empty(); } case 1: { @@ -143,7 +145,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { m_terminal.scroll(y); } - return null; + return MethodResult.empty(); } case 2: { @@ -154,7 +156,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { m_terminal.setCursorPos( x, y ); } - return null; + return MethodResult.empty(); } case 3: { @@ -164,7 +166,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { m_terminal.setCursorBlink( b ); } - return null; + return MethodResult.empty(); } case 4: { @@ -175,7 +177,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O x = m_terminal.getCursorX(); y = m_terminal.getCursorY(); } - return new Object[] { x + 1, y + 1 }; + return MethodResult.of( x + 1, y + 1 ); } case 5: { @@ -186,7 +188,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O width = m_terminal.getWidth(); height = m_terminal.getHeight(); } - return new Object[] { width, height }; + return MethodResult.of( width, height ); } case 6: { @@ -195,7 +197,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { m_terminal.clear(); } - return null; + return MethodResult.empty(); } case 7: { @@ -204,7 +206,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { m_terminal.clearLine(); } - return null; + return MethodResult.empty(); } case 8: case 9: @@ -215,7 +217,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { m_terminal.setTextColour( colour ); } - return null; + return MethodResult.empty(); } case 10: case 11: @@ -226,13 +228,13 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { m_terminal.setBackgroundColour( colour ); } - return null; + return MethodResult.empty(); } case 12: case 13: { // isColour/isColor - return new Object[] { m_environment.isColour() }; + return MethodResult.of( m_environment.isColour() ); } case 14: case 15: @@ -262,7 +264,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O m_terminal.blit( text, textColour, backgroundColour ); m_terminal.setCursorPos( m_terminal.getCursorX() + text.length(), m_terminal.getCursorY() ); } - return null; + return MethodResult.empty(); } case 19: case 20: @@ -282,7 +284,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O double b = getReal( args, 3 ); setColour( m_terminal, colour, r, g, b ); } - return null; + return MethodResult.empty(); } case 21: case 22: @@ -293,18 +295,26 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { if ( m_terminal.getPalette() != null ) { - return ArrayUtils.toObject( m_terminal.getPalette().getColour( colour ) ); + return MethodResult.of( (Object[]) ArrayUtils.toObject( m_terminal.getPalette().getColour( colour ) ) ); } } - return null; + return MethodResult.empty(); } default: { - return null; + return MethodResult.empty(); } } } - + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); + } + private static int getHighestBit( int group ) { int bit = 0; diff --git a/src/main/java/dan200/computercraft/core/apis/handles/BinaryInputHandle.java b/src/main/java/dan200/computercraft/core/apis/handles/BinaryInputHandle.java index 25cdaca5e7..c0877dc095 100644 --- a/src/main/java/dan200/computercraft/core/apis/handles/BinaryInputHandle.java +++ b/src/main/java/dan200/computercraft/core/apis/handles/BinaryInputHandle.java @@ -1,8 +1,9 @@ package dan200.computercraft.core.apis.handles; import com.google.common.io.ByteStreams; -import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import javax.annotation.Nonnull; import java.io.IOException; @@ -32,8 +33,9 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { switch( method ) { @@ -52,19 +54,19 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O byte[] bytes = new byte[ count ]; count = m_stream.read( bytes ); - if( count < 0 ) return null; + if( count < 0 ) return MethodResult.empty(); if( count < bytes.length ) bytes = Arrays.copyOf( bytes, count ); - return new Object[] { bytes }; + return MethodResult.of( bytes ); } else { int b = m_stream.read(); - return b == -1 ? null : new Object[] { b }; + return b == -1 ? MethodResult.empty() : MethodResult.of( b ); } } catch( IOException e ) { - return null; + return MethodResult.empty(); } case 1: // readAll @@ -72,18 +74,18 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O try { byte[] out = ByteStreams.toByteArray( m_stream ); - return out == null ? null : new Object[] { out }; + return out == null ? MethodResult.empty() : MethodResult.of( out ); } catch( IOException e ) { - return null; + return MethodResult.empty(); } case 2: //close close(); - return null; + return MethodResult.empty(); default: - return null; + return MethodResult.empty(); } } } diff --git a/src/main/java/dan200/computercraft/core/apis/handles/BinaryOutputHandle.java b/src/main/java/dan200/computercraft/core/apis/handles/BinaryOutputHandle.java index ae90b677d6..f4c05deef1 100644 --- a/src/main/java/dan200/computercraft/core/apis/handles/BinaryOutputHandle.java +++ b/src/main/java/dan200/computercraft/core/apis/handles/BinaryOutputHandle.java @@ -1,7 +1,8 @@ package dan200.computercraft.core.apis.handles; -import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.core.apis.ArgumentHelper; import dan200.computercraft.shared.util.StringUtil; @@ -30,8 +31,9 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { switch( method ) { @@ -54,7 +56,7 @@ else if( args.length > 0 && args[ 0 ] instanceof String ) { throw ArgumentHelper.badArgument( 0, "string or number", args.length > 0 ? args[ 0 ] : null ); } - return null; + return MethodResult.empty(); } catch( IOException e ) { @@ -66,18 +68,18 @@ else if( args.length > 0 && args[ 0 ] instanceof String ) try { m_writer.flush(); - return null; + return MethodResult.empty(); } catch( IOException e ) { - return null; + return MethodResult.empty(); } case 2: //close close(); - return null; + return MethodResult.empty(); default: - return null; + return MethodResult.empty(); } } } diff --git a/src/main/java/dan200/computercraft/core/apis/handles/EncodedInputHandle.java b/src/main/java/dan200/computercraft/core/apis/handles/EncodedInputHandle.java index 9f722f797b..5d2f7fdf34 100644 --- a/src/main/java/dan200/computercraft/core/apis/handles/EncodedInputHandle.java +++ b/src/main/java/dan200/computercraft/core/apis/handles/EncodedInputHandle.java @@ -1,12 +1,13 @@ package dan200.computercraft.core.apis.handles; -import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import javax.annotation.Nonnull; import java.io.*; -import static dan200.computercraft.core.apis.ArgumentHelper.*; +import static dan200.computercraft.core.apis.ArgumentHelper.optInt; public class EncodedInputHandle extends HandleGeneric { @@ -55,8 +56,9 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { switch( method ) { @@ -68,16 +70,16 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O String line = m_reader.readLine(); if( line != null ) { - return new Object[] { line }; + return MethodResult.of( line ); } else { - return null; + return MethodResult.empty(); } } catch( IOException e ) { - return null; + return MethodResult.empty(); } case 1: // readAll @@ -95,16 +97,16 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O result.append( "\n" ); } } - return new Object[] { result.toString() }; + return MethodResult.of( result.toString() ); } catch( IOException e ) { - return null; + return MethodResult.empty(); } case 2: // close close(); - return null; + return MethodResult.empty(); case 3: // read checkOpen(); @@ -117,16 +119,16 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O } char[] bytes = new char[ count ]; count = m_reader.read( bytes ); - if( count < 0 ) return null; + if( count < 0 ) return MethodResult.empty(); String str = new String( bytes, 0, count ); - return new Object[] { str }; + return MethodResult.of( str ); } catch( IOException e ) { - return null; + return MethodResult.empty(); } default: - return null; + return MethodResult.empty(); } } } diff --git a/src/main/java/dan200/computercraft/core/apis/handles/EncodedOutputHandle.java b/src/main/java/dan200/computercraft/core/apis/handles/EncodedOutputHandle.java index 89eb6dd2f3..125f0faf60 100644 --- a/src/main/java/dan200/computercraft/core/apis/handles/EncodedOutputHandle.java +++ b/src/main/java/dan200/computercraft/core/apis/handles/EncodedOutputHandle.java @@ -1,7 +1,8 @@ package dan200.computercraft.core.apis.handles; -import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import javax.annotation.Nonnull; import java.io.*; @@ -53,8 +54,9 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { switch( method ) { @@ -74,7 +76,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O try { m_writer.write( text, 0, text.length() ); - return null; + return MethodResult.empty(); } catch( IOException e ) { @@ -98,7 +100,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { m_writer.write( text, 0, text.length() ); m_writer.newLine(); - return null; + return MethodResult.empty(); } catch( IOException e ) { @@ -111,18 +113,18 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O try { m_writer.flush(); - return null; + return MethodResult.empty(); } catch( IOException e ) { - return null; + return MethodResult.empty(); } case 3: // close close(); - return null; + return MethodResult.empty(); default: - return null; + return MethodResult.empty(); } } } diff --git a/src/main/java/dan200/computercraft/core/apis/handles/HandleGeneric.java b/src/main/java/dan200/computercraft/core/apis/handles/HandleGeneric.java index c1cbc68e7b..e7cbf9521a 100644 --- a/src/main/java/dan200/computercraft/core/apis/handles/HandleGeneric.java +++ b/src/main/java/dan200/computercraft/core/apis/handles/HandleGeneric.java @@ -1,8 +1,12 @@ package dan200.computercraft.core.apis.handles; +import dan200.computercraft.api.lua.ICallContext; +import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.ILuaObject; import dan200.computercraft.api.lua.LuaException; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.Closeable; import java.io.IOException; @@ -32,4 +36,12 @@ protected void close() { } } + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); + } } diff --git a/src/main/java/dan200/computercraft/core/apis/http/HTTPRequest.java b/src/main/java/dan200/computercraft/core/apis/http/HTTPRequest.java index ff106af4b5..050d8472d1 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/HTTPRequest.java +++ b/src/main/java/dan200/computercraft/core/apis/http/HTTPRequest.java @@ -9,15 +9,14 @@ 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.api.lua.*; 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 javax.annotation.Nullable; import java.io.*; import java.net.*; import java.util.Arrays; @@ -259,8 +258,9 @@ public String[] getMethodNames() return newMethods; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { if( method < methodOffset ) { @@ -271,19 +271,27 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O case 0: { // getResponseCode - return new Object[] { responseCode }; + return MethodResult.of( responseCode ); } case 1: { // getResponseHeaders - return new Object[] { responseHeaders }; + return MethodResult.of( responseHeaders ); } default: { - return null; + return MethodResult.empty(); } } } + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); + } }; } } diff --git a/src/main/java/dan200/computercraft/core/lua/LuaJLuaMachine.java b/src/main/java/dan200/computercraft/core/lua/LuaJLuaMachine.java index abd7453f34..3dbc2e66d2 100644 --- a/src/main/java/dan200/computercraft/core/lua/LuaJLuaMachine.java +++ b/src/main/java/dan200/computercraft/core/lua/LuaJLuaMachine.java @@ -7,15 +7,11 @@ package dan200.computercraft.core.lua; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.api.lua.ILuaContext; -import dan200.computercraft.api.lua.ILuaObject; -import dan200.computercraft.api.lua.ILuaTask; -import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.*; import dan200.computercraft.core.apis.ILuaAPI; import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.ITask; import dan200.computercraft.core.computer.MainThread; - import org.luaj.vm2.*; import org.luaj.vm2.lib.OneArgFunction; import org.luaj.vm2.lib.VarArgFunction; @@ -340,9 +336,10 @@ public Varargs invoke( Varargs _args ) Object[] results; try { - results = apiObject.callMethod( new ILuaContext() { + ILuaContext context = new ILuaContext() { @Nonnull @Override + @Deprecated public Object[] pullEvent( String filter ) throws LuaException, InterruptedException { Object[] results = pullEventRaw( filter ); @@ -352,16 +349,18 @@ public Object[] pullEvent( String filter ) throws LuaException, InterruptedExcep } return results; } - + @Nonnull @Override + @Deprecated public Object[] pullEventRaw( String filter ) throws InterruptedException { return yield( new Object[] { filter } ); } - + @Nonnull @Override + @Deprecated public Object[] yield( Object[] yieldArgs ) throws InterruptedException { try @@ -437,6 +436,7 @@ public void execute() } @Override + @Deprecated public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws LuaException, InterruptedException { // Issue task @@ -474,7 +474,10 @@ public Object[] executeMainThreadTask( @Nonnull final ILuaTask task ) throws Lua } } - }, method, arguments ); + }; + + // TODO: Replace with custom interpreter + results = apiObject.callMethod( (ICallContext) context, method, arguments ).evaluate( context ); } catch( InterruptedException e ) { diff --git a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java index 2c8b234530..0b293ca0fc 100644 --- a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java +++ b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java @@ -8,8 +8,10 @@ import com.google.common.collect.ImmutableMap; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.core.apis.ILuaAPI; import dan200.computercraft.shared.computer.blocks.TileCommandComputer; import dan200.computercraft.shared.util.WorldUtil; @@ -24,6 +26,7 @@ import net.minecraft.world.World; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.HashMap; import java.util.Map; @@ -97,7 +100,7 @@ private Object[] doCommand( String command ) sender.clearOutput(); int result = commandManager.executeCommand( sender, command ); - return new Object[]{ (result > 0), sender.copyOutput() }; + return new Object[] { (result > 0), sender.copyOutput() }; } catch( Throwable t ) { @@ -105,7 +108,7 @@ private Object[] doCommand( String command ) { ComputerCraft.log.error( "Error running command.", t ); } - return new Object[]{ false, createOutput( "Java Exception Thrown: " + t.toString() ) }; + return new Object[] { false, createOutput( "Java Exception Thrown: " + t.toString() ) }; } } else @@ -146,7 +149,8 @@ private Object getBlockInfo( World world, BlockPos pos ) } @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + @Nonnull + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException { switch( method ) { @@ -154,19 +158,19 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O { // exec final String command = getString( arguments, 0 ); - return context.executeMainThreadTask( () -> doCommand( command ) ); + return MethodResult.onMainThread( () -> MethodResult.of( doCommand( command ) ) ); } case 1: { // execAsync final String command = getString( arguments, 0 ); long taskID = context.issueMainThreadTask( () -> doCommand( command ) ); - return new Object[] { taskID }; + return MethodResult.of( taskID ); } case 2: { // list - return context.executeMainThreadTask( () -> + return MethodResult.onMainThread( () -> { int i = 1; Map result = new HashMap<>(); @@ -197,7 +201,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O } } } - return new Object[]{ result }; + return MethodResult.of( result ); } ); } case 3: @@ -205,7 +209,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O // getBlockPosition // This is probably safe to do on the Lua thread. Probably. BlockPos pos = m_computer.getPos(); - return new Object[] { pos.getX(), pos.getY(), pos.getZ() }; + return MethodResult.of( pos.getX(), pos.getY(), pos.getZ() ); } case 4: { @@ -216,7 +220,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O final int maxx = getInt( arguments, 3 ); final int maxy = getInt( arguments, 4 ); final int maxz = getInt( arguments, 5 ); - return context.executeMainThreadTask( () -> + return MethodResult.onMainThread( () -> { // Get the details of the block World world = m_computer.getWorld(); @@ -251,7 +255,7 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O } } } - return new Object[]{ results }; + return MethodResult.of( results ); } ); } case 5: @@ -260,14 +264,14 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O final int x = getInt( arguments, 0 ); final int y = getInt( arguments, 1 ); final int z = getInt( arguments, 2 ); - return context.executeMainThreadTask( () -> + return MethodResult.onMainThread( () -> { // Get the details of the block World world = m_computer.getWorld(); BlockPos position = new BlockPos( x, y, z ); if( WorldUtil.isBlockInWorld( world, position ) ) { - return new Object[]{ getBlockInfo( world, position ) }; + return MethodResult.of( getBlockInfo( world, position ) ); } else { @@ -277,8 +281,16 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O } default: { - return null; + return MethodResult.empty(); } } } + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); + } } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java index 321889c3d3..62a3662f1e 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java @@ -6,13 +6,16 @@ package dan200.computercraft.shared.computer.blocks; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.shared.computer.core.ServerComputer; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ComputerPeripheral implements IPeripheral @@ -25,7 +28,7 @@ public ComputerPeripheral( String type, ServerComputer computer ) m_type = type; m_computer = computer; } - + // IPeripheral implementation @Nonnull @@ -49,8 +52,9 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException + public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException { switch( method ) { @@ -58,42 +62,48 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont { // turnOn m_computer.turnOn(); - return null; + return MethodResult.empty(); } case 1: { // shutdown m_computer.shutdown(); - return null; + return MethodResult.empty(); } case 2: { // reboot m_computer.reboot(); - return null; + return MethodResult.empty(); } case 3: { // getID - return new Object[] { - m_computer.assignID() - }; + return MethodResult.of( m_computer.assignID() ); } case 4: { // isOn - return new Object[] { m_computer.isOn() }; + return MethodResult.of( m_computer.isOn() ); } case 5: // getLabel - return new Object[] { m_computer.getLabel() }; + return MethodResult.of( m_computer.getLabel() ); default: { - return null; + return MethodResult.empty(); } } } + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull IComputerAccess access, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( access, (ICallContext) context, method, arguments ).evaluate( context ); + } + @Override public boolean equals( IPeripheral other ) { diff --git a/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java index a84070184f..19a333ac8c 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java @@ -6,14 +6,17 @@ package dan200.computercraft.shared.peripheral.commandblock; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import net.minecraft.tileentity.TileEntityCommandBlock; import net.minecraft.util.math.BlockPos; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static dan200.computercraft.core.apis.ArgumentHelper.getString; @@ -46,17 +49,18 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull final Object[] arguments ) throws LuaException, InterruptedException + public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int method, @Nonnull final Object[] arguments ) throws LuaException { - switch (method) + switch( method ) { case 0: { // getCommand - return context.executeMainThreadTask( () -> new Object[] { - m_commandBlock.getCommandBlockLogic().getCommand() - } ); + return MethodResult.onMainThread( () -> + MethodResult.of( m_commandBlock.getCommandBlockLogic().getCommand() ) + ); } case 1: { @@ -69,27 +73,35 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont m_commandBlock.getWorld().markBlockRangeForRenderUpdate( pos, pos ); return null; } ); - return null; + return MethodResult.empty(); } case 2: { // runCommand - return context.executeMainThreadTask( () -> + return MethodResult.onMainThread( () -> { m_commandBlock.getCommandBlockLogic().trigger( m_commandBlock.getWorld() ); int result = m_commandBlock.getCommandBlockLogic().getSuccessCount(); if( result > 0 ) { - return new Object[] { true }; + return MethodResult.of( true ); } else { - return new Object[] { false, "Command failed" }; + return MethodResult.of( false, "Command failed" ); } } ); } } - return null; + return MethodResult.empty(); + } + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull IComputerAccess access, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( access, (ICallContext) context, method, arguments ).evaluate( context ); } @Override diff --git a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java index 59ffea4bfc..a6c77e37bc 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java @@ -6,8 +6,10 @@ package dan200.computercraft.shared.peripheral.diskdrive; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.media.IMedia; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; @@ -17,6 +19,7 @@ import net.minecraft.item.ItemStack; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static dan200.computercraft.core.apis.ArgumentHelper.optString; @@ -55,17 +58,18 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException + public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException { switch( method ) { case 0: { // isPresent - return new Object[] { + return MethodResult.of( m_diskDrive.getDiskStack() != null - }; + ); } case 1: { @@ -73,9 +77,9 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont IMedia media = m_diskDrive.getDiskMedia(); if( media != null ) { - return new Object[] { media.getLabel( m_diskDrive.getDiskStack() ) }; + return MethodResult.of( media.getLabel( m_diskDrive.getDiskStack() ) ); } - return null; + return MethodResult.empty(); } case 2: { @@ -96,21 +100,21 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont throw new LuaException( "Disk label cannot be changed" ); } } - return null; + return MethodResult.empty(); } case 3: { // hasData - return new Object[] { + return MethodResult.of( m_diskDrive.getDiskMountPath( computer ) != null - }; + ); } case 4: { // getMountPath - return new Object[] { + return MethodResult.of( m_diskDrive.getDiskMountPath( computer ) - }; + ); } case 5: { @@ -118,9 +122,9 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont IMedia media = m_diskDrive.getDiskMedia(); if( media != null ) { - return new Object[] { media.getAudio( m_diskDrive.getDiskStack() ) != null }; + return MethodResult.of( media.getAudio( m_diskDrive.getDiskStack() ) != null ); } - return new Object[] { false }; + return MethodResult.of( false ); } case 6: { @@ -128,27 +132,27 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont IMedia media = m_diskDrive.getDiskMedia(); if( media != null ) { - return new Object[] { media.getAudioTitle( m_diskDrive.getDiskStack() ) }; + return MethodResult.of( media.getAudioTitle( m_diskDrive.getDiskStack() ) ); } - return new Object[] { false }; + return MethodResult.of( false ); } case 7: { // playAudio m_diskDrive.playDiskAudio(); - return null; + return MethodResult.empty(); } case 8: { // stopAudio m_diskDrive.stopDiskAudio(); - return null; + return MethodResult.empty(); } case 9: { // eject m_diskDrive.ejectDisk(); - return null; + return MethodResult.empty(); } case 10: { @@ -159,18 +163,27 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont Item item = disk.getItem(); if( item instanceof ItemDiskLegacy ) { - return new Object[] { ((ItemDiskLegacy)item).getDiskID( disk ) }; + return MethodResult.of( ((ItemDiskLegacy)item).getDiskID( disk ) ); } } - return null; + return MethodResult.empty(); } default: { - return null; + return MethodResult.empty(); } } } + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull IComputerAccess access, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( access, (ICallContext) context, method, arguments ).evaluate( context ); + } + + @Override public void attach( @Nonnull IComputerAccess computer ) { diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemPeripheral.java index ec05ae7832..61986b9a33 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemPeripheral.java @@ -6,8 +6,10 @@ package dan200.computercraft.shared.peripheral.modem; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.network.IPacketNetwork; import dan200.computercraft.api.network.IPacketReceiver; import dan200.computercraft.api.network.IPacketSender; @@ -20,6 +22,7 @@ import net.minecraft.world.World; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static dan200.computercraft.core.apis.ArgumentHelper.getInt; @@ -157,8 +160,9 @@ private static int parseChannel( Object[] arguments, int index ) throws LuaExcep return channel; } + @Nonnull @Override - public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException { switch( method ) { @@ -183,7 +187,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont } } } - return null; + return MethodResult.empty(); } case 1: { @@ -192,7 +196,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont synchronized( this ) { boolean open = m_channels.contains( channel ); - return new Object[] { open }; + return MethodResult.of( open ); } } case 2: @@ -210,7 +214,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont } } } - return null; + return MethodResult.empty(); } case 3: { @@ -228,7 +232,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont } } } - return null; + return MethodResult.empty(); } case 4: { @@ -253,7 +257,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont } } } - return null; + return MethodResult.empty(); } case 5: { @@ -262,18 +266,26 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont { if( m_network != null ) { - return new Object[] { m_network.isWireless() }; + return MethodResult.of( m_network.isWireless() ); } } - return new Object[] { false }; + return MethodResult.of(false); } default: { - return null; + return MethodResult.empty(); } } } - + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( computer, (ICallContext) context, method, arguments ).evaluate( context ); + } + @Override public synchronized void attach( @Nonnull IComputerAccess computer ) { diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java index b463a0c25e..4c16f9f4fb 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java @@ -10,8 +10,9 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IWritableMount; -import dan200.computercraft.api.lua.ILuaContext; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.network.IPacketNetwork; import dan200.computercraft.api.network.IPacketReceiver; import dan200.computercraft.api.network.Packet; @@ -119,8 +120,9 @@ public String[] getMethodNames() return newMethods; } + @Nonnull @Override - public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException { String[] methods = super.getMethodNames(); switch( method - methods.length ) @@ -136,14 +138,14 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont { table.put( idx++, name ); } - return new Object[] { table }; + return MethodResult.of( table ); } } case 1: { // isPresentRemote String type = m_entity.getTypeRemote( getString( arguments, 0 ) ); - return new Object[] { type != null }; + return MethodResult.of( type != null ); } case 2: { @@ -151,9 +153,9 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont String type = m_entity.getTypeRemote( getString( arguments, 0 ) ); if( type != null ) { - return new Object[] { type }; + return MethodResult.of( type ); } - return null; + return MethodResult.empty(); } case 3: { @@ -165,9 +167,9 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont for(int i=0; i 0 && args[0] != null ) { - text = args[0].toString(); - } else { + if( args.length > 0 && args[ 0 ] != null ) + { + text = args[ 0 ].toString(); + } + else + { text = ""; } Terminal terminal = m_monitor.getTerminal().getTerminal(); terminal.write( text ); terminal.setCursorPos( terminal.getCursorX() + text.length(), terminal.getCursorY() ); - return null; + return MethodResult.empty(); } case 1: { @@ -94,7 +101,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont int value = getInt( args, 0 ); Terminal terminal = m_monitor.getTerminal().getTerminal(); terminal.scroll( value ); - return null; + return MethodResult.empty(); } case 2: { @@ -103,7 +110,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont int y = getInt( args, 1 ) - 1; Terminal terminal = m_monitor.getTerminal().getTerminal(); terminal.setCursorPos( x, y ); - return null; + return MethodResult.empty(); } case 3: { @@ -111,39 +118,39 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont boolean blink = getBoolean( args, 0 ); Terminal terminal = m_monitor.getTerminal().getTerminal(); terminal.setCursorBlink( blink ); - return null; + return MethodResult.empty(); } case 4: { // getCursorPos Terminal terminal = m_monitor.getTerminal().getTerminal(); - return new Object[] { + return MethodResult.of( terminal.getCursorX() + 1, terminal.getCursorY() + 1 - }; + ); } case 5: { // getSize Terminal terminal = m_monitor.getTerminal().getTerminal(); - return new Object[] { + return MethodResult.of( terminal.getWidth(), terminal.getHeight() - }; + ); } case 6: { // clear Terminal terminal = m_monitor.getTerminal().getTerminal(); terminal.clear(); - return null; + return MethodResult.empty(); } case 7: { // clearLine Terminal terminal = m_monitor.getTerminal().getTerminal(); terminal.clearLine(); - return null; + return MethodResult.empty(); } case 8: { @@ -154,7 +161,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont throw new LuaException( "Expected number in range 0.5-5" ); } m_monitor.setTextScale( scale ); - return null; + return MethodResult.empty(); } case 9: case 10: @@ -163,7 +170,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont int colour = TermAPI.parseColour( args ); Terminal terminal = m_monitor.getTerminal().getTerminal(); terminal.setTextColour( colour ); - return null; + return MethodResult.empty(); } case 11: case 12: @@ -172,15 +179,15 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont int colour = TermAPI.parseColour( args ); Terminal terminal = m_monitor.getTerminal().getTerminal(); terminal.setBackgroundColour( colour ); - return null; + return MethodResult.empty(); } case 13: case 14: { // isColour/isColor - return new Object[] { + return MethodResult.of( m_monitor.getTerminal().isColour() - }; + ); } case 15: case 16: @@ -210,7 +217,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont Terminal terminal = m_monitor.getTerminal().getTerminal(); terminal.blit( text, textColour, backgroundColour ); terminal.setCursorPos( terminal.getCursorX() + text.length(), terminal.getCursorY() ); - return null; + return MethodResult.empty(); } case 20: case 21: @@ -232,7 +239,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont double b = getReal( args, 3 ); TermAPI.setColour( terminal, colour, r, g, b ); } - return null; + return MethodResult.empty(); } case 22: case 23: @@ -245,12 +252,20 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont if( palette != null ) { - return ArrayUtils.toObject( palette.getColour( colour ) ); + return MethodResult.of( (Object[]) ArrayUtils.toObject( palette.getColour( colour ) ) ); } - return null; + return MethodResult.pullEvent(); } } - return null; + return MethodResult.empty(); + } + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull IComputerAccess access, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( access, (ICallContext) context, method, arguments ).evaluate( context ); } @Override @@ -270,7 +285,7 @@ public boolean equals( IPeripheral other ) { if( other != null && other instanceof MonitorPeripheral ) { - MonitorPeripheral otherMonitor = (MonitorPeripheral)other; + MonitorPeripheral otherMonitor = (MonitorPeripheral) other; if( otherMonitor.m_monitor == this.m_monitor ) { return true; diff --git a/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterPeripheral.java index 1a97be3d13..033ca9d620 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterPeripheral.java @@ -6,13 +6,16 @@ package dan200.computercraft.shared.peripheral.printer; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.terminal.Terminal; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static dan200.computercraft.core.apis.ArgumentHelper.getInt; import static dan200.computercraft.core.apis.ArgumentHelper.optString; @@ -50,8 +53,9 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException + public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { switch( method ) { @@ -68,7 +72,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont Terminal page = getCurrentPage(); page.write( text ); page.setCursorPos( page.getCursorX() + text.length(), page.getCursorY() ); - return null; + return MethodResult.empty(); } case 1: { @@ -77,7 +81,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont int y = getInt( args, 1 ) - 1; Terminal page = getCurrentPage(); page.setCursorPos( x, y ); - return null; + return MethodResult.empty(); } case 2: { @@ -85,7 +89,7 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont Terminal page = getCurrentPage(); int x = page.getCursorX(); int y = page.getCursorY(); - return new Object[] { x + 1, y + 1 }; + return MethodResult.of( x + 1, y + 1 ); } case 3: { @@ -93,23 +97,23 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont Terminal page = getCurrentPage(); int width = page.getWidth(); int height = page.getHeight(); - return new Object[] { width, height }; + return MethodResult.of( width, height ); } case 4: { // newPage - return new Object[] { m_printer.startNewPage() }; + return MethodResult.of( m_printer.startNewPage() ); } case 5: { // endPage getCurrentPage(); - return new Object[] { m_printer.endCurrentPage() }; + return MethodResult.of( m_printer.endCurrentPage() ); } case 6: { // getInkLevel - return new Object[] { m_printer.getInkLevel() }; + return MethodResult.of( m_printer.getInkLevel() ); } case 7: { @@ -117,20 +121,28 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont String title = optString( args, 0, "" ); getCurrentPage(); m_printer.setPageTitle( title ); - return null; + return MethodResult.empty(); } case 8: { // getPaperLevel - return new Object[] { m_printer.getPaperLevel() }; + return MethodResult.of( m_printer.getPaperLevel() ); } default: { - return null; + return MethodResult.empty(); } } } + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull IComputerAccess access, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( access, (ICallContext) context, method, arguments ).evaluate( context ); + } + @Override public boolean equals( IPeripheral other ) { diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java index 65831aba3a..23de56d47e 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java @@ -7,9 +7,7 @@ package dan200.computercraft.shared.peripheral.speaker; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.api.lua.ILuaContext; -import dan200.computercraft.api.lua.ILuaTask; -import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.*; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import net.minecraft.util.ResourceLocation; @@ -92,21 +90,22 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull IComputerAccess computerAccess, @Nonnull ILuaContext context, int methodIndex, @Nonnull Object[] args ) throws LuaException + public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int methodIndex, @Nonnull Object[] args ) throws LuaException { switch( methodIndex ) { // playSound case 0: { - return playSound(args, context, false); + return MethodResult.of( playSound( args, context, false ) ); } // playNote case 1: { - return playNote(args, context); + return MethodResult.of( playNote( args, context ) ); } default: @@ -117,8 +116,16 @@ public Object[] callMethod( @Nonnull IComputerAccess computerAccess, @Nonnull IL } } - @Nonnull - private synchronized Object[] playNote( Object[] arguments, ILuaContext context ) throws LuaException + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull IComputerAccess access, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( access, (ICallContext) context, method, arguments ).evaluate( context ); + } + + + private synchronized boolean playNote( Object[] arguments, ICallContext context ) throws LuaException { String name = getString(arguments, 0); float volume = (float) optReal( arguments, 1, 1.0 ); @@ -131,7 +138,7 @@ private synchronized Object[] playNote( Object[] arguments, ILuaContext context } // If the resource location for note block notes changes, this method call will need to be updated - Object[] returnValue = playSound( + boolean success = playSound( new Object[] { "block.note." + name, (double)Math.min( volume, 3f ), @@ -139,16 +146,15 @@ private synchronized Object[] playNote( Object[] arguments, ILuaContext context }, context, true ); - if( returnValue[0] instanceof Boolean && (Boolean) returnValue[0] ) + if( success ) { m_notesThisTick++; } - return returnValue; + return success; } - @Nonnull - private synchronized Object[] playSound( Object[] arguments, ILuaContext context, boolean isNote ) throws LuaException + private synchronized boolean playSound( Object[] arguments, ICallContext context, boolean isNote ) throws LuaException { String name = getString(arguments, 0); float volume = (float) optReal( arguments, 1, 1.0 ); @@ -178,16 +184,16 @@ public Object[] execute() throws LuaException }); m_lastPlayTime = m_clock; - return new Object[]{true}; // Success, return true + return true; // Success, return true } else { - return new Object[]{false}; // Failed - sound not existent, return false + return false; // Failed - sound not existent, return false } } else { - return new Object[]{false}; // Failed - rate limited, return false + return false; // Failed - rate limited, return false } } } diff --git a/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java b/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java index 46377e98ec..d7997e9429 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java +++ b/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java @@ -7,8 +7,10 @@ package dan200.computercraft.shared.pocket.apis; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.core.apis.ILuaAPI; import dan200.computercraft.shared.pocket.core.PocketServerComputer; @@ -21,6 +23,7 @@ import net.minecraftforge.items.wrapper.PlayerMainInvWrapper; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class PocketAPI implements ILuaAPI { @@ -64,14 +67,15 @@ public String[] getMethodNames() }; } + @Nonnull @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException { switch( method ) { case 0: // equipBack - return context.executeMainThreadTask( () -> + return MethodResult.onMainThread( () -> { if( !(m_computer.getEntity() instanceof EntityPlayer) ) { @@ -109,12 +113,12 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O // Set the new upgrade m_computer.setUpgrade( newUpgrade ); - return null; + return MethodResult.empty(); } ); case 1: // unequipBack - return context.executeMainThreadTask( () -> + return MethodResult.onMainThread( () -> { if( !(m_computer.getEntity() instanceof EntityPlayer) ) { @@ -140,13 +144,21 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O } } - return null; + return MethodResult.empty(); } ); default: - return null; + return MethodResult.empty(); } } + @Override + @Nullable + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); + } + private static IPocketUpgrade findUpgrade( NonNullList inv, int start, IPocketUpgrade previous ) { for( int i = 0; i < inv.size(); i++ ) diff --git a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java index 1d5b6083f1..a11a034799 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java +++ b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java @@ -6,8 +6,10 @@ package dan200.computercraft.shared.turtle.apis; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleCommand; import dan200.computercraft.api.turtle.TurtleSide; @@ -18,6 +20,7 @@ import net.minecraft.item.ItemStack; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -110,9 +113,9 @@ public String[] getMethodNames() }; } - private Object[] tryCommand( ILuaContext context, ITurtleCommand command ) throws LuaException, InterruptedException + private MethodResult tryCommand( ITurtleCommand command ) throws LuaException { - return m_turtle.executeCommand( context, command ); + return m_turtle.executeCommand( command ); } private int parseSlotNumber( Object[] arguments, int index ) throws LuaException @@ -166,84 +169,85 @@ else if( side.equalsIgnoreCase( "right" ) ) } @Override - public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException + @Nonnull + public MethodResult callMethod( @Nonnull ICallContext context, int method, @Nonnull Object[] args ) throws LuaException { switch( method ) { case 0: { // forward - return tryCommand( context, new TurtleMoveCommand( MoveDirection.Forward ) ); + return tryCommand( new TurtleMoveCommand( MoveDirection.Forward ) ); } case 1: { // back - return tryCommand( context, new TurtleMoveCommand( MoveDirection.Back ) ); + return tryCommand( new TurtleMoveCommand( MoveDirection.Back ) ); } case 2: { // up - return tryCommand( context, new TurtleMoveCommand( MoveDirection.Up ) ); + return tryCommand( new TurtleMoveCommand( MoveDirection.Up ) ); } case 3: { // down - return tryCommand( context, new TurtleMoveCommand( MoveDirection.Down ) ); + return tryCommand( new TurtleMoveCommand( MoveDirection.Down ) ); } case 4: { // turnLeft - return tryCommand( context, new TurtleTurnCommand( TurnDirection.Left ) ); + return tryCommand( new TurtleTurnCommand( TurnDirection.Left ) ); } case 5: { // turnRight - return tryCommand( context, new TurtleTurnCommand( TurnDirection.Right ) ); + return tryCommand( new TurtleTurnCommand( TurnDirection.Right ) ); } case 6: { // dig Optional side = parseSide( args, 0 ); - return tryCommand( context, new TurtleDigCommand( InteractDirection.Forward, side ) ); + return tryCommand( new TurtleDigCommand( InteractDirection.Forward, side ) ); } case 7: { // digUp Optional side = parseSide( args, 0 ); - return tryCommand( context, new TurtleDigCommand( InteractDirection.Up, side ) ); + return tryCommand( new TurtleDigCommand( InteractDirection.Up, side ) ); } case 8: { // digDown Optional side = parseSide( args, 0 ); - return tryCommand( context, new TurtleDigCommand( InteractDirection.Down, side ) ); + return tryCommand( new TurtleDigCommand( InteractDirection.Down, side ) ); } case 9: { // place - return tryCommand( context, new TurtlePlaceCommand( InteractDirection.Forward, args ) ); + return tryCommand( new TurtlePlaceCommand( InteractDirection.Forward, args ) ); } case 10: { // placeUp - return tryCommand( context, new TurtlePlaceCommand( InteractDirection.Up, args ) ); + return tryCommand( new TurtlePlaceCommand( InteractDirection.Up, args ) ); } case 11: { // placeDown - return tryCommand( context, new TurtlePlaceCommand( InteractDirection.Down, args ) ); + return tryCommand( new TurtlePlaceCommand( InteractDirection.Down, args ) ); } case 12: { // drop int count = parseCount( args, 0 ); - return tryCommand( context, new TurtleDropCommand( InteractDirection.Forward, count ) ); + return tryCommand( new TurtleDropCommand( InteractDirection.Forward, count ) ); } case 13: { // select int slot = parseSlotNumber( args, 0 ); - return tryCommand( context, new TurtleSelectCommand( slot ) ); + return tryCommand( new TurtleSelectCommand( slot ) ); } case 14: { @@ -252,11 +256,11 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O ItemStack stack = m_turtle.getInventory().getStackInSlot( slot ); if( !stack.isEmpty() ) { - return new Object[] { stack.getCount() }; + return MethodResult.of( stack.getCount() ); } else { - return new Object[] { 0 }; + return MethodResult.of( 0 ); } } case 15: @@ -266,162 +270,162 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O ItemStack stack = m_turtle.getInventory().getStackInSlot( slot ); if( !stack.isEmpty() ) { - return new Object[] { + return MethodResult.of( Math.min( stack.getMaxStackSize(), 64 ) - stack.getCount() - }; + ); } - return new Object[] { 64 }; + return MethodResult.of( 64 ); } case 16: { // detect - return tryCommand( context, new TurtleDetectCommand( InteractDirection.Forward ) ); + return tryCommand( new TurtleDetectCommand( InteractDirection.Forward ) ); } case 17: { // detectUp - return tryCommand( context, new TurtleDetectCommand( InteractDirection.Up ) ); + return tryCommand( new TurtleDetectCommand( InteractDirection.Up ) ); } case 18: { // detectDown - return tryCommand( context, new TurtleDetectCommand( InteractDirection.Down ) ); + return tryCommand( new TurtleDetectCommand( InteractDirection.Down ) ); } case 19: { // compare - return tryCommand( context, new TurtleCompareCommand( InteractDirection.Forward ) ); + return tryCommand( new TurtleCompareCommand( InteractDirection.Forward ) ); } case 20: { // compareUp - return tryCommand( context, new TurtleCompareCommand( InteractDirection.Up ) ); + return tryCommand( new TurtleCompareCommand( InteractDirection.Up ) ); } case 21: { // compareDown - return tryCommand( context, new TurtleCompareCommand( InteractDirection.Down ) ); + return tryCommand( new TurtleCompareCommand( InteractDirection.Down ) ); } case 22: { // attack Optional side = parseSide( args, 0 ); - return tryCommand( context, new TurtleAttackCommand( InteractDirection.Forward, side ) ); + return tryCommand( new TurtleAttackCommand( InteractDirection.Forward, side ) ); } case 23: { // attackUp Optional side = parseSide( args, 0 ); - return tryCommand( context, new TurtleAttackCommand( InteractDirection.Up, side ) ); + return tryCommand( new TurtleAttackCommand( InteractDirection.Up, side ) ); } case 24: { // attackDown Optional side = parseSide( args, 0 ); - return tryCommand( context, new TurtleAttackCommand( InteractDirection.Down, side ) ); + return tryCommand( new TurtleAttackCommand( InteractDirection.Down, side ) ); } case 25: { // dropUp int count = parseCount( args, 0 ); - return tryCommand( context, new TurtleDropCommand( InteractDirection.Up, count ) ); + return tryCommand( new TurtleDropCommand( InteractDirection.Up, count ) ); } case 26: { // dropDown int count = parseCount( args, 0 ); - return tryCommand( context, new TurtleDropCommand( InteractDirection.Down, count ) ); + return tryCommand( new TurtleDropCommand( InteractDirection.Down, count ) ); } case 27: { // suck int count = parseCount( args, 0 ); - return tryCommand( context, new TurtleSuckCommand( InteractDirection.Forward, count ) ); + return tryCommand( new TurtleSuckCommand( InteractDirection.Forward, count ) ); } case 28: { // suckUp int count = parseCount( args, 0 ); - return tryCommand( context, new TurtleSuckCommand( InteractDirection.Up, count ) ); + return tryCommand( new TurtleSuckCommand( InteractDirection.Up, count ) ); } case 29: { // suckDown int count = parseCount( args, 0 ); - return tryCommand( context, new TurtleSuckCommand( InteractDirection.Down, count ) ); + return tryCommand( new TurtleSuckCommand( InteractDirection.Down, count ) ); } case 30: { // getFuelLevel if( m_turtle.isFuelNeeded() ) { - return new Object[] { m_turtle.getFuelLevel() }; + return MethodResult.of( m_turtle.getFuelLevel() ); } else { - return new Object[] { "unlimited" }; + return MethodResult.of( "unlimited" ); } } case 31: { // refuel int count = parseCount( args, 0 ); - return tryCommand( context, new TurtleRefuelCommand( count ) ); + return tryCommand( new TurtleRefuelCommand( count ) ); } case 32: { // compareTo int slot = parseSlotNumber( args, 0 ); - return tryCommand( context, new TurtleCompareToCommand( slot ) ); + return tryCommand( new TurtleCompareToCommand( slot ) ); } case 33: { // transferTo int slot = parseSlotNumber( args, 0 ); int count = parseCount( args, 1 ); - return tryCommand( context, new TurtleTransferToCommand( slot, count ) ); + return tryCommand( new TurtleTransferToCommand( slot, count ) ); } case 34: { // getSelectedSlot - return new Object[] { m_turtle.getSelectedSlot() + 1 }; + return MethodResult.of( m_turtle.getSelectedSlot() + 1 ); } case 35: { // getFuelLimit if( m_turtle.isFuelNeeded() ) { - return new Object[] { m_turtle.getFuelLimit() }; + return MethodResult.of( m_turtle.getFuelLimit() ); } else { - return new Object[] { "unlimited" }; + return MethodResult.of( "unlimited" ); } } case 36: { // equipLeft - return tryCommand( context, new TurtleEquipCommand( TurtleSide.Left ) ); + return tryCommand( new TurtleEquipCommand( TurtleSide.Left ) ); } case 37: { // equipRight - return tryCommand( context, new TurtleEquipCommand( TurtleSide.Right ) ); + return tryCommand( new TurtleEquipCommand( TurtleSide.Right ) ); } case 38: { // inspect - return tryCommand( context, new TurtleInspectCommand( InteractDirection.Forward ) ); + return tryCommand( new TurtleInspectCommand( InteractDirection.Forward ) ); } case 39: { // inspectUp - return tryCommand( context, new TurtleInspectCommand( InteractDirection.Up ) ); + return tryCommand( new TurtleInspectCommand( InteractDirection.Up ) ); } case 40: { // inspectDown - return tryCommand( context, new TurtleInspectCommand( InteractDirection.Down ) ); + return tryCommand( new TurtleInspectCommand( InteractDirection.Down ) ); } case 41: { @@ -439,17 +443,25 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O table.put( "name", name ); table.put( "damage", damage ); table.put( "count", count ); - return new Object[] { table }; + return MethodResult.of( table ); } else { - return new Object[] { null }; + return MethodResult.of( new Object[] { null } ); } } default: { - return null; + return MethodResult.empty(); } } } + + @Override + @Nullable + @Deprecated + public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( (ICallContext) context, method, arguments ).evaluate( context ); + } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java index 4813928ed2..fe77117daf 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java @@ -8,8 +8,7 @@ import com.google.common.base.Objects; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.api.lua.ILuaContext; -import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.*; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.turtle.*; import dan200.computercraft.shared.computer.core.ComputerFamily; @@ -713,6 +712,7 @@ private int issueCommand( ITurtleCommand command ) @Nonnull @Override + @Deprecated public Object[] executeCommand( @Nonnull ILuaContext context, @Nonnull ITurtleCommand command ) throws LuaException, InterruptedException { if( getWorld().isRemote ) @@ -739,6 +739,38 @@ public Object[] executeCommand( @Nonnull ILuaContext context, @Nonnull ITurtleCo } } + @Nonnull + @Override + public MethodResult executeCommand( @Nonnull ITurtleCommand command ) + { + if( getWorld().isRemote ) + { + throw new UnsupportedOperationException(); + } + + // Issue command + int commandID = issueCommand( command ); + + // Wait for response + return MethodResult.pullEvent( "turtle_response", new ILuaFunction() + { + @Nonnull + @Override + public MethodResult call( Object[] response ) + { + if( response.length >= 3 && response[ 1 ] instanceof Number && ((Number) response[ 1 ]).intValue() == commandID + && response[ 2 ] instanceof Boolean ) + { + Object[] returnValues = new Object[ response.length - 2 ]; + System.arraycopy( response, 2, returnValues, 0, returnValues.length ); + return MethodResult.of( returnValues ); + } + + return MethodResult.pullEvent( "turtle_response", this ); + } + } ); + } + @Override public void playAnimation( @Nonnull TurtleAnimation animation ) { diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/CraftingTablePeripheral.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/CraftingTablePeripheral.java index a2fb78bced..d0e7bbc9ae 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/CraftingTablePeripheral.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/CraftingTablePeripheral.java @@ -6,19 +6,21 @@ package dan200.computercraft.shared.turtle.upgrades; +import dan200.computercraft.api.lua.ICallContext; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.shared.turtle.core.TurtleCraftCommand; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static dan200.computercraft.core.apis.ArgumentHelper.optInt; -public class CraftingTablePeripheral - implements IPeripheral +public class CraftingTablePeripheral implements IPeripheral { private final ITurtleAccess m_turtle; @@ -26,7 +28,7 @@ public CraftingTablePeripheral( ITurtleAccess turtle ) { m_turtle = turtle; } - + // IPeripheral implementation @Nonnull @@ -35,7 +37,7 @@ public String getType() { return "workbench"; } - + @Nonnull @Override public String[] getMethodNames() @@ -44,7 +46,7 @@ public String[] getMethodNames() "craft", }; } - + private int parseCount( Object[] arguments ) throws LuaException { int count = optInt( arguments, 0, 64 ); @@ -54,9 +56,10 @@ private int parseCount( Object[] arguments ) throws LuaException } return count; } - + @Override - public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + @Nonnull + public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ICallContext context, int method, @Nonnull Object[] arguments ) throws LuaException { switch( method ) { @@ -64,15 +67,24 @@ public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaCont { // craft final int limit = parseCount( arguments ); - return m_turtle.executeCommand( context, new TurtleCraftCommand( limit ) ); + return m_turtle.executeCommand( new TurtleCraftCommand( limit ) ); } default: { - return null; + return MethodResult.empty(); } } } + + @Nullable + @Override + @Deprecated + public Object[] callMethod( @Nonnull IComputerAccess access, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException + { + return callMethod( access, (ICallContext) context, method, arguments ).evaluate( context ); + } + @Override public boolean equals( IPeripheral other ) { diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java index 05a7e364fa..c0c2e2de68 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java @@ -31,8 +31,7 @@ public class TurtleModem implements ITurtleUpgrade { - private static class Peripheral extends WirelessModemPeripheral - implements IPeripheral + private static class Peripheral extends WirelessModemPeripheral implements IPeripheral { private final ITurtleAccess m_turtle;