Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

email() never returns

Tanner Mills
Registered User
Join date: 12 Jun 2007
Posts: 16
09-18-2007 09:16
default
{
state_entry()
{
llSay(0, "Hello, Avatar!";);
llGetNextEmail(gEMAIL,"";);
llSay(0, "We're off!";);
}

email(string name, string address, string subj, string message, integer num_left) {
llSay(0, "I'm off of Email!";);

In this snippet, the email portions are a straight cut and paste from a script that works. In this script however, "We're off!" is the last line executed, suggesting the email() event starts. However, this never returns from that email call. "I'm off of Email!" never executes.

I can't figure out why this is hanging up. If I run the old script this came from, right beside this script, the old script will work.

Someone save me from pulling more hair out, please!
RJ Source
Green Sky Labs
Join date: 10 Jan 2007
Posts: 272
09-18-2007 09:42
What is the value of gEMAIL? Are you sure it's right?
And are you sure there is indeed email waiting to be read?
Eyana Yohkoh
Registered User
Join date: 30 Nov 2006
Posts: 33
09-18-2007 13:19
Try checking email by using this:

llGetNextEmail("", "";);

The way that works is that it's going to try to check for an email from an exact address or subject if it's anything but an empty string.

Also you might want to doublecheck that you are sending a test email to the right place. Remember if you pick up an object and rez it again then it will have a new key which means the email address changes.

Try adding a message that gives you the object's key on rez or in state entry (depending on what exactly you want to accomplish).
_____________________
http://eyanayohkoh.blogspot.com
http://www.lslwiki.net/lslwiki/wakka.php?wakka=EyanaYohkoh
Tanner Mills
Registered User
Join date: 12 Jun 2007
Posts: 16
09-19-2007 04:36
From: RJ Source
What is the value of gEMAIL? Are you sure it's right?
And are you sure there is indeed email waiting to be read?


gEMAIL is the email address I use to send to the box. This var just keeps anybody from sending to the object, well, I should say the object acting on mail from just anybody. Again, it works fine in the other old script.

I've tried double empty strings in GetNext too. It still locks up on the email event.

As to being sure there is mail to be read... I'm sure there isn't mail to be read. The state this is in is checking for mail and acting on that mail if found. It still should return from the call, even without finding any mail. Once again... in the other script it's fine.
Tanner Mills
Registered User
Join date: 12 Jun 2007
Posts: 16
09-19-2007 04:47
From: Eyana Yohkoh
Try checking email by using this:

llGetNextEmail("", "";);



Yeah, all that's done and been done Eyana, thanks.

Here's my epifany on this this morning. See if I'm right:

llGetNextEmail is checking to see if anything is pending, and because nothing is pending, the email() event is NOT called. So instead of moving anywhere else, it's dropping to the end of the event and sitting at the bottom waiting for something to happen.

What I need to do to cure this is send the code back to the timer after the GetNext function so that it all can happen again at some point. I bet doing this, when mail IS pending, it will work fine.

I'll plug it in and let you know how that works. I'm still to used to code that always falls further. States and events are a new world to me.
Tanner Mills
Registered User
Join date: 12 Jun 2007
Posts: 16
09-19-2007 06:31
grrr, grrr, and double grrrr:

Here's a bigger picture of what I'm trying to make happen:

state clock
{
state_entry()
{
llOwnerSay("Enter clock state.";);
float emailtime;
float checktime = llGetWallclock();

while ((checktime - gSTARTTIME) < gSETIMER){
emailtime = llGetWallclock();
if ((emailtime - checktime) > 60){
llOwnerSay("Time to check the email! " + (string)(emailtime - checktime));
state checkemail;
}
}
llResetScript();

}
}

state checkemail
{
state_entry()
{ float checktime = llGetWallclock();
llOwnerSay("Checking Email. Total clock time " + (string)(checktime - gSTARTTIME));
llGetNextEmail("","";);
llOwnerSay("Success on GetNextMail.";);
//state clock;
}

email (string time, string address, string subj, string message, integer num_left) {
llOwnerSay("Email Returned: " + llList2CSV([time, address, subj, message, (string)num_left]));


Here's what's going on:

The code enters a state called "clock" that waits a period of time, then checks to see if any mail is pending. On reaching the appointed time, we slip out of "clock" and into "checkemail". All other crappy crap aside, in checkemail we execute the GetNextMail function, here's where life sucks and then you die:

After GetNextMail checks the cue, it still falls to the next line of code wiether a pending mail exists or not. The difference in pending or none pending is this: If pending, the code allows itself to FALL to the email() event... it doesn't CALL the event as stated. If it CALLed the event "Success on GetNextMail" would not be seen. It would jump to the email(). This is where the commented out "state clock" made sense to me. If an email was pending, state clock wouldn't be seen, we'd jump to email(). If none was pending we'd fall to the next line and "state clock" would restart the check timer. But NOOOOO...

The code always falls to the next line after GetNextMail, the difference is that if an email is pending the code will continue to fall to the email() event, where indeed, it will do it's thing. If NO email is in the queue, the code will stop at the bottom of the checkmail "state_entry" and sit there like a bump on a log.

lslWiki touts NO return value for the GetNextMail event, not even TRUE or FALSE, if this is the case there is no way to check what the GetNextMail event has found and act upon it, thus making my life hell. This can't be true, there has to be a way to determine if GetNextMail found anything in the queue, otherwise I'm tattoo'd.

Thoughts?
Tanner Mills
Registered User
Join date: 12 Jun 2007
Posts: 16
09-19-2007 07:10
The ugly cure:

Make sure there is always an email in the queue. So I'll add an llemail function to the top of the clock state that emails to itself, then I can run through conditionals after the email event.

Ugly, ugly, ugly.
Shadow Subagja
Registered User
Join date: 29 Apr 2007
Posts: 354
09-19-2007 10:51
why not use a timer event and periodically check for email, if there is one you get your event, if not you do nothing till the next check?

default
{
state_entry()
{
llSay(0, "Hello, Avatar!";);
llSetTimerEvent(60);
llSay(0, "We're off! Playing nice in event driven environment and freeing up the script to handle incoming events instead of looping which can be a no-no";);
}

timer()
{
llSay(0, "Returning from idle to check for emails.";);
llGetNextEmail(gEMAIL,"";);
}

email(string name, string address, string subj, string message, integer num_left)
{
llSay(0, "Got an email:" + message);
llGetNextEmail(gEMAIL,"";);
}
Masakazu Kojima
ケロ
Join date: 23 Apr 2004
Posts: 232
09-19-2007 14:11
It sounds like you are confused about llGetNextEmail and LSL in general. Have you looked at the wikis?

http://rpgstats.com/wiki/index.php?title=LlGetNextEmail
http://rpgstats.com/wiki/index.php?title=Email
Tanner Mills
Registered User
Join date: 12 Jun 2007
Posts: 16
09-20-2007 07:13
From: Shadow Subagja
why not use a timer event and periodically check for email, if there is one you get your event, if not you do nothing till the next check?


First and foremost because "do nothing" is not an option.

The script is keeping track of two time periods. Is it time to check mail, is it time to make a report to the owner? The report happens via IM and email every six hours. Fetching email is how the owner gets on-demand performance from the script. Waiting six hours to do something the owner has asked to be done is truly weak.

I might be able to use a timer event for the short email wait period, but I've seen issues using the timer event, within an event... which would be, "Is 6 hours up?" So screw the timer any how.

All these problems stem from the lack of feedback from the GetNextMail. A simple true/false return from that function would keep things alive and happy. Always having something in the queue does workaround the issue.
Tanner Mills
Registered User
Join date: 12 Jun 2007
Posts: 16
09-20-2007 07:25
From: Masakazu Kojima
It sounds like you are confused about llGetNextEmail and LSL in general. Have you looked at the wikis?

http://rpgstats.com/wiki/index.php?title=LlGetNextEmail
http://rpgstats.com/wiki/index.php?title=Email


Of course I've read those Mas...

If I'm confused about anything, it's coming from a realm where code always seeks to be doing work, to a realm where code is most content sitting on it's hands.

States and events so far are proving some different from routines and subroutines, but I'm learning.

GetNextMail's requirement to finish the event it's in before stepping into the email() event is pretty funky to me. If GetNextMail would JUMP on a true, that would fix some issues, and as I've said, if GetNextMail would return a true/false I could also drive some code well.

Code that's content to do nothing is very confusing to me, yes.
Talarus Luan
Ancient Archaean Dragon
Join date: 18 Mar 2006
Posts: 4,831
09-20-2007 09:23
Welcome to the world of event-driven programming.

You'll find that most serious programming today is based on events, rather than polling. Why wait and waste time doing so for something that may/may not happen, when you can be doing other things?

/me introduces you to the concept of "paradigm shift".

That said, there is a REAL EASY way to do what you are trying to accomplish programmatically via LSL. It is called a "timer", a timeout variable, and perhaps status variables. Something like this:

CODE

float gfTimerResolution = 5.0; // Change to however often you want to check for mail

integer giReportTimeout = 21600; // 6 hours in seconds
integer giReportTimer = -1;

SendReport() {
// Send your regularly scheduled email report
giReportTimer = llGetUnixTime() + giReportTimeout;
}

default {
state_entry() {
llSetTimerEvent(gfTimerResolution);
giReportTimer = llGetUnixTime() + giReportTimeout;
}
email(string psTime, string psAddress, string psSubject, string psMessage, integer piRemaining) {
// Handle your incoming email however
llGetNextEmail("",""); // ALWAYS DO THIS IN HERE
}
timer() {
integer liTime;

liTime = llGetUnixTime();
if (liTime > giReportTimer)
SendReport();
else
llGetNextEmail("","");
}
}


That satisfies both the requirement that you send out a periodic report via email, and you are constantly listening for incoming emails from the owner.

As for your other requirements, I am sure you can cobble them into that structure up there. :)
Tanner Mills
Registered User
Join date: 12 Jun 2007
Posts: 16
09-21-2007 08:14
From: Talarus Luan
Welcome to the world of event-driven programming.

You'll find that most serious programming today is based on events, rather than polling. Why wait and waste time doing so for something that may/may not happen, when you can be doing other things?

/me introduces you to the concept of "paradigm shift".



/me walks up to Talarus and prostrates herself at his knees, her face looks to the floor...

Oh wise Master of the event driven concept, please help me sort through my feeble mind the greatness you have laid at my feet. My confusion firstly lies in the path through the code and how and what executes when. Allow me to comment as my finger passes over the wisdom you have presented me and please provide clarity to this old school mind of programming...

From: Talarus Luan

float gfTimerResolution = 5.0; // Change to however often you want to check for mail

integer giReportTimeout = 21600; // 6 hours in seconds
integer giReportTimer = -1;


Here your greatness has simply initalized some global variables that we will use. No brainer.

From: Talarus Luan

SendReport() {
// Send your regularly scheduled email report
giReportTimer = llGetUnixTime() + giReportTimeout;
}


Next however Oh Wise One, you have installed an executable section of code, that my old school feeble mind would say will want to execute first thing, upon run of the script. This code is not declared as a state, so it must be an event, yet this event does not execute on the first pass as the code seeks to find the default state. Please Master, how can this be?


From: Talarus Luan

default {
state_entry() {
llSetTimerEvent(gfTimerResolution);
giReportTimer = llGetUnixTime() + giReportTimeout;
}


Next we fall into the home state and it's first event. You've set the timer to a value, and given value to the duration between email reports. Easily understood...

From: Talarus Luan

email(string psTime, string psAddress, string psSubject, string psMessage, integer piRemaining) {
// Handle your incoming email however
llGetNextEmail("","";); // ALWAYS DO THIS IN HERE
}


We fall out of our interval assignments into the email fetch routine. Since we have not probed the queue, no action is taken on this event and it is ignored? This being true, is it also true the GetNextMail in this event is also passed over without being executed?

From: Talarus Luan

timer() {
integer liTime;

liTime = llGetUnixTime();
if (liTime > giReportTimer)
SendReport();
else
llGetNextEmail("","";);
}
}


Now Oh Wise One, here's where the realm gets interesting...

The timer is an event, but my small mind perceives the timer as a function and this function will put the code to sleep for the period described. By evoking the timer here, you have put the code to bed for our smallest period of time. Nothing will happen in the script now until the time period has elapsed.

Upon elapse, you init a current time variable, and throw the code into a conditional. If we're over the amount of time between reports, go do one, if not... probe the email queue.

Master, you now probe the queue, and most likely no mail exists, so no email() event is called. Falling out of the conditional, does the code not now sit on the bottom with no place to go, all work being done?

No, I see your code does not do this, it continues to tick. How can this be? What is driving the default to again loop through that state? I can see how if something WAS found in the queue, the email() event would be called causing a loop, but without something in the queue to call the email() event, I do not see how this code does not fall silent.

Please help me understand what I do not Master.
Talarus Luan
Ancient Archaean Dragon
Join date: 18 Mar 2006
Posts: 4,831
09-21-2007 09:06
From: Tanner Mills
Next however Oh Wise One, you have installed an executable section of code, that my old school feeble mind would say will want to execute first thing, upon run of the script. This code is not declared as a state, so it must be an event, yet this event does not execute on the first pass as the code seeks to find the default state. Please Master, how can this be?


No, as an old schooler, you should recognize that as the declaration of a User-Defined Function, and only executes when needed / called.

From: someone
We fall out of our interval assignments into the email fetch routine. Since we have not probed the queue, no action is taken on this event and it is ignored? This being true, is it also true the GetNextMail in this event is also passed over without being executed?


OK, perhaps a primer on event-driven programming is in order here. The way you do it is you declare events, pretty much like User-Defined Functions above, but they have a specific standard call interface, like an API. The difference is *you* never call them. There is a thing called an event queue out in the "operating system". When something happens (program starts up, shuts down, user types a message, clicks a button, data is returned from a server, etc), an event of the appropriate type with associated data is put into the queue. At some point, another process, called the event dispatcher, sees that your program has an event waiting in its queue. It pulls the oldest event out of the queue (since there could have been more than one queued up when it gets to your program), looks at what type it is, and calls the appropriate defined handler routine which you have declared in your program. That's where your code takes control. In LSL, there really isn't any true "startup code"; it goes straight into waiting for the event dispatcher to pull events from the queue and call your code. Think of all of your events simply as a bunch of standardized callback functions. Your entire program is nothing but a set of these. In the case of LSL, it can even have multiple states where a different set of callback functions is available to process the queued messages.

Now, the thing is, your program is STILL single-threaded in an event-driven system. It is meant to be that way to enforce proper ordered handling of the events, since it is still basically a program that would otherwise execute sequentially. That said, while an event handler is executing (your code), the event queue and dispatcher wait on it to complete and exit the event handler before it attempts to dispatch another event. So, you really, really want to make your event handlers lean and mean. Dedicate them to the task of handling the event, and exit as soon as possible. The event queue is a finite (and rather small in LSL) resource, so when it fills up, any more events after that point get dropped into the bit bucket.

From: someone
The timer is an event, but my small mind perceives the timer as a function and this function will put the code to sleep for the period described. By evoking the timer here, you have put the code to bed for our smallest period of time. Nothing will happen in the script now until the time period has elapsed.


Hopefully the above terse primer on EDP will address this, but basically, the llSetTimerEvent() call back in the state_entry() event handler set in motion a timer somewhere out there in the OS which will, on elapse, stuff an event into the event queue of the type "timer". When the event dispatcher for your script pulls that event out of the queue, it calls the declared timer() event handler (just like a user function), and thus it executes that code. Then it executes it again in the next 5 seconds or so after the OS timer for the script puts the next timer event in the queue for the dispatcher to pull out and call it, then again ad infinitum.

From: someone
Master, you now probe the queue, and most likely no mail exists, so no email() event is called. Falling out of the conditional, does the code not now sit on the bottom with no place to go, all work being done?


Correct, it simply exits the event handler, returning execution back to the event dispatcher to pull the next event out of the event queue, if there is one, else it will just go to sleep until one shows up.

From: someone
No, I see your code does not do this, it continues to tick. How can this be? What is driving the default to again loop through that state? I can see how if something WAS found in the queue, the email() event would be called causing a loop, but without something in the queue to call the email() event, I do not see how this code does not fall silent.


The part you do not see in any script code is the event dispatcher in the underlying platform, and all the pieces that can put events in the event queue for it to dispatch. You are basically operating on a platform where your code is really nothing more than a series of declarations, which are only called when needed. There are a few automatic events that are guaranteed to occur every time, in order to kick things off. When you reset/compile a script and the event dispatcher starts to run, it automatically as part of its initialization code, puts an event of the type "state_entry" for the default state into the queue to be sure your code gets a chance to run at least one event every time. In addition, any time you rez an object, the event system puts an event of the type "on_rez" into the queue, so your code can tell when the object it is contained in is rezed. The idea is that you get to start the ball rolling, and you can either set things in motion to cause an event to occur as a result of something you do in your code (like request a line out of a notecard, request an email out of the email queue, etc), or you can just simply wait on something to happen in the environment (someone/something says something in chat, someone touches the object, etc). You can even make it so that you do a normal "old-school" program all within the state_entry event handler in the default state. However, you are limited to what you can do, since many important and useful functions require events being dispatched to operate properly, and staying in one event handler prevents that from occurring.

From: someone
Please help me understand what I do not Master.


Does that help? :)

You may also want to study the paradigm elsewhere. I am sure I have butchered it pretty badly for the sake of brevity. Google or Wiki "event-driven programming".
Tanner Mills
Registered User
Join date: 12 Jun 2007
Posts: 16
09-22-2007 13:09
You have imparted much wisdom to the woefully undeserving Master. It's most appreciated. Primers on flow have been elusive for me to find, so I fit about hoping for light bulbs to ignite. You have giving me much to try and absorb. I will do my best to make your efforts bear fruit.

I remain in debt to your kindness.