THE INSIDE OF NETWORK GAMES =========================== About two years ago I released a network-game for the 8-bit Atari called MULTI DASH. Since that time, several people asked me for informations or ready routines useful for writing more such games, but I wasn't able to help them: Although I answered a lot of questions, and although I even gave a part of MULTI DASH source text to a certain man, it seems that no one was able to use it. There are lots of things around to explain, and unfortunately also the networking system of MULTI DASH was not designed for use in any other games. This year, I've written another network-game called MULTI RACE, and this time I didn't forget about you... The new improved networking system used in this game is relatively universal, and I'm now releasing it in the form of a simple example-game called MULTI WORMS (16 hungry worms eating apples), with full commented source texts. But however, there are still some more things to explain - and that's why I'm smashing my keyboard now... Well, first let me say some characteristics of a good Atari 8-bit network-game. Of course, it should be a game working on more interconnected computers, allowing several people to play together. But beside of that, the game should be also fully playable on a single computer, just like any other Atari 8-bit game - connecting is just for very few computer-meetings, but we want to have the game more useful. As for hardware requirements, the game should work on a plain 64kB machine, and be somehow playable also from keyboard. Why? Just imagine a typical computer-meeting: 9 computers in a room (only 3 with big RAM), and only 4 joysticks... In such a situation, the little requirements are quite an important thing. The game should also transfer itself into the other computers without of need to run around with a floppy disk. The networking system should be somehow "idiot-proof", ie. it should overcome a wide range of errors without of any fatal interference to the other players' game, and with no need of user's intervention. (The errors I'm talking about may look like: Switching terminal-systems on/off, pressing RESET, disconnecting computers, trying to start another network-games, accidentally disconnecting the electric-power to a half of room, data-losses caused by weak network-hardware and dirty connectors, etc. etc. - all in any possible moment...) ...And beside of all that, the game should be funny, interesting and playable! (This is allways the most difficult part.) So, now it's almost the time to look at the network-game's inside - but first I must say one thing: It's impossible to patch into an existing game to get a network version! A network game must be written in a special way from its begin to end, otherwise there's no chance... SERIAL LINE ON ATARI To understand how it all works, we must begin with some theory about serial data-transfer. We're ofcourse talking about network games for the hardware originally called GAMELINK2, which is actually a simple line connecting together the serial ports of all computers, so that any data sent from any computer will come to all others. Fortunately, we don't have to take care about exact timing and collecting single serial-bits together, because Atari already contains a powerfull hardware for this task inside the POKEY chip (although it's not so well-known like PMG or Display-List). It uses the standard format of serial data supported also by most other computers around this world: (no data) (Start) (data-bits) (Stop) (1111111) 0 x x x x x x x x 1 Each data-byte begins with one zero-bit called "Start", which is used (its begin) to synchronize the receiver. Then 8 data-bits are sent at a previously-known speed, and follows one "Stop" bit to provide high-state before another Start-bit, and also to leave some time for data-processing. The POKEY chip have several different modes for serial data-transfer (synchronic/asynchronic, timing from different sound-channels or from an external clock, frequency-modulated output etc.), but all we need is sending and asynchronic receiving with timing from sound channel 4. We must switch sound channels 3 and 4 to a 16-bit counter with 1.79MHz clock to get exact timing, and set the division-value (AUDF3,4) to the time of half a serial bit (counted in clock cycles). Processing serial data via POKEY is then quite simple: After setting the necessary sound- and serial-mode, we only need to write a byte at SEROUT ($D20D), and POKEY will send it as soon, as the previous byte is finished. When it's the time to write another byte, an IRQ interrupt come - and if no byte was written, another IRQ indicates the end of transmission after a while. Receiving is similar: POKEY reads data from the line, and once it have a whole byte, IRQ tell us to read it from SERIN ($D20D). (We must also check some error flags in the status register.) In the networking system for a game, we'll also need to detect end of data-block. For this purpose, POKEY have one almost forgotten bit in the status register, indicating whether the serial receiver works or not. Since the serial-input-IRQ is allways coming at the time of Stop-bit, we only have to wait the time of two serial bits, and then read this status-bit. When it's active, another Start-bit was found, otherwise it's the end of block. A complete description of POKEY's serial I/O functions would be too long for this article, so look into the source text (or better into some books) for details... LIVING WITH SERIAL I/O While the serial data-transfer works, there are some special limitations to the game-program (it's somehow similar to demo-effects at the time of loading from a disk drive). At first, the program may not play with the interrupts too much, because each approx. 940 clock cycles (for standard speed of transmission) comes an important IRQ - so the other interrupt routines must be short, and ofcourse we may not disable IRQ. The POKEY's sound registers are shared between the serial transfer, and game sounds. Since the channels 3 and 4 are used for I/O timing, we have only the other two channels (1 and 2) left for making sounds. We can't use the STIMER ($D209) register to start counters, and while writing to AUDCTL ($D208), the bits 3 and 5 must be allways set to keep the proper mode of I/O timing. The last limitation is a bit unexpected, and also most difficult to overcome. I didn't have a chance to examine all the details, but it's quite sure that a hardware-bug inside the 6502 causes VBI and DLI interrupts to be delayed or even lost! The problem occurs in the situation, when both the interrupt-signals supported by 6502 (NMI and IRQ) come at the same time. Depending on the phase and length of both the signals, NMI may be lost, or executed much later (DLI may "fall" even two scanlines down with a heavy IRQ jam). This 6502-bug is mostly unimportant, because normally the only rare IRQ is coming from keyboard, and during the disk operations a lost VBI is no problem (although I already succeed to crash a certain music-menu full of DLI's just by pressing a key), but while DLI's or exact VBI-timing run together with the serial I/O, the probability of troubles is much higher... (Have you already seen strange flashes of incorrect colors on a nice "loading" screen, while the disk drive works? That's it!) How to overcome this problem? We must write the interrupt routines in a special way. At first, the length of IRQ-signal activity must be minimized. Normally POKEY keeps the signal active quite long, but fortunately we can stop it by clearing IRQEN ($D20E) to zero as soon as possible. Shorter signal means lower probability of troubles. (If we'll be lucky to have IRQ shorter than NMI - which seems to be about 24 clock cycles long - the problem of lost NMI is probably solved; but this is mostly impossible.) While writing the NMI routines, we must remember that any NMI may be ignored or delayed. While with VBI mostly no fatal problem occurs, incorrect DLI's are quite a visible problem. The best way is to avoid the use of DLI's at all, but there are also other things you can do: The first very useful thing is one STA WSYNC ($D40A) instruction at the begin of NMI routine - mostly it will synchronize the routine correctly, no matter whether it was a little delayed or not. When there are more DLI's on screen, the decision which one is currently executed should be based one and only on the VCOUNT ($D40B) register. VCOUNT allways shows the correct Y-position, while methods like counting number of DLI's executed or changing DLI-address by the previous routine may fail when one DLI is lost. The last trick is quite simple: Leave some free space between your screen-windows, and put double DLI's into the gaps (when one is lost, the other works). This is probably the only safe way to really overcome the problem of lost DLI's, but unfortunately it's sometimes impossible to have gaps between the screen lines... (Note that also VBI-setting of screen colors etc. should be doubled by a DLI somewhere at the top or bottom of screen.) THE GROUNDS OF GAME-NETWORKING Now let's look at the principles of a game working on more computers together. At first, we'll divide the operations executed inside a typical game-program into two groups: Moving players, and the graphics/sound presentation. In this chapther, we'll talk about the first group only, since the presentation (re-drawing screen, playing music, screen-animation etc.) is just a local job with no output back to the network. The most important idea is: Every operations affecting the game-situation must be executed EXACTLY the same way on all the computers!!! This means, that the routines for moving players and other such operations must be executed allways in the same order, and should use ONLY the input data transferred via network - so forget about reading of local random numbers, sticks, keyboards or timers directly from your game-action code! Although we have more computers working simultaneously, it's impossible to do more operations at the same time - it would lead to different results on different computers, meaning the game's death. (Unless you're going to solve a very complex problem of transfering and re-compiling the whole game-situation after each game-step). In other words, a network game should virtually work just like any other game: One player's move, then another, then for example a new object, etc. etc. - never two things simultaneously. One of the ways is to distribute the operations between the computers, ie. each one executes its part, and sends the results (game-situation update) to the others. This is how MULTI DASH works, but it's not a good way for making an universal networking system - there are lots of game-specific things to transfer. Another method is to run the full multi-player game on all the computers, transfering only the state of all joysticks in the network. It might look like a crazy idea, but actually it's better: All the game-operations must be executed one-after-one anyway, no matter which computer(s) execute it, but now we have less data to transfer (the transfer is allways the slowest thing). This is the system used in MULTI RACE and MULTI WORMS. It's also very important to have a really safe data-transfer, because any data-loss may also cause differences in game situation - and as I've said already: It's the game's death. So we should NEVER just send some bytes, and assume that the other computers got it... We should allways send data to one and only computer - with block-type, address, and checksum - and wait for an answer. When no answer came, we should try again after a certain time, and when it wasn't successful several times, the computer should be removed from the network. (Ofcourse each computer in the network must have an unique address!) This is probably the only safe method, and actually the same system is running on your Atari everyday - while working with a disk drive or other SIO-peripherals. It's also necessary to have some block-start or block-end identificator which may not occur inside a data block; otherwise we may not be sure whether we've found a new game-networking block, or just a random sequence inside some boot-sector data. Since the game-network have no extra wire for a "block-start" signal (like the COMMAND line used between Atari and common peripherals), we must find an other way - and the solution is rather simple: When there is a gap between data bytes, it's the end of block. The easiest way to handle all the network-operations, and to overcome problems like disconnecting computers, is to have one "Master" system taking control over the whole network, ie. all communications should be done from/to the Master. In addition, in all known Atari network-games the Master also emulates a disk drive, allowing the other computers to boot the game software via network. Then it's clear who is who: The first computer (booted from a disk) is the Master, and the others (booted from this Master) are Terminals.