Jump to content

compare lists and delay response based on unixtime


Ruthven Ravenhurst
 Share

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

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

Recommended Posts

I'm working on a sensor object that will target avatars with particles. I hope I can explain the logic I'm looking for. I'm trying to figure out the logic in how to achieve the results explained below.

It will be a repeating sensor. Each time the sensor runs, it will get a list of the detected avatars and the unix time. (I intend for a short range, so I doubt cap of 16 detections would matter). I want to compare that to the list of previously detected avatars.

 

  1. If they're not in the previous list, target them and add them with their unix time. (got that part)
  2. If they are in the previous list, compare the unix time to when they were previously detected.
    1. If it's within a certain amount of time, do nothing.(got that part)
    2. If it is over that time, replace their unix time with the time now, and target them with particles.(got that part)
  3. If they're in the previous list, but not in the current one, remove them from the previous list.(this is the main part I'm having trouble with, and then in which order to do it all in)
  4. If no one is detected, empty the list. (easy enough)
Link to comment
Share on other sites

If nothing else, you can always fall back on the collection of handy extended list functions in the LSL wiki.  In particular, take a look at ListXnotY, which does exactly what you want. 

In this case, however, it's just as easy to write

 

integer idxOld = llListFindList(gOldList,[llDetectedKey(0)]);integer idxCurrent = llListFindList(gCurrentList,[llDetectedKey(0)]);if (~idxOld){    integer OldTime = llList2Integer(gOldList,idxOld+1);    if ( (llGetUnixTime() -  OldTime ) > gTriggerTime )    {        gOldList = llListReplaceList(gOldList,[llGetUnixTime()],idxOld+1,idxOld+1);    }    if (!~idxCurrent)    {        gOldList = llDeleteSubList(gOldList,idxOld,idxOld+1);        gCurrent += [llDetectedKey(0),llGetUnixTime()];    }}

 ETA:  I left out the particles and the no_sensor event (in which you set gOldList = []). but you have those.  :smileywink:

ETA2: I assume that you would put this code in a loop, iterating through all detected avatars, not just looking at llDetectedKey(0), and that you would close the sensor event with gOldList = gCurrentList.

       

 

Link to comment
Share on other sites

ok, so I got that part figured out using 2 different loops to first check the new list against the old one, then check the old one against the new one adding the ones that are present in both to a temp list, then over-writing the old list with the temp list

now, trying to use your whitelist example, I got the access part right, but I'm also trying to do the opposite for the sensor to ignore people based on different ignore levels

 this part won't compile, and I can't seem to find the error.

