[MUD-Dev] Persistent Worlds

Jon Lambert tychomud at ix.netcom.com
Fri Feb 16 21:17:18 CET 2001


Ryan Rhodes wrote:
 
> World Persistence being, as far as I can understand it, a world
> which persists in state through reboots.  To accomplish this I am
> assuming you are somehow running the world directly from disk, which
> is my first question.
 
>   Question: In the runtime environment of your worlds do you have
>   both a copy of the world in memory and a copy of the world on
>   disk?

Depending on the size of the object cache and the size of database it
is certainly possible that all objects could exist in memory at one
time.  This happens to me quite a bit since I have not built all
enough objects.  It isn't necessary though.  The only minimum
requirements are that the objects needed for a particular operation to
succeed need be in memory.

> Or do all games actions directly manipulate the DB?  

Nope.  I never manipulate the database directly.  It's done as a a
backend task.  The object manager subsystem stands as an independent
task.  I use functional partitioning, although the concept is similar
to components.  Subsystems are inherently concurrent, that is once
initialized they control their own execution requirements via thread
pooling.  They also maintain their own message queues and interfaces.
Database manipulation is buried within the object manager and exist as
successive translation layers.  Currently I can use any relational
database that supports ODBC and SQL 92.  I also support various DBMs,
the NT registry, and some other experiments.

> If you run strait from disk, what memory caching techniques do you
> use to improve performance?  Did you implement these yourself are
> have you used off the shelf software?
 
I've use both actually and at the same time.  Let me explain.  The
object manager runs in process space with the rest of the server.
Theoretically it's decoupled enough to run as an IPC, RPC or piped
process, although I've never attempted it.  My feeling is and was that
it would always run best in process space anyways.

The object manager cache is contiguous reusable and fixed memory
space.  Although fixed it can be adjusted on the fly manually.  The
cache is maintained by a thread of the object manager. A sweep is made
of the cache and valid objects that have changed are updated to the
database.  The sweep also ages _all_ objects by incrementing a byte
counter.  When that counter reaches 255 the object is marked as
removable.  When an access is attempted against an object and it is
found in the cache that counter is set to 0.  If it is not found in
the cache it is loaded in from the database.  New objects are
allocated to the first available free location. A free location is one
not occupied by an object or one where the object counter is 255.
This threshold of 255 is also manually configurable on the fly.  This
is not original.  I borrowed it from the way IBM's OS/390 manages
memory pages.  It's simple and works well.  I borrow a lot of old gems
from big blue. :-)

Now back to the off the shelf stuff.  As a database backend I've used
Oracle, Access, and Watcom SQL.  Each of these has their own caching
mechanisms.  The Oracle server ran on a different machine and easily
cached the whole database in memory on that machine.  No problem there
as long as it resides on a high speed network.  My Access ran on the
same machine and is not configurable (at least not the parts of
interest to me).  In fact it doesn't help at all, wasting memory that
I've already carefully managed myself.  OTOH Watcom SQL runs as
another process on the same machine and you can turn off cacheing
completely!  The interface is actually a nice lightweight IPC
implementation.  This is sort of ideal. Not quite though.  My homespun
database still performs better.

One thing to remember when using RDMS technologies is that cacheing
mechanisms are optimized for generalized access patterns.  In the case
of some muds, especially those using object relationships, these
access patterns will almost always be sub-optimal (MOO, Cold).  This
is not so for some other mud designs like (Diku, ROM).  I believe I
posted an extensive post on the reasons why.  The basic Diku design
also allows one to leverage referential integrity features of RDBMs.

Others have played around using using SQL with OO servers and have
posted their experiences.  A big problem with these experiments with
existing OO codebases is that by design the objects are not
reflective.  Internally yes, externally no.  Because of that it those
experiments I've seen with RDMSs have stored the bulk of Object data
in BLOBs. Obviously that negates virtually all of the query features
of SQL beyond simple indexed access.

The basic problem is this.  The object model is fundamentally
different from the relational model.  Inheritence has to be fudged.
In the OO world children know their parents, but parents do not know
their children.  In the relational world the opposite is true.

How Java and the JDBC handle these differences is something I don't
have the experience in.  I do know that Poet, an popular object
database, has a JDBC.

>   Question: Is it even neccesary to run the game strait from disk to
>   effectively have a persistent world?

Obviously from above, no.

> Can you run the game from memory and just back the whole changing
> world up to storage for reboots?

Yes.  I believe Mushes do this.  I'm currently working on a ROM mud
that is mostly persistent and uses a similar approach.  I believe many
LPMuds are using mublibs that are at least partially persistent.

> Does this make maintaining persistence somehow more difficult?

No the above method makes it easy.  But it doesn't solve certain
problems.  More below under WHY?

> Beyond the question of how your doing it, more importantly is why?
> I mean this most seriously as I'm trying to weigh the advantages.  I
> understand your projects are less geared towards PK and more towards
> realistic simulations.  The benefits I can come up with so far are:

I don't see any relationship between PK and Simulations.  PK is a
moral concept that may be present in simulations as any other style of
game.

Why?  Simulations need to save state.  Not necessarily intervening
states.  Look at Civilization, Railroad Tycoon or SimCity.  How is
state saved?  Simple.  The player hits the save button.

