Whenever posible, IP address and port values when stored as numbers rather than strings shall be stored in network byte order. All data transmitted over the network shall be in network byte order, but the source data may be stored in host byte order for easy run-time access to the values.
UDP over IPv4 will be used for all communications. Clients will use broadcast UDP for server discovery, and the server will use broadcast UDP to deliver information to all clients on the LAN. All other communication will not use broadcasts. The data inside the UDP packet should never exceed 512 bytes to eliminate packet fragmentation.
The server will listen on port 26000 for game related data, while the client will listen on port 26002 for everything. Port 26001 will be used on the server for time synchronization.
The common BSD sockets library will be used to support the networking across many platforms. On Windows, the winsock compatibility library will be used along with different code where needed to support the differences between winsock and BSD sockets.
To prevent I/O blocking from making the program unresponsive to the user, a thread for potentially blocking network operations will be used. The operations that will occur on this thread include:
It will never be assumed that any data received over the network is valid. This requires run-time checks for valid values to prevent players from circumventing the game's rules, for buffer overflows, and other events that could allow an unfair advantage or create security flaws.
Should the server see the results of a possible cheat, it should correct the data if the violation is not severe and may actually be a bug. However, if the violation is blatant or a client ignores a correction, then the client should be removed from the game.
Attempts to exploit security flaws will likely be indistinguishable from bad data. They should be silently ignored. If such a problem happens repeatedly, the server should drop the offending client from the game.
A method allowing clients to discover a server on the same LAN will be implemented. The client will broadcast a message fairly unique to the program. The server will respond directly to the client with another fairly unique message. In both cases, if the message received is not the expected fairly unique message, it will be silently ignored and the program will continue listening for the correct message.
Synchronization will be accomplished by the client sending a message to the server, timing how long it takes to receive the response, and performing a statistical analysis over multiple samples. The algorithm was published by Zachary Booth Simpson at http://www.mine-control.com/zack/timesync/timesync.html
Differences between the published algorithm and the implementation:
- The local computer's clock will not be changed.
- UDP will be used instead of TCP.
- More samples will be collected.
- The samples will not be sorted.
To allow the server and client to respond quickly to time sync messages, a separate thread, socket, and port (server only) will be used to handle the messages. The Blocking I/O Thread will run the time sync operation on the client (and server?).
The time synchronization process will begin immediately after a client has connected to the server. A minimum number of time samples and a good time delta are required before the player will be allowed to enter a game in progress. After a good time delta has been produced, synchronization will continue with a longer time between samples before the player has joined the game.
The client will obtain a time delta that when added to its own locally generated time will result in a close approximation of the server's time. The client will also know the approximate times when the server will run through the Game Simulation Loop. This will allow the clients to synchronize frame generation with the server to assist in maintaining a consistent game state across all clients. The time delta is a 32-bit signed integer.
- Revisit how the blocking I/O thread and time sync relate.
All operations to obtain the time delta will occur on the Blocking I/O Thread. Of all the data used to compute the time delta, only the delta itself will be available outside of the I/O thread. There will be no semaphore protecting the value because it is small enough for less-than-modern systems to use atomically, and the times when it is used will not be the times when it is changed.
The common game time is the time reported by server. With a time delta, clients can produce an approximate value for common game time. This is the time value used by all systems in the game for time sensitive operations, such as when to start the next game loop and knowing when a player was last updated. Common game time is in milliseconds.
The client and sever will exchange information as messages with a common header format. Only one message will be stored in each datagram. Some messages will require the recipient to send an acknowledgment to assure the message arrived. This section details the messages that are not used for time synchronization.
Detailed breakdowns of the data format for each message is on the Network Message Formats page.
Some messages require an acknowledgment. This is it. The message includes the player's ID, the number of messages being acknowledged, and the message IDs for each message acknowledged.
A connection request message is used by clients to attempt to connect to a server. The client informs the server if broadcast game updates can be received. This message may be broadcast to discover a server on the LAN. In this case, the message will be sent up to 4 times, 1 second apart, before the client concludes there is no server and takes on server functionality. All connect messages include a randomly generated 4 byte number used to identify a specific client prior to the server giving a client an ID. This allows the server to distinguish connecting clients that appear to have the same ID, and to avoid multiply connecting any single client.
A connection acceptance message is sent from the server to a client in response to connection request messages. It informs the client of its player ID and sends back the same random value sent by the client, or tells the client that the server denied the client. The client will not accept the connection unless the same random value is sent. The message is never sent as a broadcast. If accepted, the client must acknowledge the message to assure it is ready to enter the game. If denied, there is no need to acknowledge.
A disconnection message is sent from a client to the server when the player chooses to disconnect from the server or terminate the process. A disconnection message is also sent from the server to the clients when the server's user chooses to quit playing or terminate the process.
When a client disconnects, the server will send out a player update message to all remaining clients. It will specify a new value for the Player::flags field that clears the Player::active flag.
As the player who is running the server changes game options, the new options will be transmitted to all connected clients without the need for an acknowledgement. Options will also be sent to newly connected clients, in which case an acknowledgement is required. The options will include all the information entered on the Game Options menu, and a serial number to identify outdated options.
The game start message tells the clients when the game will start or if it is in progress, and a copy of the game options. All clients must acknowledge receipt of this message. The message will be resent every half second to any clients that have not yet acknowledged. Any client that fails to acknowledge after four attempts will be dropped from the game.
Tells the clients the game has concluded and the identity of the victor. All clients must acknowledge this message.
Data for multiple players may be placed in the same message, one player after another. Updates for up to 8 players may be combined into one message.
Player updates can update part or all of the player data. The portions are:
- Tank position and rotation (10 bytes)
- Shell position and rotation (12 bytes)
- Dead time, flags, and score (7 bytes)
- Player name and team (63 bytes max)
- Player::name (1 byte length followed by 62 bytes max non-terminated string encoded using UTF-16)
- Player::team (2 bits encoded with name length)
The player's name and team are not normally sent along with the other parts of the player data. If all players have long names and all the data for each player is sent, then no more than 7 players' data will fit.
The player update message includes its own 6 byte header placed at the start of all player update messages. The first byte contains the player's ID. The second byte stores a set of bit flags to tell which data portions are present. The remaining four bytes store the time of the update in common game time.
Tells the clients the current score for all teams. This message must be acknowledged by all clients.
- Maybe put this section in the network module documentation.
The messaging system controls transmission and reception of messages over the network. Its goal is to abstract the details of network communication and provide a reliable out-of-order transport.
A global next ID number is kept on each process. Every time a new message is sent that requires acknowledgment, the number is incremented. Messages that are retransmitted use the same ID number if the data has not changed. When the data for a message has been updated, the newer data will be sent for retransmissions using a new ID number. The ID number value is a 16-bit unsigned integer.
Messages will be stored along with a table of the destination clients and a table the clients that have acknowledged the message. On the client, the server is assumed to be the destination. The message will be resent up to a given maximum number of times. The time between transmissions doubles for every retransmission. Clients that are flagged as needing to respond that do not acknowledge the message will be marked as inactive and a disconnect message will be sent to all clients, including the one being disconnected.
- Message type (7 bits) plus acknowledge requested flag
- Sequence number (16 bits, only if ack flag set)
A message's life span ends either on the next resend attempt after all destinations have acknowledged, or after the timeout after the last resend attempt. At the end of its life, if there are any destinations that have not acknowledged then the message's failure function is called.
If a new message of the same type needs to be sent, then the data in the new message supersedes the old message. In this case, an old message that is still being resent is canceled before any clients are marked inactive.
The messages for player updates are not retained after they are sent to better deal with their constantly changing nature. Instead, a table is kept for each client to track the player information known to each client.
- What does the next paragraph mean?
The per client table stores the following for each player:
- The sequence number of the last update sent to the table's client (2 bytes)
- A set of bit flags to tell what parts of the player data have been updated but not yet acknowledged (1 byte)
- The number of frames since the oldest unacknowledged update (1 byte)
Store the sequence number and update flags of new updates with each send. The new update packet should include all data indicated by a bitwise and of the new updates with all previous unacknowledged updates. When an acknowledgment is received, all update data for that and previous updates are cleared.
- Update the above section to match how the code works.
This could cause data to be retransmitted too often in the case where one part of the player data requires updating one frame after a previous update was sent. The first update may be received by the server but there may not be enough time receive the acknowledgment before sending the next update.
Generated on Mon May 28 04:41:40 2007 for Retro Tank Super Attack by