After talking to kovarex about a crash problem through PMs. He mentioned that some of the utilities I've develped for MoLogicCore included in MoMods. Should be part of the base game for performance reasons. I agree that some of the utilities should be part of the modding interface.
Timer System.
The timer system should be a easy plug in play system when working with time related functions. (You tell it to wait 5 seconds, it waits 5 seconds of game-time.)
Creates a new Timer Event.
-Name: The name of the timer event we are creating, this allows us to identify timers after they are created. If a timer of the name already exists, it is overwritten by the new one.
-Length: How long the timer event waits before it calls its function.
-Repeat: How many times a timer event runs before its removed. Numbers less then or equal to 0 cause the timer to run forever.
-CallBack: The function the timer calls every time it runs.
-Data: Extra Data that is passed to the function when the timer runs.
Returns all existing timers. (Could be limited to a per mod basis.)
Entity Subscriptions.
Entity subscriptions are essentially a addition to the current event system. The idea is instead of subscribing to all the events that can happen to a entity, you just subscribe to the entity itself. So when a event is called related to the subscribed entity it calls the function you defined. And when i say entity, I mean the entity type itself. Not a singular entity withen the game world.
Creates a Subscription to the inputted entity.
-Ent:The name of the entity we are subscribing to.
-Name: The name of the subscription. For later identification.
-Func: The function that is called for all the events relating to the subscribed entity.
function(Ent,Event)
-Ent: The entity that trigged the event.
-Event: The type of event that was triggered.
Re: More Utilities
Posted: Wed Mar 19, 2014 6:32 pm
by rk84
I would like to have timer system. I also made one myself, not as neat looking as yours, I used sorted table to only check timer(s) with lowest tick.
Re: More Utilities
Posted: Wed Mar 19, 2014 6:46 pm
by FreeER
Useful. A couple proposed suggestions/changes (that may or may not be better, up to other modders and the devs).
Timer.CreateTimer()
First, I propose that it uses (or is overloaded to use) a table instead of separate parameters. I suggest this because many of the other functions provided by Factorio take tables rather than a parameters list. (also, during code refactoring I might suggest making all functions conform to using a table (or other chosen standard), if they take more than one parameter. This could be discussed elsewhere however )
Name: couldn't Factorio instead generate and return a timer object so a creation would look like myTimer=game.timers.createTimer(Length,Repeat,CallBack,Data) and you could then use myTimer.deleteTimer() and myTimer.timeLeft()
Length: first, I think 'duration' might be a better name...but more importantly is whether this should be specified in seconds or ticks (I would think ticks)
Repeat: perhaps instead of 0 and less being infinite repeat, negative (less than 0) would be infinite repeat and a repeat of 0 would be a 'run once and remove' timer. A value of 1 would repeat 1 time (so it'd run twice in total) and 2 would repeat twice, etc.
Repeat2: would it be possible to change the duration after the function is run (or from within it)? So that you could create a timer (at game start) that ran it's function after an hour, and then it's duration decreases to half an hour (and then to 20 minutes, and then 10, etc. and perhaps it could be killed (from the passed function) after running with a duration of 1 minute..)
Data: more of a question (for clarification) than a suggestion but, how would this work? I assume it would update the data before passing it to the function (or use a pointer/reference to the data), otherwise if you had a timer that sent x number of biters at the player every 20 minutes and the x was data being passed to the function you would have to remove the timer and recreate it to send a larger group of biters.
Timers.GetTimers(): with the change to the name, IF this only returned the mod's timers, would it even be needed?
Entity Subscriptions
Name: Factorio could return a generated ID (it would probably have to have one anyways), pretty much like it does for custom events now...Honestly I'm just not sure why you'd 'need' to name it Especially since it's based on entity types and not specific entities.
Re: More Utilities
Posted: Wed Mar 19, 2014 11:23 pm
by ludsoe
FreeER wrote:
Snip
Useful. A couple proposed suggestions/changes (that may or may not be better, up to other modders and the devs).
Timer.CreateTimer()
First, I propose that it uses (or is overloaded to use) a table instead of separate parameters. I suggest this because many of the other functions provided by Factorio take tables rather than a parameters list. (also, during code refactoring I might suggest making all functions conform to using a table (or other chosen standard), if they take more than one parameter. This could be discussed elsewhere however )
Name: couldn't Factorio instead generate and return a timer object so a creation would look like myTimer=game.timers.createTimer(Length,Repeat,CallBack,Data) and you could then use myTimer.deleteTimer() and myTimer.timeLeft()
Length: first, I think 'duration' might be a better name...but more importantly is whether this should be specified in seconds or ticks (I would think ticks)
Repeat: perhaps instead of 0 and less being infinite repeat, negative (less than 0) would be infinite repeat and a repeat of 0 would be a 'run once and remove' timer. A value of 1 would repeat 1 time (so it'd run twice in total) and 2 would repeat twice, etc.
Repeat2: would it be possible to change the duration after the function is run (or from within it)? So that you could create a timer (at game start) that ran it's function after an hour, and then it's duration decreases to half an hour (and then to 20 minutes, and then 10, etc. and perhaps it could be killed (from the passed function) after running with a duration of 1 minute..)
Data: more of a question (for clarification) than a suggestion but, how would this work? I assume it would update the data before passing it to the function (or use a pointer/reference to the data), otherwise if you had a timer that sent x number of biters at the player every 20 minutes and the x was data being passed to the function you would have to remove the timer and recreate it to send a larger group of biters.
Timers.GetTimers(): with the change to the name, IF this only returned the mod's timers, would it even be needed?
Entity Subscriptions
Name: Factorio could return a generated ID (it would probably have to have one anyways), pretty much like it does for custom events now...Honestly I'm just not sure why you'd 'need' to name it Especially since it's based on entity types and not specific entities.
I guess using names isn't required, but its how I initially thought of the system. One of the reasons you want to name a timer is for simplicity sake, you'd just have to create the timer and you where it is. But that I imagine returning a timer object as-well as keeping the name system can be useful.
Regarding Timer duration("Length") I guess it can be measured in game ticks for exact tuning, but initially i was thinking it would take seconds. As for changing the duration of a running timer every-time it triggers, I imagine we can have another function that's called every time the timer renews. That returns the duration of the new timer. As for the extra data that's passed to the function I was picturing it would be sent in its purest form.
Re: More Utilities
Posted: Mon Mar 31, 2014 4:48 am
by Copern
I'll throw in my two cents and a different timer.
Like FreeER mentioned, "name" I really don't think is necessary. You'll likely get a reference and I can't think of a reason why you would need to "name" it. Again as FreeER mentioned, "duration" makes more sense as a variable name for the timer as when you see "length" you would think length of an array/list or other dimensional object. "Repeat" and "data" isn't actually needed either to be honest.
All we really need is two variables. Now instead of calling the time it runs for "length" or "duration", let's say it's the time between callback calls and name it "interval." We just need to pass in an interval and an action/callback. We can perform whatever actions we want in the callback, including changing the interval of the timer. The timer can even be available for GC after the timer runs out without further action from the user if no references are kept to it; there's no need to manually remove it. So how is that done? Well here's the timer.
local Timer = {}
-- Creates a new Timer that acts in intervals
-- game: the main game object
-- timers: the table where timers are stored
-- interval: the time in ticks between actions
-- action: the ontick callback function
function Timer:new(game, timers, interval, action)
if not action or type(action) ~= "function" then
error("Timer action for ontick must be a function.")
end
local instance = {}
local ticksleft
local running = false
local next = 0
instance.ontick = action
instance.interval = interval
function instance:getnext()
return next
end
function instance:isrunning()
return running
end
-- Starts the timer from the beginning or from the time left if restarted without resetting
function instance:start()
if running or interval <= 0 then return end
next = game.tick + (ticksleft or interval)
ticksleft = nil
running = true
timers[self] = self
end
-- Stops the timer and stores the ticks left
function instance:stop()
if not running then return end
ticksleft = next - game.tick
running = false
timers[self] = nil
end
-- Resets the timer
function instance:reset()
if running then
next = game.tick + interval
else
ticksleft = nil
end
end
return instance
end
return Timer
There are two other variables passed in to the timer creation (game and timers) but they can easily be removed, they are there for unit testing which I'll exclude for brevity. But let's keep those two there and assume we have this somewhere else.
local Timer = require "lib/timer"
local timers = {}
game.onevent(defines.events.ontick, function(event)
for _, timer in pairs(timers) do
if timer.ontick and timer.interval > 0 then
if game.tick >= timer:getnext() then
timer:ontick()
timer:reset()
end
else
timer:stop()
end
end
end)
local createtimer = function(interval, action)
return Timer:new(game, timers, interval, action)
end
-- A timer that prints a line to the player and kills itself after 400 ticks
createtimer(400, function(self)
game.player.print("Ran for 400 ticks")
self:stop()
end):start()
-- A timer that prints a line every 1000 ticks and is stored somewhere to stop manually
local sometimer = createtimer(1000, function(self)
game.player.print("Printing a line every 1000 ticks")
end)
sometimer:start()
-- A while later we want to stop it and start it again later from a fresh start
sometimer:stop()
sometimer:reset()
-- Another while later we start it up again
sometimer:start()
-- More time passes and we stop it again but want to keep the time remaining
sometimer:stop()
-- Even more time passes and we start it again from the time it had left
sometimer:start()
-- Timer starts with an interval of 500 ticks and decreases the interval by 50 ticks each action
-- until it dies when the interval hits 0
createtimer(500, function(self)
game.player.print("This timer ran for " .. self.interval)
self.interval = self.interval - 50
end):start()
-- A timer that acts as a delay for another timer that continuously modifies local data every 500 ticks
local somedata = 0
local delayedtimer
createtimer(1000, function(self)
game.player.print("Delayed for 1000 ticks")
delayedtimer = createtimer(500, function(self)
game.player.print("Some data is " .. somedata)
somedata = somedata + 1
end)
delayedtimer:start()
self:stop()
end):start()
-- Time passes and something wants to stop and remove the delayed timer...
if delayedtimer then
delayedtimer:stop()
delayedtimer = nil
end
This is how I prefer a Timer work at least. You're of course free to use this in your own projects if you wish, all I ask is a little credit. It would be nice to see something like this built into the game of course.
Re: More Utilities
Posted: Mon Mar 31, 2014 6:09 am
by ssilk
This is more ore less also the preferred method how it's done in other programming languages.