for(i = 0; i < n; i++)        {            id = llDetectedKey(i);            if((ignorelevel == 7) ||              ((ignorelevel == 6) && ((idx < 0)||(llSameGroup(id) == FALSE))||              ((ignorelevel == 5) && (llSameGroup(id) == FALSE)||              ((ignorelevel == 4) && (idx < 0))||              ((ignorelevel == 3) && ((id != llGetOwner())||(idx < 0)||(llSameGroup(id) == FALSE))||              ((ignorelevel == 2) && (id != llGetOwner())||(llSameGroup(id) == FALSE)) ||              ((ignorelevel == 1) && ((id != llGetOwner())||(idx < 0)))              {gCurrent += [id];}        }

and the menu for that setting so you can see clearer what each of those levels mean

ignorelevelmenu(key id, integer chan){   llDialog(id, "Ignore Levels    \nOnly Owner Can Change This    \nCurrent: " + (string)ignorelevel +    "\n\n0 = Owner Only    \n1 = Owner + Ignore List    \n2 = Owner + Group(anyone in same group as object)    \n3 = Owner + Ignore List + Group    \n4 = Ignore List Only(does not ignore owner)    \n5 = Group(anyone in same group as object, does not ignore owner if they have different tag on)    \n6 = Ignore List + Group(see levels 4 and 5)    \n7 = Ignore Nobody (doesn't ignore anyone, including the owner)",    ["Main", "Load", "Done",    "(0)","(1)","(2)","(3)","(4)","(5)","(6)","(7)"],chan);} 
Link to comment
Share on other sites

nevermind, just had to go back and break down each line of the test again to find the missing parentheses, colored below

 

for(i = 0; i < n; i++)        {            id = llDetectedKey(i);            if((ignorelevel == 7)||            ((ignorelevel == 6) &&  ((idx < 0)||(llSameGroup(id) == FALSE))||            ((ignorelevel == 5) && (llSameGroup(id) == FALSE)||            ((ignorelevel == 4) && (idx < 0))||            ((ignorelevel == 3) && ((id != llGetOwner())||(idx < 0)||(llSameGroup(id) == FALSE))||            ((ignorelevel == 2) && (id != llGetOwner())||(llSameGroup(id) == FALSE))||            ((ignorelevel == 1) && ((id != llGetOwner())||(idx < 0)))            {gCurrent += [id];}        }
Link to comment
Share on other sites


Ruthven Willenov wrote:

ok, so I got that part figured out using 2 different loops to first check the new list against the old one, then check the old one against the new one adding the ones that are present in both to a temp list, then over-writing the old list with the temp list

The code snippet I wrote above should have done it, but maybe I missed something.  I'm not sure why it was necessary to do two loops and involve a temp list. If it works, though, I'm pleased. Congratulations. :smileywink:

Link to comment
Share on other sites

just a suggestion on how this is coded

LSL always evaluates every condition in a IF statement. Unlike other languages which exit the IF on the first condition that is false

can gain some runtime efficiencies by removing from complex IF evaluations as many API calls ( llGetOwner() and llSameGroup() ) and logic decisions (idx < 0) as is possible

example:

 

integer c1 = (integer)( idx < 0 );

for (i = 0; i < n; i++){ id = llDetectedKey(i); integer c2 = (integer)( id != llGetOwner() ); integer c3 = (integer)( llSameGroup(id) == FALSE ); if ...
( (ignorelevel == 6) && (c1 || c3) ) ... ( (ignorelevel == 3) && (c1 || c2 || c3) ) ...

 

eta ps

i agree with Rolig also. Should be able to code this in one pass

altho can understand that you maybe likely just working on the logic of your script at this time. With optimisations to follow once the logics all been worked out

Link to comment
Share on other sites

I'm looking back at this again. Before releasing it, silly me didn't test it with more than one avatar in range. It was working fine when I was the only one in range. But now that I saw someone else step into range with me, I realized something was wrong, and probably to do with the loop. It wasn't delaying the response with everyone in range, even though we all remained in range. I was running the first loop through the gCurrent list to see if they are present in the old list, and responding if the time is over the delay. If not in the old list, they are targeted, and added to the old list. I was then running the second loop through the old list, creating a temp list, then at the end of the loop, the old list gets written over with the temp list (with avatars not in range being ommited)

Maybe I misunderstood your intent with what you suggested, I guess you meant to only run one loop through the oldlist, adding the avatars that were not already there, and overwriting the unix time of the avatars that were there but over the delay time.

but then you mentioned closing the sensor with, gOldlist = gCurrentList, which didn't make sense to me, because the work being done in the tests were manipulating the old list, but replacing it with the current list would overwrite the unixtimes that were previously recorded (the sensor repeats faster than the delay time)

here's what I have for that section of the script. I'll see if you have any suggestions for cleaning it up. In the mean-time, i'll work on it some more myself to make one loop

comparedlists(list gCurrent)//fed by sensor event which creates a list of avatar id's (but not unix time){    integer i;    integer len = llGetListLength(gCurrent);    for(i = 0; i < len; i++)//run a loop through the list fed into function and see if they are in the old list    {        //llParticleSystem([]);        key id = llList2Key(gCurrent,i);        integer idxOld = llListFindList(gOldList,[id]);        integer time = llGetUnixTime();        if(~idxOld)        {            integer OldTime = llList2Integer(gOldList,idxOld+1);            if((time - OldTime) > gTriggerTime)            {                string name = llGetDisplayName(id);                gOldList = llListReplaceList(gOldList,                [time],idxOld+1,idxOld+1);                target(id);            }        }        else if(!~idxOld)        {            gOldList += [id, time];            target(id);        }    }    len = llGetListLength(gOldList);    list temp;    for(i = 0; i < len; i++)//after running through gCurrent, run a loop through the old list to see who was/wasn't sensed again. If they are, add them to the temp list.    {        i++;        key id = llList2Key(gOldList,i);        integer idxCurrent = llListFindList(gCurrent,[id]);        if(~idxCurrent)        {            temp += llList2List(gOldList,idxCurrent,idxCurrent+1);        }    }    gOldList = temp;//after running the loop through the old list, overwrite it with the temp list so as not to preserve avatars that have left the range}
Link to comment
Share on other sites

integer idxOld = llListFindList(gOldList,[llDetectedKey(0)]);

integer idxCurrent = llListFindList(gCurrentList,[llDetectedKey(0)]);

if (~idxOld)

{

integer OldTime = llList2Integer(gOldList,idxOld+1);

if ( (llGetUnixTime() - OldTime ) > gTriggerTime )

{

gOldList = llListReplaceList(gOldList,[llGetUnixTime()],idxOld+1,idxOld+1);

}

if (!~idxCurrent)

{

gOldList = llDeleteSubList(gOldList,idxOld,idxOld+1);//this is the part that I'm concerned about. Would deleting the sublist from the list the loop is looking through throw off the index if each pass of the loop?

gCurrent += [llDetectedKey(0),llGetUnixTime()];

}

}

Link to comment
Share on other sites

I've got this, but it seems to get caught up on targeting only one avatar

comparelists(list gCurrent){    integer test = 1;    integer i = 0;    integer len = llGetListLength(gOldList);    integer time = llGetUnixTime();    for(i = 0; i < len; i += 2)    {        key id = llList2Key(gOldList,i);        if(id)        {            integer idx = llListFindList(gCurrent,[id]);            integer oldtime = llList2Integer(gOldList,idx+1);            if(~idx)            {                if((time - oldtime) >= gTriggerTime)                {                    gOldList = llListReplaceList(gOldList, [time], i+1, i+1);                    target(id);                }                else                {                    llParticleSystem([]);                }                            }            else            {                gOldList = llDeleteSubList(gOldList,i,i+1);            }        }    }        }
Link to comment
Share on other sites

can think about updating the list in the sensor event

example:

 

list gCurrent;sensor(integer detected){   integer t = llGetUnixTime(); // all detected avatars will have the same time   do   {        --detected;        key k = llDetectedKey(detected);	integer i = llListFindList(gCurrent, [k]);	if (~i)  // is in current list so update time           gCurrent = llListReplaceList(gCurrent, [t], i+1, i+1);         else     // not in current list so add new           gCurrent += [k, t];   } while (detected);}

  eta: correct listreplace

i really do have to stop writing codes off the top of my head (:

Link to comment
Share on other sites

Ah yeah, I meant to add that I did do that already. When building the gCurrent list, I first check if they're in the old list, add them to the old list if they aren't, and update them if it's passed the delay Time

Then I run a loop through the old list comparing it to the new list. I originally had 3 loops. One building the current list, one running through the current list and adding/updating them in the old list, and one running through the old list and removing them if they weren't still present.

 

I put in a debug to say the name of the avatar at the targeting point, it did say everyone's name, I guess the loop just ran too fast to allow it to actually target them? I put in a sleep of 0.25 on each pass of the loop, that seemed to help, but still too fast. I think instead of targeting everyone when they come up, I'll choose a random one from the list at the end of the loop. But how to I make sure it chooses an avatar index and not the UNIX time attached to them? The avatars would be an even number and the times would be odd

Link to comment
Share on other sites

Unless I have missed something totally (always a possibility), I don't see why you need to do anything more complicated than this .....

sensor(integer num){    while (num)    {        --num;        integer idxOld = llListFindList(gOldList,[llDetectedKey(num)]);        integer idxCurrent = llListFindList(gCurrentList,[llDetectedKey(num)]);        if (~idxOld)    // If avatar is in the old list ....        {            integer OldTime = llList2Integer(gOldList,idxOld+1);            if ( (llGetUnixTime() -  OldTime ) > gTriggerTime )   // Is the old time really old?            {                gOldList = llListReplaceList(gOldList,[llGetUnixTime()],idxOld+1,idxOld+1);    // Update the time            }            if (!~idxCurrent)   // Is the av NOT in the current list?            {                gOldList = llDeleteSubList(gOldList,idxOld,idxOld+1);  // Remove from the old list                gCurrent += [llDetectedKey(0),llGetUnixTime()];  // Add to the new list, along with the time            }        }    }    // Go ahead and look at the next detected av, unless you've looked at them all now    gOld = gCurrent;   // gCurrent now becomes gOldList, ready for the next sensor scan}no_sensor()    // There is no gCurrent list becasue nobody was detected.{    gOld = [];    // So wipe out the gOldList.}

You simply loop through, looking at each detected av and asking "Is this person on the old list?" and "If so, What do I do with him?"  At the top of the loop each time, you are asking not only "Is the Person on the list?" but also finding out exactly where in the list the person is.  That what idxOld and idxCurrent are -- positions.  Those values update each time you do an iteration through the detected avs, so the fact that you may have deleted one in an earlier iteration is irrelevant.  Also, once you have the position of an av in one of the lists, you also automatically know the position of the time for that av (idx + 1), so you can update it as necessary.  The gCurrent list grows by adding a Unix time every time you find an av in the list, until, at the end of the process, each current av has a time.  At that point, you no longer need gOldList, so you replace it with gCurrent and get ready for the next sensor scan.

As I said, I may have overlooked some key point by not focusing as closely on the problem as I ought to, but the logic seems fairly straightforward.

 

Link to comment
Share on other sites

thanks, I was basing my loops on an incrementing idx, which would cause it to ignore the last list elements depending on how many are removed.

now I realize it makes more sense to decrement by starting at the end, and move towards the beginning. And if I assume correctly, decrementing at the beginning of each pass of the loop won't cause it to miss idx 0 (the first element of the list), right?

you're referencing gCurrentList, but where are you declaring it? is that a global too? and that would need to be cleared at the beginning of each sensor event then?

I have to run to work in real life, but "Ah ha" moment when realizing it was the decrementing loop you were intending.

Either way, since I don't have enough avatars around to test more than 2 avatars, I added (temporarily) PASSIVE to the sensor, and test whether the detected type is an agent, or an object with "test object" in its name. Instead of targetting everyone (after passing the delay test), i'm targeting at random, but how do I make sure it's an even number?

here's what I did, but it's never targeting the first element, 0

integer templen = llGetListLength(temp1)/2;//half the length    integer tempidx = (integer)llFrand(templen)*2;//double the index to make sure it's an even number (shouldn't this still be 0 if it's 0 before doubling it?

 

Link to comment
Share on other sites

Heh... You're right.  I should have decrimented num at the end of the loop, not at the start. You should reach num = 0 then.  Sorry about that.

Oh, and I'm following a common practice of identifying glocal variables with the "g" prefix.  When I am more careful, I also use a prefix of "i", "s", "f", "k", "v", "r", or "l" to identify the variable type.  It can be easy to get confused about what your variables are when they are scattered over several hundred lines of code, so a helpful label can be important.

Link to comment
Share on other sites


Ruthven Willenov wrote:

how do I make sure it's an even number?

here's what I did, but it's never targeting the first element, 0
integer templen = llGetListLength(temp1)/2;//half the length    integer tempidx = (integer)llFrand(templen)*2;//double the index to make sure it's an even number (shouldn't this still be 0 if it's 0 before doubling it?

 

if you not getting 0 then is not this code where the bug is

rnd (2 / 2)  = [0] * 2 = [0]

rnd (4 / 2) = [0,1] * 2 = [0,2]

rnd(6 / 2) = [0,1,2] * 2 = [0, 2, 4]

 

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 2823 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...