From 3b4c1eac1cac910f0c150eeb0c3c16486022a37f Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sun, 9 Sep 2018 16:48:55 +0100 Subject: [PATCH 1/2] Proposal for replacing ILuaContext with a non-blocking alternative This is an initial prototype for ways we could better integrate Lua's asynchronous functionality (coroutines) with peripherals and other Lua APIs. The existing system assumes that coroutines each have a backing thread, and so yields will suspect the current thread. This takes inspiration from various "promise" libraries - asynchronous operations (such as executing on the main thread or waiting for an event) return a promise - the result of which can be consumed with `.then`. While I am not aware of plans to change the Lua implementation, this does give us greater flexibility in the future, leading the way for Rembulan, single-threaded Cobalt, and even possibly OC support. --- .../computercraft/api/lua/ICallContext.java | 31 ++ .../computercraft/api/lua/ILuaCallable.java | 31 ++ .../computercraft/api/lua/ILuaContext.java | 30 +- .../api/lua/ILuaContextTask.java | 24 ++ .../computercraft/api/lua/ILuaFunction.java | 33 ++ .../computercraft/api/lua/ILuaObject.java | 35 +- .../api/lua/LuaContextResultEvaluator.java | 105 ++++++ .../computercraft/api/lua/MethodResult.java | 344 ++++++++++++++++++ .../api/peripheral/IPeripheral.java | 40 +- .../api/turtle/ITurtleAccess.java | 23 +- .../core/lua/LuaJLuaMachine.java | 21 +- .../shared/turtle/core/TurtleBrain.java | 35 +- 12 files changed, 717 insertions(+), 35 deletions(-) create mode 100644 src/main/java/dan200/computercraft/api/lua/ICallContext.java create mode 100644 src/main/java/dan200/computercraft/api/lua/ILuaCallable.java create mode 100644 src/main/java/dan200/computercraft/api/lua/ILuaContextTask.java create mode 100644 src/main/java/dan200/computercraft/api/lua/ILuaFunction.java create mode 100644 src/main/java/dan200/computercraft/api/lua/LuaContextResultEvaluator.java create mode 100644 src/main/java/dan200/computercraft/api/lua/MethodResult.java 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/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/turtle/core/TurtleBrain.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java index 4813928ed2..31edaae6ab 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; @@ -739,6 +738,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 ) { From ac8444b364c08149b7ada09d0d385b170cf48fd8 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sun, 9 Sep 2018 17:06:34 +0100 Subject: [PATCH 2/2] Migrate ComputerCraft's peripherals/APIs to use the non-blocking API This is definitely a little ugly in places, mostly due to how we've handled backwards compatibility. --- .../computercraft/core/apis/BitAPI.java | 18 +- .../computercraft/core/apis/BufferAPI.java | 43 ++- .../dan200/computercraft/core/apis/FSAPI.java | 70 ++--- .../computercraft/core/apis/HTTPAPI.java | 24 +- .../computercraft/core/apis/HTTPRequest.java | 245 ------------------ .../dan200/computercraft/core/apis/OSAPI.java | 59 +++-- .../core/apis/PeripheralAPI.java | 28 +- .../computercraft/core/apis/RedstoneAPI.java | 38 ++- .../computercraft/core/apis/TermAPI.java | 54 ++-- .../core/apis/handles/BinaryInputHandle.java | 22 +- .../core/apis/handles/BinaryOutputHandle.java | 16 +- .../core/apis/handles/EncodedInputHandle.java | 28 +- .../apis/handles/EncodedOutputHandle.java | 18 +- .../core/apis/handles/HandleGeneric.java | 12 + .../core/apis/http/HTTPRequest.java | 22 +- .../shared/computer/apis/CommandAPI.java | 38 ++- .../computer/blocks/ComputerPeripheral.java | 32 ++- .../commandblock/CommandBlockPeripheral.java | 32 ++- .../diskdrive/DiskDrivePeripheral.java | 53 ++-- .../peripheral/modem/ModemPeripheral.java | 32 ++- .../shared/peripheral/modem/TileCable.java | 22 +- .../peripheral/monitor/MonitorPeripheral.java | 65 +++-- .../peripheral/printer/PrinterPeripheral.java | 34 ++- .../peripheral/speaker/SpeakerPeripheral.java | 38 +-- .../shared/pocket/apis/PocketAPI.java | 24 +- .../shared/turtle/apis/TurtleAPI.java | 116 +++++---- .../shared/turtle/core/TurtleBrain.java | 1 + .../upgrades/CraftingTablePeripheral.java | 30 ++- .../shared/turtle/upgrades/TurtleModem.java | 3 +- 29 files changed, 614 insertions(+), 603 deletions(-) delete mode 100644 src/main/java/dan200/computercraft/core/apis/HTTPRequest.java 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/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 31edaae6ab..fe77117daf 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java @@ -712,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 ) 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;