11 Aug, 2014, Sorressean wrote in the 1st comment:
Votes: 0
Hello all:
I am looking at about a 3 month release date for Aspen (I have a few more big features to add and some cleanup to do), but I have the time for the next few weeks to really hit it.

Something I would really like to do is make this multithreaded. I would really like updates to run on their own threads, then clients to be balanced across threads to make everything a bit faster. While there are currently no bottlenecks, I'd really love to be able to queue async tasks and let them run, for example area generation for procedurally generated areas. I am also looking at writing an OLC web interface and would really like the webserver to live on it's own thread, or at least handle clients on threads. I am posing this idea to see if others have done this before and what issues you have ran into. For example, here is where I am foreseeing issues, with some potential solutions:

1) When a player enters a room: not all players are going to be on the same thread, so you're going to have to lock to send messages. You're also going to have to lock every time you access that room, then release. It seems like all of the locks to insure that a player isn't leaving while you're examining the room's contents/etc might be more trouble. My possible solution was to put zones on threads, then move the player handling around as they switch zones. So you could balance 20 zones per thread for example, or have them evenly distribute across x threads, then when you move between rooms you don't have to do any locking; same with fighting. Then when you leave the zone, it would just shift you over. This turns zones into big managers though and I want to avoid that.
2) Channels/message broadcasts, who list, etc. Following my solution from above, the channel message could simply be broadcast to the managers, which are separated by zones, then handed off to the players that are subscribed to that channel. Perhaps this could be done through some sort of async framework, but it would be much quicker than locking each player as well as the player list, then sending a message and releasing a lock. I'm also thinking the who list would require a lock on the user list and a lock on individual player information (level, name etc) to insure that nothing changes between the time you access it and write.

Any information here would be greatly appreciated.
Thanks,
11 Aug, 2014, Kelvin wrote in the 2nd comment:
Votes: 0
In the vast majority of cases, you don't want multi-threading on a MUD. If you have built sanely, it's not at all necessary, and it's likely to complicate your future development.

If you are calling external services or have a lot of DB latency, consider cooperative multi-tasking instead of straight threading. Alternatively, realize that we are talking about a MUD, and you are unlikely to ever see the traffic to need to get too fancy with stuff like this. Cache stuff as it becomes problematic and go back to building out your content and the interesting user-facing systems.
12 Aug, 2014, quixadhal wrote in the 3rd comment:
Votes: 0
Just about all of us here have gone down the multi-threading rabbit hole… for most MUD's it never works.

The problem is that a typical MUD system requires you to touch LARGE amounts of data and code to do almost any operation, and in a multi-threading environment, that means you have to impose semaphore locks to ensure ALL the data you need is consistent as you access it. THAT, in turn, means any gains you might have from multi-threading usually get lost in having the majority of your threads sitting on semaphore locks anyways.

So, you add a lot of complexity for very minimal gains, and those gains usually only happen in very specific circumstances. Yes, two npcs might be able to move independantly of each other, as long as they don't cross a zone boundry, interact with any other player or npc, or basically do anything useful. If any of that happens, there will probalby be a semaphore lock that one of them will have to wait on.
12 Aug, 2014, alteraeon wrote in the 4th comment:
Votes: 0
To back up the other posters, multithreading in this way is a colossal waste of time and effort. Both of your proposed threading schemes are a Bad Idea. Avoid if at all possible.

One of the few places where threading makes some sense is in the low level socket stack, using threads for the low level read/write system calls. Even then, a select() call is still faster.

Alter Aeon MUD
http://www.alteraeon.com
16 Aug, 2014, koteko wrote in the 5th comment:
Votes: 0
I'm developing a pretty multithreaded codebase in Java and I'm not having any problems (I find much easier to reason with threads that with game loops and ticks). I'm cheating though: for now, I'm considering each command, event or MobAction a "task" - they are put in a global queue and an ActionExecutor will run them and then block on the queue until a new task arrives.

