Jeff,
Maybe if you had a little time you could throw in some thoughts you might have regarding distributing the simulation. Personally I am still having a little trouble getting my head around the idea that the simulation can be driven by something other than what is essentially a “tick” (Yes I know the evil performance destroying tick!).
This weekend I started working on version 2 of my chat application, which is the 3D version. So far what I have done is essentially convert the thread driven graphical simulation into a headless simulation that can be driven by an external event, like a tick. The headless version keeps track of how much time has passed between updates and should update the simulation accordingly when it is time for something to happen.
The most obvious thing to do at this point (at least for me) is to drive the simulation off a PeriodicTask that executes at a specific frequency. For example lets say something like 500ms.
So with this scenario I would have something like:
tick -> -> -> done.
With this approach it seems like (for example) that any time an object requests its position in the world the operation is relatively cheap because at any given time this value is calculated and known. By adding something like event queueing, I can allow the simulation to regulate the rate at which it consumes events generated by clients to make sure that all the processing can be handled inside of one tick. By doing that, I can guarantee that the events for a given object are processed in order, but it seems that it would become more difficult to assure that globally events are processed in the order the arrive. I wonder if that is important…Hmmmmmm… 
I guess an alternative is a more “quantum” approach where the simulation is only updated when something looks at it.
Perhaps something like:
Observer.lookAtSomething() -> -> -> done.
The downside of this seems to be that basically any operation could be expensive because any given operation would require that the entire state of the simulation be re-calculated. To combat that, I could somehow add a limiter which would only perform a re-calc if the previous calculation is more than X age, but in practice that seems like it would end up being no different than just updating the whole thing every X seconds.