?

Log in

No account? Create an account
30 April 2007 @ 06:32 pm
While typing up code and compiling the basic framework of things, I've developed the Client object a bit:

class Client {
	mID clientID;   // a unique identifier for the client
	ClientConn	conn;   // used for communicating with the server
	ClientModule	clientModule;   // the game data used by the server
	DisplayDevice	dDev;
	SoundDevice sDev;
	List	ClientThings;   // a list of ClientThings
	List    Gadgets;    // a list of displayed gadgets
	List    ClientMaps;       // a list of the ClientMaps for this game
	ClientMap*  currentMap; // the current map
	public:
// the usual stuff
	Client(HWND,char*);
	~Client();
// these are called by the OS-specific I/O handling code
	void    LeftClick(unsigned int, unsigned int,unsigned int);
	void    RightClick(unsigned int, unsigned int, unsigned int);
	void    LeftRelease(unsigned int, unsigned int, unsigned int);
	void    RightRelease(unsigned int, unsigned int, unsigned int);
	void    MouseMove(unsigned int, unsigned int, unsigned int);
	void    KeyPress(unsigned int);
	void    KeyRelease(unsigned int);
//	void	findServer();
//	void    passData();
//	void    getData();
	void    draw();
};


As you can see, the mouse and keyboard member objects have been replaced with member functions that will be called by the OS-specific code (which will be kept in as small a set of OS-specific files as possible to maximize portability).

mID is an unsigned long integer, used for holding identifiers. Each Client will need an identifier (clientID) so that Servers can tell which client is which.

ClientConn is the way I'll be encapsulating OS-specific socket communications. conn is the instance of this class in the Client object.

ClientModule is a class for holding the data that the Client uses, and clientModule is the instance in the Client object.

The three List objects are for holding all of the objects, maps, and gadgets that the Client creates.
 
 
20 April 2007 @ 10:23 pm
The game will operate in a client-server way, even when in single-player mode, so the client will be an important part of the engine's architecture:

class Client {
    Module usedModule;
    DisplayDevice dDev;
    SoundDevice sDev;
    KeyboardDevice kDev;
    MouseDevice mDev;
    Game activeGame;
    Conn conn;
    public:
    Client();
    ~Client();
    void go();
};


usedModule is an instance of the Module object which contains all of the date that is peculiar to a specific game (graphics, sounds, scripts).

dDev, sDev, kDev, and mDev are objects which perform the various IO functions. Among other things, I will use them to encapsulate the system-specifc stuff so that as much of the engine as possible can be written in code that will run under Windows, Mac, or Linux.

activeGame is an instance of the Game object, which holds all of the client data that is specific to a single playing of a Game.

conn is the instance of the Conn class, which handles client-server communcations.

What I envision at the start of the client application is that if it is not supplied with a module file in the command line, then it will require the user to select a module file at start-up. Then it will load the specified module, create the various IO objects, and then run the first script in the loaded module. When that script exits, the Client object will be destroyed, and the application will exit.

Obviously, this first script must display the splash screen, then the main menu, and then run other scripts depending on the menu choices made. So it would look like this:

Client::Client(char* fileName) : usedModule(fileName) { }

void Client::go() {
    usedModule.runScript(FIRST_SCRIPT);
}


Or something like this.
 
 
22 October 2006 @ 08:17 pm
I did a lot of code layout stuff. Most of it was in the top-level organization sort of thing. One of the first things I did was to establish the division of labor between the client and the server. Basically, I am writing this as a network game, and will support single-player by having the client start up a server when user chooses a single-player game.

