View Single Post
Old 11-15-2012, 10:55 PM   #3
camlorn
Member
 
Join Date: Aug 2011
Posts: 144
camlorn is on a distinguished road
Re: Event-based with threads instead of game loop

All right, I'll bite.

Yes, it is possible. You do not want to do this.

There was an attempt, that I heard about through hearsay, to do this very thing. It ended miserably, maybe someone knows what this was. I saw it in a thread, around one of these sites...anyhow.

Coffeemud is done in java. I don't know if it's "dead", but it is more featureful than a lot of options. Do remember that whether or not a codebase is dead isn't the point: codebases are starting points, not finished games, and a quality codebase is a quality starting point even if it isn't actively developed (that's your job, once you start your mud, anyway).

So, threads. You can't do a lot of things that seem like a good idea: you can't have a thread per object, you can't have a thread per mob, you can't do any of this stuff. You're going to have all sorts of problems, especially if you want to give your builders any real power with a scripting language: now builders have to learn about locks and mutexes, or you've got a thread that's always running scripts for your mobs, and that's going to partially defeat the purpose. There's a reason game loop with non-blocking IO is popular (see merc, or just about any codebase out there): it can be debugged without pulling your hair out, you can find coders that can understand it, you can find coders that can, in short, actually manage to code on your project. If you are a really good programmer with experience in this area, you will be able to pull it off, but no one else, not the typical adopters of your codebase anyway, will be able to work with it. There is a reason that people don't use threads heavily in everyday programming. If you do this only for your game, you might be able to get away with it, but you're going to have to find a way to build an abstraction layer that makes building single-threaded, somehow, and you can't be telling builders, who in a lot of cases don't really know programming, "O, that doesn't work, it creates a race condition, write it another way". Congradulations, you've got a technologically cool project that no one else can understand, but hey, that's ok, it uses threads.

You're not gaining anything, not really: it's going to be slower than the game loop approach, at least for an average mud, whether it lags the machine or not. 99% of the time, even if you're doing some pretty serious string processing, you're not going to lag the mud noticeably; I don't remember where I got this statistic, but for the average person, the mud doesn't lag until after 100ms of unresponsiveness, that is, no one notices. So cut that in half, and you've got 50ms before you have to get back around to a player again--in computing terms, for a mud, that's rather a lot of time nowadays. Also, my understanding is this: you can have as many threads as cores on your machine before you start losing performance: if your codebase will never run on another machine, under another java runtime, you can tune it. Otherwise, it needs to self-tune.

I think, if you're going to use threads, some sort of "worker" threads aren't a bad idea. I'd say, for example, that moving the asynchronous loop to pull input from players to a thread isn't a bad idea, but I'd not give each player his or her own thread. In addition, I'd not process the commands there, I'd add them to a commands-to-process list and process them in the game loop. I'd say, if you're making a long query to a database, maybe thread with callback. But threads, in mud programming, are not the best way, not by a long-shot.

If you are dead set on this, you need to probably look at something like akka. It has actors, which are like threads but not really: an actor is like a method call, and if more than one can be executed at once, akka will attempt to do so. For real-world use, I'd avoid even that and just get some sort of implementation of futures; you're not using scala, so the code would be a bit ugly, but this is available in java, and does something that I think would be worth the cost of added complexity.

Finally, do keep in mind that this will come back to bite you one day. You'll be programming happily along, and all of a sudden the mud will just freeze, and you'll have to find the resource two of your important threads want to use at the same time. Off the top of my head, a few things that make this complex: caching data to disk (two threads want access to an object not currently loaded, and they want it at the same time. They both decide to load it. What happens?), two commands that want to delay their output getting interleaved on my terminal because you used threads (spells and skills do this, at least), the fact that combat is typically done in rounds (good luck determining which order people get hit in, if everyone might be running on a different thread, and yes this does matter: whether or not I hit first or you hit first will make the difference in some strategies, and will add an unintended random variable to combat)...

If you have a lot of prior experience with threads, go for it, I guess, but no one else will be grabbing your project in a hurry.

It is argueable, if you want to look for a happy middle ground, that continuations would be useful. Java does not support this, and it would be hackish to get it to do so, and your code would be ugly, but if you're not adverse to porting to scala or something that does, it does exist. The gain may not be worth the trouble, not at first, but it would allow you to write commands that can give other commands the ability to execute at the same time, and someone could maintain the parts that don't need you to know about it, i.e.:
pause() //give the mud a chance to do stuff.
And when it gets back to you and resumes your continuation, you're in the same state (kind of) than when you paused. But this also has issues, and in a multi-player environment is probably going to have more problems than it's worth, and I don't understand them enough that I can list those problems for you without a lot of reading that I'm not going to do right now.

For the record, I did think threads were the future for muds once. Save yourself the trouble that's coming your way and just use the game loop: there's a reason people haven't moved away from it, there really is.

edit:
I missed the part about An executor and this makes this less bad, but only slightly. You're still going to have all sorts of problems, once you move to mob scripting and combat, and you're only going to implement the command interface once anyway. Most people, on the current codebases, never have to touch the i/o portion: just add your command and function pointer to a table in c++, or in coffeemud/etc just add it to a directory and java reflection can find it. Do be aware that most muds, disregarding lpc and the 4 or 5 big ones, use under 10 mb ram and under 1% cpu (this varies depending on cpu, obviously). If you really want something that doesn't use a game loop, there are other options, i.e. the abovementioned actors and futures, that may work better, depending (actors are not deterministic, if I recall correctly, so that might be the issue there). There are codebases that have implemented events on top of a game loop, and I do like this system: "do this in 5 seconds" is fine, so long as the underlying implementation is reasonable. If you insist on using threads, and this is intended for the general mudding community to use, the fact that it uses threads needs to be hidden very, very well: i'd say 99% of the time, as someone working with the codebase, I need to not see the word thread or think the word thread. Actors, by the way, scale really well: i've heard everything from 50000 to millions, and suspect the truth is somewhere inbetween (and depends on machine).

Last edited by camlorn : 11-15-2012 at 11:18 PM.
camlorn is offline   Reply With Quote