Message Formats
A XBlast message has the basic format of a central data block to which
one or two layers are added.
A layer is a data portion containing entries that generally describe
the type of message contained in the data block. The result is a
layered data block which itself can be used with another layer.
Given a data block DATA containing N bytes, the Datagram layer leads
to the following layered data:
START - unsigned char, currently \0x68
LENGTH - unsigned char, length of DATA = \N
DATA - n bytes, given data block
END - unsigned char, currently \0x16
Obviously, the Datagram layer can only deal with data blocks up to 255
bytes. It simply enables a receiver to recognize where the end of the
data block is.
Routines dealing with the Datagram layer are defined and implemented
in net_dgram.[ch].
Given a data block DATA containing N bytes, the Telegram layer leads
to the following layered data:
START - unsigned char, currently \0x68
LENGTH - unsigned char, length of DATA = \N
COT - unsigned char, cause of transmission
ID - unsigned char, id of transmission
IOB - unsigned char, information object
DATA - n bytes, given data block
END - unsigned char, currently \0x16
The Telegram layer is only applicable to data blocks of up to 255
bytes. In comparison to the Datagram layer, the Telegram adds three
descriptive parameters.
The COT parameter roughly describes why the data is sent. Currently
the following values are recognized (see net_tele.h):
/* cause of transmission */
typedef enum {
XBT_COT_Activate, /* server demands activation */
XBT_COT_Spontaneous, /* client message */
XBT_COT_SendData, /* send data to client */
XBT_COT_RequestData, /* request data from client */
XBT_COT_DataNotAvailable, /* client does not have data */
XBT_COT_DataAvailable, /* client has data */
/* --- */
NUM_XBT_COT
} XBTeleCOT;
The ID parameter describes the format of the data itself. The following values are currently recognized (see net_tele.h):
/* telegram object id */
typedef enum {
XBT_ID_GameConfig, /* game data */
XBT_ID_PlayerConfig, /* player data */
XBT_ID_RequestDisconnect, /* host wants disconnect */
XBT_ID_HostDisconnected, /* message host has disconnected */
XBT_ID_StartGame, /* server starts the game */
XBT_ID_RandomSeed, /* seed for random numer generator */
XBT_ID_LevelConfig, /* level data */
XBT_ID_DgramPort, /* port for datagram connection */
XBT_ID_Sync, /* synchronisation between client and server */
XBT_ID_Async, /* client and server asynced */
XBT_ID_HostIsIn, /* host is in game */
XBT_ID_HostIsOut, /* host is out of game */
XBT_ID_TeamChange, /* Team Change */
XBT_ID_WinnerTeam, /* Client sends winner team for level */
XBT_ID_GameStat, /* Host sends game statistics */
XBT_ID_PID, /* Central sends PID */
XBT_ID_Chat, /* Server/Client sends chat line */
/* --- */
NUM_XBT_ID
} XBTeleID;
The IOB parameter is used to denote what the data refers to. Its
meaning usually depends on the ID value. Its main use is in
Server-to-Client transmission to identify the client the data refers
to.
Routines dealing with the Telegram layer are defined and implemented
in net_tele.[ch].
Given a data block DATA containing N bytes, the Browse layer leads
to the following layered data:
MAGIC - char[], currently "XBLAST" (what else? ;)
PROTO - unsigned char, protocol version, currently "\x01"
TYPE - unsigned char, message type
SERIAL - unsigned char, serial of transmission
DATA - n bytes, given data block
The Browse layer is theoretically applicable to any data block, but
since it is used exclusively in conjunction with the Datagram layer in
XBlast, the largest sensible data size is 246 (giving a layered data
size of 255).
The first descriptive parameter TYPE describes the format of the
data. The following values, taken from browse.h, are currently
recognized:
typedef enum {
XBBT_None, /* invalid data */
XBBT_Query, /* query for games */
XBBT_Reply, /* direct reply from game server */
XBBT_NewGame, /* XBCC inform central of new game */
XBBT_NewGameOK /* XBCC inform central of new game */
} XBBrowseTeleType;
The second descriptive parameter SERIAL denotes the message id from
the original sender. Any receivers simply use the received serial in
replies, but I have not seen that put to use yet in XBlast.
Routines dealing with the Browse layer are defined and implemented
in browse.[ch].
The data blocks are the core data portions used in the messages that
are actually sent from socket to socket.
No data, length=0.
A config is a null terminated string of the form
'entry=val\0'.
A port is a null terminated string. The value is extracted with
sscanf() and format "%u".
A hoststate is a null terminated string. The value is extracted with
sscanf() and format "%u", then cast to the XBHostState type (mi_tool.h):
/* client state */
typedef enum {
XBHS_None,
XBHS_Wait, /* waiting for client to send player data */
XBHS_In, /* client is in the game */
XBHS_Out, /* client is out of the game */
XBHS_Server, /* host is server */
XBHS_Ready, /* host is ready to start */
/*---*/
NUM_XBHS
} XBHostState;
A team is a null terminated string. The value is extracted with
sscanf() and format "%u", then cast to the XBTeamState type:
/* team state */
typedef enum {
XBTS_None,
XBTS_Red, /* unknown if this is good */
XBTS_Green,
XBTS_Blue,
/*---*/
NUM_XBTS
} XBTeamState;
A chat has the form
ORIGIN - who sent, higher 4bits are host, lower 4bits are player
TARGET - where to send, higher 4bits are host, lower 4bits are player
DATA - null terminated string, the message
A seed is a null terminated string. The value is extracted with
sscanf() and format "%u".
A result has the form
N - int (4 bytes), determines number of players and result type
SCORE - int[2] (8 bytes), pid of player and score
...
SCORE - int[2] (8 bytes), pid of player and score
The number of players is abs(N), which is of course also has to be the
number of SCORE entries. A negative N denotes the end of game, which
makes the score parts of the SCORE entries the number of wins. A
positive N denotes a level result, which makes the score parts of the
SCORE entries either =0 (player did not win) or !=0 (player won).
The int values are currently extracted using memcpy(), which invites
endian problems (needs fixing!). It should be little-endian order,
which is used consistently elsewhere in XBlast.
A times data block has the format
GAMETIME - unsigned short 0xFFFF
PING - unsigned short (2 bytes), client ping time
...
PING - unsigned short (2 bytes), client ping time
The unsigned short's are stored in little-endian byte order. A ping
time of 0xFFFF denotes a client with no ping time.
A frames data block has the format
GAMETIME - unsigned short (2 bytes), current game time
ACTION - unsigned char (1 byte), player action
...
ACTION - unsigned char (1 byte), player action
The game time is in little-endian byte order and has to be smaller
than 0xFFFE (OxFFFE is reserved for future, 0xFFFF used by times data
block).
An action byte value of 0xFF marks the end of action for that
client. Other action byte values are parsed by the routines in
action.c.
A newgame data block has the format
PORT - unsigned short (2 bytes), little-endian port number
VERSION - unsigned char[3], major/minor/patch version of server
GAME - char[48], game name string, null terminated
ID - int (4 bytes), little-endian game id
PAR - unsigned char[3], numLives/numWins/frameRate
A gamecfg data block has the format
PORT - unsigned short (2 bytes), little-endian port number
VERSION - unsigned char[3], major/minor/patch version of server
GAME - char[48], game name string, null terminated
HOST - char[32], host ip string, null terminated
ID - int (4 bytes), little-endian game id
PAR - unsigned char[3], numLives/numWins/frameRate
A gameid data block is an int (4 bytes) in little-endian byte order.
This section describes config database entries and their values as
needed for network communication. Databases consists of possibly
multiple sections with the section entries being config data blocks,
i.e. null-terminated "entry=value" strings. The entry strings are
defined by atoms - for the actual string behind the atom, see atom.c.
The game config database is a single section of config data blocks.
The server game config holds the following data, after "Create a
Network game":
# GameSetup
- atomLives = (int) : number of lives for each player
- atomRecLives = (int) : number of recommended lives
- atomIfRecLives = (bool) : use recommended lives
- atomWins = (int) : number of needed wins
- atomFrameRate = (int) : frameRate for game
- atomAllLevels = (bool) : choose from all levels
- atomRandomLevels = (bool) : choose levels randomly
- atomRandomPlayers = (bool) : random player positions
- atomRecordDemo = (bool) : record demo
- atomRatedGame = (bool) : communicate results to central
- atomInfoTime = (int) : display time for level info
- atomLevelOrder = (int) : order mode for levels
- atomBot = (bool) : use bot in game
- atomBeep = (bool) : beep when clients connect
- atomMusic = (bool) : play music during game
- atomTeamMode = (string) : team mode, only if non-NULL
# GamePlayers
- atomNumPlayers = (int) : number of local players
- atomArrayPlayer0[] = (string) : player name
- atomArrayControl0[] = (int) : control type
- atomArrayHost0[] = (int) : host type
- atomArrayTeam0[] = (int) : team
# GameHost
- atomHost = (string) : ip string of host
- atomPort = (int) : port of host
- atomFixedUdpPort = (bool) : use fixed udp ports
- atomBrowseLan = (bool) : visible in LAN
- atomAllowNat = (bool) : allow NAT on clients
- atomCentral = (bool) : visible in central
- atomGame = (string) : game name, only if non-NULL
# GameVersion
- atomVersionMajor = (int) : major version of host
- atomVersionMinor = (int) : minor version of host
- atomVersionPatch = (int) : patch version of host
The initial game config determined by the server for a freshly
connected client consists of the #GameHost group only, where only the
atomHost and atomPort entries represent meaningful data.
The client game config consists of the other three groups - of the
#GameSetup group, only atomInfoTime, atomBot, atomBeep, atomMusic seem
to contain meaningful data for a client.
The player config database is a single section of config
datablocks. It holds the following data:
# PlayerGraphics
- atomShape = (atom) : shape of player
- atomHelmet = (color) : helmet color
- atomFace = (color) : face color
- atomBody = (color) : body color
- atomArmsLegs = (color) : arms/legs color
- atomHandsFeet = (color) : hands/feet color
- atomBackpack = (color) : backpack color
# PlayerMessages
- atomMsgWinLevel = (string) : only if non-NULL
- atomMsgWinGame = (string) : only if non-NULL
- atomMsgLoseLife = (string) : only if non-NULL
- atomMsgLoseLevel = (string) : only if non-NULL
- atomMsgGloat = (string) : only if non-NULL
- atomMsgLaola = (string) : only if non-NULL
- atomMsgLoser = (string) : only if non-NULL
- atomMsgWelcome = (string) : only if non-NULL
# PlayerMisc
- atomUseStopKey = (bool) : use stop key
- atomTurnStepKeyboard = (int) : keyboard config
- atomTurnStepJoystick = (int) : joystick config
# PlayerId
- atomPass = (int) : player password for central
- atomPID = (int) : player pid at central
# PlayerName
- atomName = (string) : player name
The level config is a multi-section database, each section containing
config datablocks. The recognized section names are:
- atomInfo - 0 : mode string and level info
- atomPlayer - 1 : player positions
- atomShrink - 2 : shrink type
- atomScrambleDraw - 3 : scrambler draw
- atomScrambleDel - 4 : scrambler delete
- atomFunc - 5 : extras
- atomBombs - 6 : bomb behaviour
- atomGraphics - 7 : pixmaps
- atomMap - 8 : level map
A more detailed description to follow.