Google

CONTENT

  1. Content
  2. Files
    1. Banner.dat
    2. Avi Files
    3. Sprites.dat
    4. Room.dat
    5. Dynamic.dat
    6. Configure file
    7. Necessary Graphics Files
    8. Heroes And Corpses
  3. Data
    1. Colors
    2. Fixpoints
    3. Constants
    4. Weapons
    5. Sprites
    6. Static Level Map
  4. Objects
    1. Types
    2. Status
    3. Object Data
    4. Respawning
    5. Object Collisions
  5. Networking
    1. General Description
    2. Server
    3. Client
    4. Motion Prediction
    5. Packets
    6. Data Storage Conventions
    7. Version Compatibility Check
    8. Chunked Packets
    9. Packet Overview
  6. IO
    1. Keyboard
    2. Console
    3. X Support
    4. Screenbuffer

FILES

Banner.dat

Content of this file is cyclicaly scrolled at the bottom of initial screen. This file contains lines of ascii text. Each line is scrolled over the screen and when last letter of the line disappears at the left, first letter of a new line appears at the right. So if you want uninterrupted stream of text, you must write it into one line. Vice versa if you want text with intervals write it into several lines.

Avi Files

AVI is a shortcut of "ascii video". These files contain animated graphics in human readable form.

Avi files are divided into lines. Each line begins with initial character and ends with linefeed, empty lines are ignored. Initial characters are: p, a, l, s, #. File contains consecutive animation positions. Last line is list of positions.

# is a comment. Lines beginning with hash are ignored.

p is offset of current animation position. This line initiates each position. p letter is followed by comma separated pair of numbers. First number is horizontal offset, second is vertical offset. Number can be both positive and negative. Positive means move image right/down, negative move image left/up.

l is textual part of image scanline. This line contains ascii characters. It must be followed with attribute line. Number of image lines is not restricted.

a is attribute part of image scanline. Line can contain digits 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, small letters a, b, c, d, e, f and spaces. Digits 1-9 and letters a-f are colors, space and zero are transparency. Pixel is transparent apart from it's textual value (if it has transparent attribute).

Colors:
numbername
0transparent
1red
2green
3brown
4blue
5violet
6cyan
7light gray
8dark gray
9light red
alight green
byellow
clight blue
dmagenta
elight cyan
fwhite

s list of animation positions. Positions are numbered beginning with zero and comma separated. All animations are cyclical. So if you want have animation with positions 5, 3, 1, 7, 2, 5, 3, 5, 0, 6, 4 you simply write s5,3,1,7,2,5,3,5,0,6,4. Of course there can be only numbers of positions previously defined.

Typical avi file contains p-line followed with several couples l-line, a-line. Then p-line, several couples l-line, a-line ... And last line is s-line.

Sprites.dat

This is a list of sprites used in the game. There is name, whitespace and then filename (or path) on each line of the file. Names can't contain spaces. Sprites are used in dynamic.dat and room.dat. In these files sprites are refered to with their names.

Room.dat

This file contains description of playing area. It's divided into lines. Each line means one object. Line has several parts separated with whitespace.

Line begins with name of image, name must be defined in sprites.dat file. Then follows (after whitespace) one letter meaning type. Then there are two numbers (whitespace separated) meaning x and y coordinates of upper left corner of the object. Coordinates can be positive or zero.

Type letters
LETTERNAMEDESCRIPTION
wwallhero stops at it
bbackgroundhero can go freely through
fforegroundsame as background, but hero is behind
jjumpyou can go through and stand on it
(like in Jet Set Willy on ZX Spectrum)
ijump-foresame as jump, but hero is behind

Defined type is applied only on non-transparent pixels of a sprite. Transparent parts of sprite have no type - they don't restrict player's motion.

Sprites can overlap. Later sprite (in file) does over older one. The older one is visible through transparent pixels of the later one.

Dynamic.dat

Structure of this file is same as structure of room.dat file. This file defines dynamic objects as medikits, ammo, guns and all animating objects.

One and only difference against room.dat file are object types.

Object types in room.dat file
LETTERNAMEDESCRIPTION
1ammo nr. 1Browning cartridges
2ammo nr. 2shotgun shells
3ammo nr. 3UZI cartridges
4ammo nr. 4sniper rifle cartridges
5ammo nr. 5grenades
Nnothingabsolutely indiferent type, only animates
UUZIUZI submachine gun
Rriflesniper rifle
Sshotgunshotgun
Mmedikitmedikit, nothing more nothing less
Aarmorarmor, what else?
Iinvisibilitymakes player invisible for a time

All weapons are supplied with some basic ammo. There aren't any differences among medikits (all heal the same) and among ammo (every shotgun ammo adds the same, every grenade ammo adds the same, ...) though they can look different.

To alias new letter (e.g. X) with object type (e.g. T_X) edit data.c file, _convert_type() function. This function contains switch of letters so add a new line case 'X': return T_X;

Configure File

When you run 0verkill client for the first time, .0verkill file appears in your home directory. This file contains address of the server, your color and name. Everytime you run 0verkill, these values will be taken as default.

Configure file is readable for humans. It's each item is on separate line. There's server address on first line, second line contains player's name and on third line nuber of player's color is. It's number from 1 to 30.

