From d039129611af2a54941295a4e2da95ff5bf92188 Mon Sep 17 00:00:00 2001 From: Chris Koeritz Date: Sat, 3 Mar 2012 12:55:28 -0500 Subject: [PATCH] first bits of LSL code starting to arrive. --- huffware/jaunting/jaunting_library_v15.9.lsl | 552 +++++++++++++++++++ huffware/jaunting/readme.txt | 10 + readme.txt | 19 +- 3 files changed, 577 insertions(+), 4 deletions(-) create mode 100644 huffware/jaunting/jaunting_library_v15.9.lsl create mode 100644 huffware/jaunting/readme.txt diff --git a/huffware/jaunting/jaunting_library_v15.9.lsl b/huffware/jaunting/jaunting_library_v15.9.lsl new file mode 100644 index 00000000..6613cf3f --- /dev/null +++ b/huffware/jaunting/jaunting_library_v15.9.lsl @@ -0,0 +1,552 @@ + +// huffware script: jaunting library, by fred huffhines, released under GPL-v3 license. +// +// this script is a library of useful teleportation and movement calls. it should be added +// into an object along with other scripts that use the library. the jaunting library +// responds to linked messages as commands. +// parts of this script are based on warpPos from "Teleporter Script v 3.0 by Asira Sakai", +// which was found at the LSL wiki. +// +// this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html +// do not use it in objects without fully realizing you are implicitly accepting that license. +// + +// global constants... + +float PROXIMITY_REQUIRED = 0.1; + // how close we must be to the target location to call the jaunt done. + // we make this fairly low accuracy since we don't want jumps in space to immediately + // be counted as failures. + +// the most jumps the script will try to take. the overall distance from the start +// to the end can be 10 * MAXIMUM_JUMPS_PER_CALL meters. +integer MAXIMUM_JUMPS_PER_CALL = 84; + +// global variables... + +vector last_posn; // used to calculate jump distances. + +// jaunting library link message API... +////////////// +// do not redefine these constants. +integer JAUNT_HUFFWARE_ID = 10008; + // the unique id within the huffware system for the jaunt script to + // accept commands on. this is used in llMessageLinked as the num parameter. +string HUFFWARE_PARM_SEPARATOR = "{~~~}"; + // this pattern is an uncommon thing to see in text, so we use it to separate + // our commands in link messages. +string HUFFWARE_ITEM_SEPARATOR = "{|||}"; + // used to separate lists of items from each other when stored inside a parameter. + // this allows lists to be passed as single string parameters if needed. +integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs. +// commands available via the jaunting library: +string JAUNT_COMMAND = "#jaunt#"; + // command used to tell jaunt script to move object. pass a vector with the location. +string FULL_STOP_COMMAND = "#fullstop#"; + // command used to bring object to a halt. +string REVERSE_VELOCITY_COMMAND = "#reverse#"; + // makes the object reverse its velocity and travel back from whence it came. +string SET_VELOCITY_COMMAND = "#setvelocity#"; + // makes the velocity equal to the vector passed as the first parameter. +string JAUNT_UP_COMMAND = "#jauntup#"; +string JAUNT_DOWN_COMMAND = "#jauntdown#"; + // commands for height adjustment. pass a float for number of meters to move. +string JAUNT_LIST_COMMAND = "#jauntlist#"; + // like regular jaunt, but expects a list of vectors as the first parameter; this list + // should be in the jaunter notecard format (separated by pipe characters). + // the second parameter, if any, should be 1 for forwards traversal and 0 for backwards. +////////////// + +// returns what we consider to be safe to do in one big jump. +float distance_safe_to_jaunt() { return (float)(MAXIMUM_JUMPS_PER_CALL - 4) * 10.0; } + +// tests whether the destination is safe for an object to enter. +integer safe_for_entry(vector where) +{ + if (outside_of_sim(where)) { + // that's an obvious wrong idea; it tends to break us. + return FALSE; + } + return TRUE; +} + +// helper function for warp_across_list. this adds one jump vector to the +// list of rules as long as the destination is interesting. +list process_warp_item(vector next_jump) +{ +//log_it("mem: " + (string)llGetFreeMemory()); + // calculate the number of jumps needed. + integer jumps = (integer)(llVecDist(next_jump, last_posn) / 10.0) + 1; + last_posn = next_jump; // record for next check. + // set up our list which we'll replicate. + list rules = [ PRIM_POSITION, next_jump ]; + // Try and avoid stack/heap collisions. + if (jumps > MAXIMUM_JUMPS_PER_CALL - 1) jumps = MAXIMUM_JUMPS_PER_CALL; + // add the rules repeatedly to get the effective overall jump done. + integer count = 1; + while ( (count = count << 1) < jumps) + rules += rules; + // magnify the rule list before really adding it. this gets us to the proper + // final number of jumps. + return rules + llList2List(rules, (count - jumps) << 1 - 2, count); +} + +// originally based on warpPos from lsl wiki but drastically modified. +// uses a set of transfer points instead of a single target. +list warp_across_list(list full_journey, integer forwards) +{ + // make sure the list didn't run out. + if (llGetListLength(full_journey) == 0) return []; + if (forwards) { + // forwards traversal of the list. + vector next_jump = (vector)llList2String(full_journey, 0); + // shortcut the jumps if we're already there. + if (next_jump == llGetPos()) + return warp_across_list(chop_list(full_journey, 1, + llGetListLength(full_journey) - 1), forwards); + // calculate our trajectory for the next jump and add in all subsequent jumps. + return process_warp_item(next_jump) + + warp_across_list(chop_list(full_journey, 1, llGetListLength(full_journey) - 1), forwards); + } else { + // reverse traversal of the list. + vector next_jump = (vector)llList2String(full_journey, llGetListLength(full_journey) - 1); + // shortcut the jumps if we're already there. + if (next_jump == llGetPos()) + return warp_across_list(chop_list(full_journey, 0, + llGetListLength(full_journey) - 2), forwards); + // calculate our trajectory for the next jump and add in all subsequent jumps. + return process_warp_item(next_jump) + + warp_across_list(chop_list(full_journey, 0, llGetListLength(full_journey) - 2), forwards); + } +} + +// the main function that performs the jaunting process. +// now also supports adding a rotation into the mix to avoid paying the extra delay +// cost of calling llSetRot. +jaunt(list full_journey, integer forwards, string command_used, + integer use_rotation, rotation rot, integer reply_to_request) +{ +//log_it("jaunt: fullj=" + (string)full_journey + " forew=" + (string)forwards); + // set up our global variables... + last_posn = llGetPos(); + // calculate the trip and run it. + list rot_add; + if (use_rotation) + rot_add = [ PRIM_ROTATION, rot ]; + llSetPrimitiveParams(warp_across_list(full_journey, forwards) + rot_add); + if (reply_to_request) { + // pick out the last target in the list based on the direction we're moving. + integer last_indy = 0; + if (forwards == TRUE) last_indy = llGetListLength(full_journey) - 1; + vector last_jump = (vector)llList2String(full_journey, last_indy); + // final judge of success here is how close we got to the target. + integer landed_there = (llVecDist(llGetPos(), last_jump) <= PROXIMITY_REQUIRED); + send_reply(LINK_THIS, [landed_there, llGetPos()], command_used); + } +} + +////////////// + +// sets the object's speed to "new_velocity". +// if "local_axis" is TRUE, then it will be relative to the object's +// own local coordinates. +set_velocity(vector new_velocity, integer local_axis) +{ + vector current_velocity = llGetVel(); + + if (local_axis) { + rotation rot = llGetRot(); + current_velocity /= rot; // undo the rotation. + } + + new_velocity -= current_velocity; + new_velocity *= llGetMass(); + + llApplyImpulse(new_velocity, local_axis); +} + +// attempts to bring the object to a complete stop. +full_stop() +{ + llSetForce(<0,0,0>, FALSE); + set_velocity(<0,0,0>, FALSE); +} + +// sends an object back along its trajectory. +reverse_velocity(integer local_axis) +{ + vector current_velocity = llGetVel(); + if (local_axis) { + rotation rot = llGetRot(); + current_velocity /= rot; // undo the rotation. + } + vector new_velocity = -2 * current_velocity; + new_velocity *= llGetMass(); + llApplyImpulse(new_velocity, local_axis); +} + +// teleports to the new "location", if possible. does not change object's phantom / physical +// states. this will do the jaunt in multiple steps if the distance from here to "location" +// is too large. +apportioned_jaunt(vector location, string command_used, integer use_rotation, rotation rot, + integer should_send_reply) +{ + if (!safe_for_entry(location)) { + // that's not good. we should not allow the script to get broken. + if (should_send_reply) + send_reply(LINK_THIS, [FALSE, llGetPos()], command_used); + return; + } + // go to position specified, by leapfrogs if too long. + integer chunk_of_jump; + integer MAX_CHUNKS = 200; // multiplies the distance a single jaunt can cover. + for (chunk_of_jump = 0; chunk_of_jump < MAX_CHUNKS; chunk_of_jump++) { + vector interim_vec = location - llGetPos(); + float jump_dist = llVecDist(llGetPos(), location); + integer reply_needed = TRUE; + if (jump_dist > distance_safe_to_jaunt()) { + // the entire distance cannot be jumped. do the part of it that fits. + float proportion_can_do = distance_safe_to_jaunt() / jump_dist; + interim_vec *= proportion_can_do; + reply_needed = FALSE; // don't reply when this is not full jump. + } + interim_vec += llGetPos(); // bias jump back to where we are. +//log_it("jumping from " + (string)llGetPos() + " to " + (string)interim_vec); + jaunt([llGetPos(), interim_vec], TRUE, command_used, use_rotation, rot, + reply_needed && should_send_reply); + float dist_now = llVecDist(llGetPos(), interim_vec); + if (dist_now > PROXIMITY_REQUIRED) { +//log_it("failed to make interim jump, dist left is " + (string)dist_now); + // bail out. we failed to get as far as we thought we should. + chunk_of_jump = MAX_CHUNKS + 10; + if (!reply_needed) { + // we need to send the reply we hadn't sent yet. + if (should_send_reply) + send_reply(LINK_THIS, [FALSE, llGetPos()], command_used); + } + } else if (llVecDist(llGetPos(), location) <= PROXIMITY_REQUIRED) { + // leave loop for a different reason; we got there. + chunk_of_jump = MAX_CHUNKS + 10; + if (!reply_needed) { + // we need to send the reply we hadn't sent yet. + if (should_send_reply) + send_reply(LINK_THIS, [TRUE, llGetPos()], command_used); + } + } + } +} + +// the entire distance embodied in the list of targets. this is a little smart, +// in that if there's only one target, we assume we're going from "here" to that +// one target. +float total_jaunt_distance(list targets) +{ + if (!llGetListLength(targets)) return 0.0; + // add in "this" location if they omitted it. + if (llGetListLength(targets) < 2) + targets = [ llGetPos() ] + targets; + integer disindy; + float total_dist = 0.0; + vector prev = (vector)llList2String(targets, 0); + for (disindy = 1; disindy < llGetListLength(targets); disindy++) { + vector next = (vector)llList2String(targets, disindy); + total_dist += llVecDist(prev, next); + prev = next; + } + return total_dist; +} + +// jaunts to a target via a set of intermediate locations. can either go forwards +// through the list or backwards. +phantom_jaunt_to_list(list targets, integer forwards, string command_used, + integer use_rotation, rotation rot) +{ + vector final_loc; + if (forwards) final_loc = (vector)llList2String(targets, llGetListLength(targets) - 1); + else final_loc = (vector)llList2String(targets, 0); + +//hmmm: check each destination in list?? + if (!safe_for_entry(final_loc)) { + // that's not good. we should not allow the script to get broken. + send_reply(LINK_THIS, [FALSE, llGetPos()], command_used); + return; + } + + // this turns off the physics property for the object, so that jaunt and + // llSetPos will still work. this became necessary due to havok4. + integer original_phantomosity = llGetStatus(STATUS_PHANTOM); + integer original_physicality = llGetStatus(STATUS_PHYSICS); + if (original_physicality != FALSE) llSetStatus(STATUS_PHYSICS, FALSE); + if (original_phantomosity != TRUE) llSetStatus(STATUS_PHANTOM, TRUE); + + integer send_reply_still = TRUE; // true if we should send a reply when done. + + // iterate through our list of targets and either we will jaunt to the next + // place directly or we will have to wrap a few destinations due to sim crossing. + while (llGetListLength(targets) > 0) { + vector next_loc; + if (forwards) next_loc = (vector)llList2String(targets, 0); + else next_loc = (vector)llList2String(targets, llGetListLength(targets) - 1); + if (outside_of_sim(next_loc)) { + log_it("bad jaunt path: first target is out of sim."); + send_reply(LINK_THIS, [FALSE, llGetPos()], command_used); + return; // skip that badness. + } + + // check how much total distance we have in the path that's left. if it's under our + // limit, then we'll just take it in one jump. + float total_dist = total_jaunt_distance(targets); + + // if we're below the threshold, we'll just jump now. + integer already_jumped = FALSE; + if (total_dist < distance_safe_to_jaunt()) { + jaunt(targets, forwards, command_used, use_rotation, rot, TRUE); + targets = []; // reset the list now. + send_reply_still = FALSE; // we have already sent the reply in jaunt(). + already_jumped = TRUE; // don't do anything else. + } + if (!already_jumped) { + vector next_plus_1 = ZERO_VECTOR; // default cannot fail our "is inside sim" check. + if (llGetListLength(targets) > 1) { + if (forwards) next_plus_1 = (vector)llList2String(targets, 1); + else next_plus_1 = (vector)llList2String(targets, llGetListLength(targets) - 2); + } + if (outside_of_sim(next_plus_1)) { +//hmmm: eventually find all the negative ones in a row and do them in combo, rather than +// just giving up here and doing a jaunt to the rest.. + jaunt(targets, forwards, command_used, use_rotation, rot, TRUE); + targets = []; // reset the list now. + send_reply_still = FALSE; // we have already sent the reply in jaunt(). + } else { + // we've passed the negativity test, so we can at least jump to the next place. + + // zap the next location, since we're about to jump there. + integer zap_pos = 0; + if (!forwards) zap_pos = llGetListLength(targets) - 1; + targets = llDeleteSubList(targets, zap_pos, zap_pos); + + // only bother jumping if we're not already there. + if (llVecDist(next_loc, llGetPos()) > PROXIMITY_REQUIRED) { + apportioned_jaunt(next_loc, command_used, use_rotation, rot, FALSE); + } + } + } + } + + if (send_reply_still) { + integer yippee = TRUE; // assume we succeeded until we prove otherwise. + if (llVecDist(final_loc, llGetPos()) > PROXIMITY_REQUIRED) + yippee = FALSE; + send_reply(LINK_THIS, [yippee, llGetPos()], command_used); + } + + // return to prior characteristics. + if (original_phantomosity != TRUE) llSetStatus(STATUS_PHANTOM, original_phantomosity); + if (original_physicality != FALSE) llSetStatus(STATUS_PHYSICS, original_physicality); +} + +// implements our API for jumping around. +handle_link_message(integer sender, integer huff_id, string msg, key id) +{ + // separate the list out + list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []); + if (msg == JAUNT_COMMAND) { + // use list to string to avoid non-conversions. + vector v = (vector)llList2String(parms, 0); + rotation rot = <0.0, 0.0, 0.0, 1.0>; + integer gave_rot = llGetListLength(parms) > 1; + if (gave_rot) rot = (rotation)llList2String(parms, 1); // ooh, they gave us a rotational value also. +// log_it("gave rot? " + (string)gave_rot + " rot=" + (string)rot); + phantom_jaunt_to_list([llGetPos(), v], TRUE, msg, gave_rot, rot); + } else if (msg == FULL_STOP_COMMAND) { + full_stop(); + } else if (msg == REVERSE_VELOCITY_COMMAND) { + reverse_velocity(FALSE); + } else if (msg == SET_VELOCITY_COMMAND) { + vector v = (vector)llList2String(parms, 0); +//log_it("jaunting lib received set velocity request for " + (string)v); + set_velocity(v, FALSE); + } else if (msg == JAUNT_UP_COMMAND) { + phantom_jaunt_to_list([ llGetPos(), llGetPos() + <0.0, 0.0, llList2Float(parms, 0)> ], + TRUE, msg, FALSE, ZERO_ROTATION); + } else if (msg == JAUNT_DOWN_COMMAND) { + phantom_jaunt_to_list([ llGetPos(), llGetPos() + <0.0, 0.0, -llList2Float(parms, 0)> ], + TRUE, msg, FALSE, ZERO_ROTATION); + } else if (msg == JAUNT_LIST_COMMAND) { + string destination_list = llList2String(parms, 0); + list targets = llParseString2List(destination_list, [HUFFWARE_ITEM_SEPARATOR], []); + destination_list = ""; + integer forwards = TRUE; + // snag the directionality for the list, if specified. + if (llGetListLength(parms) > 1) forwards = llList2Integer(parms, 1); + rotation rot = <0.0, 0.0, 0.0, 1.0>; + integer gave_rot = llGetListLength(parms) > 2; + if (gave_rot) rot = llList2Rot(parms, 2); // ooh, they gave us a rotational value also. + phantom_jaunt_to_list(targets, forwards, msg, gave_rot, rot); + targets = []; + } +} + +////////////// +// from hufflets... + +integer debug_num = 0; + +// a debugging output method. can be disabled entirely in one place. +log_it(string to_say) +{ + debug_num++; + // tell this to the owner. + llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say); + // say this on open chat, but use an unusual channel. +// llSay(108, llGetScriptName() + "--" + (string)debug_num + ": " + to_say); +} +////////////// + +// a simple version of a reply for a command that has been executed. the parameters +// might contain an outcome or result of the operation that was requested. +send_reply(integer destination, list parms, string command) +{ + llMessageLinked(destination, JAUNT_HUFFWARE_ID + REPLY_DISTANCE, command, + llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR)); +} + +// returns TRUE if the value in "to_check" specifies a legal x or y value in a sim. +integer valid_sim_value(float to_check) +{ + if (to_check < 0.0) return FALSE; + if (to_check >= 257.0) return FALSE; + return TRUE; +} + +integer outside_of_sim(vector to_check) +{ + return !valid_sim_value(to_check.x) || !valid_sim_value(to_check.y); +} + +/////////////// + +// returns the portion of the list between start and end, but only if they are +// valid compared with the list length. an attempt to use negative start or +// end values also returns a blank list. +list chop_list(list to_chop, integer start, integer end) +{ + integer last_len = llGetListLength(to_chop) - 1; + if ( (start < 0) || (end < 0) || (start > last_len) || (end > last_len) ) return []; + return llList2List(to_chop, start, end); +} + +////////////// + +////////////// +// huffware script: auto-retire, by fred huffhines, version 2.5. +// distributed under BSD-like license. +// !! keep in mind that this code must be *copied* into another +// !! script that you wish to add auto-retirement capability to. +// when a script has auto_retire in it, it can be dropped into an +// object and the most recent version of the script will destroy +// all older versions. +// +// the version numbers are embedded into the script names themselves. +// the notation for versions uses a letter 'v', followed by two numbers +// in the form "major.minor". +// major and minor versions are implicitly considered as a floating point +// number that increases with each newer version of the script. thus, +// "hazmap v0.1" might be the first script in the "hazmap" script continuum, +// and "hazmap v3.2" is a more recent version. +// +// example usage of the auto-retirement script: +// default { +// state_entry() { +// auto_retire(); // make sure newest addition is only version of script. +// } +// } +// this script is partly based on the self-upgrading scripts from markov brodsky +// and jippen faddoul. +////////////// +auto_retire() { + string self = llGetScriptName(); // the name of this script. + list split = compute_basename_and_version(self); + if (llGetListLength(split) != 2) return; // nothing to do for this script. + string basename = llList2String(split, 0); // script name with no version attached. + string version_string = llList2String(split, 1); // the version found. + integer posn; + // find any scripts that match the basename. they are variants of this script. + for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) { + string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn); + if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) { + // found a basic match at least. + list inv_split = compute_basename_and_version(curr_script); + if (llGetListLength(inv_split) == 2) { + // see if this script is more ancient. + string inv_version_string = llList2String(inv_split, 1); // the version found. + // must make sure that the retiring script is completely the identical basename; + // just matching in the front doesn't make it a relative. + if ( (llList2String(inv_split, 0) == basename) + && ((float)inv_version_string < (float)version_string) ) { + // remove script with same name from inventory that has inferior version. + llRemoveInventory(curr_script); + } + } + } + } +} +// +// separates the base script name and version number. used by auto_retire. +list compute_basename_and_version(string to_chop_up) +{ + // minimum script name is 2 characters plus a version. + integer space_v_posn; + // find the last useful space and 'v' combo. + for (space_v_posn = llStringLength(to_chop_up) - 3; + (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v"); + space_v_posn--) { + // look for space and v but do nothing else. + } + if (space_v_posn < 2) return []; // no space found. + // now we zoom through the stuff after our beloved v character and find any evil + // space characters, which are most likely from SL having found a duplicate item + // name and not so helpfully renamed it for us. + integer indy; + for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) { + if (llGetSubString(to_chop_up, indy, indy) == " ") { + // found one; zap it. since we're going backwards we don't need to + // adjust the loop at all. + to_chop_up = llDeleteSubString(to_chop_up, indy, indy); + } + } + string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1); + // ditch the space character for our numerical check. + string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1); + // strip out a 'v' if there is one. + if (llGetSubString(chop_suffix, 0, 0) == "v") + chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1); + // if valid floating point number and greater than zero, that works for our version. + string basename = to_chop_up; // script name with no version attached. + if ((float)chop_suffix > 0.0) { + // this is a big success right here. + basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1); + return [ basename, chop_suffix ]; + } + // seems like we found nothing useful. + return []; +} +// +////////////// + +default { + state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; } + on_rez(integer parm) { state rerun; } +} +state rerun { state_entry() { state default; } } + +state real_default +{ + state_entry() { auto_retire(); } + + link_message(integer sender, integer huff_id, string msg, key id) { + if (huff_id != JAUNT_HUFFWARE_ID) return; // not our responsibility. + handle_link_message(sender, huff_id, msg, id); + } +} + diff --git a/huffware/jaunting/readme.txt b/huffware/jaunting/readme.txt new file mode 100644 index 00000000..f60166dc --- /dev/null +++ b/huffware/jaunting/readme.txt @@ -0,0 +1,10 @@ + +Jaunting is our term for teleporting around the grid. The term jaunt comes +from 'The Stars My Destination' by Alfred Bester and we refer to it in a +similar context. + +The jaunting library script is the useful encapsulation of a variety of +teleportation and movement control methods. It is interfaced to via a +huffware API for remote procedure calls. + + diff --git a/readme.txt b/readme.txt index 8f553742..52fac198 100644 --- a/readme.txt +++ b/readme.txt @@ -1,7 +1,7 @@ -This is the "feisty meow concerns" codebase top folder. +This is the "feisty meow (TM) concerns" codebase top folder. -Feisty Meow Concerns codebase comprises some but probably not all of several projects that +Feisty Meow (TM) Concerns codebase comprises some but probably not all of several projects that have come before. These include the Twain.COM library scripts, the Yeti / YetiCode library scripts, the HOOPLE 1 and HOOPLE 2 C++ Codebases, the Fred Huffhines opensource LSL scripts, the CLAM build system, and a variety of other efforts. @@ -9,7 +9,8 @@ the CLAM build system, and a variety of other efforts. Directories and files of interest here... nucleus/ - The hierarchies of compiled code for the basic modules of the Feisty Meow hoople2 codebase. + Source for the basic modules of the Feisty Meow (TM) codebase. This includes basic + data structures, useful utilities, and the like. database/ Some files considered critical to the operations of Feisty Meow Concerns Ltd. This includes @@ -22,6 +23,17 @@ doc/ graphiq/ GUI tools and components, somewhat busted or just absent right now. +huffware/ + The library of LSL scripts for osgrid and Second Life written by the avatar named Fred + Huffhines. Fred Huffhines is a regular contributor and a well-loved member of our + development team. He also happens to be a fictional sock-puppet for adventuring around + the grids, but a lot of code has been written in his name. We are going to release the + full corpus of the Huffhines work over time. + +kona/ + Our burgeoning Java libraries. Not much to see here yet, but there is some code piling + up for these that we will try to release soon. + octopi/ Octopus design pattern and CROMP protocol reference implementations. Assorted applications based on these. @@ -35,4 +47,3 @@ scripts/ languages, such as bash and perl. - -- 2.34.1