CC-Tweaked/src/main/java/dan200/computercraft/core/computer/Computer.java

220 lines
5.5 KiB
Java

/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.computer;
import com.google.common.base.Objects;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.terminal.Terminal;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Represents a computer which may exist in-world or elsewhere.
*
* Note, this class has several (read: far, far too many) responsibilities, so can get a little unwieldy at times.
*
* <ul>
* <li>Updates the {@link Environment}.</li>
* <li>Keeps track of whether the computer is on and blinking.</li>
* <li>Monitors whether the computer's visible state (redstone, on/off/blinking) has changed.</li>
* <li>Passes commands and events to the {@link ComputerExecutor}.</li>
* <li>Passes main thread tasks to the {@link MainThreadExecutor}.</li>
* </ul>
*/
public class Computer
{
private static final int START_DELAY = 50;
// Various properties of the computer
private int id;
private String label = null;
// Read-only fields about the computer
private final IComputerEnvironment environment;
private final Terminal terminal;
private final ComputerExecutor executor;
private final MainThreadExecutor serverExecutor;
// Additional state about the computer and its environment.
private boolean blinking = false;
private final Environment internalEnvironment = new Environment( this );
private final AtomicBoolean externalOutputChanged = new AtomicBoolean();
private boolean startRequested;
private int ticksSinceStart = -1;
public Computer( IComputerEnvironment environment, Terminal terminal, int id )
{
this.id = id;
this.environment = environment;
this.terminal = terminal;
executor = new ComputerExecutor( this );
serverExecutor = new MainThreadExecutor( this );
}
IComputerEnvironment getComputerEnvironment()
{
return environment;
}
FileSystem getFileSystem()
{
return executor.getFileSystem();
}
Terminal getTerminal()
{
return terminal;
}
public Environment getEnvironment()
{
return internalEnvironment;
}
public IAPIEnvironment getAPIEnvironment()
{
return internalEnvironment;
}
public boolean isOn()
{
return executor.isOn();
}
public void turnOn()
{
startRequested = true;
}
public void shutdown()
{
executor.queueStop( false, false );
}
public void reboot()
{
executor.queueStop( true, false );
}
public void unload()
{
executor.queueStop( false, true );
}
public void queueEvent( String event, Object[] args )
{
executor.queueEvent( event, args );
}
/**
* Queue a task to be run on the main thread, using {@link MainThread}.
*
* @param runnable The task to run
* @return If the task was successfully queued (namely, whether there is space on it).
*/
public boolean queueMainThread( Runnable runnable )
{
return serverExecutor.enqueue( runnable );
}
public IWorkMonitor getMainThreadMonitor()
{
return serverExecutor;
}
public int getID()
{
return id;
}
public int assignID()
{
if( id < 0 )
{
id = environment.assignNewID();
}
return id;
}
public void setID( int id )
{
this.id = id;
}
public String getLabel()
{
return label;
}
public void setLabel( String label )
{
if( !Objects.equal( label, this.label ) )
{
this.label = label;
externalOutputChanged.set( true );
}
}
public void tick()
{
// We keep track of the number of ticks since the last start, only
if( ticksSinceStart >= 0 && ticksSinceStart <= START_DELAY ) ticksSinceStart++;
if( startRequested && (ticksSinceStart < 0 || ticksSinceStart > START_DELAY) )
{
startRequested = false;
if( !executor.isOn() )
{
ticksSinceStart = 0;
executor.queueStart();
}
}
executor.tick();
// Update the environment's internal state.
internalEnvironment.tick();
// Propagate the environment's output to the world.
if( internalEnvironment.updateOutput() ) externalOutputChanged.set( true );
// Set output changed if the terminal has changed from blinking to not
boolean blinking = terminal.getCursorBlink() &&
terminal.getCursorX() >= 0 && terminal.getCursorX() < terminal.getWidth() &&
terminal.getCursorY() >= 0 && terminal.getCursorY() < terminal.getHeight();
if( blinking != this.blinking )
{
this.blinking = blinking;
externalOutputChanged.set( true );
}
}
void markChanged()
{
externalOutputChanged.set( true );
}
public boolean pollAndResetChanged()
{
return externalOutputChanged.getAndSet( false );
}
public boolean isBlinking()
{
return isOn() && blinking;
}
public void addApi( ILuaAPI api )
{
executor.addApi( api );
}
}