10 Apr, 2014, Davion wrote in the 41st comment:
Votes: 0
quixadhal said:
Davion said:
It is public. I've made it public since the beginning. I've already translated most of it and I have all the areas booting. The game only loads stock rom. I think. I'm currently in the process of translating all the player commands. Just knocked off act_comm.c. Heh 10k lines to go! It boots to the point where you can get in the game and watch it crash. But the world boots and resets.


Are you going to keep the old area formats, or do something clever like have it read/write JSON file formats natively? I've been pondering rewriting my own DikuMUD in another language (maybe perl!), and think I'd rather make the old C code emit JSON formatted files so the newer version stays lean and doesn't have to hold onto so much legacy stuff.


Once I get it translated I'm not all to sure what I'm going to do. Saving areas isn't part of stock ROM fortunately, so all I had to deal with was the loading. It's pretty slow compared to it's C counter part so a change is probably necessary. I'd like to see it tied into Django. That'd be pretty cool. We'll see. There's a lot more commands to translate haha.
11 Apr, 2014, Davion wrote in the 42nd comment:
Votes: 0
I'm making good progress. I've knocked off act_info, act_wiz, act_info, act_comm and act_enter. About 4k lines left to translate for the commands. Once that's done it's just the con_playing state and player saving/loading. I'm half tempted to translate saving/loading directly but at this point I'd like to just finish it off ;). The easy thing to do seems to just copy ch.__dict__ and add/replace stuff to that. However, to make it more rom like, I'm considering using an OrderedDict and creating it field-by-field and pretty printing that. It'd give the key-value feel without all the annoying file i/o, as well as keep the pfiles ordered the same as the originals.

I've neglected to translate notes, bans and jukeboxes. There's probably a better way to do notes and bans. Jukebox was just a… "why does this exist" type thing ;).

@Quix: I've been thinking more about what to do when I'm finished the translation, and I'm likely going to do nothing publicly once I finish the translation. Most of this is so I have something I can tinker with when I'm bored. Since working with Python I've found it incredibly difficult to go back to C. I do force myself every once and awhile. Recently I ventured back to C to write up a small grid-display system. It was painful. I got my desired result but I had to pull out nasty little things like "char *** ptrs;". I'll likely release snippets and of course fix bugs that popped up in my translation. I did have a notion when I started this to call it RaM Plasma but I dunno. Maybe if I get around to applying some of the changes from RaM. I think the first few things once completion will be some form of OLC system. Things like mset, rset that allow you to set extra descriptions but not save them, boggles my mind! Did people set up macros for when the MUD rebooted or something to make sure these things persisted?
11 Apr, 2014, quixadhal wrote in the 43rd comment:
Votes: 0
I can't speak for ROM, but in my old DikuMUD (which had no OLC), you used commands like pset to manipulate strings on players or instances of objects, which all got saved with the player as part of their inventory.

I would probably consider taking all those various act_foo files and making a better system than a bunch of subroutines in a few files. Since the stuff in those is generally meant to be commands, I would actually make them independant files that get searched by the interpreter, so you can add a new command by just dropping it in the right directory (player/wizard/etc).

You can, of course, pre-load those files at boot time so the disk doesn't need to be hit… but it means you could create a new command on the fly and just "rehash" the command system so it finds (and loads) the new file.

I'm doing a similar thing with my old DikuMUD, but in perl… and all by hand rather than trying to automate the process. That's why I'm thinking about stuff I'd rather do differently. I plan to store things in JSON files that can just be loaded directly into objects, rather than parsing a silly text file format… especially with OLC, that seems silly.
16 Apr, 2014, Davion wrote in the 44th comment:
Votes: 0
quixadhal said:
I can't speak for ROM, but in my old DikuMUD (which had no OLC), you used commands like pset to manipulate strings on players or instances of objects, which all got saved with the player as part of their inventory.

I would probably consider taking all those various act_foo files and making a better system than a bunch of subroutines in a few files. Since the stuff in those is generally meant to be commands, I would actually make them independant files that get searched by the interpreter, so you can add a new command by just dropping it in the right directory (player/wizard/etc).

You can, of course, pre-load those files at boot time so the disk doesn't need to be hit… but it means you could create a new command on the fly and just "rehash" the command system so it finds (and loads) the new file.


Ya, that's totally how it works. For everything but rooms it seems. Anyways. I've found sticking all the act_foo.c files into one file and just going through it line by line is the best way for speed. When I reach what would be the end of a file I just chop it off and create a new .py file. I'm trying not to drastically change anything yet ;).

