Object Persistence for a MUD

  • Thread starter Brent Dillingham
  • Start date
B

Brent Dillingham

Hi all,

For fun, I'm writing a MUD with Ruby. I thought I'd query the hive
mind here and ask some opinions on what library I might use for object
persistence -- that is, storing objects like players and rooms
somewhere.

I could do something really simple like store everything in YAML, I
could try DRb, or I could go all out and use an ORM like DataMapper...
DataMapper is the first route I took, but I soon realized that I'll
have to be really careful when using a lot of the ORM niceties while
still ensuring that a database row only every represents one object in
memory. i.e.:

# assuming Player has_one :room ...

player = Player.get(1) # get Player with ID=1
player.room # the player's current Room association

... player logs out, logs back in later ...

player.room

Oh noes! The ORM fetches the same room from the DB, but creates a
totally new object to represent it. Two players could be in the same
room ID, but actually reference different objects, and thus couldn't
see or interact with one another. Bad.

This problem is hackable by carefully keeping a cache of loaded
objects -- but might end up negating the value of using an ORM like
DataMapper. So, any suggestions out there of what I might do instead?

Thanks! ^_^
 
T

Todd Benson

Hi all,

For fun, I'm writing a MUD with Ruby. I thought I'd query the hive
mind here and ask some opinions on what library I might use for object
persistence -- that is, storing objects like players and rooms
somewhere.

I could do something really simple like store everything in YAML, I
could try DRb, or I could go all out and use an ORM like DataMapper...
DataMapper is the first route I took, but I soon realized that I'll
have to be really careful when using a lot of the ORM niceties while
still ensuring that a database row only every represents one object in
memory. i.e.:

# assuming Player has_one :room ...

player = Player.get(1) # get Player with ID=1
player.room # the player's current Room association

... player logs out, logs back in later ...

player.room

Oh noes! The ORM fetches the same room from the DB, but creates a
totally new object to represent it. Two players could be in the same
room ID, but actually reference different objects, and thus couldn't
see or interact with one another. Bad.

This problem is hackable by carefully keeping a cache of loaded
objects -- but might end up negating the value of using an ORM like
DataMapper. So, any suggestions out there of what I might do instead?

There are probably a hundred different ways to approach this. I've
never programmed a MUD, but after playing around with the Sequel gem
and Postgresql, I had a simple map with locations, players, and
objects. I updated the DB on the fly, but I'm sure there are other
paradigms.

Very, very simple example...

sql...

create database mud;

create table map (
x int not null,
y int not null,
synopsis varchar not null,
description varchar
primary key (x, y)
);

create table people (
name varchar not null primary key,
xcurrent int not null,
ycurrent int not null,
foreign key (x, y) references map (x, y)
);

insert into map values (0, 0, 'start');
insert into map values (1, 0, 'hallway');
insert into people values ('Loki', 1, 0);
insert into people values ('Thor', 0, 0);
insert into people values ('Freya', 1, 0);

...ruby...

require 'rubygems'
require 'sequel'
Area = Struct.new:)description, :synopsis, :x, :y)
DB = Sequel.connect("postgres://user_name:password@localhost:5432/mud")

#print everyone in the hallway...

DB[:map].join:)people, :xcurrent => :x, :ycurrent => :y).where :)x =>
1, :y => 0).all.each {|row| p row[:name]}

#=> Loki
#=> Freya


Todd
 
J

Jeff Moore

Brent said:
Hi all,

For fun, I'm writing a MUD with Ruby.

I've been doing something similar with a multi-user space game. I
haven't arrived at any firm conclusions but the first thing that I have
observed is the split between static/stateless data and dynamic/stateful
data.

There is a tremendous amount of static data (maps, buildings, locational
characteristics, etc) so it seems to make some sense to separate this
from the dynamic data/stateful (current health, ammo, factional
affiliations and status, etc).

