DIR : /home/kozerus/public_html/go/dgs0/clock_tick.php
/home/kozerus/public_html/go/dgs0
<?php
/*
Dragon Go Server
Copyright (C) 2001-2014 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
// build a range SQL-query-part for the field $n
// from $s to $e on a 24 clocks basis with the offset $o
function clkrng( $n, $s, $e, $o=0)
{
if ( $s>23 ) $s-= 24;
if ( $e>23 ) $e-= 24;
if ( $s>$e )
return clkrng($n,0,$e,$o)." OR ".clkrng($n,$s,23,$o);
$s+= $o; $e+= $o;
if ( $s==$e )
return "$n=$s";
return "($n BETWEEN $s AND $e)"; // ($n>=$s AND $n<=$e)
}//clkrng
function handle_game_timeouts()
{
global $NOW, $max_run_time;
// $NOW is time in UTC
$hour = gmdate('G', $NOW);
$day_of_week = gmdate('w', $NOW);
// Now increase clocks that are not sleeping
/* NIGHT_LEN hours night
* Nightstart= N means night in: [ N,(N+NIGHT_LEN)%24 [ (see edit_profile.php)
* N <= time < (N + NIGHT_LEN)%24
* if Timezone=='GMT', ClockUsed is always equal to Nightstart
* if Timezone=='GMT+x'(ou UTC+x), ClockUsed is ( Nightstart+24-x )%24
* NOTES:
* - "+24" added to avoid becoming negative
* - ClockUsed is the GMT hour on which the users night start
* - ClockUsed also used for weekends (+weekend offset), so NightStart also influences start/end of weekend
*
* When the GMT hour is 22, clock_modified= [23]U[00,13], thus:
* UserTZ UserTime UserNight UserClockUsed UserClockModified
* GMT 22 [22,07[ 22 -
* GMT+9 07 [22,07[ 13 Y
* GMT+2 00 [22,07[ 20 -
* GMT-2 20 [22,07[ 00 Y
* GMT-9 13 [22,07[ 07 Y
* GMT+2 00 [20,05[ 18 -
* GMT+2 00 [22,07[ 20 -
* GMT+2 00 [02,11[ 00 Y
* GMT-2 20 [20,05[ 22 -
* GMT-2 20 [22,07[ 00 Y
* GMT-2 20 [02,11[ 04 Y
*/
$clock_modified = clkrng( 'Clock.ID', $hour+1, $hour+24-NIGHT_LEN);
if ( $day_of_week > 0 && $day_of_week < 6 )
$clock_modified.= " OR ".
clkrng( 'Clock.ID', $hour+1, $hour+24-NIGHT_LEN, WEEKEND_CLOCK_OFFSET);
db_query( 'clock_tick.increase_clocks',
"UPDATE Clock SET Ticks=Ticks+1, Lastchanged=FROM_UNIXTIME($NOW) "
. "WHERE $clock_modified " // game-clocks
. "OR Clock.ID=".CLOCK_TIMELEFT // clock for remaining-time ordering
);
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 sleeping-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 ($clock_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') );
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, null, $score, '' );
}
ta_end();
}//time-out
}
mysql_free_result($result);
}//handle_game_timeouts
?>
//
koh5_pano
Drag mouse to navigate.
Navigation
- Left/Right Mouse drag: Changes camera heading.
- Up/Sown Mouse drag: Changes camera pitch.
- Scroll wheel: Changes camera field of view.
- I-Key: Displays Info panel with canvas size, image size and FPS.
17.Aug.2010, Martin Wengenmayer