Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Library: OO/Class Simulation in LSL (VeryPav Class)

Pavcules Superior
Registered User
Join date: 30 Oct 2006
Posts: 69
01-28-2009 13:38
Hi all,

As you you know LSL does not support Object Orientation / Classes. The code submitted below shows how you can simulate several OO features in LSL. Full OO implementation cannot be achieved using this code, as there is limitations with the LSL language. Maybe one day in the future C# could be supported in LSL.

This project is called the "VeryPav Class" and is an extension to "Very Keynes" VK-DBMS-VM work last was released last year. If you go to the documentation link below and brief description of what OO is, plus the documentation of all the functions. This page also has a license agreement, which follows the DBMS license as well.

The documentation can be found here:
http://docs.google.com/Doc?docid=ddd96bqf_29d68dktfv

To be able to use the Class functions in your LSL code, you will need to copy the all of the code from the link below.
http://docs.google.com/Doc?id=ddd96bqf_31cwgsmx58

To view a demo of the code, visit the link below:
http://docs.google.com/Doc?id=ddd96bqf_32d7jw8tdc

To view a second demo of the code, visit the link below:
http://docs.google.com/Doc?id=ddd96bqf_33c9qhhnc5

For more info about the DBMS system, see this topic:
/54/4a/290413/1.html

It would be interesting to see what you make with this code.

The library code below is split into several parts due to limitation of characters in the forum threads. Visit the links above to see the full list of the code.
Pavcules Superior
Registered User
Join date: 30 Oct 2006
Posts: 69
01-28-2009 13:39
CODE


// VeryPav Class
// Release Date : 2009-01-28
// VK-DBMS-VM : Created by "Very Keynes"
// Class : Created by "Pavcules Superior"



