This description refers to the 2.9.23 code and is not yet complete. But should provide enough foundation to enable any interested reader to fill in the missing details by inspecting the code. :) XBSocket -------- At the root of network communication in XBlast is the XBSocket structure, which stores a socket handle and basic socket properties (socket.h) - the basic socket routines are implemented in w32_socket.c and x11_socket.c for w32 and x11, respectively. To process socket i/o, the XBSocket is first wrapped in a XBComm structure. XBComm ------ The basic XBComm structure stores a XBSocket structure and handlers for reading, writing and deleting (com_base.h). XBlast keeps a list of all XBComm structures it creates (com_base.c). Each element of that list has to be uniquely identifiable by its socket handle. For a given XBComm structure, processing the i/o of its socket is done via a select call that is part of the GUI event loop (HandleSelect for w32, SelectSockets for x11). To enable processing of incoming data on a socket, it has to be registered for reading. The select call then checks that socket for readibility and if it is readable, the corresponding XBComm structure will be determined and its read handler called. If the read handler returns "finished" or "error", the delete handler is triggered, which should remove the socket from the XBComm list and free the allocated memory. Otherwise, successfully parsed data has been passed to either the Server (server.c), Client (client.c), Central (central.c) or User (user.c) code unit, which generated the XBComm structure. To be able to write data to the socket, the socket has to be registered for writing which triggers the select call to first check for writeability, and if so, determine the wrapping XBComm structure, and call its write handler. On "finish" and "error" from the write handler, the XBComm is then removed from the XBComm list via the delete handler. Refined XBComm structures built on this basic structure are the XBCommDgram (UDP), XBCommListen (TCP-Listen), XBCommStream (TCP) and XBCommBrowse (UDP). Each of those refined structures has additional refinements for specific purposes. XBCommDgram ----------- The XBCommDgram structure implements a UDP socket (com_dgram.h). The user provides the UDP socket and handlers for ping, action, finish. The read handler first ensures that incoming data is from the expected host, otherwise "error" is returned. Next, the Datagram layer is removed and - if the layer is missing, all the data is ignored and "ok" returned. The remaining data is parsed as follows: Ping no data, call ping handler(0,0) Time (gametime=ping) iteratively (from i=1) extract two bytes (time) successively and call ping handler(i,time) Reserved (gametime=reserved) for future enhancements Frame (gametime!=ping) iteratively extract an action packet The write handler writes the currently stored package to the socket und unregisters the socket for writing. So to write to a socket, data has to be stored in the structure, the socket has to be registered for writing, which will then actually write during the next event loop. During one cycle only one package can be sent, there is no queuing built into a XBCommDgram structure. This structure is implemented as XBCommDgramClient (for server) and XBCommDgramServer (for client). XBCommDgramClient ----------------- The XBCommDgramClient structure is created upon the XBCommDgram structure by a server to provide a UDP connection to the client (com_dg_client.c). Upon initialization, a UDP socket is created which is either randomly bound or to port 16168+clientID, depending on the fixedUdpPort option. The ping handler only responds to Ping signals and ignores Time signals. The ping time is stored and the difference to the previously stored ping time calculated. This time is communicated to the Server. The action handler passes clientID, gametime and keys to Server. The finish handler passes clientID to Server. XBCommDgramServer ----------------- The XBCommDgramServer structure is created upon the XBCommDgram structure by a client to provide a UDP connection to the server (com_dg_server.c). Upon initialization, a UDP socket is created which is randomly bound to a local port and connected to the server UDP port. The ping handler responds to Ping signals with a return ping; Time signals are passed on to Client with the ping time and corresponding clientID. The action handler passes gametime and keys to Client. The finish handler calls the Server finish handler. XBCommListen ------------ The XBCommListen structure is the XBlast implementation for a TCP Listen socket. The user just provides the port and the type (Server, Central), a TCP socket is created by the initialisation routine. The socket is constantly registered for reading, unregistered for writing. The read handler generates a XBCommStream structure of the same type, accept'ed from the receiving socket. The Server type XBCommStream structure is the XBCommToClient structure, the Central type structure is the XBCommFromCentral structure. The write handler is empty (not needed), the delete handler removes the basic XBComm structure from the XBComm list. XBCommStream ------------ The XBCommStream structure is the XBlast implementation of a generic TCP connection, built on the XBComm structure (com_stream.h). The user provides the XBSocket, a handle function, an error handler and a delete handler. The read handler from the basic XBComm then first reads data from the socket to an internal rcv queue. If an error occurred, the error handler is called and "error" returned (triggering the delete handler). If end-of-file was read, the opposite side of the TCP socket, was shut down, and "finish" is returned (also triggering the delete handler). Otherwise, the received data will be transformed into XBTelegram's (net_tele.c) of the form COT(ID,IOB)[DATA] - each complete XBTelegram will be passed to the handle function. The write handler tries to write as much data as possible from an internal snd queue, which means that the user has to queue outgoing data, before it makes sense to register a socket for writing. If the queue was completely emptied, the socket is automatically unregistered for writing and possibly shut down for writing, if the user set an internal flag. If there was an error, the error handler is called, "error" is returned, triggering the delete handler. There are four special types of XBCommStreams used in XBlast, XBCommToClient, XBCommFromCentral, XBCommToServer, XBCommToCentral, which are described now. XBCommToClient -------------- This is the Server-to-Client TCP connection, based on the XBCommStream structure (com_to_client.h). Upon initialization, the client gets assigned a serial/id, based on an array of client connection stored in com_to_client.c. This id is stored within the XBCommToClient structure. The handle function deals with the following types of XBTelegrams: DataAvailable GameConfig()[cfg] PlayerConfig()[cfg] DataNotAvailable GameConfig Activate DgramPort()[port] LevelConfig(okay)[] WinnerTeam(team)[] Spontaneous Sync(event)[] Chat()[] It returns "error" if COT differs from the four accepted types. The error function queues a XBNWError(id) event. The delete function removes the client from the game setup and the XBComm from the XBComm list. XBCommFromCentral ----------------- This is the Central-to-User TCP connection, based on the XBCommStream structure (com_from_central.h). Upon initialization, the connected user is a assigned a serial/id which is stored within the structure. The handle function deals with the following types of XBTelegrams: RequestData PlayerConfig()[cfg] SendData PlayerConfig()[cfg] GameStat Activate RequestDisconnect Spontaneous HostDisconnected It returns "error" if COT differs from the four accepted types. The error function queues a disconnect announcement for the user, then queues a XBNW_Error(id) event. !! Disconnect announcement never gets sent !! The delete function removes the client from the game setup and the XBComm from the XBComm list. Queues a XBNW_Disconnected(id) event. XBCommToServer -------------- This is the Client-to-Server TCP connection, based on the XBCommStream structure (com_to_server.h). It is used to connect to the XBCommListen socket of a server. The handle function deals with the following types of XBTelegrams: RequestData PlayerConfig(pnr)[cfg] GameConfig(id)[cfg] SendData PlayerConfig(id/pnr)[cfg] GameConfig(id)[cfg] LevelConfig()[cfg] Chat()[] Activate RequestDisconnect() StartGame RandomSeed()[seed] DgramPort(id)[port] Sync(event) Async(event) HostIsIn(id) HostIsOut(id) TeamChange(id)[team] LevelConfig(start) Spontaneous HostDisconnected(id) It returns "error" if COT differs from the four accepted types. The error function removes Dgram Polling from GUI and queues error events to Network and GUI. The delete function removes the XBComm from the XBComm list and from Client. XBCommToCentral --------------- This is the User-to-Central TCP connection, based on the XBCommStream structure (com_to_central.h). It is used to connect to a XBCommListen socket of Central. The handle function deals with the following types of XBTelegrams: DataAvailable PlayerConfig()[cfg] PID()[pid] DataNotAvailable PlayerConfig PID It returns "error" if COT differs from the two accepted types. The error function queues error events to GUI and Network. The delete function just invalidates the XBComm stored at User. XBCommBrowse ------------ The XBCommBrowse structure is built on the basic XBComm and handles UDP connections related to finding network games (com_browse.h). The user specifies a UDP socket and a receive handler. The read handler reads the data and the sender and removes a Dgram layer. The remaining data are parsed as Browse packages - if the parse was successfull, the receive handler is called. The write handler attempts to write the first package from the snd queue to the socket. If it fails, "error" is returned, which will cause a shutdown. If the queue is empty after sending, the socket will be automatically unregistered for writing. Writing to the socket requires first queuing data to the socket, registering it for writing - the data will be written during a future event loop. Specializations of the XBCommBrowse structure are XBCommQuery, XBCommReply, XBCommNewGame and XBCommCentral structures. XBCommQuery ----------- The XBCommQuery structure builds on the XBCommBrowse structure (com_query.c). It is used by a potential client to search LAN or central for servers. Upon initialization, a UDP socket on a given interface is created. After that a Query signal should be queued, which either is broadcast in LAN or sent to central. The receive handler only handles Reply signals and passes them to Client. The delete handler removes the structure from the XBComm list. XBCommReply ----------- The XBCommReply structure builds on the XBCommBrowse structure (com_reply.c). It is used by a server to make itself visible in LAN. Upon initialization, a UDP socket is created and bound to a given port. Moreover, the game config to reply is stored within the structure. The receive handler only handles Query signals and responds with a Reply signal based on the stored game config. The delete handler removes the structure from the XBComm list. XBCommNewGame ------------- The XBCommNewGame structure builds on the XBCommBrowse structure (com_newgame.c). It is used by a server to make itself visible in central. Upon initialization, a UDP socket is created and bound to a given interface. Moreover, the game config is stored within the structure. A NewGame signal should be queued after initialization, to prompt an id update from central. After the game ends or before closing the socket a NewGameOk should be queued to remove the entry at central. The receive handler only handles NewGameOk signals which updates the id under which central has stored the local game config. The delete handler removes the structure from the XBComm list. XBCommCentral ------------- The XBCommCentral structure builds on the XBCommBrowse structure (com_central.c). It is used by Central to provide servers to potential clients. Upon initialization, a UDP socket is bound to a given local port. The receive handler handles NewGame (servers provide/update game configs) respond NewGameOk with the id under which that config was stored NewGameOk (server requests removal) remove game config Query (potential clients request server data) respond with Reply's for each stored game config The delete handler removes the structure from the XBComm list. Server ------ The server code unit (server.c) utilizes the XBComm structures in the following way: - XBCommListen: after creation of a network game, it starts a XBCommListen socket to wait for incoming clients. An incoming client then produces a XBCommClient connection which the Server can access via its serial. The XBCommListen socket is removed when the game is started. - XBCommClient: after a client has been accepted by the XBCommListen, the Server initializes the connection to a client as follows: * The game config (local player names) for the client is initialized and sent to all connected clients (including the current one) as SendData:GameConfig. * The player configs for all connected clients are sent to the current client as SendData:PlayerConfig. * Then, a XBCommDgramClient socket is assigned for this client and tied to XBCommListen - the local port number of this new socket is sent to the current client as Activate:DgramPort. * Finally, the Server sends a RequestData:GameConfig to prompt the client to send its game config. * a network event (XBNW_Accepted) is triggered, which is picked up in menu_network.c to display the current client. On receiving an Activate:DgramPort, the server connects XBCommDgramClient socket to the remote port or sets it to delay connections until client socket has successfully sent data (NAT case). On receiving a DataAvailable:GameConfig, Server continues to setup the client: * if it's a non-empty cfg-line, it is added to the database of received config lines for that client. * if it's an empty cfg-line, the stored database is considered complete: first the remote version is checked - on failure, the connection is terminated. (new in 2.9.23 - older clients don't send a version line and get rejected automatically). * if the version check succeeded, the game config for the client is updated and then sent to all connected clients to let them update, as SendData:GameConfig. * for each player in the new game config a RequestData:PlayerConfig is sent to the current client. * a network event (XBNW_GameConfig) is triggered, which is picked in in menu_network.c to display the player names and set to host state to XBHS_Wait. On receiving DataAvailable:PlayerConfig, Server finishes the setup of client as follows: * if it's a non-empty cfg-line, it is added to the database of received config lines for that client. * if it's an empty cfg-line, the stored database is considered complete: the received player config is sent to all connected clients. * a network event (XBNW_xxxPlayerConfig) is triggered which is picked up in menu_network.c for display. If all player configs are in, the host state is then updated to XBHS_In there - the client has joined. On receiving SendData:Chat, the received chat line is displayed locally and then transmitted to all connected clients. The Server sends the current host states and team states to all connected clients as Activate:HostIsIn, Activate:HostIsOut and Activate:TeamChange when a change of state occurs. When the game is started, the XBCommListen socket is removed, the current team states are sent to all connected clients (Activate:TeamChange). Each connected client with host state XBHS_In, the start signal is sent (Activate:StartGame), to all other connected client a disconnect signal is sent (Spontaneous:HostDisconnected, Activate:RequestDisconnect) and the connection is shutdown for writing. After the game is started, the server machine waits that for each client id the XBNW_EndOfInit event, the XBNW_Error or the XBNW_Disconnect has been raised. It then sends Activate:Sync(XBNW_EndOfInit) to each connected client to trigger the XBNW_EndOfInit event there. The event XBNW_EndOfInit is raised by receiving a Spontaneous:Sync(XBNW_EndOfInit). The XBNW_Disconnect and XBNW_Error are triggered by remote shutdowns or i/o errors on the connection. The server then goes on to send the random seed and a level proposal to all connected clients as Activate:RandomSeed and SendData:LevelConfig. Then the Server waits that either of the network events XBNW_LevelConfig, XBNW_Error or XBNW_Disconnected is raised for each client. The XBNW_LevelConfig event is raised upon receiving an Activate:LevelConfig, which also sets the level as accepted or rejected by the client. Now the server checks for received rejections: if there is one, an Activate:LevelConfig(0) is sent to all clients to reset the level data. The server then chooses a new level and repeats the procedure. If it fails five times, level negotiations failed and the game is terminated. If all clients accepted the level, Activate:LevelConfig(1) is sent and the server moves on to start the level. After displaying the level intro, the server waits for XBNW_LevelIntro, XBNW_Disconnect or XBNW_Error events for each connected client. The XBNW_LevelIntro is raised by receiving a Spontaneous:Sync(XBNW_LevelIntro). In-game communication on the XBCommToClient connection consists of SendData:Chat signals (which get locally displayed and transmitted to other clients), remote shutdowns and I/O errors (which terminate the connection). The actions are transferred via the XBCommDgramClient connection. After the level ends, the server awaits the XBNW_SyncLevelResult from all clients (or alternatively XBNW_Disconnect, XBNW_Error). The XBNW_SyncLevelResult gets raised by receiving Activate:WinnerTeam. The server checks all received winner teams and sends Activate:Async, Activate:Sync depending on the result of the comparison. After that, it updates the local scores. The server continues to wait for the XBNW_SyncLevelEnd event from all clients, which is raised by receiving a Spontaneous:Snyc(SyncLevelEnd). Alternatively, XBNW_Disconnect or XBNW_Error events are accepted. i.e. disconnected clients. The server then displays the scoreboard and waits for the XBNW_SyncScoreboard event from all clients, which is raised by receiving a Spontaneous:Snyc(SyncScoreboard). Alternatively, XBNW_Disconnect or XBNW_Error events are accepted. i.e. disconnected clients. If the game is not yet over, the next level is chosen and level negotiations are restarted. Otherwise the Server sends disconnect signals to all connected clients, shutting down the XBCommToClient connect for writing. - XBCommDgramClient: this socket is created after a client has connected on the XBCommToClient socket. The server port number is communicated to the client is via the XBCommToClient connection. The Server sends Times when there was no write on that socket for more than 500ms. If either last write or last read on the socket was more than 10secs ago, the connection is considered lost and the client is disconnected. Incoming Pings update the current client ping time by using the difference time to the last write. Incoming Actions are stored for the client, as well as incoming Finish signals. Before game start only pings are expected from clients, during the game the Server handles network events for a fixed frame time after which Server sends all received player actions for that particular frame and increases the game time. The socket is removed when the game ends together with the XBCommToClient connection it is tied to. The same can also happens before when the client is disconnected for some reason. - XBCommReply: after creating the network game, a XBCommReply socket is created on port 16168 to make the server machine visible in LAN. On receiving an incoming Query, Server sends the connection parameter for the currently hosted game. When the game starts, this socket is removed. - XBCommNewGame: after creating the network game, a XBCommNewGame socket is created on the first available network interface on which the creation succeeds (if visible in central is enabled). The game parameter are then sent to central as NewGame signal. On incoming NewGameOk signal, the returned id under which central has registered the game is stored. In the pre-game phase the parameters are regularly resent every 255 cycles with the current game parameters. During the game, the parameters are resent at the start of each level and every 1024 frames theeafter. The socket is removed at the end of the game. - XBCommToCentral: after starting the game, a XBCommToCentral connection to central is established (if rated game is enabled). Each time a level result is determined, it is sent to central as SendData:GameStat. At the end of the game, the game results are sent (with negated player number) as SendData:GameStat. Then the connection is terminated. Data Formats ------------ Datagram Layer (net_dgram.c) - start - len - data (length=len) - end Browse Layer (browse.c) - magic word - protocol version - type - serial - data Telegram Layer (net_tele.c) - start - len - cot - id - iob - data (length=len) - end Every XBComm based on XBCommDgram uses Datagram Layer. Every XBComm based on XBCommBrowse uses Datagram Layer + Browse Layer. Every XBComm based on XBCommStream uses Telegram Layer. For more details look at http://xblast.sf.net/messages.html