Difference between revisions of "Open Prim Animator/Notecard Import"

From Second Life Wiki
Jump to navigation Jump to search
(documentation)
m (moar documentation)
Line 161: Line 161:
         if(m == "XDimportLength" && n == -1){
         if(m == "XDimportLength" && n == -1){
             integer i;
             integer i;
             integer j=llGetListLength(pos);
             integer j=llGetListLength(pos); // we've already verified that pos, rot and size are of the appropriate length so we only need to get the length of one of these.
             integer l;
             integer l;
             integer namesLength=llGetListLength(names);
             integer namesLength=llGetListLength(names);

Revision as of 05:42, 14 December 2011

<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); // see why we pre-populated it now ? :D
       for(i=0;i<j;++i){ // the reason we pre-populate rather than building the list here is to try and keep the level of consumed memory more consistent in this busy state, so the memory consumption doesn't steadily increase over time but regularly plateaus instead.
           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]); // get the position of the dataserver query id 
       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 NULL_KEY-filled list, if there aren't any NULL_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(){ // do some cleanup in the event the LSL VM executing this script is silly.
       _contents = [];
       multithread_fetch = [];
   }

}

state done_reading{

   state_entry(){
       integer rs_pos    = llSubStringIndex(contents,"rootscale:"); // get the string index for the rootscale block
       integer names_pos = llSubStringIndex(contents,"names:"); // get the string index for the names block
       integer pos_pos   = llSubStringIndex(contents,"pos:"); // get the string index for the position block
       integer rot_pos   = llSubStringIndex(contents,"rot:"); // get the string index for the rotation block
       integer size_pos  = llSubStringIndex(contents,"size:"); // get the string index for the size block
       rootScale  = (vector)llGetSubString(contents, rs_pos + 10, names_pos - 1); // get the root scale from the notecard
       names      = llParseString2List(llGetSubString(contents,names_pos + 6,pos_pos - 1),["|"],[]); // Fun Fact! Support for correcting link order was added in May 2011 but wasn't actually implemented until November 2011!
       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){ // since the number of names corresponds to the number of prims, we can use modulus to ensure that the notecard doesn't have a corrupt export.
           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); // notecard contents are valid, core script can start import now.
           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); // we've already verified that pos, rot and size are of the appropriate length so we only need to get the length of one of these.
           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 (finally added in November 2011)
               llMessageLinked(s,i,llDumpList2String([llList2Vector(pos,l),llList2Rot(rot,l),llList2Vector(size,l)],"|"),import); // send the import string to the core OPA script
           }
           llMessageLinked(s,0,(string)rootScale, op_alter_rootScale); // once we're done importing the position, rotation & size values for the animation frames, update the root scale to the value used in the creation of the animation frames.
           llOwnerSay("Done importing");
       }
   }
   touch_start(integer t){
       llMessageLinked(LINK_THIS,0,"XDmenu",NULL_KEY);   
   }

} </lsl>