<commit_before><?php declare(strict_types=1);

namespace Smr;

use AbstractSmrAccount;
use AbstractSmrPlayer;
use Page;
use Smr\Container\DiContainer;
use SmrAccount;
use SmrPlayer;

class Session {

	public const TIME_BEFORE_EXPIRY = 3600;

	private const URL_LOAD_DELAY = [
		'configure_hardware.php' => .4,
		'forces_drop.php' => .4,
		'forces_drop_processing.php' => .5,
		'forces_refresh_processing.php' => .4,
		'sector_jump_processing.php' => .4,
		'sector_move_processing.php' => .4,
		'sector_scan.php' => .4,
		'shop_goods_processing.php' => .4,
		'trader_attack_processing.php' => .75,
		'trader_examine.php' => .75,
	];

	protected Database $db;

	private string $sessionID;
	private int $gameID;
	private array $var;
	private array $commonIDs = [];
	private bool $generate;
	private string $SN;
	private string $lastSN;
	private int $accountID;
	private int $lastAccessed;

	protected ?array $previousAjaxReturns;
	protected array $ajaxReturns = [];

	/**
	 * Return the Smr\Session in the DI container.
	 * If one does not exist yet, it will be created.
	 * This is the intended way to construct this class.
	 */
	public static function getInstance(): self {
		return DiContainer::get(self::class);
	}

	/**
	 * Smr\Session constructor.
	 * Not intended to be constructed by hand. Use Smr\Session::getInstance().
	 */
	public function __construct() {
		// Initialize the db connector here
		$this->db = Database::getInstance();

		// now try the cookie
		$idLength = 32;
		if (isset($_COOKIE['session_id']) && strlen($_COOKIE['session_id']) === $idLength) {
			$this->sessionID = $_COOKIE['session_id'];
		} else {
			// create a new session id
			do {
				$this->sessionID = random_string($idLength);
				$dbResult = $this->db->read('SELECT 1 FROM active_session WHERE session_id = ' . $this->db->escapeString($this->sessionID));
			} while ($dbResult->hasRecord()); //Make sure we haven't somehow clashed with someone else's session.

			// This is a minor hack to make sure that setcookie is not called
			// for CLI programs and tests (to avoid "headers already sent").
			if (headers_sent() === false) {
				setcookie('session_id', $this->sessionID);
			}
		}

		// Delete any expired sessions
		$this->db->write('DELETE FROM active_session WHERE last_accessed < ' . $this->db->escapeNumber(time() - self::TIME_BEFORE_EXPIRY));

		// try to get current session
		$this->SN = Request::get('sn', '');
		$this->fetchVarInfo();

		if (!USING_AJAX && !empty($this->SN) && !empty($this->var[$this->SN])) {
			$var = $this->var[$this->SN];
			$currentPage = $var['url'] == 'skeleton.php' ? $var['body'] : $var['url'];
			$loadDelay = self::URL_LOAD_DELAY[$currentPage] ?? 0;
			$initialTimeBetweenLoads = microtime(true) - $var['PreviousRequestTime'];
			while (($timeBetweenLoads = microtime(true) - $var['PreviousRequestTime']) < $loadDelay) {
				$sleepTime = IRound(($loadDelay - $timeBetweenLoads) * 1000000);
				//echo 'Sleeping for: ' . $sleepTime . 'us';
				usleep($sleepTime);
			}
			if (ENABLE_DEBUG) {
				$this->db->insert('debug', [
					'debug_type' => $this->db->escapeString('Delay: ' . $currentPage),
					'account_id' => $this->db->escapeNumber($this->accountID),
					'value' => $this->db->escapeNumber($initialTimeBetweenLoads),
					'value_2' => $this->db->escapeNumber($timeBetweenLoads),
				]);
			}
		}
	}