Necessary Graphics Files

There are several graphics files necessary to run the game (like heroes, corpses, bullets and so on). They must appear in sprites.dat file. Here's list of their names:

  • title - 0verkill caption on initial screen
  • bulge - goggling skull on initial screen
  • hit - blood splash when hero is hit
  • bullet
  • slug - shotgun slug
  • shell
  • sshell - shotgun shell
  • grenade
  • mess1 - oily patch left on ground from player
  • mess2...mess4 - various bits and pieces of player
  • shrapnel0...shrapnel9 - various grenade shrapnels
  • hero1...hero15 - male hero (colors 1-15)
  • hero16...hero30 - female hero (colors 1-15)
  • corpse1...corpse15 - male corpse (colors 1-15)
  • corpse16...corpse30 - female corpse (colors 1-15)

Heroes And Corpses

Avi files containing hero's animations have own special structures. They stand avi file structure but each animation position (as written on the s line) have own meaning. Here it is:

Hero avi file
positiondescription
0standing, looking to the left
1-8walking to the left
9looking face toward me
10standing, looking to the right
11-18walking to the right
19looking back toward me (not used)
20standing, aiming to the left
21-28walking and aiming to the left
29standing and shooting to the left
30-37walking and shooting to the left
38standing, aiming to the right
39-46walking and aiming to the right
47standing and shooting to the right
48-55walking and shooting to the right
56-63creeping to the left
64-71creeping to the right
72creeping toward me
73looking to the left, locking grenade off
74-75throwing grenade to the left
76looking to the right, locking grenade off
77-78throwing grenade to the right

