Game Development Community

Server Pause vs. Client Pause

by Corey Punches · in Torque Game Engine · 06/13/2007 (6:41 am) · 1 replies

As part of the save/load system I'm developing for my game I want to be able to pause the game when certain dialogs are presented.

Thanks to some people letting me know about the $timeScale variable I don't have any problems with pausing the game locally

In ~client/default.bind.cs
function togglePause( %val )
{
   if ( %val )
   {
     if ( $timeScale )   //not paused
      {
          $timeScale = 0;
      }
      else
      {
          $timeScale = 1;
      }
     
   }   
}

moveMap.bind(keyboard, "alt p", togglePause);

This method works fine (barring the fact that although the sim is paused keyboard input is still being buffered).

Since my game is hopefully going to be a two->four player coop type game I need to not only pause the local client but all the clients (for saving etc).

** corrected code below

#1
06/15/2007 (10:48 am)
I got this to work finally. It does appear that when $timeScale is set to 0 commandToServer won't work (cause time isn't advancing, duh ;)). Here's the updated code to pause a game via the server and popup a gui on all the connected clients (the local pause posted in my first post is still valid for single-player games.).

In ~client/default.bind.cs add this code.
// on start pauseState needs to be false
echo("Setting pauseState to false");
$pauseState = false;

function toggleServerPause() 
{   
     echo("Toggling pause. pauseState is " @ $pauseState );
     if ($pauseState == false) 
     {   
         $pauseState = true;  
         echo("asking server to pause clients. pauseState is " @ $pauseState);  
         commandToServer('PauseGame', $pauseState);
         echo("called commandToServer to pause timeScale is now: " @ $timeScale);      
     } 
     else 
     {	
        $pauseState = false;
        echo("this client has paused the game so restart time so server will respond?");
        $timeScale = 1; 
        echo("asking server to unpause clients. pauseState is " @ $pauseState); 
        commandToServer('PauseGame', $pauseState);
        echo("called commandToServer to unpause timeScale is now: " @ $timeScale);
     }
}

// set this bind to what you want
moveMap.bindCmd(keyboard, "ctrl i", "toggleServerPause();", "");

In ~server/scripts/commands.cs add this function
// tell all clients to pause game when the pause key is pressed
function serverCmdPauseGame(%client, %spflag )
{  
    echo("serverCmdPauseGame was called with %client = " @ %client @ " and %spflag = " @ %spflag);
    if ( %spflag == true ) // not paused  
    {    
         %cpflag = true; 
         echo("Game was not paused, setting %cpflag to true");      
        // Tell clients to pop up pause dialog - TODO for now just pause them      
        for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ )      
        {            
             %cl = ClientGroup.getObject( %clientIndex );
             echo("%cl = " @ %cl);
             if ( %cl == %client )
             {
                  echo("this client paused game telling client to pause only");
                  %pdflag = false; // we want client to pause and not show pause gui            
                  commandToClient(%cl, 'pauseGame', %cpflag, %pdflag);     
             }
             else
             {
             	    echo("this client did not pause game telling client to pause and display gui");
                  %pdflag = true; // this client did not pause show pause gui            
                  commandToClient(%cl, 'pauseGame', %cpflag, %pdflag); 
             }
        }  
     }  
     else  
     {      
        echo("Game was paused, setting %cpflag to false");
        %cpflag = false;     // Tell clients to unpop pause dialog - TODO for now just unpause them     
        for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ )      
        {            
             %cl = ClientGroup.getObject( %clientIndex );
             if ( %cl == %client )
             {
            	  echo("this client paused game telling client to unpause only");
                  %pdflag = false; // we want client to unpause            
                  commandToClient(%cl, 'pauseGame', %cpflag, %pdflag);     
             }
             else
             {
             	  echo("this client did not pause game telling client to unpause and hide gui");
                  %pdflag = true; // this client paused so unpause and hide gui            
                  commandToClient(%cl, 'pauseGame', %cpflag, %pdflag); 
             }
        }   
      } 
}

In ~client/scripts/client.cs add this function
function clientCmdPauseGame( %cpflag, %pdflag )
{
   echo("Server called client to process pause command");
   if ( %cpflag == true )
   {
       echo("Asking client to pause game");
        if ( %pdflag == true )
        {
          // pause client and display pause GUI
          $timeScale = 0;
          Canvas.pushDialog(PauseGameGui);
        }
        else
        {  // this client paused game don't display pause gui
           $timeScale = 0;
           
        }
   }
   else  // unpause
   {
       echo("Asking client to unpause game"); 
        if ( %pdflag == true )
        {
          // pause client and display pause GUI
          $timeScale = 1;
          Canvas.popDialog(PauseGameGui);
          Canvas.pushDialog(PlayGui);
        }
        else
        {  // this client paused game don't hide pause gui
           if ( $timeScale != 1 )
                $timeScale = 1;
           
        } 
   }
}

My example PauseGameGui.gui - put in ~client/scripts/ui/PauseGameGui.gui
//--- OBJECT WRITE BEGIN ---
new GuiControl(PauseGameGui) {
   canSaveDynamicFields = "0";
   Profile = "GuiDefaultProfile";
   HorizSizing = "right";
   VertSizing = "bottom";
   position = "0 0";
   Extent = "640 480";
   MinExtent = "8 8";
   canSave = "1";
   Visible = "1";
   hovertime = "1000";

   new GuiWindowCtrl() {
      canSaveDynamicFields = "0";
      Profile = "GuiWindowProfile";
      HorizSizing = "center";
      VertSizing = "center";
      position = "132 88";
      Extent = "319 64";
      MinExtent = "8 8";
      canSave = "1";
      Visible = "1";
      hovertime = "1000";
      maxLength = "255";
      resizeWidth = "0";
      resizeHeight = "0";
      canMove = "0";
      canClose = "0";
      canMinimize = "0";
      canMaximize = "0";
      minSize = "50 50";

      new GuiMLTextCtrl(PauseText) {
         canSaveDynamicFields = "0";
         Profile = "GuiMLTextProfile";
         HorizSizing = "width";
         VertSizing = "relative";
         position = "129 23";
         Extent = "76 14";
         MinExtent = "8 8";
         canSave = "1";
         Visible = "1";
         hovertime = "1000";
         lineSpacing = "2";
         allowColorChars = "0";
         maxChars = "-1";
         text = "GAME PAUSED";
      };
   };
};
//--- OBJECT WRITE END ---

Don't forget to exec it in ~client/init.cs
exec("./ui/PauseGameGui.gui");

You can safely remove all the echo commands in the scripts, I just had them there to assist me in figuring out what was happening.

There are a couple of things to keep in mind if you use this. One is that even though the sim is paused, keypresses are still being buffered on the client, so if you move the mouse when you're paused and unpause the sim the view will shift. I'm thinking about popping the current actionmap when the game is paused and just binding the pause/unpause key in a new map then swapping the old actionmap back in when the game is unpaused. I'm sure there is another probably better way of doing that.

The second caveat is potential schedule issues. I haven't experienced any personally yet, but just be aware.