WindowMud - Chapter 3 - Sockets


WinSock

WindowMud, like many muds, uses TCP streaming non-blocking sockets. WinSock is an API that allows MSVC programs to communicate across the internet using TCP/IP. Windows Sockets Network Programming by Bob Quinn and Dave Shute is an excellent reference for WinSock. There are comments within the code that refer to the pages of this book. There are also numerous websites that have WinSock information like Winsock Programmer's FAQ. The WinSock calls used in WindowMud work, but there are most likely improvements that can be made.

WindowMud's game loop, BigDog, repeatedly checks for new connections. As long as there are no connections, WindowMud does nothing except check for new connections. Once there is at least one player connection, it begins checking each connection for input. When no players are connected, it quits checking for input, and only checks for new connections again.

Classes

Four classes are introduced to handle connections to WindowMud: Communication, Dnode, Descriptor, and Utility. The Communication class handles all WinSock activity and uses the Dnode and Descriptor classes to manage connections to WindowMud. The Dnode class keeps track of information concerning each connection including next and previous connection pointers, thus enabling a linked-list of connections. The Descriptor class provides functions to maintain and navigate the Dnode linked-list. The Utility class has one function, for now, called BlowUp that handles abnormal termination.

WindowMud will handle players via the Player class (to be implemented later). There will be a one-to-one relationship between connections and players. In the Dnode class there are a number of variables that begin with PlayerState which are used to track each players current state, particularly state changes during the logon process.


BigDog

BigDog is now sporting some shiny new function calls which have been highlighted. These function calls are all obviously important, like opening the port, checking for connections, closing the port, but the call that leads to all things mud is Communication::SockRecv. This is where player input is detected and saved for further processing. It could be argued that SockRecv is not the best name, because SockRecv does much more than just receive player input. But, if a player doesn't input something, very little of significance happens. So SockRecv seems appropriate.

Starting and Stopping

Global bool variables StateRunning and StateStopping are used to keep the game loop looping and to stop the game loop. Currently, there is only one way to stop the game: use the Menu->Control->Stop Game option. This renames the GoGoGo.txt file to StopIt.txt which is detected in the game loop causing StateStopping to be set to true. StateStopping being true causes all player connections to be force disconnected and prevents new connections from being established. StateRunning stays true until StateStopping is true and there are no player connections.

Ticks

BigDog has ticks, yeah yeah very funny. BigDog sleeps for 1/10th of a second during each game loop. It can also be said that BigDog wakes up 10 times a second, and therefore BigDog has 10 ticks per second. At each tick, BigDog checks to see if there are any new connections and receives available input from the connections. These ticks can be used to time other events. For example, dectecting that a player has not provided any input for x number of ticks during the logon process causing them to be disconnected.

The Code


Communication Class

The Communication class makes a number of calls to Descriptor and Dnode class functions. These functions are discussed later in this chapter. The communication class contains the socket functions which establish communication with the players and handle all input and output to the players. The sequence in which the socket functions are called is as follows:

The majority of the code in the Sock functions are WinSock calls followed by error checking. Any detected error causes WindowMud to abort via a call to Utility::BlowUp. Knowing what calls to make and what information to pass when making these calls can be a challenge. Below is a condensed list of the WinSock calls by Sock function. They are listed in the order of execution.

SockOpenPort

SockCheckForNewConnections

SockNewConnection

SockRecv

SockRecv loops through all connections receiving any available input and driving SockSend for each connection that has output to be sent. If a player is quitting, Descriptor::DeleteNode is called which calls closesocket to disconnect them. SockRecv uses several functions from the Descriptor class in conjunction with pDnodeActor. As SockRecv loops through the connections, pDnodeActor is set to the current connection.

SockSend

SockClosePort

The Code


Descriptor Class

The Descriptor class contains functions that maintain and navigate a linked list of pointers to Dnode. These functions use several variables which all begin with pDnode. pDnodeHead always points to the head/tail of the linked list of Dnode pointers. pDnodeCursor points to the currently selected Dnode. pDnodeNext and pDnodePrev point to the next and previous Dnode in the linked list.

Maintain

InitDescriptor creates a dummy Dnode and sets pDnodeHead to point to the newly created Dnode. Then the new Dnode's next and previous pointers and pDnodeCursor are all set to point to the new Dnode.

AppendIt places a newly created Dnode at the end of the linked list and updates the next/prev pointers.

DeleteNode, after closing the socket, sets pDnodeActor to NULL and deletes the Dnode pointed to by pDndoeCursor. Deleting the Dnode drives the Dnode class destructor which updates the next and previous pointers.

ClearDescriptor wipes the linked list. The linked list contains only the dummy entry when this function is called.

Navigation

SetpDnodeCursorFirst, SetpDnodeCursorNext, and SetpDnodeCursorPrev set pDnodeCursor to the first, next, and previous Dnode in the linked list.

EndOfDnodeList returns true when pDnodeCursor->DnodeFd is 0 which is the dummy entry and the head/tail of the linked list.

GetDnode returns a Dnode pointer, that being a pointer to the current Dnode which is pDnodeCursor.

Looping connections

The code to loop through all the connections looks like:


Descriptor::SetpDnodeCursorFirst();
while (!Descriptor::EndOfDnodeList())
{ // Loop thru all connections
  pDnodeActor = Descriptor::GetDnode();
  // TO DO: Code something here using pDnodeActor
  Descriptor::SetpDnodeCursorNext();
}

The Code


Dnode Class

The Dnode class contains information about each connection including the next/prev pointers, IP address, Socket handle or File Descriptor, input received, output ready to be sent, and the state of the connection/player. Once a player has connected to the server, a connection and a player have a one to one relationship. There are (for now) two PlayerState variables: PlayerStateBye and PlayerStateSendBanner. As the server is enhanced, a number of PlayerStates will be added. These are particularly useful in tracking a player's progress as they work through the character creation prompts or the login prompts.

Functions

There are only three functions in the Dnode class, the class constructor, the class destructor, and GetCount. The class constructor increments Count and initializes the variables. PlayerStateBye is set to false and PlayerStateSendBanner, which is used to begin the login process, is set to true. The class destructor decrements Count and fixes the next/prev pointers to remove the deleted Dnode from the linked list. GetCount simply returns Count.

Variables

In addition to the variables already mentioned, PlayerInp and PlayerOut serve as input and output buffers. Command strings will be extracted from PlayerInp and processed in the next chapter. To have something sent to the player, simply append it to PlayerOut.

Always code

PlayerOut +=
never code
PlayerOut =
unless you really mean to completely clear out the Player's output buffer.

The Code


Utility Class

The Utility class contains general purpose functions.

Functions

Only one, for now, BlowUp which logs the error, tries to close the listening socket, and calls WSACleanup to release WinSock resources.

The Code


Project Summary

In this chapter, the ability to service multiple TCP/IP connections was added. It is possible to see each player's input being accumualted in the CString variable PlayerInp by placing the cursor on line 295 of Communication.cpp and pressing ctrl-F10 (run to cursor) repeatedly and watching the value of PlayerInp change each time input is received (of course at least one connection must be made using a telnet client and input provided before the program will even stop on line 295).


Download the project

After downloading and unzipping the project, ensure that it is in the C:\WindowMud directory or change the HomeDir variable assignment to match the chosen directory.

Download the WindowMud project

WindowMud Home