The communication protocol between the client and the servers (both area and login servers) is symmetric in that the same thing is happening on both sides. On each side, there is a CommunicationManager that contains two threads: ConnectionIncoming and ConnectionOutgoing.
In addition to the socket communication layer, our communication protocol provides two mechanisms: one for encoding state changes to be transmitted across the socket and one for processing messages coming in over the socket.
Encoding messages to be sent over the socket is done by StateAccumulator. At its most basic, this is a message queue that uses ConnectionOutgoing to send the messages. In order for the StateAccumulator to know what to send, it must be an observer of the model in all of the right places. In order to keep the StateAccumulator and the model loosely coupled, we use the QualifiedObservableConnector
to set up the observer relationships. Since those relationships and the way an event gets encoded into a Message differ on each type of machine in the system, we encode that in separate objects that are MessagePackers. There is one Message for each MessagePacker and it is a response to one QualifiedObservableReport from the model. So each Message has two methods: pack() which takes a report and returns that report encoded as a Message and getReportWePack which returns the type of QualifiedObservableReport that the packer expects to pack. This also tells us which QualifiedObservableReport the StateAccumulator must observe in order to receive the necessary reports.
Since the MessagePackers required are different on each type of machine, we need some magic to connect the appropriate MessagePackers to the StateAccumulator. That magic is done by MessagePackerSet! MessagePackerSet is in the communication.packers package. Upon instantiation, it will look at all of the classes in that package and will keep track of those that implement MessagePacker. So, when a MessagePackerSet is given to StateAccumulator, it magically knows which packers exist on that system (provided we are smart and put all of the packers in the communication.packers package!)
The handling on incoming Messages is very similar. Each machine type in the system has a MessageHandlerSet which contains a set of MessageHandlers (one for each type of incoming message). The MessageHandlerSet is given to the ConnectionIncoming when it is created and ConnectionIncoming gives each incoming Message to the appropriate handler (based on Message type) for processing.