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.
- Note : Pressing 'L' key will highlight the whole board - used for checking the
application.
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.
Written by David Er-el.
Email : derel@cs.huji.ac.il
Updated to Tue Jun 15 14:50:33 1999