Implementation Issues


    1. How the messages works?
    2. How to add new messages?
    3. Synchronizations
    4. Objects.txt
    5. Map Hierachy
    6. Client Map Cache
    7. Dealing with URL
    8. Fail/Stop Model
    9. Dealing with Images
    10. Client/Server
    11. Communication

    How the messages works

    Every message has a method called "Action", it is invoked by all kinds of message handlers. The message handler doesn't need to know what messages it is taking care of, it merely calls its Action method.
    here is the typical code for a message handler:

       while (thread != null) {
          if ((msg = msgParser.getNextMessage()) == null) {
             thread = null;
             break;
          }
          msg.Action(gdata);
          try {
             thread.sleep(GlobalData.ClientMsgHandlerDelay);
          }
          catch (Exception e) {}
       }
    

    How to add new messages

    To extends the useage of this program, it sometimes needs to add new messages. Adding new messages is in a systemetic and simple way once you realize the components in a message, here the message refers to the message code, not the message used in runtime.
    The code of message consists of 4 parts:

    1. Message Type :
      you need to add a new type for this message in Message.java
    2. Constructor :
      2 kinds of constructor, one takes the regular parameters, the other takes a string and parse it into the regular parameters.
    3. toString method :
      convert the member data to message string and separate them using the specified separator.
    4. Action :
      This is what the message would function like. You may need to understand each of the data structure before you can write this part.

    Synchronizations

    There are several parts need to be synchronized,(means something must wait until something happens) they are:

  • Server Map Initialization
    the server has to wait until all map data have been read from the disk and constructed. This is done by calling the MapConstructor.waitdead() method, which contains a
    semaphore to do the synchronization.

  • Move Req/Ack
    In the interactive mode, every move of the client need to inform the server and wait for acknowlegement. the client has to wait on a semaphore when he requests a move and releases from that semaphore when the acknowlegement message comes back.

  • Client Block Info Request/Receive
    After the client issues the map request messages and before those data received, if the client's screen has to enter those area, the client needs to wait. This is done by updating the DataSyncTable in mapStructure.java. When the client request for a block, he puts an record (value 0) for that block in DataSyncTable. When all data in that block has been received, the value will be changed into 2. Meanwhile, if the client want's to enter that block before it becomes 2, he has to wait and set the value to 1. This wait will be waken up when all the block data has been received and the value is set to 2 (in Message.java MsgEndOfExport.Action()).

    Objects.txt

    The map information is stored in
    objects.txt. It is loaded to the server by a MapConstructor at the very beginning. The contents of objects.txt is mainly determined by the structure of the Message. They are read by the MapConstructor and parsed by the MessageParser just the same as those message strings transfered over the net socket. The parse method should refer to the corresponding constructor in Message.java

    Here is a typical content of a data info in the objects.txt :

    4@3023&2027&3&20&40#

    4 is its message type which is separated to the other data by a '@' character, in this case, 4 is a door object message. 3023 is its object id, and 2027 is the id of its belong objects. 3 is its direction, in this case is the west side. 20 is its starting pixel and 40 is its ending pixel. All intermediate data is separate by & sign, and each object is ended with a # sign. These separators are defined in Message.java

    Map Hierachy

    We have seen that every objects has a self id and a belong id, these 2 member data are defined in the mazeObject.class which is the super class of every mazeObject. (block, room, door, item and player) That means every objects has these 2 member data. Only block has no belong id, which always contains a 0 in its belong id. The following are the possible relationship between 2 maze objects.

      Block --+--- Room
              +--- Item
    	  +--- Player
    
      Room ---+--- Door
    	  +--- Item
    	  +--- Player
    
      Player -+--- Item
    
    When constructing the map (by MapConstrutor),it is very important that the belonging object arrived before the belonged objects. for example, block has to arrive to the client before the rooms that have a belong id same as that block. Otherwise, the rooms can not be construted into the mapStructre.

    Client Map Cache

    The server contains all map blocks at a time while the client only contains at most 9 blocks at a time. This is to reduce the client memory load.
    The client requests the next 3 blocks when his distance is less than a given threshold (currently it's BlockW/2) and get rid of the 3 blocks at the opposite side. The item pictures are also freed at this time.

    The code in CollisionDetector.java looks like this:

        if (gdata.map.needMoreBlock(r,c,-1,0)) {
    
    	// request the next 3 blocks
    
             gdata.reqSender.mapReq(r-1,c-1);
             gdata.reqSender.mapReq(r-1,c);
             gdata.reqSender.mapReq(r-1,c+1);
    
    	// remove the opposite 3 blocks
    
             gdata.map.removeBlock(r+2,c-1);
             gdata.map.removeBlock(r+2,c);
             gdata.map.removeBlock(r+2,c+1);
        } 
    

    Dealing with URL

    Each item contains 3 url, one is for internet exploring, one is for data file and one is its icon image. Although they are all URL, the way they are handled are a little bit different.

  • Exploring URL could be any kind of URL that is accepted by Java/Netscape, for example : http://www/home/jhlin, ftp://ftp.cs/usr/u/jhlin, mailto:jhlin@cs ,..., the function it performs is suposed to be the same that a Netscape Web browser does.
  • The file URL is restricted to 2 types, one is images file and the other is wrl file (this is the VRML 3D model file).
  • The 3rd type of URL is its icon image which is loaded at the constructing time of the item.

    There is little problem with the first 2 URL, however, the loading of the 3rd type of URL is somewhat tricky.

    When mapConstructor constructs the item into the map, if the program is in the client side, it will give a request of image for that itme to the IconReader. IconReader is a thread reading these requested icons and put them in the ImageTable. Before the IconReader read the images of that icon, it is displayed as the default image (images/it.gif>, so is that when the image can not be found. (the URL is invalid)

    When reading the image, the IconReader will test if the URL is valid by calling its url.openStream() method. This is important when no one knows if the specified URL is correct, or still there.

    Fail/Stop Model

    Fail/Stop is a term in fault tolerant. This program can't tolerate any fault, especially for the server. But it can detect if there is a crash and does what it can do. for example, it the client crash or stop due to an unexpected event like computer crash, network failure... The server can detect this situation and inform other clients which can then know the quitting of that client.

    Each client has a CrashReporter thread which generates a message to the server every 1000 miliseconds. If the server does not receive this message within the period, it assumes that the client quits and do the proper things.

    Dealing with Images

    There are several kinds of images:

  • System Images
  • Icon Images
  • File Images

    The last 2 images have been introduced in the previous topic: Dealing with URL, this part emphasizes on the System Images.

    The system images defines the default images of the moving person, and the default item images. they are put under the directory "images" with can be specified on the applet html tag. (We realize later that this is not proper for the client to change). Every Object has a drawing peer named DrawXXX, for example Room has DrawRoom, Player has DrawPlayer. It is not difficult to guess that DrawPlayer contains the images files of the default player images. DrawItem has the image files of default items.

    As to the DrawRoom and DrawDoor, they do not have images but they both have a special way to draw themselves.

    All of these DrawXXX instance are put in the ImageTable (in ClientGlobal.java and are used in the drawSelf method of each objects. For item, if there is no DrawItem instance corresponding to the id of that item put in the ImageTable, it will just display the default item image. This could happen if the specified item URL does not exist. (in the case IconReader can not find it).

    Client/Server

    To construct a client and server connection is pretty simple in Java due to the existing socket package and thread support. The former makes it easy to construct the connection and the latter makes the server easy to handle mutiple clients at the same time. There can be theoreticaly unlimited number of clients connecting to the server at the same time. The only limit is the CPU speed and Network bandwidth.

    The server forks a ClientManager for each client, which sets up the first 2 message channels (see below), waiting for a new client connects to. Once the connections are made, the server forks another ClientManager and wait another new client and so on.

    Communications

    Communication is achieved by constructing 3 stream-oriented sockets. 1 is for high priority messages, 1 is for map data and 1 is for talk messages. Those in the URL data retrieving do not count in this.

    A simple graph dipicting communications which does not include the talk channel but should be clear enough (the names do not match to the final moduals either ):