Multi User Dungeon Demo
Written by
David Er-el 1999
|
Abstract
A full functioning demo of a multi user game in which each player sees
the same board as all the other players. The players can move inside a
maze picking up objects and evading monsters. The application demonstrates group communication patters over a layer called JavaGroups which was written by Bela Ban. |
Requirement :
- Having sun's jdk1.1.7 installed.
- Having downloaded javagroups.jar 0.6.6. and swing.jar 1.0.3 and placed in the mud/jar folder.
Note : The program was tested on WinNT4.0 , Linux 2.x , Solaris 5.6.
How to install ?
-
Download the tar gziped file mud.tar.gz.
-
gunzip mud.tar.gz ; tar -xvf mud.tar ( winzip should know how to handle this ).
- Move the javagroups.jar and the swing.jar to the mud/jar folder
- cd mud/src
-
Add the JavaGroups and the Swing to your classpath and compile. For
example ( in Unix ) :
1. setenv CLASSPATH .:/usr/lib/jdk-1.1.7/lib/classes.zip:/home/mud/jar/swing.jar:/home/mud/jar/JavaGroups.jar
2. gmake
-
To run the program :
java Mud
In the demo there is a hard coded limit of maximum 4 players. Each player
is identified by it's color. He is given that color on random when
starting the application. Also on the board are the following elements
:
-
Monsters - represented by a capital M letter with red color.
-
Items - represented by their first letter. Currently there are a : Shield
, Potion and Gold.
The gold doesn't actually give anything.
The potion raises the health of the player.
The shield can be used to fight a monster. When armed the hit of the monster
is less painful. A drawback is that when the user is armed he can not
move to a different spot.
-
Other users - users can interact by stealing items from one another and
by limiting the movement of another use
- The user can move around and each move reveals only the area which
the user has stepped on to all the users. The movement is done by keypad
arrows or by pressing the mouse at the desired location. A player can not
make more then one action per 1 second.
-
Taking items : a user can take an item by clicking on it and the choosing
the action 'take' from the pop up menu.
-
Handling items : By clicking on the items on the board / on the items
panel the user can activate an action on the item. For example to drink
the potion.
-
Fight a monster : This action is done automatically when the user is close
enough to the monster but only after a certain timeout which gives the player
some time to try to escape.
All the configuration parameters are in the class MudUtil.
public static final boolean DEBUG = false;
private static final String d_mazeFile = "board.txt";
public static final String GROUP_NAME = "MUD";
public static final int CHANNEL_PORT = 9000;
public static final int RMI_TIMEOUT = 3000;
public static final String PROPS = "UDP:PING:FD:GMS";
public static final int INITIAL_PLAYER_POWER = 12;
public static final int INITIAL_MONSTER_POWER = 3;
Notes:
- DEBUG : when false no debug print outs will be printed to stderr.
- d_mazeFile: The maze file is represented by a text file with the first line containing how many lines / rows and the rest is 0/1 separated by new line for each row.
- PROPS : the layer stack for JavaGroups
My program uses the JavaGroups layer for the communication. JavaGroups was developed by Bela Ban. The javagroups is a pure Java group communication system which enables the developer to create groups , join them , leave them and get view changes on those groups. It doesn't require to run in addition any special daemon. The javagroups layer can operate above Ibus , over ensemble or on an internal layer. I have used the internal layer which works the best.
I have decided to use JavaGroup's RMI's. It is done in the following way :
- d_disp = new Dispatcher ( new JChannelFactory() , props ); // props is the java stack
- d_disp.SetMembershipListener( MudUtil.GROUP_NAME , this ); // enables receiving view changes
- d_disp.Join ( MudUtil.GROUP_NAME , this ); // Joins the group
- d_disp.SendGetN ( MudUtil.GROUP_NAME , parameters , 0 /* number of responses to wait for */ , MudUtil.RMI_TIMEOUT ); // Sends a remote method to be invoked on all members of the group
It is also possible to send a method invocation only to a specific member. I usually used this method to send messages to the master.
View change is the most complicated task in this program. It is handled in the following way :
- JavaGroups triggers ViewAccepted
- ViewAccepted calls rmiGetUserStat for all members.
Each member send to all others it's viewId , it's age (round) and it's address.
- After rmiGetUserStat is called for each member the leader is chosen in each node. The leader is chosen to be the one which has the bigger age then all others ( ensuring the correct state ) and in case of equality then there is a lexicographic comparison of urls and ports.
- Leader calls processMasterInit on it's node and saves a new state.
- The state is sent it to all other nodes using rmiStateTransfer
- rmiStateTransfer transfers all the board state + players + monsters + items ... and finally calls board initialization.
Since there is no layer of virtual synchrony yet I had to implement it myself. All messages in wrong views are discarded. Especially it is important during view change. This is why in the rmiGetUserStat that the viewId is sent. Consecutive view changes in a short time might cause this function to receive old views and it should identify it by the viewId. The viewId is produced by JavaGroups.