Peter Mowry
Data Communications and Networks
Paper for Project and Related Research
Networking "Super IsoBomb"


Index:

1. Super IsoBomb Introduction
2. Paper Goals Overview
3. Project Initial Goals Overview
4. Game Networking Middleware Search Begins - GNE and HawkNL
5. Game Networking Middleware Search Continues - DirectPlay
6. Game Networking Middleware Search Expands - SDL_Net, Plane Shift, Net-Z/Eterna, ReplicaNet
7. Game Networking Middleware Candidate Summary
8. Protocol Issues

9. Pre-Modifying "Super IsoBomb" to be Ready for Networking - Example of 2-players to n-players Logic

10. Initial Implementation - Testing Leads to Further Optimization Concerns
11. Implementation Example - Character Movement
12. Protocol Specification and Review
13. Closing Remarks





1. Super IsoBomb Introduction

At the start of this networking project, "Super IsoBomb" was a Windows 2-player split-screen real-time video game. The engine and game logic is written in C++, and it uses DirectX for graphics, sound, and input. This earlier local version ("1.0b") of "Super IsoBomb" was developed by Jason Winnebeck, Jon Hilliker, and Jim Clase.

From Jason Winnebeck's Super IsoBomb web page – http://www.rit.edu/~jpw9607/isobomb/:

Super IsoBomb consists of multiple players attempting to achieve the goal of being the last player standing in the level by eliminating the other players. The players have two tools at their disposal to eliminate the other players -- an infinite supply of bombs that they can throw at the other players, and a small supply of homing bombs. When the bombs explode they create an explosion that damages all players in their path (possibly including the player that threw it).

When a bomb explodes, it explodes on a 2D plane of its height, but only in two straight lines on its plane. The explosion will be blocked by tiles whose height is greater than the bomb's, but not by players in its path. This stricter definition is important because a bomb may explode in the air. A player will have a certain height, and if the explosion touches the player, then the player is hit by the bomb. The game has various powerups, but sometimes random powerups are "infected" with viruses:

