'use strict'; /*global events, module, require, __plugin, setInterval, clearInterval, setTimeout, addUnloadHandler*/ var utils = require('utils'); /************************************************************************ ## The at Module The at module provides a single function `at()` which can be used to schedule repeating (or non-repeating) tasks to be done at a particular time. ### at() function The utils.at() function will perform a given task at a given time in the (minecraft) day. #### Parameters * time24hr : The time in 24hr form - e.g. 9:30 in the morning is '09:30' while 9:30 pm is '21:30', midnight is '00:00' and midday is '12:00' * callback : A javascript function which will be invoked at the given time. * worlds : (optional) An array of worlds. Each world has its own clock. If no array of worlds is specified, all the server's worlds are used. * repeat : (optional) true or false, default is true (repeat the task every day) #### Example To warn players when night is approaching: ```javascript var utils = require('utils'), at = require('at'); function warning(){ utils.players(function( player ) { echo( player, 'The night is dark and full of terrors!' ); }); } at('19:00', warning); ``` To run a task only once at the next given time: ```javascript var utils = require('utils'), at = require('at'); function wakeup(){ utils.players(function( player ) { echo( player, "Wake Up Folks!" ); }); } at('06:00', wakeup, null, false); ``` ***/ var SECOND = 1000; var POLLING_INTERVAL = 3 * SECOND; // this is probably precise enough function at(time24hr, callback, pWorlds, repeat) { if (arguments.length === 0){ // TODO: Document this behaviour console.log(tasksToString()); return; } var timeParts = time24hr.split( ':' ); var timeMins = (timeParts[0] * 60) + (timeParts[1] * 1); if (!pWorlds || pWorlds === undefined ) { pWorlds = utils.worlds(); } if (repeat === undefined){ repeat = true; } utils.foreach( pWorlds, function ( world ) { atAddTask( timeMins, callback, world, repeat); }); }; var atTasks = {}; function tasksToString(){ var result = ''; for (var world in atTasks){ result += 'world: ' + world +'\n'; for (var time in atTasks[world]){ var scheduledFuncs = atTasks[world][time]; for (var i = 0;i < scheduledFuncs.length; i++){ result += ' ' + time + ': ' + scheduledFuncs[i].constructor + '\n'; } } result += '(current world time: ' + utils.time24(world) + ')\n'; } return result; } /* constructs a function which will be called every x ticks to track the schedule for a given world */ function atMonitorFactory(world){ var worldName = ''+ world.name; var lastRun = null; return function atMonitorForWorld(){ var timeMins = utils.time24(world); if (timeMins === lastRun){ return; } if (lastRun === null ){ lastRun = timeMins - 1; }else { lastRun = lastRun % 1440; } var worldSchedule = atTasks[worldName]; if (!worldSchedule){ return; } while ( lastRun > timeMins ? (lastRun <= 1440) : ( lastRun < timeMins ) ){ var tasks = worldSchedule[lastRun++]; if (!tasks){ continue; } utils.foreach(tasks, function(task, i){ if (!task){ return; } setTimeout(task.callback.bind(null, timeMins, world), 1); if (!task.repeat){ tasks[i] = null; } }); } }; } function atAddTask( timeMins, callback, world, repeat){ var worldName = ''+world.name; if (!atTasks[worldName]){ atTasks[worldName] = {}; } if (!atTasks[worldName][timeMins]){ atTasks[worldName][timeMins] = []; } atTasks[worldName][timeMins].push({callback: callback, repeat: repeat}); } var atMonitors = []; function onLoadStartMonitor(event){ var monitor = setInterval( atMonitorFactory(event.world), POLLING_INTERVAL); atMonitors.push( monitor ); } if (__plugin.canary){ events.loadWorld( onLoadStartMonitor ); } if (__plugin.bukkit){ events.worldLoad( onLoadStartMonitor ); } addUnloadHandler(function(){ utils.foreach(atMonitors, function(atInterval){ clearInterval(atInterval); }); }); module.exports = at;