Jump to content

Display message only once in a timer for loop


Emilee Edenflower
 Share

You are about to reply to a thread that has been inactive for 2111 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

OK so i'm probably overlooking something really simple in the logic, but I can't see it for myself right now.

I just want the llOwnerSay("so and so is in the region") to only display once if that avi has been detected already and added to the "seen" list (unless they are removed and come back later).

I'm sure it's something simple I'm overlooking, but any help would be nice! :) If there's a better way to do this, please suggest it also!

 

timer(){	
	//Recheck the region
	REGION_KEYS = llGetAgentList(AGENT_LIST_REGION, []);
    NUM_KEYS = llGetListLength(REGION_KEYS);
	key thisAvKey;
	string  this_agent_name;
	integer i;
        for (i = 0; i < NUM_KEYS; ++i) {
            thisAvKey = llList2Key(REGION_KEYS,i);
			this_agent_name = llKey2Name(thisAvKey);
			
			//See if targets are in your region
			integer listIndex = llListFindList(TARGET_LIST, [this_agent_name]);
			//llOwnerSay((string)listIndex);
			if (listIndex != -1)
            {
				//Update notifation list
				SEEN_NOTI += [this_agent_name];
				SEEN_NOTI = ListUnique(SEEN_NOTI);
				//llOwnerSay(llDumpList2String(SEEN_NOTI, ","));
				if (llListFindList(SEEN_NOTI, [this_agent_name]) != -1){
				//Only give notifation if it's not been said already!
					llOwnerSay(llGetDisplayName(thisAvKey) + " is in your region of " + llGetRegionName() + "!");
				}
				//Remove from SEEN list if they leave the region
				if (llGetAgentSize(thisAvKey) == ZERO_VECTOR){
				llOwnerSay("removed " + this_agent_name + "from the list!");
					SEEN_NOTI = ListItemDelete(SEEN_NOTI, this_agent_name);
				}
				//llOwnerSay(llDumpList2String(SEEN_NOTI, ","));
			}
        }	
	}

 

Link to comment
Share on other sites

something like this maybe

timer()
{
   list agents = llGetAgentList(AGENT_LIST_REGION, []);
   integer len = llGetListLength(agents);
   integer i;
   for (i = 0; i < len; i++)
   {
      key id = llList2Key(agents, i);
      if (!~llListFindList(TARGET_LIST, [id])  // not in target list
      {
         llOwnerSay(0, llGetDisplayName(id) + " some message something hi! hi!");
      }
    }
    TARGET_LIST = agents;   // target list is now the list of agents currently on the region
}

 

Link to comment
Share on other sites

i just add on a little note here about ++i vs i++

in LSL it doesn't make any difference. In other C-like languages it does. Lexically

++i means add 1 to 'i' and then use the new value of 'i'

i++ means use the current value of i then add 1 to 'i'

for (i = 0; i < n; ++i)
{
    print i;  // prints 1
}

for (i = 0; i < n; i++)
{
    print i;  // prints 0
}


that it doesn't make any difference in LSL is because its a bug and Mr Cory had heaps of other things to worry about at the time no doubt. A bug which can't ever be rectified now because content breakage

however from a programmer's pov is best to write our code according to the lexicon. Even if only to make it clear to those who do come from other languages, what our code is doing

Link to comment
Share on other sites

Thanks for the replies. Haven't tried it yet but I thought I might just add (since I forgot to say in the OP): TARGET_LIST is a list of predefined avatar user names you want to look for. So it's trying to cross reference agents in the region with your custom list and notify you if they enter the sim and notify you only once they arrive unless they leave and come back later. 

Not sure if this changes how you'd respond to my code or your examples?

Edited by Emilee Edenflower
Link to comment
Share on other sites

in this case then I probably maintain a list of targets currently on the region. A seen list and then do it in 2 passes. Something like

timer()
{
   list agents = llGetAgentList(AGENT_LIST_REGION, []);
   integer n = llGetListLength(agents)
   if (n == 0)
      return;

   key id;
   // remove a previous seen target if no longer present on region
   integer i = llGetListLength(SEEN_LIST);
   while ((--i) >= 0)
   {
      id = llList2Key(SEEN_LIST, i);
      if (llListFindList(agents, id) < 0)
         SEEN_LIST = llDeleteSubList(SEEN_LIST, i, i); 
   }

   // check for any on TARGET_LIST, when so add to SEEN_LIST
   while ((--n) >= 0)
   {
      id = llList2Key(agents, n);
      if (llListFindList(TARGET_LIST, id) >= 0)
      {
         if (llListFindList(SEEN_LIST, id) < 0))
         {
            SEEN_LIST += [id];
            llOwnerSay(... something about id ...)
         }
      }
   } 
}   

edit add:

note how i bracketed (--i)