What I'll probably do however is to make each Action run within each Room object. This means that different Rooms run tasks in parallel, but internally each Room runs them in sequence (this removes the need to lock every single thing your command/action touches). If you strip down the "Task" to the bare minimum (for example, messages send to the players involved can be only queued somewhere at this point, and executed asynchronously) and code well, in a fast language, we are talking about microseconds or at most a handful of milliseconds to execute any Task, be it a fireball or crafting or whatever. Unless you have hundreds of players spawning commands in a single room you shouldn't have noticeable lags (if I remember correctly, there's research saying we don't notice a delay that's less than about 200 milliseconds).

In my case I'm safe because each command has a delay, depending on various factors, and spamming is also blocked by the client (proprietary - if you issue too many commands too fast, you are slowed down or disconnected). Also all entities occupy space, so there cannot be more than, say, 10 players in a single Room.

All in all, I find very easy to reason about Tasks as self-contained pieces of code executed in sequence, and to assign a different Thread to each logged in player. Also I have service threads for other tasks. If they need to act on the world (like propagating an event) they issue Actions that are enqueued like commands.
16 Aug, 2014, Rarva.Riendf wrote in the 6th comment:
Votes: 0
>What I'll probably do however is to make each Action run within each Room object

Your rooms cannot interact with other rooms ? What about ranged weapons ?
16 Aug, 2014, quixadhal wrote in the 7th comment:
Votes: 0
Delays and trying to tie actions to rooms just throws a rug over the core problem. Let's look at a simple example, shall we?

NPC "uberwizard" is in Room 37. He has recently been attacked by Player "Bob", who fled and is now in Room 112. After healing himself, "uberwizard" now wants to kill "Bob" (he's on his hate list), and being a powerful wizard NPC, he can teleport. So, "uberwizard", running in his thread, casts "teleport", which tracks the target and moves the caster to the target's room.

While he's doing that, "Bob", in another thread, has just mowed down some random orc, looted the corpse, and is now casting "recall" to head back to town and sell his vendor trash.

Because they're both running in seperate threads, and are both in different Rooms so their actions aren't being limited by the 'must do things in order' cobble-code, you can't know which is going to happen first. It totally depends on how the OS handles which threads run, how much CPU time they get, how long it takes to context-switch, etc…

Possibility 1: "uberwizard" finishes his spell first, pops into Room 112, sees "Bob" and attacks. Either this will interrupt Bob's recall spell, or more likely Bob's spell will then finish and yank him off to his spawn point… and now both the npc and pc are in a combat state, but not in range of each other… so hopefully both sets of code know how to clean up that mess.

Possibility 2: "Bob" finishes his spell first and heads home. "uberwizard" now either pops into Room 112 and finds nobody there, or he hadn't gotten the target room yet and now follows Bob back to town. Is combat allowed at the spawn point? Will the guards kill "uberwizard" and thus give Bob free loot from the corpse? Will "uberwizard" throw a fireball and kill 12 newbies while he's trying to attack Bob?

The point is, since you didn't lock down the game state, you can't know how that would turn out. Maybe those two threads are both running at the same time in different CPU cores, and one gets context-switched out so some background task can do something. Maybe one CPU core exceeds a thermal threashold and drops in speed, so the thread that started first ends up taking longer…

What you'll end up seeing is lots of non-reproducable errors that you can't explain by looking at the code, because a thread will be iterating over a list and another thread will modify that list during the loop. An npc will be killed while another thread is processing health updates on it. All kinds of ugly stuff that youDelays and trying to tie actions to rooms just throws a rug over the core problem. Let's look at a simple example, shall we?

NPC "uberwizard" is in Room 37. He has recently been attacked by Player "Bob", who fled and is now in Room 112. After healing himself, "uberwizard" now wants to kill "Bob" (he's on his hate list), and being a powerful wizard NPC, he can teleport. So, "uberwizard", running in his thread, casts "teleport", which tracks the target and moves the caster to the target's room.

While he's doing that, "Bob", in another thread, has just mowed down some random orc, looted the corpse, and is now casting "recall" to head back to town and sell his vendor trash.

Because they're both running in seperate threads, and are both in different Rooms so their actions aren't being limited by the 'must do things in order' cobble-code, you can't know which is going to happen first. It totally depends on how the OS handles which threads run, how much CPU time they get, how long it takes to context-switch, etc…

Possibility 1: "uberwizard" finishes his spell first, pops into Room 112, sees "Bob" and attacks. Either this will interrupt Bob's recall spell, or more likely Bob's spell will then finish and yank him off to his spawn point… and now both the npc and pc are in a combat state, but not in range of each other… so hopefully both sets of code know how to clean up that mess.

Possibility 2: "Bob" finishes his spell first and heads home. "uberwizard" now either pops into Room 112 and finds nobody there, or he hadn't gotten the target room yet and now follows Bob back to town. Is combat allowed at the spawn point? Will the guards kill "uberwizard" and thus give Bob free loot from the corpse? Will "uberwizard" throw a fireball and kill 12 newbies while he's trying to attack Bob?

The point is, since you didn't lock down the game state, you can't know how that would turn out. Maybe those two threads are both running at the same time in different CPU cores, and one gets context-switched out so some background task can do something. Maybe one CPU core exceeds a thermal threashold and drops in speed, so the thread that started first ends up taking longer…

What you'll end up seeing is lots of non-reproducable errors that you can't explain by looking at the code, because a thread will be iterating over a list and another thread will modify that list during the loop. An npc will be killed while another thread is processing health updates on it. All kinds of ugly stuff that you['ll spend huge amounts of time and effort trying to track down and work around… so you can have threads.
16 Aug, 2014, koteko wrote in the 8th comment:
Votes: 0
Actually quixadhal, your example is pretty easy to solve, at least with my codebase :)

For starters I consider it a feature that the two spells from Bob and uberwizard can be completed "non-deterministically". But we are talking at most a few milliseconds of difference here.

Also, one point I should clarify is that the Actions that are delayed are two-phased. The first phase checks the conditions and requirements for the action to go on and sets the delay in action. When the action is "counting down", is NOT in the action executor anymore: anything can happen in the meanwhile. So, in my codebase, only two things can happen to this sleeping Action:

- the "sleeping" action is interrupted by another action. A message will be sent to the player and the action itself will not go back into the ActionExecutor.
- the delay ends and the action is "executed": this means it is put into the global queue, and eventually the ActionExecutor will execute it sequentially.

Anyway, let's go with the examples:

Possibility 1: uberwizard enters Bob's Room, does a "look" and an "attack Bob". Both this commands are going to be queued in this Room. Now, Bob's Recall spell is currently in a delay, which in my codebase is a Future. When the delay expires, Bob's Recall will be put in the Room's queue. BUT we just said that uberwizard has issued an "attack Bob". That stops the Future and Bob will receive a message about it. This mechanism is already in place for the "go <direction>" command, whereby if you digit "take <object>" the "Go Future" will be stopped. There is no possibility that the two players will end up in a messed up state, because either "attack Bob" arrives first and stops Bob's spell, or Bob's spell delay ends first and uberwizard will only receive a message "Bob is not here". It doesn't matter how quickly the two things run, there is no race condition.

Possibility 2: I'm not sure I understand what you mean. If "Teleport" uses the Room as a target, then uberwizard will pop into the Room, see nobody, look around… and swear in an arcane language. If "Teleport" uses a person as a target (like some of the spells I'll have will do, even at great distance) then of course uberwizard knows the risk of using that spell, and it's good for me if he dies because Bob had the time to teleport (for example, Bob could have layed out a trap to uberwizard, knowing he would die in a one-to-one combat and being smart about it).

Also, with concurrent programming you always have to plan ahed if using shared, mutable state (like lists accessible by concurrent threads). This is why first of all I use concurrent-safe data structures for everything, and in addition I use a sequential bottleneck (the action executor), that can be replicated to a world/area/room level as shown, and shouldn't cause any troubles. I haven't done it yet as I'm planning to ditch Rooms for a roomless system.

(I work with concurrent and distributed systems every day, so I know how dangerous they are and how difficult is to debug problems. I also know how to keep thing simple, even losing performance, to be on the safe side).

(also, I just noticed that there is a new class in Java 8 standard library: http://docs.oracle.com/javase/8/docs/api...
This means that I can remove my Futures, and just put a delay into the Action itself and then put it in the queue. Then it will only be picked up by the ActionExecutor when the delay expires. Elements are picked up both by expiration time and insertion order. Pretty cool!)
16 Aug, 2014, koteko wrote in the 9th comment:
Votes: 0
Rarva.Riendf said:
Your rooms cannot interact with other rooms ? What about ranged weapons ?


An Event in my codebase is just an Action, so if it propagates room it will enter its ActionQueue and fire when the ActionExecutor gets to it. This means for example that an explosion would "look", from above, like a real one: destroying stuff from the point it started and then all around, concentrically, as the Events are put in the nearby rooms and then recursively into the ones that are more far away (until an internal value of "intensity" goes to 0).

Same would be if I wanted to implement a normal bow+arrow (which is not the case in my mud). From Room A I issue the "shoot" command toward a player in Room B. This creates an Arrow task that is put in Room B's queue. If a player moves away before the Arrow task is executed, then he goes free. This is OK by me, as it can only happen by luck, you cannot hope to see the other player shooting and moving before the arrow gets executed, as user commands are a bit delayed anyway (a few milliseconds to avoid command DoS, plus the normal command parsing overhead, plus of course the network latency).
16 Aug, 2014, Rarva.Riendf wrote in the 10th comment:
Votes: 0
All you are saying makes ALL your threads totally useless…since you put everything in a queue that is parsed sequentially, there is no point of having threads to begin with.
16 Aug, 2014, koteko wrote in the 11th comment:
Votes: 0
You are wrong: it makes my code more comfortable for me to work with. That's enough on its own. And of course I have one Thread per user listening on the socket, I have other service threads that kick in at times. This I could do even with a game loop Thread of course, but it would make it more difficult for me to keep the multithread safety everywhere. It's a personal preference though, I hope this is clear.

However your remark only works for the Global ActionExecutor that I now have in place for testing (I'm maybe 20% toward an open beta still). Having multiple action executors is pretty easy for me (a few changes and a safety check, about a couple of days work) and would make multithreading meaningful (considering the current trend in processor design, by the time I'm finished we'll have 100+ cores in commercial PCs).
16 Aug, 2014, koteko wrote in the 12th comment:
Votes: 0
quixadhal said:
Possibility 2: "Bob" finishes his spell first and heads home. "uberwizard" now either pops into Room 112 and finds nobody there, or he hadn't gotten the target room yet and now follows Bob back to town.


I actually had misunderstood this point, now I get it. You say that Teleport is targeted on Bob, but internally, eventually has to cause a change of room. If the change is trivial, like in the following hypothetical snipped form the Teleport being executed by uberwizard, it could be problem:

// here Bob is still in the old room
roomB = Bob.getRoom();
// here Bob is in the new room
roomA.removePlayer(Uberwizard);
roomB.addPlayer(Uberwizard); // this would be synchronised of course
// uberwizard has gone to the wrong room!


However I did not consider this to be a problem. It can only be due to an enourmous amount of luck (we are talking nanoseconds on a modern JVM and machine) - something I would make possible even if I was writing a fantasy magic novel (like a materialization delay for uberwizard that allows Bob to escape death).

A real problem instead is, of course, with doors ("Dragon Dinner" anybody?). But again, it's not difficult to solve.

Player A in roomA closes the door just while Player B in roomB is executing his "Go" command (after the door check), so B will actually move into roomA to the big surprise of Player A.

However, doors (and in general directions) do "exist" in both the rooms they link, they ARE in fact shared state, so it makes sense to treat them specially. A simple lock would do the job here:

roomA executes the "CloseDoor" on east, acquires the lock and continues
roomB executes the "Go" on west, locks on the door
roomA ends the "CloseDoor" on east and releases the lock
roomB gets the lock and fails the "Go" because the door is closed

If roomB had won the lock before, player A would see the "CloseDoor" fail with a message like "You cannot close the door while P is passing through it".
17 Aug, 2014, quixadhal wrote in the 13th comment:
Votes: 0
The problem I was presenting in option 2 wasn't so much the simple issue of Uberwizard teleporting to Bob in his new location… but rather, did you plan out ahead of time what the consequences might be?

If Uberwizard is a high level aggressive npc, and you've coded him to stay in his zone, he just violated that constraint. Will he go on a rampage and slaughter all the new players who shouldn't be encountering him in the safety of the newbie village? Will the guards kill him and leave high level loot for newbies to eagerly grab? On most muds, it's an issue if a bored immortal decides to play with the players by loading high level stuff into their area to mess with them… both a balance issue (if they get ahold of the loot without the proper effort), and a trust issue (the players now can't feel safe because the rule that the further away from the starting area they go, the more dangerous it gets has been disproven).
17 Aug, 2014, koteko wrote in the 14th comment:
Votes: 0
That kind of problem doesn't apply to my theme (conveniently :)). Mobs will be spawning at particular locations and times, depending on terrain, climate conditions and other things, and then they will live (permanently, in the DB, across reboots too), moving around attracted by human settlements, until slain/captured. Players will be living constantly in danger. It's a kind of high-magic post-apocalyptic survival game where one might consider a feature a random arrival of a high level aggressive npc in the main city :P

More seriously though, I have thought about newbie safety, and come to the conclusion that you must go "all-in" with it. So the starting settlement will be teleport safe, impenetrable, and heavily guarded. In a high-magic theme I can do things like walls that reacts to aggressive events and spawn something like the Resilient Sphere on D&D until guards arrive, for example.

I need not only to protect the newbies from the aggressive npcs that ended up (by chance, a bug etc) in the safe area, but from aggressive players too (as the PKilling is free). So all this will be part of the "safe area" design. Not something I'll have to bother with in the near future though.. :)
17 Aug, 2014, plamzi wrote in the 15th comment:
Votes: 0
koteko said:
That kind of problem doesn't apply to my theme (conveniently :)).


It doesn't matter what theme you have. Obviously, quix's example can manifest itself in endless ways, and the result is you will be seeing things happen that you have checks in place to prevent from happening. That means that if you check for A, and then go on to do B based on the result of check A, there will always be a small chance that your B will wreak havoc because something else modified the game state in the meantime. Placing locks whenever entities (may) modify the same places is the only way to prevent that. And it's the nature of a MUD server that this will happen quite often, and it will often not be very obvious that it's happening.
17 Aug, 2014, koteko wrote in the 16th comment:
Votes: 0
Honestly I can't see this happening very often in a room-based system. Most of the work is always done within the same room.

In the few cases where you actually need to perform an action based on the state of your surrounding Rooms (or objects/players in the surrounding rooms), it won't break havoc if you have a good model. It can cause the action of a player to fail though, or to cause an unexpected result for the player, which is acceptable for me (from what I see, it isn't acceptable for you.. and that's fine too).

It might be that I'm overlooking a lot, as you have all worked on MUDs much longer than me, but it also may be that I see muds differently. I won't have a "combat mode" like normal muds for example, but manual hits. Ranged combat is asynchronous then, as described before, and doesn't need to take locks. Same goes for sending a message to surrounding rooms, and propagating an event to surrounding rooms. It's all fine in my model.

Also, unless you have actually coded and completed (and experienced the problems yourselves) a multithreaded MUD server, you might just be unused to concurrent applications and see problems bigger than they are. Think that multithreaded programming is a piece of cake compared to distributed programming, but people still successfully do more complicated systems than MUDs with it.

You all have given me food for thoughts though, so thanks. When I'll eventually finish my mud we'll see how it performs :)
17 Aug, 2014, quixadhal wrote in the 17th comment:
Votes: 0
*chuckle*

I think you'l find that people on these forums have coded a pretty wide range of applications, and multithreading is not a new or scary concept to many of us. It's not a question of complexity… it's a question of data partitioning.

It's funny you compare multithreading to distributed processing, because the problems are very similar. In college, I got to work on an ncube system for a little while. That was a supercomputer that was designed for parallel processing, using a hypercube architecture. It has 128 nodes, which had varying amounts of RAM, and used message passing. You programmed it by writing code that would distribute itself to as many nodes as it could, and as each node produced a partial result, they would push those results to a neighbor… collapsing the dimensions of the hypercube until the first node got all the data.

It worked really well for any problem that you could cleanly partition! Incredibly fast (for the day). However, if your calculations required you to access those partial results, you essentially had to pass it back upstream and have each neighbor check to see if it had enough to do the calculation or keep passing it back up. So, the more your problem depended on those partial results to continue, the more efficiency you lost. Worst case, it all ended up back at the root node and had to be calculated there and then re-partitioned and re-distrubuted. Essentially, falling back to single-threaded performance.

Now… back to multithreading!

When you make a complex system like a MUD, by its very nature game actions tend to need to touch a LOT of parts of the system at once. If you ignore this and rush blindly ahead, you'll end up having situations where your carefully written code doesn't work the way you know it should, because the conditions of the data change in-between steps of your processing.

You seem to be thinking that this is only something that can happen once in a while, and so it's safe to ignore it and carry on. Well, that's your call. In many ways, it's FAR FAR worse to have errors that only happen once a week, and that you cannot reproduce because it's not possible to force those same conditions to happen again.. than to have them happening regularly.

Let's say you do have some error that happened because of data being changed during a calculation. If it doesn't cause a crash, how would you know? Maybe the only visible result is a player didn't get credit for something… or an NPC takes a wrong step when tracking someone… or a player misses a healing tick… are you OK with that? What are you going to tell the players when they start saying "This game does things weird… every so often <foo> doesn't work right, or <bar> seems to act differently"?

The devil is always in the details. Writing stuff in seperate threads is easy for you at the moment, because you're ignoring the potential problems and just focusing on making it work. That's fine… but at some point, those problems are going to bite you, and it will hurt far more if you aren't planning ahead to deal with them now.
17 Aug, 2014, koteko wrote in the 18th comment:
Votes: 0
Thanks for the thought out response. Indeed, massively distributed (or concurrent) applications scaling much, much worse than linearly on the number of processing units are a pain. I have to deal with that at work very often, as I'm in the middleware team.

Back to this specific case:

Quote
If you ignore this and rush blindly ahead, you'll end up having situations where your carefully written code doesn't work the way you know it should, because the conditions of the data change in-between steps of your processing.


I don't think it's fair to say I'm ignoring this, is it? :) taking care of this during the development of a concurrent application is all that matters. It's what makes it "difficult". Of course I'll try to avoid such situations, and prevent them by refactoring the code while I understand better the implications of new features.

What I honestly don't get is how you stress that a MUD is inherently doing a lot of cross-Room, inter-dependent computations (that's the whole point of the discussion I guess, as I never said I would use one Thread per player, but one Thread per Room).

