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):
Events | Classes |
---|---|
[SS] Run server.GUI | server.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.