//Very Keynes - 2008
//
// 2008-10-31, 22:24 GMT
// Release Candidate
//
// For usage information and examples please see
// http://docs.google.com/Doc?id=d79kx35_26df2pbbd8
//
//------------------Begin VK-dbms-VM----------------------------\\
//--------------------Introduction------------------------------\\
//
// Very Keynes - db - Virtual Machine
//
// Implements a core set of registers and root functions
// to create and manage multi-table database structures as
// an LSL list. Although intended to under pin higher level
// database management tools such as VK-SQL it is usable as
// a small footprint database facility for system level
// applications.
//
//
// Naming Conventions and Code Style
//
// This Code is intended to be included as a header to user generated
// code. As such it's naming convention was selected so that it would
// minimise the possibility of duplicate names in the user code portion
// of the application.
//
// Licencing
//
// The concept and code are hereby released to the public domain on an honer system.
// If expanded upon, or altered in any positive way, please post the update,
// else use it in any way you wish for non commercial applications. If used in a
// commercial application then please be mindful of the time spent to develop this system
// and contact the author about a distribution agreement
//
// Table Control Registers
//
integer th_; // Table Handle / Index Pointer
integer tc_; // Columns in Active Table
integer tr_; // Rows in Active Table
integer ts_; // Active Table Start Address
//
list _d_ = []; // Database File
list _i_ = [0]; // Index File
//
// Exposed Variables
//
integer dbIndex; // Active Row Table Pointer
list dbRow; // User Scratch List
string dbError; // System Error String
//
// Temporary / Working Variables
//
integer t_i;
string t_s;
float t_f;
list t_l;
//
// System Functions
//
// string dbCreate( TableName, ColumnList )
//
// Creates a new Table in the database
//
string dbCreate(string tab, list col)
{
if(dbOpen(tab)) // Integrity test
{
dbError = tab + " already exists";
return "";
}
// Create a new index record for the table
_i_ += [tab, tc_ = llGetListLength(col), 0, 0, 0];
th_= 0; // release current handle
dbOpen(tab); // Activate the new table
dbInsert(col); // insert the header record
return tab; // Return the alis
}
//
// Helper function returns Col# for Column Name
//
integer dbCol(string col){return llListFindList(dbGet(0), [_trm(col)]);}
//
// integer dbDelete( Row# )
//
// Deletes the specified Row of the Active Table
//
integer dbDelete(integer ptr)
{
if(ptr > 0 && ptr < tr_) // Bounds test
{
_d_ = llDeleteSubList(_d_, t_i = ts_ + tc_ * ptr, t_i + tc_ - 1);
--tr_;
return tr_ - 1;
}
else {
dbError = (string)ptr+" is outside the Table Bounds";
return FALSE;}
}
//
// integer dbDrop( TableName )
//
// Complement to dbCreate - Removes a Table and its data
//
integer dbDrop(string tab)
{
if(-1 != (t_i = llListFindList(_i_, [tab])))
{ dbOpen(tab);
_d_ = llDeleteSubList(_d_, ts_, ts_ + tc_ * tr_ - 1);
_i_ = llDeleteSubList(_i_, th_, th_ + 4);th_= 0;
return TRUE; }
else {dbError = tab + " : Table name not recognised"; return FALSE;}
}
//
// integer dbExists( condition list )
//
// searches from last row towards row 1 stopping and returning the first match
//
integer dbExists(list cnd)
{ for(dbIndex = tr_ ; --dbIndex > 0 ; ){if(dbTest(cnd))return dbIndex;}
return FALSE;}
//
// list dbGet( row# )
//
// returns the specified row
//
list dbGet(integer ptr)
{
if(ptr < tr_ && ptr >= 0)return llList2List(_d_, t_i = ts_ + tc_ * ptr, t_i + tc_ - 1);
else dbError = (string)ptr+" is outside the Table Bounds";return [];
}
//
// Internal function to calculate a tables start address
integer _idx(integer hdl){return (integer)llListStatistics(6, llList2ListStrided(_i_, 0, hdl, 5));}
//
// integer dbInsert( list Row Data )
//
// Creates a new row at the end of the table, returns rumber of rows
//
integer dbInsert(list val)
{
if(llGetListLength(val) == tc_){_d_ = llListInsertList(_d_, val, ts_ + tc_ * (dbIndex = tr_++));return dbIndex;}
else{dbError = "Insert Failed - too many or too few Columns specified"; return FALSE;}
}
//
// integer dbOpen ( Table Name )
//
// saves current registers and opens a table if it exists
// returns Number of rows in opened table
//
integer dbOpen(string tab)
{
if(th_)_i_ = llListReplaceList(_i_, [tr_, dbIndex, tc_ * tr_], th_ + 2, th_ + 4);
if(-1 == (t_i = llListFindList(_i_, [tab]))) //if tab does not exist abort
{dbError = tab + " : Table name not recognised"; return FALSE;}
else if(th_ != t_i){th_ = t_i++;ts_ = _idx(th_);
tc_ = llList2Integer(_i_, t_i++);
tr_ = llList2Integer(_i_, t_i++);
dbIndex = llList2Integer(_i_, t_i);}
;return tr_ - 1;
}
//
// integer dbPut( list Row Data )
//
// Replaces the currently Active Row of the Table with the new data
integer dbPut(list val){if(llGetListLength(val) == tc_)
{
_d_ = llListReplaceList(_d_, val, t_i = ts_ + tc_ * dbIndex, t_i + tc_ - 1); return dbIndex;}
else {dbError = "Update Failed - too many or too few Columns specified"; return FALSE;}
}
//
// integer dbTest( condition list )
//
// tests condition against current row, returns true or false
//
integer dbTest(list cnd)
{
if(llGetListEntryType(cnd,2) >= 3){
t_s = llList2String(dbGet(dbIndex), dbCol(llList2String(cnd, 0)));
if ("==" == llList2String(cnd, 1)){t_i = t_s == _trm(llList2String(cnd, 2));}
else if("!=" == llList2String(cnd, 1)){t_i = t_s != _trm(llList2String(cnd, 2));}
else if("~=" == llList2String(cnd, 1))
{t_i = !(llSubStringIndex(llToLower(t_s), llToLower(_trm(llList2String(cnd, 2)))));}}
else{
t_f = llList2Float(dbGet(dbIndex), dbCol(llList2String(cnd, 0)));
t_s = llList2String(cnd, 1);
if ("==" == t_s){t_i = t_f == llList2Float(cnd, 2);}
else if("!=" == t_s){t_i = t_f != llList2Float(cnd, 2);}
else if("<=" == t_s){t_i = t_f <= llList2Float(cnd, 2);}
else if(">=" == t_s){t_i = t_f >= llList2Float(cnd, 2);}
else if("<" == t_s){t_i = t_f < llList2Float(cnd, 2);}
else if(">" == t_s){t_i = t_f > llList2Float(cnd, 2);}}
if(t_i)return dbIndex; else return FALSE;
}
// Internal function encapsulates llStringTrim
string _trm(string val){return llStringTrim(val, STRING_TRIM);}
//
// dbTruncate ( table Name )
//
// removes all rows from a table but leaves the header intact
//
dbTruncate(string tab){dbIndex = dbOpen(tab);while(dbIndex > 0)dbDelete(dbIndex--);}
//
// dbSort ( integer Order )
//
// permanently sorts a table by the first column, ascending or descending
//
dbSort(integer dir){_d_ = llListReplaceList(_d_,llListSort(llList2List
(_d_, t_i = ts_ + tc_, t_i + tc_ * tr_ - 2), tc_, dir), t_i, t_i + tc_ * tr_ - 2);
}
//
// dbFn( Function, Column )
//
// Preforms Statistical functions on the Column Specified
//
float dbFn(string fn, string col)
{
t_l = llList2List(_d_, t_i = ts_ + tc_, t_i + tc_ * tr_ - 2);
if(dbCol(col) != 0) t_l = llDeleteSubList(t_l, 0, dbCol(col) - 1);
return llListStatistics(llSubStringIndex("ramimaavmedesusqcoge", llGetSubString(llToLower(fn),0,1)) / 2,
llList2ListStrided(t_l, 0, -1, tc_));
}