Propagation of "shouts" or explosions or light effects to nearby rooms (as well as far away ones) I've shown to be safe in such a model. Doors are trivial to solve (and with read locks, an open door will be concurrently traversed by players of both rooms in an efficient way). Ranged combat I won't actually have it, but if I do I've shown it can be done asynchronously in a concurrent-safe way.

What else do you see as dangerous here? Remember that:

- health/stamina "ticks"
- combat
- object interaction
- any kind of events (radiation, irradiation from a fire, decaying of spells or properties)
- "immortal" bonus/punishment to a player while he's doing some action
- "live" additions of npcs/objects by builders, as well as live modifications of room/npc/objects/players

will all take place within the same Room of the player/mob/object in question, therefore sequentially. Nobody from other Rooms will be able to bypass the queue system. If I find myself needing to do this explicitly, I can use a Read-Write lock like for doors but those should be exceptions.

If you can see a real hole here (I don't mean code wise of course, but logically. Some shared state like a Door that I cannot see at this point, as I've never worked with a full codebase) please do tell me.

This seems pretty solid to me (despite being just all theory: my current system has a single global ActionExecutor where all game state is sequentially changed). And in addition to this, I can see a huge possibility for parallelisation: in a MUD even with just 10-20 players that are active, I normally see only groups of 3-4, some solitary guy and so on, all being in different rooms (sometimes very far away).

