30 Aug, 2008, Lobotomy wrote in the 1st comment:
Votes: 0
I recall hearing of wanting to discuss these sorts of things more, so I figured I'd fire up a sort of catch-all thread for speaking on the issue as a whole. If I'm not mistaken, the point will be to discuss various design features and issues, such as if there were a 'dream codebase' of sorts then what all would we expect out of it, as well as the varied drawbacks, problems, design flaws, what-have-you with existing games and codebases, whether they be commonly seen/perpetrated problems, or ones that are more rare.

One foremost thing I can bring up off the top of my head is in regards to a game's stability. Data loss is one of the big problems I've seen over the years with various games, in various incarnations.

The first problem is data file corruption. A crash may occur during a save, a string hashing table (if existing on a game) may get corrupted due to hashed/unhashed string confusion, or maybe a crash occurs on a game using a signal handler and somehow a data save succeeds in occuring that writes corrupted memory values to a file. However it occurs, it tends to be an issue with a lot of games (mostly stock ones). There are some codebases that don't address the issue even at all (mostly the major stock variety), and some codebases that try, but ultimately don't succeed in hitting all the possibilities (DBSC is one example - it backs up files, but only goes to a backup if the primary file is 0 bytes; if the file contains data, however incomplete, it will ignore the backup. And that's only with player files - all other data files are unprotected). Granted, preventing data loss of this type 100% of the time is not possible. However, it is possible 99% of the time, and I personally feel it would be great to see that amount in use as opposed to anything less.

The second problem is non-persistent copyover/hotboot states. This problem I've seen myself largely on games in active development, especially where development is performed on the active port. Some people alleviate this issue for the most part by doing development on a seperate port, then updating the primary port at particular intervals. I don't personally like this approach, as I favor developing on the active port - riskier, but a quicker source of bug reports in new code, I find. To each their own in that regard. However, that's not the point I'm getting at here. Rather, a lot of the data loss has to do with copyover/hotboot systems that are incomplete. Various data is not persisted through the process. Rather, it merely becomes more like a game reboot that you don't have to login again for. Infamous in various games is the "I dropped X item(s), copyover occurred, now my item(s) are gone." Less notable are things like being in battle (any particular type of game), or being in a sort of delayed transit (like hyperspace in space games), or being in a timer or queue of some sort, only to have all of these things reset or vanish or merely fail to complete as they normally would have. Even less notable, but by no means less annoying, is being in a login or character generation screen/system of some sort, only to be kicked off the game and/or having to start over again from the beginning as a result. I would personally like to see more games with a wholly persistent copyover state, quite honestly, meaning that everything that is even remotely possible to persist through the process should be persisted so that the differences between the game state before and after the copyover should be as miniscule as possible if not entirely nonexistent. Again, data loss in this manner is not 100% preventable, but it is at least 99%.

Anyhow, that's what I'm going to throw in for the moment. I have many more subjects to bring up on the matter of this thread, but I'll wait to see where things go first.

Edit: Fixed some typos.
30 Aug, 2008, Zenn wrote in the 2nd comment:
Votes: 0
As for the copyover thing, I'm fairly sure some SWRs have a copyover system that saves the world state. One major flaw, though, is that you can't copyover if anyone is in a line editor or fighting – thus the command 'wpeace' (world peace) was created to alleviate that. Though there are definitely flaws, the system DOES work.
30 Aug, 2008, Guest wrote in the 3rd comment:
Votes: 0
Data file corruption is indeed a big problem, something I feel is aggravated by people who insist on using things like "crashover" - detect a crash, perform a copyover, rather than let the thing just die. The chance of the copyover routine saving bad data is pretty high since a lot of what goes bad is usually player related. Smaug does make some small effort to try and prevent it when using OLC with areas. A backup file is made, by renaming the current file with .bak on the end, and then the current data is saved. If it should corrupt during save, the backup can be restored. Though that does need to be done through the shell. Smaug's general file loading routines are fairly robust. They will ignore bad data on the fly and will load with an incomplete set. Probably not the best approach, but it's better than half a pfile crashing the game when it loads.

As far as fully persistent copyover. I think what I did with the hotboot code comes closer than most efforts. It will perform a save of all items on the ground, all current states of NPCs, and of course the current status of the player. Timers and other stuff would be a lot harder to maintain, if it's even possible with Smaug's current structure. I'm not sure that treating it as a persistent method for loading new content is really the best way to handle using it though. If you want that kind of persistence then it seems more logical to move to a database driven game model. That way changes get made, they take affect immediately, and you don't need to reboot at all for the main port to pick it up. Things just reset in their new form at the next internal update cycle. For efficiency's sake perhaps a flag on the area to signal its content was changed so that the game doesn't spend too much time doing DB queries just to see if something has been updated. Then clear the change flag after the update cycle.
30 Aug, 2008, Kayle wrote in the 4th comment:
Votes: 0
Zenn, you completely missed the point. He wasn't saying that they don't make things persist, He was saying they don't make ENOUGH persist.

And I'm inclined to agree. I'd like to see changes/enhancements to the hotboot/copyover systems that allow for people who are making a character to remain connected and not have to start the process of creating a character over again. But on this note, on some games, that might not be a problem. Creation might be short and sweet. However, on MW, creation isn't short and sweet, and players can go so deep as to choose their own appearance (foundations for eventually maybe going graphical :P). The battle thing is easily prevented, like Zenn said of not allowing copyover/hotboot to proceed if there are people fighting, So you do the polite thing and allow them to finish, and same goes for the line editor. Overall, I pretty much agree with everything Lobotomy said, and can say that I'd be all for a search for solutions to all those problems in a generic way. ;)
30 Aug, 2008, David Haley wrote in the 5th comment:
Votes: 0
The funny thing about making the data files more robust to failure is that it's not even that hard. The problem now is that when you save a pfile, you destroy the old one as you write out the new. One simple fix would be to write to a temp file, and only move that on top of the old one when you're done.

