A quick tour of Meadow

Right. Everything is under the top-level package meadow. According to Sun this should probably be net.sourceforge.meadow or something... but it isn't. Anyway, from there we have 4 subpackages, client, common, server, and editor. These contain the code for client-side things, core classes, server-side code, and scenario design tools respectively. This is pretty obvious stuff. There are some sub-subpackages, for example client.ai which is where the AI opponent stuff will go. It doesn't really need to be a subpackage of client but it looks nice that way. One point I will make straight away is that all the game graphics stuff goes in client. The common and server packages are basically concerned with running an algorithmic model (or mathematical simulation) of a battle; they are not concerned with presenting it to a user. Client is the only package that needs to know about graphics, therefore it is the only package that does know. (Okay, the editor might need to know as well, bah!)

Okay, if we have a quick look at the meadow.common package. It contains, if you remember, things for creating a model of the battle. So we see that it contains things like TerrainModel... I shan't go into the details of that here (see the javadocs), just note that the classes used for presenting the terrain to the player aren't here (see the client.TerrainView hierarchy). So this is one of the main patterns in Meadow: "Model-View". This is a bastardized version of the original "Model-View-Controller" (MVC) pattern found in Smalltalk and GUIs everywhere... I just never see the need for a separate controller class. The other classes here could also use some study... in particular there is the Simulation class which is pretty fundamental. I can't really go into that without mentioning the concrete subclasses MasterSimulation and SlaveSimulation so I guess I'll have to cover the whole client/server thing now.

This is where I've had to make a few design decisions - things like how to distribute the workload, how to deal with trust, how to maintain synchronization, things like that. The solution I chose is this: for each game, there is one MasterSimulation and some number of SlaveSimulations (one for each player). The MasterSimulation is the one that makes all the important decisions and therefore must be trusted (the Slaves need not be trusted). Because the Master has to make all the important decisions, it has to be up-to-date with respect to what's going on. This means there is essentially "one more" model of the game in existence than you might think, but I think that's a reasonable price to pay for the flexibility. And the processor load doesn't seem too bad anyway, it's mostly the graphics that slow things down. Anyway: co-ordination. Obviously there has to be communication between Master and Slaves. This communication is also how I solved the synchronization problem: there is exactly one communication from the Master to the Slave per game cycle (a game cycle is where we move the pieces around, see who's getting shot and so on). (There is a feature to do some other small constant number of cycles per communication, however this is unimplemented.) This communication takes the form of a (meadow.common.)SimulationUpdate object (see the javadocs). So on a laggy network what happens? The Slaves all wait around for the next update. Is this a problem? It doesn't seem to be on a LAN.

The communication back from the Slaves to the Master come at any time or frequency - it doesn't wait for them. It takes the form of an Order, which represents a player telling his forces to do something. (See the javadocs for the Order hierarchy.) This interface between Master and Slaves has been formalized as the meadow.client.SlaveIO and meadow.server.MasterIO classes... a quick look at which will tell you I've forgotten to mention the initial SlaveSimulationData object sent out from the Master to all Slaves at the start of the game. Nevermind. There is only one implementation of these classes, which use java's serialization mechanism. This is just a convenience and could change if that poses performance problems. By abstracting that serialization out into separate classes (yay me!), the changeover shouldn't be too difficult. Oh yeah, this brings me on to the next key pattern in Meadow: Observer.

Communication between the MasterIO objects and the MasterSimulation, the SlaveIO objects and the SlaveSimulations, and the SlaveSimulation objects and their respective user interfaces, all takes place via the Observer/Observable interface. This is a common pattern in Meadow so get used to it (look it up someplace if you don't know how it works... basically it's a generalized register/callback system, like ActionListener or something). This communication between different parts brings up one of the problems you will face if you do any serious Meadow hacking: concurrency. There are so many threads running at any time in a game it's funny... luckily they tend to keep to themselves and you can mostly ignore them but really you do have to keep in mind thread issues to produce safe code. Let's count. On the server-side, there is one thread (see meadow.server.Driver) that runs the MasterSimulation; there is also another thread for each player, to listen for Orders. (This may have to be increased to two threads per player, one to listen and one to send, but so far that hasn't been necessary.) On the client side, there is one thread for the SlaveIO that waits for updates to pass to the SlaveSimulation, and there is another thread for handling the GUI (assuming we're not talking about AI players here, which do everything on a single thread at the moment). So in a game of 2 human players, and 2 AI players, you're going to have (5+4+2) 11 threads active at any one time while the game is running. Of course this is split across 5 VMs so you don't need to worry about all of them while coding any one part. [Oh dear, it's worse than that, I forgot there might be UI threads on the server side, "main" threads on the client-side etc, but you get the idea.] In fact, the main place of interaction is, as I've said, via the Observer interface... this means you have to be particularly careful when writing code that is inside - or called by code inside - an object's public void update(Observerable o, Object arg) method. In MasterServer this is really simple because messages are just queued waiting for the driver thread: since only the queue is shared, accesses to that are synchronized and everything is ok. In the SlaveSimulation however, the handling of the updates is done on the IO thread (since we don't want to receive a new one before we've handled the old one) meaning that basically all SlaveSimulation data is shared between the UI and GUI threads... note the synchronized get*Pieces methods and also that the SlaveSimulation's observer (meadow.client.GameArea) is also updated on the IO thread.

Okay, I'm running out of things I want to say, but I've still got the feeling someone could read this and still not have a clue what's going on. So here's a quick timeline of things that might happen in a typical game, together with the responsible classes ([SS]=Server-side, [CS]=Client-side):

EventsClasses
[SS] Run server.GUIserver.GUI, server.UI
[SS] Press start button, parse GUI parameters, wait for connections server.GUI, server.UI, server.Gatherer
[CS] Start client.GUI, open connection, listen for initial data client.GUI, client.GameArea, client.SlaveSimulation, client.SerializingSlaveIO
[SS] Receive connection server.Gatherer, server.SerializingMasterIO
[CS] Start AI client client.AI.AIClient, client.SlaveSimulation, client.SerializingSlaveIO
[SS] Receive connection server.Gatherer, server.SerializingMasterIO
[SS] Create MasterSimulation, load map data, create dummy victory conditions etc server.UI, server.MasterSimulation
[SS] Broadcase base data server.MasterSimulation, server.MasterIO
[CS] Receive base data client.SlaveIO, client.SlaveSimulation

Okay, I'm bored now. If anyone wants to finish off this table or say something more about the client or anything, or ask any questions... send it in!

Last update 2001-06-05 by nphillips.