this forces LSL to decrement 'i' before its used. Lexicon as I mention earlier

Edited by ellestones
Link to comment
Share on other sites

4 hours ago, ellestones said:

i just add on a little note here about ++i vs i++

in LSL it doesn't make any difference.

It does work as it should LSL.

default
{
    state_entry ()
    {
        integer x;
        integer y;
        llOwnerSay ((string) (++x));
        llOwnerSay ((string) (y++));
    }
}

[11:47:06] Object: 1
[11:47:06] Object: 0

Edited by KT Kingsley
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

i just notice that in the last script snip I posted that I never handled the seen list when is no agents on region. The fix is

   list agents = llGetAgentList(AGENT_LIST_REGION, []);
   integer n = llGetListLength(agents);
   if (n == 0)
   {
      SEEN_LIST = [];
      return;
   }

 

Edited by ellestones
dang confuzzle
Link to comment
Share on other sites

OK I thought it was working.. but it still spams the chat and the SEEN_LIST doesn't remove anyone if they leave the sim. hmmm..

Here's the code again, I tidied up a few things which were causing compiling errors

timer()
    {
    REGION_KEYS = llGetAgentList(AGENT_LIST_REGION, []);
    NUM_KEYS = llGetListLength(REGION_KEYS);
	   if (NUM_KEYS == 0)
	   {
		  SEEN_LIST = [];
		  return;
	   }

   key id;
   //llOwnerSay(llDumpList2String(SEEN_LIST, ","));
   // remove a previous seen target if no longer present on region
   integer i = llGetListLength(SEEN_LIST);
   string  this_agent_name;
   string  this_SEEN_name;
   while ((--i) >= 0)
   {
      id = llList2Key(SEEN_LIST, i);
      this_SEEN_name = llKey2Name(id);
      if (llListFindList(REGION_KEYS, [id]) < 0){
         //SEEN_LIST = llDeleteSubList(SEEN_LIST, i, i);
         SEEN_LIST = ListItemDelete(SEEN_LIST, this_SEEN_name); 
         }
   }

   // check for any on TARGET_LIST, when so add to SEEN_LIST
   while ((--NUM_KEYS) >= 0)
   {
      id = llList2Key(REGION_KEYS, NUM_KEYS);
      this_agent_name = llKey2Name(id);
      if (llListFindList(TARGET_LIST, [this_agent_name]) >= 0)
      {
         if (llListFindList(SEEN_LIST, [this_agent_name]) < 0)
         {
            SEEN_LIST += [this_agent_name];
            llOwnerSay(llGetDisplayName(id) + " is in your region of " + llGetRegionName() + "!");
         }
      }
   } 
} 

 

Link to comment
Share on other sites

You are using llKey2Name to get the names of agents who are no longer in the region.  That will not work.  llKey2Name will only work for agents currently in the region.  If you're going to use names instead of UUIDs, you'll have to use llRequestUsername.  That's awkward in this case, since it means taking a detour through a dataserver event, therefore I recommend using UUIDs in your list instead of names.  That's actually much easier, since llGetAgentList is feeding UUIDs to your list REGION_KEYS in the first place.

The rationale behind these and similar functions, BTW, has to do with how responsibility is partitioned among SL servers.  Functions like llKey2Name are designed to query the local server, which keeps track only of agents and assets in the current region that it manages.  Functions that begin with llRequest are designed to query the full databases of agents/assets in SL, so they are directed to them through  a dataserver event.  If you examine the LSL wiki, you'll find several other pairs of functions that work this way.

Edited by Rolig Loon
Link to comment
Share on other sites

2 hours ago, Rolig Loon said:

You are using llKey2Name to get the names of agents who are no longer in the region.  That will not work.  llKey2Name will only work for agents currently in the region. 

Ah, right thanks for the heads up. I was using names as it was a customisable list, but I suppose I could do it another way which I was considering which was to store UUIDs instead, but it just meant the list couldn't just be stored/edited in a note file as easily/human-readable. But that's fine :) 

As for the list (however I create it now), if I store names in it as a variable -- will they be 'remembered' even after logging out if the script doesn't have any reset functions in it?

Link to comment
Share on other sites

