asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Game Programming with ASP
By Rod Denisyuk
Rating: 4.1 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction

    The very powerful and extensible ASP-based multiplayer game engine we write in this article is not for the 3-D shooter, but it will suit almost all turn-based desk games. For example, to apply it to a card game or the game of chess, all you need to do is add specific logic and design. “Engine” is an abstract thing, so we will use a tic-tac-toe game since it has very simple logic and will allow us to pay more attention to the engine. In bonus code for download you will find the more complex game “Sea Battle,” which is very famous in the Ukraine. Sea Battle uses nontrivial logic, implemented in a server-side component written with VC++.

    ASP is not just the glue of ActiveX objects and databases but independent server-side web applications as well. This article will show the power of VBScript programming language and more. This engine does not use any ActiveX objects, JAVA applets, DHTML, databases, and transactions – just pure ASP programming. Of course, you probably will use all of the above techniques in your application to implement logic and design.

    For example, logic is not trivial in the game of chess and will require server-side ActiveX object(s) written with more powerful tools; movement of figures on the board will also require some kind of client-side scripting and/or ActiveX/Java applets. But, remember, client applet(s) support is not a feature of all browsers. Good ASP applications should work with most browsers.

    The six steps involved in developing this application are:

    1. Global variables, start page, game preparation manager, and passive players’ room. (Almost 1/3 of our application will be written here.)
    2. Active player’s room and main logic manager.
    3. First implementation of “Game Over” page.
    4. Aborting the game
    5. Protection from malicious users.
    6. Additional features: showroom, advanced design, etc.

    Step 1 : Global variables (global.asa)

    We will store all global variables in an Application object and, therefore, initialize them in an Application_OnStart event in global.asa. User-specific parameters will be stored in a Session object and initialized in a Session_OnStart event.

    First we will define the players. There are 2 players in the tic-tac-toe game – the first plays for “0” and the second for “X.” We define two arrays – the first for player nicknames and the second for unique IDs, and initialize them to empty/null values. We will use these unique IDs for validation purposes - SessionID will suite fine.

    
    
    dim plIDs(2)
    plIDs(1)=0
    plIDs(2)=0
    	
    dim plNames(2)
    plNames(1)=""
    plNames(2)=""
    
    
    
    Now we need a game board. In tic-tac-toe it’s a field divided by 9 cells, each of them can be either “0,” “X,” or empty. We will use integer values –1 for “0”, 1 for “X,” and 0 for an empty cell.
    
    
    dim field(3, 3)
    For i = 0 To 2
    For j = 0 to 2
    		field(i, j) = 0
    	Next
    Next
    
    
    
    We also need visual interpretation for these values. At this point we will use text and replace text with images at step 6.
    
    
    dim picts(3)	
    picts(0) = "0"
    picts(1) = "_"
    picts(2) = "X"
    
    
    
    Note:
    Be very careful when you store arrays in Application and Session objects. You can’t access the element of the array directly from these objects, like Application(“field”)(2). Instead, you should use following code for storing
    
    
    Dim myarr(10)
    myarr(2) = “hello”
    Application(“field”) = myarr
    
    
    
    And for reading
    
    
    Dim myarr
    myarr = Application(“field”)
    Response.Write(myarr(2))
    
    
    
    In reference to the above note, we will store these arrays with following code:
    
    
    Application("field") = field 
    Application("plIDs") = plIDs
    Application("plNames") = plNames
    Application("pictures") = picts	
    
    
    
    Now we need variables to store the game state. We will use the following two variables:

    1. First, Application("globalstatus") used for game preparation. It shows if there are vacant places for players to join the game. Tic-tac-toe game can be started only when 2 players have joined the game.
    2. Second, Application("gamestatus") used to control game flow. It determines the active player.
      
      
      '  0 - no game ;
      '  1 - 1 player waiting;
      '  2 - game is in progress
      Application("globalstatus") = 0 	
      	
      ' 0 - not ready; 
      ' 1 - first player turn;
      ' 2 - second player turn;
      Application("gamestatus") = 0 	
      
      
      
    We will also store some user-specific information in the Session object. First, let’s store the player nickname. The default nickname will be “unknown.” Initialization takes place in the Session_OnStart event.
    
    
    Session("nick") = "unknown"
    
    
    
    The theory of turn-based multiplayer games is there is one active player and one or more passive players. These passive players wait for the active player to complete his turn. Upon completion, the game’s logic manager decides whose turn is next. Usually passive players can watch the playing field but can not make changes. In web applications we use client ActiveX/Java applets or server push/client pull technologies to determine when the state of the game was changed. This engine is designed to avoid client applets, so we will use client pull. “Client pull” means that the browser refreshes (reloads) the same page every n seconds.

    So the second useful user-specific parameter is refresh time (default value will be 5 seconds):

    
    
    Session("refresh") = 5
    
    
    
    Note
    There is also another important user parameter – Session timeout. This parameter is very useful for our game since it is the only way to determine when a user has left the game (whether user broke the connection or simply browsed to another site). You can control this parameter either via the Microsoft Management Console (MMC) or the Session.Timeout property (in minutes).
    We will add code for Session_OnEnd event later.

    Start page (default.asp)

    On the start page we will ask the user for his/her nickname and refresh time. On submitting the form, our application:

    1. Initializes user-specific parameters;
    2. Checks for a vacant place and, if found, adds a user to the player list, updates Application("globalstatus"), and redirects to our game preparation manager:
      
      
      <%
      dim globstat
      dim plIDs
      dim plNames
      
      if Request.Form("nick") <> "" then
      	Session("nick") = Server.HTMLEncode(Request.Form("nick"))
      	if IsNumeric(Request.Form("refresh")) then 
      		If CInt(Request.Form("refresh")) > 0 Then
      			Session("refresh") = CInt(Request.Form("refresh"))
      		End If
      	else
      		Session("refresh") = 5
      	end if	
      	
      	if Application("globalstatus") < 2 then
      		Application.lock
      		
      		globstat = Application("globalstatus")+1
      
      		plIDs = Application("plIDs")
      		plIDs(globstat)=Session.SessionID
      		
      		plNames=Application("plNames")
      		plNames(globstat)=Session("nick")
      	
      		Application("plIDs") = plIDs
      		Application("plNames") = plNames
      
      		Application("globalstatus") = globstat
      		Application("gamestatus") = 0
      				
      		Application.unlock
      		Response.Redirect ("manager.asp")
      	
      	end if
      end if
      %>
      
      
      
      Note
      We use the Server.HTMLEncode function to encode the player name and avoid problems with names containing HTML tags and special symbols like ‘<’ and ‘>’.
      The start page also shows the current game state so the user can see if there is a vacant place to join the game.
      
      
      <%
      plNames=Application("plNames")
      Select Case Application("globalstatus") 
      Case 0 Response.Write(" no players")
      Case 1 Response.Write(" 1 player waiting - " & plNames(1))
      Case 2 Response.Write(" game is in progress - " & plNames(1) & " vs " & plNames(2))
      End Select
      %>
      
      
      

      Figure 1

      Game preparation manager (manager.asp)

      The game preparation manager is the place where players wait for each other before the game starts. In our case, the game will start only when 2 players have joined. Here we can see the first example of “client pull” technology. We use the META HTTP-EQUIV="Refresh" tag with a parameter from the Session("refresh") variable.

      
      
      <META HTTP-EQUIV="Refresh" CONTENT="<% = Session("refresh") %>; URL= manager.asp">
      
      
      
      Players will reload the same page until a minimum number of players are reached. It is monitored via Application("globalstatus"), which is changed in default.asp by new players. When this number is reached, the game manager prepares the game field and selects active player via the Application("gamestatus") variable. After this, all players are redirected to passive players’ room (wait.asp). Don’t worry – active player will be redirected to its room later.
      
      
      <%
      if Application("globalstatus") = 2 then
      
      if Application("gamestatus") = 0 then
      		Application.lock
      		Application("gamestatus") = 1
      		dim field(3, 3)
      		For i = 0 To 2
      			For j = 0 to 2
      				field(i, j) = 0
      			Next
      		Next
      		Application("field") = field
      		Application.unlock
      	end if
      
      	Response.Redirect("wait.asp")
      	
      end if
      %>
      
      
      

      Figure 2

      Passive players’ room (wait.asp)

      Once again, passive players can watch the playing field but can not make changes. Our playing field is a 3 rows by 3 columns table, each cell of which contains either a “0” or “X” mark or is empty. We will draw this table based on the Application(“field”) array. This is the array of integers (the visual equivalents for these numbers are in array Application("pictures")). This will allow us to replace text with images easily.

      
      
      <!--#include file="utils.asp"-->
      <%
      dim plNames
      plNames=Application("plNames")
      
      dim field	
      field = Application("field")
      
      dim pic
      pic = Application("pictures")
      
      dim face
      
      if checkwho(Session.SessionID)=1 then
      	face = 0 ' "0"
      else
      	face = 2 ' "X"
      end if
      		
      %>
      
      <TABLE BORDER=1 ALIGN="Center" CELLPADDING=0 CELLSPACING=0>
      <%
      For i = 0 to 2
      	Response.Write("<TR>")
      	For j = 0 to 2
      		Response.Write("<td width='20' height='20' " &_
      			align='CENTER'>" & pic(field(i,j)+1) &_
      			"</TD>")
      	Next
      	Response.Write("</TR>")
      Next
      %>
      
      
      
      In the above code we’ve used the utility function checkwho(). This is a small but very important function that helps determine from which player a request came. With this function we can see whether this player plays for “0” or for “X,” or if he is not a player at all. The implementation of this function is very simple – it just compares parameter (usually Session.SessionID) with values in the Application(“plIDs”) array. This function is located in utils.asp. We include this file to wait.asp with SSI.
      
      
      <SCRIPT LANGUAGE=VBScript RUNAT=Server>
      ' 1 - first player; 2 - second; 0 - unknown
      Function checkwho(sessionid)
      	dim plIDs
      	plIDs = Application("plIDs")
      
      	if sessionid = plIDs(1) then
      		checkwho = 1
      	elseif sessionid = plIDs(2) then
      		checkwho = 2
      	else checkwho = 0
      	end if
      End Function
      </SCRIPT>
      
      
      
      On the wait.asp page we also show the names of players and tell the user that it’s his opponent’s turn
      
      
      <TITLE>
      <%
      	Response.Write("Wait - game is in progress - " &_
      		 plNames(1) & " vs " & plNames(2))
      %>
      </TITLE>
      Hey <B><%= Session("nick")%></B> it's
      <font color=Red>your opponent’s</font> turn !
      You play for <%= pic(face) %><br>
      
      
      
      And finally this is a "wait" page. This means that a player wait here for another user , so we need “client pull” again.
      Note
      Client pull with the META HTTP-EQUIV="Refresh" tag has one disadvantage. The browser refreshes the page exactly after a specified period, even if the page was not downloaded completely. In this case (if your page has rich design or the user has a slow connection), the player will not see the whole page. To avoid this problem we can use a small sub on the client JavaScript. We will use onLoad event of BODY. This event is fired only when the whole page was received. Of course, this solution requires support of the JavaScript from the browser side. If you are not sure that the browser supports it, use the previous method.
      Referring to the above note, we will add following code to the wait.asp:
      
      
      <script language="JavaScript">
      function GotoUrl(url)
      {
      	parent.location=url;
      }
      function TimeUrl(delay, url)
      {
          setTimeout('GotoUrl("'+url+'")',delay*1000);	
      }
      </script>
      <BODY  onload="TimeUrl(<%=Session("refresh")%>, 'wait.asp')">
      
      
      
      For more complicated games with rich design and large playing fields, it’s recommended to turn on buffering:
      
      
      Response.Buffer = TRUE
      
      
      
      Well done. We have completed the first (and most difficult) step of our application. You can test it by trying to start a new game and looking at how the application waits for players, how the passive players’ room is displayed and reloaded, if other users can be seen at the start page, what would happen if they try to join the game after it was started. But be careful. At this step our application is not protected from incorrect use. Manual accessing of pages different from default.asp my cause it to work incorrectly.
      Note
      Don’t forget that this application uses global.asa, which means that if your application resides more than one level from the root, you should open properties of this folder in MMC and press the “Create Application” button.
      Note
      ASP distinguishes users and tracks state via cookies. So if you want to play such multiplayer games from the same computer (i.e., for testing purposes), then:
      1. With Win NT IE, start new instance of browser instead of using "open new window"
      2. With Win 95 IE, try to use different host names / IPs (i.e., www.mysite.com <-> ?.?.?.? ; localhost <-> 127.0.0.1 <-> mycomputername)

      Figure 3

      Step 2 : Active player’s room (yourturn.asp)

      Active player is the player whose turn it is now. In our game this player should select an empty cell and place his/her sign (either “0” or “X”) there. Our active player’s room shows the same playing field as the passive players’ room, with few exceptions:

      1. Clicking on empty cells will call our main logic manager (recalc.asp) with the player’s choice as the parameter.
      2. The active player’s room doesn’t reload itself . There is only one active player in our engine, and nobody else can change the playing field.
        
        
        <!-- #include file = "utils.asp" -->
        <%
        ...
        dim plNames
        plNames=Application("plNames")
        
        %>
        
        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
        <META HTTP-EQUIV="Pragma" CONTENT="no-proxy">
        <HTML>
        <HEAD>
        	<TITLE>
        <%
        Response.Write("Your turn - game is in progress - " &_
        plNames(1) & " vs " & plNames(2))
        %>
        </TITLE></HEAD>
        <BODY>
        <%	
        	dim field	' your
        	field = Application("field")
        
        	dim pic
        	pic = Application("pictures")
        
        	dim face
        	
        	if checkwho(Session.SessionID)=1 then
        		face = 0 ' "0"
        	else
        		face = 2 ' "X"
        	end if
        %>	
        Hey <B><%= Session("nick")%></B> it's
        <font color=Green>your</font> turn !
        You play for <b><%= pic(face) %></b><br>
        
        <TABLE BORDER=1 ALIGN="Center" CELLPADDING=0 CELLSPACING=0>
        <%
          For i = 0 to 2
            Response.Write("<TR>")
            For j = 0 to 2
        	Response.Write("<td width='20' height='20' " &_ "
        		align='CENTER'>")
        	if field(i,j)=0 then
        		Response.Write("<A HREF= 'recalc.asp")
        		Response.Write("?row="&i&"&col="&j&"'>" &_
        		pic(field(i,j)+1)&"
        		Response.Write("</A></TD>")
        	Else
        		Response.Write(pic(field(i,j)+1) & "</TD>")
        	end if
        
            Next
            Response.Write("</TR>")
           Next
        %>
        </TABLE>
        </BODY>
        </HTML>
        
        
        
        As you can see the code is very similar to the code from wait.asp. Blank cells are links to the game logic manager with cell coordinates (row and column) as parameters.

        Figure 4

        Main logic manager (recalc.asp)

        The main logic manager is the place where our application controls game flow. It modifies the playing field, updates internal state variables, chooses new active players, and, at last, determines the winner/looser. This page is called from the active player’s room, with parameters determining player’s choice. In our game these parameters are the coordinates of the cell. Using this information, the manager updates this cell or playing field with a mark identifying from which player the request came (it uses our checkwho() utility function).

        
        
        <%
        
        who = checkwho(Session.SessionID)
        
        row = Request.QueryString("row")
        col = Request.QueryString("col")
        
        dim field
        field = Application("field")
        
        if who = 1 then
        	field(row, col) = -1
        else
        	field(row, col) = 1
        end if
        
        Application.lock
        
        Application("field")=field
        
        
        
        After this we should check whether the game was finished or not. This job is done by the function checkwinner() that accepts the tic-tac-toe playing field array as a parameter. This function is a VBScript remake of code from the CGI Developer’s Guide by Eugene Eric Kim, (Sams.net Publishing, 1996).
        
        
        <SCRIPT LANGUAGE=VBScript RUNAT=Server>
        
        ' 0 = go on
        ' 1 = X wins
        ' -1 = 0 wins
        ' 2 = stalemate
        Function checkwinner(board)  
        
        	for j = 0 to 2	' horiz
        	    if board(0,j)+board(1,j)+board(2,j) = 3 then
        			checkwinner=1
        			Exit Function
        		elseif board(0,j)+board(1,j)+board(2,j) = -3 then
        			checkwinner=-1
        			Exit Function
        		end if
        	Next
        		
            for i = 0 to 2  ' vert
        		if board(i,0)+board(i,1)+board(i,2) = 3 then
        			checkwinner=1
        			Exit Function
            	elseif board(i,0)+board(i,1)+board(i,2) = -3 then
        			checkwinner=-1
        			Exit Function
        		end if
        	Next
        
        	'diagonals
        
        	i = board(0,0)+board(1,1)+board(2,2)
        	j = board(2,0)+board(1,1)+board(0,2)
        
        	if (i=3) OR (j=3) then
        		checkwinner=1
        		Exit Function
        	elseif (i=-3) OR (j=-3) then
        		checkwinner=-1
        		Exit Function
        	end if
        
        	for j=0 to 2
        	 for i=0 to 2
                if board(i,j)=0 then
        			checkwinner=0
        			Exit Function
        		end if
        	 Next
        	Next		
        		     
            checkwinner=2
        End Function
        </SCRIPT>
        
        
        
        Now we can call this function, and depending on the result, we will do one of the following:
        1. Change the active player (via Application("gamestatus") variable) and redirect the current player to the passive players’ room, if the function tells us to go on (returns 0).
        2. Display the result of the game if the function reports "stalemate" or "player n has won."
          
          
          dim result
          result = checkwinner(field)
          
          if result = 0 then
          	if Application("gamestatus") = 1 then 
          		Application("gamestatus") = 2
          	else 
          		Application("gamestatus") = 1
          	end if
          	
          	Application.unlock
          	Response.Redirect("wait.asp")
          end if
          
          if result=2 then
          	Response.Write("Stalemate")
          else
          	Response.Write("Congratulation! You have won the game.")
          end if
          %>
          
          
          
        We will also add few lines to the passive players’ room in order to give these players a choice to join the active player’s room when the game logic manager switches active player via Application(“gamestatus”).
        
        
        <!--#include file="utils.asp"-->
        <% 
        If Application("gamestatus") = checkwho(Session.SessionID) then
        	Response.Redirect("yourturn.asp")
        end if
        
        
        
        All right, we have completed the main part of our game. It works! You can even play one set. But it’s still incomplete and has the following problems:
        1. Only players who have completed their last turn will see the results.
        2. The game is not returned to a "no players" state after the game is finished.
        These and other problems will be solves in the next steps.

        Figure 5

        Step 3 : Game Over! (finish.asp)

        Let’s look closer at the first problem. We show the result of the game on the recalc.asp page. But only active players can call this page, so other users will not see the result. We need a special “game over” page.

        First we will make some changes to the global.asa:

        1. Application(“gamestatus”) may accept a new value “-1,” meaning the game is over.
        2. New variable Application(“reason”) explains why the game has finished
          
          
          ' 0 - preparing
          ' 1 - first player turn
          ' 2 - second player turn
          ' -1 game over
          
          Application("gamestatus") = 0
           
          ' 0 - no reason
          ' 1 - first player won
          ' 2 - second player
          
          Application("reason")=0	won; 3 - stalemate
          
          
          
          Now we should modify recalc.asp to support the changes in variables.
          
          
          Application("gamestatus") = -1 'game over
          
          if result=-1 then
          	Application("reason") = result + 2	‘ “0” won
          else
          	Application("reason") = result + 1	‘ “X” won or stalemate
          end if
          
          
          Application.unlock
          
          Response.Redirect("finish.asp")
          
          
          
          As you can see, game status will be updated. The application(“reason”) variable will contain the result and the active player will be redirected to our “game over” page. In order to let other players see the result, we add a few lines to wait.asp and yourturn.asp (it’s not extremely necessary to modify yourturn.asp, because active player will already be redirected ).
          
          
          if Application("gamestatus") = -1 then 
          	Response.Redirect("finish.asp")
          end if
          
          
          
          Now let’s discuss the second problem. After the game was finished we should give other users a chance to play. Our application should restore its state to “no players.” But there is one question: what does “game is finished” mean? The easiest answer is “the game is finished when the application knows the result.” But after some considerations you will choose another answer: “game is finished only when all players were informed about the result.” Only after this can we let other users start new game.

          At last we can implement the first version of the “game over” page - finish.asp. It’s very similar to the passive players’ room, with few exceptions:

          1. We tell the player the result of game.
          2. This script contains code that removes player (who accessed this page) from the players’ ID list. Now the application knows that this player has seen the result. When all players are removed (i.e., they have seen the result), the application switches to “no players” mode, and a new game can be started.
            
            
            	Application.lock
            	
            	plIDs(who)=0
            	Application("plIDs") = plIDs
            	
            	if plIDs(1)=0 AND plIDs(2)=0 then Application("globalstatus")=0
            	
            	Application.unlock
            
            
            
          3. This page doesn’t reload itself.
          4. A “back” link was added to return to the start page.
          We have successfully solved our problems with displaying results and a resetting state. But all this stuff works only with normal game flow. But what should a player do if he/she wants to exit the game immediately or if a player has lost his/her connection for a long time. Our next step is to handle such situations.

          Figure 6

          Step 4 : Aborting the game (finish.asp and global.asa)

          In a real multiplayer game we should give players a way to terminate the game at any moment, no matter if he is an active or passive player. In real Internet-based games we should be aware of loosing connection between player and server for a long time.

          Note
          In our engine we assume that a game is terminated when one of the players has left the game. Your rules may differ, but there are no games that can continue with just one player. And information from this section will be helpful even in this situation.
          First we will add new meaning to the Application("reason") variable. Now this variable may accept new values: “-1,” meaning aborted by player 1, and “-2” for aborted by player 2.
          
          
          ' 0 - no reason
          ' 1 - first player won
          ' 2 - second player won
          ' 3 - stalemate
          '  -1 - aborted by player 1
          '  -2 - aborted by player 2
          
          Application("reason")=0	
          
          
          
          There are three places where player may want to abort the game: the game preparation manager (manager.asp), the passive players’ room (wait.asp), and the active players’ room (yourturn.asp). We will use the same Game Over page (finish.asp) with one exception – it’s called with parameter “action = abort” to force game termination. So the following string will be added to manager.asp, wait.asp, and yourturn.asp:
          
          
          <A HREF="finish.asp?action=abort">Abort waiting</A><BR>
          
          
          
          Now in finish.asp we should separate situations when it’s called without parameter and with parameter. When it’s called with abort parameter, we should also check from where it was called:
          1. If called from the game preparation manager, then we simply restore the "no players" state and bring the player back to the start page.
          2. If called from the players’ rooms, then we terminate the game, grant victory to another player, and bring players to the game over page.
            
            
            if Application("globalstatus")=1 then
            		plIDs(1)=0
            		Application("plIDs") = plIDs
            		Application("globalstatus")=0
            		Application("gamestatus") = -1
            		Application.unlock	
            		Response.Redirect("default.asp")
            	end if
            		
            	if Request.QueryString("action") = "abort" then
            
            		Application("gamestatus") = -1
            		Application("reason")=-who
            
            	end if
            	
            	if Application("reason")<0 then
            		if (-Application("reason"))=1 then
            			face = 0 ' "0"
            		else
            			face = 2 ' "X"
            		end if
            		strresult = "Game aborted/timed out by player " &_
            		plNames(-Application("reason"))&" ("&pic(face)&")"
            	
            	else
            
            
            
            The old code for a normal end of game remains the same.

            We write Internet game, which means that a player can loose his/her connection for a long time or even close the browser without bothering to terminate the game manually. These may cause other players to wait a long time for a turn, while a current player has already forgotten about the game. Thanks to ASP we can handle such situations with the session timeout feature. After a period of inactivity from a user, ASP calls Session_OnEnd event from global.asa. So we will add code here. This code is very similar to one from finish.asp (the force termination part). The only exception is that there is no need to display results or redirect the player since he/she is already gone.

            
            
            <SCRIPT LANGUAGE=VBScript RUNAT=Server>
            SUB Session_OnEnd
            	dim who
            	dim plIDs
            	plIDs = Application("plIDs")
            	who = checkwho(Session.SessionID)
            
            if who<>0 then
            	Application.lock	
            
            	if Application("globalstatus")=1 then
            		plIDs(1)=0
            		Application("plIDs") = plIDs
            		Application("globalstatus")=0
            		Application("gamestatus") = -1
            
            	else
            		Application("gamestatus") = -1
            		Application("reason")=-who
            
            		plIDs(who)=0
            		Application("plIDs") = plIDs
            		if plIDs(1)=0 AND plIDs(2)=0 then
            			Application("globalstatus")=0
            		End If
            		
            	end if
            	
            	Application.unlock
            end if
            
            END SUB
            </script>
            
            
            
            
            You can control this period of inactivity via the Session.Timeout property from ASP or via application properties from the MMC.

            Our game becomes more and more user friendly. But remember, not all users are friendly to your application. Some of them may try to fool your application. The most harmful action is to make changes from the name of the opponent. They may just call recalc.asp manually from the passive player mode, causing harm to the opponent parameters, and our application will accept it as if it were from the active user. Our game is not protected . . . yet.

            Figure 7

            Step 5 : Protection from malicious users (almost all asp files)

            First we should protect the internal part of our game from nonplayers. Such users can be given a warning or simply redirected to the start page. We have already implemented the checkwho() function from utils.asp. This function allows us to determine the number of players based on its SessionID, but it also returns 0 when this user is not a player. So we can use this function. This code is the same for many pages. We will replace references to utils.asp with validate.asp where needed (such as manager.asp, yourturn.asp, wait.asp, recalc.asp, and finish.asp).

            
            
            <!-- #include file = "utils.asp" -->
            
            <% 
            if checkwho(Session.SessionID) = 0 then
            	Response.Redirect ("default.asp")
            end if 
            %>
            
            
            
            Now we should protect players from the unfair play of others. Such players may affect the game in three ways:
            1. By entering the active players’ room from the passive players’ room (direct access to URL of yourturn.asp)
            2. By making changes on the field from the name of active user (direct call of recalc.asp with parameters)
            3. By incorrect termination (direct call of finish.asp without action=abort parameter)

            The first situation is solved by adding the following string in the yourturn.asp:

            
            
            if Application("gamestatus") <> checkwho(Session.SessionID) then
            	Response.Redirect("wait.asp")
            end if
            
            
            

            The second problem is solved in recalc.asp with almost the same code:

            
            
            who = checkwho(Session.SessionID)
            
            if who <> Application("gamestatus") then
            	Response.Redirect("wait.asp")
            end if
            
            
            
            We can also solve another problem not listed above – when the active user calls recalc.asp directly. Our application simply checks parameters to handle “incorrect/missed parameter,” “out of bounds,” and “cell already used” errors.
            
            
            if IsNumeric(Request.QueryString("row")) then 
            		row = CInt(Request.QueryString("row"))
            	else
            		row = 0
            end if	
            	
            if IsNumeric(Request.QueryString("col")) then 
            		col = CInt(Request.QueryString("col"))
            	else
            		col = 0
            end if	
            
            if row < 0 OR row > 2 OR col < 0 OR col > 2 then
            	Response.Redirect("yourturn.asp")
            end if
            
            dim field
            field = Application("field")
            
            if field(row, col) <> 0 then
            	Response.Redirect("yourturn.asp")
            end if
            
            
            
            Let’s discuss the last problem. Our “game over page” (finish.asp) is called with parameter action=abort when the player wants to abort the game. In this case, the game is terminated. This page is called without parameters in two situations:
            1. The game was finished (normally) and the active player is redirected to finish.asp.
            2. The game was finished or terminated by another player and this player is redirected from one of the pages (normally wait.asp).
            In our previous version, the direct call of finish.asp without parameters may cause our application to hang. In this version, we will check if the game was really finished. If not , redirect back.
            
            
            if Request.QueryString("action") = "abort" then
            		Application("gamestatus") = -1
            		Application("reason")=-who
            	elseif Application("gamestatus") <> -1 then	
            ‘was the game really finished
            		Response.Redirect("wait.asp")
            end if
            
            
            
            That’s all about security. In the next step we will add some features to make our game more entertaining.

            Step 6 : “Let me entertain you” (showroom.asp, global.asa, images)

            At this point our application works correctly. It’s secured from unfriendly actions, but it lacks some features that are usually required by games. Now we should make our application look like game. Of course, a real game needs a good web designer/painter, but in our simple tic-tac-toe game, we’ll just replace the text symbols (“X,” “0,” and “_”) with images. The structure of our application allows us to do this very simply. Just replace few lines in global.asa and create corresponding images:

            
            
            ' field pictures
            dim picts(3)	
            picts(0) = "<img src=0.gif alt=0 height=20 width=20 border=0 align=absmiddle>"
            picts(1) = "<img src=1.gif alt=_ height=20 width=20 border=0 align=absmiddle>"
            picts(2) = "<img src=2.gif alt=X height=20 width=20 border=0 align=absmiddle>"
            
            
            
            Active and passive players’ rooms and the game over page will now use images instead of text symbols automatically.

            While our players enjoy the game, let’s entertain other visitors. The basic feature usually used in such web applications is the number of visitors on the server. The code to handle it is trivial:

            
            
            ‘ global.asa - Application_OnStart
            Application("visitors") = 0
            
            ‘ global.asa - Session_OnStart
            Application("visitors") = Application("visitors") + 1
            
            ‘ global.asa - Session_OnEnd
            Application("visitors") = Application("visitors") - 1
            
            ‘ default.asp
            Currently <%= Application("visitors") %> visitors logged on
            
            
            

            Figure 8

            The best way to keep visitors on server is to give them the possibility to watch a current game. For these visitors we’ll create our showroom (showroom.asp). The idea is to show visitors not taking part in the game, the current game field, status, and turns history.

            Turns out history is implemented very easily (don’t forget to reset it before a new game):

            
            
            ‘ global.asa - Application_OnStart
            Application("history") = ""
            
            ‘ manager.asp
            Application("history") = ""
            
            ‘ recalc.asp
            Application("history") = Application("history") & pic(face) &_
            		" - (" & row+1 &", "& col+1 &")<br>"
            
            
            
            A very interesting script implements the showroom. This page doesn’t make any changes – it’s a passive room. But it gathers code from 4 files (default.asp, manager.asp, wait.asp, finish.asp). The main idea is to show the current playing field, so wait.asp is used as a core of showroom.asp. This room is open for everyone. We don’t need validate.asp here. It’s also a good place to show our new history feature.
            
            
            <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
            <META HTTP-EQUIV="Pragma" CONTENT="no-proxy">
            <script language="JavaScript">
            function GotoUrl(url)
            {
            	parent.location=url;
            }
            function TimeUrl(delay, url)
            {
                setTimeout('GotoUrl("'+url+'")',delay*1000);	
            }
            </script>
            
            <HTML>
            <HEAD>
            	<TITLE>Showroom </TITLE></HEAD>
            <BODY  onload="TimeUrl(<%=Session("refresh")%>, 'showroom.asp')">
            
            <%	
            	dim field
            	field = Application("field")
            
            	dim pic
            	pic = Application("pictures")
            	
            	dim plNames
            	plNames=Application("plNames")
            
            	dim strresult
            
            %>	
            Global status: 
            <%
            		
            	Select Case Application("globalstatus") 
            		Case 0 Response.Write(" no players")
            		Case 1 Response.Write(" 1 player waiting - " & plNames(1))
            		Case 2
            			Response.Write(" game is in progress - " &_
            				 plNames(1)& " ("&pic(0)&")" & " vs " &_
            				 plNames(2))& " ("&pic(2)&")"
            		End Case
            	End Select
            	
            	if Application("gamestatus") <>0 then
            	
            		if Application("gamestatus")>0 then
            			if (Application("gamestatus"))=1 then
            				face = 0 ' "0"
            			else
            				face = 2 ' "X"
            			end if
            			strresult = "It's turn of player " &_
            				plNames(Application("gamestatus")) &_
            				" ("&pic(face)&")"
            
            		else
            		
            			if Application("reason")=3 then
            				strresult = "Stalemate"
            
            			elseif Application("reason")>0 then
            				
            				if (Application("reason"))=1 then
            					face = 0 ' "0"
            				else
            					face = 2 ' "X"
            				end if
            				strresult = "Player " &_
            					 plNames(-Application("gamestatus")) &_
            					 " ("&pic(face)&") has won!"
            
            			elseif Application("reason")<0 then
            				if (-Application("reason"))=1 then
            					face = 0 ' "0"
            				else
            					face = 2 ' "X"
            				end if
            				strresult = "Game aborted/timed out by player " &_
            					plNames(-Application("reason")) &_
            					" ("&pic(face)&")"
            			end if
            		end if
            %>
            
            	<br>
            	Last info: <%= strresult %>
            	<TABLE BORDER=1 ALIGN="Center" CELLPADDING=0 CELLSPACING=0>
            	<%
            		For i = 0 to 2
            			Response.Write("<TR>")
            			For j = 0 to 2
            				Response.Write("<td width='20' height='20' " &_
            					"align='CENTER'>" & pic(field(i,j)+1) &_
            					"</TD>")
            			Next
            			Response.Write("</TR>")
            		Next
            	%>
            	</TABLE>
            	<br>
            	History:<br>
            	<%= Application("history") %>
            <%
            end if
            %>
            
            <HR><a href="default.asp">Back</a>
            <BR>
            </BODY>
            </HTML>
            
            
            
            

            Figure 9

            Note
            Our last note is about redirecting. Direct call of the Response.Redirect method may cause the “Object Moved” message to be displayed to the player. We will implement our redirect sub that deals with this problem. This sub can be placed to the utils.asp:
            
            
            Sub redirect(redirURL)
            	response.buffer = TRUE
                	response.clear
                	Response.Redirect (redirURL)
                	response.end
            End Sub
            
            
            
            And all calls to Response.Redirect() should be replaced with calls to redirect() sub.

            Home Tasks

            When I first published this game, many people asked me to add new features, like computer player (bot), chat, multiple rooms, administration, etc. But I’m very busy and all these wishes remain only wishes. They are waiting for you! You can (if you want) send your solutions to me, and we will discuss them together.

            Your own games

            The idea of this article was to give you the engine for your own games. You will also find source code for the “Sea Battle” game. This is an example of a more complex game. It’s built completely on this engine, but uses a server-side component for its nontrivial logic.

            Figure 10

            Good Luck!

            Code For Download

            Our code for download consists of two parts:

            1. Code from the article (the tic-tac-toe game) http://15seconds.com/files/990203_1.zip
            2. Bonus code (the Sea Battle game) http://15seconds.com/files/990203_2.zip
            Installation of the first application was discussed above. Installation of the second is almost the same. The only exception is you should register the ActiveX object from sea.dll on your computer. Executing the following command from the command prompt can do this: regsvr32 sea.dll

            About the Author

            Rod Denisyuk is a 19 years old student of Odessa State University, Ukraine. He has been programming with C++ for over 4 years and last summer he read "Working with Active Server Pages" and start using ASP. Now he takes part in the project "Russian Active Server Pages" http://www.activeserverpages.ru (russian language only).

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry



    JupiterOnlineMedia

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info


    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers