Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Timer events in multiple states

Gregory McLeod
Registered User
Join date: 21 Oct 2006
Posts: 278
12-14-2007 11:37
I am having difficulty getting my complex script to reset when no action takes place in the main script.
I have setup a timerevent (30 seconds for testing) in the root script which when it occurs will reset the game board. I detect action in the main script through the use of llWhisper commands from sub scripts to the main script being detected by the root script as well. These are used to 'reset' the timerevent, effectively restarting the timeout. I use
llSetTimerEvent(0.0);
llSetTimerEvent(30.0);
when I detect an action.
This is complicated by needing a second state in the root script. One of the events in the root script detects that play has started and because different functions are required in the root script changes the state to 'play'. In the play state I reset the timerevent as before.
But do I need to?
I thought timer settings survive state changes mine don't seem to.
Xhawkx Holden
Registered User
Join date: 1 Nov 2006
Posts: 86
12-14-2007 12:56
They do NOT survive state changes.
What I had to do, was create a standalone timer script that would receive link
-messages whenever an action occured. This would reset the timer.

If the timer ran out, I would send a link_message to all other scripts that the time had expired.
Beverly Ultsch
Registered User
Join date: 6 Sep 2007
Posts: 229
12-14-2007 14:47
They do survive states, try the following test;

CODE

default {
state_entry()
{
llSetTimerEvent(5.0);
state test_timer;
}
}

state test_timer {

timer() {
llOwnerSay("Timer");
}

}
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
12-14-2007 15:41
timers do indeed persist across states (which can be helpful, or annoying)

but since you are changing states, do you have timer handlers in both states? when/where are you calling the set timer event from?

ideally you would only need the timer working in the 'play' state... you could start the timer in the state_entry, and stop it in the state exit (or in the timer event if that's what you're using to change states back). you could even reset the script (which would kill the timer, but also reset any globals and/or restart any initialization which you may not want)

posting the parts of the code that are relevant to the timer might help us see what is going wrong and why
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Gregory McLeod
Registered User
Join date: 21 Oct 2006
Posts: 278
12-15-2007 02:51
From: Void Singer
timers do indeed persist across states (which can be helpful, or annoying)

but since you are changing states, do you have timer handlers in both states? when/where are you calling the set timer event from?

ideally you would only need the timer working in the 'play' state... you could start the timer in the state_entry, and stop it in the state exit (or in the timer event if that's what you're using to change states back). you could even reset the script (which would kill the timer, but also reset any globals and/or restart any initialization which you may not want)

posting the parts of the code that are relevant to the timer might help us see what is going wrong and why
There is no timer event handler in the root script.

Resetting would as you say cause adverse effects.

I quote below extracts of the timer related code of the root script.
CODE

// Version and Prefix
string Version = "1.8.2.2";
integer Prefix = 'big number';
// Global Constants
float Rtime = 30; // using small value for testing // reset time
... SNIP
integer CHANNEL2109; // monitor input channel
... SNIP
// Functions
Initialise()
{
SettingUp = 0;
llSetTimerEvent(0);
... SNIP
CHANNEL2109 = Prefix + 2109;
... SNIP
} // end of Initialise function

// States
default
{
state_entry()
{
Initialise();
... SNIP
llSetTimerEvent(0);
} // end of state_entry block
... SNIP
touch_start(integer total_number)
{ // present dialog on click
llSetTimerEvent(0);
ID = llDetectedKey(0);
llDialog(ID,"What do you want to do?", MENU_MAIN, CHANNEL1002);
} // end of touch_start block

listen(integer channel, string name, key id, string message)
{
if (channel == CHANNEL1002)
{
if (llListFindList(MENU_MAIN + MENU_OPTIONS + NEW_MENU, [message]) != -1)
{
... SNIP
if (message == "2") // Set up board for 2 players
{
SettingUp = 1;
llWhisper(CHANNEL2006,"Setup2");
llSetTimerEvent(0);
state Play;
}else
if (message == "3") // Set up board for 3 players
{
SettingUp = 1;
llWhisper(CHANNEL2006,"Setup3");
llSetTimerEvent(0);
state Play;
}else
if (message == "4") // Set up board for 4 players
{
SettingUp = 1;
llWhisper(CHANNEL2006,"Setup4");
llSetTimerEvent(0);
state Play;
}else
if (message == "5") // Set up board for 5 players
{
SettingUp = 1;
llWhisper(CHANNEL2006,"Setup5");
llSetTimerEvent(0);
state Play;
}else
if (message == "6") // Set up board for 6 players
{
SettingUp = 1;
llWhisper(CHANNEL2006,"Setup6");
llSetTimerEvent(0);
state Play;
}
}
}else // end of CHANNEL1002 block
... SNIP
} // end of if blocks
} // end of listen block

} // end of default state

state Play
{
state_entry()
{
... SNIP
llSetTimerEvent(Rtime);
} // end of state_entry block
... SNIP
listen(integer channel, string name, key id, string message)
{
if (channel == CHANNEL1003)
{
if (llListFindList(MENU_ALT + MENU_ALTOPTS, [message]) != -1)
{
... SNIP
if (message == "RESET")
{
llWhisper(0,NAME + " has insisted on terminating this game.");
... SNIP
llSetTimerEvent(0);
state default;
}
} // end of if blocks
}else // end of channel 1003 block
if (channel == CHANNEL2109 && llGetSubString(message,0,2) == "Tch")
{
llSetTimerEvent(0);
llSetTimerEvent(Rtime);
}else // end of CHANNEL2109 block
... SNIP
} // end of listen block

timer()
{
integer tempint = (integer)Rtime;
llSay(0,"No action in " + (string)tempint + " Seconds, resetting the board");
... SNIP
llSetTimerEvent(0.0);
state default;
}
} // end of Play state

I hope you can make some sense of the truncated code and help me solve this.
Beverly Ultsch
Registered User
Join date: 6 Sep 2007
Posts: 229
12-15-2007 06:50
Ok if i understand this correctly, once the game starts each player has a certain amount of time in which to make a move, once they have made a move the timer needs reseting for the next player, please correct me if im wrong.

Looking at the code, all the llSetTimerEvent calls in the default state are redundent, as you say there is no event handler in that state.

I think the following will do what you want, although I have used the touch_start event to signify a move rather than the listen for the sake of clarity. You do not need to call llSetTimerEvent(0.0) untill you exit the play state, simpley calling it with a value will reset the timer.

CODE

default {

touch_start(integer total_number)
{
state play;
}
}

state play {
state_entry()
{
llSetTimerEvent(30.0);
}

state_exit()
{
llSetTimerEvent(0.0);
}

touch_start(integer total_number)
{
llSetTimerEvent(30.0);
}

timer() {
llOwnerSay("Time Out");
state default;
}

}


I hope I have understood what you are trying to do correctly.
Gregory McLeod
Registered User
Join date: 21 Oct 2006
Posts: 278
12-15-2007 07:10
Thank you for your time to check out the code.

I think you have hit it right on the nose, especially the bit about just resetting the timer with a new value.

I had the impression from something I read that an event once queued could not be removed, exception possibly a listen using the handle. This drove me to the conclusion that I would have to allow all the timer events to occur and then test for a flag which is reset by the timer event and checked the next time the timer event occurred. If unchanged then no action would have been taken place and the resetting could be done. All the required actions in the script would have to change the flag so that when the timer event occurs it would sense that an action had taken place and reset the flag and allow the timer event to continue for another cycle.

This seemed incredibly complex and I was trying to make it simpler and failed.
Beverly Ultsch
Registered User
Join date: 6 Sep 2007
Posts: 229
12-15-2007 09:46
You are correct in saying that once an event is in the que it can't be removed (someone will correct me if im wrong), however, saying you wan't to be notified of an event eg: by calling llSetTimerEvent or llListen is not the same as the event ocurring ie timer or listen is triggered. Once the event has happened it is in the que, untill it does you can say you are not intereseted in it anymore.
Gregory McLeod
Registered User
Join date: 21 Oct 2006
Posts: 278
12-15-2007 10:30
From: Beverly Ultsch
You are correct in saying that once an event is in the que it can't be removed (someone will correct me if im wrong), however, saying you wan't to be notified of an event eg: by calling llSetTimerEvent or llListen is not the same as the event ocurring ie timer or listen is triggered. Once the event has happened it is in the que, untill it does you can say you are not intereseted in it anymore.
I am not sure I undertstand you correctly. You seem to be sasying that until a timer event has happened you can't stop the event. Surely in the case of llSetTimerEvent(0) this causes the event to be dequeued even if the the interval has not expired. OR Are you saying that the event will occur and you need to handle it at least once.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
12-15-2007 13:44
actually, with timer, when you specify a new setTimerEvent, you arent specifing a queue position, but rather marking a space that will trigger the event later...

slightly less confusing description:
the timer is telling the system to 'hold my place in line', then when you call it again, it's doing the same thing in a different place... 0.0 is the same as saying nevermind.

the reason this is confusing is because there are 2 queue's, the sims (which handles when timed events should fire) and the individual script, which tells it which event is next to handle... the latter cannot be changed, the former changes constantly...

example:
you specify set timer event of 30, 15 seconds later, you call it again, it's place in line jumps and wont trigger for another 30 sec.

example2:
you are in the middle of proccessing code in another event, while stuck in there, the timer event triggers, and it goes in the script queue, but somehow you happen to trigger set timer event 0.0 after the timer entered the event queue. the timer will still fire one more time, since it should have before your other code could finish... there is no way to prevent it running... if you're bored look up "race condition" as it's very similar
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Beverly Ultsch
Registered User
Join date: 6 Sep 2007
Posts: 229
12-15-2007 14:46
i did say someone would correct me if im wrong :)