quixadhal said:
I'm doing a similar thing with my old DikuMUD, but in perl… and all by hand rather than trying to automate the process. That's why I'm thinking about stuff I'd rather do differently. I plan to store things in JSON files that can just be loaded directly into objects, rather than parsing a silly text file format… especially with OLC, that seems silly.

You are a glutton for punishment it seems ;). This is not the first time I've attempted this. My initial problem was translating from something like C which is procedural to python which is functional. This was compounded by the fact I was also attempting to change the logic on somethings to be higher level (bits, file io, bits + file io *facepalm* to name the two worst). This time around I'm focusing on just getting it working in Python. Once that happens I'll be looking at changing the backend. Not sure how big your Diku is, but stock rom is around 40k lines. It's a lot to keep together when you're changing so much so quickly.
16 Apr, 2014, quixadhal wrote in the 45th comment:
Votes: 0
That's why I'm not trying to do a direct translation automatically. wc -l `find . -name \*.[ch]` reports about 98K lines. :)

However, I'm just writing parts of it as needed by following the program flow. At the moment, it's simply the weather and time systems, along with argument parsing, and sockets… but as I build each chunk in, I also generate utility code it uses. So the basic idea of making every entity be able to save and load itself from JSON is done, which means no more stupid file I/O parsing.

For example:

The weather system is now an object (singleton) which is created at boot time, rather than a bunch of functions. It manages its own data to preserve weather state in a file between runs. When the object is created, the constructor new() sets itself up and restores any state info from disk. When it is destroyed, it saves itself back to disk (which happens automatically when the program exits).

The perl code for this:

=item new()

Constructor. WHen we create a new instance, we check for
a way to restore previous values, otherwise we use defaults.

The weather daemon relies on values from the centralized
time daemon. This should be passed into the constructor.

=cut

sub new {
my $class = shift;
my $time_daemon = shift;
my $self = {};

die "Cannot initialize weather system without a valid time system!" unless $time_daemon->isa('Mud::GameTime');
log_boot "- Resetting game weather";

if( -r $filename ) {
my $data = load_file $filename;
$self = decode_json $data;
$self->{_time_daemon} = $time_daemon;
} else {
$self = {
_time_daemon => $time_daemon,
_pressure => 960,
_change => 0,
_sky => 'clear',
_wind_speed => 0,
_wind_direction => 'calm',
_category_shift => 0,
};
}

bless $self, $class;
$self->update();
return $self;
}

=item TO_JSON()

This method is called by the JSON package to convert a blessed reference
into a normal hashref that can be encoded directly with JSON. We take
advantage of this to omit fields we do not want to save.

=cut

sub TO_JSON {
my $self = shift;

my $obj = {};
$obj->{$_} = $self->{$_} foreach grep { ! /^(_time_daemon)$/ } (keys %$self);
return $obj;
}

=item DESTROY()

A special function called when the weather object is destroyed.
Normally this will happen during shutdown. This code should save
the weather state back to disk.

=cut

sub DESTROY {
my $self = shift;

log_info "Saving weather data to $filename";
my $json = JSON->new->allow_blessed(1)->convert_blessed(1);
my $data = $json->encode($self) or die "Invalid JSON conversion: $!";
open FP, ">$filename" or die "Cannot open $filename: $!";
print FP "$data\n";
close FP;
log_warn "Weather daemon shut down.";
}


The only code I need to write is the TO_JSON function which the JSON library calls automatically to serialize a class object (giving you a chance to control what gets saved… no value in saving an object reference for example). The load_file() routine is just a few lines that I didn't feel like repeating in every module that has to load files into memory.

=item load_file()

A simple routine to read in a text file from disk and
return the result as a string. This is trivial, but it is
more convenient to use 1 line of code elsewhere than 3 or 4.

=cut

sub load_file {
my $filename = shift;

return undef if !defined $filename or !-r $filename;
my $data = '';
open FP, "<$filename" or die "Cannot open $filename: $!";
while(<FP>) {
$data .= $_;
}
close FP;
return $data;
}


The actual JSON file it creates and uses looks like this:

