Object to Data v1.4
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.
(WARNING: EACH PRIM IS DELETED ONCE THE SCRIPT RUNS!!)
string SERIALIZER_DELIMITER = "$!#";
string hexc="0123456789ABCDEF";//faster
string Float2Hex(float a) {// Copyright Strife Onizuka, 2006, LGPL, http://www.gnu.org/copyleft/lesser.html
if(a) { float b = llFabs(a); integer c = llFloor(llLog(b) / 0.69314718055994530941723212145818);//floor(log2(b)) if(c > 127) c = 127;//catch fatal rounding error in exponent. integer d = (integer)((b / llPow(2.0,c)) * 0x1000000);//shift up into integer range string f = "p"; while(!(d & 0xf)) {//strip extra zeros off before converting or they break "p" d = d >> 4; c+=4; } do f = llGetSubString(hexc, 0xf & d, 0xf & d) + f; while(d = d >> 4); if(a < 0) return "-0x" + f + (string)(c - 24); return "0x" + f + (string)(c - 24); } return "0";//zero would hang the zero stripper.
}
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(); }
}