Author Topic: Almost full persistance under MudOS (DS)  (Read 2285 times)

Offline silenus

  • BFF
  • ***
  • Posts: 196
    • View Profile
Almost full persistance under MudOS (DS)
« on: June 09, 2007, 03:02:31 PM »
Here is a prototype version of a rudimentary persistence mechanism I wrote for MudOS (DS). Currently it's not without it's flaws- specifically on a large mud with alot of objects the memory usage of the daemon is not optimized. I will add this feature in the future. The code consists of two files and requires some minor modifications to /lib/user/autosave.c to get it to work. Some of the other files need some minor modifications also since the new inheritable does not contain currently contain SetSaveRecurse() and SetSave() has been changed to return int instead of string array. Here is both the inheritable and the daemon. Hopefully someone will find it useful.

Code: [Select]
/*
        /lib/persist.c
  Silenus@Desperate Sorrows - 2007-06-10
Designed to work with the DeadSouls Library v2.4.x
Version : 0.001
*/

#include <daemons.h>

private static array Save = ({});

protected int AddSave(string array a)
{
Save += a;
return 1;
}

string array GetSave() { return copy(Save); }

void eventRestore(mapping m)
{
if( previous_object() != find_object(PERSIST_D) ) error("Illegal call to eventRestore()." + identify( previous_object(-1) ) );

foreach(string v in Save)
{
store_variable(v, m[v] );
}

foreach(object o in m["#inventory#"] )
{
o->eventMove( this_object() );
}
}

void eventSave()
{
PERSIST_D->SaveObject( this_object() );
}

int eventDestruct()
{
PERSIST_D->SaveObject( this_object() );
return destruct( this_object() );
}

mapping GetSaveMap()
{
// if( previous_object() != PERSIST_D ) error("Illegal call to GetSaveMap().");

mapping sMap = ([]);
foreach(mixed v in Save)
{
sMap[v] = fetch_variable(v);
}
sMap["#inventory#"] = ({});
foreach(object o in all_inventory( this_object() ) )
{
sMap["#inventory#"] += ({ o });
}

sMap["#base_name#"] = base_name( this_object() );
return sMap;
}

protected void create()
{
PERSIST_D->GetObjectID( this_object() );
}

Code: [Select]
/*
    /secure/daemon/persist_d.c
  Silenus@Desperate Sorrows - 2007-06-10
Designed to work with the DeadSouls Library v2.4.x
Version : 0.001
*/
 

#include <save.h>

string GetObjectID(object);
object GetObject(string);
mapping ConvertSaveMap(mapping);
mapping ConvertRestoreMap(mapping);
string array FindMapObjectIDs(mapping);
mixed ConvertSaveVariable(mixed);
mixed ConvertRestoreVariable(mixed);
string array FindVarObjectIDs(mixed);

private static mapping Objects = ([]);
private static mapping ObjectIDs = ([]);
private mapping SavedObjects = ([]);

private string GenerateObjectID(object o)
{
string baseName, objectID;
int n;

baseName = base_name(o);
if( file_name(o) == baseName )
{
objectID = baseName;
}
else
{
n = 0;
while( !undefinedp( SavedObjects[baseName + "#" + n] ) )
{
++n;
}
objectID = baseName + "#" + n;
}
ObjectIDs[o] = objectID;
Objects[ objectID ] = o;
SavedObjects[ objectID ] = ConvertSaveMap( o->GetSaveMap() );
return objectID;
}

string SaveObject(object o)
{
string objectID;

if( !inherits(LIB_PERSIST, o ) ) return 0;
if( undefinedp( ObjectIDs[o] ) )
return GenerateObjectID(o);

SavedObjects[ ObjectIDs[o] ] = ConvertSaveMap( o->GetSaveMap() );

return ObjectIDs[o];
}

string GetObjectID(object o)
{
string objectID;

if( !inherits(LIB_PERSIST, o ) ) return 0;
if( !undefinedp( ObjectIDs[o] ) ) return ObjectIDs[o];
// otherwise return a new reference not in the SavedObjects keys

return GenerateObjectID(o);
}

int RemoveObjectID(string s)
{
// might need to add a secure override here for stray references...
if( undefinedp( SavedObjects[s] ) ) return 0;
if( previous_object() != Objects[s] ) return 0;
map_delete( SavedObjects, s );
map_delete( Objects, s);
map_delete( ObjectIDs, previous_object() );
return 1;
}

varargs object GetObject(string s)
{
object o;

if( !undefinedp( Objects[s] ) || Objects[s] != 0 ) return Objects[s];

if( SavedObjects[s] )
{
if( SavedObjects[s]["#base_name#"][1] == s )
o = load_object(s);
else 
o = new( SavedObjects[s]["#base_name#"][1] );
// maybe add patching behavior here as well......
o->eventRestore( ConvertRestoreMap( SavedObjects[s] ) );

ObjectIDs[o] = s;
Objects[s] = o;


return o;
}
return 0;
}