{"_sky":"stormy","_change":0,"_wind_direction":"calm","_pressure":988,"_wind_speed":0,"_category_shift":0}
14 Jun, 2014, Davion wrote in the 46th comment:
Votes: 0
I've made a lot of strides on this codebase. I've got all the code translated and a lot of it functioning. There'll still be a few things that got missed in the translation but it works mostly! I uploaded the first official alpha release, and have been doing a bit of tinkering since. I'm enjoying it so far. Something really has to be done about the boot time.
[link=file]2895[/link]
15 Jun, 2014, quixadhal wrote in the 47th comment:
Votes: 0
Neat, I'll take a look! :)
15 Jun, 2014, Davion wrote in the 48th comment:
Votes: 0
quixadhal said:
Neat, I'll take a look! :)


I've updated it quite a bit since the initial release. There were a few scoping issues from the translation that made for some really interesting bugs and made the game look extremely broken. It's a lot more stable now to move around and look at stuff. Makes it a lot easier to start testing. The boot time is terrible and it seems to be from the mass loading of objects and not necessarily the I/O.

I'm hoping once I can verify the loading is 100% I'll work on converting it to json in hopes it speeds everything up.
16 Jun, 2014, syn wrote in the 49th comment:
Votes: 0
As I have been speaking with Davion, I have forked the project and gotten it up and running with python 3.3

Overall this is an awesome project and I am very happy to be helping out in any small way.

Amazing job so far Davion!
16 Jun, 2014, quixadhal wrote in the 50th comment:
Votes: 0
I wouldn't worry overmuch about the boot time… unlike the old C version, you probably won't have many crashes to require reboots, since you won't have pointer errors every time you turn around. *grin*

I think I'll try to get this working in visual studio, also with python 3. That sounds fun. :)
16 Jun, 2014, syn wrote in the 51st comment:
Votes: 0
Right now im trying to work out why in p3 in visual studio everything's initial position is being set to 'stand' like the dict entry for do_stand thats the only place in the code that uses 'stand'
16 Jun, 2014, Davion wrote in the 52nd comment:
Votes: 0
quixadhal said:
I wouldn't worry overmuch about the boot time… unlike the old C version, you probably won't have many crashes to require reboots, since you won't have pointer errors every time you turn around. *grin*

I think I'll try to get this working in visual studio, also with python 3. That sounds fun. :)

Don't work too hard :) Just run the latest checkout. I was able to port Syn's Python 3 changes in without rolling back any of my fixes. A bunny can still kill you, don't worry!
16 Jun, 2014, syn wrote in the 53rd comment:
Votes: 0
So I figured out why it's crapping out for me, when switching back from POS_XXXX "some string"

On mob load it calls read_word(area, FALSE) making mob.start_pos and mob.default_pos into string carriers

I am working on making a function similar to what is used in ROM to conver that to the value thereof instead
16 Jun, 2014, Davion wrote in the 54th comment:
Votes: 0
syn said:
So I figured out why it's crapping out for me, when switching back from POS_XXXX "some string"

On mob load it calls read_word(area, FALSE) making mob.start_pos and mob.default_pos into string carriers

I am working on making a function similar to what is used in ROM to conver that to the value thereof instead


I wrote a very simple one called name_lookup. I have this all fixed in the latest update.
16 Jun, 2014, Davion wrote in the 55th comment:
Votes: 0
So I guess I don't really have these documented anywhere so here's as good a place as any. Through the translation of this I've had to create a few helper functions to get things done quickly without much headache. Here are the 4 main ones.

def name_lookup(dict, arg, key='name'):
for i, n in dict.items():
if n.__dict__[key] == arg:
return i

A vast majority of the table translations were turned into orderedicts, containing a class of the values, identical to those in ROM. So the syntax "attack_table.noun" works without change through the codebase. What this lets you do is go through and lookup by name, or any other value for that matter.

eg.

attack = name_lookup(attack_table, "bite")
position = name_lookup(position_table, "stand", "short_name")

The first gives attack who's name matches bite.
The second lets searches for the position with the short_name of stand.

def prefix_lookup(dict, arg):
if not arg:
return None
results = [v for k,v in dict.items() if k.startswith(arg)]
if results:
return results[0]
return None


This is a lookup that searches the keys of a dictionary with prefix matching. Eg "exa" will match "example"
skill = prefix_lookup(skill_table, argument)

This only works on dicts that have strings as keys.

def value_lookup(dict, arg):
if not arg:
return None
for k,v in dict.items():
if v == arg:
return k
return None

Just a simple value lookup for dictionaries. Could probably just use dict.index(v) instead.

def mass_replace(str, dict):
for k,v in dict.items():
if v:
str = str.replace(k,v)
return str


This is what it says it is. It replaces all the key:value pairs of dict with str. I use this in act

