The next step: interactive games using CGI. In this multipart series, John and Robert provide a framework for writing your own CGI games.
by John W. Fawcett and Robert W. Jepson
As most of you are probably aware, the World Wide Web is expanding at an unbelievable rate. So many people have discovered that they can use the Web as a publishing and promotional tool that it's now possible to spend months at a time on-line and never visit the same site twice. Unfortunately, most of these sites are pretty dull. Static text with static pictures and static hyper-links are nice for sitting and reading information, but so is a trip to the library. What the Web needs is interactivity! Programs that actually do something, reacting to those button clicks--programs that give customized response depending on the user and the information the user supplies!
It is possible to provide interactivity for the Web. Using the Common Gateway Interface (CGI), programmers can create entire systems that make the user forget that they're operating within the restrictions of HTML and feel more like they're interacting with their own personal copy of your program.
Most CGI ``programming'' to date has been restricted to passing information from the web page to UNIX filters like perl and awk, where it is operated on to create a new page, and this new page downloaded. This approach is fine for simple tasks that don't require tracking information across multiple pages, but it falls apart quickly when you try to tackle more complex tasks. Pretty quickly, you get to the point where you find that your concerns for speed, unique requirements, or access to a database require that you create a custom program on the back end.
Fortunately, CGI can support custom programs. In fact, with careful programming, you can create the illusion that the user is actually interacting with your program the same way they interact with any program running locally on their machine. This illusion of interactivity requires careful design and some clever programming to pull off, though, since there are a number of rather serious restrictions to working with CGI over the Web.
This article will discuss some of the problems associated with creating interactive programs with CGI. We'll draw on our experience as designers and programmers on the Sierra On-Line game Internet Stock Market Challenge, which is an interactive, stock trading simulation game. If you want to take a look at the game, link to http://www.sierra.com and follow the link to the Stock Market Challenge game. We're going to assume that you're familiar with basic HTML and HTML forms. If you're not, there is an excellent reference at http://www.w3.org/hypertext/WWW/MarkUp/MarkUp.html.
One of the first steps in creating any program should be the design. In this, CGI programs are no different, but they have some special restrictions that need to be addressed at the beginning, and considered throughout the development lifecycle.
The first thing you'll need to do is design the pages that the user will see. For Stock Market, we used static HTML documents to let us get an idea of our page layout, the words we wanted to use on the page, and the features of the game we needed and were able to support. These static pages didn't actually do anything, but they let us (1) design the user interface and (2) get a handle on the paths the users might take through the system. When we started, we only had a few pages defined. By the time we got to actually writing the code, we had over 20.
You'll also need to remember that CGI only works with forms. Most of the forms you've seen to date have probably had a Submit button on them somewhere. That button was used when you'd finished filling in the form and wanted to send it to the host computer.
Buttons aren't the only submit device; Listing 1 shows the HTML code that produces Figure 2 (the page returned to the user which contains a bitmapped button bar). Listing 2 is the chunk of code that handles figuring out what section of the button bar was pressed. If you use the tag:
<INPUT TYPE=IMAGE SRC={.GIF file} NAME={bitmap name}>in your document, the user will send the form whenever they click in the bitmap. You can extract the X and Y locations of their click from the data passed into stdin from the HTTP server. This is the method we used to create ``button rows'' that allowed the user a great deal of navigational flexibility when using Stock Market Challenge.
When you select a link on your browser, what really happens? You know that when a link on a document is activated, either the entire document pointed to is sent directly from the server, or the program pointed to is started on the server. But what really happens on the server? The answer has a profound impact on your design. For the purposes of this discussion, we'll only deal with what happens when the link points to a program.
When you activate the link, a request gets transferred over the Internet to the TCP/IP service on the target computer (server). The TCP/IP service routes the request to the correct port (typically port 80), where the HTTP server is attached. The HTTP server examines the request and launches a copy of the program pointed to (your program), with the stdin and stdout for that program pointing to sockets that are connected to the client browser. Your program reads from stdin to get data and writes information to stdout. Assuming that the information written out is in standard HTML format, the results are displayed on the users machine. Your program then exits.
Once the HTTP service activates your program, it waits for another connection. Your program can run as long as it needs to without preventing other users from getting into the system with requests of their own. This means, of course, that you might have two or more copies of your program running at the same time.
Back in the old days, we called this Block Mode operation. If you're really old, you probably remember card decks as data input devices. You'd create your card deck, submit it for processing, then examine the results. This is actually pretty close to the way you're going to operate with CGI and the Web. Welcome to 1972.
Remember that your program exits after you write the page back to the user. How, then, do you create the illusion of interactivity? How do you know which user is sending which page to your program?
The answer is the HIDDEN
input type.
You can use this HTML tag to place information on the page that the
user never sees, but is passed to your program when the user activates
the form. Be careful about what you place in the hidden fields. You
need to place enough there to reconstruct the entire user context, but
you also want to keep the amount of information to a minimum to
prevent long transmission times. In Stock Market Challenge, every
page has at least 4 items. These items (the user ID, the challenge
ID, the number of the current page, and the number of the page to
return to if this page causes an error) are enough to get started
decoding the rest of the form. The main program for Stock Market
Challenge consists primarily of a switch statement based on the
current page ID. The routines called from that switch know what
information they expect to be on the page, and decode it
separately.
Think about the persistent information up front during the design period. What items are needed all the time? What items are common to groups of pages? What items are specific to only one or two pages? Remember to allow for error recovery and help. If there is an error, how do you get back to the page the user was on before the error occurred (after you tell them about the error, of course)? Your help needs to be specific to a page, and the user will expect to go back to the page that they caused the error on after they have read the help.
You might be tempted to place standard hyper-links for help and error
pages. Don't. Of all the browsers we've checked out so far, only the
most recent version of Netscape retains enough information to allow
the use of the previous and next arrow buttons for navigation with CGI
pages. All the rest seem to lose some critical information, meaning
that the user has to go back to the beginning after getting help.
Instead, treat help and error just like any other page--create them
on the fly and use the HIDDEN
input
type to store the critical navigation information.
The host platform for your program isn't as important as it used to be. Your code must read from stdin and write to stdout. The Stock Market Challenge code was first developed on a Linux system, then moved to a Windows NT environment, then moved to a mixed Windows NT/Sun environment. Because we stayed with standard C and C++ when we wrote the code, it moved around with very little modification. Choose the development environment that you feel most comfortable with. Write portable code. Do all the things that good programmers do. You shouldn't have any major problems.
The next major issue you'll face is how you get user information from the page. The answer is actually a lot simpler than you might think: name everything. When you create your form, give every input field, bitmap, and button a unique name. This way, you can tell what button or bitmap the user hit, retrieve any text they entered, figure out which selections they made, or which radio or check boxes were selected. You have a fairly complete set of input field types to choose from, and fairly good control over the appearance of each.
Within a form, any button or bitmap selection will cause the
transmission. The only way you can tell which button or bitmap was hit
(and where) is to see if the name of the item was passed to your
program. For this reason, use the NAME=
parameter in the
<INPUT TYPE>
tag to create a unique name for the item. You
can then use a query function (we'll get to that in section III, Basic
CGI Operations) to see if the item was selected and, if so, what its
value was.
Remember that the names are clear ASCII text and shouldn't contain any blanks. We also suggest that the names be meaningful, so you can figure them out next year when you go to revise your code.
You'll need to error check everything you read in. Data corrupted
during transmission, the user not entering a critical piece of
information, and a whole host of other problems can all cause your
program to violate the cardinal rule of C -- Thou Shall Not Follow The
Null Pointer. The code fragment we'll give you later in this series
that reads in the page information and returns it to your application
will return a NULL
if the parameter you ask for wasn't
present in the page transmission. You'll need to check this against
NULL
and generate an error page if
there was some problem. Like all other pages, the error page needs to
contain adequate recovery information so that you can get the user
back to the page that caused the error after they've viewed the error.
You'll also need to be careful about errors on your server. If, for
example, your program requires access to a disk file, then you'll need
to check the return from the open()
statement to see if the
file was in use by another copy of the program when you tried to open
it. The actions you take on these errors can range from simply
waiting until the resource becomes available, to generating an error
page and sending it back to the user. Again, error detection and
recovery should be considered carefully during the design phase.
Performance considerations should drive a lot of your design. You can't control the transport delay of the Internet, but you can minimize the impact on your users through careful design. The first and most obvious means is by limiting the number and size of the graphics you use in your program. We used a number of bitmaps in Stock market Challenge, but we made a significant effort to make sure that they were small and reusable whenever possible.
Another issue that can impact performance is the amount of data you ship across the Internet. If you use hidden fields, try to make sure that you're only sending down information that is pertinent to the page being displayed. Keep the names short but meaningful.
Finally, look at your server code itself and try to optimize it. Open files as read only, if you can, and close them as soon as you're done so that other copies of the program can get to them. Use optimized search and sort algorithms. Convert the strings that the HTTP server sends you to numbers, and operate on them that way internally. Many of us have gotten sloppy in the past few years as machines have gained power and speed. Pull out all your old ``good programming'' tools and use them again. Remember, the user is going to be waiting long enough for the transport delay. Don't make them wait longer for inefficient code.
John Fawcett and Bob Jepson are Senior Systems Programmers for Sierra On-Line, Inc., in Bellevue, Washington. They are the authors of Sierra's Web based Internet Stock Market Challenge game. Fawcett has been a programmer for over 15 years, with a background in Unix, data communications, and graphics. Jepson has been a programmer for over 20 years, with a background in business applications and DOS/Windows programming.