Scripts will survive region restarts, being taken to inventory and re-rezzed, being detachd and reattached, and a variety of other things, unless you have put explicit llResetScript statements in them.  As long as a script is not reset (and doesn't stall irreversibly because of a math or memory error), it should retain current values of all variables.  That's small comfort when something does reset your script or when you get a stack/heap error and the thing locks up tight, but it's usually fine for most scripts.  If you are doing anything that is supposed to maintain long-term records, though, it's a good idea to explore options for storing data elsewhere.  I do that whenever it's important to keep track of money transactions, for example.

If you only have a few important bits of data to save (your object's position, for example, or a delimited string of control flags), you can store those in the object's Description field or in the description fields of child prims.  On the more esoteric side, you can store floats as components of vectors (for color, for example) and then apply the vector to a prim property.  Prim properties, in general, persist even if the script that set them is removed.  You can recover values stored in them later with a different script, if necessary.

The memory limits in LSL mean that no script can hold HUGE amounts of data, but one ploy for long-term storage is to create a scripted object that is a data repository.  Create a couple of them and shuffle data around as you update them. It's clumsy, but doable.  It's more common for people to simply send data off world to a Google server or whatever.  That's especially attractive if the data are in large sets or if you will need to do a lot or sorting and number crunching on them.  Now that we have Experiences in SL, KVP has become our best option for storing and retrieving small to mid-sized chunks of data.  Data in KVP can be manipulated very quickly, and are as stable and secure as the LL servers are.  The only real downside to KVP is that your data are only accessible if you are in a region where the Experience that hold them is enabled.  Linden Lab has been talking about the possibility of grid-wide experiences, but they are not available publicly yet, for some obvious security reasons.

EDIT:  I forgot the other obvious possibility.  Scripts cannot write notecards, but they can dump information to chat, where you can copy them manually to a notecard if you are patient and don't mind doing some editing.  Variables that do not change often (start parameters, access lists, etc.) can be read in from notecards whenever you wish and, of course, can be updated manually as needed.  If you look through the Scripting Library here in the forums, you'll find quite a number of scripts for access control or whatever that use that approach.

Edited by Rolig Loon
Link to comment
Share on other sites

5 minutes ago, Rolig Loon said:

Scripts will survive region restarts, being taken to inventory and re-rezzed, being detachd and reattached, and a variety of other things, unless you have put explicit llResetScript statements in them. 

Thanks, that's what I was hoping :) For this script, I'm mainly just thinking in terms of people storing a list of avatar keys/UUIDs now for the purpose of the snippets posted above in this thread. I have considered using the HTTP request functions to update and retrieve data stored on a server via PHP scripts instead to make sure things are more durable and/or persistent.

Link to comment
Share on other sites

Thanks I'll check it out! I don't expect there'd be many more than 20 (at most!) in a list at any one time with what I'm planning, but you never know. Even if I don't hash my list, this part looks like it could be useful to utilise in a modified way maybe:

if ((llStringLength( hashedNames ) * 4.2) > llGetFreeMemory()) // In case memory gets tight
            {
                hashedNames = llGetSubString(hashedNames,3,-1); //Delete the oldest entry in hashedNames
            }

 

Link to comment
Share on other sites

Yes.  Lists are always front-loaded, so if the values at the tail end have outlived their usefulness you can safely lop them off.  Do you really care about who visited your parcel last week, for example?  When I wrote that script, my greatest worry would be that it would run out of memory and lock up.  Hence that particular trigger.

Link to comment
Share on other sites

18 minutes ago, KT Kingsley said:

If you have access to an experience, don't forget key-value pairs as a way of storing data.

Absolutely!  ?

2 hours ago, Rolig Loon said:

Now that we have Experiences in SL, KVP has become our best option for storing and retrieving small to mid-sized chunks of data.  Data in KVP can be manipulated very quickly, and are as stable and secure as the LL servers are.  The only real downside to KVP is that your data are only accessible if you are in a region where the Experience that hold them is enabled.  Linden Lab has been talking about the possibility of grid-wide experiences, but they are not available publicly yet, for some obvious security reasons.

 

Link to comment
Share on other sites

6 hours ago, Rolig Loon said:

Yes.  Lists are always front-loaded, so if the values at the tail end have outlived their usefulness you can safely lop them off.  Do you really care about who visited your parcel last week, for example?  When I wrote that script, my greatest worry would be that it would run out of memory and lock up.  Hence that particular trigger.

Maybe I misunderstand, but LSL adds new items to the end of the existing list.    I've just tested with

default {
	state_entry() {
		list test = ["a","b","c"];
		test +=["d"];
		llOwnerSay(llList2CSV(test));
	}
}

It returns a,b,c,d.   

So if you want to get rid of the oldest entry then it should be test = llDeleteSubList(test,0,0);  or so it seems to me.

(I thought lsl lists were front-loaded, too, until recently, and then I found myself having problems with a script which turned out to be based on my erroneous assumptions about lists -- I don't feel so bad now I realise I'm in good company in my mistake!)

Edited by Innula Zenovka
  • Like 1
Link to comment
Share on other sites

Uh-oh.  I stand corrected. I don't know what I was thinking when I typed that.  You just startled me enough that I went back and checked that gate counter script that I pointed the OP to, just to be sure I didn't make the mistake there.  I didn't.  The script is right (Whew).  I just had a brain failure here today.  Thanks, Innula.

  • Like 1
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 2111 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...