mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-13 11:40:29 +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
|
||||
{
|
||||
String TIMER_EVENT = "timer";
|
||||
|
||||
@FunctionalInterface
|
||||
interface IPeripheralChangeListener
|
||||
{
|
||||
@ -64,6 +66,10 @@ public interface IAPIEnvironment
|
||||
|
||||
void setLabel( @Nullable String label );
|
||||
|
||||
int startTimer( long ticks );
|
||||
|
||||
void cancelTimer( int id );
|
||||
|
||||
void addTrackingChange( @Nonnull TrackingField field, long change );
|
||||
|
||||
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.LuaException;
|
||||
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 java.time.Instant;
|
||||
@ -24,24 +26,12 @@ public class OSAPI implements ILuaAPI
|
||||
{
|
||||
private IAPIEnvironment m_apiEnvironment;
|
||||
|
||||
private final Map<Integer, Timer> m_timers;
|
||||
private final Map<Integer, Alarm> m_alarms;
|
||||
private final Int2ObjectMap<Alarm> m_alarms = new Int2ObjectOpenHashMap<>();
|
||||
private int m_clock;
|
||||
private double m_time;
|
||||
private int m_day;
|
||||
|
||||
private int m_nextTimerToken;
|
||||
private int m_nextAlarmToken;
|
||||
|
||||
private static class Timer
|
||||
{
|
||||
int m_ticksLeft;
|
||||
|
||||
Timer( int ticksLeft )
|
||||
{
|
||||
m_ticksLeft = ticksLeft;
|
||||
}
|
||||
}
|
||||
private int m_nextAlarmToken = 0;
|
||||
|
||||
private static class Alarm implements Comparable<Alarm>
|
||||
{
|
||||
@ -66,10 +56,6 @@ public class OSAPI implements ILuaAPI
|
||||
public OSAPI( IAPIEnvironment environment )
|
||||
{
|
||||
m_apiEnvironment = environment;
|
||||
m_nextTimerToken = 0;
|
||||
m_nextAlarmToken = 0;
|
||||
m_timers = new HashMap<>();
|
||||
m_alarms = new HashMap<>();
|
||||
}
|
||||
|
||||
// ILuaAPI implementation
|
||||
@ -87,11 +73,6 @@ public class OSAPI implements ILuaAPI
|
||||
m_day = m_apiEnvironment.getComputerEnvironment().getDay();
|
||||
m_clock = 0;
|
||||
|
||||
synchronized( m_timers )
|
||||
{
|
||||
m_timers.clear();
|
||||
}
|
||||
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
m_alarms.clear();
|
||||
@ -101,26 +82,7 @@ public class OSAPI implements ILuaAPI
|
||||
@Override
|
||||
public void update()
|
||||
{
|
||||
synchronized( m_timers )
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
m_clock++;
|
||||
|
||||
// Wait for all of our alarms
|
||||
synchronized( m_alarms )
|
||||
@ -155,11 +117,6 @@ public class OSAPI implements ILuaAPI
|
||||
@Override
|
||||
public void shutdown()
|
||||
{
|
||||
synchronized( m_timers )
|
||||
{
|
||||
m_timers.clear();
|
||||
}
|
||||
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
m_alarms.clear();
|
||||
@ -229,11 +186,8 @@ public class OSAPI implements ILuaAPI
|
||||
{
|
||||
// startTimer
|
||||
double timer = getFiniteDouble( args, 0 );
|
||||
synchronized( m_timers )
|
||||
{
|
||||
m_timers.put( m_nextTimerToken, new Timer( (int) Math.round( timer / 0.05 ) ) );
|
||||
return new Object[] { m_nextTimerToken++ };
|
||||
}
|
||||
int id = m_apiEnvironment.startTimer( Math.round( timer / 0.05 ) );
|
||||
return new Object[] { id };
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
@ -278,10 +232,7 @@ public class OSAPI implements ILuaAPI
|
||||
return null;
|
||||
}
|
||||
case 10: // clock
|
||||
synchronized( m_timers )
|
||||
{
|
||||
return new Object[] { m_clock * 0.05 };
|
||||
}
|
||||
return new Object[] { m_clock * 0.05 };
|
||||
case 11:
|
||||
{
|
||||
// time
|
||||
@ -345,10 +296,7 @@ public class OSAPI implements ILuaAPI
|
||||
{
|
||||
// cancelTimer
|
||||
int token = getInt( args, 0 );
|
||||
synchronized( m_timers )
|
||||
{
|
||||
m_timers.remove( token );
|
||||
}
|
||||
m_apiEnvironment.cancelTimer( token );
|
||||
return null;
|
||||
}
|
||||
case 14:
|
||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.core.apis.http.websocket;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.ArgumentHelper;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaObject;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
@ -23,6 +24,7 @@ import java.io.Closeable;
|
||||
import java.util.Arrays;
|
||||
|
||||
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.MESSAGE_EVENT;
|
||||
|
||||
@ -53,7 +55,21 @@ public class WebsocketHandle implements ILuaObject, Closeable
|
||||
switch( method )
|
||||
{
|
||||
case 0: // receive
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 1: // send
|
||||
{
|
||||
|
@ -183,7 +183,7 @@ public class Computer
|
||||
executor.tick();
|
||||
|
||||
// Update the environment's internal state.
|
||||
internalEnvironment.update();
|
||||
internalEnvironment.tick();
|
||||
|
||||
// Propagate the environment's output to the world.
|
||||
if( internalEnvironment.updateOutput() ) externalOutputChanged.set( true );
|
||||
|
@ -435,6 +435,7 @@ final class ComputerExecutor
|
||||
}
|
||||
|
||||
// Init APIs
|
||||
computer.getEnvironment().reset();
|
||||
for( ILuaAPI api : apis ) api.startup();
|
||||
|
||||
// Init lua
|
||||
@ -478,6 +479,7 @@ final class ComputerExecutor
|
||||
|
||||
// Shutdown our APIs
|
||||
for( ILuaAPI api : apis ) api.shutdown();
|
||||
computer.getEnvironment().reset();
|
||||
|
||||
// Unload filesystem
|
||||
if( fileSystem != null )
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||
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.tracking.Tracking;
|
||||
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 java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* 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 IPeripheralChangeListener peripheralListener = null;
|
||||
|
||||
private final Int2ObjectMap<Timer> timers = new Int2ObjectOpenHashMap<>();
|
||||
private int nextTimerToken = 0;
|
||||
|
||||
Environment( 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 )
|
||||
{
|
||||
inputChanged = false;
|
||||
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 );
|
||||
}
|
||||
|
||||
@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
|
||||
public void addTrackingChange( @Nonnull TrackingField field, long 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