Void, in practicle terms if i have a timer that is 15 sec into a 30 sec call and i call it again will it trigger twice or once.
Gregory McLeod
Registered User
Join date: 21 Oct 2006
Posts: 278
12-15-2007 14:48
Thanks for the explanation. The second example particularily.
I have regressed back to my original thoughts set the timer to fire every 30 secs and flag it for action by the events in the other scripts. Works, is a bit kludgy but heck I only program for fun.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
12-16-2007 02:08
From: Beverly Ultsch
i did say someone would correct me if im wrong :)

Void, in practicle terms if i have a timer that is 15 sec into a 30 sec call and i call it again will it trigger twice or once.

assuming the following code, the timer only triggers once... 30 sec from the last call

CODE

default{
state_entry(){
llSetTimerEvent( 30.0 );
llSleep( 15.0 );
llSetTimerEvent( 30.0 );
}

timer(){
llSetTimerEvent( .0 );
llOwnerSay( "timer triggered" ); //-- triggers once 45sec after compile/reset
}
}


if you omit the 0.0 call it will keep triggering every 30 secs after the second call in state_entry...

basically it works out to only the last call before the timer is actually triggered is obeyed. so a new call before it runs out, resets it to the new specified time.

if you are in the middle of another event when a the timer is triggered it won't be obeyed, as seen in the following code

CODE

default{
state_entry(){
llSetTimerEvent( 30.0 );
llSleep( 35.0 );
llSetTimerEvent( 0.0 );
llOwnerSay( "this message will display first, even though it's after the timer triggers" );
}

timer(){
llOwnerSay( "timer triggered" ); //-- triggers once, because the timer is queued
// llSetTimerEvent( 30.0 ); //-- uncommenting this line will overide the last set timer event, and cause it to repeat every 30sec
}
}


put another way, the timer itself will enter a queue (and is limited to one instance in the queue), but the call to it (set timer event) does NOT queue up, only the last one is obeyed
llSetTimerEvent is not an event
timer is an event

all code in an event runs before moving to the next event in the queue... AFAIK timers and sensors can only have one entry in the event queue, total event queue is 64 events....
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Gregory McLeod
Registered User
Join date: 21 Oct 2006
Posts: 278
12-18-2007 02:01
As an alternaative to the example provided by Void I show my own test script below.

CODE

integer Action = 0;
integer Rtime = 10;
default
{
state_entry()
{
llOwnerSay("Default state");
llOwnerSay("TimerEvent set to 0");
llListen(0,"",llGetOwner(),"");
llSetTimerEvent(0);
}

on_rez(integer start_param)
{
llOwnerSay("Ready");
}

touch_start(integer total_number)
{
llOwnerSay("Touched");
llOwnerSay("Changing to Play state");
state Play;
}

listen(integer channel, string name, key id, string message)
{
if (message == "hello")
{
llOwnerSay("hello received in default state");
llOwnerSay("TimerEvent set to Rtime: " + (string)Rtime + " seconds");
llSetTimerEvent(Rtime);
llOwnerSay("Action set to 1");
Action = 1;
}
}

timer()
{
llOwnerSay("Default state timer triggered");
if (Action == 0)
{
llOwnerSay("Action is 0");
llOwnerSay("Setting timer event to 0");
llSetTimerEvent(0);
}
else
{
llOwnerSay("Action not 0 setting to zero");
Action = 0;
}
}
}

state Play
{
state_entry()
{
llOwnerSay("Play state");
llListen(0,"",llGetOwner(),"");
}


touch_start(integer total_number)
{
llOwnerSay("Touched");
llOwnerSay("Changing to default state");
state default;
}

listen(integer channel, string name, key id, string message)
{
if (message == "hello")
{
llOwnerSay("hello received in Play state");
llOwnerSay("TimerEvent set to Rtime: " + (string)Rtime + " seconds");
llSetTimerEvent(Rtime);
Action = 1;
}
}

timer()
{
llOwnerSay("Play state timer triggered");
if (Action == 0)
{
llOwnerSay("Action is 0");
llOwnerSay("Reverting to default state");
state default;
}
else
{
llOwnerSay("Action not 0 setting to zero");
Action = 0;
}
}
}


This has messages to indicate what is happening when.
It changes from default state to Play state and back on touch.
The timing only comes into play when you say 'hello' on the chat channel.
In Play state if you repeatedly say 'hello' before the timer triggers it never triggers.
If you say 'hello' after the first trigger it will revert back to the never triggering mode.
if you do not respond when in Play state it will trigger once setting Action to 0 and then on the next trigger revert to default state.

This explains to me when the timer event fires once or twice.

Maybe this could be used as an example in the Wiki??