mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-08-06 13:58:34 +00:00
Merge pull request #153 from SquidDev-CC/feature/main-thread-limits
This commit is contained in:
commit
a600213b00
@ -24,6 +24,7 @@ import dan200.computercraft.api.turtle.event.TurtleAction;
|
|||||||
import dan200.computercraft.core.apis.AddressPredicate;
|
import dan200.computercraft.core.apis.AddressPredicate;
|
||||||
import dan200.computercraft.core.apis.ApiFactories;
|
import dan200.computercraft.core.apis.ApiFactories;
|
||||||
import dan200.computercraft.core.apis.http.websocket.Websocket;
|
import dan200.computercraft.core.apis.http.websocket.Websocket;
|
||||||
|
import dan200.computercraft.core.computer.MainThread;
|
||||||
import dan200.computercraft.core.filesystem.ComboMount;
|
import dan200.computercraft.core.filesystem.ComboMount;
|
||||||
import dan200.computercraft.core.filesystem.FileMount;
|
import dan200.computercraft.core.filesystem.FileMount;
|
||||||
import dan200.computercraft.core.filesystem.JarMount;
|
import dan200.computercraft.core.filesystem.JarMount;
|
||||||
@ -95,6 +96,7 @@ import java.net.URL;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
@ -133,9 +135,12 @@ public class ComputerCraft
|
|||||||
public static boolean disable_lua51_features = false;
|
public static boolean disable_lua51_features = false;
|
||||||
public static String default_computer_settings = "";
|
public static String default_computer_settings = "";
|
||||||
public static boolean debug_enable = true;
|
public static boolean debug_enable = true;
|
||||||
public static int computer_threads = 1;
|
|
||||||
public static boolean logPeripheralErrors = false;
|
public static boolean logPeripheralErrors = false;
|
||||||
|
|
||||||
|
public static int computer_threads = 1;
|
||||||
|
public static long maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos( 10 );
|
||||||
|
public static long maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos( 5 );
|
||||||
|
|
||||||
public static boolean http_enable = true;
|
public static boolean http_enable = true;
|
||||||
public static boolean http_websocket_enable = true;
|
public static boolean http_websocket_enable = true;
|
||||||
public static AddressPredicate http_whitelist = new AddressPredicate( DEFAULT_HTTP_WHITELIST );
|
public static AddressPredicate http_whitelist = new AddressPredicate( DEFAULT_HTTP_WHITELIST );
|
||||||
@ -304,6 +309,7 @@ public class ComputerCraft
|
|||||||
{
|
{
|
||||||
ComputerCraft.serverComputerRegistry.reset();
|
ComputerCraft.serverComputerRegistry.reset();
|
||||||
WirelessNetwork.resetNetworks();
|
WirelessNetwork.resetNetworks();
|
||||||
|
MainThread.reset();
|
||||||
Tracking.reset();
|
Tracking.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,6 +321,7 @@ public class ComputerCraft
|
|||||||
{
|
{
|
||||||
ComputerCraft.serverComputerRegistry.reset();
|
ComputerCraft.serverComputerRegistry.reset();
|
||||||
WirelessNetwork.resetNetworks();
|
WirelessNetwork.resetNetworks();
|
||||||
|
MainThread.reset();
|
||||||
Tracking.reset();
|
Tracking.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ package dan200.computercraft.api.peripheral;
|
|||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.filesystem.IMount;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||||
|
import dan200.computercraft.api.lua.ILuaContext;
|
||||||
|
import dan200.computercraft.api.lua.ILuaTask;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -179,7 +181,7 @@ public interface IComputerAccess
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a reachable peripheral with the given attachement name. This is a equivalent to
|
* Get a reachable peripheral with the given attachment name. This is a equivalent to
|
||||||
* {@link #getAvailablePeripherals()}{@code .get(name)}, though may be more performant.
|
* {@link #getAvailablePeripherals()}{@code .get(name)}, though may be more performant.
|
||||||
*
|
*
|
||||||
* @param name The peripheral's attached name
|
* @param name The peripheral's attached name
|
||||||
@ -191,4 +193,23 @@ public interface IComputerAccess
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a {@link IWorkMonitor} for tasks your peripheral might execute on the main (server) thread.
|
||||||
|
*
|
||||||
|
* This should be used to ensure your peripheral integrates with ComputerCraft's monitoring and limiting of how much
|
||||||
|
* server time each computer consumes. You should not need to use this if you use
|
||||||
|
* {@link ILuaContext#issueMainThreadTask(ILuaTask)} - this is intended for mods with their own system for running
|
||||||
|
* work on the main thread.
|
||||||
|
*
|
||||||
|
* Please note that the returned implementation is <em>not</em> thread-safe, and should only be used from the main
|
||||||
|
* thread.
|
||||||
|
*
|
||||||
|
* @return The work monitor for the main thread, or {@code null} if this computer does not have one.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default IWorkMonitor getMainThreadMonitor()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.peripheral;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monitors "work" associated with a computer, keeping track of how much a computer has done, and ensuring every
|
||||||
|
* computer receives a fair share of any processing time.
|
||||||
|
*
|
||||||
|
* This is primarily intended for work done by peripherals on the main thread (such as on a tile entity's tick), but
|
||||||
|
* could be used for other purposes (such as complex computations done on another thread).
|
||||||
|
*
|
||||||
|
* Before running a task, one should call {@link #canWork()} to determine if the computer is currently allowed to
|
||||||
|
* execute work. If that returns true, you should execute the task and use {@link #trackWork(long, TimeUnit)} to inform
|
||||||
|
* the monitor how long that task took.
|
||||||
|
*
|
||||||
|
* Alternatively, use {@link #runWork(Runnable)} to run and keep track of work.
|
||||||
|
*
|
||||||
|
* @see IComputerAccess#getMainThreadMonitor()
|
||||||
|
*/
|
||||||
|
public interface IWorkMonitor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* If the owning computer is currently allowed to execute work.
|
||||||
|
*
|
||||||
|
* @return If we can execute work right now.
|
||||||
|
*/
|
||||||
|
boolean canWork();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the owning computer is currently allowed to execute work, and has ample time to do so.
|
||||||
|
*
|
||||||
|
* This is effectively a more restrictive form of {@link #canWork()}. One should use that in order to determine if
|
||||||
|
* you may do an initial piece of work, and {@link #shouldWork()} to determine if any additional task may be
|
||||||
|
* performed.
|
||||||
|
*
|
||||||
|
* @return If we should execute work right now.
|
||||||
|
*/
|
||||||
|
boolean shouldWork();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inform the monitor how long some piece of work took to execute.
|
||||||
|
*
|
||||||
|
* @param time The time some task took to run
|
||||||
|
* @param unit The unit that {@code time} was measured in.
|
||||||
|
*/
|
||||||
|
void trackWork( long time, @Nonnull TimeUnit unit );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a task if possible, and inform the monitor of how long it took.
|
||||||
|
*
|
||||||
|
* @param runnable The task to run.
|
||||||
|
* @return If the task was actually run (namely, {@link #canWork()} returned {@code true}).
|
||||||
|
*/
|
||||||
|
default boolean runWork( @Nonnull Runnable runnable )
|
||||||
|
{
|
||||||
|
Objects.requireNonNull( runnable, "runnable should not be null" );
|
||||||
|
if( !canWork() ) return false;
|
||||||
|
|
||||||
|
long start = System.nanoTime();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
trackWork( System.nanoTime() - start, TimeUnit.NANOSECONDS );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -10,8 +10,7 @@ import com.google.common.base.Preconditions;
|
|||||||
import dan200.computercraft.api.filesystem.IMount;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||||
import dan200.computercraft.core.computer.IComputerOwned;
|
|
||||||
import dan200.computercraft.core.filesystem.FileSystem;
|
import dan200.computercraft.core.filesystem.FileSystem;
|
||||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ import java.util.HashSet;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public abstract class ComputerAccess implements IComputerAccess, IComputerOwned
|
public abstract class ComputerAccess implements IComputerAccess
|
||||||
{
|
{
|
||||||
private final IAPIEnvironment m_environment;
|
private final IAPIEnvironment m_environment;
|
||||||
private final Set<String> m_mounts = new HashSet<>();
|
private final Set<String> m_mounts = new HashSet<>();
|
||||||
@ -128,9 +127,9 @@ public abstract class ComputerAccess implements IComputerAccess, IComputerOwned
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Computer getComputer()
|
public IWorkMonitor getMainThreadMonitor()
|
||||||
{
|
{
|
||||||
return m_environment.getComputer();
|
return m_environment.getMainThreadMonitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String findFreeLocation( String desiredLoc )
|
private String findFreeLocation( String desiredLoc )
|
||||||
|
@ -7,9 +7,8 @@
|
|||||||
package dan200.computercraft.core.apis;
|
package dan200.computercraft.core.apis;
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||||
import dan200.computercraft.core.computer.IComputerEnvironment;
|
import dan200.computercraft.core.computer.IComputerEnvironment;
|
||||||
import dan200.computercraft.core.computer.IComputerOwned;
|
|
||||||
import dan200.computercraft.core.filesystem.FileSystem;
|
import dan200.computercraft.core.filesystem.FileSystem;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.tracking.TrackingField;
|
||||||
@ -17,7 +16,7 @@ import dan200.computercraft.core.tracking.TrackingField;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public interface IAPIEnvironment extends IComputerOwned
|
public interface IAPIEnvironment
|
||||||
{
|
{
|
||||||
String[] SIDE_NAMES = new String[] {
|
String[] SIDE_NAMES = new String[] {
|
||||||
"bottom", "top", "back", "front", "right", "left",
|
"bottom", "top", "back", "front", "right", "left",
|
||||||
@ -30,15 +29,14 @@ public interface IAPIEnvironment extends IComputerOwned
|
|||||||
void onPeripheralChanged( int side, @Nullable IPeripheral newPeripheral );
|
void onPeripheralChanged( int side, @Nullable IPeripheral newPeripheral );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
Computer getComputer();
|
|
||||||
|
|
||||||
int getComputerID();
|
int getComputerID();
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
IComputerEnvironment getComputerEnvironment();
|
IComputerEnvironment getComputerEnvironment();
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
IWorkMonitor getMainThreadMonitor();
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Terminal getTerminal();
|
Terminal getTerminal();
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ package dan200.computercraft.core.computer;
|
|||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import dan200.computercraft.api.lua.ILuaAPI;
|
import dan200.computercraft.api.lua.ILuaAPI;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||||
import dan200.computercraft.core.filesystem.FileSystem;
|
import dan200.computercraft.core.filesystem.FileSystem;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
@ -25,6 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
* <li>Keeps track of whether the computer is on and blinking.</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>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 commands and events to the {@link ComputerExecutor}.</li>
|
||||||
|
* <li>Passes main thread tasks to the {@link MainThreadExecutor}.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class Computer
|
public class Computer
|
||||||
@ -39,6 +41,7 @@ public class Computer
|
|||||||
private final IComputerEnvironment m_environment;
|
private final IComputerEnvironment m_environment;
|
||||||
private final Terminal m_terminal;
|
private final Terminal m_terminal;
|
||||||
private final ComputerExecutor executor;
|
private final ComputerExecutor executor;
|
||||||
|
private final MainThreadExecutor serverExecutor;
|
||||||
|
|
||||||
// Additional state about the computer and its environment.
|
// Additional state about the computer and its environment.
|
||||||
private boolean m_blinking = false;
|
private boolean m_blinking = false;
|
||||||
@ -55,6 +58,7 @@ public class Computer
|
|||||||
m_terminal = terminal;
|
m_terminal = terminal;
|
||||||
|
|
||||||
executor = new ComputerExecutor( this );
|
executor = new ComputerExecutor( this );
|
||||||
|
serverExecutor = new MainThreadExecutor( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
IComputerEnvironment getComputerEnvironment()
|
IComputerEnvironment getComputerEnvironment()
|
||||||
@ -112,6 +116,22 @@ public class Computer
|
|||||||
executor.queueEvent( event, 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()
|
public int getID()
|
||||||
{
|
{
|
||||||
return m_id;
|
return m_id;
|
||||||
|
@ -133,7 +133,7 @@ final class ComputerExecutor
|
|||||||
*
|
*
|
||||||
* Note, this should be empty if this computer is off - it is cleared on shutdown and when turning on again.
|
* Note, this should be empty if this computer is off - it is cleared on shutdown and when turning on again.
|
||||||
*/
|
*/
|
||||||
private final Queue<Event> eventQueue = new ArrayDeque<>();
|
private final Queue<Event> eventQueue = new ArrayDeque<>( 4 );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether we interrupted an event and so should resume it instead of executing another task.
|
* Whether we interrupted an event and so should resume it instead of executing another task.
|
||||||
|
@ -112,7 +112,7 @@ public class ComputerThread
|
|||||||
|
|
||||||
long at = a.virtualRuntime, bt = b.virtualRuntime;
|
long at = a.virtualRuntime, bt = b.virtualRuntime;
|
||||||
if( at == bt ) return Integer.compare( a.hashCode(), b.hashCode() );
|
if( at == bt ) return Integer.compare( a.hashCode(), b.hashCode() );
|
||||||
return Long.compare( at, bt );
|
return at < bt ? -1 : 1;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
package dan200.computercraft.core.computer;
|
package dan200.computercraft.core.computer;
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||||
import dan200.computercraft.core.filesystem.FileSystem;
|
import dan200.computercraft.core.filesystem.FileSystem;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
@ -58,13 +59,6 @@ public final class Environment implements IAPIEnvironment
|
|||||||
this.computer = computer;
|
this.computer = computer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public Computer getComputer()
|
|
||||||
{
|
|
||||||
return computer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getComputerID()
|
public int getComputerID()
|
||||||
{
|
{
|
||||||
@ -78,6 +72,13 @@ public final class Environment implements IAPIEnvironment
|
|||||||
return computer.getComputerEnvironment();
|
return computer.getComputerEnvironment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public IWorkMonitor getMainThreadMonitor()
|
||||||
|
{
|
||||||
|
return computer.getMainThreadMonitor();
|
||||||
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public Terminal getTerminal()
|
public Terminal getTerminal()
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.computer;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link IComputerOwned} marks objects which are known to belong to a computer.
|
|
||||||
*
|
|
||||||
* The primary purpose of this is to allow Plethora (and potentially other mods) to run the various tracking methods
|
|
||||||
* on {@link Computer}.
|
|
||||||
*
|
|
||||||
* You can generally assume {@link IComputerAccess} implements this interface, though you should always check first.
|
|
||||||
*
|
|
||||||
* @see dan200.computercraft.core.apis.ComputerAccess
|
|
||||||
* @see dan200.computercraft.shared.peripheral.modem.wired.WiredModemPeripheral and the peripheral wrapper
|
|
||||||
*/
|
|
||||||
public interface IComputerOwned
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the computer associated with this object
|
|
||||||
*
|
|
||||||
* @return The associated object, or {@code null} if none is known.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
Computer getComputer();
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.computer;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
public interface ITask
|
|
||||||
{
|
|
||||||
@Nonnull
|
|
||||||
Computer getOwner();
|
|
||||||
|
|
||||||
void execute();
|
|
||||||
}
|
|
@ -6,66 +6,189 @@
|
|||||||
|
|
||||||
package dan200.computercraft.core.computer;
|
package dan200.computercraft.core.computer;
|
||||||
|
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
import dan200.computercraft.ComputerCraft;
|
||||||
|
import dan200.computercraft.api.lua.ILuaTask;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.Queue;
|
import java.util.HashSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs tasks on the main (server) thread, ticks {@link MainThreadExecutor}s, and limits how much time is used this
|
||||||
|
* tick.
|
||||||
|
*
|
||||||
|
* Similar to {@link MainThreadExecutor}, the {@link MainThread} can be in one of three states: cool, hot and cooling.
|
||||||
|
* However, the implementation here is a little different:
|
||||||
|
*
|
||||||
|
* {@link MainThread} starts cool, and runs as many tasks as it can in the current {@link #budget}ns. Any external tasks
|
||||||
|
* (those run by tile entities, etc...) will also consume the budget
|
||||||
|
*
|
||||||
|
* Next tick, we put {@link ComputerCraft#maxMainGlobalTime} into our budget (and clamp it to that value to). If we're
|
||||||
|
* still over budget, then we should not execute <em>any</em> work (either as part of {@link MainThread} or externally).
|
||||||
|
*/
|
||||||
public class MainThread
|
public class MainThread
|
||||||
{
|
{
|
||||||
private static final int MAX_TASKS_PER_TICK = 1000;
|
/**
|
||||||
private static final int MAX_TASKS_TOTAL = 50000;
|
* An internal counter for {@link ILuaTask} ids.
|
||||||
|
*
|
||||||
|
* @see dan200.computercraft.api.lua.ILuaContext#issueMainThreadTask(ILuaTask)
|
||||||
|
* @see #getUniqueTaskID()
|
||||||
|
*/
|
||||||
|
private static final AtomicLong lastTaskId = new AtomicLong();
|
||||||
|
|
||||||
private static final Queue<ITask> m_outstandingTasks = new ArrayDeque<>();
|
/**
|
||||||
private static final Object m_nextUnusedTaskIDLock = new Object();
|
* The queue of {@link MainThreadExecutor}s with tasks to perform.
|
||||||
private static long m_nextUnusedTaskID = 0;
|
*/
|
||||||
|
private static final TreeSet<MainThreadExecutor> executors = new TreeSet<>( ( a, b ) -> {
|
||||||
|
if( a == b ) return 0; // Should never happen, but let's be consistent here
|
||||||
|
|
||||||
|
long at = a.virtualTime, bt = b.virtualTime;
|
||||||
|
if( at == bt ) return Integer.compare( a.hashCode(), b.hashCode() );
|
||||||
|
return at < bt ? -1 : 1;
|
||||||
|
} );
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set of executors which went over budget in a previous tick, and are waiting for their time to run down.
|
||||||
|
*
|
||||||
|
* @see MainThreadExecutor#tickCooling()
|
||||||
|
* @see #cooling(MainThreadExecutor)
|
||||||
|
*/
|
||||||
|
private static final HashSet<MainThreadExecutor> cooling = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current tick number. This is used by {@link MainThreadExecutor} to determine when to reset its own time
|
||||||
|
* counter.
|
||||||
|
*
|
||||||
|
* @see #currentTick()
|
||||||
|
*/
|
||||||
|
private static int currentTick;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The remaining budgeted time for this tick. This may be negative, in the case that we've gone over budget.
|
||||||
|
*/
|
||||||
|
private static long budget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we should be executing any work this tick.
|
||||||
|
*
|
||||||
|
* This is true iff {@code MAX_TICK_TIME - currentTime} was true <em>at the beginning of the tick</em>.
|
||||||
|
*/
|
||||||
|
private static boolean canExecute = true;
|
||||||
|
|
||||||
|
private static long minimumTime = 0;
|
||||||
|
|
||||||
public static long getUniqueTaskID()
|
public static long getUniqueTaskID()
|
||||||
{
|
{
|
||||||
synchronized( m_nextUnusedTaskIDLock )
|
return lastTaskId.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void queue( @Nonnull MainThreadExecutor executor, boolean sleeper )
|
||||||
|
{
|
||||||
|
synchronized( executors )
|
||||||
{
|
{
|
||||||
return ++m_nextUnusedTaskID;
|
if( executor.onQueue ) throw new IllegalStateException( "Cannot queue already queued executor" );
|
||||||
|
executor.onQueue = true;
|
||||||
|
executor.updateTime();
|
||||||
|
|
||||||
|
// We're not currently on the queue, so update its current execution time to
|
||||||
|
// ensure its at least as high as the minimum.
|
||||||
|
long newRuntime = minimumTime;
|
||||||
|
|
||||||
|
// Slow down new computers a little bit.
|
||||||
|
if( executor.virtualTime == 0 ) newRuntime += ComputerCraft.maxMainComputerTime;
|
||||||
|
|
||||||
|
executor.virtualTime = Math.max( newRuntime, executor.virtualTime );
|
||||||
|
|
||||||
|
executors.add( executor );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean queueTask( ITask task )
|
static void cooling( @Nonnull MainThreadExecutor executor )
|
||||||
{
|
{
|
||||||
synchronized( m_outstandingTasks )
|
cooling.add( executor );
|
||||||
{
|
}
|
||||||
if( m_outstandingTasks.size() < MAX_TASKS_TOTAL )
|
|
||||||
{
|
static void consumeTime( long time )
|
||||||
m_outstandingTasks.offer( task );
|
{
|
||||||
return true;
|
budget -= time;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
static boolean canExecute()
|
||||||
|
{
|
||||||
|
return canExecute;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int currentTick()
|
||||||
|
{
|
||||||
|
return currentTick;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void executePendingTasks()
|
public static void executePendingTasks()
|
||||||
{
|
{
|
||||||
int tasksThisTick = 0;
|
// Move onto the next tick and cool down the global executor. We're allowed to execute if we have _any_ time
|
||||||
while( tasksThisTick < MAX_TASKS_PER_TICK )
|
// allocated for this tick. This means we'll stick much closer to doing MAX_TICK_TIME work every tick.
|
||||||
|
//
|
||||||
|
// Of course, we'll go over the MAX_TICK_TIME most of the time, but eventually that overrun will accumulate
|
||||||
|
// and we'll skip a whole tick - bringing the average back down again.
|
||||||
|
currentTick++;
|
||||||
|
budget += Math.min( budget + ComputerCraft.maxMainGlobalTime, ComputerCraft.maxMainGlobalTime );
|
||||||
|
canExecute = budget > 0;
|
||||||
|
|
||||||
|
// Cool down any warm computers.
|
||||||
|
cooling.removeIf( MainThreadExecutor::tickCooling );
|
||||||
|
|
||||||
|
if( !canExecute ) return;
|
||||||
|
|
||||||
|
// Run until we meet the deadline.
|
||||||
|
long start = System.nanoTime();
|
||||||
|
long deadline = start + budget;
|
||||||
|
while( true )
|
||||||
{
|
{
|
||||||
ITask task = null;
|
MainThreadExecutor executor;
|
||||||
synchronized( m_outstandingTasks )
|
synchronized( executors )
|
||||||
{
|
{
|
||||||
task = m_outstandingTasks.poll();
|
executor = executors.pollFirst();
|
||||||
}
|
}
|
||||||
if( task != null )
|
if( executor == null ) break;
|
||||||
{
|
|
||||||
long start = System.nanoTime();
|
|
||||||
task.execute();
|
|
||||||
|
|
||||||
long stop = System.nanoTime();
|
long taskStart = System.nanoTime();
|
||||||
Computer computer = task.getOwner();
|
executor.execute();
|
||||||
if( computer != null ) Tracking.addServerTiming( computer, stop - start );
|
|
||||||
|
|
||||||
++tasksThisTick;
|
long taskStop = System.nanoTime();
|
||||||
}
|
synchronized( executors )
|
||||||
else
|
|
||||||
{
|
{
|
||||||
break;
|
if( executor.afterExecute( taskStop - taskStart ) ) executors.add( executor );
|
||||||
|
|
||||||
|
// Compute the new minimum time (including the next task on the queue too). Note that this may also include
|
||||||
|
// time spent in external tasks.
|
||||||
|
long newMinimum = executor.virtualTime;
|
||||||
|
if( !executors.isEmpty() )
|
||||||
|
{
|
||||||
|
MainThreadExecutor next = executors.first();
|
||||||
|
if( next.virtualTime < newMinimum ) newMinimum = next.virtualTime;
|
||||||
|
}
|
||||||
|
minimumTime = Math.max( minimumTime, newMinimum );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( taskStop >= deadline ) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
consumeTime( System.nanoTime() - start );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reset()
|
||||||
|
{
|
||||||
|
currentTick = 0;
|
||||||
|
budget = 0;
|
||||||
|
canExecute = true;
|
||||||
|
minimumTime = 0;
|
||||||
|
lastTaskId.set( 0 );
|
||||||
|
cooling.clear();
|
||||||
|
synchronized( executors )
|
||||||
|
{
|
||||||
|
executors.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
* 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.computer;
|
||||||
|
|
||||||
|
import dan200.computercraft.ComputerCraft;
|
||||||
|
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||||
|
import dan200.computercraft.core.tracking.Tracking;
|
||||||
|
import dan200.computercraft.shared.turtle.core.TurtleBrain;
|
||||||
|
import net.minecraft.tileentity.TileEntity;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of tasks that a {@link Computer} should run on the main thread and how long that has been spent executing
|
||||||
|
* them.
|
||||||
|
*
|
||||||
|
* This provides rate-limiting mechanism for tasks enqueued with {@link Computer#queueMainThread(Runnable)}, but also
|
||||||
|
* those run elsewhere (such as during the turtle's tick - see {@link TurtleBrain#update()}). In order to handle this,
|
||||||
|
* the executor goes through three stages:
|
||||||
|
*
|
||||||
|
* When {@link State#COOL}, the computer is allocated {@link ComputerCraft#maxMainComputerTime}ns to execute any work
|
||||||
|
* this tick. At the beginning of the tick, we execute as many {@link MainThread} tasks as possible, until our timeframe
|
||||||
|
* or the global time frame has expired.
|
||||||
|
*
|
||||||
|
* Then, when other objects (such as {@link TileEntity}) are ticked, we update how much time we've used using
|
||||||
|
* {@link IWorkMonitor#trackWork(long, TimeUnit)}.
|
||||||
|
*
|
||||||
|
* Now, if anywhere during this period, we use more than our allocated time slice, the executor is marked as
|
||||||
|
* {@link State#HOT}. This means it will no longer be able to execute {@link MainThread} tasks (though will still
|
||||||
|
* execute tile entity tasks, in order to prevent the main thread from exhausting work every tick).
|
||||||
|
*
|
||||||
|
* At the beginning of the next tick, we increment the budget e by {@link ComputerCraft#maxMainComputerTime} and any
|
||||||
|
* {@link State#HOT} executors are marked as {@link State#COOLING}. They will remain cooling until their budget is
|
||||||
|
* fully replenished (is equal to {@link ComputerCraft#maxMainComputerTime}). Note, this is different to
|
||||||
|
* {@link MainThread}, which allows running when it has any budget left. When cooling, <em>no</em> tasks are executed -
|
||||||
|
* be they on the tile entity or main thread.
|
||||||
|
*
|
||||||
|
* This mechanism means that, on average, computers will use at most {@link ComputerCraft#maxMainComputerTime}ns per
|
||||||
|
* second, but one task source will not prevent others from executing.
|
||||||
|
*
|
||||||
|
* @see MainThread
|
||||||
|
* @see IWorkMonitor
|
||||||
|
* @see Computer#getMainThreadMonitor()
|
||||||
|
* @see Computer#queueMainThread(Runnable)
|
||||||
|
*/
|
||||||
|
final class MainThreadExecutor implements IWorkMonitor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The maximum number of {@link MainThread} tasks allowed on the queue.
|
||||||
|
*/
|
||||||
|
private static final int MAX_TASKS = 5000;
|
||||||
|
|
||||||
|
private final Computer computer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lock used for any changes to {@link #tasks}, or {@link #onQueue}. This will be
|
||||||
|
* used on the main thread, so locks should be kept as brief as possible.
|
||||||
|
*/
|
||||||
|
private final Object queueLock = new Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The queue of tasks which should be executed.
|
||||||
|
*
|
||||||
|
* @see #queueLock
|
||||||
|
*/
|
||||||
|
private final Queue<Runnable> tasks = new ArrayDeque<>( 4 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if this executor is currently present on the queue.
|
||||||
|
*
|
||||||
|
* This should be true iff {@link #tasks} is non-empty.
|
||||||
|
*
|
||||||
|
* @see #queueLock
|
||||||
|
* @see #enqueue(Runnable)
|
||||||
|
* @see #afterExecute(long)
|
||||||
|
*/
|
||||||
|
volatile boolean onQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The remaining budgeted time for this tick. This may be negative, in the case that we've gone over budget.
|
||||||
|
*
|
||||||
|
* @see #tickCooling()
|
||||||
|
* @see #consumeTime(long)
|
||||||
|
*/
|
||||||
|
private long budget = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last tick that {@link #budget} was updated.
|
||||||
|
*
|
||||||
|
* @see #tickCooling()
|
||||||
|
* @see #consumeTime(long)
|
||||||
|
*/
|
||||||
|
private int currentTick = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current state of this executor.
|
||||||
|
*
|
||||||
|
* @see #canWork()
|
||||||
|
*/
|
||||||
|
private State state = State.COOL;
|
||||||
|
|
||||||
|
private long pendingTime;
|
||||||
|
|
||||||
|
long virtualTime;
|
||||||
|
|
||||||
|
MainThreadExecutor( Computer computer )
|
||||||
|
{
|
||||||
|
this.computer = computer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push a task onto this executor's queue, pushing it onto the {@link MainThread} if needed.
|
||||||
|
*
|
||||||
|
* @param runnable The task to run on the main thread.
|
||||||
|
* @return Whether this task was enqueued (namely, was there space).
|
||||||
|
*/
|
||||||
|
boolean enqueue( Runnable runnable )
|
||||||
|
{
|
||||||
|
synchronized( queueLock )
|
||||||
|
{
|
||||||
|
if( tasks.size() >= MAX_TASKS || !tasks.offer( runnable ) ) return false;
|
||||||
|
if( !onQueue && state == State.COOL ) MainThread.queue( this, true );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute()
|
||||||
|
{
|
||||||
|
if( state != State.COOL ) return;
|
||||||
|
|
||||||
|
Runnable task;
|
||||||
|
synchronized( queueLock )
|
||||||
|
{
|
||||||
|
task = tasks.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( task != null ) task.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the time taken to run an {@link #enqueue(Runnable)} task.
|
||||||
|
*
|
||||||
|
* @param time The time some task took to run.
|
||||||
|
* @return Whether this should be added back to the queue.
|
||||||
|
*/
|
||||||
|
boolean afterExecute( long time )
|
||||||
|
{
|
||||||
|
consumeTime( time );
|
||||||
|
|
||||||
|
synchronized( queueLock )
|
||||||
|
{
|
||||||
|
virtualTime += time;
|
||||||
|
updateTime();
|
||||||
|
if( state != State.COOL || tasks.isEmpty() ) return onQueue = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether we should execute "external" tasks (ones not part of {@link #tasks}).
|
||||||
|
*
|
||||||
|
* @return Whether we can execute external tasks.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean canWork()
|
||||||
|
{
|
||||||
|
return state != State.COOLING && MainThread.canExecute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldWork()
|
||||||
|
{
|
||||||
|
return state == State.COOL && MainThread.canExecute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trackWork( long time, @Nonnull TimeUnit unit )
|
||||||
|
{
|
||||||
|
long nanoTime = unit.toNanos( time );
|
||||||
|
synchronized( queueLock )
|
||||||
|
{
|
||||||
|
pendingTime += nanoTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
consumeTime( nanoTime );
|
||||||
|
MainThread.consumeTime( nanoTime );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void consumeTime( long time )
|
||||||
|
{
|
||||||
|
Tracking.addServerTiming( computer, time );
|
||||||
|
|
||||||
|
// Reset the budget if moving onto a new tick. We know this is safe, as this will only have happened if
|
||||||
|
// #tickCooling() isn't called, and so we didn't overrun the previous tick.
|
||||||
|
if( currentTick != MainThread.currentTick() )
|
||||||
|
{
|
||||||
|
currentTick = MainThread.currentTick();
|
||||||
|
budget = ComputerCraft.maxMainComputerTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
budget -= time;
|
||||||
|
|
||||||
|
// If we've gone over our limit, mark us as having to cool down.
|
||||||
|
if( budget < 0 && state == State.COOL )
|
||||||
|
{
|
||||||
|
state = State.HOT;
|
||||||
|
MainThread.cooling( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move this executor forward one tick, replenishing the budget by {@link ComputerCraft#maxMainComputerTime}.
|
||||||
|
*
|
||||||
|
* @return Whether this executor has cooled down, and so is safe to run again.
|
||||||
|
*/
|
||||||
|
boolean tickCooling()
|
||||||
|
{
|
||||||
|
state = State.COOLING;
|
||||||
|
currentTick = MainThread.currentTick();
|
||||||
|
budget += Math.min( budget + ComputerCraft.maxMainComputerTime, ComputerCraft.maxMainComputerTime );
|
||||||
|
if( budget < ComputerCraft.maxMainComputerTime ) return false;
|
||||||
|
|
||||||
|
state = State.COOL;
|
||||||
|
synchronized( queueLock )
|
||||||
|
{
|
||||||
|
if( !tasks.isEmpty() && !onQueue ) MainThread.queue( this, false );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTime()
|
||||||
|
{
|
||||||
|
virtualTime += pendingTime;
|
||||||
|
pendingTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum State
|
||||||
|
{
|
||||||
|
COOL,
|
||||||
|
HOT,
|
||||||
|
COOLING,
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,6 @@ package dan200.computercraft.core.lua;
|
|||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.lua.*;
|
import dan200.computercraft.api.lua.*;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.core.computer.Computer;
|
||||||
import dan200.computercraft.core.computer.ITask;
|
|
||||||
import dan200.computercraft.core.computer.MainThread;
|
import dan200.computercraft.core.computer.MainThread;
|
||||||
import dan200.computercraft.core.computer.TimeoutState;
|
import dan200.computercraft.core.computer.TimeoutState;
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
import dan200.computercraft.core.tracking.Tracking;
|
||||||
@ -536,53 +535,36 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
{
|
{
|
||||||
// Issue command
|
// Issue command
|
||||||
final long taskID = MainThread.getUniqueTaskID();
|
final long taskID = MainThread.getUniqueTaskID();
|
||||||
final ITask iTask = new ITask()
|
final Runnable iTask = () -> {
|
||||||
{
|
try
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public Computer getOwner()
|
|
||||||
{
|
{
|
||||||
return m_computer;
|
Object[] results = task.execute();
|
||||||
|
if( results != null )
|
||||||
|
{
|
||||||
|
Object[] eventArguments = new Object[results.length + 2];
|
||||||
|
eventArguments[0] = taskID;
|
||||||
|
eventArguments[1] = true;
|
||||||
|
System.arraycopy( results, 0, eventArguments, 2, results.length );
|
||||||
|
m_computer.queueEvent( "task_complete", eventArguments );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_computer.queueEvent( "task_complete", new Object[] { taskID, true } );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
catch( LuaException e )
|
||||||
@Override
|
|
||||||
public void execute()
|
|
||||||
{
|
{
|
||||||
try
|
m_computer.queueEvent( "task_complete", new Object[] { taskID, false, e.getMessage() } );
|
||||||
{
|
}
|
||||||
Object[] results = task.execute();
|
catch( Throwable t )
|
||||||
if( results != null )
|
{
|
||||||
{
|
if( ComputerCraft.logPeripheralErrors ) ComputerCraft.log.error( "Error running task", t );
|
||||||
Object[] eventArguments = new Object[results.length + 2];
|
m_computer.queueEvent( "task_complete", new Object[] {
|
||||||
eventArguments[0] = taskID;
|
taskID, false, "Java Exception Thrown: " + t.toString()
|
||||||
eventArguments[1] = true;
|
} );
|
||||||
System.arraycopy( results, 0, eventArguments, 2, results.length );
|
|
||||||
m_computer.queueEvent( "task_complete", eventArguments );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_computer.queueEvent( "task_complete", new Object[] { taskID, true } );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( LuaException e )
|
|
||||||
{
|
|
||||||
m_computer.queueEvent( "task_complete", new Object[] {
|
|
||||||
taskID, false, e.getMessage()
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
catch( Throwable t )
|
|
||||||
{
|
|
||||||
if( ComputerCraft.logPeripheralErrors )
|
|
||||||
{
|
|
||||||
ComputerCraft.log.error( "Error running task", t );
|
|
||||||
}
|
|
||||||
m_computer.queueEvent( "task_complete", new Object[] {
|
|
||||||
taskID, false, "Java Exception Thrown: " + t.toString()
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if( MainThread.queueTask( iTask ) )
|
if( m_computer.queueMainThread( iTask ) )
|
||||||
{
|
{
|
||||||
return taskID;
|
return taskID;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static dan200.computercraft.ComputerCraft.DEFAULT_HTTP_BLACKLIST;
|
import static dan200.computercraft.ComputerCraft.DEFAULT_HTTP_BLACKLIST;
|
||||||
import static dan200.computercraft.ComputerCraft.DEFAULT_HTTP_WHITELIST;
|
import static dan200.computercraft.ComputerCraft.DEFAULT_HTTP_WHITELIST;
|
||||||
@ -32,6 +33,7 @@ public class Config
|
|||||||
private static final int MODEM_MAX_RANGE = 100000;
|
private static final int MODEM_MAX_RANGE = 100000;
|
||||||
|
|
||||||
private static final String CATEGORY_GENERAL = "general";
|
private static final String CATEGORY_GENERAL = "general";
|
||||||
|
private static final String CATEGORY_EXECUTION = "execution";
|
||||||
private static final String CATEGORY_HTTP = "http";
|
private static final String CATEGORY_HTTP = "http";
|
||||||
private static final String CATEGORY_PERIPHERAL = "peripheral";
|
private static final String CATEGORY_PERIPHERAL = "peripheral";
|
||||||
private static final String CATEGORY_TURTLE = "turtle";
|
private static final String CATEGORY_TURTLE = "turtle";
|
||||||
@ -44,9 +46,12 @@ public class Config
|
|||||||
private static Property disableLua51Features;
|
private static Property disableLua51Features;
|
||||||
private static Property defaultComputerSettings;
|
private static Property defaultComputerSettings;
|
||||||
private static Property debugEnabled;
|
private static Property debugEnabled;
|
||||||
private static Property computerThreads;
|
|
||||||
private static Property logComputerErrors;
|
private static Property logComputerErrors;
|
||||||
|
|
||||||
|
private static Property computerThreads;
|
||||||
|
private static Property maxMainGlobalTime;
|
||||||
|
private static Property maxMainComputerTime;
|
||||||
|
|
||||||
private static Property httpEnable;
|
private static Property httpEnable;
|
||||||
private static Property httpWebsocketEnable;
|
private static Property httpWebsocketEnable;
|
||||||
private static Property httpWhitelist;
|
private static Property httpWhitelist;
|
||||||
@ -97,21 +102,16 @@ public class Config
|
|||||||
maximumFilesOpen.setMinValue( 0 );
|
maximumFilesOpen.setMinValue( 0 );
|
||||||
|
|
||||||
disableLua51Features = config.get( CATEGORY_GENERAL, "disable_lua51_features", ComputerCraft.disable_lua51_features );
|
disableLua51Features = config.get( CATEGORY_GENERAL, "disable_lua51_features", ComputerCraft.disable_lua51_features );
|
||||||
disableLua51Features.setComment( "Set this to true to disable Lua 5.1 functions that will be removed in a future update. Useful for ensuring forward compatibility of your programs now." );
|
disableLua51Features.setComment( "Set this to true to disable Lua 5.1 functions that will be removed in a future " +
|
||||||
|
"update. Useful for ensuring forward compatibility of your programs now." );
|
||||||
|
|
||||||
defaultComputerSettings = config.get( CATEGORY_GENERAL, "default_computer_settings", ComputerCraft.default_computer_settings );
|
defaultComputerSettings = config.get( CATEGORY_GENERAL, "default_computer_settings", ComputerCraft.default_computer_settings );
|
||||||
defaultComputerSettings.setComment( "A comma seperated list of default system settings to set on new computers. Example: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\" will disable all autocompletion" );
|
defaultComputerSettings.setComment( "A comma seperated list of default system settings to set on new computers. Example: " +
|
||||||
|
"\"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\" will disable all autocompletion" );
|
||||||
|
|
||||||
debugEnabled = config.get( CATEGORY_GENERAL, "debug_enabled", ComputerCraft.debug_enable );
|
debugEnabled = config.get( CATEGORY_GENERAL, "debug_enabled", ComputerCraft.debug_enable );
|
||||||
debugEnabled.setComment( "Enable Lua's debug library. This is sandboxed to each computer, so is generally safe to be used by players." );
|
debugEnabled.setComment( "Enable Lua's debug library. This is sandboxed to each computer, so is generally safe to be used by players." );
|
||||||
|
|
||||||
computerThreads = config.get( CATEGORY_GENERAL, "computer_threads", ComputerCraft.computer_threads );
|
|
||||||
computerThreads
|
|
||||||
.setMinValue( 1 )
|
|
||||||
.setRequiresMcRestart( true )
|
|
||||||
.setComment( "Set the number of threads computers can run on. A higher number means more computers can run at once, but may induce lag.\n" +
|
|
||||||
"Please note that some mods may not work with a thread count higher than 1. Use with caution." );
|
|
||||||
|
|
||||||
logComputerErrors = config.get( CATEGORY_GENERAL, "log_computer_errors", ComputerCraft.logPeripheralErrors );
|
logComputerErrors = config.get( CATEGORY_GENERAL, "log_computer_errors", ComputerCraft.logPeripheralErrors );
|
||||||
logComputerErrors.setComment( "Log exceptions thrown by peripherals and other Lua objects.\n" +
|
logComputerErrors.setComment( "Log exceptions thrown by peripherals and other Lua objects.\n" +
|
||||||
"This makes it easier for mod authors to debug problems, but may result in log spam should people use buggy methods." );
|
"This makes it easier for mod authors to debug problems, but may result in log spam should people use buggy methods." );
|
||||||
@ -119,7 +119,42 @@ public class Config
|
|||||||
setOrder(
|
setOrder(
|
||||||
CATEGORY_GENERAL,
|
CATEGORY_GENERAL,
|
||||||
computerSpaceLimit, floppySpaceLimit, maximumFilesOpen,
|
computerSpaceLimit, floppySpaceLimit, maximumFilesOpen,
|
||||||
disableLua51Features, defaultComputerSettings, debugEnabled, computerThreads, logComputerErrors
|
disableLua51Features, defaultComputerSettings, debugEnabled, logComputerErrors
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Execution
|
||||||
|
renameProperty( CATEGORY_GENERAL, "computer_threads", CATEGORY_EXECUTION, "computer_threads" );
|
||||||
|
|
||||||
|
config.getCategory( CATEGORY_EXECUTION )
|
||||||
|
.setComment( "Controls execution behaviour of computers. This is largely intended for fine-tuning " +
|
||||||
|
"servers, and generally shouldn't need to be touched" );
|
||||||
|
|
||||||
|
computerThreads = config.get( CATEGORY_EXECUTION, "computer_threads", ComputerCraft.computer_threads );
|
||||||
|
computerThreads
|
||||||
|
.setMinValue( 1 )
|
||||||
|
.setRequiresMcRestart( true )
|
||||||
|
.setComment( "Set the number of threads computers can run on. A higher number means more computers can " +
|
||||||
|
"run at once, but may induce lag.\n" +
|
||||||
|
"Please note that some mods may not work with a thread count higher than 1. Use with caution." );
|
||||||
|
|
||||||
|
maxMainGlobalTime = config.get( CATEGORY_EXECUTION, "max_main_global_time", (int) TimeUnit.NANOSECONDS.toMillis( ComputerCraft.maxMainGlobalTime ) );
|
||||||
|
maxMainGlobalTime
|
||||||
|
.setMinValue( 1 )
|
||||||
|
.setComment( "The maximum time that can be spent executing tasks in a single tick, in milliseconds.\n" +
|
||||||
|
"Note, we will quite possibly go over this limit, as there's no way to tell how long a will take - this aims " +
|
||||||
|
"to be the upper bound of the average time." );
|
||||||
|
|
||||||
|
maxMainComputerTime = config.get( CATEGORY_EXECUTION, "max_main_computer_time", (int) TimeUnit.NANOSECONDS.toMillis( ComputerCraft.maxMainComputerTime ) );
|
||||||
|
maxMainComputerTime
|
||||||
|
.setMinValue( 1 )
|
||||||
|
.setComment( "The ideal maximum time a computer can execute for in a tick, in milliseconds.\n" +
|
||||||
|
"Note, we will quite possibly go over this limit, as there's no way to tell how long a will take - this aims " +
|
||||||
|
"to be the upper bound of the average time." );
|
||||||
|
|
||||||
|
setOrder(
|
||||||
|
CATEGORY_EXECUTION,
|
||||||
|
computerThreads, maxMainGlobalTime, maxMainComputerTime
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,20 +164,28 @@ public class Config
|
|||||||
renameProperty( CATEGORY_GENERAL, "http_whitelist", CATEGORY_HTTP, "whitelist" );
|
renameProperty( CATEGORY_GENERAL, "http_whitelist", CATEGORY_HTTP, "whitelist" );
|
||||||
renameProperty( CATEGORY_GENERAL, "http_blacklist", CATEGORY_HTTP, "blacklist" );
|
renameProperty( CATEGORY_GENERAL, "http_blacklist", CATEGORY_HTTP, "blacklist" );
|
||||||
|
|
||||||
|
config.getCategory( CATEGORY_HTTP )
|
||||||
|
.setComment( "Controls the HTTP API" );
|
||||||
|
|
||||||
httpEnable = config.get( CATEGORY_HTTP, "enabled", ComputerCraft.http_enable );
|
httpEnable = config.get( CATEGORY_HTTP, "enabled", ComputerCraft.http_enable );
|
||||||
httpEnable.setComment( "Enable the \"http\" API on Computers (see \"http_whitelist\" and \"http_blacklist\" for more fine grained control than this)" );
|
httpEnable.setComment( "Enable the \"http\" API on Computers (see \"http_whitelist\" and \"http_blacklist\" for " +
|
||||||
|
"more fine grained control than this)" );
|
||||||
|
|
||||||
httpWebsocketEnable = config.get( CATEGORY_HTTP, "websocket_enabled", ComputerCraft.http_websocket_enable );
|
httpWebsocketEnable = config.get( CATEGORY_HTTP, "websocket_enabled", ComputerCraft.http_websocket_enable );
|
||||||
httpWebsocketEnable.setComment( "Enable use of http websockets. This requires the \"http_enable\" option to also be true." );
|
httpWebsocketEnable.setComment( "Enable use of http websockets. This requires the \"http_enable\" option to also be true." );
|
||||||
|
|
||||||
httpWhitelist = config.get( CATEGORY_HTTP, "whitelist", DEFAULT_HTTP_WHITELIST );
|
httpWhitelist = config.get( CATEGORY_HTTP, "whitelist", DEFAULT_HTTP_WHITELIST );
|
||||||
httpWhitelist.setComment( "A list of wildcards for domains or IP ranges that can be accessed through the \"http\" API on Computers.\n" +
|
httpWhitelist.setComment( "A list of wildcards for domains or IP ranges that can be accessed through the " +
|
||||||
"Set this to \"*\" to access to the entire internet. Example: \"*.pastebin.com\" will restrict access to just subdomains of pastebin.com.\n" +
|
"\"http\" API on Computers.\n" +
|
||||||
|
"Set this to \"*\" to access to the entire internet. Example: \"*.pastebin.com\" will restrict access to " +
|
||||||
|
"just subdomains of pastebin.com.\n" +
|
||||||
"You can use domain names (\"pastebin.com\"), wilcards (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\")." );
|
"You can use domain names (\"pastebin.com\"), wilcards (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\")." );
|
||||||
|
|
||||||
httpBlacklist = config.get( CATEGORY_HTTP, "blacklist", DEFAULT_HTTP_BLACKLIST );
|
httpBlacklist = config.get( CATEGORY_HTTP, "blacklist", DEFAULT_HTTP_BLACKLIST );
|
||||||
httpBlacklist.setComment( "A list of wildcards for domains or IP ranges that cannot be accessed through the \"http\" API on Computers.\n" +
|
httpBlacklist.setComment( "A list of wildcards for domains or IP ranges that cannot be accessed through the " +
|
||||||
"If this is empty then all whitelisted domains will be accessible. Example: \"*.github.com\" will block access to all subdomains of github.com.\n" +
|
"\"http\" API on Computers.\n" +
|
||||||
|
"If this is empty then all whitelisted domains will be accessible. Example: \"*.github.com\" will block " +
|
||||||
|
"access to all subdomains of github.com.\n" +
|
||||||
"You can use domain names (\"pastebin.com\"), wilcards (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\")." );
|
"You can use domain names (\"pastebin.com\"), wilcards (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\")." );
|
||||||
|
|
||||||
httpTimeout = config.get( CATEGORY_HTTP, "timeout", ComputerCraft.httpTimeout );
|
httpTimeout = config.get( CATEGORY_HTTP, "timeout", ComputerCraft.httpTimeout );
|
||||||
@ -150,15 +193,18 @@ public class Config
|
|||||||
httpTimeout.setMinValue( 0 );
|
httpTimeout.setMinValue( 0 );
|
||||||
|
|
||||||
httpMaxRequests = config.get( CATEGORY_HTTP, "max_requests", ComputerCraft.httpMaxRequests );
|
httpMaxRequests = config.get( CATEGORY_HTTP, "max_requests", ComputerCraft.httpMaxRequests );
|
||||||
httpMaxRequests.setComment( "The number of http requests a computer can make at one time. Additional requests will be queued, and sent when the running requests have finished. Set to 0 for unlimited." );
|
httpMaxRequests.setComment( "The number of http requests a computer can make at one time. Additional requests " +
|
||||||
|
"will be queued, and sent when the running requests have finished. Set to 0 for unlimited." );
|
||||||
httpMaxRequests.setMinValue( 0 );
|
httpMaxRequests.setMinValue( 0 );
|
||||||
|
|
||||||
httpMaxDownload = config.get( CATEGORY_HTTP, "max_download", (int) ComputerCraft.httpMaxDownload );
|
httpMaxDownload = config.get( CATEGORY_HTTP, "max_download", (int) ComputerCraft.httpMaxDownload );
|
||||||
httpMaxDownload.setComment( "The maximum size (in bytes) that a computer can download in a single request. Note that responses may receive more data than allowed, but this data will not be returned to the client." );
|
httpMaxDownload.setComment( "The maximum size (in bytes) that a computer can download in a single request. " +
|
||||||
|
"Note that responses may receive more data than allowed, but this data will not be returned to the client." );
|
||||||
httpMaxDownload.setMinValue( 0 );
|
httpMaxDownload.setMinValue( 0 );
|
||||||
|
|
||||||
httpMaxUpload = config.get( CATEGORY_HTTP, "max_upload", (int) ComputerCraft.httpMaxUpload );
|
httpMaxUpload = config.get( CATEGORY_HTTP, "max_upload", (int) ComputerCraft.httpMaxUpload );
|
||||||
httpMaxUpload.setComment( "The maximum size (in bytes) that a computer can upload in a single request. This includes headers and POST text." );
|
httpMaxUpload.setComment( "The maximum size (in bytes) that a computer can upload in a single request. This " +
|
||||||
|
"includes headers and POST text." );
|
||||||
httpMaxUpload.setMinValue( 0 );
|
httpMaxUpload.setMinValue( 0 );
|
||||||
|
|
||||||
httpMaxWebsockets = config.get( CATEGORY_HTTP, "max_websockets", ComputerCraft.httpMaxWebsockets );
|
httpMaxWebsockets = config.get( CATEGORY_HTTP, "max_websockets", ComputerCraft.httpMaxWebsockets );
|
||||||
@ -185,6 +231,9 @@ public class Config
|
|||||||
renameProperty( CATEGORY_GENERAL, "modem_highAltitudeRangeDuringStorm", CATEGORY_PERIPHERAL, "modem_high_altitude_range_during_storm" );
|
renameProperty( CATEGORY_GENERAL, "modem_highAltitudeRangeDuringStorm", CATEGORY_PERIPHERAL, "modem_high_altitude_range_during_storm" );
|
||||||
renameProperty( CATEGORY_GENERAL, "maxNotesPerTick", CATEGORY_PERIPHERAL, "max_notes_per_tick" );
|
renameProperty( CATEGORY_GENERAL, "maxNotesPerTick", CATEGORY_PERIPHERAL, "max_notes_per_tick" );
|
||||||
|
|
||||||
|
config.getCategory( CATEGORY_PERIPHERAL )
|
||||||
|
.setComment( "Various options relating to peripherals." );
|
||||||
|
|
||||||
commandBlockEnabled = config.get( CATEGORY_PERIPHERAL, "command_block_enabled", ComputerCraft.enableCommandBlock );
|
commandBlockEnabled = config.get( CATEGORY_PERIPHERAL, "command_block_enabled", ComputerCraft.enableCommandBlock );
|
||||||
commandBlockEnabled.setComment( "Enable Command Block peripheral support" );
|
commandBlockEnabled.setComment( "Enable Command Block peripheral support" );
|
||||||
|
|
||||||
@ -226,6 +275,9 @@ public class Config
|
|||||||
renameProperty( CATEGORY_GENERAL, "turtlesCanPush", CATEGORY_TURTLE, "can_push" );
|
renameProperty( CATEGORY_GENERAL, "turtlesCanPush", CATEGORY_TURTLE, "can_push" );
|
||||||
renameProperty( CATEGORY_GENERAL, "turtle_disabled_actions", CATEGORY_TURTLE, "disabled_actions" );
|
renameProperty( CATEGORY_GENERAL, "turtle_disabled_actions", CATEGORY_TURTLE, "disabled_actions" );
|
||||||
|
|
||||||
|
config.getCategory( CATEGORY_HTTP )
|
||||||
|
.setComment( "Various options relating to turtles." );
|
||||||
|
|
||||||
turtlesNeedFuel = config.get( CATEGORY_TURTLE, "need_fuel", ComputerCraft.turtlesNeedFuel );
|
turtlesNeedFuel = config.get( CATEGORY_TURTLE, "need_fuel", ComputerCraft.turtlesNeedFuel );
|
||||||
turtlesNeedFuel.setComment( "Set whether Turtles require fuel to move" );
|
turtlesNeedFuel.setComment( "Set whether Turtles require fuel to move" );
|
||||||
|
|
||||||
@ -238,10 +290,12 @@ public class Config
|
|||||||
advancedTurtleFuelLimit.setMinValue( 0 );
|
advancedTurtleFuelLimit.setMinValue( 0 );
|
||||||
|
|
||||||
turtlesObeyBlockProtection = config.get( CATEGORY_TURTLE, "obey_block_protection", ComputerCraft.turtlesObeyBlockProtection );
|
turtlesObeyBlockProtection = config.get( CATEGORY_TURTLE, "obey_block_protection", ComputerCraft.turtlesObeyBlockProtection );
|
||||||
turtlesObeyBlockProtection.setComment( "If set to true, Turtles will be unable to build, dig, or enter protected areas (such as near the server spawn point)" );
|
turtlesObeyBlockProtection.setComment( "If set to true, Turtles will be unable to build, dig, or enter protected " +
|
||||||
|
"areas (such as near the server spawn point)" );
|
||||||
|
|
||||||
turtlesCanPush = config.get( CATEGORY_TURTLE, "can_push", ComputerCraft.turtlesCanPush );
|
turtlesCanPush = config.get( CATEGORY_TURTLE, "can_push", ComputerCraft.turtlesCanPush );
|
||||||
turtlesCanPush.setComment( "If set to true, Turtles will push entities out of the way instead of stopping if there is space to do so" );
|
turtlesCanPush.setComment( "If set to true, Turtles will push entities out of the way instead of stopping if " +
|
||||||
|
"there is space to do so" );
|
||||||
|
|
||||||
turtleDisabledActions = config.get( CATEGORY_TURTLE, "disabled_actions", new String[0] );
|
turtleDisabledActions = config.get( CATEGORY_TURTLE, "disabled_actions", new String[0] );
|
||||||
turtleDisabledActions.setComment( "A list of turtle actions which are disabled." );
|
turtleDisabledActions.setComment( "A list of turtle actions which are disabled." );
|
||||||
@ -252,11 +306,12 @@ public class Config
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupLanguage( config.getCategory( CATEGORY_GENERAL ), "gui.computercraft:config" );
|
|
||||||
for( String child : config.getCategoryNames() )
|
for( String child : config.getCategoryNames() )
|
||||||
{
|
{
|
||||||
if( child.equals( CATEGORY_GENERAL ) ) continue;
|
setupLanguage(
|
||||||
setupLanguage( config.getCategory( child ), "gui.computercraft:config." + child );
|
config.getCategory( child ),
|
||||||
|
child.equals( CATEGORY_GENERAL ) ? "gui.computercraft:config" : "gui.computercraft:config." + child
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sync();
|
sync();
|
||||||
@ -374,9 +429,13 @@ public class Config
|
|||||||
ComputerCraft.disable_lua51_features = disableLua51Features.getBoolean();
|
ComputerCraft.disable_lua51_features = disableLua51Features.getBoolean();
|
||||||
ComputerCraft.default_computer_settings = defaultComputerSettings.getString();
|
ComputerCraft.default_computer_settings = defaultComputerSettings.getString();
|
||||||
ComputerCraft.debug_enable = debugEnabled.getBoolean();
|
ComputerCraft.debug_enable = debugEnabled.getBoolean();
|
||||||
ComputerCraft.computer_threads = computerThreads.getInt();
|
|
||||||
ComputerCraft.logPeripheralErrors = logComputerErrors.getBoolean();
|
ComputerCraft.logPeripheralErrors = logComputerErrors.getBoolean();
|
||||||
|
|
||||||
|
// Execution
|
||||||
|
ComputerCraft.computer_threads = computerThreads.getInt();
|
||||||
|
ComputerCraft.maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos( Math.max( 1, maxMainGlobalTime.getLong() ) );
|
||||||
|
ComputerCraft.maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos( Math.max( 1, maxMainComputerTime.getLong() ) );
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
ComputerCraft.http_enable = httpEnable.getBoolean();
|
ComputerCraft.http_enable = httpEnable.getBoolean();
|
||||||
ComputerCraft.http_websocket_enable = httpWebsocketEnable.getBoolean();
|
ComputerCraft.http_websocket_enable = httpWebsocketEnable.getBoolean();
|
||||||
|
@ -16,8 +16,7 @@ import dan200.computercraft.api.network.wired.IWiredNode;
|
|||||||
import dan200.computercraft.api.network.wired.IWiredSender;
|
import dan200.computercraft.api.network.wired.IWiredSender;
|
||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||||
import dan200.computercraft.core.computer.IComputerOwned;
|
|
||||||
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
|
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
|
||||||
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
@ -267,7 +266,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
return wrappers == null ? null : wrappers.get( remoteName );
|
return wrappers == null ? null : wrappers.get( remoteName );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class RemotePeripheralWrapper implements IComputerAccess, IComputerOwned
|
private static class RemotePeripheralWrapper implements IComputerAccess
|
||||||
{
|
{
|
||||||
private final WiredModemElement m_element;
|
private final WiredModemElement m_element;
|
||||||
private final IPeripheral m_peripheral;
|
private final IPeripheral m_peripheral;
|
||||||
@ -376,6 +375,13 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
m_computer.queueEvent( event, arguments );
|
m_computer.queueEvent( event, arguments );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public IWorkMonitor getMainThreadMonitor()
|
||||||
|
{
|
||||||
|
return m_computer.getMainThreadMonitor();
|
||||||
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String getAttachmentName()
|
public String getAttachmentName()
|
||||||
@ -402,12 +408,5 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
return m_element.getRemotePeripherals().get( name );
|
return m_element.getRemotePeripherals().get( name );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Computer getComputer()
|
|
||||||
{
|
|
||||||
return m_computer instanceof IComputerOwned ? ((IComputerOwned) m_computer).getComputer() : null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import dan200.computercraft.api.lua.ILuaContext;
|
|||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.turtle.*;
|
import dan200.computercraft.api.turtle.*;
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
|
||||||
import dan200.computercraft.shared.computer.blocks.ComputerProxy;
|
import dan200.computercraft.shared.computer.blocks.ComputerProxy;
|
||||||
import dan200.computercraft.shared.computer.blocks.TileComputerBase;
|
import dan200.computercraft.shared.computer.blocks.TileComputerBase;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
@ -41,6 +40,7 @@ import net.minecraftforge.items.IItemHandlerModifiable;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class TurtleBrain implements ITurtleAccess
|
public class TurtleBrain implements ITurtleAccess
|
||||||
{
|
{
|
||||||
@ -957,53 +957,50 @@ public class TurtleBrain implements ITurtleAccess
|
|||||||
|
|
||||||
private void updateCommands()
|
private void updateCommands()
|
||||||
{
|
{
|
||||||
if( m_animation == TurtleAnimation.None )
|
if( m_animation != TurtleAnimation.None || m_commandQueue.isEmpty() ) return;
|
||||||
|
|
||||||
|
// If we've got a computer, ensure that we're allowed to perform work.
|
||||||
|
ServerComputer computer = m_owner.getServerComputer();
|
||||||
|
if( computer != null && !computer.getComputer().getMainThreadMonitor().canWork() ) return;
|
||||||
|
|
||||||
|
// Pull a new command
|
||||||
|
TurtleCommandQueueEntry nextCommand = m_commandQueue.poll();
|
||||||
|
if( nextCommand == null ) return;
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
long start = System.nanoTime();
|
||||||
|
TurtleCommandResult result = nextCommand.command.execute( this );
|
||||||
|
long end = System.nanoTime();
|
||||||
|
|
||||||
|
// Dispatch the callback
|
||||||
|
if( computer == null ) return;
|
||||||
|
computer.getComputer().getMainThreadMonitor().trackWork( end - start, TimeUnit.NANOSECONDS );
|
||||||
|
int callbackID = nextCommand.callbackID;
|
||||||
|
if( callbackID < 0 ) return;
|
||||||
|
|
||||||
|
if( result != null && result.isSuccess() )
|
||||||
{
|
{
|
||||||
// Pull a new command
|
Object[] results = result.getResults();
|
||||||
TurtleCommandQueueEntry nextCommand = m_commandQueue.poll();
|
if( results != null )
|
||||||
if( nextCommand != null )
|
|
||||||
{
|
{
|
||||||
ServerComputer computer = m_owner.getServerComputer();
|
Object[] arguments = new Object[results.length + 2];
|
||||||
|
arguments[0] = callbackID;
|
||||||
// Execute the command
|
arguments[1] = true;
|
||||||
long start = System.nanoTime();
|
System.arraycopy( results, 0, arguments, 2, results.length );
|
||||||
TurtleCommandResult result = nextCommand.command.execute( this );
|
computer.queueEvent( "turtle_response", arguments );
|
||||||
long end = System.nanoTime();
|
|
||||||
|
|
||||||
// Dispatch the callback
|
|
||||||
if( computer != null )
|
|
||||||
{
|
|
||||||
Tracking.addServerTiming( computer.getComputer(), end - start );
|
|
||||||
int callbackID = nextCommand.callbackID;
|
|
||||||
if( callbackID >= 0 )
|
|
||||||
{
|
|
||||||
if( result != null && result.isSuccess() )
|
|
||||||
{
|
|
||||||
Object[] results = result.getResults();
|
|
||||||
if( results != null )
|
|
||||||
{
|
|
||||||
Object[] arguments = new Object[results.length + 2];
|
|
||||||
arguments[0] = callbackID;
|
|
||||||
arguments[1] = true;
|
|
||||||
System.arraycopy( results, 0, arguments, 2, results.length );
|
|
||||||
computer.queueEvent( "turtle_response", arguments );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
computer.queueEvent( "turtle_response", new Object[] {
|
|
||||||
callbackID, true
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
computer.queueEvent( "turtle_response", new Object[] {
|
|
||||||
callbackID, false, result != null ? result.getErrorMessage() : null
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
computer.queueEvent( "turtle_response", new Object[] {
|
||||||
|
callbackID, true
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
computer.queueEvent( "turtle_response", new Object[] {
|
||||||
|
callbackID, false, result != null ? result.getErrorMessage() : null
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ commands.computercraft.dump.usage=[ID]
|
|||||||
commands.computercraft.dump.action=Zeigt mehr Informationen über einen Computer
|
commands.computercraft.dump.action=Zeigt mehr Informationen über einen Computer
|
||||||
|
|
||||||
commands.computercraft.shutdown.synopsis=Fährt den Computer aus der Ferne herunter.
|
commands.computercraft.shutdown.synopsis=Fährt den Computer aus der Ferne herunter.
|
||||||
commands.computercraft.shutdown.desc=Fährt die angegebenen Computer herunter. Falls keine Computer angegeben sind, werden alle heruntergefahren. Der Computer kann entweder über seine Instanz ID (z.B. 123), seine Computer ID (z.B. #123) oder seinen Namen (z.B. "@Mein Computer") angegeben werden.
|
commands.computercraft.shutdown.desc=Fährt die angegebenen Computer herunter. Falls keine Computer angegeben sind, werden alle heruntergefahren. Der Computer kann entweder über seine Instanz ID (z.B. 123), seine Computer ID (z.B. #123) oder seinen Namen (z.B. "@Mein Computer") angegeben werden.
|
||||||
commands.computercraft.shutdown.usage=[IDs...]
|
commands.computercraft.shutdown.usage=[IDs...]
|
||||||
commands.computercraft.shutdown.done=Fährt die Computer %s/%s herunter
|
commands.computercraft.shutdown.done=Fährt die Computer %s/%s herunter
|
||||||
|
|
||||||
@ -156,9 +156,10 @@ gui.computercraft:config.maximum_open_files=Maximalanzahl an gleichzeitig offene
|
|||||||
gui.computercraft:config.disable_lua51_features=Lua 5.1-Funktionen deaktivieren
|
gui.computercraft:config.disable_lua51_features=Lua 5.1-Funktionen deaktivieren
|
||||||
gui.computercraft:config.default_computer_settings=Computer-Standardeinstellungen
|
gui.computercraft:config.default_computer_settings=Computer-Standardeinstellungen
|
||||||
gui.computercraft:config.debug_enabled=Debug-Library aktivieren
|
gui.computercraft:config.debug_enabled=Debug-Library aktivieren
|
||||||
gui.computercraft:config.computer_threads=Computer Threads
|
|
||||||
gui.computercraft:config.log_computer_errors=Computerfehler loggen
|
gui.computercraft:config.log_computer_errors=Computerfehler loggen
|
||||||
|
|
||||||
|
gui.computercraft:config.execution.computer_threads=Computer Threads
|
||||||
|
|
||||||
gui.computercraft:config.http=HTTP
|
gui.computercraft:config.http=HTTP
|
||||||
gui.computercraft:config.http.enabled=HTTP-API aktivieren
|
gui.computercraft:config.http.enabled=HTTP-API aktivieren
|
||||||
gui.computercraft:config.http.websocket_enabled=Websockets aktivieren
|
gui.computercraft:config.http.websocket_enabled=Websockets aktivieren
|
||||||
|
@ -156,9 +156,13 @@ gui.computercraft:config.maximum_open_files=Maximum files open per computer
|
|||||||
gui.computercraft:config.disable_lua51_features=Disable Lua 5.1 features
|
gui.computercraft:config.disable_lua51_features=Disable Lua 5.1 features
|
||||||
gui.computercraft:config.default_computer_settings=Default Computer settings
|
gui.computercraft:config.default_computer_settings=Default Computer settings
|
||||||
gui.computercraft:config.debug_enabled=Enable debug library
|
gui.computercraft:config.debug_enabled=Enable debug library
|
||||||
gui.computercraft:config.computer_threads=Computer threads
|
|
||||||
gui.computercraft:config.log_computer_errors=Log computer errors
|
gui.computercraft:config.log_computer_errors=Log computer errors
|
||||||
|
|
||||||
|
gui.computercraft:config.execution=Execution
|
||||||
|
gui.computercraft:config.execution.computer_threads=Computer threads
|
||||||
|
gui.computercraft:config.execution.max_main_global_time=Server tick global time limit
|
||||||
|
gui.computercraft:config.execution.max_main_computer_time=Server tick computer time limit
|
||||||
|
|
||||||
gui.computercraft:config.http=HTTP
|
gui.computercraft:config.http=HTTP
|
||||||
gui.computercraft:config.http.enabled=Enable the HTTP API
|
gui.computercraft:config.http.enabled=Enable the HTTP API
|
||||||
gui.computercraft:config.http.websocket_enabled=Enable websockets
|
gui.computercraft:config.http.websocket_enabled=Enable websockets
|
||||||
|
@ -56,9 +56,10 @@ gui.computercraft:config.maximum_open_files=Massimo file aperti per computer
|
|||||||
gui.computercraft:config.disable_lua51_features=Disattiva features Lua 5.1
|
gui.computercraft:config.disable_lua51_features=Disattiva features Lua 5.1
|
||||||
gui.computercraft:config.default_computer_settings=Impostazioni Computer predefinite
|
gui.computercraft:config.default_computer_settings=Impostazioni Computer predefinite
|
||||||
gui.computercraft:config.debug_enabled=Attiva libreria di debug
|
gui.computercraft:config.debug_enabled=Attiva libreria di debug
|
||||||
gui.computercraft:config.computer_threads=Threads computer
|
|
||||||
gui.computercraft:config.log_computer_errors=Salva errori computer
|
gui.computercraft:config.log_computer_errors=Salva errori computer
|
||||||
|
|
||||||
|
gui.computercraft:config.execution.computer_threads=Threads computer
|
||||||
|
|
||||||
gui.computercraft:config.http=HTTP
|
gui.computercraft:config.http=HTTP
|
||||||
gui.computercraft:config.http.enabled=Attiva l'API HTTP
|
gui.computercraft:config.http.enabled=Attiva l'API HTTP
|
||||||
gui.computercraft:config.http.websocket_enabled=Attiva websocket
|
gui.computercraft:config.http.websocket_enabled=Attiva websocket
|
||||||
|
@ -56,9 +56,10 @@ gui.computercraft:config.maximum_open_files=Número máximo de arquivos em um co
|
|||||||
gui.computercraft:config.disable_lua51_features=Desabilitar funcionalidade da Lua 5.1
|
gui.computercraft:config.disable_lua51_features=Desabilitar funcionalidade da Lua 5.1
|
||||||
gui.computercraft:config.default_computer_settings=Configurações padrão para Computadores
|
gui.computercraft:config.default_computer_settings=Configurações padrão para Computadores
|
||||||
gui.computercraft:config.debug_enabled=Habilitar biblioteca de debug
|
gui.computercraft:config.debug_enabled=Habilitar biblioteca de debug
|
||||||
gui.computercraft:config.computer_threads=Threads por computador
|
|
||||||
gui.computercraft:config.log_computer_errors=Registrar erros de computadores
|
gui.computercraft:config.log_computer_errors=Registrar erros de computadores
|
||||||
|
|
||||||
|
gui.computercraft:config.execution.computer_threads=Threads por computador
|
||||||
|
|
||||||
gui.computercraft:config.http=HTTP
|
gui.computercraft:config.http=HTTP
|
||||||
gui.computercraft:config.http.enabled=Habilitar a biblioteca de HTTP
|
gui.computercraft:config.http.enabled=Habilitar a biblioteca de HTTP
|
||||||
gui.computercraft:config.http.websocket_enabled=Habilitar websockets
|
gui.computercraft:config.http.websocket_enabled=Habilitar websockets
|
||||||
|
@ -56,9 +56,10 @@ gui.computercraft:config.maximum_open_files=Max antal filer öppna per dator
|
|||||||
gui.computercraft:config.disable_lua51_features=Avaktivera Lua 5.1 funktioner
|
gui.computercraft:config.disable_lua51_features=Avaktivera Lua 5.1 funktioner
|
||||||
gui.computercraft:config.default_computer_settings=Standard Datorinställningar
|
gui.computercraft:config.default_computer_settings=Standard Datorinställningar
|
||||||
gui.computercraft:config.debug_enabled=Aktivera debug bibliotek
|
gui.computercraft:config.debug_enabled=Aktivera debug bibliotek
|
||||||
gui.computercraft:config.computer_threads=Dator trådar
|
|
||||||
gui.computercraft:config.log_computer_errors=Logga datorfel
|
gui.computercraft:config.log_computer_errors=Logga datorfel
|
||||||
|
|
||||||
|
gui.computercraft:config.execution.computer_threads=Dator trådar
|
||||||
|
|
||||||
gui.computercraft:config.http=HTTP
|
gui.computercraft:config.http=HTTP
|
||||||
gui.computercraft:config.http.enabled=Aktivera HTTP API
|
gui.computercraft:config.http.enabled=Aktivera HTTP API
|
||||||
gui.computercraft:config.http.websocket_enabled=Aktivera websockets
|
gui.computercraft:config.http.websocket_enabled=Aktivera websockets
|
||||||
|
Loading…
x
Reference in New Issue
Block a user