$b) { $template = str_replace("{{{$a}}}", $b, $template); } return $template; } function getmicrotime() { // Used for timing script operations. list($usec, $sec) = explode(" ",microtime()); return ((float)$usec + (float)$sec); } function prettydate($uglydate) { // Change the MySQL date format (YYYY-MM-DD) into something friendlier. return date("F j, Y", mktime(0,0,0,substr($uglydate, 5, 2),substr($uglydate, 8, 2),substr($uglydate, 0, 4))); } function prettyforumdate($uglydate) { // Change the MySQL date format (YYYY-MM-DD) into something friendlier. return date("F j, Y", mktime(0,0,0,substr($uglydate, 5, 2),substr($uglydate, 8, 2),substr($uglydate, 0, 4))); } function is_email($email) { // Thanks to "mail(at)philipp-louis.de" from php.net! return(preg_match("/^[-_.[:alnum:]]+@((([[:alnum:]]|[[:alnum:]][[:alnum:]-]*[[:alnum:]])\.)+(ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|com|coop|cr|cs|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|in|info|int|io|iq|ir|is|it|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mil|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no|np|nr|nt|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)$|(([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5])\.){3}([0-9][0-9]?|[0-1][0-9][0-9]|[2][0-4][0-9]|[2][5][0-5]))$/i",$email)); } function makesafe($d) { return htmlentities($d); } /** * Finalize page and output to browser. */ function admindisplay($content, $title) { global $userrow, $controlrow; if (!isset($controlrow)) { $query = db()->query('SELECT * FROM control WHERE id=1 LIMIT 1;'); $controlrow = $query->fetchArray(SQLITE3_ASSOC); } $page = parsetemplate(gettemplate("admin"), [ "title" => $title, "content" => $content, "totaltime" => round(getmicrotime() - START, 4), "numqueries" => db()->count, "version" => VERSION, "build" => BUILD ]); echo "\n" . $page; exit; } function display($content, $title, $topnav=true, $leftnav=true, $rightnav=true, $badstart=false) { // Finalize page and output to browser. global $numqueries, $userrow, $controlrow, $version, $build; if (!isset($controlrow)) { $query = db()->query('SELECT * FROM control WHERE id=1 LIMIT 1;'); $controlrow = $query->fetchArray(SQLITE3_ASSOC); } if ($badstart == false) { global $starttime; } else { $starttime = $badstart; } if ($rightnav == true) { $rightnav = gettemplate("rightnav"); } else { $rightnav = ""; } if ($leftnav == true) { $leftnav = gettemplate("leftnav"); } else { $leftnav = ""; } if ($topnav == true) { $topnav = "\"Log \"Help\""; } else { $topnav = "\"Log \"Register\" \"Help\""; } if (isset($userrow)) { // Get userrow again, in case something has been updated. $userquery = db()->query('SELECT * FROM users WHERE id = ? LIMIT 1;', [$userrow['id']]); unset($userrow); $userrow = $userquery->fetchArray(SQLITE3_ASSOC); // Current town name. if ($userrow["currentaction"] == "In Town") { $townquery = db()->query('SELECT * FROM towns WHERE latitude = ? AND longitude = ? LIMIT 1;', [$userrow["latitude"], $userrow["longitude"]]); $townrow = $townquery->fetchArray(SQLITE3_ASSOC); $userrow["currenttown"] = "Welcome to ".$townrow["name"].".

"; } else { $userrow["currenttown"] = ""; } $userrow["forumslink"] = "Forum
"; // Format various userrow stuffs... if ($userrow["latitude"] < 0) { $userrow["latitude"] = $userrow["latitude"] * -1 . "S"; } else { $userrow["latitude"] .= "N"; } if ($userrow["longitude"] < 0) { $userrow["longitude"] = $userrow["longitude"] * -1 . "W"; } else { $userrow["longitude"] .= "E"; } $userrow["experience"] = number_format($userrow["experience"]); $userrow["gold"] = number_format($userrow["gold"]); if ($userrow["authlevel"] == 1) { $userrow["adminlink"] = "Admin
"; } else { $userrow["adminlink"] = ""; } // HP/MP/TP bars. $stathp = ceil($userrow["currenthp"] / $userrow["maxhp"] * 100); if ($userrow["maxmp"] != 0) { $statmp = ceil($userrow["currentmp"] / $userrow["maxmp"] * 100); } else { $statmp = 0; } $stattp = ceil($userrow["currenttp"] / $userrow["maxtp"] * 100); $stattable = "\n"; $stattable .= "
\n"; $stattable .= "
\n"; if ($stathp >= 66) { $stattable .= "
\"\"
"; } if ($stathp < 66 && $stathp >= 33) { $stattable .= "
\"\"
"; } if ($stathp < 33) { $stattable .= "
\"\"
"; } $stattable .= "
\n"; $stattable .= "
\n"; if ($statmp >= 66) { $stattable .= "
\"\"
"; } if ($statmp < 66 && $statmp >= 33) { $stattable .= "
\"\"
"; } if ($statmp < 33) { $stattable .= "
\"\"
"; } $stattable .= "
\n"; $stattable .= "
\n"; if ($stattp >= 66) { $stattable .= "
\"\"
"; } if ($stattp < 66 && $stattp >= 33) { $stattable .= "
\"\"
"; } if ($stattp < 33) { $stattable .= "
\"\"
"; } $stattable .= "
HPMPTP
\n"; $userrow["statbars"] = $stattable; // Now make numbers stand out if they're low. if ($userrow["currenthp"] <= ($userrow["maxhp"]/5)) { $userrow["currenthp"] = "*".$userrow["currenthp"]."*"; } if ($userrow["currentmp"] <= ($userrow["maxmp"]/5)) { $userrow["currentmp"] = "*".$userrow["currentmp"]."*"; } $spellquery = db()->query('SELECT id, name, type FROM spells;'); $userspells = explode(",",$userrow["spells"]); $userrow["magiclist"] = ""; while ($spellrow = $spellquery->fetchArray(SQLITE3_ASSOC)) { $spell = false; foreach($userspells as $a => $b) { if ($b == $spellrow["id"] && $spellrow["type"] == 1) { $spell = true; } } if ($spell == true) { $userrow["magiclist"] .= "".$spellrow["name"]."
"; } } if ($userrow["magiclist"] == "") { $userrow["magiclist"] = "None"; } // Travel To list. $townslist = explode(",",$userrow["towns"]); $townquery2 = db()->query('SELECT * FROM towns ORDER BY id;'); $userrow["townslist"] = ""; while ($townrow2 = $townquery2->fetchArray(SQLITE3_ASSOC)) { $town = false; foreach($townslist as $a => $b) { if ($b == $townrow2["id"]) { $town = true; } } if ($town == true) { $userrow["townslist"] .= "".$townrow2["name"]."
\n"; } } } else { $userrow = []; } $page = parsetemplate(gettemplate("primary"), [ "dkgamename" => $controlrow["gamename"], "title" => $title, "content" => $content, "rightnav" => parsetemplate($rightnav, $userrow), "leftnav" => parsetemplate($leftnav, $userrow), "topnav" => $topnav, "totaltime" => round(getmicrotime() - START, 4), "numqueries" => db()->count, "version" => VERSION, "build" => BUILD ]); echo "\n" . $page; exit; } function checkcookies() { $row = false; if (isset($_COOKIE["dkgame"])) { // COOKIE FORMAT: // {ID} {USERNAME} {PASSWORDHASH} {REMEMBERME} $theuser = explode(" ",$_COOKIE["dkgame"]); $query = db()->query('SELECT * FROM users WHERE id = ? AND username = ? AND password = ? LIMIT 1;', [$theuser[0], $theuser[1], $theuser[2]]); if ($query === false) { set_cookie('dkgame', '', -3600); die("Invalid cookie data. Please log in again."); } $row = $query->fetchArray(SQLITE3_ASSOC); set_cookie('dkgame', implode(" ", $theuser), (int) $theuser[3] === 1 ? time() + 31536000 : 0); db()->exec('UPDATE users SET onlinetime = CURRENT_TIMESTAMP WHERE id = ?;', [$theuser[0]]); } return $row; } /** * Set a cookie with secure and HTTP-only flags. */ function set_cookie($name, $value, $expires) { setcookie($name, $value, [ 'expires' => $expires, 'path' => '/', 'domain' => '', // Defaults to the current domain 'secure' => true, // Ensure the cookie is only sent over HTTPS 'httponly' => true, // Prevent access to cookie via JavaScript 'samesite' => 'Strict' // Enforce SameSite=Strict ]); } /** * Get the current control row from the database. */ function get_control_row(): array|false { $query = db()->query('SELECT * FROM control WHERE id = 1 LIMIT 1;'); if ($query === false) return false; return $query->fetchArray(SQLITE3_ASSOC); } /** * Get a town's data by it's coordinates. */ function get_town_by_xy(int $x, int $y): array|false { $query = db()->query('SELECT * FROM towns WHERE longitude = ? AND latitude = ? LIMIT 1;', [$x, $y]); if ($query === false) return false; return $query->fetchArray(SQLITE3_ASSOC); } /** * Get a town's data by it's ID. */ function get_town_by_id(int $id): array|false { $query = db()->query('SELECT * FROM towns WHERE id = ? LIMIT 1;', [$id]); if ($query === false) return false; return $query->fetchArray(SQLITE3_ASSOC); } /** * Get an item by it's ID. */ function get_item(int $id): array|false { $query = db()->query('SELECT * FROM items WHERE id=? LIMIT 1;', [$id]); if ($query === false) return false; return $query->fetchArray(SQLITE3_ASSOC); } /** * Get a drop by it's ID. */ function get_drop(int $id): array|false { $query = db()->query('SELECT * FROM drops WHERE id=? LIMIT 1;', [$id]); if ($query === false) return false; return $query->fetchArray(SQLITE3_ASSOC); } /** * Get a spell by it's ID. */ function get_spell(int $id): array|false { $query = db()->query('SELECT * FROM spells WHERE id=? LIMIT 1;', [$id]); if ($query === false) return false; return $query->fetchArray(SQLITE3_ASSOC); } /** * Get a monster by it's ID. */ function get_monster(int $id): array|false { $query = db()->query('SELECT * FROM monsters WHERE id=? LIMIT 1;', [$id]); if ($query === false) return false; return $query->fetchArray(SQLITE3_ASSOC); } /** * Translate a Specials keyword to it's string. */ function special_to_string(string $special): string { return match ($special) { 'maxhp' => 'Max HP', 'maxmp' => 'Max MP', 'maxtp' => 'Max TP', 'goldbonus' => 'Gold Bonus (%)', 'expbonus' => 'Experience Bonus (%)', 'strength' => 'Strength', 'dexterity' => 'Dexterity', 'attackpower' => 'Attack Power', 'defensepower' => 'Defense Power', default => $special }; } /** * Generate a pretty dope token. */ function token($length = 32): string { return bin2hex(random_bytes($length)); } /** * Validate any given array of data against rules. Returns [valid, data, error]. Data contains the trimmed * values from the input array. Note: all fields with rules are assumed to be required, unless the optional * rule is used. * * Example: ['required', 'no-trim', 'length:5-20', 'alphanum-spaces'] */ function validate(array $input_data, array $rules): array { $data = []; $errors = []; foreach ($rules as $field => $field_rules) { $value = $input_data[$field] ?? null; $field_name = ucfirst(str_replace('_', ' ', $field)); $is_required = true; $default_value = null; if (in_array('optional', $field_rules)) { $is_required = false; } foreach ($field_rules as $rule) { if (strpos($rule, 'default:') === 0) { $default_value = substr($rule, 8); break; } } if (($value === null || $value === '') && $default_value !== null) { $value = $default_value; } if (($value === null || $value === '') && !$is_required) continue; if ($is_required && ($value === null || $value === '')) { $errors[$field][] = "{$field_name} is required."; continue; } if (!in_array('no-trim', $field_rules)) { $value = trim($value); } $data[$field] = $value; foreach ($field_rules as $rule) { // Parse rule and arguments if (strpos($rule, ':') !== false) { list($rule_name, $rule_args) = explode(':', $rule, 2); } else { $rule_name = $rule; $rule_args = null; } if ($rule_name === 'optional') continue; switch ($rule_name) { case 'bool': if (!isset($input_data[$field]) || empty($value)) { $value = false; } else { $value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); if ($value === null) { $errors[$field][] = "{$field_name} must be a valid boolean value."; } } break; case 'length': list($min, $max) = explode('-', $rule_args); $len = strlen((string)$value); if ($len < $min || $len > $max) { $errors[$field][] = "{$field_name} must be between {$min} and {$max} characters."; } break; case 'alphanum': if (!preg_match('/^[a-zA-Z0-9]+$/', $value)) { $errors[$field][] = "{$field_name} must contain only letters and numbers."; } break; case 'alpha': if (!preg_match('/^[a-zA-Z]+$/', $value)) { $errors[$field][] = "{$field_name} must contain only letters and numbers."; } break; case 'alphanum-spaces': if (!preg_match('/^[a-zA-Z0-9\s_]+$/', $value)) { $errors[$field][] = "{$field_name} must contain only letters, numbers, spaces, and underscores."; } break; case 'alpha-spaces': if (!preg_match('/^[a-zA-Z\s_]+$/', $value)) { $errors[$field][] = "{$field_name} must contain only letters, numbers, spaces, and underscores."; } break; case 'email': if (!filter_var($value, FILTER_VALIDATE_EMAIL)) { $errors[$field][] = "{$field_name} must be a valid email address."; } break; case 'int': if (!filter_var($value, FILTER_VALIDATE_INT)) { $errors[$field][] = "{$field_name} must be an integer."; } break; case 'min': if ($value < $rule_args) { $errors[$field][] = "{$field_name} must be at least {$rule_args}."; } break; case 'max': if ($value > $rule_args) { $errors[$field][] = "{$field_name} must be no more than {$rule_args}."; } break; case 'regex': if (!preg_match($rule_args, $value)) { $errors[$field][] = "{$field_name} does not match the required pattern."; } break; case 'in': $options = explode(',', $rule_args); if (!in_array($value, $options)) { $errors[$field][] = "{$field_name} must be one of: " . implode(', ', $options); } break; case 'confirm': $field_to_confirm = substr($field, 8); $confirm_value = $data[$field_to_confirm] ?? ''; $confirm_field_name = ucfirst(str_replace('_', ' ', $field_to_confirm)); if ($value !== $confirm_value) { $errors[$field][] = "{$field_name} must match {$confirm_field_name}."; } break; case 'unique': list($table, $column) = explode(',', $rule_args, 2); if (db()->exists($table, $column, $value)) { $errors[$field][] = "{$field_name} must be unique."; } break; } } } foreach ($input_data as $field => $value) { if (!isset($data[$field])) $data[$field] = trim($value); } return [ 'valid' => empty($errors), 'data' => $data, 'errors' => $errors ]; } /** * Generates a ul list from `validate()`'s errors. */ function ul_from_validate_errors(array $errors): string { $string = ''; }