SiteExperts.com Logo

Inside Technique : Client-side Data Access
By Rajeev Hariharan

-> Go to the 2. part of this article "Sokoban Level Designer"

Beginning this month, SiteExperts.com will feature a new twice-monthly column on Web-based game design and programming. We hope to bring you a brand new full-featured game every month, along with the techniques and technology used in its implementation. It is our belief that the level of sophistication that most on-line gamers expect presents unique design challenges, whose solution would be of interest not only to game programmers, but to the entire Web development community as well. This column will focus only on Microsoft Windows Web technologies and their uses in solving some of these challenges.

In this article we will cover the data access technologies used in our implementation of the classic puzzle game Sokoban, which is scheduled for release exclusively on SiteExperts.com later this month. The game was written with certain design goals in mind:

  • It should have the look and feel of a commercial-quality executable (.EXE) game.
  • It should be playable both on-line and off-line.
  • While on-line, the emphasis should be on speedy game-play and a minimum of features.
  • While off-line, more advanced features like saving game state and recording high scores should be possible.
  • It should be playable on IE4.

The last goal was the biggest challenge - IE4 has a very strict security model which makes it difficult to modify, create, or find data stored on the client-side. We will discuss a variety of techniques that we used to make this possible including:

  • Using the Common Dialog Control for File Selection.
  • Using the Tabular Data Control for data retrieval and manipulation.
  • Saving changes made to the data on the client disk.

First of all, let us look at some common techniques used by web developers to enable the user to choose a client-side data file, the problems associated with these techniques, and how the Common Dialog Control offers an elegant solution to these problems.

Inside Technique : Client-side Data Access : Selecting a Client-side data file

In order to keep the Sokoban game open-ended, we decided not to hard-code the puzzle levels into the game. We chose a DOOM-like implementation, where the game levels are stored in an external data file, and the user has the option to design and add more levels by creating new data files. It follows then that one of the first decisions we were faced with was - designing the interface by which the user could select a game file locally.

At first glance this appears ridiculously simple, just add a File Input like this:

<INPUT ID='F1' type='file'>

But, seriously, have you looked at the File Input lately? It sits there, fat, gray and ugly on your carefully designed, color co-ordinated page. You could dabble in a bit of CSS and change it's appearance, but you'd rather not have a button anyway. And definitely not the textbox associated with it. A link with a cool hover rollover, maybe?

That's easy. First you hide the File Input by dumping it in some godforsaken area of your screen, like this:

<INPUT ID='F1' type='file' style='position:absolute;top:-200'>

Then you add your cool link, which calls the Click method of the File Input:

<A HREF='javascript:doFileOpen()'>Open File</A>
<SCRIPT LANGUAGE='JavaScript'>
function doFileOpen(){
F1.click();
alert(F1.value==""?"You did not choose a file":"You chose "+F1.value);
}
</SCRIPT>

See this script in action by clicking the link below.

Open File

Ok, so that solves the look problem, but you notice some usage problems here. The first is that the Open File dialog pops up in a random directory, not necessarily the directory from which the user started the game, where the game data files are stored. The next is that by default, the filter in this dialog is set to 'All files(*.*)', whereas you would prefer that filter to be 'Level Data files(*.lvl)' , which happens to be the default extension of your data files. This basic fuctionality would be expected of even the simplest of executable programs. If you're doing cross-browser/OS work, I'm afraid you're stuck with these problems. If, however, you are developing on IE/Windows, you have an option. It's called the Common Dialog Control.

Inside Technique : Client-side Data Access : Using the Common Dialog Control

Windows defines several dialogs which can be used by programs for common tasks like opening or saving a file, selecting a font or color, or printing a document. The code for these dialogs resides in commdlg.dll in the System directory, but this file cannot be accessed by your browser. Microsoft has however provided an ActiveX control which acts as a wrapper for this file, and this control is accessible by means of a scripting language.

The Common Dialog Control can be added to your page by using the following tag:

<OBJECT ID="CommonDialog1" WIDTH=0 HEIGHT=0
 CLASSID="CLSID:F9043C85-F6F2-101A-A3C9-08002B2F49FB">
</OBJECT>

In order to show the Open File dialog, we use this code:
(Note that we are using VBScript here, I will explain why in a moment.)

CommonDialog1.ShowOpen