Heros are in male and female version, each in 15 different colors. Not to have to edit all 30 files when something changes there are only two files hero_univ.avi and girl_univ.avi containing heroes, but instead of color which changes there are letters G (this letter isn't in any pixel part). The same is with corpses (files corpse_univ.avi and corpse_girl_univ.avi). And there's a shell script make_hero generating all 30 files. Script simply substitues G letter with digits 1...F.

DATA

Fixpoints

Coordinates and velocities of all objects in the game are stored in fixpoint numbers. Floats are inappropriate because two float numbers can't be compared, fixpoint arithmetics is faster than float arithmetics and such accuracy is idle.

Fixpoints and all necessary fixpoint arithmetics functions are defined in file math.h. Fixpoints are stored in 32-bit integers. Original number is multiplied with 1024 and then stored in integer. To get integer from fixpoint (or vice versa) you make only one shift.

Fixpoint type is called my_double.Precision of fixpoint arithmetics is defined with PREC - it's number of bits you have to shift integer to the left to get fixpoint.

There're macros float2double, int2double to convert float (integer) number to fixpoint and double2int to convert fixpoint back to integer.

math.h file contains these fixpoint arithmetical functions (they're macros, but I'll write them as functions for better understanding):

  • my_double max(my_double,my_double) - maximum of 2 fixpoints

  • my_double min(my_double,my_double) - minimum of 2 fixpoints

  • my_double my_abs(my_double) - absolute value of fixpoint

  • int round_up(my_double) - round up fixpoint

  • my_double add_int(my_double,int) - add integer to fixpoint

  • my_double sub_int(my_double,int) - subtract integer from fixpoint

  • my_double sub_from_int(int,my_double) - subtract fixpoint from integer

  • my_double mul_int(my_double,int) - multiply fixpoint with integer

  • my_double mul(my_double,my_double) - multiply 2 fixpoints

Addition and subtraction isn't anything abnormal - it's like addition and substraction of two integers. There's no function for division because division is slow and is not used in the game.

Constants

All game constants are in file cfg.h. Now I'll describe most important of them:

  • VERSION_MAJOR and VERSION_MINOR - current program version, it's used for version compatibility checking.

  • MIN_CLIENT_VERSION_MINOR and MIN_SERVER_VERSION_MINOR - lowest client's/server's minor version number to be avoided to play the game. See Version Compatibility Check section.

  • DEFAULT_FONT_NAME - default font for X display

  • SPRITES_FILE - path to sprites.dat file

  • DATA_FILE - path to room.dat file

  • DYNAMIC_DATA_FILE - path to dynamic.dat file

  • BANNER_FILE - path to banner.dat file

  • CFG_FILE - path to 0verkill configure file in player's home directory

  • AREA_X - horizontal size of level

  • AREA_Y - vertical size of level

  • MAX_DUMB_TIME - time (in microseconds) after which are not responding client's kicked out of the game

  • FPS - server's timer frequency

  • CLIENT_FPS - client's timer frequency (frames per second)

  • PERIOD - period (in microseconds) to FPS

  • CLIENT_PERIOD - period (in microseconds) to CLIENT_FPS

  • MENU_PERIOD - refresh period of initial screen

  • PLAYER_WIDTH and PLAYER_HEIGHT - dimensions of player

  • CREEP_WIDTH and CREEP_HEIGHT - dimensions of creeping player

  • CORPSE_WIDTH and CORPSE_HEIGHT - dimensions of player's corpse

  • CREEP_YOFFSET - vertical offset of creeping player to standing position (in my_double)

  • WALK_ACCEL - player's horizontal acceleration

  • MAX_SPEED_WALK_FAST - maximal horizontal speed of running player

  • MAX_SPEED_CREEP - maximal horizontal speed of creeping player

  • FALL_ACCEL - vertical acceleration of falling objects

  • MAX_X_SPEED - maximal horizontal speed of player

  • MAX_Y_SPEED - maximal vertical speed of objects

  • PLAYER_SLOW_DOWN_X - player's horizontal slow down when walking on ground

  • MIN_X_SPEED - objects stop when their horizontal velocity is lower than this

  • MIN_Y_SPEED - similar to previous

  • SPEED_JUMP - initial vertical speed of jumping player

  • OVERKILL - when player's damage is less than minus this value player melts down

  • HOLD_GUN_AFTER_SHOOT - how long player holds gun after shot (higher number=hold longer), this constant is added to hero's time to live during shooting

  • FIRE_YOFFSET - vertical distance between player's gun (where bullets goes out from) and player's upper left corner

  • GRENADE_FIRE_YOFFSET - vertical distance between created grenade and player's upper left corner

  • FIRE_IMPACT - acceleration shot gives to shootin' player and bullet gives to victim

  • SHRAPNEL_IMPACT - acceleration shrapnel gives to victim

  • GRENADE_DELAY - time between grenade locking off and grenade throwing

  • MEDIKIT_HEALTH_ADD - number of health points medikit adds

  • MAX_AMMO - maximal ammo for each weapon

Weapons

Weapons in the game have lot of properties. For example name, cadence, basic ammo, rebound, ... Weapon attributes are defined in weapon table in file data.c (definition of this type is in data.h). Weapon is definite determined with it's number (weapon type) - so table is indexed with weapon type. To change weapon properties simply increase ARMS constant and add new line to the table - nothing else is needed.

Weapon properties
nametypedescription
namestringweapon name, it's displayed in status bar
cadenceunsigned charlower number=higher cadence
ttl16-bit intlength of bullet's live
speedfixpointhorizontal speed of bullet
impactfixpointacceleration bullet gives to shooting player and to victim
lethalnessunsigned charnumber of health points bullet recovers from victim
armor_damageunsigned charnumber of armor points bullet takes away from victim
basic_ammounsigned charammo player gets when takes the weapon
add_ammounsigned charammo player gets taking ammo box for this weapon type
max_ammounsigned charmaximal ammo player can have for this weapon
shell_xspeedfixpointhorizontal speed of shell flying away
shell_yspeedfixpointvertical speed of shell flying away

When player fires server creates new object "bullet" and object "shell". Bullet has vertical velocity zero, horizontal velocity is speed. Time to live gives striking distance. Bullet's sprite is stored in bullet_sprite variable, shell's sprite is in shell_sprite (both are global variables in server.c source, they're initialized during start of the game).

Server sets shooting flag (bit 4 in hero's status) at hero's object and hero's time to live is set to weapon cadence. When shooting flag is up player can't shoot again. When hero's time to live decreases to zero shooting flag is shut down. When client gets update of a hero with wielding gun flag (bit 5 in status) he sets time to live to weapon cadence. Animation of shooting player is displayed only when hero's time to live==weapon cadence. It's here not to display shooting player everytime client gets status update.

Shotgun shooting is a bit different. Server creates shell too but it's sprite is in shotgun_shell_sprite and creates not one bullet but six slugs (slug_sprite). Their vertical velocities are not zero - they're given in sources. Thus every shotgun shot creates the same slugs with the same velocities.

Grenade throwing is absolute different because grenades have timeout and grenade isn't created when fire is pressed because hero must lock it off first. So when player fires his hero's time to live is set to grenade cadence and bit 9 in his hero's status is set to 1. When ttl is lower than GRENADE_DELAY server creates grenade object with initial velocity grenade shell_xspeed and shell_yspeed and initial time to live. Then when grenade's ttl lowers to zero grenade explodes: server deletes grenade and creates shrapnels instead.

When player is hit his health is lowered by lethalness of the weapon. Damage decreases (linearly) with bullet/slug/shrapnel time to live. It also depends on place on hero's body where bullet crashes into. It's linear interpolated too - head hit damages twice as legs hit.

If player's health lowers below zero player is dead. If healt lowers below minus OVERKILL player melts down (create_mess function is called), otherwise corpse is created (create_corpse function).

To find out whose bullet or slug or shrapnel killed a player object bullet/slug/shrapnel contains owner's hero object number in data item. It could contain a pointer to player but player could shoot and immediately leave the game (player will be removed from server's data structures) and bullet could kill someone and consequently server would crash on sigsegv because the pointer would be invalid.

When grenade shrapnel hits corpse or mess (object with type T_CORPSE_TYPE) blood gushes and raw meat flies - function create_mess is called and original object (corpse) is deleted. It offers great fun!

Not to load network so much server doesn't send bullet, shells, slugs and shrapnels (objects with type T_SHELL, T_BULLET or T_SHRAPNEL) updates. Clients compute them theirselves. It's because bullets and shells appear in the game very often and their updates would load network hard. It may be a bit unaccurate (clients could see reality a bit different) but it's worth network discharge.

Sprites

In this section I'll describe how sprites are stored in memory. Data structures I'm gonna describe can be found in sprite.h file.

First I'll describe data types. Sprite=animation, animation is set of (one or more) ascii-art pictures with given order, picture is set of scan-lines (not explicit with same length), each line is set of pixels, pixel have textual part and attribute part. This is a philosophy of sprites storage in memory.

There are three structures for each described part of sprite: sprite (struct sprite), animation position (struct pos) and scan-line (struct line).

  • struct sprite - contains array of positions, number of members of the array, order of positions - array of indexes into position array and number of members of last
  • struct pos - contains offset (horizontal and vertical) of the image, number of lines and array of lines
  • struct line - contains length of line (number of pixels), array of pixels and array of attributes (both the same length)

For better understanding see sprite.h file.

All sprites are stored in sprites variable - field of struct sprite. Sprite names are stored in sprite_names variable (field of strings). Sprites are searched through their names using find_sprite() function. (All this is described in data.c and data.h files.)

Avi files are loaded from sprites.dat using load_sprite() function. This function makes everything needed: fills sprites variable and sprite_names variable too.

Game expects both client and server have the same sprites.dat file. This is very important presumption - files shall not be changed. At this time there's no check that files are same. Because both client and server use the same function for loading sprites sprites are stored with the same order into memory. So to determine sprite over the network sprite number (index in sprites array) is enough.

Static Level Map

Game is compact of static map and dynamic objects. Static map is a rectangle of pixels filled with walls, empty space, background, ... . Level dimensions are AREA_X and AREA_Y. Description of static map is in room.dat file. Each pixel of the level contains textual information (letter of the pixel) and attribute. Map is stored in area (textual data) and area_a (attributes) variables. Both are one dimensional arrays of chars. Lower 4 bits of attribute are color of the pixel, higher 4 bits are pixel type. Type can be:

  • TYPE_BACKGROUND - it's behind objects
  • TYPE_FOREGROUND - it's in front of objects
  • TYPE_WALL - objects stop at it
  • TYPE_JUMP - player can go through but can stand on it
  • TYPE_JUMP_FOREGROUND - same as TYPE_JUMP, but it's in front of player

These types are defined in data.h file.

OBJECTS

Objects is everything that moves, animates or can be picked up. Game without static map are objects. The only thing that happens in the game is object moving, updating, creating and deleting. Nothing more. Heroes are objects, shooting is new objects creating, ...

Object have own unique ID, it's 24 bits long, to flow over ID there would have to be over 16 millions of objects - it would be really heavy game ;-).

Object is represented in struct it structure (data definition is in data.h file), objects are stored in bidirectional list (struct object_list). This data type was chosen because we want to create and delete objects and we also want to have access to each object. Because searching objects through their IDs is necessary objects are hashed (hash.h, hash.c sources) by their IDs. Hash table contains pointers to the list. Hash table 32k entries - table ss about 120k large.

struct it
ItemTypeDescription
xfixpointhorizontal coordinate
yfixpointvertical coordinate
xspeedfixpointhorizontal velocity
yspeedfixpointvertical velocity
typeunsigned charobject type
id24 bit intunique ID
ttl16 bit inttime to live - some kind of counter
when non zero it's decreased in every game tick
when reaches zero some kind of action happens (in update_game function)
sprite16 bit intsprite number (index in array of sprites)
anim_pos16 bit unsigned intcurrent animation position
status16 bit intobject status
last_updatedunsigned long longtime of last update
client uses this to determine if object update (from server) isn't old
server uses at with respawning objects
datavoid *aditional data

There are functions for creating and deleting objects: new_obj() and delete_obj(). They add/remove object to/from object list and hash table. new_object functino also initializes object's data with given values. There's function find_in_table() to find object with given ID, this function returns poniter to the list of all objects.

Object Types

Object type gives information about what the object is, e.g medikit, hero, grenade, shotgun, corpse, ... Object types differ in attributes. Attributes are stored in obj_attr table (in data.c, type is defined in data.h), they say for example if it falls, how much it bounces and so on.

Here is list of object types:

  • T_PLAYER - hero
  • T_BULLET
  • T_CORPSE
  • T_MEDIKIT
  • T_SHOTGUN
  • T_UZI
  • T_RIFLE
  • T_SHELL
  • T_AMMO_GUN
  • T_AMMO_SHOTGUN
  • T_AMMO_UZI
  • T_AMMO_RIFLE
  • T_NOTHING - this type is indifferent to all and is here only to animate (this type are e.g. burning barrels)
  • T_MESS - blood and guts from melt down player
  • T_GRENADE
  • T_AMMO_GRENADE
  • T_SHRAPNEL - grenade shrapnel
  • T_ARMOR
  • T_INVISIBILITY - invisibility dope
  • T_NOISE - noise when player's respawning
  • T_NOTHING_FORE - as T_NOTHING but in foreground

Similar to weapon attributes object attributes table is indexed through object type too.

Object attributes:
NameTypeDescription
fallunsigned charpossible values: 0 or 1
says if object can fall or not
bounce_xfixpointhorizontal bounce slow down
speed is multiplied with this constant
bounce_yfixpointvertical bounce slow down
slow_down_xfixpointhorizontal slow down when not falling
maintainerunsigned charwho updates the object
bit 0 = client updates
bit 1 = server updates
bit 2 = server sends updates to clients
foregroundunsigned charpossible values: 0 or 1
defines if the object is in foreground

To add new object type increase N_TYPES constant, add new type constant with number N_TYPES-1 (both in data.h file) and add new line describing object's attributes into obj_attr table (in data.c file). Now you can use new object type in the game.

Object Status

Object status:
BitDescription
0player walks
1,2player is looking
01=left
10=right
00=center
(left digit is bit 1)
3object falls
4player's shooting
5player's wielding gun
6object is hidden
7player is hit (blood gushes)
8player's creeping
9player's throwing grenade
10player is dead
11player falls down ladders etc.
12player's just respawning

Only heroes use all status bits, other objects use only bits 3 and 6. Bullets (object with type T_BULLET) use status for absolute different purpose. Server stores type of weapon in the status. It's a little cheat because bullets don't fall and can't be hidden thus status would be unused. And bullet must remember type of gun it was shot off.

Object Data

data item in struct it is used in several ways:

  • client, hero object - when player was hit it contains information about where to draw blood splash, otherwise contains zero
    bits 0-7 are horizontal offset
    bits 8-15 are vertical offset
    bits 16-23 is direction (0=left,1=right)
    It won't work on platforms with 16-bit void* !

  • server, hero object - pointer to player structure of the hero owner (struct player*)

  • server, bullet, shrapnel, grenade - ID of owner of the bullet/grenade/shrapnel
    when grenade explodes, owner of grenade moves to shrapnels
    If there was pointer to owner and owner leaved it would cause crash.

Respawning

Some objects (ammo, weapons, medikits, ...) a while after picking up appear again in the game. It's called respawning.

Server have time queue (time_queue variable) - time queue is a bidirectional list of objects waiting for respawning. Objects in queue have respawning time, it's stored in last_updated variable. Respawning time is time in microseconds since 1970 (at server's machine) when object's gonna appear in the game. List is sorted by respawn time (object with lowest time comes first) - that's why it's called queue.

When the object is picked up, it's not deleted, but moved to time queue (add_to_timeq() function). Queue is updated every game tick calling update_timeq() function. This function removes from queue and adds to the game objects with time older than current time.

When server moves object to the queue it sets flag hidden to the object and sends status update to all clients. So clients have the object in the game but it's invisible. When object gets back into the game hidden flag is switched off. When new player enters the game server sends all objects in the queue to the client.

Object Collisions

Server computes collisions among objects (dynamic_collision() function in server.c file). Corpses, grenades shells doesn't collide. Weapons, ammo, armor and medikits collide with players. Bullets collide with players, shrapnels collide with players and corpses.

NETWORKING

General Description

0verkill is a client-server game. In the game there's one server and unlimited number of clients. They communicate through UDP sockets. Server runs on an IP address and socket that are well known by the others.

Clients initiate connection with server. Each client has it's IP address and port. They are not so essential because server gets it with each packet. They don't have to be known by players.

Server and clients are numbered with IDs. Server's ID is always 0. Client's are numbered from 1. ID numbers are sent with packets too - to correctly determine sender and recipient.

Both server and client have their clock, game is updated (recomputed) every tick.

Server

Server has entire information about the game. State of game on the server is one and only true. Clients can have a bit different informations (due to network inconsistency), but the server has absolute truth.

When everything's OK and server gets client's request he creates a new player, new hero object and sends all objects to the client. Now client's in the game and can control his hero.

As I've said server has own clock. In every tick server reads data on input - requests from clients (read_data() function), updates players (update_players() function )and updates objects in the game (update_game function).

Server has in each client's record time when last keyboard update came. Clients should send keyboard update every tick. But when client is more than 30 seconds dumb he's kicked out of the game.

This kickout wasn't implemented in earlier server's versions. So when client wanted to enter the game he sent request - it came to the server, server sent client "OK you're in the game" and created a new player. But due to a network error client didn't get it and though he he wasn't accepted and there was a dumb player in the game.

Client

The only person client's communicating with is server. If there's a packet not only for server but for other players too, client sends it to server and server resends it farther. So networking topology is star - server's in the center.

As it was said clients have own ID. At the beginning when clients wants to enter the game he doesn't have any. So server sends information about player's acceptance to client's address but puts zero as recipient's ID to the packet and client expects recipient's ID to be zero. Then client reads his ID from the packet and hence expects all packet to have recipient's ID his ID.

Client in every tick reads data from the socket - game updates from the server, chat messages from clients ... (read_data() function), updates game - recomputes objects (update_game() function), draws view on the screen (draw_scene()), reads keyboard and sends keyboard status to the server (send_keyboard()).

Earlier client versions were sending keyboard update only when something changed. It loaded network a little bit less but on slow lines (e.g. 28.8k modem) motion was jerky. The line wasn't able to transmit all the packets so when keyboard update didn't come server though client didn't press any key so he didn't move the player.

You may think sending keyboard status even if nothing's pressed loads network too much, but during game you almost always hold any key. So it isn't so dramatic.

Motion Prediction

Clients have motion prediction to achieve smoother game. In every tick client updates his objects himself. Client knows velocities - he can recompute positions, he also knows which object slows down with friction - he can update velocities. Clients don't compute object collisions, it's server's job.

When client gets update of an object (packet contains object ID, current position, velocity, time, status and ttl) he copies velocity, status and ttl. And sets object's position to position in packet plus velocity multiplied with time difference among time in packet and current client's game time.

Packets

Packet contain not only data but also sender's and recipient's ID and CRC check.

Packet structure:
OFFSET
(bytes)
LENGTH
(bytes)
CONTENT
04CRC check
44sender's ID
84recipient's ID
12nown data

CRC is 32-bit CRC checksum of own data (not sender's or recipient's ID !) as described in ISO 3309 standard.

There are two routines in file net.c for packet transmitting: send_packet() and recv_packet(). These functions are wraparound of sendto() and recvfrom() functions. They add crc check and sender's and recipient's ID to data and transmit it through network. They behave as there wasn't any additional information with data. recv_packet() returns number of received data bytes (not crc nor IDs) on success.

Data Storage Conventions

In this section I'll describe conventions used to store some data types in packets. I'll use following data types: byte, int, fixpoint, time and string.

  • Byte: is always one 8 bits long. There's no problem with storing byte into packet.
  • Int: Can be 16 or 32 bits long, it can be both signed or unsigned. Apart from sign first byte of int comes always first in a packet, then second, third and fourth (it comes last). There are functions put_int() (put_int16()) and get_int() (get_int16()) for simple manipulation with ints.
  • Fixpoint: is number in fix-point arithmetics. 0verkill uses fixpoints for inner data representation too (type is called my_double). Fixpoints are stored in 32 bit integers, thus conventions arethe same as of integers. There are functions get_float() and put_float() for easier manipulation.
  • Time: This type stores time in the game - in microseconds since 1970. It's unsigned long long. It's stored alike int. First byte first, eight byte last. There are functions get_long_long() and put_long_long() for manipulation.
  • String: is sequence of bytes. It's always terminated with null character.

Data in packet always start with one byte head. Head informs about type of following data. Here is overview of packets. Packet heads are defined in file net.h

Version Compatibility Check

Since version 0.12 0verkill contains improved version compatibility check. It guarantees that client and server with improper version numbers can't play the game.

Client sends his major and minor version number to server in the P_NEW_PLAYER packet. Server checks its version with client's version and accepts or denies the client.

If server accepts the client server sends his version number in the P_PLAYER_ACCEPTED packet. Now client checks versions and continues or sends quit to the server.

Now how check works. It is the same for both client and server only in one case server is denied and in second case client's denied, so I won't describe it extra for server and extra for client. I'll use "check fails" phrase - it means this combination of client and server won't work together.

  1. When version number is not contained in the packet (old version not supporting the check) check fails.

  2. If major versions differ check fails.

  3. Now minor version number check. File cfg.h contains constants MIN_CLIENT_VERSION_MINOR and MIN_SERVER_VERSION_MINOR. First constant defines lowest minor version number server accepts. The second is lowest server minor version client is willing to connect to.

Chunked Packets

To decrease network load, server sends most frequent packets in chunks. Packet created in one server's tick are stored in server's memory (separately for each client). When total size of packets exceeds MAX_PACKET_SIZE or when tick ends server sends a chunk packet (with head P_CHUNK) containing all the data from the buffer to the client.

This mechanism doesn't work at clients.

Chunked packets are:

  • object updates (including status and other updates)
  • message
  • player update
  • grenade explosion packet
  • player hit packet

And these packets aren't chunked:

  • info
  • end of the game
  • new player packet
  • delete player packet

Packet Overview:
HEAD SENDER DESCRIPTION OFFSET
(bytes)
LENGTH
(bytes)
TYPE DESCRIPTION
P_NEW_PLAYER client New player wants to enter the game 1 1 byte 0; to recognize old clients, they had color here and it was never 0
2 1 byte client's major version number
3 1 byte client's minor version number
4 1 byte player's color (number from 1 to 30)
5 n string player's name
P_CHANGE_LEVEL server Server is changing the level 1 4 int level number (line number in the level.dat file - counted from 0)
5 33 string level MD5 checksum in hexadecimal notation
P_LEVEL_ACCEPTED client Client changes to this level 1 4 int level number (line number in the level.dat file - counted from 0)
P_PLAYER_ACCEPTED server Player was accepted 1 4 int hero object ID
5 2 int hero sprite number
7 4 fixpoint x coordinate
11 4 fixpoint y coordinate
15 4 fixpoint horizontal speed
19 4 fixpoint vertical speed
23 2 int status
25 8 time game start time
33 4 int player's ID
37 1 byte server major version number (since version 0.12)
38 1 byte server minor version number (since version 0.12)
P_PLAYER_REFUSED server Player was refused. 1 1 byte error:
E_PLAYER_REFUSED or
E_INCOMPATIBLE_VERSION)
P_END server Server's shutting down, client's should end. 1 n string Name of player who ended the game
client Player's ending the game.
P_NEW_OBJ server New object 1 4 int object ID
5 2 int sprite number
7 4 fixpoint x coordinate
11 4 fixpoint y coordinate
15 4 fixpoint horizontal speed
19 4 fixpoint vertical speed
23 2 int status
25 1 byte type
26 2 int time to live
P_UPDATE_STATUS server Update status of an object 1 4 int object ID
5 2 int object status
P_UPDATE_OBJECT server Object update 1 4 int object ID
5 1 int 8-bit counter, increased with each update
6 4 fixpoint x coordinate
10 4 fixpoint y coordinate
14 4 fixpoint horizontal speed
18 4 fixpoint vertical speed
22 2 int status
24 2 int time to live
P_UPDATE_OBJECT_POS server Update of position and speed of an object 1 4 int object ID
5 1 int 8-bit counter, increased with each update
6 4 fixpoint x coordinate
10 4 fixpoint y coordinate
14 4 fixpoint horizontal speed
18 4 fixpoint vertical speed
P_UPDATE_OBJECT_SPEED server Speed update of an object 1 4 int object ID
5 1 int 8-bit counter, increased with each update
6 4 fixpoint horizontal speed
10 4 fixpoint vertical speed
P_UPDATE_OBJECT_COORDS server Position update of an object 1 4 int object ID
5 1 int 8-bit counter, increased with each update
6 4 fixpoint x coordinate
10 4 fixpoint y coordinate
P_UPDATE_OBJECT_SPEED_STATUS server Speed and status update of an object 1 4 int object ID
5 1 int 8-bit counter, increased with each update
6 4 fixpoint horizontal speed
10 4 fixpoint vertical speed
14 2 int status
P_UPDATE_OBJECT_COORDS_STATUS server Position and status update of an object 1 4 int object ID
5 1 int 8-bit counter, increased with each update
6 4 fixpoint x coordinate
10 4 fixpoint y coordinate
14 2 int status
P_UPDATE_OBJECT_SPEED_STATUS_TTL server Speed, status and ttl update of an object 1 4 int object ID
5 1 int 8-bit counter, increased with each update
6 4 fixpoint horizontal speed
10 4 fixpoint vertical speed
14 2 int status
16 2 int time to live
P_UPDATE_OBJECT_COORDS_STATUS_TTL server Position, status and ttl update of an object 1 4 int object ID
5 1 int 8-bit counter, increased with each update
6 4 fixpoint x coordinate
10 4 fixpoint y coordinate
14 2 int status
16 2 int time to live
P_QUIT_REQUEST client Client's leaving the game
P_DELETE_OBJECT server This object's no longer in the game 1 4 int object ID
P_PLAYER_DELETED server Answer to P_QUIT_REQUEST
player was deleted, client can end
P_MESSAGE server Chat message or message from server 1 n string message
client Chat message from client. Server adds player's name and broadcasts to all players 1 n string message
P_UPDATE_PLAYER server Player update 1 1 byte health
2 1 byte armor
3 2 int gun ammo
5 2 int shotgun ammo
7 2 int UZI ammo
9 2 int rifle ammo
11 2 int grenades
13 4 int frags
17 4 int deaths
21 1 byte number of current weapon
22 1 byte player's weapons
0.bit=gun, 1.bit=shotgun, 2.bit=uzi, 3.bit=rifle, 4.bit=grenades
P_EXPLODE_GRENADE server Grenade is exploding (client should create shrapnels) 1 4 int ID of first shrapnel, other shrapnels have increasing ID
5 4 int grenade ID (gives coordinates of explosion too, grenade should be deleted)
P_HIT server Player was hit; clients should draw blood splash 1 4 int object ID
5 1 byte direction: 0=left, 1=right
6 1 byte horizontal offset (in pixels)
7 1 byte vertical offset (in pixels)
P_KEYBOARD client Player holds these keys pressed 1 1 byte keys: 0.bit=right, 1.bit=left, 2.bit=jump, 3.bit=creep, 4.bit=speed up, 5.bit=fire, 6.bit=climb down ladders
2 1 byte change weapon key: 0=none or 1-5
P_INFO server Info on players 1 4 int number of active players
5 1 byte number of players in top list
6 4 int frags of player #1
10 4 int deaths of player #1
14 1 byte color of player #1 (number from 1 to 30)
15 n string name of player #1
15+n 4 int frags of player #2
19+n 4 int deaths of player #2
23+n 1 byte color of player #2 (number from 1 to 30)
24+n m string name of player #2
and so on...
client,
someone else
Send me how many players are in the game
P_REENTER_GAME client Client wants to be born again. This packet is sent when player presses space
P_BELL server Client should ring the bell (sent e.g. when new player enters the game)
P_CHUNK server Several packed chunked into one 1 n byte content of packets (stored packet by packet, byte by byte)