If I see a bottleneck, it's easy to change the model: I can partition my map and put action executors for specific areas, so that separate "communities" will run in parallel, but internally each community would be sequential. Or go back to a single one until I fix the bottleneck. Nothing extreme.
18 Aug, 2014, Runter wrote in the 19th comment:
Votes: 0
@koteko The faults that Quix is talking about is not just a convenient error that will give your world more flavor (like wizards phasing into rooms they should be, lol?) but things with more dire consequences. Like violation of assertions and unit tests about how your system should work. It might not even be a game concept – it could corrupt your data and require maintenance to recover the game state.

For what it's worth, almost every new mud developer starts from this perspective about concurrency that they think simplifies the problem. The truth is multithreading adds in an entire class of problems that are a multitude of complexity higher than a synchronous system. If you're finding that not to be true, it's only because you haven't yet discovered the dire mistake you're making in how your multithreading will work in practice for you.

I'm not saying it's impossible to build. I'm not saying it doesn't have merits. But you're not appreciating the trade offs yet and you're happy with bugs you aren't aware of because you don't have enough action in the system yet to see the weird faults that will occur from time to time.
18 Aug, 2014, alteraeon wrote in the 20th comment:
Votes: 0
At this point, I'd recommend we simply wait and see what happens. The overwhelming majority of experienced developers are saying "you don't want to do this"; Koteko himself is saying, "I'm going to do it anyway because I want to". Explaining to him why something might be a problem isn't going to convince him - he has a solution for everything. His mind is already made up, let's let him implement it and see what happens.

-dentin

Alter Aeon MUD
http://www.alteraeon.com
0.0/59