//------------------End VK-dbms-VM----------------------------\\


Pavcules Superior
Registered User
Join date: 30 Oct 2006
Posts: 69
01-28-2009 13:40
CODE



//------------------Begin VeryPav Object Orientaiton ----------------------------\\

integer CLASS_TYPE_VAR_FLOAT = 0;
integer CLASS_TYPE_VAR_INTEGER = 1;
integer CLASS_TYPE_VAR_KEY = 2;
integer CLASS_TYPE_VAR_LIST = 3;
integer CLASS_TYPE_VAR_ROTATION = 4;
integer CLASS_TYPE_VAR_STRING = 5;
integer CLASS_TYPE_VAR_VECTOR = 6;

integer CLASS_TYPE_METHOD = 10;
integer CLASS_TYPE_METHOD_FLOAT = 11;
integer CLASS_TYPE_METHOD_INTEGER = 12;
integer CLASS_TYPE_METHOD_KEY = 13;
integer CLASS_TYPE_METHOD_ROTATION = 14;
integer CLASS_TYPE_METHOD_STRING = 15;
integer CLASS_TYPE_METHOD_VECTOR = 16;



// Internal function to copy properties from one class to another.
classCopyProperties(string p_strFromClassName, string p_strToClassName)
{
integer intIndex;
integer intRows = dbOpen(p_strFromClassName);
list lstProperties;

// Get a list of the properties in the Class we want to copy from.
for(intIndex = 1; intIndex <= intRows; intIndex++)
{
dbRow = dbGet(intIndex);
lstProperties = lstProperties + dbRow;
}

// Copy the properties into our class.
dbOpen(p_strToClassName);

for(intIndex = 0; intIndex < intRows * 4; intIndex += 4)
{
classAddProperty(
llList2String(lstProperties, intIndex),
llList2Integer(lstProperties, intIndex + 1),
llList2List(lstProperties, intIndex + 2, intIndex + 3));
}
}


// Creates a new class.
integer classCreation(string p_strClassName, list p_listInheritClassNames)
{
dbCreate(p_strClassName, [
"_classpropertyname_",
"_classpropertytype_",
"_classpropertydefaultvalue_",
"_classpropertymethodparameters_"]);

classAddProperty("_newmaxcount_", CLASS_TYPE_VAR_INTEGER, [-1]);
classAddProperty("_newcount_", CLASS_TYPE_VAR_INTEGER, [0]);
classAddProperty("_baseclass_", CLASS_TYPE_VAR_STRING, [""]);

integer intIndex;

for(intIndex = 0; intIndex < llGetListLength(p_listInheritClassNames); intIndex++)
{
classCopyProperties(llList2String(p_listInheritClassNames, intIndex), p_strClassName);
}

return dbOpen(p_strClassName);

}


// Create a new instance of a class.
integer classNew(string p_strClassName, string p_strBaseClassName, list p_lstParameters)
{

// Open the base class so that we update the number of times we are usng it.
dbOpen(p_strBaseClassName);

integer intNewCount = classGetPropertyValueInteger("_newcount_", []);
integer intMaxCount = classGetPropertyValueInteger("_newmaxcount_", []);

if(intNewCount >= intMaxCount && intMaxCount > 0)
{
llOwnerSay("ERROR: New limit has been reached.");
return FALSE;
}

// Update the number of times we've created it.
classSetPropertyValue("_newcount_", [intNewCount + 1]);

// Create a copy of the properties for our new class.
classCreation(p_strClassName, [p_strBaseClassName]);

classSetPropertyValue("_baseclass_", [p_strBaseClassName]);

// Call the New part.
if(dbExists(["_classpropertyname_", "==", "_new_"]))
{
dbRow = dbGet(dbIndex);

if(llGetListLength(p_lstParameters) == 0 && llList2String(dbRow, 3) != "")
{
p_lstParameters = llList2List(dbRow, 3, 3);
}
classCallUserMethod(llList2String(dbRow, 2), p_lstParameters);
}

return TRUE;

}


// Destroy the class.
integer classDestroy(string p_strClassName)
{
if(!dbOpen(p_strClassName)) { return FALSE; }

dbOpen(classGetPropertyValueString("_baseclass_",[]));

classSetPropertyValue("_newcount_", [classGetPropertyValueInteger("_newcount_", []) - 1]);

return dbDrop(p_strClassName);
}