So why didn't I just build in an administrative save button.  Well the
save button approach doesn't solve the problem of fault tolerance.
Nor does it solve the problems in light of concurrency, incomplete
transactions, etc.  Now it is certainly acceptable for a single player
of Civilization to roll back to the last time you saved.  Or is it?
How about a multiplayer game of Civilization?  Maybe not.  Because
positions and plans may have been revealed that are now rolled back.
Generally when one saves such a game all action freezes while the
system saves its state.  Is that acceptable in mud where maybe 20-100+
players are playing?  How often can you get away doing that save of
state?  Not too often.

Then we have the ugly problem of concurrency.  Concurrency in my
system is hard-threaded.  Other servers like MUQ and Cold concurrency
is soft-threaded.  Yet it's still present.

The problem here with the global save technique is establishing a
quiesce point.  One must both prevent new messages from being issued
or processed, and allow outstanding tasks to complete.  And since most
tasks (transactions) end up reissuing messages, one must then save the
also state of the message queues.  In short while a global save
mechanism works, they aren't really satisfactory as a restart point
for crash recovery.  You still go back to an arbitrary point in time.

I wanted the latest complete transactions recovery.  To do this
required building my own transaction commit and rollback mechanisms.
I didn't need to do this.  But part of my design requirements of the
programming language was to make commit and rollback transparent to
the user programmer.  Traditional application programming puts the
onus on the programmer. (think COBOL/DB2, PRO-C/Oracle, or maybe
not.. :-P )

> While pondering this, it occured to me that the developers role
> might become something different in this context.  For the world to
> remain persistant it would now seem not enough to just store the
> original version of a room or object in the DB.  You are now storing
> the element in all of its various states.

I dont do this.  If a room changes it changes.. the original is lost
forever.  The only exception is I maintain seperate heirarchies of
prototypes.  Those prototypes are used to generate critters and
things.  The only time I do this with rooms is with player housing.
These are prototypes of houses, castles, etc. that player purchases
from an architect and then customizes oneself.

>   Question: If you are storing the original version of a room in the
>   DB and it becomes burnt down, are you now storing the burnt
>   version and completely throwing away the original?  Or did you
>   store both versions from the begining?

I never thought of that.  But then I haven't implemented any sort of
in game terraforming outside of the housing stuff I mentioned above.
However it is a very interesting idea though.  :-)

> In general this principle would seem to persist with the world, that
> you are no longer just adding creatures or items.  You are now
> required to provide for the way these things are generated and
> repopulate.

Not really.  There is fundamentally no difference between the
groundhog day style resets of Diku-style games and fancy population
algorithms.  In fact some of the latest Diku derivatives do indeed
take the current population of a given item or critter into question
in determining whether to execute a reset.  The major difference is
mainly in appearance, perception and level of abstraction from
"realism".

Now that I said that.  I made major changes in the resets system when
I converted a certain ROM mud to be persistent. ;-)

>   Question: Do you see putting together a more PK action oriented
>   game with a persistent world as opposed ideas?  What elements of a
>   pk action world might in fact be less workable in a persistent
>   world setup?

I don't see any relationship design-wise between PK and persistence.
PK or PvP is a game moral concept.  It certainly does exist in
simulations.

> reflective.  We do not use dynamic inheritence or dynamic
> attributes.
 
>   Question: Will this system map to an RDBMs?

If you extend a class with an attribute then NO.  Unless the JDBC
supports what I call DDL or data definition language.  I may be wrong,
but the dynamic class loading of Java isn't going to be very useful in
this situation if that class is one that is persisted.

> for storage.  Now if this is true, it would seem its not just
> runtime morphic systems that won't map, but all systems which are
> adding classes at runtime.

Yes.  In fact outside of people working on Mud Servers, in all my
research I've only found a research project by an IBM engineer who was
looking at this sort of runtime dynamic database schema.

>   Question: How would you suggest mapping collections to an RDBMS.
>   Normally every field would map to a column in the corresponding
>   classes table.  An object field would end up mapping to a forein
>   key reference to another table where that object would be stored.
>   Collections like java Vectors, Hashtables, and arrays seem to pose
>   a problem.  I don't think I would want each object in the
>   collection to map to a foreign key in the previous item in the
>   lists table.

Map each collection type to a relationship table.  If every object in
the system gets a unique key then any particular collection type can
be mapped to a single table.

For example:

  Monster table
    ID          10101
    Name        
    Inventory   "Monster_Inventory"
    Worn        "Monster_Worn"
    ...

  GenericVector table
    OwnerID  10101
    CollectionName   "Monster_Inventory" 
    Order  "1" 
    ItemID  50505
    ...more rows for this collection
    another row in same table
    OwnerID 10101
    CollectionName   "Monster_Worn" 
    Order  "1" 
    ItemID  50515
    ...more rows  

  Thing table
    ID   50505
    Name Sword
    ...

You need not use a string for collection name.  You could assign
unique ids to specific collections as well.  If the collection is not
ordered then you don't need a field to hold that.  If the collection
type is keyed or an associated array then you'd need to hold more info
or keys.

--
--* Jon A. Lambert - TychoMUD        Email:tychomud at ix.netcom.com *--
--* Mud Server Developer's Page <http://tychomud.home.netcom.com> *--

_______________________________________________
MUD-Dev mailing list
MUD-Dev at kanga.nu
https://www.kanga.nu/lists/listinfo/mud-dev



More information about the mud-dev-archive mailing list