format = "$n, $e, $m, $s"
act_trans = {}
act_trans['$n'] = "Me"
act_trans['$e'] = "He"
act_trans['$m'] = "Him"
act_trans['$s'] = "His"
format = mass_replace(format, act_trans)

Yields "Me, He, Him, His"
16 Jun, 2014, quixadhal wrote in the 56th comment:
Votes: 0
Well, I still want to get things working properly in visual studio, but for a start.. here's a version of miniboa that's refactored back like the original author's project was setup. As an added bonus, it now supports translation of Pinkfish-style color tokens into various output styles, including unknown (colors removed), ANSI, MXP, I3, and IMC2.

I'll probably add a reverse conversion at some point to take IMC2/I3 tokens and convert them to native Pinkfish, which would allow for a future I3/IMC2 client.

Note that the color handling really isn't dependent on anything in miniboa, so it could easily be ripped out as an independent thing. I just figured since the original author had terminal handling, it would be nice to restore the functionality, even if it's not used at the moment. I'll probably also change the methods a little… I think optional parameters are nicer than multiple functions that do mostly the same thing.

IE: send(text [, wrap = 0] [, color = 1]) instead of send(), send_cc() and send_wrapped()

https://bitbucket.org/quixadhal/miniboa
16 Jun, 2014, quixadhal wrote in the 57th comment:
Votes: 0
Davion said:
def value_lookup(dict, arg):
if not arg:
return None
for k,v in dict.items():
if v == arg:
return k
return None

Just a simple value lookup for dictionaries. Could probably just use dict.index(v) instead.


I should figure out how to do unit testing in python, so we can compare timings. :)

def value_lookup(dict, arg):
if not arg:
return None
return dict.keys()[dict.values().index(arg)]


One of the python pages I was reading suggested that your method is likely faster for smaller dictionaries (higher likelihood of finding the value without traversing the whole list), and mine is faster for larger ones (where building the list of keys and values once is faster than looping over them).

Always fun to learn new stuff. Now you see why I've grown to despise C over the years. Once you start using a dynamic language, it's painful to go back to freeing the mallocs and having the compiler nitpick you to death. :)
16 Jun, 2014, Davion wrote in the 58th comment:
Votes: 0
quixadhal said:
One of the python pages I was reading suggested that your method is likely faster for smaller dictionaries (higher likelihood of finding the value without traversing the whole list), and mine is faster for larger ones (where building the list of keys and values once is faster than looping over them).

Always fun to learn new stuff. Now you see why I've grown to despise C over the years. Once you start using a dynamic language, it's painful to go back to freeing the mallocs and having the compiler nitpick you to death. :)


There's so many ways to do things, sometimes its hard to pick one ;). I picked this one because mostly I'm searching short lists. The longer lists of ROM (eg. char_list) wont ever be searched that way.

Ya, I've been working with python for awhile now and this is my second attempt to translate the codebase, and by far the most successful. Since I started workin on websites venturing back into C has not been pleasant. I'd occasionally venture back and write something in pure C just to make sure I still could. Never knew when I'd want to pick up a version of ROM and start pluggin away. Fortunately now I don't ever have to! Woo!

It'll be interesting to see how much can get changed once I get going. The code has already shrunk significantly in the translation. Stock ROM is about 45k lines of code, and I think Pyom is just over 21k. That's by far the most impressive thing to me. That, and while playing I notice not speed difference.
16 Jun, 2014, syn wrote in the 59th comment:
Votes: 0
the speed/response while playing is amazing.

I didn't realize you had the name_lookup, silly me so i made a redundant function but for position_table, oh well. good learning experience!

Hey I was thinking it may make sense to set up a proper wiki to document all of the important things, or 'helpers'

I would be happy to set one up, probably will if only for myself anyway
16 Jun, 2014, Davion wrote in the 60th comment:
Votes: 0
Bitbucket has a wiki built into it, as does MudBytes :) It'd be nice if you picked one of those so I don't have to visit another place.

Unfortunately moving to Python 3 has opened a new batch of bugs. It seems the conditions for turning an int into a float for python 3 are considerably looser. I started noticing it first in HP/Mana/Move. Now I fear it may be in hidden places in the formulas. :(. I've notice PC players miss a lot. I'm going to be running while printing a lot of info to hope it's close to ROM style. Unfortunately the effort involved in getting rom to print the same numbers so I can compare is far more then I care to do.

…Has anyone ever actually leveled on stock rom?
40.0/93