IO

0verkill has own routines for controlling screen and keyboard. They can also be used as stand-alone library. Output routines are in console.c, console.h files. Keyboard controlling routines in kbd.c and kbd.h files.

If you want to use it stand-alone, include console.h file in your source.

Keyboard

Keyboard has two modes. Standard and raw mode. In standard mode keyboard sends periodically information about key currently pressed. That has one disadvantage - you can't press more than one at once. During game like 0verkill it's quite essential disadvantage (you often need e.g. walk and shoot together).

Keyboard in raw mode sends information which key was pressed and which was released. But not all keyboads can be switched into this mode (when you're playing over telnet or in X). So I implemented both keyboards and if it's possible raw keyboard is used otherwise standard is used.

If you want to use keyboard you must initialize it first. To initialize keyboard function kbd_init() is called. This function tries to switch keyboard to raw mode or it it isn't possible it leaves it in standard mode. Global variable keyboard_type contains information about keyboard mode. It can be KBD_RAW or KBD_STD. Each function contains switch of keyboard_type and for each value one branch.

To shut down keyboard call kbd_close() - it returns keyboard into original mode.

Interface and data storage is raw mode orientated. There are two key tables one for current keyboard state (keyboard) and one for previous one (old_keyboard). Each table cell says if appropriate key is pressed or not.

Keyboard must be updated calling kbd_update() function. This function copies keyboard table into old table, reads keys from input and fills key table. It also handles virtual terminal switching and other events like ctrl-c pressing.


!!! WHEN KBD_UPDATE ISN'T CALLED YOU CAN'T SWITCH VIRTUAL TERMINAL!!!
!!! NOR BREAK THE PROGRAM !!!

Function kbd_wait_for_key waits until any key is pressed.

There are two functions kbd_is_pressed() and kbd_was_pressed() for testing which key is actually pressed or which was pressed and not released since. Both functions have key as an argument. Key can be either character constant for a letter or a number or several other characters or a constant from kbd.h. They return 1 if key was pressed and 0 otherwise.

Standard keyboard has the same interface but functions are more simple and it doesn't handle virtual terminal switch nor break.

Console

File console.c contains all necessary functions to handle console. Console is driven with ANSI terminal sequencies. Console is accessed through standard output. Output is buffered with functions fputc(), fprintf(), fwrite(). There is also my own buffering in the code, but it's commented out. It uses own buffer for output and writes to stdout using function write(). But built in buffering seems to be more effective.

This driver includes keyboard driver (which was described before) too. It wraps its functions into own functions.

To initialize console driver call c_init() function. It automatically initializes keyboard too. To shut it down call c_shutdown() function.

Because all functions for writing to console (cleaning, setting colors, writing text, ...) are buffered you must call c_refresh() function to changes take effect.

I'm gonna speak about coordinates on screen. Coordinates start in upper left corner of the screen, x coordinate goes to right, y coordinate goes down - both in positive direction. Upper left corner of the screen has coordinates [0,0].

I'm going to speak about colors too. 0verkill uses only foreground color and background color is always black. It's due to author's obsession that other background color will be ugly (or is it laziness? ;-) ) There are several functions for setting color - for setting separate highlight, separate color and to set both. It's speed optimalization - to write minimum of characters to stdout, because kernel has very sloooooooooooooow console driver.

And now description of functions:

  • void c_cls(void) - it may be unexpected, but this function clears the screen

  • void c_clear(int x1,int y1,int x2,int y2) - clears block on screen, [x1,y1] is upper left corner, [x2,y2] is lower right corner, both corners are cleared too.

  • void c_print(char *) - prints string with current color on current cursor position

  • void c_putc(char) - writes character with current color to current cursor position

  • void c_cursor(int) - sets cursor shape, argument values are C_NORMAL or C_HIDE

  • void c_goto(int x,int y) - sets cursor position to [x,y]

  • void c_bell(void) - rings the bell

  • void c_setcolor(unsigned char) - sets 4-bit color: lower three bits are color, fourth bit (not bit 4) is highlight

  • void c_setcolor_3b(unsigned char) - sets 3-bit color, without highlight, highlight is the same as was set last time

  • void c_sethlt(unsinged char) - sets separate highlight

  • void c_refresh(void) - calling this function all changes take effect

  • void c_get_size(int *x,int *y) - gets screen dimensions

As I've said before there are wrapped keyboard functions too. They have the same arguments, return the same value and do the same as functions in kbd.h. They are: c_pressed, c_was_pressed, c_wait_for_key and c_update_kbd.

X Support

Since version 0.11 0verkill has full support for X window system.

Functions for displaying under X are in xinterface.c file and functions driving X keyboard are in xkbd.c. All functions in these files use the same interface (defined in console.h and kbd.h) as console variants.

Game is displayed in single window, created in c_init() function. This function also initiates connection with the X server.

X interface has several global variables and constants which can affect layout. They are defined in x.h file. Most important are x_font_name which says name of the font used for displaying and x_display_name which determines X display.

Events comming from X server are caught in kbd_update() function. Interesting events are KeyPress, KeyRelease and ConfigureNotify and Expose. Last two ones raise SIGWINCH signal. Consequently picture is redrawn.

Screenbuffer

0verkill uses screenbuffer for displaying on the screen. Screenbuffer is array of size SCREEN_X and SCREEN_Y, it's stored in screen (textual part) and screen_a (attributes) variables. Whole scene is displayed into screenbuffer and then screenbuffer is flushed to stdout (blit_screen() function).

To achieve highest displaying speed attribute setting is high optimized. Number of characters written to stdout is minimized. Attributes are set only when necessary - when previous pixel color is different than color of currently drawn pixel.

There are three color setting functions: c_setcolor(), c_setcolor_3b() and c_sethlt(). When attributes differ only in highlight c_sethlt() function is called. When highlight doesn't change c_setcolor_3b() is called. In other cases the c_setcolor() is called.