// Open the class.
integer classOpen(string p_strClassName)
{
return dbOpen(p_strClassName);
}


// Add a property.
integer classAddProperty(string p_strPropertyName, integer p_intPropertyType, list p_lstDefaultValue)
{

list lstValues;
list lstParams;

if(llGetListLength(p_lstDefaultValue ) == 1)
{
lstValues = p_lstDefaultValue + [""];
}
else if(llGetListLength(p_lstDefaultValue) > 1)
{
lstValues = llList2List(p_lstDefaultValue, 0, 1);
}
else if(llGetListLength(p_lstDefaultValue ) == 0)
{
llOwnerSay("ERROR: Default value must be specified for property '" + p_strPropertyName + "'.");
return 0;
}

return dbInsert([p_strPropertyName, p_intPropertyType] + lstValues);

}


Pavcules Superior
Registered User
Join date: 30 Oct 2006
Posts: 69
01-28-2009 13:41
CODE



// Set the property value.
integer classSetPropertyValue(string p_strPropertyName, list p_lstValue)
{
if(dbExists(["_classpropertyname_", "==", p_strPropertyName]))
{
dbRow = dbGet(dbIndex);
return dbPut([ llList2String(dbRow, 0), llList2Integer(dbRow, 1), llList2String(p_lstValue, 0), llList2String(dbRow, 2) ]);
}

return FALSE;

}


// Get the property value.
list classGetPropertyValue(string p_strPropertyName, list p_lstParameters)
{
if(dbExists(["_classpropertyname_", "==", p_strPropertyName]))
{
integer intType;

dbRow = dbGet(dbIndex);

intType = llList2Integer(dbRow, 1);

if(intType >= CLASS_TYPE_METHOD && intType <= CLASS_TYPE_METHOD_VECTOR)
{
return classCallUserMethod(llList2String(dbRow, 2), p_lstParameters);
}

return llList2List(dbRow, 2, 2);
}

return [];

}


// Get the property value as a string.
string classGetPropertyValueString(string p_strPropertyName, list p_lstParameters)
{
return llList2String(classGetPropertyValue(p_strPropertyName, p_lstParameters), 0);
}


// Get the property value as an integer.
integer classGetPropertyValueInteger(string p_strPropertyName, list p_lstParameters)
{
return llList2Integer(classGetPropertyValue(p_strPropertyName, p_lstParameters), 0);
}


// Get the property value as an float.
float classGetPropertyValueFloat(string p_strPropertyName, list p_lstParameters)
{
return llList2Float(classGetPropertyValue(p_strPropertyName, p_lstParameters), 0);
}


// Get the property value as an key.
key classGetPropertyValueKey(string p_strPropertyName, list p_lstParameters)
{
return llList2Key(classGetPropertyValue(p_strPropertyName, p_lstParameters), 0);
}


// Get the property value as an vector.
vector classGetPropertyValueVector(string p_strPropertyName, list p_lstParameters)
{
return llList2Vector(classGetPropertyValue(p_strPropertyName, p_lstParameters), 0);
}


// Get the property value as a rotation.
rotation classGetPropertyValueRotation(string p_strPropertyName, list p_lstParameters)
{
return llList2Rot(classGetPropertyValue(p_strPropertyName, p_lstParameters), 0);
}


//------------------End of VeryPav Object Orientaiton ----------------------------\\


//------------------Begin 'classCallUserMethod' Routine --------------------------\\


list classCallUserMethod(string p_strMethodName, list p_lstParameters)
{

// Add the list of the methods that can be called.

if(p_strMethodName == "<ENTER METHOD NAME>") { return []; }
else
{
llOwnerSay("ERROR: Method '" + p_strMethodName + "' is not declared.");
}

// Return an empty list for all other cases.
return [];

}

//------------------End 'classCallUserMethod' Routine --------------------------\\

xteraco Ratner
Registered User
Join date: 6 Mar 2006
Posts: 4
04-13-2009 12:44
At a glance this looks like it would increase the complexity of my projects and create a TON of glue code that I don't need. Not to mention additional overhead.

I once read a warning in a C# book, [Game Engine Toolset Development] "Dont code in C# as you would in C, its wrong.".
Imaze Rhiano
Registered User
Join date: 27 Dec 2007
Posts: 39
05-24-2009 22:13
From: xteraco Ratner
At a glance this looks like it would increase the complexity of my projects and create a TON of glue code that I don't need. Not to mention additional overhead.

I once read a warning in a C# book, [Game Engine Toolset Development] "Dont code in C# as you would in C, its wrong.".


I agree - don't force language to another language. Also, because of LSL's memory limits this approach is not valid way to write code. :(