Sim Status script...
|
Folco Boffin
Mad Moo Cow Cultist
Join date: 27 Feb 2005
Posts: 66
|
06-21-2005 23:57
Ok, as far as I can tell, this should work, but I have at least one (more likely more than that) error in it. It doesn't update the status of the sims. Here's my code. key gKTotalSims; key gKStatus; key gTouched; key gStatus; string gNotecard = "Known Sims"; string gCurrentSim = ""; string realTime = ""; integer gListener = FALSE; integer gLine = 0; integer randomChannel = 0; integer gTotalSims = 0; integer up = 0; integer down = 0; integer unknown = 0; list gKnownSims = []; list gUpOrDown = []; float time1 = 0.0; float time2 = 0.0;
dialogBox(){ llListenRemove(gListener); randomChannel = 10 + (integer)llFrand(100000); gListener = llListen(randomChannel,"",gTouched,""); llSetTimerEvent(20.0); llDialog(gTouched,"To see the status of all the sims, say yes.\nWarning, this is spammy.",["Yes"],randomChannel); }
hoverText(){ vector color = <1.0,0.3,0.6>; llSetText("Known Sims: "+(string)gTotalSims+"\nSims Up: "+(string)up+"\nSims Down: "+(string)down+"\nUnknown Sims: "+(string)unknown+"\nLast Updated: "+realTime,color,1.0); }
default{ state_entry(){ gKTotalSims = llGetNumberOfNotecardLines(gNotecard); llGetNotecardLine(gNotecard,gLine); gLine++; integer x = 0; for (x = 0; x < gTotalSims; x++){ gCurrentSim = llList2String(gKnownSims,x); gKStatus = llRequestSimulatorData(gCurrentSim, DATA_SIM_STATUS); } time1 = llGetGMTclock(); realTime = llGetTimestamp(); hoverText(); } touch_start(integer total_number){ time2 = llGetGMTclock(); if (time2 - time1 >= 300.0){ integer x = 0; for (x = 0; x < gTotalSims; x++){ gCurrentSim = llList2String(gKnownSims,x); gKStatus = llRequestSimulatorData(gCurrentSim, DATA_SIM_STATUS); } time1 = llGetGMTclock(); } gTouched = llDetectedKey(0); if (unknown > 0) { llInstantMessage(gTouched,"Out of the "+(string)gTotalSims+" known, "+(string)up+" are up, "+(string)down+" are down, and status of "+(string)unknown+" of the sims are unknown."); } else { llInstantMessage(gTouched,"Out of the "+(string)gTotalSims+" known, "+(string)up+" are up and "+(string)down+" are down."); } dialogBox(); realTime = llGetTimestamp(); hoverText(); } dataserver(key queryId, string data){ if (queryId == gKTotalSims){ gTotalSims = (integer)data; } else if (queryId == gKStatus){ if (data == "up"){ up++; } else if (data == "down"){ down++; } else { unknown++; } gUpOrDown += [data]; } else { if (data != EOF) { // not at the end of the notecard gKnownSims += [data]; // insert line into list llGetNotecardLine(gNotecard, gLine); // request next line gLine++; // increase line count } } } listen(integer chan, string name, key id, string msg){ if (chan == randomChannel){ if (msg == "Yes"){ integer y; for (y = 0; y < gTotalSims; y++){ llInstantMessage(gTouched,llList2String(gKnownSims,y)+"'s status is: "+llList2String(gUpOrDown,y)+"."); } } llListenRemove(gListener); } } timer(){ llSetTimerEvent(0.0); llListenRemove(gListener); } }
Now I know that this could easily have the sim names hard coded into the script instead of the notecard, but I saw that you can find out the status of sims with this, and since I visit a few islands and sims that are buggy upon occasion I figured I'd use this as an attempt to learn how to work with notecards. And so if I give the script to a friend, they can easily change which sims it is they want to check on. I'm not sure how well this code will look once posted, but I hope it's readable without too many line breaks in weird spots. P.S. If possible, a hint on how to change llGetTimestamp() into a more readable format would be welcome. I'm still having a bit of trouble with parsing strings. And obviously with other things too. 
|
Jeffrey Gomez
Cubed™
Join date: 11 Jun 2004
Posts: 3,522
|
06-22-2005 19:10
Folco, while you did exactly what you probably should in this instance, I'm finding this code difficult to digest. A large part of this is because it has very few comments, and as a result appears to be a lot of data all at once.
Off the top of my head, I know dataserver calls are potentially slow in an odd way. Try consolidating the calls into one event if you can.
Also from experience, a notecard update via a save may sometimes fail if the script is reading that note at the same time. Be careful with this.
Also, by "isn't updating," do you want the script to run constantly, or just when touched? It looks to me like it only runs 20 seconds and shuts off.
_____________________
---
|
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
|
06-22-2005 21:37
Im still reading through your code, but Im noticing one glaring logic error. You're expecting things to happen concurrently that really dont. For example, here: state_entry(){ gKTotalSims = llGetNumberOfNotecardLines(gNotecard); llGetNotecardLine(gNotecard,gLine); gLine++; integer x = 0; for (x = 0; x < gTotalSims; x++){ gCurrentSim = llList2String(gKnownSims,x); gKStatus = llRequestSimulatorData(gCurrentSim, DATA_SIM_STATUS); } time1 = llGetGMTclock(); realTime = llGetTimestamp(); hoverText(); }
In this event, your requesting the total number of sims and each sim, before you go through an iterative process to use the results of those two requests (the for loop). Here, your expecting that the gTotalSims variable and the gKnownSims variable be already filled with the data you requested, before you go into the for loop. This is the wrong mindset for this scenerio. Dataserver functions (and single-threaded event-driven programming) take a little getting used to. What's truly happening, is that two pieces of data are requested from the dataserver, but your state_entry event hasn't let the dataserver event get processed yet. Scripts can only execute one event at a time, so your two variables are filled only *after* the state_entry event has fully completed. So, during state_entry's execution, the two requests are made, then the for loop executes when gTotalSims still equals 0 and gKnownSims still equals [] (empty list). Here's how you should program it: code in state_entry requests notecard length, dataserver returns notecard length, code in dataserver checks if line 0 is < notecard length, if so, then it requests line 0, if not, its done getting all the notecard lines, and should start requesting the sim statuses. dataserver returns line 0, code in dataserver checks if line 1 is < notecard length, if so, then it requests line 1, if not, its done getting all the notecard lines, and should start requesting the sim statuses. dataserver returns line 1, code in dataserver checks if line 2 is < notecard length, if so, then it requests line 2, if not, its done getting all the notecard lines, and should start requesting the sim statuses. ... and so on Your gkStatus variable is pretty much useless as you are using it in your for loop. Your basically erasing it and writing over it with a new key each time you iterate. The dataserver event isn't called while the llRequestSimulatorData function is running, it will be called once the state_entry event is over. Instead of storing a single key when you request all the sim statuses, store the request keys in a list (Ill refer to this list as gStatusRequests). This way, you can look up the index of the key the dataserver returns, and get the sim name that the data refers to (by iterating over the gKnownSims list while building the gStatusRequests list, each index in gKnownSims corresponds to the request key in gStatusRequests.) I hope this helps, please don't hesitate reply if I confused you, I often run circles around myself when I try to explain something.  ==Chris
|
Folco Boffin
Mad Moo Cow Cultist
Join date: 27 Feb 2005
Posts: 66
|
06-23-2005 11:10
Jeff: The 20 second timer is for a timeout on the listen for the dialog box. (That I got from a post you made actually. *lol*) Yeah, I didn't really comment it because I wasn't expecting it to not to work. Thought I understood how the dataserver calls were made. Sorry about that. I should have gone and inserted comments on what I thought I was doing. If you want, I can go and edit the post and add comments in if it will make it easier to understand. By isn't updating I mean, it says that I have 10 sims in the list, and will list all 10 sims, but it doesn't return the status of the sims. The llGetGMTclock() is there because someone could go and put a list of like 100 or so sims, or even thousands of bogus sim names and it would still want to do a call to see if they were up or not, so to make it a bit harder to lag a sim to hell, I check the clock to make sure that at least 5 minutes have gone by since the last update of the sim status. The dataserver is slow enough from what I understand without someone going and trying to request 10000 sim statuses ever 10 seconds or something. Hehe. I use the timestamp so I can show when the statuses were last updated. Though, I suppose I could also add a line that checks to see if the list is longer than like 15 or something and return an error message if it is telling them to reduce the number of sims being checked.I just need to figure out how LSL handles parsing to make it into a more readable format, but that's after I figure out these damned dataserver calls. Hehe. Chris: There's a lot of info in that post. *lol* I'm having a bit of trouble digesting it all, but I think I understand some of it. Something I'm not sure of, is llGetNotecardLines() also retrieving the value of the line? If that is true, that saves a dataserver call as I can do both in one shot. I'm not sure what you are talking about when you say it doesn't work concurrently, since the way I was thinking has everything beind done sequentially. Except from the two of your posts, my dataserver calls only happen on leaving the state. Would it work better if I made them into their own functions? On a side note, the dataserver does return everything sequentially correct? Like sim 1 will be sim 1 in both of my lists right? So when I say Sim 1 is up, I actually am reporting Sim 1's status and not like Sim 5's? Me: Did I do a good job of trying to figure out what you both wanted to know in order to help me better? hehe. If there's anything else I can do just let me know. Code optimization is always good. As well as anything to help make it more sim friendly. Esp. since I have the feeling it can be a rather sim-intensive script at times. EDIT: Added another comment and did a poor job of trying to make it look nicer.
|
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
|
06-23-2005 12:08
From: Folco Boffin Chris: There's a lot of info in that post. *lol* I'm having a bit of trouble digesting it all, but I think I understand some of it. Something I'm not sure of, is llGetNotecardLines() also retrieving the value of the line? If that is true, that saves a dataserver call as I can do both in one shot. I'm not sure what you are talking about when you say it doesn't work concurrently, since the way I was thinking has everything beind done sequentially. Except from the two of your posts, my dataserver calls only happen on leaving the state. Would it work better if I made them into their own functions?
[/B] Well, your making the requests (llGetNumberOfNotecardLines/llGetNotecardLine), then acting on the data retrieved from those requests (in your for loop), before the data is even stored (in the dataserver event). The dataserver event doesn't run until the state_entry event is done, so your looping over an empty list in state_entry. When data is requested from the dataserver, when the data is retrieved, a dataserver event is pushed on to the script's event queue. As you request more data in the current running event, more dataserver events are queued. The current running event has to complete before those dataserver events are executed by the script. From: Folco Boffin On a side note, the dataserver does return everything sequentially correct? Like sim 1 will be sim 1 in both of my lists right? So when I say Sim 1 is up, I actually am reporting Sim 1's status and not like Sim 5's? [/B] Most of the time, yes. But the system is designed so that it might *not* happen sequentially. I would really recommend against assuming that it does. ==Chris
|
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
|
06-23-2005 14:41
you have a nasty logic error with the for loop. It will only recognize the last sim status that comes back as a sim status and all the others will be recognized as notecard lines. This is where i use states. Just because it simplifies debuging. list Data; list Query; list Request; integer Timer; integer ReturnFrom;
list NotecardInfo;
default { state_entry() { if(Data != []) { if(ReturnFrom == 1) {//returned from GetSimData if(Timer < 0 && llGetListLength(Request) < 5) {//this event always returns data even if it times out so we have to check the timer Request += 18; state GetSimData; } llOwnerSay("Sim: "+ llList2String(Request,0)); llOwnerSay("Status: "+ llList2String(Data,1)); llOwnerSay("Pos: "+ llList2String(Data,0)); } else if(ReturnFrom == 2) {//returned from GetNotecardLines Request = NotecardInfo = [llList2String(Request, 0), 0, llList2Integer(Data, 0), 1]; state GetNotecardLine; } else if(ReturnFrom == 3) {//returned from GetNotecardLine NotecardInfo = Request; Request = Data; state GetSimData; } else if(ReturnFrom == 4) {//returned from PickAssetType Request = Data; state GetNotecardLines; } } else if(ReturnFrom) {//Data Request Timed Out, halt the script llOwnerSay("Fatal Error: Date request timed out, Returning From: " + (string)ReturnFrom + ", Request: [" + llList2CSV(Request) + "]"); return; } integer a = llGetListLength(NotecardInfo); if(!a) { Request = [INVENTORY_NOTECARD]; state PickAsset; } else if(a < 2) { Request = NotecardInfo; state GetNotecardLines; } else { a = llList2Integer(NotecardInfo,1) + 1; integer b = llList2Integer(NotecardInfo,2); if(a < b && b) { Request = NotecardInfo = llListReplaceList(NotecardInfo, [a], 1, 1); state GetNotecardLine; } } } }
state GetSimData { state_entry() {//Request = [SimName, {Timer}], Data = [SimPos, SimStatus] // llOwnerSay("GetSimData"); string sim = llList2String(Request,0); Data = Query = []; ReturnFrom = 2; //we be bad :P Query = [llRequestSimulatorData(sim, DATA_SIM_POS), llRequestSimulatorData(sim, DATA_SIM_STATUS)]; Data += ["unknown", "unknown"]; if(!(Timer = llList2Integer(Request, 1))) Timer = 12; llSetTimerEvent(5.0); }
dataserver(key a, string b) { integer d = llListFindList(Query,[a]); if(1 + d) { Data = llListReplaceList(Data,,d,d); if(b == "unknown" || !(--ReturnFrom)) state default; else ++Timer; } } timer() { if(--Timer < 0) state default; } state_exit() { Query = []; ReturnFrom = 1; } }
state GetNotecardLines { state_entry() {//Request = [NotecardName, {Timer}], Data = [NumberOfLines] // llOwnerSay("GetNotecardLines" ; Query = [llGetNumberOfNotecardLines(llList2String(Request,0))]; if(!(Timer = llList2Integer(Request, 1))) Timer = 6; Data = []; llSetTimerEvent(5.0); } dataserver(key a, string b) { if(1 + llListFindList(Query,[a])) { Data = [(integer)b]; state default; } } timer() { if(--Timer < 0) state default; } state_exit() { Query = []; ReturnFrom = 2; } }
state GetNotecardLine {//Request = [NotecardName, {Line, NumberOfLines, IgnoreWhiteSpace, {Timer}}], Data = [Line] state_entry() { // llOwnerSay("GetNotecardLine" ; Query = [llGetNotecardLine(llList2String(Request,0), llList2Integer(Request,1))]; if(!(Timer = llList2Integer(Request, 2))) Timer = 8; Data = []; llSetTimerEvent(5.0); } dataserver(key a, string b) { if(1 + llListFindList(Query,[a])) { if(llList2Integer(Request,3)) { if((string)llParseString2List(b,[" ","\n","\t"],[]) == "" { integer t = llList2Integer(Request,1) + 1; if(t < llList2Integer(Request, 2)) { Query = [llGetNotecardLine(llList2String(Request,0), t)]; Request = llListReplaceList(Request, [t], 1, 1); if(!(Timer = llList2Integer(Request, 2))) Timer = 8; return; } } else Data = ; } else Data = ; Timer = 0; } } timer() { if(--Timer < 0) state default; } state_exit() { Query = []; ReturnFrom = 3; } }
state PickAsset {//Request = [AssetType, {Timer}], Data = [AssetName] state_entry() { // llOwnerSay("PickAsset" ; ReturnFrom = 4; //save an event; integer type = llList2Integer(Request,0); integer a = llGetInventoryNumber(type); if(a>1) { integer b = ~((integer)llFrand(0x7FFFFFDF) + 0x20); Query = []; Data = []; integer c; do { Data += ((string)c + (": " + llGetInventoryName(type,c))); Query += (string)c; } while(++c < a); llListen(b, "", llGetOwner(), "" ; llDialog(llGetOwner(), llDumpList2String(Data, "\n" , Query, b); if(!(Timer = llList2Integer(Request, 1))) Timer = 8; Data = Query = []; llSetTimerEvent(5.0); } else { if(a) Data = [llGetInventoryName(type, 0)]; else Data = []; state default; } } listen(integer a, string b, key c, string d) { Data = [llGetInventoryName(llList2Integer(Request,0), (integer)d)]; state default; } timer() { if(--Timer < 0) state default; } }
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river. - Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence. - James Nachtwey
|
Folco Boffin
Mad Moo Cow Cultist
Join date: 27 Feb 2005
Posts: 66
|
06-23-2005 19:58
Chris: Damn, that kinda sucks. Both me looping over an empty list (which explains my results), and that the results might not be returned in the order requested. What can I do to ensure that I'm saying that Sim 1 is up and Sim 2 is down and not vice-versa?
Strife: I had a feeling states would make this easier. And after looking over your code real quick, I have come to the conclusion that I really need to look at states in the wikki. Or really look at your code for a bit after getting a drink or two. Just the first glance at it I was like damn, this looks odd. I'm not sure why, because I'm fairly certain that I have most likely used states or something like them in one of the other languages I know. A brief glance is definatly not enough to pick it up anyway. Give me an hour or so and if I don't have it figured out by then, I'll be back in the forums asking about states next. *lol* I've read that states can make debugging really easy, and that it can make scripts much easier to work with, and I've also heard the exact opposite as well. But in this case, I think states are probabally the best way to go, and as I'm typing this I'm going over in my head what I saw in your code and I think I'm getting a better understanding of it. That or I'm remembering it in a simplified way that makes it much easier. Find out soon. But for now, I think I need some Jack and Coke.
|
Folco Boffin
Mad Moo Cow Cultist
Join date: 27 Feb 2005
Posts: 66
|
06-23-2005 21:06
Ok yeah, right now, that code is looking way more advanced than I can think right now. (And I haven't even had those drinks yet.) And it doesn't help that it didn't work. *lol* But hey, it's free help, so I'll try and figure out what needs tweeking to get it to do so. But first I'm gonna have to figure out how some of the other stuff in this code works first. I know there are beginning scripting classes in SL, but what about advanced scripting? I have a feeling I'm gonna have to have someone walk me through it a time or two before it catches. And then I'll be like "Wtf?! Why didn't I see that to begin with." I wouldn't be surprised at all if that's how it happens either.
Well, need to go and put together a bike now while my daughter falls asleep for her birthday tomorrow. Well, guess it's today actually. Damn our kid's a night owl just like us. Midnight and still up. *lol* I'll take another look at this when we're done.
_____________________
^-^
Signed, Gorgarath, Whom in this game called Second Life, plays the avatar Folco Boffin, and in this game called First Life, plays the avatar John McDonnell.
|
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
|
06-24-2005 13:06
I've updated it so it now works
Changes: fixed the syntax error in calling state PickAsset. fixed the logic error in the listen event in PickAsset. fixed oversight in displaying of returned data from GetSimData. added support for setting a custom timeout on each state, but it is not required. added a timeout catch for GetSimData and made it give more time and try again.
While states aren't the fastest way to do this they make debugging easy. What i'm doing is using states as subroutines.
I pass them a set of paramaters, in this case in the form of a list "Request" Each state sets an ReturnFrom equal to a unque number for the state so we know what type of data is being returned. We then process Data.
The advantage of how this script works is that only a few globals are needed, the states are modular and compatible with each other. Code wise there are very similar so it would be possible to actualy merge them into some more generic state; this similarity also makes it easier to debug them and change them.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river. - Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence. - James Nachtwey
|
Folco Boffin
Mad Moo Cow Cultist
Join date: 27 Feb 2005
Posts: 66
|
06-24-2005 20:26
Ok, yeah, the code works much better now. *lol* It actually compiled on first try without me having to fix it. Thank you. Much easier trying to learn with working code, though I don't always expect to find working code here as it is free and you get nothing for helping me out. Except for that warm fuzzy feeling on the bottoms of your feet I suppose. Hehe, anyway yeah, I think given some time, I can figure out what's going on. You're explanation helped explain a couple things I wasn't sure about. It doesn't work how I wanted it to, but that's a good thing too. Makes me have to learn in order to get it to do exactly what I want. ^_^ But it gives me the basics of what I need, so it should be easy sailing from here. Or at least not through a hurricane.
Thank you all for your help. I'll probabally be back.
-Folco
_____________________
^-^
Signed, Gorgarath, Whom in this game called Second Life, plays the avatar Folco Boffin, and in this game called First Life, plays the avatar John McDonnell.
|
Zindorf Yossarian
Master of Disaster
Join date: 9 Mar 2004
Posts: 160
|
06-26-2005 12:27
From: Folco Boffin Except for that warm fuzzy feeling on the bottoms of your feet I suppose. I usually get that feeling when my foot is dying from lack of oxygen.
_____________________
Badass Ninja Penguin: Killing stuff it doesn't like since sometime in May 2004.
|