Open Prim Animator/Notecard Import
<lsl> //! @brief Imports a set of animation frames from a notecard /**
- @author SignpostMarv Martin
- /
//! name of notecard to parse string notecard = "Open Prim Animator: Import";
//! inventory key of notecard key _notecard;
//! Temporarily stores the entire notecard contents string contents;
//! Since llGetNotecardLine() only reads the first 255 bytes of a notecard line, we need to temporarily store the notecard contents in a list before dumping it to the contents string. list _contents;
//! Whilst not truly multi-threaded, this script attempts non-sequential loading of the notecard (as opposed to sequential loading), theoretically providing some small performance gain list multithread_fetch = [];
//! dataserver query variable used to determine how many lines are in a notecard- needed to pre-populate multithread_fetch to the correct size. key nlq;
//! import command key import = "6b78fcc8-e147-4105-99a6-ff19b4bf559d";
//! export command key export = "7c2ca168-2b64-4836-8727-8e62b78dbd44";
//! command for updating the root scale in the core OPA script key op_alter_rootScale = "f9d3389e-a78c-43f8-9e35-c11adec112a5";
//! command for restoring link order key op_adjust_link_order_by_names = "e907917f-e5a3-490e-aaac-4596a0dc176d";
//! temporary store of link names list names;
//! temporary store of animation frame positions list pos;
//! temporary store of animation frame rotations list rot;
//! temporary store of animation frame sizes list size;
//! temporary store of root scale. This is needed in case a new animation was generated on an object with a different root scale to the prim the object the notecard is being imported to vector rootScale;
//! I think this is here solely due to copypasta. export_string(string _export_string){
integer s=1000;
while(llStringLength(_export_string) > s){
llOwnerSay(llGetSubString(_export_string,0,(s-1)));
_export_string = llGetSubString(_export_string,s,-1);
}
if(llStringLength(_export_string) > 0){
llOwnerSay(_export_string);
}
}
default{
state_entry(){
if(llGetInventoryType(notecard) == INVENTORY_NOTECARD){ // if the specified notecard is in the object,
_notecard = llGetInventoryKey(notecard); // get the inventory key
state num_lines; // then attempt to get the number of lines in the notecard
}
}
changed(integer c){
if(c & CHANGED_INVENTORY){ // if the inventory has changed (e.g. if the notecard *wasn't* in the object),
llResetScript(); // restart the script
}
}
}
state num_lines{ // there should probably be a CHANGED_INVENTORY check in case the scripts are running reaaaaallllyyyy slow.
state_entry(){
nlq = llGetNumberOfNotecardLines(notecard); // attempt to get the number of lines in the notecard
}
dataserver(key q, string m){
if(q == nlq){
integer i;
integer j = (integer)m;
list k = []; // declaring list k = []; isn't exactly necessary, you could do list k;
list l = [];
for(i=0;i<j;++i){ // since we don't have the concept of transactions in LSL scripts, we're going to build up these lists separately
k += [NULL_KEY];
l += [NULL_KEY];
}
_contents = k; // this is where we do the atomic writes due to lack of transactions on variables
multithread_fetch = l; // this is where we do the atomic writes due to lack of transactions on variables
k = []; // cleaning up the local variable on purpose in case the LSL VM executing this script is inefficient
l = []; // cleaning up the local variable on purpose in case the LSL VM executing this script is inefficient
state mtf; // now we've pre-populated the lists to the correct size, start the "multi-threaded fetch" of notecard contents.
}
}
}
state mtf{
state_entry(){
integer i;
integer j=llGetListLength(multithread_fetch);
for(i=0;i<j;++i){
multithread_fetch = llListReplaceList(multithread_fetch,[llGetNotecardLine(notecard,i)],i,i); // sets the notecard fetching away
}
}
dataserver(key q, string m){
integer i = llListFindList(multithread_fetch,[q]);
if(i >= 0){
_contents = llListReplaceList(_contents,[m],i,i); // the traditional method of notecard fetching in LSL is single-threaded, getting one line at a time
}
if(llListFindList(_contents,[NULL_KEY]) == -1){ // since we started out with a NULLL_KEY-filled list, if there aren't any NUL_KEY in the list then we know it's done. caveat scriptor: since key and string types are interchangeable
contents = llDumpList2String(_contents,"");
state done_reading;
}
}
state_exit(){
_contents = [];
multithread_fetch = [];
}
}
state done_reading{
state_entry(){
integer rs_pos = llSubStringIndex(contents,"rootscale:");
integer names_pos = llSubStringIndex(contents,"names:");
integer pos_pos = llSubStringIndex(contents,"pos:");
integer rot_pos = llSubStringIndex(contents,"rot:");
integer size_pos = llSubStringIndex(contents,"size:");
rootScale = (vector)llGetSubString(contents, rs_pos + 10, names_pos - 1);
names = llParseString2List(llGetSubString(contents,names_pos + 6,pos_pos - 1),["|"],[]);
pos = llParseString2List(llGetSubString(contents,pos_pos + 4,rot_pos - 1),["|"],[]);
rot = llParseString2List(llGetSubString(contents,rot_pos + 4,size_pos - 1),["|"],[]);
size = llParseString2List(llGetSubString(contents,size_pos + 5,-1),["|"],[]);
contents = ""; // clearing memory up, we don't need the string in memory now.
integer a = llGetListLength(names);
integer b = llGetListLength(pos);
if(b % a || llGetListLength(rot) % a || llGetListLength(size) % a){
llOwnerSay("List Lengths do not match, possible corrupted notecard (" + llList2CSV([a,llGetListLength(pos),llGetListLength(rot),llGetListLength(size)]) + ")");
}else{
llMessageLinked(LINK_THIS,b,"XDimportLength",NULL_KEY);
llOwnerSay("Do import now");
}
}
changed(integer c){
if(c & CHANGED_INVENTORY && llGetInventoryKey(notecard) != _notecard){
llResetScript();
}
}
link_message(integer s, integer n, string m, key k){
if(m == "XDimportLength" && n == -1){
integer i;
integer j=llGetListLength(pos);
integer l;
integer namesLength=llGetListLength(names);
for(i=0;i<j;++i){
l = llListFindList(names, [llGetLinkName(2 + (i % namesLength))]) + (llFloor(i / namesLength) * namesLength); // this is to fix any bugs caused by link order changes
llMessageLinked(s,i,llDumpList2String([llList2Vector(pos,l),llList2Rot(rot,l),llList2Vector(size,l)],"|"),import);
}
llMessageLinked(s,0,(string)rootScale, op_alter_rootScale);
llOwnerSay("Done importing");
}
}
touch_start(integer t){
llMessageLinked(LINK_THIS,0,"XDmenu",NULL_KEY);
}
} </lsl>