(After this quote, powerups are then described on the page, followed by screen shots. The page also contains a link to a binary distribution setup, and also to the game's source code.)

This original 1.0 local version of "Super IsoBomb" is currently played on one keyboard by two players (each get half of the split screen). Before reading this paper, it is advised that the reader install and play some version of "Super IsoBomb".


2. Paper Goals Overview

This paper is about the goal of modifying the local "1.0b" framework of "Super IsoBomb" into an Internet (IP) network version of "Super IsoBomb".

During this period of upgrading "Super IsoBomb", I also added other functionality to the game. That functionality is not discussed here - this paper focuses only on the networking aspect.

Since I started this project without any experience in C++ networking or real-time game networking, this paper also includes discussions about the research that was necessary for creating the network version of "Super IsoBomb". The project requires the consideration of and research of general design issues in real-time Internet video games.

I must research potential game networking middleware libraries. This project (C++ real-time game networking) is more expansive than the otherwise assigned project (Java simple client/server protocol). But "Super IsoBomb" is simple compared to larger Internet video games (such as more complex commercial games or even MMO (massively multiplayer on-line)). This paper will include a discussion on potential game networking middleware libraries, and one will be chosen for use in implementation.

Also based on the research, a network protocol is to be designed. It is also necessary to design and plan how the "local game" code framework needs to be changed to use to the protocol. (That is, when to send messages and how to react when receiving messages). A significant portion of this paper will discuss protocol design issues and present the final protocol.  Optimization is the most important goal of the design and implementation of the game's networking component.  The second most important goal is security against cheating, which is really only attempted when using a dedicated server.

Note that for more code oriented design issues, "doxygen" (Java doc style HTML pages) documentation will also be created (generated from code comments).

This paper was written to document and explain the project's goals, research, design, implementation, and progress. This paper includes project issues overviews; it includes research (such as a comparison of game networking middleware); it includes design and specifications (such as of the network protocol); and it also covers other areas (such as product testing).


3. Project Initial Goals Overview

As a source on general design issues, I read "http://unreal.epicgames.com/Network.htm". This article is on a much more complicated game networking engine than I intend to make. But its introduction mentions the general ideas behind predecessors.

Mentioned first is "peer-to-peer games like Doom and Duke Nukem", which sounds like it could work for "Super IsoBomb". But peer-to-peer games don't seem to have the opportunity for much of any security (since there is no authoritative game host), and would lack much potential for future scalability.

Next mentioned are "the monolithic client-server architecture, pioneered by Quake, and later used by Ultima Online. Here, one machine was designated "server", and was responsible for making all of the gameplay decisions. The other machines were "clients", and they were regarded as dumb rendering terminals, which sent their keystrokes to the server, and received a list of objects to render."... "The client-server architecture was later extended by QuakeWorld and Quake 2, which moved additional simulation and prediction logic to the client side".

Largely based on this history (and on my general knowledge of on-line games), I will explain my preliminary design plans overview.

In the final project, 2 players will be able to play "Super IsoBomb" over the Internet. To do this, a host will be started. Then, each client player must connect to the host via IP. The authoritative game logic will take place on the host computer. The host computer runs the model; the host does not need to render graphics (or play sound). The client will be displayed from a one camera (not split screen) version of the game. The client applications will communicate between the server and the user. The clients will receive input from the user and send movement/action requests to the host. The clients will receive game updates from the host and render the game display (and sound effects) in real-time.

To change the local split-screen version of "Super IsoBomb" into an Internet-based IP game, I need to create a client and a server version. Modifying the existing local game code will do this.

Originally I intended on simplifying the goals by making separate client and server projects (instead of having them share the same project with boolean flags), and by keeping the game logic 2-player logic (instead of n-player logic). I later decided to make everything in one project, and to generalize the game logic into n-player logic. This is much better in the long term.

In fact, I ended up creating four different game types (set by boolean flags): local, client, dedicated server, and non-dedicated server. Local game is 2-player split screen, just like the original. Client controls one player on the
 (remotely). Dedicated server runs authoritative game logic for the Client applications, but it does not draw, play sounds, or control its own player. Non-dedicated server run authoritative game logic for the Client applications, and it also controls one player like the client applications.

In an ideal situation, one computer can be designated for the dedicated server. On the other hand, the non-dedicated server is created so that two people can play with only two computers, without one having to run a server in the background. This is necessary, because having a client and a dedicated server run on the same machine causes redundant calculations and unnecessary CPU load.

Next, I shall explain the general networking concepts through the example of player movement. The client sending the server a movement request (one of four directions or a stop message) can do general player movement. The server will then move the player in the specified direction (as long as it's legal) until a "direction changed" message is received or a "stop" message is received. Every so often, the server will send the client player positions of a player with a direction. The client will reposition the player at the given location and start him/her moving.

The above example doesn't take into account networking time delays. So I may want to have an on-going clock in the server. The server could send this start time to the client at game start. Server to client messages (not necessarily all of them) can send the current server time. When a client receives a message about an object with a velocity, the object's position can be set to the received position plus (time_changed * current_velocity).

As another example, prediction logic for bombs can come from a bomb with a velocity. If I have the server send a client a bomb creation message, then the client can create a bomb at a certain position with a certain velocity. But I'm not sure on some of the specifics with bombs yet. The bombs could have IDs. And each time a message from the host was received, the bomb with the correct ID could be repositioned or created and positioned if no such bomb ID exists. Or maybe all the bombs should be sent each update.

Finally, I end this section with a list of my general protocol plans. These are general changes that need to be made to the local game framework for a client and a server executable. This is essentially a preliminary overview version of the protocol that I will plan to implement. Each of these protocol messages (which are also my guideline for game changes) have further specific details and issues that will be outlined later. "???" signifies much uncertainty.

Client Changes:

Server Changes:

This is an early preliminary overview of the protocol. The protocol's design will be further discussed and detailed in later sections. But first, I needed to research potential C++ game networking middleware libraries.


4. Game Networking Middleware Search Begins - GNE and HawkNL

My first thought for game networking middleware would be DirectPlay (since the game already uses DirectX for graphics and sound). But first I asked for advice from one of the author's of "Super IsoBomb" who had done some game networking with a multiplayer tank game called "Itana". HawkNL and GNE (Game Networking Engine) were mentioned.

GNE is the C++ game networking middleware project of Jason Winnebeck (the "Super IsoBomb" creator) built from HawkNL. GNE is cross-platform to Unix and Mac-OS since it doesn't use DirectX, but this isn't much of an advantage since "Super IsoBomb" is already closely tied with DirectX for graphics and sound. According to its home page, GNE has two parts: "the mid-level classes that handle packeted, error checked, and bandwidth throttled communication between two peers", and the high-level classes to "handle things like unique player ID's, player-to-player text communications, and develop a framework for a game server". After reading through a good deal of GNE's documentation, I decided the version "0.55 beta" was to in early in development to be much better than DirectPlay.

HawkNL has similar issues to GNE, since GNE is built from HawkNL. HawkNL is lower level and written for C. HawkNL is the networking component of Hawk (a OpenGL-based game engine). It's home page describes it as - "HawkNL (NL) is a fairly low level API, a wrapper over Berkeley/Unix Sockets and Winsock. But NL also provides other features including support for many OSs, groups of sockets, socket statistics, high accuracy timer, CRC functions, macros to read and write data to packets with endian conversion, and support for multiple network transports. NL has been tested on Windows 9x/ME/NT/2000/XP/CE, Linux, Solaris, IRIX, AIX, BSDs, MacOS 7-9 and MacOS X. There are also the two high level APIs, HawkNLU™ (NLU) and HawkVoice™, which are built on top of NL. It is NLU and HawkVoice™ that are most exciting, since they give developers portable, easy to use alternatives to Microsoft®'s DirectPlay® and DirectPlay® Voice." HawkNLU sounds more like GNE, but is also in early development - in fact it is "on hold again waiting for a beta of NL 2.0".

HawkNL's main appeal seems to be that it is cross platform. It looks fairly simple and has decent documentation, but I found no example code. I wanted to use something a bit higher level than HawkNL that at least has features like reliable UDP (HawkNL uses TCP instead of a slightly faster reliable UDP; speed is very important for real-time games). HawkNL is relatively low level, and I decided I may come back to it if I was unable to find anything higher level that is easy enough to use.



5. Game Networking Middleware Search Continues - DirectPlay

Next I went to an obvious potential canidate - DirectPlay. This involved reading through introductions/tutorials and Microsoft DirectX documentation. The Microsoft DirectX documentation describes DirectPlay: "The Microsoft® DirectPlay® application programming interface (API) is the component of Microsoft DirectX® that enables you to write network applications such as multiplayer games. DirectPlay performs all of the hard work associated with connecting players, even those behind Network Address Translation (NAT) devices, and managing sessions. It allows you to create, find, and connect to multiplayer games. Once connected, DirectPlay enables you to send guaranteed or non-guaranteed messages to other players. A common framework for launching applications and in-game voice communications is also provided. In addition, DirectPlay provides support for Microsoft Windows® Powered Pocket PC 2002 and connectivity with DirectPlay 8.0 applications."

From this description, it sounds fairly simple - especially the "performs all of the hard work" (for you) part. I was hoping to find an example for a simple server and simple client. I hoped for a basic example with classes such as GUI, server networking, and client networking. I hoped for a separate server class with some method to start a server, wait for connections, and create simple connections for communication with clients (once connected). And I imagined the client class to contain something similar.

The simplest example I found that comes with the DirectX SDK was "SimpleClientServer". With the executable, the user makes a dedicated server to handle the clients (entering a port number), and clients join by entering the IP and port number. The user does this all through a simple Windows Form GUI. Once the server has at least two clients, the clients can send each other a wave (a text message that says "PlayerNameSender is waving to you, PlayerNameReceiver").

The code for this example was much less simple than I had expected.  This is part of what drove me to continue searching.  I also didn't want to make the choice without researching other the possibilities.  I suspected that there may be other C++ Game Networking APIs that may have some of the following advantages over DirectPlay: easier to use (this is largely an issue of documentation), more portable, more optimized, easier to optimize, more options for my particular goals.  So I continued the search for good potential Game Networking Middleware canidates.


6. Game Networking Middleware Search Expands - SDL_Net, Plane Shift, Net-Z/Eterna, ReplicaNet

I found an article on www.GameDev.net called "Network middleware comparison" (http://www.gamedev.net/columns/books/productreview.asp?productid=240). The article briefly evaluates four network middleware packages - "The commercial packages were Net-Z/Eterna from Quazal (http://www.quazal.com) and ReplicaNet from Replica Systems (http://www.replicanet.com). The open source packages were SDLNet (http://www.libsdl.org/projects/SDL_net/) and 'Plane Shift' (http://www.planeshift..it/)".

SDL_Net reminded me a lot of HawkNL - It's a relatively low level cross-platform C library. The article accurately describes SDL_Net, "The API doesn't include a lot of the higher-level stuff you might want, such as latency calculation and reliable UDP packets". Like HawkNL, SDL_Net is lower level than what I'm hoping to find for this project, and the advantage that it's free for commercial use does not apply to my project's academic goals.

Plane Shift was reviewed in the article, but it's different than the others reviewed. Plane Shift is developing free MMORPG, with available developing source. It's really didn't sound like what I wanted, so I'm not looking into Plane Shift's complicated MMO-based networking code for this project.

Quazal included two components - Net-Z and Eterna. Eterna is more geared towards larger MMO games. Its general purpose is described by the article as "to provide managed classes of objects that are duplicated across a network session. Each class and object can have member variables and functions that are updated using latency compensation and prediction"…"The idea of having member variables getting updated by the API so that when the controlling class moves the other duplicated classes move is elegant. It removes the need to think in terms of individual packets." Net-Z definitely sounded like a potentially good candidate for "Super IsoBomb" networking. However, I had trouble getting the "Personal Edition" from their download page (I tried making an account with multiple e-mail addresses). Plus, the www.GameDev.net article said it was a 30-day trial (that's not good "Super IsoBomb", which isn't planned to be a commercial product).

ReplicaNet seems similar to Eterna/Net-Z (and fortunately its free version isn't limited to a 30-day trial). The API is further abstracted from the other options. ReplicaNet's page describes itself, "Each C++ class is treated as a potential network shareable object on the machine that allocates it. This machine has control over the C++ classes and can change variables or call member functions as normal. Once the object is ready to be shared to other machines the object is published on to the ReplicaNet network session. The underlying ReplicaNet software detects changes in the object and automatically updates the replicated classes on the machines connected to the network session. Any changes made to the member variables of the C++ classes can be extrapolated by ReplicaNet using several pre-defined filters to reduce the amount of network traffic when transmitting changes in the object." Documentation for ReplicaNet came in doxygen html files, and in 5 examples. This different approach sounds like it could be simple to use. It took a few hours to realize that it was not that simple; it involved use of things like writing a .rol to define sharing attributes and compiling it with the ROLCompiler compiler to create parent .cpp and .h files for files with variables that wish to be shared. The dependencies were also a pain to set up, and the documentation gave little help.

RakNet was mentioned in a separate www.GameDev.net article (http://www.gamedev.net/columns/books/productreview.asp?productid=220). RakNet is a more traditional data sending library (like HawkNL or DirectPlay; not like Net-Z/Eterna or ReplicaNet. On www.GameDeveloper.net, RakNet is mentioned as, "a commercial game-orientated multiplayer API that is intended to replace DirectPlay as an easier to use and more powerful alternative to Microsoft's multiplayer engine" (http://www.gamedeveloper.net/gdn/index.php?&page=viewresource&rid=1802). After looking through some samples and its documentation, I was strongly attracted to RakNet because of its very straightforward documentation, which included simple tutorials and "Quick Start" set-up directions. This was very important because of the limited time and my personal lack of experience with real-time game networking.


7. Game Networking Middleware Candidate Summary

I spent at least a few hours looking specifically into each of the candidates listed in the previous three sections (sections 4, 5, and 6). This included download/set-up/installation, reading of documentation, and perusal of example code. These candidates are all C++ (or C/C++) networking APIs geared towards use in real-time video games. The table below represents an overall assessment of the top choices for my specific project - networking "Super IsoBomb". This overall assessment is my personal view (at the time of writing), and is based on my brief research.

Overall Assessment

API Name and URL
Overall Assessment Explanation
5 - Most Likely
RakNet - http://www.rakkarsoft.com/

Commercial with freeware version. Easy to use packet-based library; and very helpful documentation/tutorials. Even a forum that is commonly answered. Automatic time stamping support. Built in object ID support. Easy to use, great docs, quick for set up and trivial implementations. No automatic message combining or compression yet (is planned for a future release).

4 - More Likely
ReplicaNet - http://www.replicanet.com/

Commercial with freeware version. Abstracts packet networking into network shared C++ objects. Unfortunately, I found it to be unnecessarily complicated. This is possibly due to lack of tutorials; though decent examples were included. Did not include automatic load balancing when my project began (but has it as of version 3.70).

4 - More Likely
DirectPlay - http://www.microsoft.com/windows/directx/
Because this is a component of Microsoft's DirectX, there is also a lot of information and tutorials on the subject. Appears to have most necessary features. However, it's not as easy to use as it could be, possibly because it includes a some features that are unnecessary for this project.
3 - Potential
SDL_Net - http://www.libsdl.org/projects/SDL_net/
Open Source cross-platform project; related to cross-platform SDL (Simple Direct-Media Layer).
2 - Less Likely
HawkNL - http://www.hawksoft.com/hawknl/
Low level and simple C-based library. Not much documentation/tutorials. Version 1.66 is still pretty low level; it is soon going to be replaced by a higher level version - HawkNLU (HawkNLU will is "on hold" for HawkNL 2.0).
2 - Less Likely
GNE - http://www.rit.edu/~jpw9607/gne/
Game Networking Engine is Jason Winnebeck's (one of the authors of "Super IsoBomb") largest individual project; built from HawkNL. It's essentially still under development.
2 - Less Likely
Quazal Net-Z - http://www.quazal.com/
Distributed objects for sharing game objects. Extrapolation, prediction, fault tolerance, error correction, and other under the hood optimizations. But current freeware version's 30 day trial makes this API not usable for my non-commercial project.


8. Protocol Issues

The end of section "3. Project Initial Goals Overview" defines an early design overview of the protocol. This section describes the issues deeper, and discusses protocol design trade-offs.

Character Movement

Character movement should be fluid and able to change often - movement delays can be very noticeable to the client.

This movement "Client-side authority" is significantly easier to program. "Client-side authority" has the advantage that what the client player sees is what really happens. However, it is totally open to warp cheating, since each client is authoritative of its own position. (With "Client-side authority", clients could send bogus position messages warp). Request/Update allows the server to be authoritative.

Similar issues arise in any real-time Internet game with an authoritative server. It would be fast to simply run independent games. Each client would just send the other clients results like that say "I win", "you die", "I scored a point", "I teleport to this position across the map", etc. At least in commercial internet networked games, it is important not to make client-based cheating as easy as sending a few simple packets that say "I win", "you die", "I scored a point", "I teleport to this position across the map", etc.

Even without cheating, if there is no centralized authority, inconsistencies can easily occur. Although in the case of "Super IsoBomb", each client could be made separately authoritative of their own set of data (their own character, their own bombs, and their own stats). Of course, that method is extremely open to cheating.

For Request/Update, movement prediction and smoothing schemes are helpful.

For prediction, one method I used was to simply add the timestamp to the velocity when a position/velocity was received.  This wasn't good enough because the client has given input after the position/veloicity message's time stamp.  So, I tried a scheme similar to that of the Unreal Network Engine.  The main idea is for the client to store a list of moves that were created at the time of input.  These are given time stamps.  The moves are a change in direction or velocity (if moving) that is sent to the server.  When a new update is received, the list is iterated.  Each move in the list with a time later than the position/velocity update's time stamp is applied instantly with dt of the time between each pair of move time stamps.  Note that in between updates, the client's character continues to move locally as if in a non-networked game until it gets an update.

Also, the client has time stamps in its moves sent to the server.  These are used to undo part of the previous move (dt = currentTime - timeStamp) and then instantly do the current move (dt = currentTime - timeStamp).  This has a slight vulnerability.  The client could fake an earlier time stamp and thus undo a previous move.  It is also less accurate to the client because in the dt of the move undone, the server might see the character getting hit by an explosion.

After testing, this wasn't smooth enough with my particular implementation.  The client still jumped a bit when getting a server update.  One way to further improve this sort of situation is to gradually smooth an object to a point over time, instead of instantly.  This is a simple trade-off between accuracy and smoothness.

I eventually came back to client-side positioning.  And the reason is because the security vulnerability can be fixed by having the server authorize the legality of the position move requests.  For "Super IsoBomb", this can be done based on distance and time (and height).  Time stamps are always verified as after the current server time.  Any time stamp faking in the past just makes the current move shorter, which meaks the position check shorter.


Thrown Bombs

I came up with a few different general approaches to the design of thrown bomb messages:

First notice that "bomb spawn" specifies "bomb properties" and not "player ID". This is because the player positions of the client are not as reliable as including it in the message.

"basic B" would have less overall traffic than "basic A" and spawn the bomb immediately when the player requests it to spawn. When the bomb explodes, "basic B" displays the explosion before it happens on the server, and "basic A" displays the explosion before it happens on the server.

The main problem with the two "basic" approaches is that bomb bouncing is not fully reliable. This is a problem because the bombs can bounce off of players, and player positions are not fully reliable. "ID-based continual updates" is meant to give a more accurate picture of the server world (especially if bomb velocity and change in time were used for extrapolation of bomb position). However, "ID-based continual updates" would result in by far the most traffic, and require throwing bomb code to be modified with the addition of IDs.

"hit floor" is an attempt to remedy the unreliable bouncing bomb problem in a much simpler and less costly manner than "ID-based continual updates". "hit floor" assumes that wrongly bouncing bombs are not expected to occur very frequently. Thus, the "hit floor" solution is intended to fix a set of these with about as little network traffic as "basic A". When a bomb lands in the wrong position, it gets repositioned

Unfortunately, the player can still be mislead significantly more by "hit floor" than by "ID-based continual updates". For example, a client bomb could bounce mid-air off a character that is not who is wrongly positioned and explode before it hits the ground. This would cause the bomb to be displayed exploding on the wrong side of the player.

I tested "basic A" with a form of player movement implement. I tried "basic A" over "basic B" because displaying the explosion after it explodes means the client may walk in front of the bomb and die slightly before seeing the explosion occur. That seems worse than having a player die for walking through a bomb explosion slightly after it dissipates (the bad side of "basic A"). However, with "basic A", the bombs were much less consisent than I expected. This is especially true if players are near each other, and bombs have the potential to bounce back and forth between two players.

With the form of "basic A" that I tested, thrown bombs often immediately bounced off of the player that spawned them. This happened because the player would send a bomb throw request, move forward, and then get a message to throw a bomb from the position behind him. This could be fixed a few different ways. For example, the bomb could not collide with its owner for the first few milliseconds. Or the bomb could be time stamped and use the time difference to spawn the bomb forward. Or the bomb could spawn from the client player instead of the specified position. There are many different ways to fix this particular problem, but it's not the only bomb inconsistency problem.

I tried "basic B" to fix the problem of throwing a bomb from behind the player (by throwing it immediately before the player moves in front of the bomb spawn point). Bombs would blow each other up mid-air on one side, but land on the other side. A bomb would bounce off a player on one side and go over the player on the other.

Through implementing and testing "basic B", I discovered that another method ("ID-based continual updates'" or "ID-based bounce and hit floor") was necessary for the bomb protocol. So I decided that for guaranteed accuracy, the optimial solution would be to send the bombs only when their path changes (in the event of bounces). So I implemented "ID-based bounce and hit floor".

This turned out not to be enough. If I (as a client) stood still and just threw bombs straight forward, the results would be noticeably inconsistent. A bomb on the client would make it on top of a ledge, but not on the server. Sometimes the client representation of a bomb would be off from the server by an entire tile or more. The float precision is lost faster than I originally anticipated. Accuracy is regained when updating a bomb on a bounce, but this is lost shortly after. And bombs that don't bounce on the server never send such updates, until the separate "hit floor" update warps the bomb when it lands. But that means the client user would see a bomb almost land and then warp. It also has especially bad consequences in the event of a mid-air explosion. If a separate explosion message is not sent, then the user could see an explosion in the wrong position. If an explosion message is sent, then the user would see the explosion in a different place from where the bomb was right before the explosion.

Why is the position's accuracy inconsistent on the client? Each frame, "float dt" is passed to a bomb's "update" function. This function calculates the new position of the bomb based on "dt". This includes calculations such as the following (and others):

height += vect.getMagy() * dt;
newPos.x += vect.getMagx() * dt; // if direction is DIRECTION_DOWNRIGHT

These are floating point calculations, and thus lose accuracy. This accuracy loss is made worse not only because the calculations are done often, but because the calculations are done with different numbers on the server than on the client. The "dt" passed to an update function depends on the framerate for a particular frame.

That particular part of the problem could be fixed. An easy fix would be for the "dt" values to be substituted by consistent dt values that are used only when the timer reaches certain points. For example, a dt of 100 ms could be used whenever a full 100 ms passes. So if the first frame happened in 150 ms, then one 100 ms dt would be used. And if the second frame happened another 180 ms later (for a total of 330 ms), then two more 100 ms dt values would be used.

That would give much more consistent logic updates, but would not fix the current problem. The floating point accuracy would still be lost much more quickly than desireable. To acheive a fairly accurate display on the client as on the server, continual updates can be sent as in "ID-based continual updates". This is how the player updates are sent (periodically).

Bombs only last a few seconds, and exact precision isn't necessary. What is desireable is the following:

So the necessary protocol is like "ID-based continual updates". But it also includes a "hit floor" so that bomb landing position is correct. Also necessary is a separate "explosion" message to ensure that the explosion is displayed on the correct tiles.

None of these approaches mention the authority on bomb thrown power. Each of these approaches could work with client authority on power, or with a "start power charge" message and a "release power charge" message. However, taking away the client's control would result in the following: increased bandwidth usage, a decrease in the client player's control, and a decrease in display accuracy (due to the delay). With default settings of 0.75 power charge per second and 1.0 max power, the longest power charge happens quickly (1.33 seconds) and is not difficult for the player to control. Also, without consuming bandwidth, the server can still check to make sure a legal power value was sent (between the player's current min power and max power settings).

Homing Bombs

The movement path of homing bombs is very different than that of thrown bombs. The path of a thrown bomb is a projectile motion determined by current velocity and any character that it might bounce off of. The path of a homing bomb is only determined by the character it is tracking. In most situations, a slightly lagged character position would not greatly alter the path of a homing bomb.

There is still the possibility of significantly altering a homing bombs path with an unreliable target position. Next, a bad case scenario is described. The player is on a ledge walking slightly forward. The homing bomb is at a point when it must decide whether to go up the stairs or straight towards the character. At the time the choice is made, the client may be wrongly predicting that the character falls. However, the game will shortly thereafter receive the clients true position, and the bomb would probably alter its path to the correct one (though slightly behind where it should be). In this situation, a wrong character position couldn't continue to trick the homing bomb. This is because once the character is on the ground, he can't jump back up (the homing bomb and character are allowed to move the same height in a vertical steps).

However, it is theoretically that a more significantly wrong decision could be made by a homing bomb. See the figure below. The homing bomb must decide how to go around the obstacles - left or right. A slightly incorrect character position for only a short period of time could cause the client to predict homing the wrong homing bomb path. Imagine if the bomb wrongly chose north, and then the character moved south west when the homing bomb was 1/2-way (similar to what is shown in the next figure). In such a situation, the client bomb would have to go around the obstacle, while the server bomb would already be on the other side.

HBomb Figure


Explosions

Not mentioned yet of bombs is the time of bomb detonation. No matter how the bomb's position gets is displayed, it is extremely important that the client sees the bomb's explosion correctly. The only way for a player to get hurt is by being hit by an explosion. So, even if the bomb positioning isn't perfect, the explosions should be consistent. For example, if the server bomb explodes midair, the client bomb should not land and explode on the ground 1/2 a second later.

Even with "ID based" or "bounce and hit floor", a little lag could cause a thrown bomb's timer to run out at a time when it or surrounding bombs are in an incorrect position. It is not safe to assume the following: if a bomb explodes midair, then surrounding bombs will be in the right position for the client to predict their explosion. A midair explosion could also cause a homing bomb to explode. To be safe, explosion messages could be sent.

This does have the drawback that an explosion happens on the client after it happens on the server. The server doesn't know where the bomb will explode until it explodes, and then it can send the positional explosion message. This way, the player can at least see visually reliable explosions (albeit delayed).

This method can be acheived by having client bombs simply disappear without an explosion, and then the explosion happens when it is received. Or the bombs could wait for an explosion message that specifies the bomb to disappear by ID.

Power-ups and Viruses

A power-up affects a player when the player collides with it; player position authority resides on the server (and is not reliable on the clients' side). Power-ups also randomly have viruses; this is another case in which the server must be given authority.

Power-ups have unique static positions. Thus, a power-up could be referenced by position. However, it is less data to send an ID (an unsigned char) than position (one float value per dimension). Thus, the power-ups are referenced by ID.

Power-ups types are determined at game start by the map, for the duration of the game. Thus, power-up collision messages do not need to mention the power-up type.

If (on the server) a player gets a power-up with a virus, this will be sent as part of the power-up message. However, a player can also get a virus if a non-infected player collides with an infected player.

For viruses and power-ups, timer lengths are predetermined. Both the virus and power-up start times begin on the client shortly after the server. These timers are further synchronized with time stamping. Thus, the client can independantly simulate its own timers for both the power-up respawns and the virus durations.

Notice that the virus state of a power-up is kept on the server. Whether or not power-up has a virus is determined at spawn time, but this is never sent to a client. Instead, clients are informed when a player gets a virus from a power-up. This prevents cheating clients from knowing which power-ups have viruses earlier than they should.


Damage Player and Fatality

The protocol for damaging the player is simple:

Player deaths do not need to be sent. They are done on the client (as on the server) when a player reaches 0 hit points.


Start Map

Maps are referenced by number (map1.map, map2.map.... map0.map).

"Super IsoBomb" level maps can easily be created by the form-based tilemap editor (even with new tiles if the graphics are labeled properly). However, at the initial stage, there will be no more than 10 maps, and the server will assume that the clients have the correct maps.

It is feasible in the future to have the server send clients the correct maps when they join. The biggest existing map file is under 5 KB, and even the tiles are only 12 KB each. So the server could just assume that the client has the all wrong maps and tiles. The files could be sent at connection time, or sent when a new map is loaded.

For a simple and easy UI, the number 10 keys are used to signify map change (this is done in the local game version). For clients, map selection is only a request:

The server also reloads/restarts the existing map when the first player joins, and when the second player joins. After that, the map is reloaded/restarted whenever the round is completed (all but one or zero players dead). The sequence for map reload/restart is as follows:

Note that a "start player" message includes the following: player ID, player starting position, and player score. Thus, the client can associate a player character in the game with each opponent client (and himself/herself), and the each player's score is updated.


9. Pre-Modifying "Super IsoBomb" to be Ready for Networking - Example of 2-players to n-players Logic

Before networking the code, there was a series of changes I made for it to be ready. These changes did include simple things that would have been as easy to add later, like adding a "setPosition" method to the "Character" class. Or making the camera full screen on one character instead of split screen on two characters. But it also included more significant changes.

For example, although the "Character" class was fairly well designed as separate "Entity" objects, 2 players were hardcoded into the "Game" class logic in a number of different ways. This included pairs of variables like the following: player1, player2, keys1, keys2, numWins1, numWins2, ctrl1, ctrl2, etc. A significant amount of game logic was based on this 2-player assumption, such as the logic for deciding the winner. The logic for deciding the winner at the end of a battle was (in simplified pseudocode):

If player1 and player2 are not both dead, then:
If player1 is dead, then inc player2's score
If player2 is dead, then inc player1's score

The player-oriented pairs of variables were replaced with lists and vectors. The logic was generalized. As in the particular example of deciding the winner at the end of battle (this is after after the logic which defines when the end of a battle happens), the new logic was made to be more generalized:

allDead = true
Iterate through the players collection for each player
If a player is not dead, Then all = false
If( not allDead )
Iterate through each player in the player collection
If current player is not dead, then inc the current player's score

A closely related example is that of when a battle is triggered to end. This is done by creating a timer-based "EndGame : GameLogic" and adding it to game's "GameLogic" list to be updated every frame. The EndGame object ends the game by calling resetMap on the global Game object when time runs out. In the 2-player version, the "EndGame" is created when either player dies. In the n-player version, the number of players living is checked each frame and if it is less than 2 (equal 1 or 0), then the timer to trigger the end of the game begins. This is done in the following code:

// count how many players are alive
int aliveCount = 0;
for( playersIt = players.begin(); playersIt != players.end(); ++playersIt ) {
    if( !playersIt->second->isDead() ) {
        aliveCount++;
    }
}
// End the game if there is less than 2 players alive
if( aliveCount < 2 ) {
    g_game->addLogic( new EndGame() );
}



10. Initial Implementation - Testing Leads to Further Optimization Concerns

Laggy Results Leads to Organization

The necessary code was written and the game worked okay with a local server and local client.  But the sharing of the CPU and graphics card made it difficult for this test to be accurate.  And more importantly, this doesn’t simulate real network lag.  So the game was tested over the Internet.  The network connection was between an RIT T3 connection and a cable modem connection.

A non-dedicated server hosted the initial test.  One and two client connections were tested.  The non-dedicated server’s game-play was fine, but it was unplayable for the client players.  The client player’s own characters generally worked okay, but their opponents did not move often.  The clients would still see the results of their opponents’ positions (such as bomb spawns and movement blocking), but the actual opponents did not move.

At the time of this test, all message received processing was handled in a centralized area.  To ease the testing and reduce code redundancy, I decided to move all of the send messages into a centralized area.

These were very simple methods, such as the following:

void Game::SendGameStartStruct( GameStartStruct* msg ) {
    rakServer->Send( (char*)&msg, sizeof(GameStartStruct), HIGH_PRIORITY,
        RELIABLE_ORDERED, 0, UNASSIGNED_PLAYER_ID, true, false );
}


The code for the sending of many messages in general was previously in separate files, and the code for the sending of many messages of the same type were sent in different files.  The point of centralizing them was so that the sending method parameters could be easily evaluated and kept consistent.  This works because the flexibility to send messages of the same type in different scenarios is not desirable.  On the other hand, the population of the game packets is often dependent on where (in the code) the message is sent.

Reducing Data Inside Each Packet Type

A significant portion of the bandwidth saved was from conserving the amount of information sent inside each message.  Part of this was simply checking the packets for any unnecessary information.  For example, a BombSpawn packet does not need to send the delay before explosion.  This can vary, but in the current game logic is it specified in a configuration file and kept static for the rest of the game.  Thus, the configuration files can either be assumed to be consistent or the configuration information can be sent before the game starts.

The same issue exists for other packets, such as PowerupCollision.  PowerupCollision packet sends a type message with no information about the size of the bonus (because the client already knows this).

Another method for reducing packet sizes is simple and direct data compression.  This can be at the expense of precision or not.  I will mention an example for each.

An example in which abosolutely no precision is lost by a data compression conversion is the case of BombExplode messages.  The bomb explosion's x, y position could be sent as a pair of 4 byte floats.  But this is completely unnecessary because explosions happen on discrete tiles.  This is an intentional part of the game logic so that players can hide from bombs by centering themselves on discrete tiles based on explosion size.  Since the map dimensions typically range from 10 to 30, sending an unsigned char of range 0 to 255 is enough.  Thus, the BombExplode message was reduced from 15 bytes to 9 bytes.

Precision is lost in the conversion done for player movement positions.  Player position is stored as three dimensional float values: x, y, height.  Height does not need to be sent because it is a function of x, y.  Float variable types are each 4 bytes of data.  Although these positions are stored as floats, the real range of these x and y positions is 0 to the number of tiles x and 0 to the number of tiles y.  And the max values of x and y are small because maps aren't meant to be big.  Typical max tile values range from around 10 to 30.  Thus, these values can be compressed from 4 byte float values into 2 byte unsigned short values with very little loss in precision.  This is done as follows:

Position.X = Position.X / MapWidth * 65535.0f
Position.Y = Position.Y / MapHeight * 65535.0f

Then position is sent as a 2 byte unsigned short.  On the client side, it is converted back into float as follows:

Position.X = Position.X * MapWidth / 65535.0f
Position.Y = Position.Y * MapWidth / 65535.0f

This results in a very small loss of precision.  Below are some sample measurements of the results of this compression.  These measurements are taken at a few different levels and positions.  Results are displayed in the following table:

X or Y
Start Value
End Value
Loss (Start - End)
Map Dimension Max (X or Y)
X:
6.2449999
6.2447243
0.00027561188
21
Y:
0.24500000
0.24490730
9.2700124e-005
25
X:
10.421012
10.421012
0
21
Y:
12.197021
12.196918
0.00010395050
25
X:
3.0210016
3.0209811
2.0503998e-005
10
Y:
7.9969897
7.9969482
4.1484833e-005
10
X:
16.564997
16.564814
0.00018310547
25
Y:
16.956985
16.956970
1.5258789e-005
25
X:
24.485085
24.485008
7.6293945e-005
25
Y:
24.485107
24.485008
9.9182129e-005
25


As can be seen by the data, the loss in precision is tiny.  This is not at all suprising because the real data range of 0 to 25 (with 7 digits of float precision); this means that the float range is much larger than is needed.  And this simple compression reduced the PosVel packet size from 17 bytes to 13 bytes - a significant amount.  The result is in fact more precision.  This is because client side prediction is more lossy than this conversion, and this optimization allows the data to be sent more often.

Though this is still just an example.  For this case, a better way might be to use discrete integer values in the first place.  Such as using an unsigned short and making each tile width and length a constant number of units (like 500).  Or even better, or a number of units proportional to the width and length of the entire map (65,535 / width and 65,535 / length).

Thrown Bomb Logic Determinism

One major optimization issue I wanted to deal with was the significant amount of bandwidth necessary per bomb object.  Continuous bomb position updates were needed due to the client's inability to predict movement consistently with the server.  I wanted to reduce this network traffic by making the game logic more deterministic.  I would like to not have to send continuous updates when the bomb moves in a simple unobstructed path.  Ideally, instead of continuous updates, I could just send event updates when the bomb bounces off of a player.  This non-determinism is due to the inconsistencies of different roundings with different floating point calculations.

The first part of this was the fact that update dt values were not consistent.  Depending on a specific frame's frame rate, a different dt would be used for the update.  To fix this, I wrapped the update calls with a timer logic that would only call update with a constant dt value.  This was done as follows:

    // update logic once every UPDATE_FRAME_DT_MS ms
    unsigned long current = getTime();
    for( unsigned long count = lastUpdateMsSinceStart + UPDATE_FRAME_DT_MS;
        count < current; count += UPDATE_FRAME_DT_MS )
    {
        g_game->update( UPDATE_FRAME_DT_MS / 1000.0f ); // seconds
        // Control the game logic returns true when it's time to exit
        if( g_game->control( UPDATE_FRAME_DT_MS / 1000.0f ) ) {
            g_game->disconnect();
        }
        lastUpdateMsSinceStart += UPDATE_FRAME_DT_MS;
    }

This was good, but not good enough, because ThrownBomb::update can be called from other places.  In particular, I want to call update with time stamps.  To fix this problem, I created an addPartialUpdate function for ThrownBomb.  It's a very simple function, so I display it here:

    void ThrownBomb::addNonStandardUpdate( float dt ) {
        dt -= UPDATE_FRAME_DT_MS / 1000.0f;
        while( dt >= -UPDATE_FRAME_DT_MS / 500.0f ) {
            update( UPDATE_FRAME_DT_MS / 1000.0f );
            dt -= UPDATE_FRAME_DT_MS / 1000.0f;
        }
    }

This method rounds any dt value greater than or equal to UPDATE_FRAME_DT_MS / 2 to a whole UPDATE_FRAME_DT_MS increment.  For a UPDATE_FRAME_DT_MS of 16 milliseconds, this can give a visual delay of 8ms, which is minior.  But more importantly, it keeps the inputs synchronized.

The other part of the problem was the method by which the position was being calculated.  Originally it used a euler integral method, summing the deltas of each new position.  Each frame a dx and dy would be calculated and added to the current x and y.  Each frame, imprecise calculations were done based on the results of previous imprecise calculations.  This incereased the potential for inconsistencies each frame.  I redid the thrown bomb physics using a constant projectile motion equation:

    x = x0 + v0 * cos (q ) * t
    y = y0 + v0 * sin ( q ) t - 1/2 * g * t * t

With this method of calculation, the only variable input is t.  With consisitent simple dt inputs (such as 0.016), t can be kept accurate without the loss of precision.  The position is then recalculated at each update.  This saves precision from the other method in which each frame added possible loss of precision.

Because not all FPUs are completely consistent in floating point calculations, it might be better to redo the calculations with integers.  However, to convert all game logic from float-based to integer-based would be a significant amount of work.  And for thrown bombs with a constant dt of 0.016, the float calculations are not lossy.  I tested summing from 0 to 1000 by increments of 0.016.  At 10,000, the float calculation gave the number 999.988.  There was almost no loss at the point of 5.0 - the number of seconds a thrown bomb is intended to last.  The calculations for a thrown bomb are even fewer than this, because a thrown bomb typically either lands within 5.0 seconds, or bounces off a player's head much earlier than 5.0 seconds.  A bomb can only bounce upwards if it hits a players head; it bounces downwards if it hits a wall.

The result of this deterministic thrown bomb movement is that it is a new method of sending for the thrown bomb protocol:

Request and Spawn still have to be sent.  The new method allows Update messages to only be sent in the event of a player bounce.  The Land and Explode messages are no longer necessary because they can be predicted exactly from the most recent Spawn or Update.  With the new method, bomb explosion times can be predicted at the correct position at a fairly accurate time (in fact more accurate than waiting for an explode message with the old method).


Packet Combining

Packets for a real time game are sent often, and can often be small.  In this situation, packet header size becomes significant.  An optimization is to group small packets and send them in periodic updates (this requires data parsing).

RakNet does not automatically combine packets.  Therefor, each of these messages was being sent as separate UDP packets (and each with their own UDP header information).  The messages below are grouped in accordance to a plan for grouping the messages.  Byte sizes are included; byte sizes are based on Microsoft Visual Studios .NET C++ documentation.

The packet combining was made much easier by the organization of the sending functions into a central area.  With this method, the data combining can be done when a packet's sending function is called.  Then, the combined messages are sent elsewhere at periodic intervals.

At the time of this paper, I have not yet combined packets.  This is because the next version of RakNet is planned to make this significantly easier.  Additionally, the next version of RakNet is planned to include performance optimizations such as "streaming and a transparent compression / encryption scheme" that are expected to "knock down bandwidth usage by a good 70%" (posted by RakNet's created on www.rakkarsoft.com/forum/).


11. Implementation (Code Design) Overview Example - Character Movement

This section explains the essentials of what was necessary to implement the example of character movement.  This example is chosen for a few reasons.  Character movement is a common game networking problem since many games include client control over moving characters in a real-time environment.  This is also the area where I began implementation.  It's also one of the more important areas; it is especially important for the client's character not to move unsmoothly since the camera follows the client.  Largerly because it is important, it is also one of the more complicated areas of the network protocol and implementation.

In a local game, all movement is done directly by keyboard input.  This is done using the class KeyboardController that extends from Controller (an interface for controlling a Character).  In a network game, there are the following types of character movement: movement of the client's opponents, movement of character's on the server, and movement of a non-dedicated server's player, movement of the client's player.  The core of the functionality is broken into these respective areas, and three Controller classes were constructed.

The client's opponents and the server's characters both use the DeadReckononingController.  Dead reckoning is a commonly used term for movement prediction.  The DeadReckoningController uses a very simple algorithm.  Each character has a state of their movement direction and if they are moving.  When the DeadReckoningController gets updated, it moves the character in its current movement direction if and only if it is in the is moving state.  The DeadReckoningController's state is dependent on where it is being used.

One use is for the client side prediction of opponents' movements.  When a client gets an update for an opponent's movement state, this includes a position and a velocity (direction and is moving bool for "Super IsoBomb").

For the server, client character movement is done through movement requests.  When the server gets a movement request, it sets the state of the character's DeadReckoningController.  Then, the server does future character movement based on this state, until another movement request is received.

I tried a few different schemes for the specifics of character movement.  For more on this, see "8. Protocl Issues" ("Character Movement") or "12. Protocol Specification and Review" ("Character Movement - MoveRequest, PosVel").


12. Protocol Specification and Review

This is specification defines the protocol for the important messages.  This is largely an extension of "8. Protocol Issues".  But unlike "8. Protocol Issues", this section is more concise and describes the final protocols actually implemented.  Comments are made where thare is area for future improvement (in optimization or security).


Game Setup Triggers - PlayerJoined, PlayerDisc, GameStart, GameStartPlayer, MapChangeRequest

These messages are fairly self explanatory.

When a player joins the server, the other clients get a PlayerJoined message and adds the specified player ID to a list.  The joining player also gets PlayerJoined messages for each player ID already in the game.  When a player quits, the clients get a PlayerDisc message from the server.

When a game round ends, GameStart is sent to each client.  This indicates how many GameStartPlayers are to be sent.  The client then waits until it has received a GameStartPlayer message for the expected number of players.  GameStartPlayer includes position and score.

A client sends MapChangeRequest to the server.  MapChangeRequest specifies the desired map, and the server stores each client's desired map choice.  When all client desired map choices are the same and are different than the current map, the map is changed.  For a non-dedicated server, the non-dedicated server decides when to change the map.


Character Movement - MoveRequest, PosVel


Client moves locally assuming authority.  Client periodically sends MoveRequest with position/velocity to Server (time stamp included).  Server verifies legality of move.  If it is legal, Client's requested position authority is respected.  Otherwise, Server sends PosVel as a correction.

In between MoveRequest messages, the server predicts client movement using velocity.  But it keeps stored the real position of the last MoveRequest.

The movement legalization check involves a few steps.  The time stamp is verified as before the current time.  The distance is verified as legal (based on dt and velocity).  The height steps are verified so that the character does not try to move upwards in height more than 1 height unit per tile change.  None of these initial tests are expected to fail unless the client program is trying to cheat.

Finally, positions between the source and destination point are tested for collisions.  This can cause a character to collide with a powerup, or become blocked by another character.  Server has authority over the real character positions.  So if Server finds that a character's path is blocked by another character, then a correction PosVel update is necessary.

For "Super IsoBomb", this method is excellent.  It requires lower bandwidth - fewer Server to Client PosVel messages about a client's character.  The time check, distance check, and height check is really an anti-cheat - these checks should never fail unless the client is trying to cheat.  The only time that a client should get a repositioning message for its character is if the client's character tries to walk through a another character.  This could happen without cheating because the client sees an unreliable view of other characters.  The method is also secure against warp cheats.  And it's perfectly smooth for the client, except when it gets the relatively rare PosVel correction message.


Thrown Bombs - ThrownBombRequest ThrownBombSpawn, ThrownBombHeadBounce

Client sends ThrownBombRequest.  Server returns a ThrownBombSpawn with a time stamp.  Thrown Bomb is created with specified position, direction, power, bomb size, blast size, player ID, object ID.  Thrown Bomb is immediately updated (currentTime - timeStamp).  Client predicts deterministic logic.  In the event of bounce off of player, Server sends Client a ThrownBombHeadBounce.  This update includes a new start time, start position, and start height for the Thrown Bomb equation-based logic.  The equation used is an overall projectile motion equations, and not based on deltas.  See "10. Initial Implementation - Testing Leads to Further Optimization Concerns" or documentation/code for more specific information.

Thrown bomb logic is deterministic to a point of satisfaction.  The only real worry is if a bomb explosion might be on the edge of a tile and possibly off by one tile on the client.  If different FPUs give too quickly divergent results, there are two good solutions.

One would be to convert all calculations to integers; this would be ideal if a significant problem is found to exist through rigorous hardware testing.

A quicker solution is to send bomb explode for thrown bombs.  This will cause explosions on the client to happen at ping/2 after they happen on the server.  This isn't very significant, but could still be further optimized.  The explode message could be sent at the client's average ping/2.  Or it could be sent with time left when the bomb lands (and is thus not going to change position).

The current solution has worked extremely well so far.  But it may be found to need improvement once more diverse hardware testing has been done.


Homing Bombs - HomingBombRequest, HomingBombSpawn, HomingBombUpdate, BombExplode

Client sends HomingBombRequest.  Server sends HomingBombSpawn to all clients.  Server sends periodic position updates (with time stamps).  On impact, Server sends BombExplode (and Client displays explosions).  Server sends periodic HomingBombUpdate messages.  In between updates, Client predicts movement with the same AStar pathfinding algorithm used on the client.  However, this prediction is imperfect because character positions are unreliable.  Thus, the HomingBombUpdate messages are necessary.

The main drawback to this method is that when homing bombs are used, they take up very significant bandwidth for continuous updates.  But this is difficult to avoid with a dynamic path-finding algorithm based on character positions that are unreliable on the client side.  However, it is tile-based.  Thus, a possible optimization might be found in some more complex scheme.  For example, maybe an update is only necessary when a player tile position changes (and the update could be sent with the player position update).  This may be considered for future optimization.


Game Triggers - PowerupCollision, VirusSpawn, DamagePlayer

Server sends PowerupCollision when a character collides with a powerup.  PowerupCollision includes target player ID, powerup type, powerup ID, and virus type.  Client applies powerup or specified virus to player.  Powerup respawn timer is set (and time stamp is used to immediately update the respawn timer currentTime - timeStamp).

Server sends VirusSpawn when a player collides with another player and spreads the virus.  VirusSpawn includes player ID and virus type.  Client applies virus for the expected time (and time stamp is used to immediately update this timer).  In the case of "Bombarea" virus, the client does randomly send bomb requests.  Instead the server character sends the client ThrownBombSpawn messages.

Server sends DamagePlayer when a character is hurt by an explosion.  Client receives DamagePlayer, and damages the specified player.  If player HP is 0, then Client runs a fatality death animation.

These triggers are all simple and have little need for considering protocol design optimizations.  Like the other messages, they can benefit from general optimizations such as packet combining and data compression.  Though one possibility that might be worth consideration would be to send unreliable current values as part of unreliable game updates insetad of reliable messages.  For example, the reliable DamagePlayer could instead be inferred from unreliable PlayerHP updates.  But sending these updates too often would be wasteful, while sending them too rarely would be a risk due to unreliability.


13. Closing Remarks

"Super IsoBomb" was successfully networked and significantly upgraded in other respects.  The protocol is secure, smooth, and low bandwidth.  But there is still room for further optimization, especially when the new version of RakNet is released.  And the game has not yet been stress tested for multiple clients.

Also, there is a good amount that could be added to the game as a whole that also relates to networking.  For example, an options screen could be added.  And a start-up Windows Form network connection and game type screen that edits the configuration file and then starts the game executable.

There is also room for non-networking features.  DirectPlay support would be especially cool for "Super IsoBomb".  Also, the levels could still use some further balancing and upgrading.  I have plans to add themes.  This is planned to be done with things like level-specific music, themed textures, and level backgrounds.

And now that the basics are in place, more networking features can also be added.  The lack of in-game chat is especially noticeable.

Finally, the new version needs to be cleaned up a bit and more thoroughly tested/debugged.  After this and possibly some other upgrades, it will be packaged to be released as the next official version of "Super IsoBomb".  This will be released at "www.rit.edu/~pem1491/IsoBomb/".




References - Research:

Sweeney, Tim.  Unreal Networking Architecture.  21 July 1999.  Epic Mega Games, Inc.  <http://unreal.epicgames.com/Network.htm>

Aranson, Jesse.  Dead Reckoning: Latency Hiding for Networked Games.  <http://www.gamasutra.com/features/19970919/aronson_01.htm>

Jones, Kevin.  Network middleware comparison.  30 May 2003.  GameDev.net.  <http://www.gamedev.net/columns/books/productreview.asp?productid=240>

Simpson, Jake.  Game Networking 101.  31 Jan. 2000.  loonygames.  <http://www.loonygames.com/content/2.10/feat/index.shtml>

Dickinson, Patrick.  Instant Replay: Building a Game Engine with Reproducible Behavior.  13 July 2001.  Gamasutra.com.  <http://www.gamasutra.com/features/20010713/dickinson_pfv.htm>

Lincroft, Peter.  The Internet Sucks: Or What I Learned Coding X-Wing vs. TIE Fighter.  3 Sept. 1999.  Gamasutra.com.  <http://www.gamasutra.com/features/19990903/lincroft_01.htm>

Adamns, Jim. Programming Roleplaying Games with DirectX. USA: Premier Press, 2002

Microsoft Corporation.  Microsoft Visual Studios .NET C++ Documentation

Microsoft Corporation.  Microsoft DirectX 9.0 C++ Documentation

gamedeveloper.net news.  9 Feb. 2003.  gamedeveloper.net.  <http://www.gamedeveloper.net/gdn/index.php?&page=viewresource&rid=1802>


References - Game Networkinig API Home Pages:

Rakkarsoft L.L.C.  <http://www.rakkarsoft.com/>

ReplicaNet.  <http://www.replicanet.com/>

Microsoft DirectX.  <http://www.microsoft.com/windows/directx/>

SDL_net.  <http://www.libsdl.org/projects/SDL_net/>

HawkNL.  <http://www.hawksoft.com/hawknl/>

GNE.  <http://www.rit.edu/~jpw9607/gne/>

Quazal.  <http://www.quazal.com/>


References - Super IsoBomb:

Winnebeck, Jason. Gillius's Programming - Super IsoBomb.  <http://www.rit.edu/~jpw9607/isobomb/>

Mowry, Peter.  Super IsoBomb.  <http://www.rit.edu/~pem1491/IsoBomb/>