mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-14 20:20:30 +00:00
Merge branch 'feature/new-metrics' into mc-1.16.x
This commit is contained in:
commit
68da044ff2
@ -16,7 +16,7 @@ import dan200.computercraft.core.apis.handles.EncodedWritableHandle;
|
|||||||
import dan200.computercraft.core.filesystem.FileSystem;
|
import dan200.computercraft.core.filesystem.FileSystem;
|
||||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||||
import dan200.computercraft.core.filesystem.FileSystemWrapper;
|
import dan200.computercraft.core.filesystem.FileSystemWrapper;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
@ -108,7 +108,7 @@ public class FSAPI implements ILuaAPI
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final String[] list( String path ) throws LuaException
|
public final String[] list( String path ) throws LuaException
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
environment.observe( Metrics.FS_OPS );
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return fileSystem.list( path );
|
return fileSystem.list( path );
|
||||||
@ -276,7 +276,7 @@ public class FSAPI implements ILuaAPI
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
environment.observe( Metrics.FS_OPS );
|
||||||
fileSystem.makeDir( path );
|
fileSystem.makeDir( path );
|
||||||
}
|
}
|
||||||
catch( FileSystemException e )
|
catch( FileSystemException e )
|
||||||
@ -299,7 +299,7 @@ public class FSAPI implements ILuaAPI
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
environment.observe( Metrics.FS_OPS );
|
||||||
fileSystem.move( path, dest );
|
fileSystem.move( path, dest );
|
||||||
}
|
}
|
||||||
catch( FileSystemException e )
|
catch( FileSystemException e )
|
||||||
@ -322,7 +322,7 @@ public class FSAPI implements ILuaAPI
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
environment.observe( Metrics.FS_OPS );
|
||||||
fileSystem.copy( path, dest );
|
fileSystem.copy( path, dest );
|
||||||
}
|
}
|
||||||
catch( FileSystemException e )
|
catch( FileSystemException e )
|
||||||
@ -345,7 +345,7 @@ public class FSAPI implements ILuaAPI
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
environment.observe( Metrics.FS_OPS );
|
||||||
fileSystem.delete( path );
|
fileSystem.delete( path );
|
||||||
}
|
}
|
||||||
catch( FileSystemException e )
|
catch( FileSystemException e )
|
||||||
@ -411,7 +411,7 @@ public class FSAPI implements ILuaAPI
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] open( String path, String mode ) throws LuaException
|
public final Object[] open( String path, String mode ) throws LuaException
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
environment.observe( Metrics.FS_OPS );
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
switch( mode )
|
switch( mode )
|
||||||
@ -532,7 +532,7 @@ public class FSAPI implements ILuaAPI
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.FS_OPS );
|
environment.observe( Metrics.FS_OPS );
|
||||||
return fileSystem.find( path );
|
return fileSystem.find( path );
|
||||||
}
|
}
|
||||||
catch( FileSystemException e )
|
catch( FileSystemException e )
|
||||||
|
@ -11,8 +11,8 @@ import dan200.computercraft.core.computer.ComputerEnvironment;
|
|||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
import dan200.computercraft.core.computer.GlobalEnvironment;
|
import dan200.computercraft.core.computer.GlobalEnvironment;
|
||||||
import dan200.computercraft.core.filesystem.FileSystem;
|
import dan200.computercraft.core.filesystem.FileSystem;
|
||||||
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -75,10 +75,7 @@ public interface IAPIEnvironment
|
|||||||
|
|
||||||
void cancelTimer( int id );
|
void cancelTimer( int id );
|
||||||
|
|
||||||
void addTrackingChange( @Nonnull TrackingField field, long change );
|
void observe( @Nonnull Metric.Event event, long change );
|
||||||
|
|
||||||
default void addTrackingChange( @Nonnull TrackingField field )
|
void observe( @Nonnull Metric.Counter counter );
|
||||||
{
|
|
||||||
addTrackingChange( field, 1 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import dan200.computercraft.core.asm.LuaMethod;
|
|||||||
import dan200.computercraft.core.asm.NamedMethod;
|
import dan200.computercraft.core.asm.NamedMethod;
|
||||||
import dan200.computercraft.core.asm.PeripheralMethod;
|
import dan200.computercraft.core.asm.PeripheralMethod;
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import dan200.computercraft.shared.util.LuaUtil;
|
import dan200.computercraft.shared.util.LuaUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -108,7 +108,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
|||||||
|
|
||||||
if( method == null ) throw new LuaException( "No such method " + methodName );
|
if( method == null ) throw new LuaException( "No such method " + methodName );
|
||||||
|
|
||||||
environment.addTrackingChange( TrackingField.PERIPHERAL_OPS );
|
environment.observe( Metrics.PERIPHERAL_OPS );
|
||||||
return method.apply( peripheral, context, this, arguments );
|
return method.apply( peripheral, context, this, arguments );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import dan200.computercraft.core.apis.http.NetworkUtils;
|
|||||||
import dan200.computercraft.core.apis.http.Resource;
|
import dan200.computercraft.core.apis.http.Resource;
|
||||||
import dan200.computercraft.core.apis.http.ResourceGroup;
|
import dan200.computercraft.core.apis.http.ResourceGroup;
|
||||||
import dan200.computercraft.core.apis.http.options.Options;
|
import dan200.computercraft.core.apis.http.options.Options;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
@ -148,8 +148,8 @@ public class HttpRequest extends Resource<HttpRequest>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add request size to the tracker before opening the connection
|
// Add request size to the tracker before opening the connection
|
||||||
environment.addTrackingChange( TrackingField.HTTP_REQUESTS, 1 );
|
environment.observe( Metrics.HTTP_REQUESTS );
|
||||||
environment.addTrackingChange( TrackingField.HTTP_UPLOAD, requestBody );
|
environment.observe( Metrics.HTTP_UPLOAD, requestBody );
|
||||||
|
|
||||||
HttpRequestHandler handler = currentRequest = new HttpRequestHandler( this, uri, method, options );
|
HttpRequestHandler handler = currentRequest = new HttpRequestHandler( this, uri, method, options );
|
||||||
connectFuture = new Bootstrap()
|
connectFuture = new Bootstrap()
|
||||||
|
@ -13,7 +13,7 @@ import dan200.computercraft.core.apis.handles.HandleGeneric;
|
|||||||
import dan200.computercraft.core.apis.http.HTTPRequestException;
|
import dan200.computercraft.core.apis.http.HTTPRequestException;
|
||||||
import dan200.computercraft.core.apis.http.NetworkUtils;
|
import dan200.computercraft.core.apis.http.NetworkUtils;
|
||||||
import dan200.computercraft.core.apis.http.options.Options;
|
import dan200.computercraft.core.apis.http.options.Options;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.CompositeByteBuf;
|
import io.netty.buffer.CompositeByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
@ -202,7 +202,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fire off a stats event
|
// Fire off a stats event
|
||||||
request.environment().addTrackingChange( TrackingField.HTTP_DOWNLOAD, getHeaderSize( responseHeaders ) + bytes.length );
|
request.environment().observe( Metrics.HTTP_DOWNLOAD, getHeaderSize( responseHeaders ) + bytes.length );
|
||||||
|
|
||||||
// Prepare to queue an event
|
// Prepare to queue an event
|
||||||
ArrayByteChannel contents = new ArrayByteChannel( bytes );
|
ArrayByteChannel contents = new ArrayByteChannel( bytes );
|
||||||
|
@ -8,7 +8,7 @@ package dan200.computercraft.core.apis.http.websocket;
|
|||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import dan200.computercraft.api.lua.*;
|
import dan200.computercraft.api.lua.*;
|
||||||
import dan200.computercraft.core.apis.http.options.Options;
|
import dan200.computercraft.core.apis.http.options.Options;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import dan200.computercraft.shared.util.StringUtil;
|
import dan200.computercraft.shared.util.StringUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
@ -89,7 +89,7 @@ public class WebsocketHandle implements Closeable
|
|||||||
throw new LuaException( "Message is too large" );
|
throw new LuaException( "Message is too large" );
|
||||||
}
|
}
|
||||||
|
|
||||||
websocket.environment().addTrackingChange( TrackingField.WEBSOCKET_OUTGOING, text.length() );
|
websocket.environment().observe( Metrics.WEBSOCKET_OUTGOING, text.length() );
|
||||||
|
|
||||||
Channel channel = this.channel;
|
Channel channel = this.channel;
|
||||||
if( channel != null )
|
if( channel != null )
|
||||||
|
@ -7,7 +7,7 @@ package dan200.computercraft.core.apis.http.websocket;
|
|||||||
|
|
||||||
import dan200.computercraft.core.apis.http.NetworkUtils;
|
import dan200.computercraft.core.apis.http.NetworkUtils;
|
||||||
import dan200.computercraft.core.apis.http.options.Options;
|
import dan200.computercraft.core.apis.http.options.Options;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
@ -66,14 +66,14 @@ public class WebsocketHandler extends SimpleChannelInboundHandler<Object>
|
|||||||
{
|
{
|
||||||
String data = ((TextWebSocketFrame) frame).text();
|
String data = ((TextWebSocketFrame) frame).text();
|
||||||
|
|
||||||
websocket.environment().addTrackingChange( TrackingField.WEBSOCKET_INCOMING, data.length() );
|
websocket.environment().observe( Metrics.WEBSOCKET_INCOMING, data.length() );
|
||||||
websocket.environment().queueEvent( MESSAGE_EVENT, websocket.address(), data, false );
|
websocket.environment().queueEvent( MESSAGE_EVENT, websocket.address(), data, false );
|
||||||
}
|
}
|
||||||
else if( frame instanceof BinaryWebSocketFrame )
|
else if( frame instanceof BinaryWebSocketFrame )
|
||||||
{
|
{
|
||||||
byte[] converted = NetworkUtils.toBytes( frame.content() );
|
byte[] converted = NetworkUtils.toBytes( frame.content() );
|
||||||
|
|
||||||
websocket.environment().addTrackingChange( TrackingField.WEBSOCKET_INCOMING, converted.length );
|
websocket.environment().observe( Metrics.WEBSOCKET_INCOMING, converted.length );
|
||||||
websocket.environment().queueEvent( MESSAGE_EVENT, websocket.address(), converted, true );
|
websocket.environment().queueEvent( MESSAGE_EVENT, websocket.address(), converted, true );
|
||||||
}
|
}
|
||||||
else if( frame instanceof CloseWebSocketFrame )
|
else if( frame instanceof CloseWebSocketFrame )
|
||||||
|
@ -36,7 +36,6 @@ public class Computer
|
|||||||
private String label = null;
|
private String label = null;
|
||||||
|
|
||||||
// Read-only fields about the computer
|
// Read-only fields about the computer
|
||||||
private final ComputerEnvironment computerEnvironment;
|
|
||||||
private final GlobalEnvironment globalEnvironment;
|
private final GlobalEnvironment globalEnvironment;
|
||||||
private final Terminal terminal;
|
private final Terminal terminal;
|
||||||
private final ComputerExecutor executor;
|
private final ComputerExecutor executor;
|
||||||
@ -44,7 +43,7 @@ public class Computer
|
|||||||
|
|
||||||
// Additional state about the computer and its environment.
|
// Additional state about the computer and its environment.
|
||||||
private boolean blinking = false;
|
private boolean blinking = false;
|
||||||
private final Environment internalEnvironment = new Environment( this );
|
private final Environment internalEnvironment;
|
||||||
private final AtomicBoolean externalOutputChanged = new AtomicBoolean();
|
private final AtomicBoolean externalOutputChanged = new AtomicBoolean();
|
||||||
|
|
||||||
private boolean startRequested;
|
private boolean startRequested;
|
||||||
@ -55,16 +54,11 @@ public class Computer
|
|||||||
if( id < 0 ) throw new IllegalStateException( "Id has not been assigned" );
|
if( id < 0 ) throw new IllegalStateException( "Id has not been assigned" );
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.globalEnvironment = globalEnvironment;
|
this.globalEnvironment = globalEnvironment;
|
||||||
this.computerEnvironment = environment;
|
|
||||||
this.terminal = terminal;
|
this.terminal = terminal;
|
||||||
|
|
||||||
executor = new ComputerExecutor( this );
|
internalEnvironment = new Environment( this, environment );
|
||||||
serverExecutor = new MainThreadExecutor( this );
|
executor = new ComputerExecutor( this, environment );
|
||||||
}
|
serverExecutor = new MainThreadExecutor( environment.getMetrics() );
|
||||||
|
|
||||||
ComputerEnvironment getComputerEnvironment()
|
|
||||||
{
|
|
||||||
return computerEnvironment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalEnvironment getGlobalEnvironment()
|
GlobalEnvironment getGlobalEnvironment()
|
||||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.core.computer;
|
|||||||
|
|
||||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||||
import dan200.computercraft.core.filesystem.FileMount;
|
import dan200.computercraft.core.filesystem.FileMount;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@ -26,6 +27,14 @@ public interface ComputerEnvironment
|
|||||||
*/
|
*/
|
||||||
double getTimeOfDay();
|
double getTimeOfDay();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link MetricsObserver} for this computer. This should be constant for the duration of this
|
||||||
|
* {@link ComputerEnvironment}.
|
||||||
|
*
|
||||||
|
* @return This computer's {@link MetricsObserver}.
|
||||||
|
*/
|
||||||
|
MetricsObserver getMetrics();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct the mount for this computer's user-writable data.
|
* Construct the mount for this computer's user-writable data.
|
||||||
*
|
*
|
||||||
|
@ -15,9 +15,11 @@ import dan200.computercraft.core.filesystem.FileSystem;
|
|||||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||||
import dan200.computercraft.core.lua.ILuaMachine;
|
import dan200.computercraft.core.lua.ILuaMachine;
|
||||||
|
import dan200.computercraft.core.lua.MachineEnvironment;
|
||||||
import dan200.computercraft.core.lua.MachineResult;
|
import dan200.computercraft.core.lua.MachineResult;
|
||||||
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
|
||||||
import dan200.computercraft.shared.util.Colour;
|
import dan200.computercraft.shared.util.Colour;
|
||||||
import dan200.computercraft.shared.util.IoUtil;
|
import dan200.computercraft.shared.util.IoUtil;
|
||||||
|
|
||||||
@ -57,6 +59,8 @@ final class ComputerExecutor
|
|||||||
private static final int QUEUE_LIMIT = 256;
|
private static final int QUEUE_LIMIT = 256;
|
||||||
|
|
||||||
private final Computer computer;
|
private final Computer computer;
|
||||||
|
private final ComputerEnvironment computerEnvironment;
|
||||||
|
private final MetricsObserver metrics;
|
||||||
private final List<ILuaAPI> apis = new ArrayList<>();
|
private final List<ILuaAPI> apis = new ArrayList<>();
|
||||||
final TimeoutState timeout = new TimeoutState();
|
final TimeoutState timeout = new TimeoutState();
|
||||||
|
|
||||||
@ -157,12 +161,14 @@ final class ComputerExecutor
|
|||||||
*/
|
*/
|
||||||
final AtomicReference<Thread> executingThread = new AtomicReference<>();
|
final AtomicReference<Thread> executingThread = new AtomicReference<>();
|
||||||
|
|
||||||
ComputerExecutor( Computer computer )
|
ComputerExecutor( Computer computer, ComputerEnvironment computerEnvironment )
|
||||||
{
|
{
|
||||||
// Ensure the computer thread is running as required.
|
// Ensure the computer thread is running as required.
|
||||||
ComputerThread.start();
|
ComputerThread.start();
|
||||||
|
|
||||||
this.computer = computer;
|
this.computer = computer;
|
||||||
|
this.computerEnvironment = computerEnvironment;
|
||||||
|
metrics = computerEnvironment.getMetrics();
|
||||||
|
|
||||||
Environment environment = computer.getEnvironment();
|
Environment environment = computer.getEnvironment();
|
||||||
|
|
||||||
@ -345,7 +351,7 @@ final class ComputerExecutor
|
|||||||
|
|
||||||
private IWritableMount getRootMount()
|
private IWritableMount getRootMount()
|
||||||
{
|
{
|
||||||
if( rootMount == null ) rootMount = computer.getComputerEnvironment().createRootMount();
|
if( rootMount == null ) rootMount = computerEnvironment.createRootMount();
|
||||||
return rootMount;
|
return rootMount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,7 +401,9 @@ final class ComputerExecutor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the lua machine
|
// Create the lua machine
|
||||||
ILuaMachine machine = luaFactory.create( computer, timeout );
|
ILuaMachine machine = luaFactory.create( new MachineEnvironment(
|
||||||
|
new LuaContext( computer ), metrics, timeout, computer.getGlobalEnvironment().getHostString()
|
||||||
|
) );
|
||||||
|
|
||||||
// Add the APIs. We unwrap them (yes, this is horrible) to get access to the underlying object.
|
// Add the APIs. We unwrap them (yes, this is horrible) to get access to the underlying object.
|
||||||
for( ILuaAPI api : apis ) machine.addAPI( api instanceof ApiWrapper ? ((ApiWrapper) api).getDelegate() : api );
|
for( ILuaAPI api : apis ) machine.addAPI( api instanceof ApiWrapper ? ((ApiWrapper) api).getDelegate() : api );
|
||||||
@ -522,7 +530,7 @@ final class ComputerExecutor
|
|||||||
timeout.stopTimer();
|
timeout.stopTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
Tracking.addTaskTiming( getComputer(), timeout.nanoCurrent() );
|
metrics.observe( Metrics.COMPUTER_TASKS, timeout.nanoCurrent() );
|
||||||
|
|
||||||
if( interruptedEvent ) return true;
|
if( interruptedEvent ) return true;
|
||||||
|
|
||||||
|
@ -10,9 +10,9 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
|||||||
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
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.metrics.Metric;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
|
||||||
@ -42,6 +42,8 @@ import java.util.Iterator;
|
|||||||
public final class Environment implements IAPIEnvironment
|
public final class Environment implements IAPIEnvironment
|
||||||
{
|
{
|
||||||
private final Computer computer;
|
private final Computer computer;
|
||||||
|
private final ComputerEnvironment environment;
|
||||||
|
private final MetricsObserver metrics;
|
||||||
|
|
||||||
private boolean internalOutputChanged = false;
|
private boolean internalOutputChanged = false;
|
||||||
private final int[] internalOutput = new int[ComputerSide.COUNT];
|
private final int[] internalOutput = new int[ComputerSide.COUNT];
|
||||||
@ -60,9 +62,11 @@ public final class Environment implements IAPIEnvironment
|
|||||||
private final Int2ObjectMap<Timer> timers = new Int2ObjectOpenHashMap<>();
|
private final Int2ObjectMap<Timer> timers = new Int2ObjectOpenHashMap<>();
|
||||||
private int nextTimerToken = 0;
|
private int nextTimerToken = 0;
|
||||||
|
|
||||||
Environment( Computer computer )
|
Environment( Computer computer, ComputerEnvironment environment )
|
||||||
{
|
{
|
||||||
this.computer = computer;
|
this.computer = computer;
|
||||||
|
this.environment = environment;
|
||||||
|
metrics = environment.getMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -75,7 +79,7 @@ public final class Environment implements IAPIEnvironment
|
|||||||
@Override
|
@Override
|
||||||
public ComputerEnvironment getComputerEnvironment()
|
public ComputerEnvironment getComputerEnvironment()
|
||||||
{
|
{
|
||||||
return computer.getComputerEnvironment();
|
return environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -367,9 +371,15 @@ public final class Environment implements IAPIEnvironment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTrackingChange( @Nonnull TrackingField field, long change )
|
public void observe( @Nonnull Metric.Event event, long change )
|
||||||
{
|
{
|
||||||
Tracking.addValue( computer, field, change );
|
metrics.observe( event, change );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void observe( @Nonnull Metric.Counter counter )
|
||||||
|
{
|
||||||
|
metrics.observe( counter );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Timer
|
private static class Timer
|
||||||
|
@ -3,14 +3,12 @@
|
|||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
*/
|
*/
|
||||||
package dan200.computercraft.core.lua;
|
package dan200.computercraft.core.computer;
|
||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.lua.ILuaContext;
|
import dan200.computercraft.api.lua.ILuaContext;
|
||||||
import dan200.computercraft.api.lua.ILuaTask;
|
import dan200.computercraft.api.lua.ILuaTask;
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
|
||||||
import dan200.computercraft.core.computer.MainThread;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
@ -7,7 +7,8 @@ package dan200.computercraft.core.computer;
|
|||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
import dan200.computercraft.shared.turtle.core.TurtleBrain;
|
import dan200.computercraft.shared.turtle.core.TurtleBrain;
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ final class MainThreadExecutor implements IWorkMonitor
|
|||||||
*/
|
*/
|
||||||
private static final int MAX_TASKS = 5000;
|
private static final int MAX_TASKS = 5000;
|
||||||
|
|
||||||
private final Computer computer;
|
private final MetricsObserver metrics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A lock used for any changes to {@link #tasks}, or {@link #onQueue}. This will be
|
* A lock used for any changes to {@link #tasks}, or {@link #onQueue}. This will be
|
||||||
@ -109,9 +110,9 @@ final class MainThreadExecutor implements IWorkMonitor
|
|||||||
|
|
||||||
long virtualTime;
|
long virtualTime;
|
||||||
|
|
||||||
MainThreadExecutor( Computer computer )
|
MainThreadExecutor( MetricsObserver metrics )
|
||||||
{
|
{
|
||||||
this.computer = computer;
|
this.metrics = metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -194,7 +195,7 @@ final class MainThreadExecutor implements IWorkMonitor
|
|||||||
|
|
||||||
private void consumeTime( long time )
|
private void consumeTime( long time )
|
||||||
{
|
{
|
||||||
Tracking.addServerTiming( computer, time );
|
metrics.observe( Metrics.SERVER_TASKS, time );
|
||||||
|
|
||||||
// Reset the budget if moving onto a new tick. We know this is safe, as this will only have happened if
|
// 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.
|
// #tickCooling() isn't called, and so we didn't overrun the previous tick.
|
||||||
|
@ -12,10 +12,9 @@ import dan200.computercraft.api.lua.ILuaContext;
|
|||||||
import dan200.computercraft.api.lua.ILuaFunction;
|
import dan200.computercraft.api.lua.ILuaFunction;
|
||||||
import dan200.computercraft.core.asm.LuaMethod;
|
import dan200.computercraft.core.asm.LuaMethod;
|
||||||
import dan200.computercraft.core.asm.ObjectSource;
|
import dan200.computercraft.core.asm.ObjectSource;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
|
||||||
import dan200.computercraft.core.computer.TimeoutState;
|
import dan200.computercraft.core.computer.TimeoutState;
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
import dan200.computercraft.shared.util.ThreadUtils;
|
import dan200.computercraft.shared.util.ThreadUtils;
|
||||||
import org.squiddev.cobalt.*;
|
import org.squiddev.cobalt.*;
|
||||||
import org.squiddev.cobalt.compiler.CompileException;
|
import org.squiddev.cobalt.compiler.CompileException;
|
||||||
@ -52,7 +51,6 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
|
|
||||||
private static final LuaMethod FUNCTION_METHOD = ( target, context, args ) -> ((ILuaFunction) target).call( args );
|
private static final LuaMethod FUNCTION_METHOD = ( target, context, args ) -> ((ILuaFunction) target).call( args );
|
||||||
|
|
||||||
private final Computer computer;
|
|
||||||
private final TimeoutState timeout;
|
private final TimeoutState timeout;
|
||||||
private final TimeoutDebugHandler debug;
|
private final TimeoutDebugHandler debug;
|
||||||
private final ILuaContext context;
|
private final ILuaContext context;
|
||||||
@ -63,19 +61,19 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
private LuaThread mainRoutine = null;
|
private LuaThread mainRoutine = null;
|
||||||
private String eventFilter = null;
|
private String eventFilter = null;
|
||||||
|
|
||||||
public CobaltLuaMachine( Computer computer, TimeoutState timeout )
|
public CobaltLuaMachine( MachineEnvironment environment )
|
||||||
{
|
{
|
||||||
this.computer = computer;
|
timeout = environment.timeout;
|
||||||
this.timeout = timeout;
|
context = environment.context;
|
||||||
context = new LuaContext( computer );
|
|
||||||
debug = new TimeoutDebugHandler();
|
debug = new TimeoutDebugHandler();
|
||||||
|
|
||||||
// Create an environment to run in
|
// Create an environment to run in
|
||||||
|
MetricsObserver metrics = environment.metrics;
|
||||||
LuaState state = this.state = LuaState.builder()
|
LuaState state = this.state = LuaState.builder()
|
||||||
.resourceManipulator( new VoidResourceManipulator() )
|
.resourceManipulator( new VoidResourceManipulator() )
|
||||||
.debug( debug )
|
.debug( debug )
|
||||||
.coroutineExecutor( command -> {
|
.coroutineExecutor( command -> {
|
||||||
Tracking.addValue( this.computer, TrackingField.COROUTINES_CREATED, 1 );
|
metrics.observe( Metrics.COROUTINES_CREATED );
|
||||||
COROUTINES.execute( () -> {
|
COROUTINES.execute( () -> {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -83,7 +81,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Tracking.addValue( this.computer, TrackingField.COROUTINES_DISPOSED, 1 );
|
metrics.observe( Metrics.COROUTINES_DISPOSED );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
} )
|
} )
|
||||||
@ -110,7 +108,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
|||||||
|
|
||||||
// Add version globals
|
// Add version globals
|
||||||
globals.rawset( "_VERSION", valueOf( "Lua 5.1" ) );
|
globals.rawset( "_VERSION", valueOf( "Lua 5.1" ) );
|
||||||
globals.rawset( "_HOST", valueOf( computer.getAPIEnvironment().getGlobalEnvironment().getHostString() ) );
|
globals.rawset( "_HOST", valueOf( environment.hostString ) );
|
||||||
globals.rawset( "_CC_DEFAULT_SETTINGS", valueOf( ComputerCraft.defaultComputerSettings ) );
|
globals.rawset( "_CC_DEFAULT_SETTINGS", valueOf( ComputerCraft.defaultComputerSettings ) );
|
||||||
if( ComputerCraft.disableLua51Features )
|
if( ComputerCraft.disableLua51Features )
|
||||||
{
|
{
|
||||||
|
@ -7,8 +7,6 @@ package dan200.computercraft.core.lua;
|
|||||||
|
|
||||||
import dan200.computercraft.api.lua.IDynamicLuaObject;
|
import dan200.computercraft.api.lua.IDynamicLuaObject;
|
||||||
import dan200.computercraft.api.lua.ILuaAPI;
|
import dan200.computercraft.api.lua.ILuaAPI;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
|
||||||
import dan200.computercraft.core.computer.TimeoutState;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -77,6 +75,6 @@ public interface ILuaMachine
|
|||||||
|
|
||||||
interface Factory
|
interface Factory
|
||||||
{
|
{
|
||||||
ILuaMachine create( Computer computer, TimeoutState timeout );
|
ILuaMachine create( MachineEnvironment environment );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.core.lua;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.ILuaContext;
|
||||||
|
import dan200.computercraft.core.computer.GlobalEnvironment;
|
||||||
|
import dan200.computercraft.core.computer.TimeoutState;
|
||||||
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arguments used to construct a {@link ILuaMachine}.
|
||||||
|
*
|
||||||
|
* @see ILuaMachine.Factory
|
||||||
|
*/
|
||||||
|
public class MachineEnvironment
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Lua context to execute main-thread tasks with.
|
||||||
|
*/
|
||||||
|
public final ILuaContext context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sink to submit metrics to. You do not need to submit task timings here, it should only be for additional
|
||||||
|
* metrics such as {@link Metrics#COROUTINES_CREATED}
|
||||||
|
*/
|
||||||
|
public final MetricsObserver metrics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current timeout state. This should be used by the machine to interrupt its execution.
|
||||||
|
*/
|
||||||
|
public final TimeoutState timeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@linkplain GlobalEnvironment#getHostString() host string} to identify the current environment.
|
||||||
|
*/
|
||||||
|
public final String hostString;
|
||||||
|
|
||||||
|
public MachineEnvironment( ILuaContext context, MetricsObserver metrics, TimeoutState timeout, String hostString )
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.metrics = metrics;
|
||||||
|
this.timeout = timeout;
|
||||||
|
this.hostString = hostString;
|
||||||
|
}
|
||||||
|
}
|
141
src/main/java/dan200/computercraft/core/metrics/Metric.java
Normal file
141
src/main/java/dan200/computercraft/core/metrics/Metric.java
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.core.metrics;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.LongFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A metric is some event which is emitted by a computer and observed by a {@link MetricsObserver}.
|
||||||
|
* <p>
|
||||||
|
* It comes in two forms: a simple {@link Metric.Counter} counts how many times an event has occurred (for instance,
|
||||||
|
* how many HTTP requests have there been) while a {@link Metric.Event} has a discrete value for each event (for
|
||||||
|
* instance, the number of bytes downloaded in this HTTP request). The values tied to a {@link Metric.Event}s can be
|
||||||
|
* accumulated, and thus used to derive averages, rates and be sorted into histogram buckets.
|
||||||
|
*/
|
||||||
|
public abstract class Metric
|
||||||
|
{
|
||||||
|
private static final Map<String, Metric> allMetrics = new ConcurrentHashMap<>();
|
||||||
|
private static final AtomicInteger nextId = new AtomicInteger();
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
private final String name;
|
||||||
|
private final String unit;
|
||||||
|
private final LongFunction<String> format;
|
||||||
|
|
||||||
|
private Metric( String name, String unit, LongFunction<String> format )
|
||||||
|
{
|
||||||
|
if( allMetrics.containsKey( name ) ) throw new IllegalStateException( "Duplicate key " + name );
|
||||||
|
|
||||||
|
id = nextId.getAndIncrement();
|
||||||
|
this.name = name;
|
||||||
|
this.unit = unit;
|
||||||
|
this.format = format;
|
||||||
|
allMetrics.put( name, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique ID for this metric.
|
||||||
|
* <p>
|
||||||
|
* Each metric is assigned a consecutive metric ID starting from 0. This allows you to store metrics in an array,
|
||||||
|
* rather than a map.
|
||||||
|
*
|
||||||
|
* @return The metric's ID.
|
||||||
|
*/
|
||||||
|
public int id()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique name for this metric.
|
||||||
|
*
|
||||||
|
* @return The metric's name.
|
||||||
|
*/
|
||||||
|
public String name()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unit used for this metric. This should be a lowercase "identifier-like" such as {@literal ms}. If no unit is
|
||||||
|
* relevant, it can be empty.
|
||||||
|
*
|
||||||
|
* @return The metric's unit.
|
||||||
|
*/
|
||||||
|
public String unit()
|
||||||
|
{
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a value according to the metric's formatting rules. Implementations may choose to append units to the
|
||||||
|
* returned value where relevant.
|
||||||
|
*
|
||||||
|
* @param value The value to format.
|
||||||
|
* @return The formatted value.
|
||||||
|
*/
|
||||||
|
public String format( long value )
|
||||||
|
{
|
||||||
|
return format.apply( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return getClass().getName() + ":" + name();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a map of all metrics.
|
||||||
|
*
|
||||||
|
* @return A map of all metrics.
|
||||||
|
*/
|
||||||
|
public static Map<String, Metric> metrics()
|
||||||
|
{
|
||||||
|
return Collections.unmodifiableMap( allMetrics );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Counter extends Metric
|
||||||
|
{
|
||||||
|
public Counter( String id )
|
||||||
|
{
|
||||||
|
super( id, "", Metric::formatDefault );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Event extends Metric
|
||||||
|
{
|
||||||
|
public Event( String id, String unit, LongFunction<String> format )
|
||||||
|
{
|
||||||
|
super( id, unit, format );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatTime( long value )
|
||||||
|
{
|
||||||
|
return String.format( "%.1fms", value * 1e-6 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatDefault( long value )
|
||||||
|
{
|
||||||
|
return String.format( "%d", value );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int KILOBYTE_SIZE = 1024;
|
||||||
|
private static final String SI_PREFIXES = "KMGT";
|
||||||
|
|
||||||
|
public static String formatBytes( long bytes )
|
||||||
|
{
|
||||||
|
if( bytes < KILOBYTE_SIZE ) return String.format( "%d B", bytes );
|
||||||
|
int exp = (int) (Math.log( (double) bytes ) / Math.log( KILOBYTE_SIZE ));
|
||||||
|
if( exp > SI_PREFIXES.length() ) exp = SI_PREFIXES.length();
|
||||||
|
return String.format( "%.1f %siB", bytes / Math.pow( KILOBYTE_SIZE, exp ), SI_PREFIXES.charAt( exp - 1 ) );
|
||||||
|
}
|
||||||
|
}
|
41
src/main/java/dan200/computercraft/core/metrics/Metrics.java
Normal file
41
src/main/java/dan200/computercraft/core/metrics/Metrics.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.core.metrics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Built-in metrics that CC produces.
|
||||||
|
*/
|
||||||
|
public final class Metrics
|
||||||
|
{
|
||||||
|
private Metrics()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Metric.Event COMPUTER_TASKS = new Metric.Event( "computer_tasks", "ms", Metric::formatTime );
|
||||||
|
public static final Metric.Event SERVER_TASKS = new Metric.Event( "server_tasks", "ms", Metric::formatTime );
|
||||||
|
|
||||||
|
public static final Metric.Counter PERIPHERAL_OPS = new Metric.Counter( "peripheral" );
|
||||||
|
public static final Metric.Counter FS_OPS = new Metric.Counter( "fs" );
|
||||||
|
|
||||||
|
public static final Metric.Counter HTTP_REQUESTS = new Metric.Counter( "http_requests" );
|
||||||
|
public static final Metric.Event HTTP_UPLOAD = new Metric.Event( "http_upload", "bytes", Metric::formatBytes );
|
||||||
|
public static final Metric.Event HTTP_DOWNLOAD = new Metric.Event( "http_download", "bytes", Metric::formatBytes );
|
||||||
|
|
||||||
|
public static final Metric.Event WEBSOCKET_INCOMING = new Metric.Event( "websocket_incoming", "bytes", Metric::formatBytes );
|
||||||
|
public static final Metric.Event WEBSOCKET_OUTGOING = new Metric.Event( "websocket_outgoing", "bytes", Metric::formatBytes );
|
||||||
|
|
||||||
|
public static final Metric.Counter COROUTINES_CREATED = new Metric.Counter( "coroutines_created" );
|
||||||
|
public static final Metric.Counter COROUTINES_DISPOSED = new Metric.Counter( "coroutines_dead" );
|
||||||
|
|
||||||
|
public static final Metric.Counter TURTLE_OPS = new Metric.Counter( "turtle_ops" );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures metrics are registered.
|
||||||
|
*/
|
||||||
|
public static void init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.core.metrics;
|
||||||
|
|
||||||
|
import dan200.computercraft.core.computer.ComputerEnvironment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A metrics observer is used to report metrics for a single computer.
|
||||||
|
* <p>
|
||||||
|
* Various components (such as the computer scheduler or http API) will report statistics about their behaviour to the
|
||||||
|
* observer. The observer may choose to consume these metrics, aggregating them and presenting them to the user in some
|
||||||
|
* manner.
|
||||||
|
*
|
||||||
|
* @see ComputerEnvironment#getMetrics()
|
||||||
|
* @see Metrics Built-in metrics which will be reported.
|
||||||
|
*/
|
||||||
|
public interface MetricsObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Increment a counter by 1.
|
||||||
|
*
|
||||||
|
* @param counter The counter to observe.
|
||||||
|
*/
|
||||||
|
void observe( Metric.Counter counter );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observe a single instance of an event.
|
||||||
|
*
|
||||||
|
* @param event The event to observe.
|
||||||
|
* @param value The value corresponding to this event.
|
||||||
|
*/
|
||||||
|
void observe( Metric.Event event, long value );
|
||||||
|
}
|
@ -1,156 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
|
||||||
*/
|
|
||||||
package dan200.computercraft.core.tracking;
|
|
||||||
|
|
||||||
import com.google.common.base.CaseFormat;
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
|
||||||
import dan200.computercraft.core.computer.Computer;
|
|
||||||
import net.minecraft.util.text.LanguageMap;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.management.*;
|
|
||||||
import java.lang.management.ManagementFactory;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
import java.util.function.LongSupplier;
|
|
||||||
|
|
||||||
public final class ComputerMBean implements DynamicMBean, Tracker
|
|
||||||
{
|
|
||||||
private static final Set<TrackingField> SKIP = new HashSet<>( Arrays.asList(
|
|
||||||
TrackingField.TASKS, TrackingField.TOTAL_TIME, TrackingField.AVERAGE_TIME, TrackingField.MAX_TIME,
|
|
||||||
TrackingField.SERVER_COUNT, TrackingField.SERVER_TIME
|
|
||||||
) );
|
|
||||||
|
|
||||||
private static ComputerMBean instance;
|
|
||||||
|
|
||||||
private final Map<String, LongSupplier> attributes = new HashMap<>();
|
|
||||||
private final Map<TrackingField, Counter> values = new HashMap<>();
|
|
||||||
private final MBeanInfo info;
|
|
||||||
|
|
||||||
private ComputerMBean()
|
|
||||||
{
|
|
||||||
List<MBeanAttributeInfo> attributes = new ArrayList<>();
|
|
||||||
for( Map.Entry<String, TrackingField> field : TrackingField.fields().entrySet() )
|
|
||||||
{
|
|
||||||
if( SKIP.contains( field.getValue() ) ) continue;
|
|
||||||
|
|
||||||
String name = CaseFormat.LOWER_UNDERSCORE.to( CaseFormat.LOWER_CAMEL, field.getKey() );
|
|
||||||
add( name, field.getValue(), attributes, null );
|
|
||||||
}
|
|
||||||
|
|
||||||
add( "task", TrackingField.TOTAL_TIME, attributes, TrackingField.TASKS );
|
|
||||||
add( "serverTask", TrackingField.SERVER_TIME, attributes, TrackingField.SERVER_COUNT );
|
|
||||||
|
|
||||||
this.info = new MBeanInfo(
|
|
||||||
ComputerMBean.class.getSimpleName(),
|
|
||||||
"metrics about all computers on the server",
|
|
||||||
attributes.toArray( new MBeanAttributeInfo[0] ), null, null, null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void register()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ManagementFactory.getPlatformMBeanServer().registerMBean( instance = new ComputerMBean(), new ObjectName( "dan200.computercraft:type=Computers" ) );
|
|
||||||
}
|
|
||||||
catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException |
|
|
||||||
MalformedObjectNameException e )
|
|
||||||
{
|
|
||||||
ComputerCraft.log.warn( "Failed to register JMX bean", e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerTracker()
|
|
||||||
{
|
|
||||||
if( instance != null ) Tracking.add( instance );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getAttribute( String attribute ) throws AttributeNotFoundException, MBeanException, ReflectionException
|
|
||||||
{
|
|
||||||
LongSupplier value = attributes.get( attribute );
|
|
||||||
if( value == null ) throw new AttributeNotFoundException();
|
|
||||||
return value.getAsLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAttribute( Attribute attribute ) throws InvalidAttributeValueException
|
|
||||||
{
|
|
||||||
throw new InvalidAttributeValueException( "Cannot set attribute" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AttributeList getAttributes( String[] attributes )
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AttributeList setAttributes( AttributeList attributes )
|
|
||||||
{
|
|
||||||
return new AttributeList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object invoke( String actionName, Object[] params, String[] signature ) throws MBeanException, ReflectionException
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nonnull
|
|
||||||
public MBeanInfo getMBeanInfo()
|
|
||||||
{
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addTaskTiming( Computer computer, long time )
|
|
||||||
{
|
|
||||||
addValue( computer, TrackingField.TOTAL_TIME, time );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addServerTiming( Computer computer, long time )
|
|
||||||
{
|
|
||||||
addValue( computer, TrackingField.SERVER_TIME, time );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addValue( Computer computer, TrackingField field, long change )
|
|
||||||
{
|
|
||||||
Counter counter = values.get( field );
|
|
||||||
counter.value.addAndGet( change );
|
|
||||||
counter.count.incrementAndGet();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MBeanAttributeInfo addAttribute( String name, String description, LongSupplier value )
|
|
||||||
{
|
|
||||||
attributes.put( name, value );
|
|
||||||
return new MBeanAttributeInfo( name, "long", description, true, false, false );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void add( String name, TrackingField field, List<MBeanAttributeInfo> attributes, TrackingField count )
|
|
||||||
{
|
|
||||||
Counter counter = new Counter();
|
|
||||||
values.put( field, counter );
|
|
||||||
|
|
||||||
String prettyName = LanguageMap.getInstance().getOrDefault( field.translationKey() );
|
|
||||||
attributes.add( addAttribute( name, prettyName, counter.value::longValue ) );
|
|
||||||
if( count != null )
|
|
||||||
{
|
|
||||||
String countName = LanguageMap.getInstance().getOrDefault( count.translationKey() );
|
|
||||||
attributes.add( addAttribute( name + "Count", countName, counter.count::longValue ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Counter
|
|
||||||
{
|
|
||||||
AtomicLong value = new AtomicLong();
|
|
||||||
AtomicLong count = new AtomicLong();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
|
||||||
*/
|
|
||||||
package dan200.computercraft.core.tracking;
|
|
||||||
|
|
||||||
import dan200.computercraft.core.computer.Computer;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
public class ComputerTracker
|
|
||||||
{
|
|
||||||
private final WeakReference<Computer> computer;
|
|
||||||
private final int computerId;
|
|
||||||
|
|
||||||
private long tasks;
|
|
||||||
private long totalTime;
|
|
||||||
private long maxTime;
|
|
||||||
|
|
||||||
private long serverCount;
|
|
||||||
private long serverTime;
|
|
||||||
|
|
||||||
private final Object2LongOpenHashMap<TrackingField> fields;
|
|
||||||
|
|
||||||
public ComputerTracker( Computer computer )
|
|
||||||
{
|
|
||||||
this.computer = new WeakReference<>( computer );
|
|
||||||
computerId = computer.getID();
|
|
||||||
fields = new Object2LongOpenHashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
ComputerTracker( ComputerTracker timings )
|
|
||||||
{
|
|
||||||
computer = timings.computer;
|
|
||||||
computerId = timings.computerId;
|
|
||||||
|
|
||||||
tasks = timings.tasks;
|
|
||||||
totalTime = timings.totalTime;
|
|
||||||
maxTime = timings.maxTime;
|
|
||||||
|
|
||||||
serverCount = timings.serverCount;
|
|
||||||
serverTime = timings.serverTime;
|
|
||||||
|
|
||||||
fields = new Object2LongOpenHashMap<>( timings.fields );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Computer getComputer()
|
|
||||||
{
|
|
||||||
return computer.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getComputerId()
|
|
||||||
{
|
|
||||||
return computerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTasks()
|
|
||||||
{
|
|
||||||
return tasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTotalTime()
|
|
||||||
{
|
|
||||||
return totalTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getMaxTime()
|
|
||||||
{
|
|
||||||
return maxTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getAverage()
|
|
||||||
{
|
|
||||||
return totalTime / tasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addTaskTiming( long time )
|
|
||||||
{
|
|
||||||
tasks++;
|
|
||||||
totalTime += time;
|
|
||||||
if( time > maxTime ) maxTime = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addMainTiming( long time )
|
|
||||||
{
|
|
||||||
serverCount++;
|
|
||||||
serverTime += time;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addValue( TrackingField field, long change )
|
|
||||||
{
|
|
||||||
synchronized( fields )
|
|
||||||
{
|
|
||||||
fields.addTo( field, change );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long get( TrackingField field )
|
|
||||||
{
|
|
||||||
if( field == TrackingField.TASKS ) return tasks;
|
|
||||||
if( field == TrackingField.MAX_TIME ) return maxTime;
|
|
||||||
if( field == TrackingField.TOTAL_TIME ) return totalTime;
|
|
||||||
if( field == TrackingField.AVERAGE_TIME ) return tasks == 0 ? 0 : totalTime / tasks;
|
|
||||||
|
|
||||||
if( field == TrackingField.SERVER_COUNT ) return serverCount;
|
|
||||||
if( field == TrackingField.SERVER_TIME ) return serverTime;
|
|
||||||
|
|
||||||
synchronized( fields )
|
|
||||||
{
|
|
||||||
return fields.getLong( field );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFormatted( TrackingField field )
|
|
||||||
{
|
|
||||||
return field.format( get( field ) );
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
|
||||||
*/
|
|
||||||
package dan200.computercraft.core.tracking;
|
|
||||||
|
|
||||||
import dan200.computercraft.core.computer.Computer;
|
|
||||||
|
|
||||||
public interface Tracker
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Report how long a task executed on the computer thread took.
|
|
||||||
*
|
|
||||||
* Computer thread tasks include events or a computer being turned on/off.
|
|
||||||
*
|
|
||||||
* @param computer The computer processing this task
|
|
||||||
* @param time The time taken for this task.
|
|
||||||
*/
|
|
||||||
default void addTaskTiming( Computer computer, long time )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Report how long a task executed on the server thread took.
|
|
||||||
*
|
|
||||||
* Server tasks include actions performed by peripherals.
|
|
||||||
*
|
|
||||||
* @param computer The computer processing this task
|
|
||||||
* @param time The time taken for this task.
|
|
||||||
*/
|
|
||||||
default void addServerTiming( Computer computer, long time )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increment an arbitrary field by some value. Implementations may track how often this is called
|
|
||||||
* as well as the change, to compute some level of "average".
|
|
||||||
*
|
|
||||||
* @param computer The computer to increment
|
|
||||||
* @param field The field to increment.
|
|
||||||
* @param change The amount to increment said field by.
|
|
||||||
*/
|
|
||||||
default void addValue( Computer computer, TrackingField field, long change )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
|
||||||
*/
|
|
||||||
package dan200.computercraft.core.tracking;
|
|
||||||
|
|
||||||
import dan200.computercraft.core.computer.Computer;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
public final class Tracking
|
|
||||||
{
|
|
||||||
static final AtomicInteger tracking = new AtomicInteger( 0 );
|
|
||||||
|
|
||||||
private static final Object lock = new Object();
|
|
||||||
private static final HashMap<UUID, TrackingContext> contexts = new HashMap<>();
|
|
||||||
private static final List<Tracker> trackers = new ArrayList<>();
|
|
||||||
|
|
||||||
private Tracking() {}
|
|
||||||
|
|
||||||
public static TrackingContext getContext( UUID uuid )
|
|
||||||
{
|
|
||||||
synchronized( lock )
|
|
||||||
{
|
|
||||||
TrackingContext context = contexts.get( uuid );
|
|
||||||
if( context == null ) contexts.put( uuid, context = new TrackingContext() );
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void add( Tracker tracker )
|
|
||||||
{
|
|
||||||
synchronized( lock )
|
|
||||||
{
|
|
||||||
trackers.add( tracker );
|
|
||||||
tracking.incrementAndGet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addTaskTiming( Computer computer, long time )
|
|
||||||
{
|
|
||||||
if( tracking.get() == 0 ) return;
|
|
||||||
|
|
||||||
synchronized( contexts )
|
|
||||||
{
|
|
||||||
for( TrackingContext context : contexts.values() ) context.addTaskTiming( computer, time );
|
|
||||||
for( Tracker tracker : trackers ) tracker.addTaskTiming( computer, time );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addServerTiming( Computer computer, long time )
|
|
||||||
{
|
|
||||||
if( tracking.get() == 0 ) return;
|
|
||||||
|
|
||||||
synchronized( contexts )
|
|
||||||
{
|
|
||||||
for( TrackingContext context : contexts.values() ) context.addServerTiming( computer, time );
|
|
||||||
for( Tracker tracker : trackers ) tracker.addServerTiming( computer, time );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addValue( Computer computer, TrackingField field, long change )
|
|
||||||
{
|
|
||||||
if( tracking.get() == 0 ) return;
|
|
||||||
|
|
||||||
synchronized( lock )
|
|
||||||
{
|
|
||||||
for( TrackingContext context : contexts.values() ) context.addValue( computer, field, change );
|
|
||||||
for( Tracker tracker : trackers ) tracker.addValue( computer, field, change );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void reset()
|
|
||||||
{
|
|
||||||
synchronized( lock )
|
|
||||||
{
|
|
||||||
contexts.clear();
|
|
||||||
trackers.clear();
|
|
||||||
tracking.set( 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
|
||||||
*/
|
|
||||||
package dan200.computercraft.core.tracking;
|
|
||||||
|
|
||||||
import com.google.common.collect.MapMaker;
|
|
||||||
import dan200.computercraft.core.computer.Computer;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tracks timing information about computers, including how long they ran for
|
|
||||||
* and the number of events they handled.
|
|
||||||
*
|
|
||||||
* Note that this <em>will</em> track computers which have been deleted (hence
|
|
||||||
* the presence of {@link #timingLookup} and {@link #timings}
|
|
||||||
*/
|
|
||||||
public class TrackingContext implements Tracker
|
|
||||||
{
|
|
||||||
private boolean tracking = false;
|
|
||||||
|
|
||||||
private final List<ComputerTracker> timings = new ArrayList<>();
|
|
||||||
private final Map<Computer, ComputerTracker> timingLookup = new MapMaker().weakKeys().makeMap();
|
|
||||||
|
|
||||||
public synchronized void start()
|
|
||||||
{
|
|
||||||
if( !tracking ) Tracking.tracking.incrementAndGet();
|
|
||||||
tracking = true;
|
|
||||||
|
|
||||||
timings.clear();
|
|
||||||
timingLookup.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean stop()
|
|
||||||
{
|
|
||||||
if( !tracking ) return false;
|
|
||||||
|
|
||||||
Tracking.tracking.decrementAndGet();
|
|
||||||
tracking = false;
|
|
||||||
timingLookup.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized List<ComputerTracker> getImmutableTimings()
|
|
||||||
{
|
|
||||||
ArrayList<ComputerTracker> timings = new ArrayList<>( this.timings.size() );
|
|
||||||
for( ComputerTracker timing : this.timings ) timings.add( new ComputerTracker( timing ) );
|
|
||||||
return timings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized List<ComputerTracker> getTimings()
|
|
||||||
{
|
|
||||||
return new ArrayList<>( timings );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addTaskTiming( Computer computer, long time )
|
|
||||||
{
|
|
||||||
if( !tracking ) return;
|
|
||||||
|
|
||||||
synchronized( this )
|
|
||||||
{
|
|
||||||
ComputerTracker computerTimings = timingLookup.get( computer );
|
|
||||||
if( computerTimings == null )
|
|
||||||
{
|
|
||||||
computerTimings = new ComputerTracker( computer );
|
|
||||||
timingLookup.put( computer, computerTimings );
|
|
||||||
timings.add( computerTimings );
|
|
||||||
}
|
|
||||||
|
|
||||||
computerTimings.addTaskTiming( time );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addServerTiming( Computer computer, long time )
|
|
||||||
{
|
|
||||||
if( !tracking ) return;
|
|
||||||
|
|
||||||
synchronized( this )
|
|
||||||
{
|
|
||||||
ComputerTracker computerTimings = timingLookup.get( computer );
|
|
||||||
if( computerTimings == null )
|
|
||||||
{
|
|
||||||
computerTimings = new ComputerTracker( computer );
|
|
||||||
timingLookup.put( computer, computerTimings );
|
|
||||||
timings.add( computerTimings );
|
|
||||||
}
|
|
||||||
|
|
||||||
computerTimings.addMainTiming( time );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addValue( Computer computer, TrackingField field, long change )
|
|
||||||
{
|
|
||||||
if( !tracking ) return;
|
|
||||||
|
|
||||||
synchronized( this )
|
|
||||||
{
|
|
||||||
ComputerTracker computerTimings = timingLookup.get( computer );
|
|
||||||
if( computerTimings == null )
|
|
||||||
{
|
|
||||||
computerTimings = new ComputerTracker( computer );
|
|
||||||
timingLookup.put( computer, computerTimings );
|
|
||||||
timings.add( computerTimings );
|
|
||||||
}
|
|
||||||
|
|
||||||
computerTimings.addValue( field, change );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
|
||||||
*/
|
|
||||||
package dan200.computercraft.core.tracking;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.LongFunction;
|
|
||||||
|
|
||||||
public final class TrackingField
|
|
||||||
{
|
|
||||||
private static final Map<String, TrackingField> fields = new HashMap<>();
|
|
||||||
|
|
||||||
public static final TrackingField TASKS = TrackingField.of( "tasks", x -> String.format( "%4d", x ) );
|
|
||||||
public static final TrackingField TOTAL_TIME = TrackingField.of( "total", x -> String.format( "%7.1fms", x / 1e6 ) );
|
|
||||||
public static final TrackingField AVERAGE_TIME = TrackingField.of( "average", x -> String.format( "%4.1fms", x / 1e6 ) );
|
|
||||||
public static final TrackingField MAX_TIME = TrackingField.of( "max", x -> String.format( "%5.1fms", x / 1e6 ) );
|
|
||||||
|
|
||||||
public static final TrackingField SERVER_COUNT = TrackingField.of( "server_count", x -> String.format( "%4d", x ) );
|
|
||||||
public static final TrackingField SERVER_TIME = TrackingField.of( "server_time", x -> String.format( "%7.1fms", x / 1e6 ) );
|
|
||||||
|
|
||||||
public static final TrackingField PERIPHERAL_OPS = TrackingField.of( "peripheral", TrackingField::formatDefault );
|
|
||||||
public static final TrackingField FS_OPS = TrackingField.of( "fs", TrackingField::formatDefault );
|
|
||||||
public static final TrackingField TURTLE_OPS = TrackingField.of( "turtle", TrackingField::formatDefault );
|
|
||||||
|
|
||||||
public static final TrackingField HTTP_REQUESTS = TrackingField.of( "http", TrackingField::formatDefault );
|
|
||||||
public static final TrackingField HTTP_UPLOAD = TrackingField.of( "http_upload", TrackingField::formatBytes );
|
|
||||||
public static final TrackingField HTTP_DOWNLOAD = TrackingField.of( "http_download", TrackingField::formatBytes );
|
|
||||||
|
|
||||||
public static final TrackingField WEBSOCKET_INCOMING = TrackingField.of( "websocket_incoming", TrackingField::formatBytes );
|
|
||||||
public static final TrackingField WEBSOCKET_OUTGOING = TrackingField.of( "websocket_outgoing", TrackingField::formatBytes );
|
|
||||||
|
|
||||||
public static final TrackingField COROUTINES_CREATED = TrackingField.of( "coroutines_created", x -> String.format( "%4d", x ) );
|
|
||||||
public static final TrackingField COROUTINES_DISPOSED = TrackingField.of( "coroutines_dead", x -> String.format( "%4d", x ) );
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
private final String translationKey;
|
|
||||||
private final LongFunction<String> format;
|
|
||||||
|
|
||||||
public String id()
|
|
||||||
{
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String translationKey()
|
|
||||||
{
|
|
||||||
return translationKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TrackingField( String id, LongFunction<String> format )
|
|
||||||
{
|
|
||||||
this.id = id;
|
|
||||||
translationKey = "tracking_field.computercraft." + id + ".name";
|
|
||||||
this.format = format;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String format( long value )
|
|
||||||
{
|
|
||||||
return format.apply( value );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TrackingField of( String id, LongFunction<String> format )
|
|
||||||
{
|
|
||||||
TrackingField field = new TrackingField( id, format );
|
|
||||||
fields.put( id, field );
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<String, TrackingField> fields()
|
|
||||||
{
|
|
||||||
return Collections.unmodifiableMap( fields );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatDefault( long value )
|
|
||||||
{
|
|
||||||
return String.format( "%6d", value );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* So technically a kibibyte, but let's not argue here.
|
|
||||||
*/
|
|
||||||
private static final int KILOBYTE_SIZE = 1024;
|
|
||||||
|
|
||||||
private static final String SI_PREFIXES = "KMGT";
|
|
||||||
|
|
||||||
private static String formatBytes( long bytes )
|
|
||||||
{
|
|
||||||
if( bytes < 1024 ) return String.format( "%10d B", bytes );
|
|
||||||
int exp = (int) (Math.log( bytes ) / Math.log( KILOBYTE_SIZE ));
|
|
||||||
if( exp > SI_PREFIXES.length() ) exp = SI_PREFIXES.length();
|
|
||||||
return String.format( "%10.1f %siB", bytes / Math.pow( KILOBYTE_SIZE, exp ), SI_PREFIXES.charAt( exp - 1 ) );
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,10 +9,9 @@ import dan200.computercraft.ComputerCraft;
|
|||||||
import dan200.computercraft.core.apis.http.NetworkUtils;
|
import dan200.computercraft.core.apis.http.NetworkUtils;
|
||||||
import dan200.computercraft.core.computer.MainThread;
|
import dan200.computercraft.core.computer.MainThread;
|
||||||
import dan200.computercraft.core.filesystem.ResourceMount;
|
import dan200.computercraft.core.filesystem.ResourceMount;
|
||||||
import dan200.computercraft.core.tracking.ComputerMBean;
|
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
|
||||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.ComputerMBean;
|
||||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
|
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.loot.ConstantRange;
|
import net.minecraft.loot.ConstantRange;
|
||||||
@ -74,7 +73,7 @@ public final class CommonHooks
|
|||||||
|
|
||||||
resetState();
|
resetState();
|
||||||
ServerContext.create( server );
|
ServerContext.create( server );
|
||||||
ComputerMBean.registerTracker();
|
ComputerMBean.start( server );
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
@ -88,7 +87,6 @@ public final class CommonHooks
|
|||||||
ServerContext.close();
|
ServerContext.close();
|
||||||
MainThread.reset();
|
MainThread.reset();
|
||||||
WirelessNetwork.resetNetworks();
|
WirelessNetwork.resetNetworks();
|
||||||
Tracking.reset();
|
|
||||||
NetworkUtils.reset();
|
NetworkUtils.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,17 +9,17 @@ import com.mojang.brigadier.CommandDispatcher;
|
|||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
import dan200.computercraft.core.tracking.ComputerTracker;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
|
||||||
import dan200.computercraft.core.tracking.TrackingContext;
|
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
|
||||||
import dan200.computercraft.shared.command.text.TableBuilder;
|
import dan200.computercraft.shared.command.text.TableBuilder;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
|
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.basic.BasicComputerMetricsObserver;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.basic.ComputerMetrics;
|
||||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||||
import net.minecraft.command.CommandSource;
|
import net.minecraft.command.CommandSource;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
@ -46,7 +46,7 @@ import static dan200.computercraft.shared.command.Exceptions.*;
|
|||||||
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.getComputerArgument;
|
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.getComputerArgument;
|
||||||
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.oneComputer;
|
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.oneComputer;
|
||||||
import static dan200.computercraft.shared.command.arguments.ComputersArgumentType.*;
|
import static dan200.computercraft.shared.command.arguments.ComputersArgumentType.*;
|
||||||
import static dan200.computercraft.shared.command.arguments.TrackingFieldArgumentType.trackingField;
|
import static dan200.computercraft.shared.command.arguments.TrackingFieldArgumentType.metric;
|
||||||
import static dan200.computercraft.shared.command.builder.CommandBuilder.args;
|
import static dan200.computercraft.shared.command.builder.CommandBuilder.args;
|
||||||
import static dan200.computercraft.shared.command.builder.CommandBuilder.command;
|
import static dan200.computercraft.shared.command.builder.CommandBuilder.command;
|
||||||
import static dan200.computercraft.shared.command.builder.HelpingArgumentBuilder.choice;
|
import static dan200.computercraft.shared.command.builder.HelpingArgumentBuilder.choice;
|
||||||
@ -248,7 +248,7 @@ public final class CommandComputerCraft
|
|||||||
.then( command( "start" )
|
.then( command( "start" )
|
||||||
.requires( UserLevel.OWNER_OP )
|
.requires( UserLevel.OWNER_OP )
|
||||||
.executes( context -> {
|
.executes( context -> {
|
||||||
getTimingContext( context.getSource() ).start();
|
getMetricsInstance( context.getSource() ).start();
|
||||||
|
|
||||||
String stopCommand = "/computercraft track stop";
|
String stopCommand = "/computercraft track stop";
|
||||||
context.getSource().sendSuccess( translate( "commands.computercraft.track.start.stop",
|
context.getSource().sendSuccess( translate( "commands.computercraft.track.start.stop",
|
||||||
@ -259,17 +259,17 @@ public final class CommandComputerCraft
|
|||||||
.then( command( "stop" )
|
.then( command( "stop" )
|
||||||
.requires( UserLevel.OWNER_OP )
|
.requires( UserLevel.OWNER_OP )
|
||||||
.executes( context -> {
|
.executes( context -> {
|
||||||
TrackingContext timings = getTimingContext( context.getSource() );
|
BasicComputerMetricsObserver timings = getMetricsInstance( context.getSource() );
|
||||||
if( !timings.stop() ) throw NOT_TRACKING_EXCEPTION.create();
|
if( !timings.stop() ) throw NOT_TRACKING_EXCEPTION.create();
|
||||||
displayTimings( context.getSource(), timings.getImmutableTimings(), TrackingField.AVERAGE_TIME, DEFAULT_FIELDS );
|
displayTimings( context.getSource(), timings.getSnapshot(), new AggregatedMetric( Metrics.COMPUTER_TASKS, Aggregate.AVG ), DEFAULT_FIELDS );
|
||||||
return 1;
|
return 1;
|
||||||
} ) )
|
} ) )
|
||||||
|
|
||||||
.then( command( "dump" )
|
.then( command( "dump" )
|
||||||
.requires( UserLevel.OWNER_OP )
|
.requires( UserLevel.OWNER_OP )
|
||||||
.argManyValue( "fields", trackingField(), DEFAULT_FIELDS )
|
.argManyValue( "fields", metric(), DEFAULT_FIELDS )
|
||||||
.executes( ( context, fields ) -> {
|
.executes( ( context, fields ) -> {
|
||||||
TrackingField sort;
|
AggregatedMetric sort;
|
||||||
if( fields.size() == 1 && DEFAULT_FIELDS.contains( fields.get( 0 ) ) )
|
if( fields.size() == 1 && DEFAULT_FIELDS.contains( fields.get( 0 ) ) )
|
||||||
{
|
{
|
||||||
sort = fields.get( 0 );
|
sort = fields.get( 0 );
|
||||||
@ -362,50 +362,48 @@ public final class CommandComputerCraft
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private static TrackingContext getTimingContext( CommandSource source )
|
private static BasicComputerMetricsObserver getMetricsInstance( CommandSource source )
|
||||||
{
|
{
|
||||||
Entity entity = source.getEntity();
|
Entity entity = source.getEntity();
|
||||||
return entity instanceof PlayerEntity ? Tracking.getContext( entity.getUUID() ) : Tracking.getContext( SYSTEM_UUID );
|
return ServerContext.get( source.getServer() ).metrics().getMetricsInstance( entity instanceof PlayerEntity ? entity.getUUID() : SYSTEM_UUID );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final List<TrackingField> DEFAULT_FIELDS = Arrays.asList( TrackingField.TASKS, TrackingField.TOTAL_TIME, TrackingField.AVERAGE_TIME, TrackingField.MAX_TIME );
|
private static final List<AggregatedMetric> DEFAULT_FIELDS = Arrays.asList(
|
||||||
|
new AggregatedMetric( Metrics.COMPUTER_TASKS, Aggregate.COUNT ),
|
||||||
|
new AggregatedMetric( Metrics.COMPUTER_TASKS, Aggregate.NONE ),
|
||||||
|
new AggregatedMetric( Metrics.COMPUTER_TASKS, Aggregate.AVG ),
|
||||||
|
new AggregatedMetric( Metrics.COMPUTER_TASKS, Aggregate.MAX )
|
||||||
|
);
|
||||||
|
|
||||||
private static int displayTimings( CommandSource source, TrackingField sortField, List<TrackingField> fields ) throws CommandSyntaxException
|
private static int displayTimings( CommandSource source, AggregatedMetric sortField, List<AggregatedMetric> fields ) throws CommandSyntaxException
|
||||||
{
|
{
|
||||||
return displayTimings( source, getTimingContext( source ).getTimings(), sortField, fields );
|
return displayTimings( source, getMetricsInstance( source ).getTimings(), sortField, fields );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int displayTimings( CommandSource source, @Nonnull List<ComputerTracker> timings, @Nonnull TrackingField sortField, @Nonnull List<TrackingField> fields ) throws CommandSyntaxException
|
private static int displayTimings( CommandSource source, List<ComputerMetrics> timings, AggregatedMetric sortField, List<AggregatedMetric> fields ) throws CommandSyntaxException
|
||||||
{
|
{
|
||||||
if( timings.isEmpty() ) throw NO_TIMINGS_EXCEPTION.create();
|
if( timings.isEmpty() ) throw NO_TIMINGS_EXCEPTION.create();
|
||||||
|
|
||||||
Map<Computer, ServerComputer> lookup = new HashMap<>();
|
timings.sort( Comparator.<ComputerMetrics, Long>comparing( x -> x.get( sortField.metric(), sortField.aggregate() ) ).reversed() );
|
||||||
int maxId = 0, maxInstance = 0;
|
|
||||||
for( ServerComputer server : ServerContext.get( source.getServer() ).registry().getComputers() )
|
|
||||||
{
|
|
||||||
lookup.put( server.getComputer(), server );
|
|
||||||
|
|
||||||
if( server.getInstanceID() > maxInstance ) maxInstance = server.getInstanceID();
|
|
||||||
if( server.getID() > maxId ) maxId = server.getID();
|
|
||||||
}
|
|
||||||
|
|
||||||
timings.sort( Comparator.<ComputerTracker, Long>comparing( x -> x.get( sortField ) ).reversed() );
|
|
||||||
|
|
||||||
ITextComponent[] headers = new ITextComponent[1 + fields.size()];
|
ITextComponent[] headers = new ITextComponent[1 + fields.size()];
|
||||||
headers[0] = translate( "commands.computercraft.track.dump.computer" );
|
headers[0] = translate( "commands.computercraft.track.dump.computer" );
|
||||||
for( int i = 0; i < fields.size(); i++ ) headers[i + 1] = translate( fields.get( i ).translationKey() );
|
for( int i = 0; i < fields.size(); i++ ) headers[i + 1] = fields.get( i ).displayName();
|
||||||
TableBuilder table = new TableBuilder( TRACK_ID, headers );
|
TableBuilder table = new TableBuilder( TRACK_ID, headers );
|
||||||
|
|
||||||
for( ComputerTracker entry : timings )
|
for( ComputerMetrics entry : timings )
|
||||||
{
|
{
|
||||||
Computer computer = entry.getComputer();
|
ServerComputer serverComputer = entry.computer();
|
||||||
ServerComputer serverComputer = computer == null ? null : lookup.get( computer );
|
|
||||||
|
|
||||||
ITextComponent computerComponent = linkComputer( source, serverComputer, entry.getComputerId() );
|
ITextComponent computerComponent = linkComputer( source, serverComputer, entry.computerId() );
|
||||||
|
|
||||||
ITextComponent[] row = new ITextComponent[1 + fields.size()];
|
ITextComponent[] row = new ITextComponent[1 + fields.size()];
|
||||||
row[0] = computerComponent;
|
row[0] = computerComponent;
|
||||||
for( int i = 0; i < fields.size(); i++ ) row[i + 1] = text( entry.getFormatted( fields.get( i ) ) );
|
for( int i = 0; i < fields.size(); i++ )
|
||||||
|
{
|
||||||
|
AggregatedMetric metric = fields.get( i );
|
||||||
|
row[i + 1] = text( entry.getFormatted( metric.metric(), metric.aggregate() ) );
|
||||||
|
}
|
||||||
table.row( row );
|
table.row( row );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ public final class ArgumentSerializers
|
|||||||
|
|
||||||
public static void register()
|
public static void register()
|
||||||
{
|
{
|
||||||
register( new ResourceLocation( ComputerCraft.MOD_ID, "tracking_field" ), TrackingFieldArgumentType.trackingField() );
|
register( new ResourceLocation( ComputerCraft.MOD_ID, "tracking_field" ), TrackingFieldArgumentType.metric() );
|
||||||
register( new ResourceLocation( ComputerCraft.MOD_ID, "computer" ), ComputerArgumentType.oneComputer() );
|
register( new ResourceLocation( ComputerCraft.MOD_ID, "computer" ), ComputerArgumentType.oneComputer() );
|
||||||
register( new ResourceLocation( ComputerCraft.MOD_ID, "computers" ), ComputersArgumentType.class, new ComputersArgumentType.Serializer() );
|
register( new ResourceLocation( ComputerCraft.MOD_ID, "computers" ), ComputersArgumentType.class, new ComputersArgumentType.Serializer() );
|
||||||
registerUnsafe( new ResourceLocation( ComputerCraft.MOD_ID, "repeat" ), RepeatArgumentType.class, new RepeatArgumentType.Serializer() );
|
registerUnsafe( new ResourceLocation( ComputerCraft.MOD_ID, "repeat" ), RepeatArgumentType.class, new RepeatArgumentType.Serializer() );
|
||||||
|
@ -5,21 +5,24 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.shared.command.arguments;
|
package dan200.computercraft.shared.command.arguments;
|
||||||
|
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
|
||||||
import dan200.computercraft.shared.command.Exceptions;
|
import dan200.computercraft.shared.command.Exceptions;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
||||||
|
|
||||||
import static dan200.computercraft.shared.command.text.ChatHelpers.translate;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public final class TrackingFieldArgumentType extends ChoiceArgumentType<TrackingField>
|
public final class TrackingFieldArgumentType extends ChoiceArgumentType<AggregatedMetric>
|
||||||
{
|
{
|
||||||
private static final TrackingFieldArgumentType INSTANCE = new TrackingFieldArgumentType();
|
private static final TrackingFieldArgumentType INSTANCE = new TrackingFieldArgumentType();
|
||||||
|
|
||||||
private TrackingFieldArgumentType()
|
private TrackingFieldArgumentType()
|
||||||
{
|
{
|
||||||
super( TrackingField.fields().values(), TrackingField::id, x -> translate( x.translationKey() ), Exceptions.TRACKING_FIELD_ARG_NONE );
|
super(
|
||||||
|
AggregatedMetric.aggregatedMetrics().collect( Collectors.toList() ),
|
||||||
|
AggregatedMetric::name, AggregatedMetric::displayName, Exceptions.TRACKING_FIELD_ARG_NONE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TrackingFieldArgumentType trackingField()
|
public static TrackingFieldArgumentType metric()
|
||||||
{
|
{
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import dan200.computercraft.core.apis.IAPIEnvironment;
|
|||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.core.computer.Computer;
|
||||||
import dan200.computercraft.core.computer.ComputerEnvironment;
|
import dan200.computercraft.core.computer.ComputerEnvironment;
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||||
import dan200.computercraft.shared.network.NetworkHandler;
|
import dan200.computercraft.shared.network.NetworkHandler;
|
||||||
@ -37,6 +38,7 @@ public class ServerComputer implements InputHandler, ComputerEnvironment
|
|||||||
private BlockPos position;
|
private BlockPos position;
|
||||||
|
|
||||||
private final ComputerFamily family;
|
private final ComputerFamily family;
|
||||||
|
private final MetricsObserver metrics;
|
||||||
private final Computer computer;
|
private final Computer computer;
|
||||||
|
|
||||||
private final Terminal terminal;
|
private final Terminal terminal;
|
||||||
@ -53,6 +55,7 @@ public class ServerComputer implements InputHandler, ComputerEnvironment
|
|||||||
ServerContext context = ServerContext.get( world.getServer() );
|
ServerContext context = ServerContext.get( world.getServer() );
|
||||||
instanceID = context.registry().getUnusedInstanceID();
|
instanceID = context.registry().getUnusedInstanceID();
|
||||||
terminal = new Terminal( terminalWidth, terminalHeight, family != ComputerFamily.NORMAL, this::markTerminalChanged );
|
terminal = new Terminal( terminalWidth, terminalHeight, family != ComputerFamily.NORMAL, this::markTerminalChanged );
|
||||||
|
metrics = context.metrics().createMetricObserver( this );
|
||||||
|
|
||||||
computer = new Computer( context.environment(), this, terminal, computerID );
|
computer = new Computer( context.environment(), this, terminal, computerID );
|
||||||
computer.setLabel( label );
|
computer.setLabel( label );
|
||||||
@ -262,8 +265,6 @@ public class ServerComputer implements InputHandler, ComputerEnvironment
|
|||||||
computer.setLabel( label );
|
computer.setLabel( label );
|
||||||
}
|
}
|
||||||
|
|
||||||
// IComputerEnvironment implementation
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTimeOfDay()
|
public double getTimeOfDay()
|
||||||
{
|
{
|
||||||
@ -276,6 +277,12 @@ public class ServerComputer implements InputHandler, ComputerEnvironment
|
|||||||
return (int) ((world.getDayTime() + 6000) / 24000) + 1;
|
return (int) ((world.getDayTime() + 6000) / 24000) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MetricsObserver getMetrics()
|
||||||
|
{
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWritableMount createRootMount()
|
public IWritableMount createRootMount()
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
|||||||
import dan200.computercraft.api.filesystem.IMount;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
import dan200.computercraft.core.computer.GlobalEnvironment;
|
import dan200.computercraft.core.computer.GlobalEnvironment;
|
||||||
import dan200.computercraft.shared.CommonHooks;
|
import dan200.computercraft.shared.CommonHooks;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
|
||||||
import dan200.computercraft.shared.util.IDAssigner;
|
import dan200.computercraft.shared.util.IDAssigner;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
@ -42,6 +43,7 @@ public final class ServerContext
|
|||||||
private final MinecraftServer server;
|
private final MinecraftServer server;
|
||||||
|
|
||||||
private final ServerComputerRegistry registry = new ServerComputerRegistry();
|
private final ServerComputerRegistry registry = new ServerComputerRegistry();
|
||||||
|
private final GlobalMetrics metrics = new GlobalMetrics();
|
||||||
private final GlobalEnvironment environment;
|
private final GlobalEnvironment environment;
|
||||||
private final IDAssigner idAssigner;
|
private final IDAssigner idAssigner;
|
||||||
private final Path storageDir;
|
private final Path storageDir;
|
||||||
@ -145,6 +147,16 @@ public final class ServerContext
|
|||||||
return storageDir;
|
return storageDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current global metrics store.
|
||||||
|
*
|
||||||
|
* @return The current metrics store.
|
||||||
|
*/
|
||||||
|
public GlobalMetrics metrics()
|
||||||
|
{
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
private static final class Environment implements GlobalEnvironment
|
private static final class Environment implements GlobalEnvironment
|
||||||
{
|
{
|
||||||
private final MinecraftServer server;
|
private final MinecraftServer server;
|
||||||
|
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.shared.computer.metrics;
|
||||||
|
|
||||||
|
import com.google.common.base.CaseFormat;
|
||||||
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.management.*;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.function.LongSupplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An MBean which exposes aggregate statistics about all computers on the server.
|
||||||
|
*/
|
||||||
|
public final class ComputerMBean implements DynamicMBean, ComputerMetricsObserver
|
||||||
|
{
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger( ComputerMBean.class );
|
||||||
|
|
||||||
|
private static @Nullable ComputerMBean instance;
|
||||||
|
|
||||||
|
private final Map<String, LongSupplier> attributes = new HashMap<>();
|
||||||
|
private final Int2ObjectMap<Counter> values = new Int2ObjectOpenHashMap<>();
|
||||||
|
private final MBeanInfo info;
|
||||||
|
|
||||||
|
private ComputerMBean()
|
||||||
|
{
|
||||||
|
Metrics.init();
|
||||||
|
|
||||||
|
List<MBeanAttributeInfo> attributes = new ArrayList<>();
|
||||||
|
for( Map.Entry<String, Metric> field : Metric.metrics().entrySet() )
|
||||||
|
{
|
||||||
|
String name = CaseFormat.LOWER_UNDERSCORE.to( CaseFormat.LOWER_CAMEL, field.getKey() );
|
||||||
|
add( name, field.getValue(), attributes );
|
||||||
|
}
|
||||||
|
|
||||||
|
info = new MBeanInfo( ComputerMBean.class.getSimpleName(), "metrics about all computers on the server", attributes.toArray( new MBeanAttributeInfo[0] ), null, null, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register()
|
||||||
|
{
|
||||||
|
if( instance != null ) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ManagementFactory.getPlatformMBeanServer().registerMBean( instance = new ComputerMBean(), new ObjectName( "dan200.computercraft:type=Computers" ) );
|
||||||
|
}
|
||||||
|
catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException |
|
||||||
|
MalformedObjectNameException e )
|
||||||
|
{
|
||||||
|
LOGGER.warn( "Failed to register JMX bean", e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void start( MinecraftServer server )
|
||||||
|
{
|
||||||
|
if( instance != null ) ServerContext.get( server ).metrics().addObserver( instance );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getAttribute( String attribute ) throws AttributeNotFoundException
|
||||||
|
{
|
||||||
|
LongSupplier value = attributes.get( attribute );
|
||||||
|
if( value == null ) throw new AttributeNotFoundException();
|
||||||
|
return value.getAsLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute( Attribute attribute ) throws InvalidAttributeValueException
|
||||||
|
{
|
||||||
|
throw new InvalidAttributeValueException( "Cannot set attribute" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AttributeList getAttributes( String[] names )
|
||||||
|
{
|
||||||
|
AttributeList result = new AttributeList( names.length );
|
||||||
|
for( String name : names )
|
||||||
|
{
|
||||||
|
LongSupplier value = attributes.get( name );
|
||||||
|
if( value != null ) result.add( new Attribute( name, value.getAsLong() ) );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AttributeList setAttributes( AttributeList attributes )
|
||||||
|
{
|
||||||
|
return new AttributeList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object invoke( String actionName, Object[] params, String[] signature )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MBeanInfo getMBeanInfo()
|
||||||
|
{
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void observe( Metric field, long change )
|
||||||
|
{
|
||||||
|
Counter counter = values.get( field.id() );
|
||||||
|
counter.value.addAndGet( change );
|
||||||
|
counter.count.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void observe( ServerComputer computer, Metric.Counter counter )
|
||||||
|
{
|
||||||
|
observe( counter, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void observe( ServerComputer computer, Metric.Event event, long value )
|
||||||
|
{
|
||||||
|
observe( event, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
private MBeanAttributeInfo addAttribute( String name, String description, LongSupplier value )
|
||||||
|
{
|
||||||
|
attributes.put( name, value );
|
||||||
|
return new MBeanAttributeInfo( name, "long", description, true, false, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add( String name, Metric field, List<MBeanAttributeInfo> attributes )
|
||||||
|
{
|
||||||
|
Counter counter = new Counter();
|
||||||
|
values.put( field.id(), counter );
|
||||||
|
|
||||||
|
String prettyName = new AggregatedMetric( field, Aggregate.NONE ).displayName().getString();
|
||||||
|
attributes.add( addAttribute( name, prettyName, counter.value::longValue ) );
|
||||||
|
if( field instanceof Metric.Event )
|
||||||
|
{
|
||||||
|
String countName = new AggregatedMetric( field, Aggregate.COUNT ).displayName().getString();
|
||||||
|
attributes.add( addAttribute( name + "Count", countName, counter.count::longValue ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Counter
|
||||||
|
{
|
||||||
|
final AtomicLong value = new AtomicLong();
|
||||||
|
final AtomicLong count = new AtomicLong();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.shared.computer.metrics;
|
||||||
|
|
||||||
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A global version of {@link MetricsObserver}, which monitors multiple computers.
|
||||||
|
*/
|
||||||
|
public interface ComputerMetricsObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Increment a computer's counter by 1.
|
||||||
|
*
|
||||||
|
* @param computer The computer which incremented its counter.
|
||||||
|
* @param counter The counter to observe.
|
||||||
|
* @see MetricsObserver#observe(Metric.Counter)
|
||||||
|
*/
|
||||||
|
void observe( ServerComputer computer, Metric.Counter counter );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observe a single instance of an event.
|
||||||
|
*
|
||||||
|
* @param computer The computer which incremented its counter.
|
||||||
|
* @param event The event to observe.
|
||||||
|
* @param value The value corresponding to this event.
|
||||||
|
* @see MetricsObserver#observe(Metric.Event, long)
|
||||||
|
*/
|
||||||
|
void observe( ServerComputer computer, Metric.Event event, long value );
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.shared.computer.metrics;
|
||||||
|
|
||||||
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.basic.BasicComputerMetricsObserver;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The global metrics system.
|
||||||
|
*
|
||||||
|
* @see ServerContext#metrics() To obtain an instance of this system.
|
||||||
|
*/
|
||||||
|
public final class GlobalMetrics
|
||||||
|
{
|
||||||
|
volatile boolean enabled = false;
|
||||||
|
final Object lock = new Object();
|
||||||
|
final List<ComputerMetricsObserver> trackers = new ArrayList<>();
|
||||||
|
|
||||||
|
private final HashMap<UUID, BasicComputerMetricsObserver> instances = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a metrics observer for a specific player. This will not be active until
|
||||||
|
* {@link BasicComputerMetricsObserver#start()} is called.
|
||||||
|
*
|
||||||
|
* @param uuid The player's UUID.
|
||||||
|
* @return The metrics instance for this player.
|
||||||
|
*/
|
||||||
|
public BasicComputerMetricsObserver getMetricsInstance( UUID uuid )
|
||||||
|
{
|
||||||
|
synchronized( lock )
|
||||||
|
{
|
||||||
|
BasicComputerMetricsObserver context = instances.get( uuid );
|
||||||
|
if( context == null ) instances.put( uuid, context = new BasicComputerMetricsObserver( this ) );
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new global metrics observer. This will receive metrics data for all computers.
|
||||||
|
*
|
||||||
|
* @param tracker The observer to add.
|
||||||
|
*/
|
||||||
|
public void addObserver( ComputerMetricsObserver tracker )
|
||||||
|
{
|
||||||
|
synchronized( lock )
|
||||||
|
{
|
||||||
|
if( trackers.contains( tracker ) ) return;
|
||||||
|
trackers.add( tracker );
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a previously-registered global metrics observer.
|
||||||
|
*
|
||||||
|
* @param tracker The observer to add.
|
||||||
|
*/
|
||||||
|
public void removeObserver( ComputerMetricsObserver tracker )
|
||||||
|
{
|
||||||
|
synchronized( lock )
|
||||||
|
{
|
||||||
|
trackers.remove( tracker );
|
||||||
|
enabled = !trackers.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an observer for a computer. This will delegate to all registered {@link ComputerMetricsObserver}s.
|
||||||
|
*
|
||||||
|
* @param computer The computer to create the observer for.
|
||||||
|
* @return The instantiated observer.
|
||||||
|
*/
|
||||||
|
public MetricsObserver createMetricObserver( ServerComputer computer )
|
||||||
|
{
|
||||||
|
return new DispatchObserver( computer );
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class DispatchObserver implements MetricsObserver
|
||||||
|
{
|
||||||
|
private final ServerComputer computer;
|
||||||
|
|
||||||
|
private DispatchObserver( ServerComputer computer )
|
||||||
|
{
|
||||||
|
this.computer = computer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void observe( Metric.Counter counter )
|
||||||
|
{
|
||||||
|
if( !enabled ) return;
|
||||||
|
synchronized( lock )
|
||||||
|
{
|
||||||
|
// TODO: The lock here is nasty and aggressive. However, in my benchmarks I've found it has about
|
||||||
|
// equivalent performance to a CoW list and atomics. Would be good to drill into this, as locks do not
|
||||||
|
// scale well.
|
||||||
|
for( ComputerMetricsObserver observer : trackers ) observer.observe( computer, counter );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void observe( Metric.Event event, long value )
|
||||||
|
{
|
||||||
|
if( !enabled ) return;
|
||||||
|
synchronized( lock )
|
||||||
|
{
|
||||||
|
for( ComputerMetricsObserver observer : trackers ) observer.observe( computer, event, value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.shared.computer.metrics.basic;
|
||||||
|
|
||||||
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An aggregate over a {@link Metric}.
|
||||||
|
* <p>
|
||||||
|
* Only {@link Metric.Event events} support non-{@link Aggregate#NONE} aggregates.
|
||||||
|
*/
|
||||||
|
public enum Aggregate
|
||||||
|
{
|
||||||
|
NONE( "none" ),
|
||||||
|
COUNT( "count" ),
|
||||||
|
AVG( "avg" ),
|
||||||
|
MAX( "max" );
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
Aggregate( String id )
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String id()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.shared.computer.metrics.basic;
|
||||||
|
|
||||||
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
|
import net.minecraft.util.text.ITextComponent;
|
||||||
|
import net.minecraft.util.text.TranslationTextComponent;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An aggregate of a specific metric.
|
||||||
|
*/
|
||||||
|
public class AggregatedMetric
|
||||||
|
{
|
||||||
|
private static final String TRANSLATION_PREFIX = "tracking_field.computercraft.";
|
||||||
|
|
||||||
|
private final Metric metric;
|
||||||
|
private final Aggregate aggregate;
|
||||||
|
|
||||||
|
public AggregatedMetric( Metric metric, Aggregate aggregate )
|
||||||
|
{
|
||||||
|
this.metric = metric;
|
||||||
|
this.aggregate = aggregate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Metric metric()
|
||||||
|
{
|
||||||
|
return metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Aggregate aggregate()
|
||||||
|
{
|
||||||
|
return aggregate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream<AggregatedMetric> aggregatedMetrics()
|
||||||
|
{
|
||||||
|
Metrics.init();
|
||||||
|
return Metric.metrics().values().stream()
|
||||||
|
.flatMap( m -> m instanceof Metric.Counter
|
||||||
|
? Stream.of( new AggregatedMetric( m, Aggregate.NONE ) )
|
||||||
|
: Arrays.stream( Aggregate.values() ).map( a -> new AggregatedMetric( m, a ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name()
|
||||||
|
{
|
||||||
|
return aggregate() == Aggregate.NONE ? metric.name() : metric().name() + "_" + aggregate().id();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITextComponent displayName()
|
||||||
|
{
|
||||||
|
TranslationTextComponent name = new TranslationTextComponent( TRANSLATION_PREFIX + metric().name() + ".name" );
|
||||||
|
return aggregate() == Aggregate.NONE ? name : new TranslationTextComponent( TRANSLATION_PREFIX + aggregate().id(), name );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.shared.computer.metrics.basic;
|
||||||
|
|
||||||
|
import com.google.common.collect.MapMaker;
|
||||||
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.ComputerMetricsObserver;
|
||||||
|
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks timing information about computers, including how long they ran for and the number of events they handled.
|
||||||
|
* <p>
|
||||||
|
* Note that this will retain timings for computers which have been deleted.
|
||||||
|
*/
|
||||||
|
public class BasicComputerMetricsObserver implements ComputerMetricsObserver
|
||||||
|
{
|
||||||
|
private final GlobalMetrics owner;
|
||||||
|
private boolean tracking = false;
|
||||||
|
|
||||||
|
private final List<ComputerMetrics> timings = new ArrayList<>();
|
||||||
|
private final Map<ServerComputer, ComputerMetrics> timingLookup = new MapMaker().weakKeys().makeMap();
|
||||||
|
|
||||||
|
public BasicComputerMetricsObserver( GlobalMetrics owner )
|
||||||
|
{
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void start()
|
||||||
|
{
|
||||||
|
if( !tracking ) owner.addObserver( this );
|
||||||
|
tracking = true;
|
||||||
|
|
||||||
|
timings.clear();
|
||||||
|
timingLookup.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean stop()
|
||||||
|
{
|
||||||
|
if( !tracking ) return false;
|
||||||
|
|
||||||
|
owner.removeObserver( this );
|
||||||
|
tracking = false;
|
||||||
|
timingLookup.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized List<ComputerMetrics> getSnapshot()
|
||||||
|
{
|
||||||
|
ArrayList<ComputerMetrics> timings = new ArrayList<>( this.timings.size() );
|
||||||
|
for( ComputerMetrics timing : this.timings ) timings.add( new ComputerMetrics( timing ) );
|
||||||
|
return timings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized List<ComputerMetrics> getTimings()
|
||||||
|
{
|
||||||
|
return new ArrayList<>( timings );
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComputerMetrics getMetrics( ServerComputer computer )
|
||||||
|
{
|
||||||
|
ComputerMetrics existing = timingLookup.get( computer );
|
||||||
|
if( existing != null ) return existing;
|
||||||
|
|
||||||
|
ComputerMetrics metrics = new ComputerMetrics( computer );
|
||||||
|
timingLookup.put( computer, metrics );
|
||||||
|
timings.add( metrics );
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void observe( ServerComputer computer, Metric.Counter counter )
|
||||||
|
{
|
||||||
|
getMetrics( computer ).observe( counter );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void observe( ServerComputer computer, Metric.Event event, long value )
|
||||||
|
{
|
||||||
|
getMetrics( computer ).observe( event, value );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.shared.computer.metrics.basic;
|
||||||
|
|
||||||
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metrics for an individual computer.
|
||||||
|
*/
|
||||||
|
public final class ComputerMetrics
|
||||||
|
{
|
||||||
|
private static final int DEFAULT_LEN = 16;
|
||||||
|
|
||||||
|
private final WeakReference<ServerComputer> computer;
|
||||||
|
private final int computerId;
|
||||||
|
private long[] counts;
|
||||||
|
private long[] totals;
|
||||||
|
private long[] max;
|
||||||
|
|
||||||
|
ComputerMetrics( ServerComputer computer )
|
||||||
|
{
|
||||||
|
this.computer = new WeakReference<>( computer );
|
||||||
|
computerId = computer.getID();
|
||||||
|
counts = new long[DEFAULT_LEN];
|
||||||
|
totals = new long[DEFAULT_LEN];
|
||||||
|
max = new long[DEFAULT_LEN];
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputerMetrics( ComputerMetrics other )
|
||||||
|
{
|
||||||
|
computer = other.computer;
|
||||||
|
computerId = other.computerId;
|
||||||
|
counts = Arrays.copyOf( other.counts, other.counts.length );
|
||||||
|
totals = Arrays.copyOf( other.totals, other.totals.length );
|
||||||
|
max = Arrays.copyOf( other.max, other.max.length );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public ServerComputer computer()
|
||||||
|
{
|
||||||
|
return computer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int computerId()
|
||||||
|
{
|
||||||
|
return computerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long get( long[] values, Metric metric )
|
||||||
|
{
|
||||||
|
return metric.id() >= values.length ? 0 : values[metric.id()];
|
||||||
|
}
|
||||||
|
|
||||||
|
private long avg( long total, long count )
|
||||||
|
{
|
||||||
|
return count == 0 ? 0 : total / count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long get( Metric metric, Aggregate aggregate )
|
||||||
|
{
|
||||||
|
if( metric instanceof Metric.Counter ) return get( counts, metric );
|
||||||
|
if( metric instanceof Metric.Event )
|
||||||
|
{
|
||||||
|
switch( aggregate )
|
||||||
|
{
|
||||||
|
case NONE:
|
||||||
|
return get( totals, metric );
|
||||||
|
case COUNT:
|
||||||
|
return get( counts, metric );
|
||||||
|
case AVG:
|
||||||
|
return avg( get( totals, metric ), get( counts, metric ) );
|
||||||
|
case MAX:
|
||||||
|
return get( max, metric );
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException( "Unknown metric " + metric.name() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFormatted( Metric field, Aggregate aggregate )
|
||||||
|
{
|
||||||
|
long value = get( field, aggregate );
|
||||||
|
switch( aggregate )
|
||||||
|
{
|
||||||
|
case COUNT:
|
||||||
|
return Metric.formatDefault( value );
|
||||||
|
case AVG:
|
||||||
|
case MAX:
|
||||||
|
case NONE:
|
||||||
|
return field.format( value );
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureCapacity( Metric metric )
|
||||||
|
{
|
||||||
|
if( metric.id() < counts.length ) return;
|
||||||
|
|
||||||
|
int newCapacity = Math.max( metric.id(), counts.length * 2 );
|
||||||
|
counts = Arrays.copyOf( counts, newCapacity );
|
||||||
|
totals = Arrays.copyOf( totals, newCapacity );
|
||||||
|
max = Arrays.copyOf( max, newCapacity );
|
||||||
|
}
|
||||||
|
|
||||||
|
void observe( Metric.Counter counter )
|
||||||
|
{
|
||||||
|
ensureCapacity( counter );
|
||||||
|
counts[counter.id()]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void observe( Metric.Event event, long value )
|
||||||
|
{
|
||||||
|
ensureCapacity( event );
|
||||||
|
counts[event.id()]++;
|
||||||
|
totals[event.id()] += value;
|
||||||
|
if( value > max[event.id()] ) max[event.id()] = value;
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ import dan200.computercraft.api.turtle.TurtleSide;
|
|||||||
import dan200.computercraft.api.turtle.event.TurtleActionEvent;
|
import dan200.computercraft.api.turtle.event.TurtleActionEvent;
|
||||||
import dan200.computercraft.api.turtle.event.TurtleInspectItemEvent;
|
import dan200.computercraft.api.turtle.event.TurtleInspectItemEvent;
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
|
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
|
||||||
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
|
import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods;
|
||||||
import dan200.computercraft.shared.turtle.core.*;
|
import dan200.computercraft.shared.turtle.core.*;
|
||||||
@ -93,7 +93,7 @@ public class TurtleAPI implements ILuaAPI
|
|||||||
|
|
||||||
private MethodResult trackCommand( ITurtleCommand command )
|
private MethodResult trackCommand( ITurtleCommand command )
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
environment.observe( Metrics.TURTLE_OPS );
|
||||||
return turtle.executeCommand( command );
|
return turtle.executeCommand( command );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ public class TurtleAPI implements ILuaAPI
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final MethodResult dig( Optional<TurtleSide> side )
|
public final MethodResult dig( Optional<TurtleSide> side )
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
environment.observe( Metrics.TURTLE_OPS );
|
||||||
return trackCommand( TurtleToolCommand.dig( InteractDirection.FORWARD, side.orElse( null ) ) );
|
return trackCommand( TurtleToolCommand.dig( InteractDirection.FORWARD, side.orElse( null ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +207,7 @@ public class TurtleAPI implements ILuaAPI
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final MethodResult digUp( Optional<TurtleSide> side )
|
public final MethodResult digUp( Optional<TurtleSide> side )
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
environment.observe( Metrics.TURTLE_OPS );
|
||||||
return trackCommand( TurtleToolCommand.dig( InteractDirection.UP, side.orElse( null ) ) );
|
return trackCommand( TurtleToolCommand.dig( InteractDirection.UP, side.orElse( null ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +223,7 @@ public class TurtleAPI implements ILuaAPI
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final MethodResult digDown( Optional<TurtleSide> side )
|
public final MethodResult digDown( Optional<TurtleSide> side )
|
||||||
{
|
{
|
||||||
environment.addTrackingChange( TrackingField.TURTLE_OPS );
|
environment.observe( Metrics.TURTLE_OPS );
|
||||||
return trackCommand( TurtleToolCommand.dig( InteractDirection.DOWN, side.orElse( null ) ) );
|
return trackCommand( TurtleToolCommand.dig( InteractDirection.DOWN, side.orElse( null ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,12 +92,8 @@
|
|||||||
"argument.computercraft.computer.many_matching": "Multiple computers matching '%s' (instances %s)",
|
"argument.computercraft.computer.many_matching": "Multiple computers matching '%s' (instances %s)",
|
||||||
"argument.computercraft.tracking_field.no_field": "Unknown field '%s'",
|
"argument.computercraft.tracking_field.no_field": "Unknown field '%s'",
|
||||||
"argument.computercraft.argument_expected": "Argument expected",
|
"argument.computercraft.argument_expected": "Argument expected",
|
||||||
"tracking_field.computercraft.tasks.name": "Tasks",
|
"tracking_field.computercraft.computer_tasks.name": "Tasks",
|
||||||
"tracking_field.computercraft.total.name": "Total time",
|
"tracking_field.computercraft.server_tasks.name": "Server tasks",
|
||||||
"tracking_field.computercraft.average.name": "Average time",
|
|
||||||
"tracking_field.computercraft.max.name": "Max time",
|
|
||||||
"tracking_field.computercraft.server_count.name": "Server task count",
|
|
||||||
"tracking_field.computercraft.server_time.name": "Server task time",
|
|
||||||
"tracking_field.computercraft.peripheral.name": "Peripheral calls",
|
"tracking_field.computercraft.peripheral.name": "Peripheral calls",
|
||||||
"tracking_field.computercraft.fs.name": "Filesystem operations",
|
"tracking_field.computercraft.fs.name": "Filesystem operations",
|
||||||
"tracking_field.computercraft.turtle.name": "Turtle operations",
|
"tracking_field.computercraft.turtle.name": "Turtle operations",
|
||||||
@ -108,6 +104,9 @@
|
|||||||
"tracking_field.computercraft.websocket_outgoing.name": "Websocket outgoing",
|
"tracking_field.computercraft.websocket_outgoing.name": "Websocket outgoing",
|
||||||
"tracking_field.computercraft.coroutines_created.name": "Coroutines created",
|
"tracking_field.computercraft.coroutines_created.name": "Coroutines created",
|
||||||
"tracking_field.computercraft.coroutines_dead.name": "Coroutines disposed",
|
"tracking_field.computercraft.coroutines_dead.name": "Coroutines disposed",
|
||||||
|
"tracking_field.computercraft.max": "%s (max)",
|
||||||
|
"tracking_field.computercraft.avg": "%s (avg)",
|
||||||
|
"tracking_field.computercraft.count": "%s (count)",
|
||||||
"gui.computercraft.tooltip.copy": "Copy to clipboard",
|
"gui.computercraft.tooltip.copy": "Copy to clipboard",
|
||||||
"gui.computercraft.tooltip.computer_id": "Computer ID: %s",
|
"gui.computercraft.tooltip.computer_id": "Computer ID: %s",
|
||||||
"gui.computercraft.tooltip.disk_id": "Disk ID: %s",
|
"gui.computercraft.tooltip.disk_id": "Disk ID: %s",
|
||||||
|
@ -10,8 +10,8 @@ import dan200.computercraft.core.computer.ComputerEnvironment
|
|||||||
import dan200.computercraft.core.computer.ComputerSide
|
import dan200.computercraft.core.computer.ComputerSide
|
||||||
import dan200.computercraft.core.computer.GlobalEnvironment
|
import dan200.computercraft.core.computer.GlobalEnvironment
|
||||||
import dan200.computercraft.core.filesystem.FileSystem
|
import dan200.computercraft.core.filesystem.FileSystem
|
||||||
|
import dan200.computercraft.core.metrics.Metric
|
||||||
import dan200.computercraft.core.terminal.Terminal
|
import dan200.computercraft.core.terminal.Terminal
|
||||||
import dan200.computercraft.core.tracking.TrackingField
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
@ -43,7 +43,8 @@ abstract class NullApiEnvironment : IAPIEnvironment {
|
|||||||
override fun setLabel(label: String?) {}
|
override fun setLabel(label: String?) {}
|
||||||
override fun startTimer(ticks: Long): Int = 0
|
override fun startTimer(ticks: Long): Int = 0
|
||||||
override fun cancelTimer(id: Int) {}
|
override fun cancelTimer(id: Int) {}
|
||||||
override fun addTrackingChange(field: TrackingField, change: Long) {}
|
override fun observe(field: Metric.Counter) {}
|
||||||
|
override fun observe(field: Metric.Event, change: Long) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EventResult(val name: String, val args: Array<Any?>)
|
class EventResult(val name: String, val args: Array<Any?>)
|
||||||
|
@ -11,6 +11,8 @@ import dan200.computercraft.api.filesystem.IWritableMount;
|
|||||||
import dan200.computercraft.core.filesystem.FileMount;
|
import dan200.computercraft.core.filesystem.FileMount;
|
||||||
import dan200.computercraft.core.filesystem.JarMount;
|
import dan200.computercraft.core.filesystem.JarMount;
|
||||||
import dan200.computercraft.core.filesystem.MemoryMount;
|
import dan200.computercraft.core.filesystem.MemoryMount;
|
||||||
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -24,7 +26,7 @@ import java.net.URL;
|
|||||||
/**
|
/**
|
||||||
* A very basic environment.
|
* A very basic environment.
|
||||||
*/
|
*/
|
||||||
public class BasicEnvironment implements ComputerEnvironment, GlobalEnvironment
|
public class BasicEnvironment implements ComputerEnvironment, GlobalEnvironment, MetricsObserver
|
||||||
{
|
{
|
||||||
private final IWritableMount mount;
|
private final IWritableMount mount;
|
||||||
|
|
||||||
@ -56,6 +58,12 @@ public class BasicEnvironment implements ComputerEnvironment, GlobalEnvironment
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MetricsObserver getMetrics()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String getHostString()
|
public String getHostString()
|
||||||
@ -144,4 +152,14 @@ public class BasicEnvironment implements ComputerEnvironment, GlobalEnvironment
|
|||||||
return new File( url.getPath() );
|
return new File( url.getPath() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void observe( Metric.Counter counter )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void observe( Metric.Event event, long value )
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates "fake" computers, which just run user-defined tasks rather than Lua code.
|
* Creates "fake" computers, which just run user-defined tasks rather than Lua code.
|
||||||
*
|
* <p>
|
||||||
* Note, this will clobber some parts of the global state. It's recommended you use this inside an {@link IsolatedRunner}.
|
* Note, this will clobber some parts of the global state. It's recommended you use this inside an {@link IsolatedRunner}.
|
||||||
*/
|
*/
|
||||||
public class FakeComputerManager
|
public class FakeComputerManager
|
||||||
@ -43,7 +43,7 @@ public class FakeComputerManager
|
|||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
ComputerExecutor.luaFactory = ( computer, timeout ) -> new DummyLuaMachine( timeout, machines.get( computer ) );
|
ComputerExecutor.luaFactory = args -> new DummyLuaMachine( args.timeout );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,7 +57,9 @@ public class FakeComputerManager
|
|||||||
{
|
{
|
||||||
BasicEnvironment environment = new BasicEnvironment();
|
BasicEnvironment environment = new BasicEnvironment();
|
||||||
Computer computer = new Computer( environment, environment, new Terminal( 51, 19, true ), 0 );
|
Computer computer = new Computer( environment, environment, new Terminal( 51, 19, true ), 0 );
|
||||||
machines.put( computer, new ConcurrentLinkedQueue<>() );
|
ConcurrentLinkedQueue<Task> tasks = new ConcurrentLinkedQueue<>();
|
||||||
|
computer.addApi( new QueuePassingAPI( tasks ) );
|
||||||
|
machines.put( computer, tasks );
|
||||||
return computer;
|
return computer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,20 +160,36 @@ public class FakeComputerManager
|
|||||||
throw (T) e;
|
throw (T) e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class QueuePassingAPI implements ILuaAPI
|
||||||
|
{
|
||||||
|
final Queue<Task> tasks;
|
||||||
|
|
||||||
|
private QueuePassingAPI( Queue<Task> tasks )
|
||||||
|
{
|
||||||
|
this.tasks = tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getNames()
|
||||||
|
{
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class DummyLuaMachine implements ILuaMachine
|
private static class DummyLuaMachine implements ILuaMachine
|
||||||
{
|
{
|
||||||
private final TimeoutState state;
|
private final TimeoutState state;
|
||||||
private final Queue<Task> handleEvent;
|
private @javax.annotation.Nullable Queue<Task> tasks;
|
||||||
|
|
||||||
DummyLuaMachine( TimeoutState state, Queue<Task> handleEvent )
|
DummyLuaMachine( TimeoutState state )
|
||||||
{
|
{
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.handleEvent = handleEvent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAPI( @Nonnull ILuaAPI api )
|
public void addAPI( @Nonnull ILuaAPI api )
|
||||||
{
|
{
|
||||||
|
if( api instanceof QueuePassingAPI ) tasks = ((QueuePassingAPI) api).tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -185,7 +203,8 @@ public class FakeComputerManager
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return handleEvent.remove().run( state );
|
if( tasks == null ) throw new IllegalStateException( "Not received tasks yet" );
|
||||||
|
return tasks.remove().run( state );
|
||||||
}
|
}
|
||||||
catch( Throwable e )
|
catch( Throwable e )
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user