	public function fetchVarInfo(): void {
		$dbResult = $this->db->read('SELECT * FROM active_session WHERE session_id = ' . $this->db->escapeString($this->sessionID));
		if ($dbResult->hasRecord()) {
			$dbRecord = $dbResult->record();
			$this->generate = false;
			$this->sessionID = $dbRecord->getField('session_id');
			$this->accountID = $dbRecord->getInt('account_id');
			$this->gameID = $dbRecord->getInt('game_id');
			$this->lastAccessed = $dbRecord->getInt('last_accessed');
			$this->lastSN = $dbRecord->getField('last_sn');
			// We may not have ajax_returns if ajax was disabled
			$this->previousAjaxReturns = $dbRecord->getObject('ajax_returns', true, true);
<commit_msg>Session: simpler logic for load delay<commit_after><?php declare(strict_types=1);

namespace Smr;

use AbstractSmrAccount;
use AbstractSmrPlayer;
use Page;
use Smr\Container\DiContainer;
use SmrAccount;
use SmrPlayer;

class Session {

	public const TIME_BEFORE_EXPIRY = 3600;

	private const URL_LOAD_DELAY = [
		'configure_hardware.php' => .4,
		'forces_drop.php' => .4,
		'forces_drop_processing.php' => .5,
		'forces_refresh_processing.php' => .4,
		'sector_jump_processing.php' => .4,
		'sector_move_processing.php' => .4,
		'sector_scan.php' => .4,
		'shop_goods_processing.php' => .4,
		'trader_attack_processing.php' => .75,
		'trader_examine.php' => .75,
	];

	protected Database $db;

	private string $sessionID;
	private int $gameID;
	private array $var;
	private array $commonIDs = [];
	private bool $generate;
	private string $SN;
	private string $lastSN;
	private int $accountID;
	private int $lastAccessed;

	protected ?array $previousAjaxReturns;
	protected array $ajaxReturns = [];

	/**
	 * Return the Smr\Session in the DI container.
	 * If one does not exist yet, it will be created.
	 * This is the intended way to construct this class.
	 */
	public static function getInstance(): self {
		return DiContainer::get(self::class);
	}

	/**
	 * Smr\Session constructor.
	 * Not intended to be constructed by hand. Use Smr\Session::getInstance().
	 */
	public function __construct() {
		// Initialize the db connector here
		$this->db = Database::getInstance();

		// now try the cookie
		$idLength = 32;
		if (isset($_COOKIE['session_id']) && strlen($_COOKIE['session_id']) === $idLength) {
			$this->sessionID = $_COOKIE['session_id'];
		} else {
			// create a new session id
			do {
				$this->sessionID = random_string($idLength);
				$dbResult = $this->db->read('SELECT 1 FROM active_session WHERE session_id = ' . $this->db->escapeString($this->sessionID));
			} while ($dbResult->hasRecord()); //Make sure we haven't somehow clashed with someone else's session.

			// This is a minor hack to make sure that setcookie is not called
			// for CLI programs and tests (to avoid "headers already sent").
			if (headers_sent() === false) {
				setcookie('session_id', $this->sessionID);
			}
		}

		// Delete any expired sessions
		$this->db->write('DELETE FROM active_session WHERE last_accessed < ' . $this->db->escapeNumber(time() - self::TIME_BEFORE_EXPIRY));

		// try to get current session
		$this->SN = Request::get('sn', '');
		$this->fetchVarInfo();

		if (!USING_AJAX && !empty($this->SN) && !empty($this->var[$this->SN])) {
			$var = $this->var[$this->SN];
			$currentPage = $var['url'] == 'skeleton.php' ? $var['body'] : $var['url'];
			$loadDelay = self::URL_LOAD_DELAY[$currentPage] ?? 0;
			$timeBetweenLoads = microtime(true) - $var['PreviousRequestTime'];
			if ($timeBetweenLoads < $loadDelay) {
				$sleepTime = IRound(($loadDelay - $timeBetweenLoads) * 1000000);
				//echo 'Sleeping for: ' . $sleepTime . 'us';
				usleep($sleepTime);
			}
			if (ENABLE_DEBUG) {
				$this->db->insert('debug', [
					'debug_type' => $this->db->escapeString('Delay: ' . $currentPage),
					'account_id' => $this->db->escapeNumber($this->accountID),
					'value' => $this->db->escapeNumber($timeBetweenLoads),
				]);
			}
		}
	}

	public function fetchVarInfo(): void {
		$dbResult = $this->db->read('SELECT * FROM active_session WHERE session_id = ' . $this->db->escapeString($this->sessionID));
		if ($dbResult->hasRecord()) {
			$dbRecord = $dbResult->record();
			$this->generate = false;
			$this->sessionID = $dbRecord->getField('session_id');
			$this->accountID = $dbRecord->getInt('account_id');
			$this->gameID = $dbRecord->getInt('game_id');
			$this->lastAccessed = $dbRecord->getInt('last_accessed');
			$this->lastSN = $dbRecord->getField('last_sn');
			// We may not have ajax_returns if ajax was disabled
			$this->previousAjaxReturns = $dbRecord->getObject('ajax_returns', true, true);

{'fraction_non_alphanumeric': 0.1666876020600427, 'fraction_numerical': 0.007159904534606206, 'mean_word_length': 4.173489278752436, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 132, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '203355', 'n_tokens_mistral': 2942, 'n_tokens_neox': 2602, 'n_words': 615}