BZRC: BZFlag Remote Control

I/O Tips

BZRC communicates over TCP. In some languages, networking code is extremely simple. However, in others it can be a little complicated. One option is to have another program do the network code (like Netcat), while your program simply reads and writes from standard input and standard output. We have written a UNIX-friendly script to set this up. However, there are still a number of issues to worry about.

antenna.sh

Download the antenna script: antenna.sh. The name reflects its purpose: it plugs in to your agent code and abstracts out the low-level communication with the robot tanks on the other end. Note that you will need Netcat (nc) installed to use the script. Call the script in the following manner:

antenna.sh localhost 3000 your-agent-program args to your program

Why Life Isn't Perfect

There are two major issues that still need to be resolved: blocking and buffering. Blocking is when a system call needs to wait before returning. If your program calls some sort of get_line() function, it will probably wait for a response before returning. There are probably things your program would like to do, but a blocking read will add delays and possibly cause serious problems. The second problem is buffering. For purposes of efficiency, the operating system usually waits to send data that you output, so that it can send in larger chunks. However, when you ask about the position of a tank, you want the answer now rather than when the outgoing buffer meets some threshold.

Blocking

There are three main ways to get around the blocking problem: threads, select, and non-blocking I/O:

Threads
If you are comfortable with threads, I would recommend spawning a thread to do all of the communication while your main thread handles the other logic. The communication thread can block as much as it wants.
Select (Polling)
Select is a UNIX system call that takes a list of I/O streams and blocks until any one of them is ready. It also allows a time-limit to be set (if no new data are available within x milliseconds, it will return anyway). When it returns, it reports which, if any, streams are ready to be read from.
Non-blocking I/O
System calls can set O_NONBLOCK on an input/output stream, which makes read either return data or return immediately with an error if no data are available.

Read the documentation for your language to decide which of the above works best for you.

Buffering

A great description of buffering on standard input and output is: buffering in standard streams. See also GNU C Library: Buffering Concepts. One solution is to call the following after each write:

fflush(stdout);

For output, it's probably ideal to use line-buffering, so that a flush happens automatically at each newline. The following sample code will set buffering on standard output to be line-buffering:

#include 
#include 

int main(int argc, char** argv)
{
    if (setlinebuf(stdout)) {
        fprintf(stderr,"setlinebuf() failed\n");
    }
}

Input buffering question: if we set input buffering to line-buffering, will select tell us that the file is ready for reading before a line is ready (and thus causing us to block when we don't want to)?