ID:151994
 
For my game, there are certain instances where there an event must occur after a certain period of time and it's not possible through the use of ONLY sleep() or spawn() themselves because the time counter can stop and continue based on certain conditions like so:
var/time=100
while(time>0)//waiting 10 seconds
if(!stop_time)
sleep(1)
time--
else
sleep(1)

Now, let's say I have about 20-40 of those loops running at the same time (spawn()ed of course). Will that cause massive lag? If so, how can I have a time counter that can stop and start and have a lot of them without having it cause major lag to the game?
It will definitely cause lag. A lot. To prevent this you should have one global "ticker" which handles all of these timers in a single loop. See Deadron's Event Loop library for an example.
In response to DivineO'peanut
Ok thanks! Another question: would it cause more lag to do a recursive loop with spawn() or to do a loop that does something with sleep() at the end (with the loop being spawn()ed)?
In response to Kakashi24142
It largely depends on the contents of the loop. Iterative loops are usually faster and take less memory (although this could be compenstated for using tail recursion), but recursive loops are usually much less uglier and in calculations they have the advantage of taking a top-down approach. I'm not too familair with the pros and cons of both, so you should probably read an article or two about them which can explain this better.
In response to DivineO'peanut
Ok thanks, and I took at look at Event Loop. It's purely amazing and VERY useful (and very simple). I looked at it back when I was a newbie and I didn't understand it's power. Now I do :p.
In response to Kakashi24142
It's exactly the same for me! A year ago I thought "why would he constantly run a single loop when he can use lots of small loops," and, shame on me, these small loops were the cause of 90% of the lag in my projects (okay, project).
In response to DivineO'peanut
Lol. We need someone to submit an article that explains it's genious to beginners(and to people who don't know how useful it is yet), so they don't go through the trouble we did. I'm going to have to change a lot of my source to support in since most of it is based on time, but it'll be worth it.
Kakashi24142 wrote:
For my game, there are certain instances where there an event must occur after a certain period of time and it's not possible through the use of ONLY sleep() or spawn() themselves because the time counter can stop and continue based on certain conditions like so:
> var/time=100
> while(time>0)//waiting 10 seconds
> if(!stop_time)
> sleep(1)
> time--
> else
> sleep(1)
>

Now, let's say I have about 20-40 of those loops running at the same time (spawn()ed of course). Will that cause massive lag? If so, how can I have a time counter that can stop and start and have a lot of them without having it cause major lag to the game?

Indeed it's not possible to use only sleep() or spawn() because of the need to pause the timer, but a countdown every tick or every second isn't the answer. To implement successful pausing the only critical thing to know is how much time is left on the clock. For a running timer, there should be a specific world.time value where it will elapse. For a paused timer, obviously you can't do that so it's only important to know how much time is left. Fortunately, these two pieces of information are easy to change from one state to the other.

timer
var/end
var/paused
var/fired
var/object
var/procname
var/arg1,arg2
var/isglobal

New(time, object, procname, arg1, arg2)
end = world.time + time
src.object = object
src.procname = procname
src.arg1 = arg1
src.arg2 = arg2
isglobal = !object
spawn(time) Check()

proc/Fire()
if(fired) return
fired = 1
// don't call the proc if it's for an object and the object is gone
if(object || isglobal) call(object,procname)(arg1,arg2)

proc/Check()
// if the event can no longer be called, stop checking
if(!fired && (object || isglobal))
if(!paused && end <= world.time) Fire()
else spawn(TimeLeft()) Check()

proc/Pause()
if(!fired && !paused)
paused = 1
end -= world.time // change end time to time remaining

proc/Unpause()
if(!fired && paused)
paused = 0
end += world.time

proc/TimeLeft()
if(fired) return 0
if(paused) return end
return end - world.time


This simple timer datum lets you create a new event to call a proc after a certain time has expired. The timer normally knows when it's supposed to end, but you can put it into a paused state where it only knows how much time is left on the clock. By always waiting until the right amount of time has passed, you're not using up cycles doing anything unnecessary like counting down.

As you can see the workhorse here is Check(). When the timer is first created (and automatically started), Check() is spawned off and won't be called until the timer is due to elapse. If you call Pause() during that time and then Unpause(), the end time will have moved forward to a new value, so Check() will merely be respawned for the new end time. If you Pause() but don't unpause, Check() will see that end contains the amount of time left on the clock, so it will respawn by exactly that much, and keep doing so until you Unpause().

If spawning while paused concerns you, you can make Check() stop running if it sees it's currently paused, and then have Unpause() spawn Check() again if it knows Check() is no longer running. However, this introduces a rather rare problem in that the timer is originally running its own "proc chain"; if whatever object made the timer is deleted, the timer will keep going. If you make Unpause() spawn the Check() call, you have effectively made the timer part of a different object's proc chain. Getting around that isn't too hard but it's not worth going into here; I'd just use the code as-is and avoid the problem that way.

Lummox JR
In response to Lummox JR
mob
proc
loops()
while(world)
sleep(1)
//various code

Should I change this in my game? And how should I change it?