2012-04-30

Today I tackled WEB-4587

I've been following WEB-4587 for a while now, but hadn't noticed anything wrong with any of the Z&A listings. Admittedly, we've been rather busy with other things so neither Zardia nor myself had looked at every single one of our MP pages.

We're now slowly migrating from our magic boxes to direct delivery and, as we migrate items, we tweak the listings a little. While doing such a set of tweaks today I noticed that we had acquired some broken listings. At this point I went through and checked every single one. Turns out we had five broken ones. Five of them had the wrong image. Five of them had lost their item associations. One of them had a different category assigned and one of them had become unlisted.


Thankfully we have a copy of every single product vendor image so fixing the images was easy enough (assuming the fix "sticks" -- I'll be keeping a close eye on things). While doing the fix I noticed that, as per some of the comments on WEB-4587, all of the products that had a problem were in the 14xxxxx range of product IDs. However, not every product we had in the 14xxxxx had the problem. Which seemed odd.

But there was something about this collection of products that felt familiar.

Thankfully Z&A has had a blog for quite a while now and we always write about new releases on there. Generally we release products in batches so a group of new products, released on the same day, will get blogged on the same day. I went back to the start of the blog and, just a few entries in, I found this entry. Sure enough, that's the five products that ended up with broken listings. The previous product release looks fine, as does the subsequent set of product release.

So it looks like the problem, for us at least, stems from that day, or a period of time that takes in that day. Remind me again when the XSteetSL to Marketplace migration period was...

It'll be interesting to see if this has fixed it. I'll be keeping an eye on those listings now.

2012-04-26

New phishing scam doing the rounds?

Twice this week I've had a message sent to me from an object, while I'm offline. The message looks like this:

The object 'Object' has sent you a message from Second Life:
Hi Everyone! Receive 2000L just for signing up here --> http://bit.ly/[URL REMOVED]

 = Object is owned by [NAME REMOVED] Resident
 = http://slurl.com/secondlife/[REGION REMOVED]/45/119/3000

In both cases the object has been up at 3000m above a Linden Home that has public build turned on. Both times, when I've gone over there, there's been a day-old avi at the location, along with a prim or two (curiously devoid of any scripts -- which would suggest that whatever script was used to send out the message has been removed again).


I've not followed the URL but I'm guessing this is a variation on the phishing scams that seem pretty rife in Second Life at the moment.

Anyone else seeing this? While I've seen in-group phishing attempts plenty of times of late this is a new one on me. It's also a great illustration as to why you should never have public build turn on on your plots (unless you're a sandbox, or have a good auto-return, etc...).

2012-04-10

Simple security system

I've had this one in my inventory for a while, and it's one I do use to help shepherd nosy people out of the Z&A workshop. I'm not sure why I've not posted it here yet.

As with many of the scripts I've posted on my blog, this was done as an experiment, just to get to know how these things work. There are, of course, some very sophisticated security systems out there; I imagine there's even some really nice free ones too. But I wanted to have a go at writing a simple one myself and what you see below is the result.

To use it, all you need to do is create a prim and drop the script inside. Create a notecard called "Security" and also drop that in the prim. The notecard is what configures the security script. There are two types of setting for the notecard: "Owner=" and "Access=". The owner setting (one per line) lets you set up the list of people who can turn the security system on and off (by touching the prim). The access setting lets you set up the list of people who simply have have access. People who aren't on the owner or access list will be sent home when they come in range of the prim.

You can also set the rage (in meters) with "range=" and the frequency of the scans (in seconds) with "frequency=". Note that only avatars over land you own will be ejected -- this means it's safe to have a range that might extend out of parts of the plot being covered.

Also note that if the land is owned by a group, the object that contains the script will also have to be deeded to the group to work.

Here's an example notecard:

Owner = Antony Fairport
Owner = Zardia Avindar
Access = Zanda Slacker

And here's the script itself:

//////////////////////////////////////////////////////////////////////
// Simple security system.
// By Antony Fairport.
//
// Revision history.
// =================
//
// 2011-06-27
// Initial version.
//////////////////////////////////////////////////////////////////////
// Constants.
string CONFIG_FILE = "Security";
//////////////////////////////////////////////////////////////////////
// Global configuration.
float g_nRange; // Range to scan.
float g_nFrequency; // Frequency of checks.
list g_lOwners; // Owner list.
list g_lAccess; // Access list.
//////////////////////////////////////////////////////////////////////
// Config reading globals.
key g_keyConfig;
integer g_iConfig;
//////////////////////////////////////////////////////////////////////
// Is the given avatar on the given list?
integer AvatarInList( string sAvatar, list l )
{
return ~llListFindList( l, [ sAvatar ] );
}
//////////////////////////////////////////////////////////////////////
// Is the given avatar an owner?
integer IsOwner( string sName )
{
return AvatarInList( sName, g_lOwners );
}
//////////////////////////////////////////////////////////////////////
// Does the given avatar have access?
integer HasAccess( string sName )
{
return AvatarInList( sName, g_lAccess );
}
//////////////////////////////////////////////////////////////////////
// Eject the intruder.
Eject( key keyIntruder )
{
// Tell the intruder what we're doing to them.
llInstantMessage( keyIntruder, "You do not have access to this location. Sending you home." );
// And send them home.
llTeleportAgentHome( keyIntruder );
}
//////////////////////////////////////////////////////////////////////
// Default state.
default
{
//////////////////////////////////////////////////////////////////
// State entry.
state_entry()
{
// Read the configuration.
state Configure;
}
}
//////////////////////////////////////////////////////////////////////
// Configuration state.
state Configure
{
//////////////////////////////////////////////////////////////////
// State entry.
state_entry()
{
// Let the user know what's happening.
llWhisper( 0, "Reading configuration." );
// Clear out the config.
g_nRange = 96.0;
g_nFrequency = 5.0;
g_lOwners = [];
g_lAccess = [];
// If it looks like we've got config...
if ( llGetInventoryType( CONFIG_FILE ) == INVENTORY_NOTECARD )
{
// ...start reading it.
g_keyConfig = llGetNotecardLine( CONFIG_FILE, g_iConfig = 0 );
}
else
{
// Tell the user things aren't good.
llWhisper( 0, "No security configuration found. Turning off." );
}
}
//////////////////////////////////////////////////////////////////
// Handle data server responses.
dataserver( key queryid, string data )
{
// If this is our query...
if ( queryid == g_keyConfig )
{
// If this isn't the end of the file...
if ( data != EOF )
{
// If the line doesn't look like it's a comment, and it isn't empty...
if ( ( llGetSubString( data, 0, 0 ) != "#" ) && ( llStringTrim( data, STRING_TRIM ) != "" ) )
{
// Split the line into a list.
list sLineData = llParseString2List( data, [ "=" ], [] );
// Pull out the setting name.
string sSetting = llToLower( llStringTrim( llList2String( sLineData, 0 ), STRING_TRIM ) );
// Pull out the setting value.
string sValue = llStringTrim( llList2String( sLineData, 1 ), STRING_TRIM );
// Owner?
if ( sSetting == "owner" )
{
g_lOwners += [ sValue ];
g_lAccess += [ sValue ];
llWhisper( 0, "Owner: " + sValue );
}
// Access list?
else if ( sSetting == "access" )
{
g_lAccess += [ sValue ];
llWhisper( 0, "Access: " + sValue );
}
// Range?
else if ( sSetting == "range" )
{
g_nRange = (float) sValue;
llWhisper( 0, "Range: " + sValue );
}
// Frequency?
else if ( sSetting == "frequency" )
{
g_nFrequency = (float) sValue;
llWhisper( 0, "Frequency: " + sValue );
}
}
// Read the next line.
g_keyConfig = llGetNotecardLine( CONFIG_FILE, ++g_iConfig );
}
else
{
// Config read. Get protecting.
state Protect;
}
}
}
}
//////////////////////////////////////////////////////////////////////
// Paused state -- does nother but waits to be activated again.
state Paused
{
//////////////////////////////////////////////////////////////////
// State entry.
state_entry()
{
// Tell the user we're paused.
llWhisper( 0, "Security protection is paused." );
// And add some hover text too.
llSetText( "Security system paused.", < 1.0, 0.0, 0.0 >, 1.0 );
}
//////////////////////////////////////////////////////////////////
// Handle a touch.
touch_end( integer num_detected )
{
// If it's an owner who touched us...
if ( IsOwner( llDetectedName( 0 ) ) )
{
// Jump back to the protect state.
state Protect;
}
}
//////////////////////////////////////////////////////////////////
// Detect change.
changed( integer change )
{
// If the inventory has changed...
if ( change & CHANGED_INVENTORY )
{
// ...start over.
state Configure;
}
}
}
//////////////////////////////////////////////////////////////////////
// Protect state -- does the scanning and ejecting.
state Protect
{
//////////////////////////////////////////////////////////////////
// State entry.
state_entry()
{
// Let the user know what's happening.
llWhisper( 0, "Security protection is enabled." );
// Remove any hover text.
llSetText( "", < 1.0, 1.0, 1.0 >, 0.0 );
// Start the sensor.
llSensorRepeat( "", "", AGENT_BY_LEGACY_NAME, g_nRange, PI, g_nFrequency );
}
//////////////////////////////////////////////////////////////////
// State exit.
state_exit()
{
// Make sure the sensor is removed.
llSensorRemove();
}
//////////////////////////////////////////////////////////////////
// Handle the sensor result.
sensor( integer num_detected )
{
integer i;
// For each detected avatar...
for ( i = 0; i < num_detected; i++ )
{
// If they are over our land...
if ( llOverMyLand( llDetectedKey( i ) ) )
{
// If they're not on the access list...
if ( !HasAccess( llDetectedName( i ) ) )
{
// Eject them.
Eject( llDetectedKey( i ) );
}
}
}
}
//////////////////////////////////////////////////////////////////
// Handle a touch.
touch_end( integer num_detected )
{
// If it's an owner who touched us...
if ( IsOwner( llDetectedName( 0 ) ) )
{
// Jump to the paused state.
state Paused;
}
}
//////////////////////////////////////////////////////////////////
// Detect change.
changed( integer change )
{
// If the inventory has changed...
if ( change & CHANGED_INVENTORY )
{
// ...start over.
state Configure;
}
}
}

Play sounds when walking (updated)

I've just uploaded an update to the simple script I wrote that plays a sound when you walk. This change takes running to be the same as walking. So, no matter if you're running or walking, and no matter how often you transition between the two states, the sound keeps on playing. Only when you're neither running nor walking does the sound stop.

Of course, as before, it's not a very sophisticated approach -- doubtless most people would have a need for different sounds for different movement speeds -- but it works for me as I don't have a visible run. I've got my AO configured so I'm either walking or walking a bit faster.

Here's the updated code:

//////////////////////////////////////////////////////////////////////
// Z&A Simple Walking Sound -- Play a sound when an avatar walks
// By Antony Fairport.
//
// Written as an experiment in reacting to llGetAnimation() return
// values (and to give me a barefoot padding sound when I'm not wearing
// any boots or shoes).
//
// I don't claim this is the best way, most efficient way, or most elegent
// way of doing this. But it's a way of doing it.
//
// Revision history:
// -----------------
//
// 2012-04-10
// Changed the code so that running is considered to be the same as
// walking. Before running was just ignored and no sound was played.
//
// 2012-03-26
// Initial revision.
//////////////////////////////////////////////////////////////////////
// Globals.
key g_kOwner;
string g_sWalking;
string g_sLastAnimation;
//////////////////////////////////////////////////////////////////////
// Default state.
default
{
//////////////////////////////////////////////////////////////////
// State entry.
state_entry()
{
// Grab the owner's key. Might as well hold it in a global
// vs call llGetOwner() evert single timer tick.
g_kOwner = llGetOwner();
// Grab the sound.
g_sWalking = llGetInventoryName( INVENTORY_SOUND, 0 );
// If we've got a sound...
if ( g_sWalking != "" )
{
// ...kick off the timer.
llSetTimerEvent( 0.2 );
}
}
//////////////////////////////////////////////////////////////////
// React to a timer event.
timer()
{
// Work out what we're doing right now.
string sAnimation = llGetAnimation( g_kOwner );
// Walking or running?
if ( ( sAnimation == "Walking" ) || ( sAnimation == "Running" ) )
{
// Wasn't walking or running last we checked?
if ( ( g_sLastAnimation != "Walking" ) && ( g_sLastAnimation != "Running" ) )
{
// Start the sound.
llLoopSound( g_sWalking, 1.0 );
}
}
// Not walking or running now but was a moment ago?
else if ( ( g_sLastAnimation == "Walking" ) || ( g_sLastAnimation == "Running" ) )
{
// Stop the sound.
llStopSound();
}
// Remember what we were doing.
g_sLastAnimation = sAnimation;
}
//////////////////////////////////////////////////////////////////
// React to changes.
changed( integer change )
{
// Has the owner, or inventory, changed?
if ( ( change & CHANGED_OWNER ) || ( change & CHANGED_INVENTORY ) )
{
// Yes. Reset the script.
llResetScript();
}
}
}

2012-04-08

Season Lag

A little earlier I was attempting to get over to The Velvet Thorn for a live performance. SL seemed to have other ideas, which was a bit annoying. Generally I'm kind of reclusive, enjoying tinkering with stuff in the Z&A workshop, but after having worked a good few hours today on something I decided I should make the effort to stop being so damn anti-social and actually go do a thing.

Live music sounded like just the thing.

But, like I said, SL seemed to have other plans for me. The first three attempts to get to the venue resulted in either rezzing problems or just a general failure to properly connect to the sim (or something like that -- you know the sort of thing, money turns to ??? in the viewer, parcel details show the last location, profiles don't load, etc...). Finally, with the help of Miss Eve to guide me in (no, really, things were so slow for me she sort of helped talk me to a spot I could safely sit on), I got there. And a bloody good performance it was too.

Miss Eve did comment that it was probably the infamous SL Sunday lag, possibly made worse by more people being online because, in many parts of the world, it's a holiday weekend. That seemed like a reasonable explanation.

Only after the gig, when I got home, did I realise just how bad an impact this Sunday was having on SL. Stood outside my shop was the Easter Bunny, apparently lagging back to Christmas with their costume. ;-)


In case you're wondering, yes, that is one of our bunny hoods. Not sure about the glow though. We don't make them glow.

Scripting away...

In the workshop, working away on a brand new bunch of scripts. This might or might not end up being a Z&A product, it depends how happy I am with how it turns out and if I think it'll make sense.


You can ignore the prim itself -- that's no clue at all. It's just a little more interesting to look at than a wooden cube (not that a screen full of script windows gives much of a view of the world around me).

2012-04-04

Not a new shop...

...but, instead, playing with a set design for a shoot to show off the build for a hunt that's starting this weekend.