mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-05 23:10:30 +00:00
Add a timeout to websocket.receive
- Move timer handling to IAPIEnvironment, rather than OSAPI. This means the environment is reset on startup/shutdown, much like normal APIs. - Websocket.receive now accepts an optional timetout (much like rednet.receive). This starts a timer, and waits for it to complete. Closes #201
This commit is contained in:
parent
be89fc25f9
commit
79f42e35ce
@ -18,6 +18,8 @@ import javax.annotation.Nullable;
|
|||||||
|
|
||||||
public interface IAPIEnvironment
|
public interface IAPIEnvironment
|
||||||
{
|
{
|
||||||
|
String TIMER_EVENT = "timer";
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface IPeripheralChangeListener
|
interface IPeripheralChangeListener
|
||||||
{
|
{
|
||||||
@ -64,6 +66,10 @@ public interface IAPIEnvironment
|
|||||||
|
|
||||||
void setLabel( @Nullable String label );
|
void setLabel( @Nullable String label );
|
||||||
|
|
||||||
|
int startTimer( long ticks );
|
||||||
|
|
||||||
|
void cancelTimer( int id );
|
||||||
|
|
||||||
void addTrackingChange( @Nonnull TrackingField field, long change );
|
void addTrackingChange( @Nonnull TrackingField field, long change );
|
||||||
|
|
||||||
default void addTrackingChange( @Nonnull TrackingField field )
|
default void addTrackingChange( @Nonnull TrackingField field )
|
||||||
|
@ -9,6 +9,8 @@ import dan200.computercraft.api.lua.ILuaAPI;
|
|||||||
import dan200.computercraft.api.lua.ILuaContext;
|
import dan200.computercraft.api.lua.ILuaContext;
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
import dan200.computercraft.shared.util.StringUtil;
|
import dan200.computercraft.shared.util.StringUtil;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@ -24,24 +26,12 @@ public class OSAPI implements ILuaAPI
|
|||||||
{
|
{
|
||||||
private IAPIEnvironment m_apiEnvironment;
|
private IAPIEnvironment m_apiEnvironment;
|
||||||
|
|
||||||
private final Map<Integer, Timer> m_timers;
|
private final Int2ObjectMap<Alarm> m_alarms = new Int2ObjectOpenHashMap<>();
|
||||||
private final Map<Integer, Alarm> m_alarms;
|
|
||||||
private int m_clock;
|
private int m_clock;
|
||||||
private double m_time;
|
private double m_time;
|
||||||
private int m_day;
|
private int m_day;
|
||||||
|
|
||||||
private int m_nextTimerToken;
|
private int m_nextAlarmToken = 0;
|
||||||
private int m_nextAlarmToken;
|
|
||||||
|
|
||||||
private static class Timer
|
|
||||||
{
|
|
||||||
int m_ticksLeft;
|
|
||||||
|
|
||||||
Timer( int ticksLeft )
|
|
||||||
{
|
|
||||||
m_ticksLeft = ticksLeft;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Alarm implements Comparable<Alarm>
|
private static class Alarm implements Comparable<Alarm>
|
||||||
{
|
{
|
||||||
@ -66,10 +56,6 @@ public class OSAPI implements ILuaAPI
|
|||||||
public OSAPI( IAPIEnvironment environment )
|
public OSAPI( IAPIEnvironment environment )
|
||||||
{
|
{
|
||||||
m_apiEnvironment = environment;
|
m_apiEnvironment = environment;
|
||||||
m_nextTimerToken = 0;
|
|
||||||
m_nextAlarmToken = 0;
|
|
||||||
m_timers = new HashMap<>();
|
|
||||||
m_alarms = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ILuaAPI implementation
|
// ILuaAPI implementation
|
||||||
@ -87,11 +73,6 @@ public class OSAPI implements ILuaAPI
|
|||||||
m_day = m_apiEnvironment.getComputerEnvironment().getDay();
|
m_day = m_apiEnvironment.getComputerEnvironment().getDay();
|
||||||
m_clock = 0;
|
m_clock = 0;
|
||||||
|
|
||||||
synchronized( m_timers )
|
|
||||||
{
|
|
||||||
m_timers.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized( m_alarms )
|
synchronized( m_alarms )
|
||||||
{
|
{
|
||||||
m_alarms.clear();
|
m_alarms.clear();
|
||||||
@ -101,26 +82,7 @@ public class OSAPI implements ILuaAPI
|
|||||||
@Override
|
@Override
|
||||||
public void update()
|
public void update()
|
||||||
{
|
{
|
||||||
synchronized( m_timers )
|
m_clock++;
|
||||||
{
|
|
||||||
// Update the clock
|
|
||||||
m_clock++;
|
|
||||||
|
|
||||||
// Countdown all of our active timers
|
|
||||||
Iterator<Map.Entry<Integer, Timer>> it = m_timers.entrySet().iterator();
|
|
||||||
while( it.hasNext() )
|
|
||||||
{
|
|
||||||
Map.Entry<Integer, Timer> entry = it.next();
|
|
||||||
Timer timer = entry.getValue();
|
|
||||||
timer.m_ticksLeft--;
|
|
||||||
if( timer.m_ticksLeft <= 0 )
|
|
||||||
{
|
|
||||||
// Queue the "timer" event
|
|
||||||
queueLuaEvent( "timer", new Object[] { entry.getKey() } );
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for all of our alarms
|
// Wait for all of our alarms
|
||||||
synchronized( m_alarms )
|
synchronized( m_alarms )
|
||||||
@ -155,11 +117,6 @@ public class OSAPI implements ILuaAPI
|
|||||||
@Override
|
@Override
|
||||||
public void shutdown()
|
public void shutdown()
|
||||||
{
|
{
|
||||||
synchronized( m_timers )
|
|
||||||
{
|
|
||||||
m_timers.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized( m_alarms )
|
synchronized( m_alarms )
|
||||||
{
|
{
|
||||||
m_alarms.clear();
|
m_alarms.clear();
|
||||||
@ -229,11 +186,8 @@ public class OSAPI implements ILuaAPI
|
|||||||
{
|
{
|
||||||
// startTimer
|
// startTimer
|
||||||
double timer = getFiniteDouble( args, 0 );
|
double timer = getFiniteDouble( args, 0 );
|
||||||
synchronized( m_timers )
|
int id = m_apiEnvironment.startTimer( Math.round( timer / 0.05 ) );
|
||||||
{
|
return new Object[] { id };
|
||||||
m_timers.put( m_nextTimerToken, new Timer( (int) Math.round( timer / 0.05 ) ) );
|
|
||||||
return new Object[] { m_nextTimerToken++ };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
@ -278,10 +232,7 @@ public class OSAPI implements ILuaAPI
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case 10: // clock
|
case 10: // clock
|
||||||
synchronized( m_timers )
|
return new Object[] { m_clock * 0.05 };
|
||||||
{
|
|
||||||
return new Object[] { m_clock * 0.05 };
|
|
||||||
}
|
|
||||||
case 11:
|
case 11:
|
||||||
{
|
{
|
||||||
// time
|
// time
|
||||||
@ -345,10 +296,7 @@ public class OSAPI implements ILuaAPI
|
|||||||
{
|
{
|
||||||
// cancelTimer
|
// cancelTimer
|
||||||
int token = getInt( args, 0 );
|
int token = getInt( args, 0 );
|
||||||
synchronized( m_timers )
|
m_apiEnvironment.cancelTimer( token );
|
||||||
{
|
|
||||||
m_timers.remove( token );
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case 14:
|
case 14:
|
||||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.core.apis.http.websocket;
|
|||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
|
import dan200.computercraft.api.lua.ArgumentHelper;
|
||||||
import dan200.computercraft.api.lua.ILuaContext;
|
import dan200.computercraft.api.lua.ILuaContext;
|
||||||
import dan200.computercraft.api.lua.ILuaObject;
|
import dan200.computercraft.api.lua.ILuaObject;
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
@ -23,6 +24,7 @@ import java.io.Closeable;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static dan200.computercraft.api.lua.ArgumentHelper.optBoolean;
|
import static dan200.computercraft.api.lua.ArgumentHelper.optBoolean;
|
||||||
|
import static dan200.computercraft.core.apis.IAPIEnvironment.TIMER_EVENT;
|
||||||
import static dan200.computercraft.core.apis.http.websocket.Websocket.CLOSE_EVENT;
|
import static dan200.computercraft.core.apis.http.websocket.Websocket.CLOSE_EVENT;
|
||||||
import static dan200.computercraft.core.apis.http.websocket.Websocket.MESSAGE_EVENT;
|
import static dan200.computercraft.core.apis.http.websocket.Websocket.MESSAGE_EVENT;
|
||||||
|
|
||||||
@ -53,7 +55,21 @@ public class WebsocketHandle implements ILuaObject, Closeable
|
|||||||
switch( method )
|
switch( method )
|
||||||
{
|
{
|
||||||
case 0: // receive
|
case 0: // receive
|
||||||
|
{
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
int timeoutId;
|
||||||
|
if( arguments.length <= 0 || arguments[0] == null )
|
||||||
|
{
|
||||||
|
// We do this rather odd argument validation to ensure we can tell the difference between a
|
||||||
|
// negative timeout and an absent one.
|
||||||
|
timeoutId = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double timeout = ArgumentHelper.getFiniteDouble( arguments, 0 );
|
||||||
|
timeoutId = websocket.environment().startTimer( Math.round( timeout / 0.05 ) );
|
||||||
|
}
|
||||||
|
|
||||||
while( true )
|
while( true )
|
||||||
{
|
{
|
||||||
Object[] event = context.pullEvent( null );
|
Object[] event = context.pullEvent( null );
|
||||||
@ -63,9 +79,17 @@ public class WebsocketHandle implements ILuaObject, Closeable
|
|||||||
}
|
}
|
||||||
else if( event.length >= 2 && Objects.equal( event[0], CLOSE_EVENT ) && Objects.equal( event[1], websocket.address() ) && closed )
|
else if( event.length >= 2 && Objects.equal( event[0], CLOSE_EVENT ) && Objects.equal( event[1], websocket.address() ) && closed )
|
||||||
{
|
{
|
||||||
|
// If the socket is closed abort.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if( event.length >= 2 && timeoutId != -1 && Objects.equal( event[0], TIMER_EVENT )
|
||||||
|
&& event[1] instanceof Number && ((Number) event[1]).intValue() == timeoutId )
|
||||||
|
{
|
||||||
|
// If we received a matching timer event then abort.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 1: // send
|
case 1: // send
|
||||||
{
|
{
|
||||||
|
@ -183,7 +183,7 @@ public class Computer
|
|||||||
executor.tick();
|
executor.tick();
|
||||||
|
|
||||||
// Update the environment's internal state.
|
// Update the environment's internal state.
|
||||||
internalEnvironment.update();
|
internalEnvironment.tick();
|
||||||
|
|
||||||
// Propagate the environment's output to the world.
|
// Propagate the environment's output to the world.
|
||||||
if( internalEnvironment.updateOutput() ) externalOutputChanged.set( true );
|
if( internalEnvironment.updateOutput() ) externalOutputChanged.set( true );
|
||||||
|
@ -435,6 +435,7 @@ final class ComputerExecutor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init APIs
|
// Init APIs
|
||||||
|
computer.getEnvironment().reset();
|
||||||
for( ILuaAPI api : apis ) api.startup();
|
for( ILuaAPI api : apis ) api.startup();
|
||||||
|
|
||||||
// Init lua
|
// Init lua
|
||||||
@ -478,6 +479,7 @@ final class ComputerExecutor
|
|||||||
|
|
||||||
// Shutdown our APIs
|
// Shutdown our APIs
|
||||||
for( ILuaAPI api : apis ) api.shutdown();
|
for( ILuaAPI api : apis ) api.shutdown();
|
||||||
|
computer.getEnvironment().reset();
|
||||||
|
|
||||||
// Unload filesystem
|
// Unload filesystem
|
||||||
if( fileSystem != null )
|
if( fileSystem != null )
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.core.computer;
|
package dan200.computercraft.core.computer;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.ILuaAPI;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||||
@ -12,9 +13,12 @@ import dan200.computercraft.core.filesystem.FileSystem;
|
|||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.core.tracking.Tracking;
|
import dan200.computercraft.core.tracking.Tracking;
|
||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.tracking.TrackingField;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the "environment" that a {@link Computer} exists in.
|
* Represents the "environment" that a {@link Computer} exists in.
|
||||||
@ -53,6 +57,9 @@ public final class Environment implements IAPIEnvironment
|
|||||||
private final IPeripheral[] peripherals = new IPeripheral[ComputerSide.COUNT];
|
private final IPeripheral[] peripherals = new IPeripheral[ComputerSide.COUNT];
|
||||||
private IPeripheralChangeListener peripheralListener = null;
|
private IPeripheralChangeListener peripheralListener = null;
|
||||||
|
|
||||||
|
private final Int2ObjectMap<Timer> timers = new Int2ObjectOpenHashMap<>();
|
||||||
|
private int nextTimerToken = 0;
|
||||||
|
|
||||||
Environment( Computer computer )
|
Environment( Computer computer )
|
||||||
{
|
{
|
||||||
this.computer = computer;
|
this.computer = computer;
|
||||||
@ -198,17 +205,47 @@ public final class Environment implements IAPIEnvironment
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called on the main thread to update the internal state of the computer.
|
* Called when the computer starts up or shuts down, to reset any internal state.
|
||||||
*
|
*
|
||||||
* This just queues a {@code redstone} event if the input has changed.
|
* @see ILuaAPI#startup()
|
||||||
|
* @see ILuaAPI#shutdown()
|
||||||
*/
|
*/
|
||||||
void update()
|
void reset()
|
||||||
|
{
|
||||||
|
synchronized( timers )
|
||||||
|
{
|
||||||
|
timers.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on the main thread to update the internal state of the computer.
|
||||||
|
*/
|
||||||
|
void tick()
|
||||||
{
|
{
|
||||||
if( inputChanged )
|
if( inputChanged )
|
||||||
{
|
{
|
||||||
inputChanged = false;
|
inputChanged = false;
|
||||||
queueEvent( "redstone", null );
|
queueEvent( "redstone", null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized( timers )
|
||||||
|
{
|
||||||
|
// Countdown all of our active timers
|
||||||
|
Iterator<Int2ObjectMap.Entry<Timer>> it = timers.int2ObjectEntrySet().iterator();
|
||||||
|
while( it.hasNext() )
|
||||||
|
{
|
||||||
|
Int2ObjectMap.Entry<Timer> entry = it.next();
|
||||||
|
Timer timer = entry.getValue();
|
||||||
|
timer.ticksLeft--;
|
||||||
|
if( timer.ticksLeft <= 0 )
|
||||||
|
{
|
||||||
|
// Queue the "timer" event
|
||||||
|
queueEvent( TIMER_EVENT, new Object[] { entry.getIntKey() } );
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -303,9 +340,38 @@ public final class Environment implements IAPIEnvironment
|
|||||||
computer.setLabel( label );
|
computer.setLabel( label );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int startTimer( long ticks )
|
||||||
|
{
|
||||||
|
synchronized( timers )
|
||||||
|
{
|
||||||
|
timers.put( nextTimerToken, new Timer( ticks ) );
|
||||||
|
return nextTimerToken++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelTimer( int id )
|
||||||
|
{
|
||||||
|
synchronized( timers )
|
||||||
|
{
|
||||||
|
timers.remove( id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTrackingChange( @Nonnull TrackingField field, long change )
|
public void addTrackingChange( @Nonnull TrackingField field, long change )
|
||||||
{
|
{
|
||||||
Tracking.addValue( computer, field, change );
|
Tracking.addValue( computer, field, change );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class Timer
|
||||||
|
{
|
||||||
|
long ticksLeft;
|
||||||
|
|
||||||
|
Timer( long ticksLeft )
|
||||||
|
{
|
||||||
|
this.ticksLeft = ticksLeft;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user