The main advantage of using this OCX is the control that you have over the appearance of the dialog. You could change the title of the Dialog by setting the DialogTitle property, and the initial directory displayed in the dialog (your game directory, most likely) by the InitDir property. What’s really interesting, however, is that you can set the Filter property to define which types of files are displayed in the dialog. The Filter property is a string of descriptions and filters, separated by the pipe character. It looks something like this:

Filter='description|filter|description|filter|...'

In order to detect the file that the user selected, you can either use the FileName property, which contains the full path of the selected file, or the FileTitle property, which contains only the file name without the path.

So now let us look at the full implementation of our Open File procedure using the Common Dialog control:

<OBJECT ID="CommonDialog1" WIDTH=0 HEIGHT=0
 CLASSID="CLSID:F9043C85-F6F2-101A-A3C9-08002B2F49FB">
    <PARAM NAME="CancelError" VALUE="1">
    <PARAM NAME="DialogTitle" VALUE="Select Data File">
    <PARAM NAME="Filter" VALUE="Level Data Files (*.lvl)|*.lvl|All
    Files (*.*)|*.*">
</OBJECT>

<A HREF="javascript: LoadFile(location.pathname.substr(1));">
Load Data File</A>

<SCRIPT LANGUAGE="VBScript">
Sub LoadFile(GameDir)
On Error Resume Next
CommonDialog1.InitDir=GameDir
CommonDialog1.ShowOpen
If Err.Number=32755 Then
Err.Clear
On Error Goto 0  ‘ Restore Default Error Handler
Exit Sub
End If
On Error Goto 0
MsgBox "You chose "+CommonDialog1.FileTitle
End Sub
</SCRIPT>
;

See the script in action by clicking on the link below.

Load Data File

The first thing you will notice is the parameter CancelError which is set to the value '1'. The explanation for this is as follows: If the user clicks on the Cancel button, the FileTitle property will still hold the name of the selected file. (This is unlike the File Input object where, if the user clicks Cancel, the Value is set to a null string). If the CancelError property is set to '1' (it is '0' by default), the ActiveX object will raise an exception in it’s parent document, namely your web page.

Unfortunately, prior to version 5, Jscript had no built-in error handling capabilities, and IE's window.onError() handler is - how shall we put this nicely - inconsistent. This means that if the user does click the Cancel button, an ugly Jscript error dialog will pop up. However, VBScript does have error handling capabilities. The On Error Resume Next statement will prevent this dialog from being popped up, and is generally used to define custom error handlers. The On Error Goto 0 statement restores the default error handling of the page. This is why the LoadFile procedure is written in VBScript.

Moving on, we see that the LoadFile routine is called with the pathname property of the location object, with the initial '/' character stripped off (that’s what the substr(1) is for). This property contains the directory from which the page was loaded without the preceding protocol string ('http:/' or 'file:/').

The first line after the ShowOpen call checks if an error happened by examining the VBScript Err object. If it’s value is set to 32755 (cdlCancel), it indicates that the user has pressed the Cancel button, so the routine exits after resetting the Err object. If not, the routine restores default error handling and displays the file name of the selected file.

That’s all for the Common Dialog control for now. It may look a bit complicated, but it adds a professional touch to your page. A final word of warning: if you mix VBScript and Jscript on your page, never forget to use the LANGUAGE= attribute of the SCRIPT tag, or you risk confusing the scripting engine, resulting in weird and esoteric error messages like the following:


Fig 1: A 'Zen' error message

Next, we take a look at how the Tabular Data control considerably eases the task of managing structured data.

Inside Technique : Client-side Data Access : Using the Tabular Data Control

Now that the user has selected the file, the next step is to open and read it. Since the game data file, despite it’s extension of .lvl, is essentially a text file, we decided to use the Tabular Data Control to manipulate the data stored in the file. Originally released with IE4, the TDC remains even today the simplest and most efficient means to access tabular data, and more importantly, it works seamlessly in both on-line as well as off-line mode.

Those of you who are unfamiliar with this control, or need to jog your memories a little, could take a dekko at the official Microsoft TDC overview page, or the more comprehensive TDC reference. SiteExperts.com has covered advanced usage of this control in the Data Binding section. Therefore, rather than covering this extemely useful control from scratch, we will look at how it was used in the Sokoban game.

The biggest advantage of the TDC is that it allows both sequential and random access of data. The standard sequential access loop looks like this:

TDC1.recordset.moveFirst() ;
while (!TDC1.recordset.eof) { 
      // Do your sequential processing here 
     TDC1.recordset.moveNext();
     }

To randomly access a record based on it’s position within the recordset, set the absolutePosition property of the recordset like this:

TDC1.recordset.absolutePosition=n

Now for the interesting part. The TDC is signed as safe for scripting because it cannot write data back to the data file. This does not mean that the TDC recordset is readonly, however. You can alter the value of individual fields like this:

TDC1.recordset.Fields(n).value='my data'

You can also insert and delete records by using the AddNew and Delete methods of the recordset. All changes made to the TDC’s recordset remain in a local cache, and therefore last as long as the page is displayed in the browser. The moment the user moves to another page - boom - all changes are lost.

Which is a big problem, because we wanted to include several features which would require that changes to the data be preserved across page accesses. In the next section we will look at the approach we used to preserve the changes made to the recordset, by writing these changes to the client disk. It is, unfortunately, not the most elegant solution, but it happens to be the only safe approach for IE4.

Inside Technique : Client-side Data Access : Saving Changes to disk

It is a fallacy that a Web page cannot write to the client hard disk. ActiveX controls embedded within the page are permitted to do so, but the price they pay for it is of being signed unsafe for scripting. What that means is the user will see this message when the control is initialized by the page :


Fig 2:A nice way to scare the pants off your user

At first glance, this looks like the ideal approach if one has to save data on the client disk. Use a signed, though unsafe control, and let the users decide if they want additional features (which require access to the client disk), or safety. Unfortunately, it doesn't quite work.

IE provides users with a variety of security options, based on Current Zone (Internet, Intranet, Local) or technology (Script, Java VM). What this means is that a page author has no guarantee whether a user will even see the security dialog, let alone respond to it positively.

A less than ideal option would involve the use of cookies. These little tidbits are designed to store and retrieve information from the client disk, but they have too many restrictions applied to them. To begin with, a cookie cannot be longer than 4 KB (4096 bytes), a page cannot hold more than 20 cookies, and, worst of all, even if you set an expiry date, cookies can be randomly deleted by the browser if it finds it is running out of storage space. Cookies also have the problem of failing silently, just setting a cookie is no guarantee that you will be able to retrieve it in the future.

The least sophisticated option also happens to be the most robust one, and is guaranteed to work regardless of the user's security settings, or custom browser installation. This approach transfers the burden of saving the data to the user. You would implement such an approach like this :

function dosave(){
newwin=window.open('','','scrollbars=yes,status=no');
newwin.document.open("text/plain");
for(i=0;i<TDC1.recordset.Fields.count-1;i++)
  newwin.document.write('"'+TDC1.recordset.Fields(i).name+'",');
newwin.document.writeln('"'+
     TDC1.recordset.Fields(TDC1.recordset.Fields.count-1).name+'"')

TDC1.recordset.MoveFirst();
while(!TDC1.recordset.eof){
  for(i=0;i<TDC1.recordset.Fields.count;i++){
     FldValue=TDC1.recordset.Fields(i).value;
     NotNumericFld=isNaN(FldValue);
     if(NotNumericFld) 
        newwin.document.write('"') 
     newwin.document.write(FldValue);
     if(NotNumericFld) 
        newwin.document.write('",') 
     else
        newwin.document.write(',') 
	  
     if(i==TDC1.recordset.Fields.count-1)
        newwin.document.writeln('')
	}
  TDC1.recordset.moveNext();
  }

newwin.document.close();

}

What this code does is pop up a window, then open a text channel to the new window, then dump the entire contents of the TDC to it. Obviously, before the window is opened, the user has to be told to right-click the document, choose View Source, and when Notepad pops up, choose the File->Save As menu item to save the file as a text file in the appropriate directory. Extremely clunky, yes, but as we mentioned before, the only safe way to ensure that the data changes are written to disk.

As a demonstration, we have created a data file which you can alter using the functionality of the TDC. When you attempt to exit this page, you will be asked if you wish to save the changes to the TDC. If you answer in the affirmative, you will see the above scipt in action. Use the gray buttons above the textboxes to manipulate the TDC's data.


World's Best Comic Performances
Performer Rating

Performer :  
Rating :       
Comments :

Till the next column then, which will be the final release of the Sokoban game... In the meantime, be sure to read Rajeev's first article that demonstrates recursion to create an interactive maze game.