Right now I have the following C++ classes declared:

  • Link: This is my class for items that are in a doubly-linked list. I'd have used one of the standard C++ containers, but I always get too much bloat in the executable when I do that. It will probably look like this:

    class Link {
    	class Link *Next;
    	class Link *Prev;
    	Link();
    	~Link();
    	void remove();
    	Link *next();
    	Link *prev();
    };
    


  • List: This is a class for keeping a bunch of Links in line. It looks like this:


    class List {
    	class Link Head;
    	class Link Tail;
    	List();
    	~List();
    	class Link* first();
    	class Link* last();
    	void addHead(Link&);
    	void addTail(Link&);
    };
    


  • Anim: This class is the base class for handling the display of various things that are in the game, except for the land surface. It will handle monsters, missiles, treasure boxes, loot, doors, walls, NPCs, and just about anything else that has a visual presence on the screen. It will (through the items it manages) handle every direction faced by the item, and every frame of its posing during its passage through a given state. It will probably look like this:


    class Anim : public Link {
    	TAG	tag;
    	List	Poses;
    };
    


    Not much to it, eh?

  • The Pose class handles a bit more detail:

    class Pose : public Link {
    	TAG	state;
    	USHORT	frames;
    	USHORT	dirs;
    	
    };
    


    Anim is sub-classed:

    • Anim1: This class implements sprite-based graphic objects.
    • Anim2: This class implements polygon-based graphics obects.


  • Thing: This is the class which handles an instance of an entity of some type in the game.

    The difference between Anim and Thing is that Anim describes how a type of object will be displayed in the game, whereas the Thing class tracks each such object, and in addition to the information required for display, each Thing object will also have other user-defined data (such as health points, possessions, etc.). For instance, if the player is facing three skeletons, there will be one Anim object to determine how the skeleton is drawn, and three Thing objects to track the location and health of each skeleton. At the moment class Thing is sub-classed to:

    • ClientThing: The client-side version of Thing. It has extra members for that data that only the client-side needs.
    • ServerThing: The server-side version of Thing. It has a member which keeps track of the extra data that the mod designers put into the scripts. It will have a subclass:

      • Player: This differs from the ServerThing object in that it has a reference to the client which is passing all of the input to the server; the interpretation of this input stands in place of the AI for that ServerThing.



  • Conn: This is my class for handling the socket communcations. It is sub-classed:


    • Client: This is the object that handles things from the player's perspective. So far it has a list of the things that are to be displayed, as well as the gadgets and a specfication of which Map to draw.




  • Packet: This is the class which holds the data that is passed between the client and the server. There is a sub-class for the client-generated messages and another for the server-generated messages.




  • Gadget: Named after the same object from my old Amiga programming days, the Gadget does for Midge what controls do for Windows. It will have many of the features of Windows controls, such as being active or inactive, and each control can have scripts for mouse rollover, mouse click, and mouse release. At the moment it is a virtual class which I expect to sub-class to:

    • Button: A gadget which reacts to the mouse, either from mouse rollover, mouse click, and mouse release. Depending on the behavior specified, the object can be either static, a checkbox, a push button, or other wonderful things.
    • Edit box: This is for taking text input. I expect to subclass it as:

      • Number box: This is for entering numbers.
      • Address box: This is for entering IP addresses.
      • Password box: This is for entering passwords.


  • Game: This is the server-side object for a game that has been started by some client out there, and which other clients may have joined.

  • Tile: This is a small square of the game surface, and is one game distance unit in size. It is merely the coloring for that part of the land surface. I suppose that some of these could be animated in order to represent running water, lava, etc. There will also be a passability flag.

  • Section: This is an object designed for the automated generation of Maps. It consists of a array of references to one or more tiles, and optionally includes specifications for other objects to rest on the surface when a Map is generated using this Section. This enables Maps to be built from sections that are designed to be joined together.

  • Map: A map represents one of the areas in which action can take place. It is built from a script that is devised by the module designer. The Map object itself is merely an array of references to Tiles. The objects that are part of the play area are simply Thing objects, and are maintained in the list for the Game.

  • Generator: This is the object which handles map generation. It has a field to specify the ID of the map that it generates, and a set of instructions for building the map, including references to Section objects, and creation of the Things that are placed in the Map at the time of generation.

  • Module: A given package of graphics, sounds, and scripts which together create a game. It will be subclassed into ClientModule and ServerModule, each of which use different portions of the Module. At the moment I only plan to give them different constructors.

  • Script: This is where the soft-coded behavior (mod designer supplied) behavior of the game is implemented. The Script object itself will be nothing more than an array of script instructions, which will be compiled from the scripts supplied by the mod designer and in turn interpreted by the game engine as needed.

  • Sound: The object for producing various sounds at various times.

  • Music: The object for playing various sounds over time.
    </ul>

  • Server: This object handles the internal logic of the game; just about everything except user-interface functions. I hope that one server will be able to handle multiple games using multiple clients and multiple modules. The server has no need for the graphic or sound data, and therefore will not load it when loading a module (only the scripts and other data that drives the game mechanics).

  • Display: This is the class which will encapsulate all of the graphic work.

  • List: This is the header for the doubly-linked lists.

  • Property: This is a class that handles user-defined data for Things in the game. As matters stand, Things will have an index for the Anim they use, a field for their state, another field for the frame number within that state, a value to indicate the map in which the Thing presently is, and another two coordinates for the location of the Thing within that map. All other values, such as health points, AI settings, flags for being cursed, and so on, are needed for some items (and some modules) and not for others, so I left all of them as extensions for the mod designer. The Property class allows the mod designer to extend the data within a Thing as needed. Each Property object contains a pointer to the next Property object in the list, a field for the id of the specific setting, a member to indicate whether the setting is an integer, a floating-point value, or a string, and another member for the data itself.

    On the scripting side of thing, I will need to define a script file format for the menus. These will consist of a background graphic, a set of gadgets, and a script to go with each gadget. I imagine that some of these scripts will do nothing more than open another menu.
    </ul>

    I suppose that not defining a background graphic for a menu will direct that the existing graphics are to be used, which gives me the ability to define in-game menus (the kind that appear in D2 when the player hits the Esc key).

    Program flow

    Some of the flow ideas:

    • If the client-side application is started up without a module specified in the command line, the app searches for a list of modules and asks the user to select one.
    • Once the application has a module, it reads the data from the module that is required by the client side and then proceeds to the top-level menu for that module.
    • More about this later...
  •  
     
    17 June 2006 @ 12:04 am
    As I said early in the blog, portability will be a primary focus of this application. Yeah, Java is supposed to be platform-independent, but I'm not sure that Java delivers the speed that a game will need.

    But to maximize portability, all I have to do is ensure that everything platform-dependent is implemented in the smallest number of source files, and group them accordingly:

  • Client-server communications will be implemented in one object (or set of related classes), which will have its platform-dependent source all in one file.

  • I chose OpenGL because every platform supports it. All the OpenGL-specific stuff can go into the source files which control objects getting drawn.

  • Sound will be handled the same way.

  • Mouse and keyboard I/O will also be encapsulated.
  •  
     
    14 June 2006 @ 06:14 pm
    But here's some real code!

    class Link {
        Link*   n;
        Link*   p;
        public:
        Link();
        Link*   next();
        Link*   prev();
        void    remove();
        ~Link();
        friend class List;
    };
    
    Link::Link() { n=p=(Link*)0; }
    
    Link::~Link() {
        remove();
    }
    
    void Link::remove() {
        if(n) n->p=p;
        if(p) p->n=n;
        n=p=(Link*)0;
    }
    
    Link*   Link::next() {
        if(n==0) return 0;
        if(n->n==0) return 0;
        return n;
    }
    
    Link*   Link::prev() {
        if(p==0) return 0;
        if(p->p==0) return 0;
        return p;
    }
    
    class List {
        Link    Head;
        Link    Tail;
        public:
        List();
        Link*   first();
        Link*   last();
        void    addHead(Link*);
        void    addTail(Link*);
        bool    isEmpty();
    };
    
    List::List () {
        Head.n=&Tail;
        Tail.p=&Head;
        Head.p=Tail.n=(Link*)0;
    }
    
    Link*   List::first() {
        return Head.next();
    }
    
    Link*   List::last() {
        return Tail.prev();
    }
    
    void    List::addHead(Link* l) {
        l->p=&Head;
        l->n=Head.n;
        Head.n=l;
        l->n->p=l;
    }
    
    void    List::addTail(Link* l) {
        l->n=&Tail;
        l->p=Tail.p;
        Tail.p=l;
        l->p->n=l;
    }
    
    bool    List::isEmpty() {
        return (first()==(Link*)0);
    }
    


    This is just my standard code for managing a doubly-linked list.

    "But why aren't you using C++ Standard Containers?"

    Because I don't feel like it.
     
     
     
    14 June 2006 @ 06:10 pm
    Another reason for this client-server model is the ability to ensure platform portability by dumping the nuts and bolts of the communications onto the Conn object, so that the rest of the code is shielded from it.

    Basically, all the machine-specific coding for the client-server communications will be in the implementation file for the Conn class, so that—hopefully—porting will consist of changing just that one file.

    And today I learned that to get the client and server talking, I need to learn about socket programming. Finding out that it was called socket programming took quite a bit of time on search.msn.com .
     
     
    14 June 2006 @ 05:37 pm
    I have an idea of how I'll implement multiplayer options. What I'll do is to define a class to handle server stuff, another class to handle client stuff, and a third class to handle communication between the client and the server.

    This third class can be a simple pass-through during the time that this is a single-player game, when the client and server exists in the same machine (and in the same application); later I can modify the connector class to handle network communications when real multi-player becomes a feature. I don't expect the modifications to be extreme when that happens.

    One issue with multiplayer games is the need to ensure that all players are using the same data module. This can be accomplished by having the client take a checksum of the data module, passing the resultant value to the server, which compares it to what it gets from its own copy of the module.

    My checksum technique will be to treat the whole module bytestream as if it were one humongous integer, divide that integer by a set of prime numbers between 32771 and 65521 (the primes that are 16-bit unsigned integers), and return the remainders for each divisor. If I select four of these primes, then the checksum reduces the chances of mismatched modules to less than one in a quintillion. Which ought to be good enough.
     
     
    20 March 2006 @ 11:39 pm
    I have no idea how I'm going to implement multiplayers in one game yet.
     
     
    20 March 2006 @ 10:51 pm
    I changed my mind on the definition of the anim1 object (sprite-style object graphics).

    anim_id anim1 { // data for a single animation object
      height;
      width;
      pose {
        state_id,direction_count,frame_count;
        body: filepath;
        // additional body entries
        head: filepath,x_offset,y_offset;
        // additional head entries
        larm: filepath,x_offset,y_offset;
        // additional left arm entries
        rarm: filepath,x_offset,y_offset;
        // additional right arm entries
      }
      // additional pose entries
    }
    


    height is the height of the object in 65536ths of world units.

    width is the width of the object in 65536ths of world units.

    Within each anim1 object there is a set of poses. Each pose object controls the appearance of the anim when it is in a given state. Most monsters will have a state for standing still, for walking, perhaps for running, for attacking (and likely a different one for each kind of attack), perhaps one for reacting from being injured, and probably one for dying. A crafty designer could have monsters that play dead...

    state_id is the state id corresponding to this pose, meaning that every object using this anim will use this pose when in this state.

    direction_count specifies the number of different directions the object can appear to face in the given state. Directions will actually be in 65536ths of a full circle (or maybe 256ths, that's not defined yet), and moving objects will move along their actual angles; the directions here are for display purposes.

    frame_count specifies the number of frames for this state.

    body: The body entry states that the body of the anim will be taken from an icon sheet in the file specified by filepath. The picture is divided into direction_count rows and frame_count columns. There can be more than one body setting; the specific one will be chosen for each unit randomly when it is generated in the game. This way skeletons can appear to have remnants of different kinds of armor, for instance. The body icons should be a consistent size from one pose to the next in the same anim so that they are all resized in the same way for display; the width and height settings at the top of the anim specify the size of the body icons.

    head, larm, and rarm: These entries state that extra graphics will be added to the body before it is displayed for each unit. There can be multiple entries with the head tag; doing so will allow the engine to generate creatures with different heads. larm and rarm will work the same way. Note that the head, larm, and rarm tags are arbitrary to some extent; if the actual graphics for the head of the creature are put into larm entries, the engine won't care. I suppose I could design the engine so that any tag but body will be taken to be an overlay, and that matching tags will be taken to represent different options for the same overlay. The offset entries are in pixels, and indicate that the icon graphics are adjusted horizontally and/or vertically before being laid over the body graphic for the object. This offset allows overlay icons to be smaller than the body icons.

    It should be apparent that the graphic files will require an alpha channel.

    State ids will be defined by the mod designer. If the creature enters a state for which there is no defined pose, the creature will not be displayed while in that state (which may be what the designer wants).

    At some point I will define the anim2 object for 3d modeled object graphics. The main difference is that the direction settings will be unnecessary, because the same 3d model can be made to face any direction and still look okay, whereas sprites essentially need a different graphic for each direction that the object appears to face. The overlays will not need the offsets either, since the precise placement of the heads and arms will be inherent in the model definition. To be pendatic, they won't really be overlaid; they will simply be extra geometry, to be rendered in the same way as the body.

    The idea behind icon sheets is that the designer can draw or render the figure in as many steps and directions as necessary, and bundle all of the separate pictures into one file; this saves a bit on the complexity of the module distribution.

    I'll need some way to define palette shifting, if I decide to implement that in the engine.
     
     
    24 February 2006 @ 07:50 pm
    It turns out that the AI for a given creature can be implemented in only a handful of different calls. Therefore I can make things look like this:

    ai_label ai {
        onTick() { /* code to decide what happens at each clock tick */ }
        onAttack(attack) { /* code to decide the result of any attack */ }
        onMeet(intruder) { /* code to decide the result when another
                              object intrudes on this one */ }
    }
    


    I hope I can keep things this simple.