mapping ConvertSaveMap(mapping m)
{
mapping cMap = ([]);
foreach( string v in keys(m) )
{
cMap[v] = ConvertSaveVariable( m[v] );
}
return cMap;
}

mapping ConvertRestoreMap(mapping m)
{
mapping cMap = ([]);
foreach( string v in keys(m) )
{
cMap[v] = ConvertRestoreVariable( m[v] );
}
return cMap;
}

string array FindMapObjectIDs(mapping m)
{
string array a = ({});
foreach(string v in keys(m) )
{
a += FindVarObjectIDs( m[v] );
}
return a;
}

mixed ConvertSaveVariable(mixed v)
{
mapping m;
switch ( typeof(v) )
{
case "int": case "float": case "string": case "function": case "class":
return ({ typeof(v), v });
case "object":
return ({ "object", GetObjectID(v) });
case "array" :
return ({ "array", map( v, (: ConvertSaveVariable($1) :) ) });
case "mapping" :
m = ([]);
foreach( mixed k in keys( v ) )
{
m[ k ] = ConvertSaveVariable( v[k] );
}
return ({"mapping", m});
}
}

mixed ConvertRestoreVariable(mixed v)
{
mapping m;
switch( v[0] )
{
case "int": case "float": case "string": case "function": case "class":
return v[1];
case "object":
return GetObject(v[1]);
case "array":
return map( v[1], (: ConvertRestoreVariable($1) :) );
case "mapping":
m = ([]);
foreach( mixed k in keys( v[1] ) )
{
m[ k ] = ConvertRestoreVariable( v[1][k] );
}
return m;
}
}

string array FindVarObjectIDs(mixed v)
{
string array a;
switch( v[0] )
{
case "int": case "float": case "string": case "function": case "class":
return ({});
case "object" :
return ({ v[1] });
case "array" :
return implode( map( v[1], (: FindVarObjectIDs($1) :) ), (: $1 + $2 :), ({}) );
case "mapping":
a = ({});
foreach( mixed k in keys( v[1] ) )
{
a += FindVarObjectIDs( v[1][k] );
}
return a;
}
}

void eventGarbageCollect()
{
//what is the terminal condition?

    mapping CheckedMap = ([]);
    mapping CheckMap = ([]); //until check list is empty
    string array CheckArr = ({});
    string Current;

foreach( string i in keys(SavedObjects) )
{
debug("a",i);
debug("b", SavedObjects[i]["#base_name#"] );
if(i == SavedObjects[i]["#base_name#"][1] )
{
debug("c", i );
CheckMap[i] = 1;
CheckArr += ({i});
}
}

while( sizeof(CheckArr) )
{
Current = CheckArr[0];

foreach( string i in FindMapObjectIDs( SavedObjects[ Current ] ) )
{
debug("hmmmm", i );
if( !CheckedMap[i] && !CheckMap[i] )
{
CheckMap[i] = 1;
CheckArr += ({i});
}
}

CheckArr = CheckArr[1..];
map_delete(CheckMap, Current);
CheckedMap[Current] = 1;
}

foreach( string j in keys(SavedObjects) )
{
if( !CheckedMap[j] ) map_delete(SavedObjects,j);
}
}

mapping GetSavedObjectsMap()
{
return copy(SavedObjects);
}

private int eventSave()
{
call_out( (: eventSave :), 1800 );
return save_object(SAVE_PERSIST, 1);
}

int eventDestruct()
{
save_object(SAVE_PERSIST, 1);
return destruct( this_object() );
}

static protected void create()
{
unguarded( (: restore_object, SAVE_PERSIST, 1 :) );
call_out( (: eventSave :), 1800 );
eventGarbageCollect();
}
 

Offline silenus

  • BFF
  • ***
  • Posts: 196
    • View Profile
Re: Almost full persistance under MudOS (DS)
« Reply #1 on: June 09, 2007, 04:24:53 PM »
In theory I feel the mechanism is capable like java serialization of saving almost any object graph. In this case however the saving is not into a serialized stream but into a registry in the persistence daemon. There are however two notable exceptions function pointers are still not saved nor are classes. Both these types are impossible to inspect through any mechanism in MudOS and thus are not easy to convert into a savable form.

The way so far I have been using the system is as an extended replacement for the old persist.c where graphs only related to the player object are saved. However I speculate that the system could be extended (I am not sure about the performance here) to save the whole state of a mud (minus the two items above) but then again if you went this far perhaps you might as well use something like DGD.