ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ MOTOR CITY SOFTWARE ³ ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ ³ JPDoor - Version 3.1 SE ³ ³ ³ ³ Copyright 1990 - 1992 ³ ³ ³ ³ ÚÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ ³ ³ ³\ ³ ³ ³ ³ ³ ³ \ ³ ³ ³ ³ ³ ³ \ P ³ ³ ³ ³ ³ ³ \ A ³ ³ ³ ³ ³ ³ ³ S ³ ³ ³ ³ ³ ³ ³ C ³ ³ ³ ³ ³ 5.5 ³ ³ A ³ 6.0 ³ ³ ³ ³ ³ o³ L ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ \ ³ÄÄÄÄÄÄÙ ³ ³ ³ ³ \ ³ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\ ³ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ ³ The Ultimate \³ Door Writing Unit. ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This file contains information on writing doors that work in a multi-node environment. While your door may not be multi-player, we would recommend that you read over the following information anyways. With the number of Multi-Node BBS's steadily increasing, we felt it was very important to make it easy on the programmer to make his doors multi- node aware. This release of JPDoor has many new features that make it easy to write programs which work in a multi-node environment, even if you are not running multi-node yourself. We have taken this one step further, in that you can easily write doors which allow players to compete head-to-head! While some of this may sound complex, it really is quite simple. JPDoor will only allow multi-player games on RemoteAccess V 1.xx and QuickBBS V 2.75. Future versions will also include full support for PCBoard. SHARE MUST BE LOADED! Some of the multi-node capabilities included are: - Listing Other Users Currently Online - Sending Online Messages to other users - Determining the node number that the current user is logged onto - Determining who is logged onto other nodes - File/Record locking routines so your door can run on more than one node at a time - A method to ensure that your door is only run on one node at a time if need be - The ability for two nodes to pass data back and forth This document is going to try to explain how to write a door which allows two players, and passes game data back and forth between them. These routines have been tested extensively, and while they appear to be working fine, there exists a possibility that there are systems which may have problems. Because of this, we will be working closely with anyone having problems. If problems are discovered, then beta versions will be made available to any registered user who requests it. To start with, some basic concepts... Multi-player doors will only work with QBBS 2.75 or RA 1.xx All data is passed in a file called JPDOOR.USE which will be created in the system directory pointed to by either the RA or QUICK environment variable. JPDoor has defined the structure for this file as follows: TYPE JPUseData = Array[1..2048] of Byte; JPUSErecord = record Name : String[35]; Line : Byte; DoorName : String[20]; Status : Byte; MaxPlayers : Byte; Filler : Array[1..13] of byte; MultiNode : Boolean; PID : Integer; Data : JPUseData; End; { Status Byte : 0 - New Record Added } { 1 - New Data Written } { 2 - Data Has Been Read } Var JPUse : JPUseRecord; { Variable to hold MY record} JPUseOther : JPUseRecord; { Variable to hold OTHER users record } Lets look at each field : Name - This is the name of the user currently online Line - This is the node number the user is on. This does not default to 1 like the dorinfo1.def does, and you can get the users node with the function GetNode. GetNode will simply search USERON.BBS for the users name as defined in dorinfo1.def, and return the actual node he is loggend onto. DoorName - This is the name of your door. Its best to keep this to 15 characters or less. When JPDoor's Whoson procedure is called, it will display this so other users can see that your door is in use. Status - This is used internally, and you need not worry about it. The following values are used : 0 - New Record Added 1 - New Data Written 2 - Data Has Been Read MaxPlayers - This contains the maximum number of users who can play at one time. Currently, JPDoor will only support 2 players, but this will be increased in future versions. MultiNode - True if your door allows more than one user to run the door at the same time. PID - If your Door is a multi-player door, then you MUST apply for a Product ID code. You must be registered in order to receive one. In order to test your door, use a PID of 0. If you attempt to use any other value, it will abort! Data - This is where all the data is passed back and forth between two nodes. This is explained in detail below. A note about PIDs. The PID is used to ensure that you only try to read data written by YOUR door on another node. You dont want a battleship door reading a data file from an X's and O's door etc... The PID is validated internally by JPDoor. The Value of 0 is for test purposes only, as we do not want to force you to register BEFORE you can test it. If you release a multi-player door using a 0 for a PID, then you run the risk of it conflicting with other doors! You will need a PID for each and every different multi- player game you write. There is no fee for registering a PID, and there is no limit to the number of PIDs you may register, but you must be a registered user of JPDoor. PID 0 is ONLY for testing purposes, and you are expected to register JPDoor and request a PID BEFORE you release your door. How it all works. First, you need to decide what data you wish to pass back and forth. Define a structure that is exactly 2048 bytes in size. An example from my BattleShips door follows: TYPE Grid = Array[1..9,0..9] of Char; { Letter,Number } NodeData = Record Status : Byte; PlayerName : String[25]; SeaGrid : Grid; ShotN, ShotL : Byte; ShipHits : Array[1..6] of byte; ChatLine : String[78]; Local : Boolean; Filler : Array[1..1843] of Byte; {this is added to ensure that} { the record is exactly 2048 } { bytes in length} End; Once you have defined this structure, you need to be able to reference it two ways. You need to be able to modify each field, and also be able to reference it within the JPUseRecord. This is accomplished by using pointers. You must also remember that you need to reference YOUR nodes data, as well as the OTHER nodes data. Var JPD : JPUseData; { MY nodes Data as a field in JPUseRecord} OJPD : JPUseData; { OTHER nodes data as a field in JPUseRecord} NDPtr : ^NodeData; { My Nodes Data a I defined it} ONDPtr : ^NodeData; { Other nodes data as I defined it} Now JPD and OJPD are an Array[1..2048] of Byte; NDPtr and ONDPtr are pointers to the Type we defined above. All of these structures occupy the same amount of space, 2048 bytes. We can now tell our program that NDPtr points to the same location in memory where the variable JPD is stored, and ONDPtr points to the same location in memory where the variable OJPD is stored. If your not familiar with pointers, don't worry, I'm no pro either . Basically what we are doing, is saying that NDPtr points to the location where the variable JPD is located in memory. Because they are the same size, we can now reference the same data in two ways! NDPtr := @JPD; {OUR nodes data} ONDPtr := @OJPD; {OTHER nodes data} If we want to put the users name into the JPDoor.Use file, we simply refer to the name field defined in JPUseRecord as JPD.NAME To reference a field in OUR defined structure, we use the pointer. For example if I wish to pass a short note to the user on the other node, I can use the CHATLINE field I defined in MY structure. NDPtr^.ChatLine := 'Hi there...'; Because this points to JPD which is 2048 bytes in size, and ChatLine starts at 126 bytes into MY structure, is is saved in the same memory location as JPD, but at offset 126. Now JPD contains this string starting at byte 126. Like I said, it may sound confusing, but don't worry about it. Its kinda like driving a car, you don't have to understand how it works in order to use it, Afterall, thats what JPDoor is all about, making it easy to do things you may not understand! Adding a user to JPDOOR.USE --------------------------- Function JPUseAdd : Boolean; In your door, you need to add the user information to the JPDOOR.USE file. This is done with the JPUseAdd function. If JPDOOR.USE does not exist, it will be created. This function will count the number of nodes by counting the number of records in your USERON.BBS and one record will be added to JPDOOR.USE for each one in USERON.BBS. Here is a sample from my MAIN code: BEGIN (* M A I N *) ASSIGN(output,'') ; REWRITE(output) ; InitVars ; GetDorInfo('1',DropPath) ; NDPtr := @JPD; {Set the pointer to point to JPD} ONDPtr := @OJPD; FillChar(NDPtr^,SizeOf(NDPtr^),#0); {Fill the memory location with Nulls} JPUse.Pid := 0; {Set Your PID - 0 if not registered} JPUse.Name := UserName; JPUse.MultiNode := True; JPUse.DoorName := 'BattleShip'; JPUse.Line := GetNode; NDPtr^.Status := 1; JPUse.MaxPlayers := 2; JPUse.Data := JPD; { Now move JPD into the JPUse.Data field} If Not JPUseAdd then Begin CRLF ; Display(0,15,0,'Motor City Battle Ships allows for two players at once,') ; Display(0,15,0,'and there are currently two people playing.') ; CRLF ; HALT(0) ; End; You will notice that I set JPUse.MaxPlayers to 2 players. When JPUseAdd is called, it counts the number of users already in JPDoor.Use with the same PID, and if there are already MaxPlayers playing, JPUseAdd will NOT add you, and will return False. Removing a user from JPDOOR.USE ------------------------------- Procedure JPUseExit; When the user exists the door, under ANY circumstances, you MUST make sure you remove him from the JPDOOR.USE file. This is done by adding the JPUseExit procedure to your Terminate procedure as follows: Procedure Terminate(n:byte) ; begin JPUseExit; {This ensures that the user is removed!} case n of 0 : begin CloseGame ; LastComments ; end ; 1 : begin CloseGame ; Writeln('þ Carrier lost') ; end ; 2 : begin CloseGame ; Writeln('þ Time limit exceeded') ; end ; 3 : begin CloseGame ; Writeln('þ User inactive') ; end ; 4, 5, 6 : begin end ; 7 : begin CloseGame ; Writeln('þ CTS timeout error') ; End ; else CloseGame ; end ; end ; Updating Data in JPDOOR.USE --------------------------- Procedure JPUseUpdate; Data is passed to the other node by updating your JPUse.Data field, so that the other node can read it. It is similar to the JPUseAdd function except this is a procedure, and does not return any values. You may find it easier to create a PlayerUpdate procedure as I did in BattleShips: Procedure UpDateMyData; Begin NDPtr^.Local := Local; If Not SendChat then NDPtr^.ChatLine := ''; SendChat := False; JPUse.Data := JPD; JPUseUpDate; End; Your code would update any relevent fields, then call this procedure. When you Update your data, JPUse.Status will be set to 1 signifying that new data has been written to the file. Getting Other Users Data from JPDOOR.USE ---------------------------------------- Function JPUseGet : Boolean; JPUseGet will scan the JPDOOR.USE file for another user (it checks the username) with the SAME PID as your door. If one is found, and new data is read (JPUse.Status = 1) then JPUseGet returns TRUE and the other users data is stored in the variable JPUseOther. JPUseOther is exactly the same as JPUse, but is a seperate variable to hold the other players data. Both are defined in JPDoor. If there is no other user, then JPUseGet returns false, and the variable is filled with nulls. If it finds a user, but no new data is found, it returns false, and contains the same data as before the call was made. If you are waiting for the other player to take his turn, you may simply keep calling this funtion until it returns TRUE. The other players data is read the same as you write your data. If JPUseGet then Begin OJPD := JPUseOther.Data; { Put OTHER users data into OJPD} End; To get his name : JPUseOther.Name To Get his Node# : JPUseOther.Line To Get his Chatline : ONDPtr^.ChatLine etc...