From WinBolo

Jump to: navigation, search

This page provides some more details on WinBolo's networking.

WinBolo uses a client/server networking model where game clients join a server.

WinBolo has 61 different types of packets in the WinBolo networking protocol. They are defined in netpacks.h



WinBolo was originally designed to allow players with very high pings to be able to play the game. This allowed players on dialup to play as well as true international games to be able to be played. e.g. players from Australia, Canada, Germany, United Kingdom and the United States all in the same server.

The way this was done was via a combination of client side authoritative code (e.g. a client decides if a tank has been hit) and client side prediction such as map changes ( mapnet code) Players could interact fairly well in these situations as pillbox targeting (done locally as well) was completely accurate. The downside to this was that dog fighting between players suffered and pill warring become a much bigger part of the game.

The bigger problem with the source becoming available is that it will be even easier to cheat. A user could recompile a client disabling pillboxes from shooting at them or make their tank invulnerable. The current code base would allow this. The server does some preliminary checking to make sure a user is not cheating but it is a stop gap solution. The code needs to be modified so the server has the authority for everything.

For example, to move the pillbox functions to the server, we need to understand when pills move. This comes down to a couple instances:

  • A tank picks up a pill (on or off a boat, see tank.c)
  • A tank dies and drops pills (see tank.c)
  • An LGM dies and drops a pill (see lgm.c)
  • An LGM plants a pill from the tank (see lgm.c)
  • A player quits the game who is holding pills (see servernet.c)

Then netpnb.c is used to move the event about between the server and clients.

Implementing other anti cheat mechanisms such as the Quakeworld speed cheat detection and Netrek blessed binaries with RSA encryption would also be good ideas. See Discussion of RSA implementation.

Packet Types

There are three types of packets in WinBolo:

  • Unreliable packets
    • These packets are consider to be non critical and if they never reach the destination aren't important if they do not all reach their destination.
    • Third last byte contains UDP_NON_RELIABLE_PACKET
    • Last two bytes contain a 16bit CRC
  • Reliable packets
    • These packets are critical and must be received in order. If any are dropped it will put WinBolo into a netFailed state and the game will lock up till it has resynced.
    • Third last byte contains reliable packet number
    • Last two bytes contain a 16bit CRC
  • Setup and miscellaneous packets.
    • May or may not contain a CRC.
    • Used in game setup and initial connections.
    • Also used ???

Client Functions

The network.c file contains all the logic for joining a game and managing sending/receiving packets.

The main functions are by the front end are:

Joining a game

*NAME:          netSetup
*AUTHOR:        John Morrison
*CREATION DATE: 21/02/99
*LAST MODIFIED: 01/04/02
* Sets the network kind of game being played and sets up
* netClient
*  value       - The network type of game being played
*  myPort      - netClient port on this machine
*  targetIP    - Target IP on a machine to join
*  targetPort  - Target port on that machine
*  password    - Password for the net game (NULL for none)
*  usCreate    - TRUE if we started the game, FALSE if we
*                joined
*  trackerAddr - Address of the tracker to use
*  trackerPort - Port of the tracker
*  useTracker  - Whether to use the tracker or not
*  wantRejoin  - TRUE if we want to rejoin the game else
*                just join
*  useWinboloNet - TRUE if we want to participate in
*  wbnPassword   - Our password
bool netSetup(netType value, unsigned short myPort, char *targetIp, unsigned short targetPort,
              char *password, bool usCreate, char *trackerAddr, unsigned short trackerPort,
              bool useTracker, bool wantRejoin, bool useWinboloNet, char *wbnPassword);

This is the function that setups the networking routines. It must be called for single player and multiplayer games. For single player games (netType = netSingle) all values can be 0 or NULL.

After networking is initialised it starts the join game process:

  • netJoinInit(); is called first
    • Sends an INFO_PACKET request to the targetIp and targetPort. Expects an INFO_PACKET back. (This can be sent to a server or a game client as the game client will include the servers details in the response)
    • Checks to see the game is not full and sets up the backend options (hidden mines, ai type)
    • If the game is passworded prompts for password and sends a BOLOPACKET_PASSWORDCHECK packet (Expects a BOLOPACKET_PASSWORDACCEPT packet back)
    • Checks the players name is avialable with a BOLOPACKET_NAMECHECK packet. Expects BOLOPACKET_NAMEACCEPT packet back
      • may fail with a game locked BOLOPACKET_GAMELOCKED packet or BOLOPACKET_NAMEFAIL name used packet.
  • netJoinFinalise(); is then called if we have not errored out.
      • If the response packet contains a WBN key and this player is using WBN perform player name check via the WBN Protocol.
    • Sends BOLOPACKET_PLAYERNUMREQUEST to request a player number. Expects BOLOPACKET_PLAYERNUMRESPONSE packet back. This packet is the first packet to have a 16bit CRC appended to it.
    • Sends rejoin request packet BOLOREJOINREQUEST if a player rejoin is requested. This is a non-reliable packet.
    • Requests player data with a a BOLOPACKET_PLAYERDATAREQUEST packet. This contains information about each player in the game.

After joining the netJoinDataRequests(); function is used to request the different things needed to start the game. pillbox items, bases, map download etc.

Incoming Packet

*NAME:          netUdpPacketArrive
*AUTHOR:        John Morrison
* A UDP packet has arrived. It is processed here.
*  buff  - Buffer that has arrived.
*  len   - length of the packet
*  port  - The port this packet arrived on
void netUdpPacketArrive(BYTE *buff, int len, unsigned short port);

