DIR : /home/kozerus/public_html/dgs/clock_tick.php

/home/kozerus/public_html/dgs

<?php
/*
Dragon Go Server
Copyright (C) 2001-  Erik Ouchterlony, Rod Ival, Jens-Uwe Gaspar

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


require_once 'include/std_functions.php';
require_once 'include/rating.php';
require_once 'include/game_functions.php';
require_once 'include/cache_clock.php';

$TheErrors->set_mode(ERROR_MODE_COLLECT);

if ( !$is_down )
{
   $tick_diff = floor(SECS_PER_HOUR / TICK_FREQUENCY); // called every 5 minutes (300s)
   if ( $chained )
      $chained = $tick_diff;
   else
      connect2mysql();
   $tick_diff -= 10;
   $max_run_time = $NOW + $tick_diff;
   set_time_limit(0); // don't want script-break during "transaction" with multi-db-queries


   // Check that ticks are not too frequent

   $row = mysql_single_fetch( 'clock_tick.check_frequency',
      "SELECT ($NOW-UNIX_TIMESTAMP(Lastchanged)) AS timediff"
      ." FROM Clock WHERE ID=".CLOCK_CRON_TICK." LIMIT 1" );
   if ( !$row )
      $TheErrors->dump_exit('clock_tick');
   if ( $row['timediff'] < $tick_diff )
      $TheErrors->dump_exit('clock_tick');

   db_query( 'clock_tick.set_lastchanged',
         "UPDATE Clock SET Ticks=1, Lastchanged=FROM_UNIXTIME($NOW) WHERE ID=".CLOCK_CRON_TICK." LIMIT 1" )
      or $TheErrors->dump_exit('clock_tick');


   // ---------- BEGIN ------------------------------

// Handle game timeouts

   if ( !$clocks_stopped )
      handle_game_timeouts();



   // ---------- END --------------------------------

   db_query( 'clock_tick.reset_tick',
         "UPDATE Clock SET Ticks=0, Finished=FROM_UNIXTIME(".time().") WHERE ID=".CLOCK_CRON_TICK." LIMIT 1" );

   if ( !$chained )
      $TheErrors->dump_exit('clock_tick');

}//$is_down
//end main


function handle_game_timeouts()
{
   global $NOW, $max_run_time;

   // $NOW is time in UTC
   $hour = gmdate('G', $NOW);
   $day_of_week = gmdate('w', $NOW);

   // Increase running clocks
   // - NOTE: ClockUsed is the GMT hour for game clocks (GAME_CLOCK=0: std-clock, GAME_WEEKEND_CLOCK: weekend)

   $clocks_modified = array( GAME_CLOCK );
   if ( $day_of_week > 0 && $day_of_week < 6 )
      $clocks_modified[] = GAME_WEEKEND_CLOCK;
   $game_clocks_modified = sprintf('Clock.ID IN (%s)', join(',', $clocks_modified));
   $clocks_modified[] = CLOCK_TIMELEFT; // clock for remaining-time ordering

   db_query( 'clock_tick.increase_clocks',
      "UPDATE Clock SET Ticks=Ticks+1, Lastchanged=FROM_UNIXTIME($NOW) "
         . "WHERE Clock.ID IN (".join(',', $clocks_modified).") ");
   ClockCache::delete_cache_clocks();


   // Check if any game has timed out

   /* TODO(keep as note) BUG in old & new version:
     if maintenance spans more than a whole hour and graces the non-running-clocks,
     the time-window has passed to detect time-outs within that hour.
     So time-outs are delayed by a full day till the games with the respective clock is checked again!

     => keep Clock.ID>=0 (as safety for clones; else join with Games would be empty anyway
        as there is no Clock.ID<0 any more)
     => remove Clock.Lastchanged=$NOW to match all clock-timeouts
    */

   /*
    In a first approximation, as $has_moved==false (see time_remaining()),
     $main>$hours will just produce $main-=$hours which will never
     activate $time_is_up and so, will produce nothing here...
    So the "find_timeout_games" query may be restricted with:
    - given IF(ToMove_ID=Black_ID, Black_Maintime, White_Maintime) AS ToMove_Maintime
    - WHERE ToMove_Maintime <= $hours
    because $hours is always < $ticks/TICK_FREQUENCY (see ticks_to_hours())
    - Clock.ticks is always > Games.LastTicks
    - given ((ticks-LastTicks) / TICK_FREQUENCY) AS Upper_Ellapsed
    - WHERE ToMove_Maintime < Upper_Ellapsed
    which is:
    - WHERE IF(ToMove_ID=Black_ID, Black_Maintime, White_Maintime)
         < ((Clock.Ticks-Games.LastTicks) / TICK_FREQUENCY)

    During a test, this lowered the number of returned rows from 10,960 to 675.
    This is a table scan, but indeed reduces the number of affected rows.
   */

   $result = db_query( 'clock_tick.find_timeout_games',
      "SELECT Games.*, Games.ID as gid, Clock.Ticks as ticks"
      ." FROM Games"
         ." INNER JOIN Clock ON Clock.ID=Games.ClockUsed AND ($game_clocks_modified)"
      ." WHERE Clock.ID >= 0 AND" // older DGS-clones may still have clocks<0
      ." IF(ToMove_ID=Black_ID, Black_Maintime, White_Maintime) * ".TICK_FREQUENCY." < Clock.Ticks - Games.LastTicks "
      ." AND Status" . IS_STARTED_GAME
      );

   /* TODO: The following UPDATE should be optimized, splited in smaller chunk?
            use TEMPORARY TABLEs for generated UPDATEs ??? */

   while ($row = mysql_fetch_assoc($result))
   {
      if ( time() > $max_run_time ) break; // stop script if running too long to avoid concurrent runs
      extract($row);

      //$game_clause (lock) needed. See *** HOT_SECTION *** in GameActionHelper.init_query()
      $game_clause = " WHERE ID=$gid AND Status".IS_STARTED_GAME." AND Moves=$Moves LIMIT 1";

      $hours = ticks_to_hours($ticks - $LastTicks);

      if ( $ToMove_ID == $Black_ID )
      {
         time_remaining( $hours, $Black_Maintime, $Black_Byotime, $Black_Byoperiods,
            $Maintime, $Byotype, $Byotime, $Byoperiods, false);

         $time_is_up = ( $Black_Maintime == 0 && $Black_Byotime == 0 );
      }
      else if ( $ToMove_ID == $White_ID )
      {
         time_remaining( $hours, $White_Maintime, $White_Byotime, $White_Byoperiods,
            $Maintime, $Byotype, $Byotime, $Byoperiods, false);

         $time_is_up = ( $White_Maintime == 0 && $White_Byotime == 0 );
      }
      else
         continue;


      if ( $time_is_up )
      {
         //TODO(feature): Delete games with too few moves ??? (if so -> send delete-game-msg)

         $score = ( $ToMove_ID == $Black_ID ) ? SCORE_TIME : -SCORE_TIME;
         $game_finalizer = new GameFinalizer( ACTBY_CRON, /*cron*/0, $gid, $tid,
            $Status, $GameType, $GamePlayers, $Flags, $Black_ID, $White_ID, $Moves, ($Rated != 'N'),
            $Black_Start_Rating, $White_Start_Rating );

         ta_begin();
         {//HOT-section to save game-timeout
            // send message to my opponent / all-players / observers about the result
            $game_finalizer->finish_game( "clock_tick.time_is_up", /*del*/false, $score, null, '' );
         }
         ta_end();
      }//time-out
   }
   mysql_free_result($result);

}//handle_game_timeouts

?>
//


koh5_pano



Your browser does not support the HTML5 canvas element.


Drag mouse to navigate.

Navigation





17.Aug.2010, Martin Wengenmayer