Last modified: Tue Apr 24 2001 - Oskari Mertalo
This document is made for the Monrovia project ordered by Mgine Technologies (formerly known as Done Wireless Oy) in order to specify the technical design of the product. Various parts and modules of the system and their interfaces are described in this document. The interaction of the modules and the overall architecture of the system are also addressed.
The product consists of the game engine and the game itself. The game engine is based on a client-server architecture. The server is a Linux server and the clients are Palm handheld computers. The game is a simple map based MUD-style game with tiled graphics.
Chapter 1 is the overview of the document.
Chapter 2 discusses the environment the product runs in.
Chapter 3 gives an overview of the technical architecture of the system and its various parts.
Chapter 4 gives the detailed interfaces and format of the various modules.
Chapter 5 lists the solutions that have been considered, but abandoned.
Chapter 6 describes the error handling system and error handling standards.
The language of the variables and any other elements of the produced code is English. The language of the comments and documentation related to the code is also English.
The server needed is a x86-based computer of average computing power compared to today's standards. The server's processing power limits the amount of concurrent clients, the size of the map and the amount of NPCs on the map. As the computing power of the server increases, these values increase also, but the minimum requirement is a standard PC with reasonable enough equipment.
We have Palms as handheld computers and they have limited memory, limited processor power, limited graphical representational powers and a specialized user interface. All these aspects have to be taken into account as the client software should be able to run on a standard Palm. Advantage can be taken of the specialized user interface and many functionalities can be implemented on the server side instead of the client side.
Communication between the clients and the server takes place on a network that is bandwith restricted and of variable latency. The protocol needs to take this into account.
The operating system of the server is Linux Redhat 6.2. CVS, which is distributed with Redhat 6.2, is used as the version control system. The operating system of the handheld computers is Palm OS. All the coding is done in Java compliant with ver. 1.3. As the Java compiler javac is used to compile the finished code and jikes, because of faster compiling, is used for testing purposes. The server has J2SE and the Palm handhelds have K Virtual Machine (KVM) for running the J2ME-compilant code.
It must be possible to demonstrate a working multiplayer game with concurrent users.
The system consists of a server and the clients. The clients and the server communicate with a specified protocol (see Diagram 1).
Diagram 1
The philosophy behind the implementation is to use object oriented designing and coding using Java as the language for coding. The design of the Java language forces the coders to think OO when coding and the design of the software will also support this approach. The architecture of the system is a client-server environment, where the server does a major part of the processing and the client is used mainly for displaying the game state and for interacting with the user.
The file loading and saving in the server is hidden behind an interface, so that the method can be changed easily. For now the implemented method will be XML. This eases creation of the game data as it can be edited with some free XML editor or even with an ordinary text editor. The java library, that is being used for serializing and deserializing java objects to and from files, is JSX
The game data structure is as follows: In the root directory there is an xml file named arenas.xml, that contains a String array. Each string is a name of a game arena and for every arena there exists a directory, named after the arena, where the arena specific data is stored. Each arena directory contains three xml files labeled map.xml, creatures.xml and portals.xml. map.xml contains a string array with the names of the pictures of the map tiles. creatures.xml contains the data for the creatures of the arena. Every creature has a name, an implementing class, an icon file and a variety of parameters, which are specified by name/value pairs in the file. portals.xml contains the data for the portals, or gateways to other arenas. Every portal has a name, implementing class, a location and a variety of parameters in same format as creatures have. The icons are in palm raw format and 16x16 pixels in size. The map of the arena is a Windows bmp file named map.bmp, where one pixel represents one tile on the arena map. The tile picture is the tile that has the pixel value as index in the string array found in map.xml.
Player data is saved to a separate XML file. The players are stored in the players directory. Every player object contains information such as player name, password, arena the player is on and location of the player on the arena and the character of the player.
The game log is also saved.
File tree:
/gamedata/
/gamedata/arenas.xml
/gamedata/arena1/
/gamedata/arena1/map.xml
/gamedata/arena1/creatures.xml
/gamedata/arena1/map.bmp
/gamedata/arena1/icon1.bin
/gamedata/arena1/icon2.bin
/gamedata/arena2/
.
.
/gamedata/arenaN/
/gamedata/players
/gamedata/players/player1.xml
/gamedata/players/player2.xml
.
.
/gamedata/players/playerN.xml
The serializability for the network data transfer of dynamic objects is implemented by converting the objects to binary data, so that for example an integer and a string of size 16 are converted into 20 consecutive bytes in network byte order.
The monrovia protocol consists of two logically separate connections, a control connection and a game connection. The control connection handles login, authentication, reconnect and exit while the game connection handles game situation messages such as movement and view updates. The control connection is implemented with TCP whilst the game connection may use either UDP (suitable for unreliable networks) or the same TCP connection as control.
The server listens to a specified TCP port for clients. When a client connects to the server it sends 32 bytes consisting of the players username, password, the clients udp port and protocol version. If the client wishes to use the control connection also as its game connection it will send 0 as its udp port.
Username | Password | UDP Port | Protocol Version |
8 Byte | 16 Byte | 4 Byte | 4 Byte |
This is answered by the server with a 32 byte message:
Authentication Result | Protocol Version | Udp Port | Map Size | Bitmaps Size | Commands Size | Character Stats Size | Reserved |
4 Byte | 4 Byte | 4 Byte | 4 Byte | 4 Byte | 4 Byte | 4 Byte | 4 Byte |
If the authentication fails or the protocol versions differ too much, the server indicates this by setting the corresponding opcode in the Authentication Result field and leaves all other fields empty. After a failed login the TCP connection is discontinued.
If however the login succeeds the server next sends the map, bitmaps, commands and stats to the client.
After this exchange both the server thread and the client will begin listening to the open TCP connection for control messages from each other.
TCP size is the size of the next message to be received. This field is not sent when using UDP.
Turn Count is a 16 Bit increasing number by which packets and turns can be matched. This field is naturally only in use when the connection uses UDP.
PacketID is an 8 Bit increasing number by which packets per turn can be identified. This field is naturally only in use when the connection uses UDP.
X Map (16 Bit) distance from the map's corner. This restricts area sizes to 65k*65k map tiles.
Y Map (16 Bit) distance from the map's corner.
Op codes (16 Bit) specify the real information in the packets. The Op code indicates the size of the Op code Data field. An Op code may for example indicate that there is a dynamic map object one square south of your character and that you may say something to it. The second Op code in the below example displays some information. If there are more Op codes and their Data would fit in one packet, then another packet will be used but Op codes and their data will NOT be split. The last packet sent will carry a this-is-the-last-packet-for-this-turn op code.
TCP size | TurnCount | PacketID | X Map | Y Map | Op code | Op code data | Op code 2 | Op code 2 data |
32 Bit | 16 Bit | 8 Bit | 16 Bit | 16 Bit | 16 Bit | Op code Data size | 16 Bit | Op code 2 Data size |
Tile ID | X Delta | Y Delta |
16 Bit | 8 Bit | 8 Bit |
Tile ID is the code for the symbol representing the tile. 16 bits restricts dynamic object symbols to 65536. 0 means no symbol.
X Delta is the horizontal distance from your character. 8 Bits “restricts” screen size to 256 map tiles (at least you cannot see further than 128). 0 means right here, first bit indicates west(1) or east(0).
Y Delta is the vertical distance from you character. Same rules as above, north first bit set, south 0.
|
or |
|
Opcodes differ for String and int stats.
Command ID | Name | Type |
32 Bit | 128 Bit | 8 Bit |
Commands appear because these static/dynamic objects are where they are now. Command Id is the identifier that the client uses when sending back a message that the command was used. The Name field is the text to be displayed on screen. The Type field is one of the following: no arguments, 1 int argument, 2 int arguments, 1 String argument or remove command. The Type field tells the client what to ask the player when he uses the command. For example a say command would be of type string, the client will then ask the player to write a message which will be sent. While all other types add or update a command at the client side, the remove command type removes a command from the available commands list.
Last packet is put at the end of an udp packet when there are no more udp packets for this turn. Last opcode is put at the end of the packet if there still are some packets coming this turn. Both op code types signal the end of the current packet. If the TCP control connection is used as the game connection only last packet messages are used as tcp packets are streched or shrunk to fit the needs of the sender.
Message |
400 Bit |
This message will be displayed in the message screen.
Item |
Item Picture | Item Text | Item property | Command 1 | Command 2 |
8 Bit | 16 Bit | 320 Bit | 8 Bit | 8 Bit | 8 Bit |
Item field describes the inventory slot which the item occupies. (The fact that it is in the inventory is identified by the op code.)
Item Picture is the items visualization code = same as above.
Item Text is the text that describes the item.
Item property is a property of the item that needs to be displayed.
Command 1 and Command 2 are the commands that become available because the item is where it is.
An example:
In slot 8 there is a Ring of Incredible POWER +100 (text) (Ring picture) that weighs 1 (property) and it can be worn (command 1) and dropped (command 2).
TCP size | TurnCount | PacketID | ClientID | Op code | Op code data |
32 Bit | 16 Bit | 8 Bit | 16 Bit | 16 Bit | Op code Data size |
TCP size is the size of this message. This field is not sent with UDP.
Turn Count is a 16 Bit increasing number by which packets and turns can be matched. This field is naturally only in use when the connection uses UDP.
PacketID is an 8 Bit increasing number by which packets per turn can be identified. This field is naturally only in use when the connection uses UDP.
ClientID is a 16 Bit identifier by which players from the same ip can be identified. This field was added due to the inability of J2ME datagram classes (not being able to work as both sending and receiving sockets).
For now there are two Op codes on the client side: command Op codes where the Data field specifies the action and the op code its arguments and a last opcode to mark the end of the packet.
Example of a say command:
Command ID of Say | the message to be said |
8 Bit | 40 Bit |
If the command requires arguments, they will be specified by extra fields as indicated by the command type (given by the server) - for example an action requiring direction or coordinates will receive one or two extra fields respectively.
The product consists of the client and of the server software. The server has a thread for each map area, that processes the information gathered by the connection threads and changes the state of the game. The connection threads send and receive information to/from the clients.
The map consists of the map objects that resolve situations happening on the object. There are dynamic objects on the server such as characters and the users.
The client has a thread for processing protocol data, a thread for processing user input and a thread for updating the graphics. Basically the client does not compute any game decisions, but just interacts with the user and the server.
The server class diagrams are shown in the Diagrams 2 and 3.
Diagram 2. Server application classes. Boxed arrows describe
inheritance and line arrows describe attribute reference.
Diagram 3. Server game platform classes. Boxed arrows describe
inheritance and line arrows describe attribute reference.
See the Javadoc documentation for more details:
-
GameDataStorage
-
PlayerStorage
-
AppControl
- Starter
-
Player
-
GameDataImpl
-
PlayerDataImpl
-
ProtocolHandlerImpl
-
GameInterface
-
AppControlImpl
Class monrovia.server.core.Starter is used to start the server. The following system parameters can be used to configure the server:
The GameDataStorage is the interface to load and save game data and the PlayerStorage is the interface to load and save players. The GameInterface contains methods to use in the game implementation. The AppControl RMI is an interface for controlling the server operation.
The starter first calls methods that are used to load the game data needed. Then it instantiates and starts the threads that are used to listening for network connections and UDP stream. After that it responds to the needs of the clients and the game implementation.
If there is an error in the game data, print an error message
and exit. This is implemented with the
StorageException
. It can be thrown anywhere in the
loading code and then it is caught in the code that calls the
loading method. The string parameter of that object contains the
error message.
See the Javadoc documentation for more details:
If the tool is executed without arguments, it first asks what the user wants to do. Otherwise it tries to use the given arguments as the command. Then it will ask necessary questions to be able to execute the commands. The tool should be self documenting.
Just use the standard in and out to communicate with the user and then call the methods in the AppControl RMI interface.
Show good error messages.
See the Javadoc documentation for more details:
The game consists of classes that are extended from the basic classes in this platform core. Those game objects can then be further modified with parameters from the game data. The API documentation tells more.
One class represents the map area and then there is the hierarchy of game objects that are the base for the game implementation. The map area contains the thread that does all the things that are needed to run the game in that map area.
The thread does the following in a loop:
A command object is created for each incoming command and then that object is inserted to the map. If we think each command as a force, the stabilization means calculating with these forces which commands get to be executed. The commands can also add new commands in the stabilization phase to affect the situation. For example, if there is someone in the square where we are going, we can either disable ourselves or add a command that moves that someone away from the square.
The game objects form a hierarchy like this:
The goal is to keep the game running. To achieve this we can even kill creatures that have given commands that do something odd like throw exceptions. Still this is very difficult to do so that the situation stays correct, so the game implementation should behave good.
See the Javadoc documentation for more details:
See the Javadoc documentation for more details:
See the Javadoc documentation for more details:
-
ProtocolServer
-
ProtocolServerThread
-
ServerTcpSender
-
ServerUdpSender
-
ServerUdpReceiver
-
ProtocoHandler
The ProtocolServer class is used for starting the tcp connection server which will start listening on the given port for client connections. The monrovia.server.platform package uses this package to talk to the clients. The ProtocolServer communicates with the server through the ProtocolHandler interface.
The ProtocolServer class starts a ProtocolServerThread for each
client. Each ProtocolServerThread will require the client to
authenticate itself after which (if successful) static arena and
character information will be sent to the client through the control
connection. The game messages will be used to send dynamic data
during the game while the tcp connection will be left open for
control messages. For each client and each turn the
monrovia.server.platform package will ask the protocol to build
an udp message.
For each client (successfully authenticated),
that wishes to use UDP as the game connection, the ProtocolServer
starts 2 threads, one for listening to tcp control messages
(ProtocolServerThread) and one for listening to udp messages
(ServerUdpReceiver). For each client, that wishes to use the control
connection as the game connection, only one thread is started for
listening to both connections (ProtocolServerThread). In such a case
the ServerUdpReceiver is given the game messages to interpret.
Naturally the ProtocolServer itself is a thread listening for new
clients.
If the server socket cannot be allocated, log error message and throw IOException. If a client misbehaves, log error message, terminate that client's connection and exit that thread.
See the Javadoc documentation for more details:
This package has no single interface but rather a collection of mostly static methods. See the API documentation link above for details.
The ByteConverter class uses simple methods for writing to and from byte arrays. The TcpOpcodes and UdpOpcodes contain the static opcodes needed for client-server signaling.
The methods throw standard exceptions.
The client class diagram is shown in the Diagram 4.
Diagram 4. Client classes. Boxed arrows describe inheritance and
line arrows describe attribute reference.
See the Javadoc documentation for more details:
The MonroviaClient class initializes the other user interface objects based on the default values set in the class. See the Javadoc documentation for MonroviaClient class for more information.
Catch possible exceptions thrown by the other packages. Most of the possible errors in this class deal with the network access.
See the Javadoc documentation for more details:
Most of the UI classes are based on the KVM UI classes provided by Sun Microsystems. See the Javadoc documentation for more details.
Throw possible exceptions to other classes. Not many of them are used because MonroviaClient class handles most of the error checking.
See the Javadoc documentation for more details:
monrovia.client.procotol.ProcotolClient is used to initialize and connect the tcp connection which will begin the dialog between the server and the client.
If the client is capable of using UDP, the ProtocolClient class
connects to the given server over
tcp after allocating two udp sockets (one for sending and one
for receiving). It will send client authentication information
and its newly allocated udp port (the receiving port)
and receive static information if the authentication was
successful. The udp port will be used to communicate with the
server during the game. If the client wishes to use only TCP
the control connection is used for all communication.
The ProtocolClient like the server allocates one or two threads,
one for listening to tcp control messages (ProtocolClient) and
if UDP is used one for listening to udp messages (UdpReceiver).
If the client socket (udp or tcp) cannot be allocated throw an IOException. If the server or sockets misbehave the connections are terminated and an IOException is thrown to the above client.
The use of a database for storing character and user information was abandoned, because storing the information in XML files makes the modification of the data easier and also the usage of the data more flexible. It was although decided that the interface for serializing and deserializing data should be written as generic as possible so that the implementation could be later changed if decided to do so for example if performance problems arise from the usage of XML files.
Error handling is done by using java's Exceptions. The Exceptions thrown should reflect the nature of the error. Also if an error situation is similar to another one and it is known that it will be handled the same way, the exception thrown should be the same. There is a Logger class that can be used to log error messages into a file. The logger is created using a log level and when an error is logged an error level is given to the logger method so that the verbosity of the logging can be chosen.