mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-13 11:40:29 +00:00
Further improvements to computer execution
Oh goodness, when will it end? - Computer errors are shown in red. - Lua machine operations provide whether they succeeded, and an optional error message (reason bios failed to load, timeout error, another Lua error), which is then shown to the user. - Clear the Cobalt "thrown soft abort" flag when resuming, rather than every n instructions. - Computers will clear their "should start" flag once the time has expired, irrespective of whether it turned on or not. Before computers would immediately restart after shutting down if the flag had been set much earlier. Errors within the Lua machine are displayed in a more friendly
This commit is contained in:
parent
d02575528b
commit
218f8e53bb
@ -150,11 +150,14 @@ public class Computer
|
||||
// We keep track of the number of ticks since the last start, only
|
||||
if( m_ticksSinceStart >= 0 && m_ticksSinceStart <= START_DELAY ) m_ticksSinceStart++;
|
||||
|
||||
if( startRequested && !executor.isOn() && (m_ticksSinceStart < 0 || m_ticksSinceStart > START_DELAY) )
|
||||
if( startRequested && (m_ticksSinceStart < 0 || m_ticksSinceStart > START_DELAY) )
|
||||
{
|
||||
m_ticksSinceStart = 0;
|
||||
startRequested = false;
|
||||
executor.queueStart();
|
||||
if( !executor.isOn() )
|
||||
{
|
||||
m_ticksSinceStart = 0;
|
||||
executor.queueStart();
|
||||
}
|
||||
}
|
||||
|
||||
executor.tick();
|
||||
|
@ -16,8 +16,10 @@ import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.lua.MachineResult;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.tracking.Tracking;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import dan200.computercraft.shared.util.IoUtil;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@ -183,7 +185,7 @@ final class ComputerExecutor
|
||||
synchronized( queueLock )
|
||||
{
|
||||
// We should only schedule a start if we're not currently on and there's turn on.
|
||||
if( closed || isOn || this.command != null ) return;
|
||||
if( closed || isOn || command != null ) return;
|
||||
|
||||
command = StateCommand.TURN_ON;
|
||||
enqueue();
|
||||
@ -327,7 +329,7 @@ final class ComputerExecutor
|
||||
IMount romMount = getRomMount();
|
||||
if( romMount == null )
|
||||
{
|
||||
displayFailure( "Cannot mount rom" );
|
||||
displayFailure( "Cannot mount ROM", null );
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -339,7 +341,7 @@ final class ComputerExecutor
|
||||
if( filesystem != null ) filesystem.close();
|
||||
ComputerCraft.log.error( "Cannot mount computer filesystem", e );
|
||||
|
||||
displayFailure( "Cannot mount computer system" );
|
||||
displayFailure( "Cannot mount computer system", null );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -358,7 +360,7 @@ final class ComputerExecutor
|
||||
|
||||
if( biosStream == null )
|
||||
{
|
||||
displayFailure( "Error loading bios.lua" );
|
||||
displayFailure( "Error loading bios.lua", null );
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -369,13 +371,13 @@ final class ComputerExecutor
|
||||
for( ILuaAPI api : apis ) machine.addAPI( api );
|
||||
|
||||
// Start the machine running the bios resource
|
||||
machine.loadBios( biosStream );
|
||||
MachineResult result = machine.loadBios( biosStream );
|
||||
IoUtil.closeQuietly( biosStream );
|
||||
|
||||
if( machine.isFinished() )
|
||||
if( result.isError() )
|
||||
{
|
||||
machine.close();
|
||||
displayFailure( "Error starting bios.lua" );
|
||||
displayFailure( "Error loading bios.lua", result.getMessage() );
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -421,7 +423,7 @@ final class ComputerExecutor
|
||||
}
|
||||
|
||||
// Now actually start the computer, now that everything is set up.
|
||||
machine.handleEvent( null, null );
|
||||
resumeMachine( null, null );
|
||||
}
|
||||
|
||||
private void shutdown() throws InterruptedException
|
||||
@ -556,19 +558,14 @@ final class ComputerExecutor
|
||||
|
||||
case ABORT:
|
||||
if( !isOn ) return;
|
||||
displayFailure( TimeoutState.ABORT_MESSAGE );
|
||||
displayFailure( "Error running computer", TimeoutState.ABORT_MESSAGE );
|
||||
shutdown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if( event != null )
|
||||
{
|
||||
machine.handleEvent( event.name, event.args );
|
||||
if( machine.isFinished() )
|
||||
{
|
||||
displayFailure( "Error resuming bios.lua" );
|
||||
shutdown();
|
||||
}
|
||||
resumeMachine( event.name, event.args );
|
||||
}
|
||||
}
|
||||
finally
|
||||
@ -577,15 +574,39 @@ final class ComputerExecutor
|
||||
}
|
||||
}
|
||||
|
||||
private void displayFailure( String message )
|
||||
private void displayFailure( String message, String extra )
|
||||
{
|
||||
Terminal terminal = computer.getTerminal();
|
||||
boolean colour = computer.getComputerEnvironment().isColour();
|
||||
terminal.reset();
|
||||
|
||||
// Display our primary error message
|
||||
if( colour ) terminal.setTextColour( 15 - Colour.Red.ordinal() );
|
||||
terminal.write( message );
|
||||
terminal.setCursorPos( 0, 1 );
|
||||
|
||||
if( extra != null )
|
||||
{
|
||||
// Display any additional information. This generally comes from the Lua Machine, such as compilation or
|
||||
// runtime errors.
|
||||
terminal.setCursorPos( 0, terminal.getCursorY() + 1 );
|
||||
terminal.write( extra );
|
||||
}
|
||||
|
||||
// And display our generic "CC may be installed incorrectly" message.
|
||||
terminal.setCursorPos( 0, terminal.getCursorY() + 1 );
|
||||
if( colour ) terminal.setTextColour( 15 - Colour.White.ordinal() );
|
||||
terminal.write( "ComputerCraft may be installed incorrectly" );
|
||||
}
|
||||
|
||||
private void resumeMachine( String event, Object[] args ) throws InterruptedException
|
||||
{
|
||||
MachineResult result = machine.handleEvent( event, args );
|
||||
if( !result.isError() ) return;
|
||||
|
||||
displayFailure( "Error running computer", result.getMessage() );
|
||||
shutdown();
|
||||
}
|
||||
|
||||
private enum StateCommand
|
||||
{
|
||||
TURN_ON,
|
||||
|
@ -28,7 +28,6 @@ import org.squiddev.cobalt.lib.*;
|
||||
import org.squiddev.cobalt.lib.platform.VoidResourceManipulator;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@ -53,6 +52,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
|
||||
private final Computer m_computer;
|
||||
private final TimeoutState timeout;
|
||||
private final TimeoutDebugHandler debug;
|
||||
|
||||
private LuaState m_state;
|
||||
private LuaTable m_globals;
|
||||
@ -64,48 +64,12 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
{
|
||||
m_computer = computer;
|
||||
this.timeout = timeout;
|
||||
debug = new TimeoutDebugHandler();
|
||||
|
||||
// Create an environment to run in
|
||||
LuaState state = this.m_state = LuaState.builder()
|
||||
.resourceManipulator( new VoidResourceManipulator() )
|
||||
.debug( new DebugHandler()
|
||||
{
|
||||
private int count = 0;
|
||||
private boolean hasSoftAbort;
|
||||
|
||||
@Override
|
||||
public void onInstruction( DebugState ds, DebugFrame di, int pc ) throws LuaError, UnwindThrowable
|
||||
{
|
||||
// We check our current abort state every so 128 instructions.
|
||||
if( (count = (count + 1) & 127) == 0 ) handleAbort();
|
||||
super.onInstruction( ds, di, pc );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void poll() throws LuaError
|
||||
{
|
||||
handleAbort();
|
||||
}
|
||||
|
||||
private void handleAbort() throws LuaError
|
||||
{
|
||||
// If we've been hard aborted or closed then abort.
|
||||
if( timeout.isHardAborted() || m_state == null ) throw HardAbortError.INSTANCE;
|
||||
|
||||
// If the soft abort has been cleared then we can reset our flags and continue.
|
||||
if( !timeout.isSoftAborted() )
|
||||
{
|
||||
hasSoftAbort = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have fired our soft abort, but we haven't been hard aborted then everything is OK.
|
||||
if( hasSoftAbort ) return;
|
||||
|
||||
hasSoftAbort = true;
|
||||
throw new LuaError( TimeoutState.ABORT_MESSAGE );
|
||||
}
|
||||
} )
|
||||
.debug( debug )
|
||||
.coroutineExecutor( command -> {
|
||||
Tracking.addValue( m_computer, TrackingField.COROUTINES_CREATED, 1 );
|
||||
COROUTINES.execute( () -> {
|
||||
@ -166,37 +130,43 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBios( @Nonnull InputStream bios )
|
||||
public MachineResult loadBios( @Nonnull InputStream bios )
|
||||
{
|
||||
// Begin executing a file (ie, the bios)
|
||||
if( m_mainRoutine != null ) return;
|
||||
if( m_mainRoutine != null ) return MachineResult.OK;
|
||||
|
||||
try
|
||||
{
|
||||
LuaFunction value = LoadState.load( m_state, bios, "@bios.lua", m_globals );
|
||||
m_mainRoutine = new LuaThread( m_state, value, m_globals );
|
||||
return MachineResult.OK;
|
||||
}
|
||||
catch( CompileException e )
|
||||
{
|
||||
close();
|
||||
return MachineResult.error( e );
|
||||
}
|
||||
catch( IOException e )
|
||||
catch( Exception e )
|
||||
{
|
||||
ComputerCraft.log.warn( "Could not load bios.lua ", e );
|
||||
ComputerCraft.log.warn( "Could not load bios.lua", e );
|
||||
close();
|
||||
return MachineResult.GENERIC_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent( String eventName, Object[] arguments )
|
||||
public MachineResult handleEvent( String eventName, Object[] arguments )
|
||||
{
|
||||
if( m_mainRoutine == null ) return;
|
||||
if( m_mainRoutine == null ) return MachineResult.OK;
|
||||
|
||||
if( m_eventFilter != null && eventName != null && !eventName.equals( m_eventFilter ) && !eventName.equals( "terminate" ) )
|
||||
{
|
||||
return;
|
||||
return MachineResult.OK;
|
||||
}
|
||||
|
||||
// If the soft abort has been cleared then we can reset our flag.
|
||||
if( !timeout.isSoftAborted() ) debug.thrownSoftAbort = false;
|
||||
|
||||
try
|
||||
{
|
||||
Varargs resumeArgs = Constants.NONE;
|
||||
@ -206,30 +176,34 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
}
|
||||
|
||||
Varargs results = LuaThread.run( m_mainRoutine, resumeArgs );
|
||||
if( timeout.isHardAborted() ) throw new LuaError( TimeoutState.ABORT_MESSAGE );
|
||||
if( timeout.isHardAborted() ) throw HardAbortError.INSTANCE;
|
||||
|
||||
LuaValue filter = results.first();
|
||||
m_eventFilter = filter.isString() ? filter.toString() : null;
|
||||
|
||||
if( m_mainRoutine.getStatus().equals( "dead" ) ) close();
|
||||
if( m_mainRoutine.getStatus().equals( "dead" ) )
|
||||
{
|
||||
close();
|
||||
return MachineResult.GENERIC_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MachineResult.OK;
|
||||
}
|
||||
}
|
||||
catch( HardAbortError | InterruptedException e )
|
||||
{
|
||||
close();
|
||||
return MachineResult.TIMEOUT;
|
||||
}
|
||||
catch( LuaError e )
|
||||
{
|
||||
close();
|
||||
ComputerCraft.log.warn( "Top level coroutine errored", e );
|
||||
return MachineResult.error( e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished()
|
||||
{
|
||||
return m_mainRoutine == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
@ -637,11 +611,53 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link DebugHandler} which observes the {@link TimeoutState} and responds accordingly.
|
||||
*/
|
||||
private class TimeoutDebugHandler extends DebugHandler
|
||||
{
|
||||
private final TimeoutState timeout;
|
||||
private int count = 0;
|
||||
boolean thrownSoftAbort;
|
||||
|
||||
TimeoutDebugHandler()
|
||||
{
|
||||
this.timeout = CobaltLuaMachine.this.timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInstruction( DebugState ds, DebugFrame di, int pc ) throws LuaError, UnwindThrowable
|
||||
{
|
||||
// We check our current abort state every 128 instructions.
|
||||
if( (count = (count + 1) & 127) == 0 ) handleAbort();
|
||||
|
||||
super.onInstruction( ds, di, pc );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void poll() throws LuaError
|
||||
{
|
||||
handleAbort();
|
||||
}
|
||||
|
||||
private void handleAbort() throws LuaError
|
||||
{
|
||||
// If we've been hard aborted or closed then abort.
|
||||
if( timeout.isHardAborted() || m_state == null ) throw HardAbortError.INSTANCE;
|
||||
|
||||
// If we already thrown our soft abort error then don't do it again.
|
||||
if( !timeout.isSoftAborted() || thrownSoftAbort ) return;
|
||||
|
||||
thrownSoftAbort = true;
|
||||
throw new LuaError( TimeoutState.ABORT_MESSAGE );
|
||||
}
|
||||
}
|
||||
|
||||
private static class HardAbortError extends Error
|
||||
{
|
||||
private static final long serialVersionUID = 7954092008586367501L;
|
||||
|
||||
public static final HardAbortError INSTANCE = new HardAbortError();
|
||||
static final HardAbortError INSTANCE = new HardAbortError();
|
||||
|
||||
private HardAbortError()
|
||||
{
|
||||
|
@ -38,27 +38,27 @@ public interface ILuaMachine
|
||||
|
||||
/**
|
||||
* Create a function from the provided program, and set it up to run when {@link #handleEvent(String, Object[])} is
|
||||
* called
|
||||
* called.
|
||||
*
|
||||
* This should destroy the machine if it failed to load the bios.
|
||||
*
|
||||
* @param bios The stream containing the boot program.
|
||||
* @return The result of loading this machine. Will either be OK, or the error message when loading the bios.
|
||||
*/
|
||||
void loadBios( @Nonnull InputStream bios );
|
||||
MachineResult loadBios( @Nonnull InputStream bios );
|
||||
|
||||
/**
|
||||
* Resume the machine, either starting or resuming the coroutine.
|
||||
*
|
||||
* This should destroy the machine if it failed to execute successfully.
|
||||
*
|
||||
* @param eventName The name of the event. This is {@code null} when first starting the machine. Note, this may
|
||||
* do nothing if it does not match the event filter.
|
||||
* @param arguments The arguments for this event.
|
||||
* @return The result of loading this machine. Will either be OK, or the error message that occurrred when
|
||||
* executing.
|
||||
*/
|
||||
void handleEvent( @Nullable String eventName, @Nullable Object[] arguments );
|
||||
|
||||
/**
|
||||
* If this machine has finished executing, either due to an error or it just shutting down.
|
||||
*
|
||||
* @return If this machine is finished.
|
||||
*/
|
||||
boolean isFinished();
|
||||
MachineResult handleEvent( @Nullable String eventName, @Nullable Object[] arguments );
|
||||
|
||||
/**
|
||||
* Close the Lua machine, aborting any running functions and deleting the internal state.
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.lua;
|
||||
|
||||
import dan200.computercraft.core.computer.TimeoutState;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* The result of executing an action on a machine.
|
||||
*
|
||||
* Errors should halt the machine and display the error to the user.
|
||||
*
|
||||
* @see ILuaMachine#loadBios(InputStream)
|
||||
* @see ILuaMachine#handleEvent(String, Object[])
|
||||
*/
|
||||
public final class MachineResult
|
||||
{
|
||||
/**
|
||||
* Represents a successful execution
|
||||
*/
|
||||
public static final MachineResult OK = new MachineResult( false, null );
|
||||
|
||||
/**
|
||||
* An execution which timed out.
|
||||
*/
|
||||
public static final MachineResult TIMEOUT = new MachineResult( true, TimeoutState.ABORT_MESSAGE );
|
||||
|
||||
/**
|
||||
* An error with no user-friendly error message
|
||||
*/
|
||||
public static final MachineResult GENERIC_ERROR = new MachineResult( true, null );
|
||||
|
||||
private final boolean error;
|
||||
private final String message;
|
||||
|
||||
private MachineResult( boolean error, String message )
|
||||
{
|
||||
this.message = message;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public static MachineResult error( @Nonnull String error )
|
||||
{
|
||||
return new MachineResult( true, error );
|
||||
}
|
||||
|
||||
public static MachineResult error( @Nonnull Exception error )
|
||||
{
|
||||
return new MachineResult( true, error.getMessage() );
|
||||
}
|
||||
|
||||
public boolean isError()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getMessage()
|
||||
{
|
||||
return message;
|
||||
}
|
||||
}
|
@ -102,10 +102,6 @@ public class ComputerBootstrap
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ComputerThread.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static class AssertApi implements ILuaAPI
|
||||
|
Loading…
Reference in New Issue
Block a user