Forum: Ruby Object Persistence for a MUD

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
E71822bf2365c34214d8e69bb3b432f3?d=identicon&s=25 Brent Dillingham (Guest)
on 2008-10-05 03:48
(Received via mailing list)
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! ^_^
289cf19aa581c445915c072bf45c5e25?d=identicon&s=25 Todd Benson (Guest)
on 2008-10-06 01:49
(Received via mailing list)
On Sat, Oct 4, 2008 at 8:46 PM, Brent Dillingham
<brentdillingham@gmail.com> wrote:
> have to be really careful when using a lot of the ORM niceties while
> 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
C1b6b5557723c9db912b075e954166d3?d=identicon&s=25 Jeff Moore (djief)
on 2008-10-06 02:29
Brent Dillingham wrote:
> 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
6e366eb5a71be2bad7f383d42aeb4788?d=identicon&s=25 Justin Collins (Guest)
on 2008-10-06 04:32
(Received via mailing list)
Brent Dillingham wrote:
> have to be really careful when using a lot of the ORM niceties while
> player.room
> 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
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2008-10-06 20:24
Jeff Moore wrote:
> 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.
E71822bf2365c34214d8e69bb3b432f3?d=identicon&s=25 Brent Dillingham (Guest)
on 2008-10-08 00:00
(Received via mailing list)
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
This topic is locked and can not be replied to.