/* * This file is part of ComputerCraft - http://www.computercraft.info * Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ package dan200.computercraft.core.apis; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.shared.util.StringUtil; import javax.annotation.Nonnull; import java.util.*; public class OSAPI implements ILuaAPI { private IAPIEnvironment m_apiEnvironment; private final Map m_timers; private final Map m_alarms; private int m_clock; private double m_time; private int m_day; private int m_nextTimerToken; private int m_nextAlarmToken; private static class Timer { public int m_ticksLeft; public Timer( int ticksLeft ) { m_ticksLeft = ticksLeft; } } private class Alarm implements Comparable { public final double m_time; public final int m_day; public Alarm( double time, int day ) { m_time = time; m_day = day; } @Override public int compareTo( @Nonnull Alarm o ) { double t = (double)m_day * 24.0 + m_time; double ot = (double)m_day * 24.0 + m_time; if( t < ot ) { return -1; } else if( t > ot ) { return 1; } else { return 0; } } } public OSAPI( IAPIEnvironment environment ) { m_apiEnvironment = environment; m_nextTimerToken = 0; m_nextAlarmToken = 0; m_timers = new HashMap(); m_alarms = new HashMap(); } // ILuaAPI implementation @Override public String[] getNames() { return new String[] { "os" }; } @Override public void startup() { m_time = m_apiEnvironment.getComputerEnvironment().getTimeOfDay(); m_day = m_apiEnvironment.getComputerEnvironment().getDay(); m_clock = 0; synchronized( m_timers ) { m_timers.clear(); } synchronized( m_alarms ) { m_alarms.clear(); } } @Override public void advance( double dt ) { synchronized( m_timers ) { // Update the clock m_clock++; // Countdown all of our active timers Iterator> it = m_timers.entrySet().iterator(); while( it.hasNext() ) { Map.Entry entry = it.next(); Timer timer = entry.getValue(); timer.m_ticksLeft = timer.m_ticksLeft - 1; if( timer.m_ticksLeft <= 0 ) { // Queue the "timer" event queueLuaEvent( "timer", new Object[] { entry.getKey() } ); it.remove(); } } } // Wait for all of our alarms synchronized( m_alarms ) { double previousTime = m_time; int previousDay = m_day; double time = m_apiEnvironment.getComputerEnvironment().getTimeOfDay(); int day = m_apiEnvironment.getComputerEnvironment().getDay(); if( time > previousTime || day > previousDay ) { double now = (double)m_day * 24.0 + m_time; Iterator> it = m_alarms.entrySet().iterator(); while( it.hasNext() ) { Map.Entry entry = it.next(); Alarm alarm = entry.getValue(); double t = (double)alarm.m_day * 24.0 + alarm.m_time; if( now >= t ) { queueLuaEvent( "alarm", new Object[]{ entry.getKey() } ); it.remove(); } } } m_time = time; m_day = day; } } @Override public void shutdown( ) { synchronized( m_timers ) { m_timers.clear(); } synchronized( m_alarms ) { m_alarms.clear(); } } @Nonnull @Override public String[] getMethodNames() { return new String[] { "queueEvent", "startTimer", "setAlarm", "shutdown", "reboot", "computerID", "getComputerID", "setComputerLabel", "computerLabel", "getComputerLabel", "clock", "time", "day", "cancelTimer", "cancelAlarm", "epoch" }; } private float getTimeForCalendar(Calendar c) { float time = c.get(Calendar.HOUR_OF_DAY); time += (float)c.get(Calendar.MINUTE) / 60.0f; time += (float)c.get(Calendar.SECOND) / (60.0f * 60.0f); return time; } private int getDayForCalendar(Calendar c) { GregorianCalendar g = (c instanceof GregorianCalendar) ? (GregorianCalendar)c : new GregorianCalendar(); int year = c.get(Calendar.YEAR); int day = 0; for( int y=1970; y= 24.0 ) { throw new LuaException( "Number out of range" ); } synchronized( m_alarms ) { int day = (time > m_time) ? m_day : (m_day + 1); m_alarms.put( m_nextAlarmToken, new Alarm( time, day ) ); return new Object[] { m_nextAlarmToken++ }; } } case 3: { // shutdown m_apiEnvironment.shutdown(); return null; } case 4: { // reboot m_apiEnvironment.reboot(); return null; } case 5: case 6: { // computerID/getComputerID return new Object[] { getComputerID() }; } case 7: { // setComputerLabel String label = null; if( args.length > 0 && args[0] != null ) { if(!(args[0] instanceof String)) { throw new LuaException( "Expected string or nil" ); } label = StringUtil.normaliseLabel( (String) args[0] ); } m_apiEnvironment.setLabel( label ); return null; } case 8: case 9: { // computerLabel/getComputerLabel String label = m_apiEnvironment.getLabel(); if( label != null ) { return new Object[] { label }; } return null; } case 10: { // clock synchronized( m_timers ) { return new Object[] { (double)m_clock * 0.05 }; } } case 11: { // time String param = "ingame"; if (args.length > 0 && args[0] != null) { if (!(args[0] instanceof String)) { throw new LuaException("Expected string"); } param = (String)args[0]; } if (param.equals("utc")) { // Get Hour of day (UTC) Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); return new Object[] {getTimeForCalendar(c)}; } else if (param.equals("local")) { // Get Hour of day (local time) Calendar c = Calendar.getInstance(); return new Object[] {getTimeForCalendar(c)}; } else if (param.equals("ingame")) { // Get ingame hour synchronized (m_alarms) { return new Object[]{m_time}; } } else { throw new LuaException("Unsupported operation"); } } case 12: { // day String param = "ingame"; if (args.length > 0 && args[0] != null) { if (!(args[0] instanceof String)) { throw new LuaException("Expected string"); } param = (String)args[0]; } if (param.equals("utc")) { // Get numbers of days since 1970-01-01 (utc) Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); return new Object[] {getDayForCalendar(c)}; } else if (param.equals("local")) { // Get numbers of days since 1970-01-01 (local time) Calendar c = Calendar.getInstance(); return new Object[] {getDayForCalendar(c)}; } else if (param.equals("ingame")){ // Get game day synchronized (m_alarms) { return new Object[]{m_day}; } } else { throw new LuaException("Unsupported operation"); } } case 13: { // cancelTimer if( args.length < 1 || args[0] == null || !(args[0] instanceof Number) ) { throw new LuaException( "Expected number" ); } int token = ((Number)args[0]).intValue(); synchronized( m_timers ) { if( m_timers.containsKey( token ) ) { m_timers.remove( token ); } } return null; } case 14: { // cancelAlarm if( args.length < 1 || args[0] == null || !(args[0] instanceof Number) ) { throw new LuaException( "Expected number" ); } int token = ((Number)args[0]).intValue(); synchronized( m_alarms ) { if( m_alarms.containsKey( token ) ) { m_alarms.remove( token ); } } return null; } case 15: { // epoch String param = "ingame"; if (args.length > 0 && args[0] != null) { if (!(args[0] instanceof String)) { throw new LuaException("Expected string"); } param = (String)args[0]; } if (param.equals("utc")) { // Get utc epoch Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); return new Object[] {getEpochForCalendar(c)}; } else if (param.equals("local")) { // Get local epoch Calendar c = Calendar.getInstance(); return new Object[] {getEpochForCalendar(c)}; } else if (param.equals("ingame")){ // Get in-game epoch synchronized (m_alarms) { return new Object[] { m_day * 86400000 + (int)(m_time * 3600000.0f) }; } } else { throw new LuaException("Unsupported operation"); } } default: { return null; } } } // Private methods private void queueLuaEvent( String event, Object[] args ) { m_apiEnvironment.queueEvent( event, args ); } private Object[] trimArray( Object[] array, int skip ) { return Arrays.copyOfRange( array, skip, array.length ); } private int getComputerID() { return m_apiEnvironment.getComputerID(); } }