Difference between revisions of "Object to Data v1.4"

From Second Life Wiki
Jump to navigation Jump to search
m (Updated Float2Hex (the serialize code looks like it's borrowing code I wrote, i don't mind but a little credit would have been nice).)
m (Object to Data v1.0 moved to Object to Data v1.1: Version Change.)
(No difference)

Revision as of 12:49, 19 August 2007

(http://www.gnu.org/copyleft/fdl.html) in the spirit of which this script is GPL'd. Copyright (C) 2007 Xaviar Czervik

(This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.


I took the list serialize list code from someone, and it was posted on a wiki somewhere... (If this was you, please add your name so I can credit you).

I'm not one for writing documentation, so I apologize in advance for the confusion of the following.


Here is a map of the objects:

ObjectMakerFromNotecard
|
|---Setup (Notecard)
|---Main (Script)
|---Replicated (Object)
|---|
|---|---Main (Script)


ObjectDataSaver
|
|---Main (Script)
|---ListenToReplicate(Object)
|---|
|---|---Main (Script)


The following code goes in an object called Replicated. It is needed for the ObjectMakerFromNotecard.

string SERIALIZER_DELIMITER = "$!#"; 
integer num = 0;

list unserializeList(string serialized_data) {
    // TODO: add some checking in-case we encounter a poorly formed serialization
    //       consider using the same mem-packing list pushing technique used above
    //       (want to run performace tests first)
    list result = [];
    list t;
    list l = llParseStringKeepNulls(serialized_data, [SERIALIZER_DELIMITER], []);

    string item;
    integer i = (l != []);//This is a hack, it gets list lenght.
    integer type = 0;
    do
    {
        if((type = (integer)(item = llList2String(l, (i=~-i)))))
        {//Little error checking (also takes care of null strings).
            integer p = llSubStringIndex(item, ",");
            item = llDeleteSubString(item, 0, p);
            // How about those switch statements, Lindens???
            if (TYPE_INTEGER == type)
                t = [(integer)item];
            else if (TYPE_FLOAT == type)
                t = [(float)item];
            else if (TYPE_STRING == type)
                t = [item];
            else if (TYPE_KEY == type)
                t = [(key)item];
            else
            {
                if (TYPE_ROTATION ^ type)// if (TYPE_VECTOR == type)
                    t = [(vector)("<" + item + ">")];
                else// if (TYPE_ROTATION == type)
                    t = [(rotation)("<" + item + ">")];
            }
            //when dealing with very long lists it might be advantagous to use the commented out line instead.
            //result = [result = t] + result;
            result = t + result;

        }
    }while(i);
    return result;
}

default {
    on_rez(integer i) {
        llListen(i, "", "", "");
    }
    listen(integer i, string n, key id, string m) {
        if (llGetOwnerKey(id) == llGetOwner()) {
            list l = unserializeList(m);
            num++;
            if (num == 1) {
                list l2 = [PRIM_TYPE] + l;
                llSetPrimitiveParams(l2);
            }
            if (num == 2) {
                llSetColor((vector)m, ALL_SIDES);
            }
            if (num == 3) {
                llSetRot((rotation)m);
            }
            if (num == 4) {
                llSetScale((vector)m);
            }
            if (num == 5) {
                llSetTexture(m, ALL_SIDES);
            }
            if (num == 6) {
                vector t = (vector)m;
                llScaleTexture(t.x, t.y, ALL_SIDES);
            }
            if (num == 7) {
                vector t = (vector)m;
                llOffsetTexture(t.x, t.y, ALL_SIDES);
            }
            if (num == 8) {
                llRotateTexture((integer)m, ALL_SIDES);
            }
            if (num == 9) {
                list l2 = [PRIM_BUMP_SHINY, ALL_SIDES] + l;
                llSetPrimitiveParams(l2);
            }
            if (num == 10) {
                while (llVecDist(llGetPos(), (vector)m) > .1) llSetPos((vector)m);
                llRemoveInventory(llGetScriptName());
            }
            
            
            
        }
    }
}


The following object is called ObjectMakerFromNotecard. It has the following script and a note called Setup. (The data for setup will be given later).

key gSetupQueryId;
integer gSetupNotecardLine;
string  gSetupNotecardName = "Setup";


readSettingsNotecard() {
   gSetupNotecardLine = 0;
   gSetupQueryId = llGetNotecardLine(gSetupNotecardName,gSetupNotecardLine); 
}


default {
    state_entry() {
         readSettingsNotecard();
    }
    dataserver(key queryId, string data) {
        if(queryId == gSetupQueryId)  {
            if(data != EOF) {
                integer f = (integer)llFrand(10000) - 10000;
                llRezObject("Replicated", llGetPos() + <0,0,2>, <0,0,0>, <0,0,0,0>, f);
                list lis = llParseString2List(data, ["-=!!=-"], []);
                integer i = 0;
                while (i < llGetListLength(lis)) {
                    llSay(f, llList2String(lis, i));
                    i++;
                }
                gSetupQueryId = llGetNotecardLine(gSetupNotecardName,++gSetupNotecardLine); 
            } else {
                state running;   
            }
        }
    }        
    changed(integer change) {
        if (change&CHANGED_INVENTORY)
            llResetScript();
    }
}

state running {
    changed(integer change) {
        if (change&CHANGED_INVENTORY)
            llResetScript();
    }
}


The following object is called ListenToReplicate. Place it in ObjectDataSaver.

integer num = 0;
list total = [];

default {
    on_rez(integer i) {
        llListen(i, "", "", "");
    }
    listen(integer i, string n, key id, string m) {
        if (llGetOwnerKey(id) == llGetOwner()) {
            num++;
            total += m;
            if (num == 10) {
                llShout(-10, llDumpList2String(total, "-=!!=-"));
                llDie();
            }
            
            
        }
    }
}


The following code is for the ObjectDataSaver. It will also have and object called ListenToReplicate.

list data;

default {
    state_entry() {
        llListen(-5, "", "", "");
        llListen(-10, "", "", "");
        llListen(1, "", "", "");
    }
    listen(integer i, string n, key id, string m) {
        if (i == -5)
            llRezObject("ListenToReplicate", llGetPos() + <0,0,2>, <0,0,0>, <0,0,0,0>, (integer)m);
        if (i == -10)
            data += m;
        if (i == 1) {
            integer i = 0;
            while (i < llGetListLength(data)) {
                llOwnerSay("\n" + llList2String(data, i));
                i++;
            }
        }
    }
}


After you created the tree, drag the ObjectDataSaver down. Then place the following script in every prim of the object you want to save. Lastly, type any message on ch 1 and it will tell you the data. Then copy and paste it into a note called Setup and place it in the ObjectMakerFromNotecard. (WARNING: EACH PRIM IS DELETED ONCE THE SCRIPT RUNS!!)

string SERIALIZER_DELIMITER = "$!#";

string hexc="0123456789ABCDEF";//faster

string Float2Hex(float input)
{// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html
    if((integer)input != input)//LL screwed up hex integers support in rotation & vector string typecasting
    {//this also keeps zero from hanging the zero stripper.
        float unsigned = llFabs(input);//logs don't work on negatives.
        integer exponent = llFloor(llLog(unsigned) / 0.69314718055994530941723212145818);//floor(log2(b)) + rounding error
        integer mantissa = (integer)((unsigned / (float)("0x1p"+(string)(exponent -= (exponent == 128)))) * 0x1000000);//shift up into integer range
        integer index = (integer)(llLog(mantissa & -mantissa) / 0.69314718055994530941723212145818);//index of first 'on' bit
        string str = "p" + (string)((exponent += index) - 24);
        mantissa = mantissa >> index;
        do
            str = llGetSubString(hexc,15&mantissa,15&mantissa) + str;
        while(mantissa = mantissa >> 4);
        if(input < 0)
            return "-0x" + str;
        return "0x" + str;
    }//integers pack well so anything that qualifies as an integer we dump as such, supports netative zero
    return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
}

string serializeList(list l) {
    integer i = (l != []);//This is a hack, it gets list lenght.
    if(i)
    {
        string serialized_data = "";
        integer type = 0;
        string result;
        {@loop;
            // this custom loop is about as fast as a while loop.
            // we build the string backwords for memory reasons.
            // My kingdom for select statements....

            if (TYPE_FLOAT == (type = llGetListEntryType(l, (i=~-i))))

                // floats get extra love
                result = Float2Hex(llList2Float(l, i));
            else if (TYPE_VECTOR == type) {
                vector v = llList2Vector(l, i);
                result = Float2Hex(v.x) + "," + Float2Hex(v.y) + "," + Float2Hex(v.z);
            } else  if (TYPE_ROTATION == type) {
                rotation r = llList2Rot(l, i);
                result = Float2Hex(r.x) + "," + Float2Hex(r.y) + "," + Float2Hex(r.z) + "," + Float2Hex(r.s);
            } else //if ((TYPE_INTEGER == type) || (TYPE_STRING ==  type) || (TYPE_KEY == type))
                result = llList2String(l, i);// integers, strings and keys required no voodoo

            if(i)
            {
                //This came to me after reverse engeneering LSL bytecode, the realization that LSL memory management sucks.
                serialized_data = SERIALIZER_DELIMITER + (string)type + (serialized_data = result = ",") + result + serialized_data;
                jump loop;
            }
        }
        return (string)type + (serialized_data = result = ",") + result + serialized_data;
    }
    return "";
}

Say(integer i, string m) {
    llSleep(.15);
    llShout(i, m);
}

default {
    state_entry() {
        integer f = (integer)llFrand(10000) + 1691507124;
        llShout(-5, (string)f);
        llSleep(1);
        list total;
        string tot;
        total = llGetPrimitiveParams([PRIM_TYPE]);
        tot = serializeList(total);
        Say(f, tot);
        Say(f, (string)llGetColor(ALL_SIDES));
        Say(f, (string)llGetRot());
        Say(f, (string)llGetScale());
        Say(f, (string)llGetTexture(ALL_SIDES));
        Say(f, (string)llGetTextureScale(ALL_SIDES));
        Say(f, (string)llGetTextureOffset(ALL_SIDES));
        Say(f, (string)llGetTextureRot(ALL_SIDES));
        total = llGetPrimitiveParams([PRIM_BUMP_SHINY, ALL_SIDES]);
        total = [llList2Integer(total, 0), llList2Integer(total, 1)];
        tot = serializeList(total);
        Say(f, tot);
        Say(f, (string)(llGetPos()));
        llSleep(1);
        llDie();
    }
}