<?php

/**
* Script for saving all previous seasons.
* From 8 to latest, which is provided as a parameter.
*/
set_time_limit(0);
//Can take long time
require_once 'config/.keys.php';
//Auth key $admin_key is stored here
require_once 'data_functions.php';
//Check if qualified call
if (!isset($_GET['key']) || htmlspecialchars($_GET["key"]) != $admin_key) {
    die("Unauthorized call.");
}
if (!isset($_GET['current'])) {
    die("Missing parameter \"current\"");
}
$current_season = $_GET['current'];
$chars = getCharacterData();
for ($i = 8; $i < $current_season; $i++) {
    $ids = getLeaderboardId($i);
    $xml = getLeaderboardXML($ids[1]);
    $data = getLeaderboardData($xml, $chars);
    if (saveLeaderboardData($ids[0], $data)) {
        echo "Data saved for season " . $i . "<br>\n";
    } else {
        echo "Couldn't save data for season " . $i . "<br>\n";
    }
}
<?php

//Checks whether the last completed season's data has been saved.
//Ideally this is called on the first day of every second month (February, April..)
require_once 'config/.keys.php';
//Auth key $admin_key is stored here
require_once 'data_functions.php';
//Check if qualified call
if (!isset($_GET['key']) || htmlspecialchars($_GET["key"]) != $admin_key) {
    die("Unauthorized call.");
}
$current_season_ids = getLeaderboardId(NULL);
$penultimate_season = $current_season_ids[0] - 1;
if (checkSeasonTable($penultimate_season)) {
    echo "Historic data for season {$penultimate_season} is already saved.";
} else {
    $chars = getCharacterData();
    $ids = getLeaderboardId($penultimate_season);
    $xml = getLeaderboardXML($ids[1]);
    $data = getLeaderboardData($xml, $chars);
    if (saveLeaderboardData($penultimate_season, $data)) {
        echo "Data saved for season " . $penultimate_season . "<br>\n";
    } else {
        echo "Couldn't save data for season " . $penultimate_season . "<br>\n";
    }
}
function updateLeaderboard($lbid = "")
{
    global $db;
    global $config;
    global $colNames_details;
    global $colNames_scores;
    global $colNames_times;
    global $colNames_players;
    global $date;
    global $today;
    if ($lbid === "") {
        $r = getLeaderboardId();
        $r = getLeaderboardIdFromSteam();
    } elseif ($lbid === "yesterday") {
        $r = getLeaderboardId(1);
        $yesterday = true;
    } else {
        $r = $lbid;
    }
    if ($r == 0) {
        echo "[!] couldn't retrieve the leaderboard from the db,\n";
        echo "[!] trying to retrieve it from steam\n";
        $lbid = getLeaderboardIdFromSteam($yesterday ? 1 : $lbid);
    } else {
        $lbid = $r;
    }
    // get today's number of entries
    $url = $config['leaderboards']['baseUrl'] . $lbid . '/?xml=1&start=0&end=1';
    $data = getData($url);
    $xml = new SimpleXMLElement($data);
    $how_many = $xml->totalLeaderboardEntries;
    // get all entries for today
    $rank = 1;
    $rank_hax = 999999;
    $current_count = 1;
    // start the huge loop
    $q = $db->prepare("\n        SELECT date\n          FROM dates\n         WHERE dayid = :lbid");
    $q->execute(array(':lbid' => $lbid));
    $date = $q->fetch()[0];
    if ($date == date('Y-m-d')) {
        $q = $db->prepare("\n            UPDATE dates\n               SET done = 1,\n                   entries = :entries\n             WHERE dayid = :lbid");
        $q->execute(array(':lbid' => $lbid, ':entries' => $how_many));
    }
    // get a list of known, diehard cheaters
    // previously disabled, now once again enforced.
    // seems like they just won't get bored.
    // players who have cheated 3 times or more
    $res = $db->query("\n        SELECT DISTINCT steamid\n          FROM players\n         WHERE hax >= 3\n         ORDER BY steamid ASC");
    $permabanned = $res->fetchAll(PDO::FETCH_COLUMN);
    // players who have been caught today
    $res = $db->query("\n        SELECT DISTINCT steamid\n          FROM scores\n         WHERE dayid = {$lbid}\n           AND hidden = 1\n         ORDER BY steamid ASC");
    $todaysbanned = $res->fetchAll(PDO::FETCH_COLUMN);
    // manually hidden
    $res = $db->query("\n        SELECT DISTINCT steamid\n          FROM scores\n         WHERE dayid = {$lbid}\n           AND hidden = 3\n         ORDER BY steamid ASC");
    $todayshidden = $res->fetchAll(PDO::FETCH_COLUMN);
    // alts to skip
    $res = $db->query("\n        SELECT DISTINCT steamid\n          FROM alt_accounts\n         WHERE alt_id = {$lbid}\n         ORDER BY steamid ASC");
    $known_alts = $res->fetchAll(PDO::FETCH_COLUMN);
    $entries = 0;
    while ($current_count <= $how_many) {
        echo "[" . date('H:i:s') . "] " . "updating leaderboards: {$current_count} / {$how_many} done\n";
        $url = $config['leaderboards']['baseUrl'] . $lbid . "/?xml=1&start={$current_count}&end=" . ($current_count + 4999);
        // get the xml element for the leaderboards
        try {
            $data = getData($url);
            $xml = new SimpleXMLElement($data);
            $how_many = $xml->totalLeaderboardEntries;
        } catch (Exception $e) {
            echo "[" . date('H:i:s') . "] " . "[{$c}/{$t}] Failed to update leaderboard: {$e->getMessage()}\n";
            sleep(10);
            updateLeaderboard();
        }
        // prepare the arrays that will hold 5k entries each
        $scores = array();
        $times = array();
        $details = array();
        $players = array();
        $alts_to_check = array();
        // populate the arrays
        foreach ($xml->entries->entry as $e) {
            // skip ongoing / crashed runs
            if ($e->score <= 0) {
                if (binsearch($e->steamid, $known_alts) !== false) {
                    continue;
                }
                $alts_to_check[] = $e->steamid;
                continue;
            }
            // compute the unique hash between dailyid and the steamid
            // return a binary hash since we're storing blobs and not chars
            $hash = md5($lbid . $e->steamid, true);
            // pre-populate the detailed score array
            // needed because the earlier versions of the game didn't have some
            // fields and we still want to use a common insert routine
            // [0] = hash, [1] = dayid, [2] = stage, [3] = swag, [4] = xxx
            // [5] = lamb, [6] = mega,  [7] = rush,  [8] = expl, [9] = dmg
            // [10]= time, [11]= item,  [12]= floor, [13]= type, [14]= time
            // [15]= end,  [16]= build
            $tmp = array($hash, $lbid, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
            // convert the detailed score blob into useful ints
            for ($i = 0; $i <= strlen($e->details) - 1; $i += 8) {
                $hex = substr($e->details, $i, 8);
                $dec = implode('', array_reverse(str_split($hex, 2)));
                $tmp[$i / 8 + 2] = hexdec($dec);
            }
            // get the time
            $time = floor($tmp[14] / 30);
            $tmp[14] = $time;
            // get the floor value for the time leaderboard
            $floor = $tmp[12];
            // keep tally of the real amount of entries
            $entries++;
            // everyone is innocent...
            $hidden = 0;
            $perma = false;
            $ban = $e->steamid;
            // hide today's cheaters' scores and don't increase
            // their hax value
            if (binsearch($ban, $todaysbanned) !== false) {
                $hidden = 1;
            } elseif (binsearch($ban, $todayshidden) !== false) {
                $hidden = 3;
            } elseif (binsearch($ban, $permabanned) !== false) {
                $perma = true;
                $hidden = 3;
            }
            // determine the level of cheating unless we've manually hidden
            // this score
            if ($hidden == 0 && $perma === false) {
                // exp.bonus > 26k (game can be broken up to 40k afaik
                // using the partial-room completion strategy.
                // see the comment below for the relevant db query)
                //
                if ($tmp[8] > 26000 || $tmp[3] > 16500) {
                    $hidden = 2;
                }
                //    schwag > 16k
                //    by game engine (40k)
                if ($tmp[3] > 16500 || $tmp[8] > 26000 || $floor >= 6 && $time <= 120) {
                    // ... until proven guilty
                    $hidden = 1;
                }
            }
            // adjust rank accordingly
            if ($hidden == 1 || $hidden == 3 || $perma === true) {
                $final_rank = $rank_hax;
            } else {
                $final_rank = $rank;
                $rank++;
            }
            // add the detailed score to the main array
            $details[] = $tmp;
            // populate the score array and the 'hidden' value
            $scores[] = array('hash' => $hash, 'dayid' => $lbid, 'steamid' => $e->steamid, 'score' => $e->score, 'rank' => $final_rank, 'hidden' => $hidden);
            // do the same with the times array
            $times[] = array('hash' => $hash, 'dayid' => $lbid, 'steamid' => $e->steamid, 'floor' => $floor, 'time' => $time, 'rank' => 999999, 'hidden' => $hidden);
            // populate the players array
            $players[] = array('steamid' => $e->steamid);
        }
        if (count($details) == 0) {
            break;
        }
        // scores, times and players aren't that huge
        bulkInsert('players', $colNames_players, $players);
        bulkInsert('scores', $colNames_scores, $scores);
        bulkInsert('times', $colNames_times, $times);
        // the detailed score array is huge, we need to partition it
        // in smaller bulks or mariadb will die for an excess of prepared
        // parameters
        if (count($details) > 2500) {
            foreach (array_chunk($details, floor(count($details) / 2)) as $bulk) {
                bulkInsert('details', $colNames_details, $bulk);
            }
        } else {
            bulkInsert('details', $colNames_details, $details);
        }
        $current_count += 4999;
    }
    // set today's amount of players
    setTodaysEntries($lbid, $entries, $rank);
    // compute time rankings
    computeTimeRankings($lbid);
    // fix last_updated in scores and times tables if we just updated
    // a 'yesterday' daily
    if ($yesterday === true) {
        fixDates($lbid);
    }
    checkAltAccounts($alts_to_check, $lbid);
}
<?php

/* Script for updating current leaderboard */
//set_time_limit(0); //Can take long time
require_once 'config/.keys.php';
//Auth key $admin_key is stored here
require_once 'data_functions.php';
//Check if qualified call
if (!isset($_GET['key']) || htmlspecialchars($_GET["key"]) != $admin_key) {
    die("Unauthorized call.");
} else {
    $t = time();
    $chars = getCharacterData();
    // IDS
    $ids = getLeaderboardId(NULL);
    $xml = getLeaderboardXML($ids[1]);
    $data = getLeaderboardData($xml, $chars);
    //NULL -> saving to current leaderboard
    if (saveLeaderboardData(NULL, $data)) {
        echo "Current leaderboard data saved (season " . $ids[0] . "). Time spent on script: " . (time() - $t) . " seconds.<br>\n";
    } else {
        echo "Couldn't save current leaderboard data (season " . $ids[0] . ", leaderboard " . $ids[1] . ")<br>\n";
    }
}