At this point, I'm generating the static part of the universe and
Marshaling it to file so it can be re-used. Some would argue with
Marshal as a choice but I'm using DRb between clients and servers and
Marshal is the default object protocol within DRb so in for a penny, in
for a pound as they say...

On the plus side, it's considerably faster than YAML or a database.

On the minus side but isn't readable or mindful changes in object
version. If you've modified the object definition between store and
load, you need to rebuild. This is the reason I've relegated it to
managing static data only.

The dynamic/stateful part looks like it's going to require a database of
some sort largely to provide a relatively stable/secure repository.

SQLite looks like my first choice at this point as it's supposed to be
fast, small and zero maintenance. We'll see...

Best of luck

djief
 
J

Justin Collins

Brent said:
Hi all,

For fun, I'm writing a MUD with Ruby. I thought I'd query the hive
mind here and ask some opinions on what library I might use for object
persistence -- that is, storing objects like players and rooms
somewhere.

I could do something really simple like store everything in YAML, I
could try DRb, or I could go all out and use an ORM like DataMapper...
DataMapper is the first route I took, but I soon realized that I'll
have to be really careful when using a lot of the ORM niceties while
still ensuring that a database row only every represents one object in
memory. i.e.:

# assuming Player has_one :room ...

player = Player.get(1) # get Player with ID=1
player.room # the player's current Room association

... player logs out, logs back in later ...

player.room

Oh noes! The ORM fetches the same room from the DB, but creates a
totally new object to represent it. Two players could be in the same
room ID, but actually reference different objects, and thus couldn't
see or interact with one another. Bad.

This problem is hackable by carefully keeping a cache of loaded
objects -- but might end up negating the value of using an ORM like
DataMapper. So, any suggestions out there of what I might do instead?

Thanks! ^_^

I am working on a similar project. I started off using PStore but
switched to using Marshal and gdbm, which is quite a bit faster. But, in
either case, objects are not read and written to/from disk all the time.
Most of the time they are in memory and only occasionally saved (a
player would be saved when logging out, for example). To avoid the
situation you mentioned, just keep track of which objects are in memory
(give each a unique ID) and then only load them if they are not already
loaded.
If keeping everything in memory doesn't sound like a good plan, you can
also dynamically load/unload objects as needed.

I guess it just depends on how you want to approach it, though. I don't
have a strong need for any kind of fancy database functions, so storing
them with gdbm works fine for me.

-Justin
 
B

Brian Candler

Jeff said:
At this point, I'm generating the static part of the universe and
Marshaling it to file so it can be re-used. Some would argue with
Marshal as a choice but I'm using DRb between clients and servers and
Marshal is the default object protocol within DRb so in for a penny, in
for a pound as they say...

On the plus side, it's considerably faster than YAML or a database.

On the minus side but isn't readable or mindful changes in object
version. If you've modified the object definition between store and
load, you need to rebuild. This is the reason I've relegated it to
managing static data only.

The dynamic/stateful part looks like it's going to require a database of
some sort largely to provide a relatively stable/secure repository.

You might want to have a look at Madeleine, which could be a suitable
half-way house: it uses a Marshal (or YAML) dump as a snapshot of your
universe, plus incremental transactions for updates.

At startup it reads in the last snapshot plus the intervening
transactions. This means you only have to write out a full snapshot
occasionally. The code is also small and simple enough to be easily
understood.
 
B

Brent Dillingham

Some great responses here! Thanks everyone.

Madeleine is definitely something I'm going to give a try. I'd heard
of it before, but no one seems to talk much about it... so I was kind
of hoping someone would mention it here.

I want to avoid hitting a database as much as possible, so it seems
like a decent solution. RAM is cheap copious enough these days that I
shouldn't have any problem keeping the entire game in memory (we'll
see). It's possible that I'll end up with some kind of hybrid solution
like Jeff mentioned.

I'll try to report back when I get a little further along for the
benefit of others that may have similar questions down the line :)

Thanks again,

Brent
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,740
Latest member
JudsonFrie

Latest Threads

Top