Copyover is pretty complicated because it's unclear what the semantics of resetting the code while maintaining the world are. What if you save a world state that is no longer consistent with the new code?

I've never really liked the idea of a copyover. It's kind of like a lazy man's reboot when you don't want to disconnect your players. (Oh no, a reconnect!) I'm starting to favor an approach that moves game logic to Lua, so that you can refresh game logic without even a copyover. Nobody would really know anything is actually happening. Of course, you still have issues with data being perhaps nonsensical across logic updates, but I'm not sure that is really avoidable when you have major changes. It's pretty analogous to save games for some games not being compatible between versions of the same game mod: if something fundamental enough changes, it's really not obvious how to preserve the data.
31 Aug, 2008, Kline wrote in the 6th comment:
Votes: 0
Why even go as far to use Lua? Compile separate shared object files and just reload those in memory. It's how I run a good bit of code in one of my games. Trying to keep only the "core" of things in the executable, like a SocketMud, and put the rest into separate object files to load/reload in memory when needed.
31 Aug, 2008, David Haley wrote in the 7th comment:
Votes: 0
Because Lua has lots of other advantages anyhow. :wink: The shared object approach is great though if you don't want to leave C/C++ land.
31 Aug, 2008, Guest wrote in the 8th comment:
Votes: 0
Using dynamic modules is something I wanted to do with AFKMud but never figured out how to do on a grander scale than isolated do_fun commands. I wanted to make things like IMC2 into plugin files that would compile if you had them, not compile if you didn't, and the base code would adjust itself properly. Things like Lua just make that easier to deal with since it's designed with this in mind.
31 Aug, 2008, Caius wrote in the 9th comment:
Votes: 0
Lobotomy said:
The first problem is data file corruption. A crash may occur during a save, a string hashing table (if existing on a game) may get corrupted due to hashed/unhashed string confusion, or maybe a crash occurs on a game using a signal handler and somehow a data save succeeds in occuring that writes corrupted memory values to a file. However it occurs, it tends to be an issue with a lot of games (mostly stock ones). There are some codebases that don't address the issue even at all (mostly the major stock variety), and some codebases that try, but ultimately don't succeed in hitting all the possibilities (DBSC is one example - it backs up files, but only goes to a backup if the primary file is 0 bytes; if the file contains data, however incomplete, it will ignore the backup. And that's only with player files - all other data files are unprotected). Granted, preventing data loss of this type 100% of the time is not possible. However, it is possible 99% of the time, and I personally feel it would be great to see that amount in use as opposed to anything less.

A possible solution to this is to build the entire file in memory first (In C++ you could use for example an ostringstream for this. In C you would of course need to deal with possible overflows if you use char based strings). Then you can dump the data to file afterwards. This effectively eliminates the problem of a partially written file, and your only real concern at this point is the lower level function that actually writes the file itself (like fprintf, fstream, etc).

It's not a 100% solution of course, as you can still get garbage data. But I think it's a step in the right direction at least.
31 Aug, 2008, Kline wrote in the 10th comment:
Votes: 0
Samson said:
Using dynamic modules is something I wanted to do with AFKMud but never figured out how to do on a grander scale than isolated do_fun commands. I wanted to make things like IMC2 into plugin files that would compile if you had them, not compile if you didn't, and the base code would adjust itself properly. Things like Lua just make that easier to deal with since it's designed with this in mind.


It's not too bad. I do have a lot of simple do_funs in object files instead now, but also some core functions like move_char and some other handler stuff. The more the better, less that I have to recompile the actual game just to change. The only issue's I've had are if you typo when calling for an object symbol – the game doesn't tend to like that too much.
31 Aug, 2008, Lobotomy wrote in the 11th comment:
Votes: 0
Caius said:
A possible solution to this is to build the entire file in memory first (In C++ you could use for example an ostringstream for this. In C you would of course need to deal with possible overflows if you use char based strings). Then you can dump the data to file afterwards. This effectively eliminates the problem of a partially written file, and your only real concern at this point is the lower level function that actually writes the file itself (like fprintf, fstream, etc).

It's not a 100% solution of course, as you can still get garbage data. But I think it's a step in the right direction at least.

That is a rather interesting method, as compared to the usual. Maybe a combination of that along with temporary backup files would be an ideal situation (in a flat-file system). Currently, I only make use of the temporary backup method; it works well - but additionally security can't hurt (other than a little extra overhead for each save). Additionally, something like that would open the way for another debugging tool of sorts, in that you could do a 'test save' and view the output of a save function instead of actually doing it, if one needed to test what the data would look like directly; such as when adding new systems and whatnot.

Kline said:
Why even go as far to use Lua? Compile separate shared object files and just reload those in memory. It's how I run a good bit of code in one of my games. Trying to keep only the "core" of things in the executable, like a SocketMud, and put the rest into separate object files to load/reload in memory when needed.

That sounds interesting too, although I wonder what the particular usefulness of it really is. If the code of a game is particularly large, bloated, or otherwise convoluted and requires a long while to compile I suppose this could help somehow, but I'm not sure I really see where this applies to a wholly persistent copyover system. Then again I'm not familiar with the practice of loading individual object files at all (your post is news to me :tongue:).
31 Aug, 2008, Kayle wrote in the 12th comment:
Votes: 0
Lobotomy said:
Then again I'm not familiar with the practice of loading individual object files at all (your post is news to me :tongue:).


The way I've always understood it, is that it's like how Windows handles .dll files.
31 Aug, 2008, Kline wrote in the 13th comment:
Votes: 0
Kayle said:
The way I've always understood it, is that it's like how Windows handles .dll files.

Pretty much. While it's not a direct route to a persistent game, if you implemented it well enough you'd not even need to reboot hardly ever (ala LPC).
31 Aug, 2008, Conner wrote in the 14th comment:
Votes: 0
Kayle said:
Lobotomy said:
Then again I'm not familiar with the practice of loading individual object files at all (your post is news to me :tongue:).


The way I've always understood it, is that it's like how Windows handles .dll files.

Oh, so he's talking about making a game that's entirely centered on achieving the 'blue screen of death'? :wink:
31 Aug, 2008, Caius wrote in the 15th comment:
Votes: 0
Samson said:
Data file corruption is indeed a big problem, something I feel is aggravated by people who insist on using things like "crashover" - detect a crash, perform a copyover, rather than let the thing just die. The chance of the copyover routine saving bad data is pretty high since a lot of what goes bad is usually player related.

"crashover" :lol: I like that.

I fully agree with this. I can't see many compelling reasons for using such a system. In fact, I suspect an emergency copyover system can easily tempt you to postpone investigating and fixing the source of the problem.
31 Aug, 2008, David Haley wrote in the 16th comment:
Votes: 0
id Software used a separation of game engine from game logic for several of their games. It's what made it so easy to mod them: they could "give away" the source code to the game logic, which really wasn't the most interesting part of their game, and let people do whatever they wanted with it. It's how things like Counterstrike game about (and, for that matter, how Half-Life itself came about!). Well, anyhow, what Kline is proposing is basically the same thing, but instead of having a core and one dynamic library, you have a core and several libraries. Now, a copyover is just reloading code objects, instead of reloading the whole executable. As a result, you don't lose the data space, which is the problem with persistent copyovers.
02 Sep, 2008, Caius wrote in the 17th comment:
Votes: 0
Speaking of dynamically loaded modules. I came across a great article on linuxjournal.com about using the dlsym interface to load C++ classes dynamically, while retaining the ability to use them polymorphically. I also tried the examples provided, and they compiled and worked properly. Worth a read.
24 Sep, 2008, Vassi wrote in the 18th comment:
Votes: 0
DavidHaley said:
id Software used a separation of game engine from game logic for several of their games. It's what made it so easy to mod them: they could "give away" the source code to the game logic, which really wasn't the most interesting part of their game, and let people do whatever they wanted with it. It's how things like Counterstrike game about (and, for that matter, how Half-Life itself came about!). Well, anyhow, what Kline is proposing is basically the same thing, but instead of having a core and one dynamic library, you have a core and several libraries. Now, a copyover is just reloading code objects, instead of reloading the whole executable. As a result, you don't lose the data space, which is the problem with persistent copyovers.


I've never really understood what this hotbooting stuff was all about, much like David here I don't see the problem with just making people reconnect. Either that, or don't update in the middle of the day.

On the other hand, the above comment I do agree with. Most of the time, I'm guessing, this hotbooting stuff is to hotfix smaller issues in a command, a spell, or something like that. In which case a scripting language like LUA\Python will do you right by having the logic figured out at runtime. What I do is identical, just recompile the C# command and create, then replace, the instance of it in my command Dictionary with reflection. Its the equivalent of swapping out a .lua or .py command file.

If you're making a core\server update though, I don't see why it's such a big deal to just make people reconnect in 5 minutes or so when you're done.

Edit: If you're not familiar with C# or .NET in general, when I say re-compile I mean at runtime via the CodeDom. It creates another assembly in memory and then I can swap out my command class for the one it just compiled without having to restart the game.


V
24 Sep, 2008, Vassi wrote in the 19th comment:
Votes: 0
One other contribution to this topic that I'd like to make is the idea of a paired client\server or a more widely-adopted (and opensource or not commercial) mud client that features plugin-type architecture for making muds more 'robust'.

There's only so much you can do on the server end before you have to rely on the client end to take things forward that extra notch. For instance, with the advent of Javascript frameworks there has been a huge rise in functionality and usability on websites lately. We need some kind of universal client, or client-pairing on a per-codebase basis, that makes it easier to do slightly fancier stuff.

Things like mapping, GUI bars, etc. Something as simple as keeping the names of creatures\players in the room always visible\updated on some kind of display, etc. The human brain processes things visually, MUDs can become more complex if they can learn to visually display data in more efficient ways. (ASCII art doesn't count, they're still text that has to be interpreted in the brain - and reliant on your use having a monospace font at that, it's pretty much the ceiling on what can be done server-side)

A client supporting an extensible protocol, like ZMP, has to come into existence. Until then, I'll keep writing my own client to match my codebase. Someday™ though, I would like to seriously take a stab at making an easily extensible MUD client that can, for instance, connect to a MUD, be recognized by said MUD and fed some startup scripts, use said startup scripts to configure its front-end GUI\Panels and just work without the user having to download files and install plugins and all that crap.

Edit: Here's a video to my current client: http://www.cyra.ws/clientpreviewed/ It loads most of its graphics, like the GUI textures, day\night icons from the web at startup. This kind of client is easy to build in newer technologies - like WPF - but it would need to be ported to Linux\Mac to make it truly universal. I suppose Java is an option, but I don't know much about its presentation frameworks, I've heard not good things about them.


V
24 Sep, 2008, David Haley wrote in the 20th comment:
Votes: 0
MUSHclient shows that you can really do a lot with client-side support for rich window handling, and output from the MUD that is easily machine readable. You don't – strictly speaking – need any universal protocols, you just need modules for each client to parse the data from the MUD in question and do something appropriate with it. Unfortunately, this (obviously) means that you'll need one parser module per MUD/client pair. Still, I think that is a more realistic prospect than trying to convince everybody to adopt a single protocol…
0.0/40