This is the function called when a new packet has arrived. What it does.

  • Rejects any packet that does not contain the WinBolo header.
  • Handles non-reliable packets first
    • Checks to see if it is a PING_PACKET which contains the reliability packet numbers. These tell the client the last reliable packet number the server received and the last packet the server sent. From this the client can compare that to its own reliability packet numbers.
      • If it is out of sync it puts the game into netFailed mode and sends a BOLOPACKET_PACKETREREQUEST packet.
    • Checks if it is an BOLOPACKET_INFOREQUEST packet and sends back an INFO_PACKET;
    • Checks to see if it is other player positional data and calls netDataPosPacket();
    • Checks for REREQUEST_PACKET's from the server and sends back any packets requested
    • Checks for BOLOPACKET_RETRANSMITTED_PACKETS and processes them if in netFailed status.
  • Handles reliable packets
    • Checks CRC adds
    • Checks reliable packet number to ensure it is in sync still. If not follow resync process above.
    • Calls netTcpPacketArrive(); to process packet contents.


Contains data for each of the players in the game that are around us or our pillboxes. Players not around those items are not sent to reduce data size and prevent cheating. All shell data is currently sent(?) to allow the client to predict each shells path and changes to the map.

Does not contain the players own tank location. In moving to a pure server model this would be included.


Processes each of the incoming packets types and data payloads. Its still called this as WinBolo used to use a combination of UDP for nonreliable packets and TCP for reliable data. It no longer does as of version 1.09 .

Sending Data

  • netMakeDataPosPacket(); - Sends the tanks location, speed and direction to the server. (Used to also send LGM data before it was moved to the server) This is sent as an [[#Packet_Types| unreliable packet]. Also sends current time in the packet to stop the server from processing older tank location data. In a pure server model, you would probably only want to send what keys are being pressed.
  • netMakeTokenPacket(); - Sends the token packet (or what used to be under the very early ring implementation) This contains all the data from the client that must be received by the server. This includes shells that have been fired, mines that have been laid, builder actions etc. Sent as a [[#Packet_Types| reliable packet]. Uses the netpnb/netmt event queues to get the list of events that have occurred to sent.

Server Functions

TODO: Very similar to the client just reverse :)

Helper Modules


Netplayer.c is an extension of players.c but stores each players network details and udppackets reference. The typedef defines what is in it:

typedef struct { /* Obj */
  bool inUse[MAX_TANKS];                /* Is this in use */
  bool inGame[MAX_TANKS];               /* Have they entered the game or are they still joining? */
  struct sockaddr_in addr[MAX_TANKS];   /* Last packet from address */
  bool locked[MAX_TANKS];               /* Are they locked */
  bool passed[MAX_TANKS];               /* Have they entered the password (if required by the server) */
  udpPackets udpp[MAX_TANKS];           /* udppackets ADT reference */
  time_t lastHeard[MAX_TANKS];          /* When we last heard from them */
  time_t lastServerTime[MAX_TANKS];
  time_t lastClientTime[MAX_TANKS];
  BYTE cheatCount[MAX_TANKS];
} netPlayers;

netmt.c and netpnb.c

These two ADT's are used as an intirim place to store data that occurs during a tick that needs to be sent across the network. They provide functions to add events to send, make the packet data to send and then extract the packet data and apply to the game world. The event types are defined in netmt.h and netpnb.h

void netMNTAdd(netMntContext *nmtc, BYTE event, BYTE itemNum, BYTE owner, BYTE opt1, BYTE opt2);
int netMNTMake(netMntContext *nmtc, BYTE *buff);
bool netMNTExtractServer(netMntContext *nmtc, map *mp, pillboxes *pb, bases *bs, BYTE event, BYTE itemNum, BYTE owner, BYTE opt1, BYTE opt2);
bool netMNTExtractClient(netMntContext *nmtc, map *mp, pillboxes *pb, bases *bs, tank *tnk, BYTE event, BYTE itemNum, BYTE owner, BYTE opt1, BYTE opt2);


The udppackets ADT is used by both the client and the server to store copies of packets in case the network is interrupted and they need to be resent to the clients (or server) The udppackets.c header comment explains how it works best (below) It allows for saving up to 200 packets.

*Name:          UDP Packets
*Filename:      udppackets.h
*Author:        John Morrison
*Creation Date: 24/02/02
*Last Modified: 24/02/02
* Handles keeping track of network packets for
* retransmission on errors
*  Process: Client to Server
* -------------------------
* Client--->
* 1. Build reliable packet and give it a sequence number
* 2. Send
* 3. Copy packet to sequence number position X of buffer.
* 4. Increment next packet sequence number.
* Server--->
* 5. Server Receives packet
* 6. Server checks sequence number. If valid increments to
*    next available sequence number and process packet.
* 7. If invalid sends back a packet request for missing
*    sequence number(s) and stores packets at sequence
*    position for processing.
* Process: Server to Client
* -------------------------
* Server-->
* 1. Build reliable packet.
* 2. For each client makes the correct sequence number
*    and sends it.
* 3. Copy packet to sequence number position x of buffer
* 4. Increment next packet sequence number
* Client--->
* 5. Client Receives packet
* 6. Client checks sequence number. If valid increments
*    to next available sequence number.
* 7. If invalid sends back a packet request for missing
*    sequence number(s) and stores packets at sequence
*    position for processing.
Personal tools