get('/', function() { if (user()) redirect('/world'); echo render('layouts/basic', ['view' => 'pages/hello']); }); /* ============================================================ Auth ============================================================ */ $r->get('/register', 'Actions\Auth::register_get')->middleware('guest_only'); $r->post('/register', 'Actions\Auth::register_post')->middleware('guest_only'); $r->get('/login', 'Actions\Auth::login_get')->middleware('guest_only'); $r->post('/login', 'Actions\Auth::login_post')->middleware('guest_only'); $r->post('/logout', 'Actions\Auth::logout')->middleware('auth_only'); if (env('debug', false)) $r->get('/debug/logout', 'Actions\Auth::logout'); /* ============================================================ Characters ============================================================ */ $r->get('/characters', function() { //echo page('chars/list', ['chars' => user()->char_list()]); })->middleware('must_have_character'); $r->post('/characters', function() { $char_id = (int) ($_POST['char_id'] ?? 0); $action = $_POST['action'] ?? ''; // If the character ID is not a number, or the action is not a string, return a 400. if (!is_numeric($char_id) || !is_string($action)) error_response(400); // If the character ID is 0, return to the list. if ($char_id === 0) { flash('alert_character_list_1', ['', 'No character selected.']); redirect('/characters'); } // If the action is not one of the allowed actions, return a 400. if (!in_array($action, ['select', 'delete'])) error_response(400); // If the action is to select a character, change the user's selected character. if ($action === 'select') { // If the character ID is the current character, do nothing. if ($char_id === user()->char_id || $char_id === 0) { flash('alert_character_list_1', ['info', 'You are already using ' . char()->name . '.']); redirect('/characters'); } if (!Character::belongs_to($char_id, user()->id)) error_response(999); change_user_character($char_id); flash('alert_character_list_1', ['success', 'Switched to ' . char()->name . '!']); } // If the action is to delete a character, move to the confirmation page. if ($action === 'delete') { if (!Character::belongs_to($char_id, user()->id)) error_response(999); //echo page('chars/delete', ['char' => Character::find($char_id)]); exit; } redirect('/characters'); })->middleware('must_have_character'); $r->get('/character/create-first', function() { // If the user already has a character, redirect them to the main page. if (user()->char_count() > 0) redirect('/'); //echo page('chars/first'); })->middleware('auth_only'); $r->post('/character/create', function() { $errors = []; $name = trim($_POST['n'] ?? ''); /* A name is required. A name must be between 3 and 18 characters. A name must contain only alphanumeric characters and spaces. */ if (empty($name) || strlen($name) < 3 || strlen($name) > 18 || !ctype_alnum(str_replace(' ', '', $name))) { $errors['n'][] = 'Name is required and must be between 3 and 18 characters long and contain only alphanumeric characters and spaces.'; } /* A character's name must be unique. */ if (Character::name_exists($name)) $errors['n'][] = 'Name is already taken.'; // If there are errors at this point, send them to the page with errors flashed. if (!empty($errors)) { $GLOBALS['form-errors-create-character'] = $errors; if (isset($_POST['first']) && $_POST['first'] === 'true') { // If this is the first character, return to the first character creation page. //echo page('chars/first'); exit; } else { // If this is not the first character, return to the character list page. //echo page('chars/list', ['chars' => user()->char_list()]); exit; } } if (($char = Character::create(user()->id, $name)) === false) error_response(400); // Create the auxiliary tables $char->create_location(); $char->create_gear(); // Award the Adventurer title. $char->award_title(1); // Set the character as the user's selected character change_user_character($char->id); flash('alert_character_list_1', ['success', 'Character ' . $name . ' created!']); redirect('/characters'); })->middleware('auth_only'); $r->post('/character/delete', function() { $char_id = (int) ($_POST['char_id'] ?? 0); // If the character ID is not a number, return a 400. if (!is_numeric($char_id)) error_response(400); // Ensure the character ID is valid and belongs to the user. if (!Character::belongs_to($char_id, user()->id)) error_response(999); $char = Character::find($char_id); // Confirm the name matches the name of the character. CASE SENSITIVE. if ($char['name'] !== trim($_POST['n'] ?? '')) { flash('alert_character_list_1', ['danger', 'Failed to delete ' . $char['name'] . '. Name confirmation did not match.']); redirect('/characters'); } // Delete the character Character::delete($char_id); // If the character being deleted is the currently selected character, select the first character. if (user()->char_id === $char_id) { $chars = user()->char_list(); if (count($chars) > 0) change_user_character($chars[0]['id']); } flash('alert_character_list_1', ['danger', 'Character ' . $char['name'] . ' deleted.']); redirect('/characters'); })->middleware('must_have_character'); /* ============================================================ World ============================================================ */ $r->get('/world', function() { echo render('layouts/game'); })->middleware('must_have_character'); $r->post('/move', function() { /* This endpoint is used to move the character around the world. The client sends a POST request with the direction they want to move the character. The server will update the character's position in the database and return the new position to the client. We should only be using this endpoint as an AJAX request from the world page. Since we don't need all the character's data to move them, we can just get and update their lcoation using the user's currently selected character ID. */ define('directions', [ [0, -1], // Up [0, 1], // Down [-1, 0], // Left [1, 0] // Right ]); // direction must exist $d = (int) $_POST['direction'] ?? -1; // Update the character's position // 0 = up, 1 = down, 2 = left, 3 = right $x = location('x'); $y = location('y'); if (isset(directions[$d])) { $x += directions[$d][0]; $y += directions[$d][1]; } else { error_response(999); } $r = live_db()->query('UPDATE char_locations SET x = :x, y = :y WHERE char_id = :c', [ ':x' => $x, ':y' => $y, ':c' => user()->char_id ]); if ($r === false) throw new Exception('Failed to move character. (wcmp)'); json_response(['x' => $x, 'y' => $y]); })->middleware('ajax_only')->middleware('must_have_character'); /* ============================================================ UI Components ============================================================ */ $r->get('/ui/stats', function() { echo c_profile_stats(char()); })->middleware('ajax_only')->middleware('must_have_character'); /* ============================================================ Router ============================================================ */ // [code, handler, params, middleware] $l = $r->lookup($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']); if ($l['code'] !== 200) error_response($l['code']); if (!empty($l['middleware'])) foreach ($l['middleware'] as $middleware) $middleware(); $l['handler'](...$l['params'] ?? []); /* ============================================================ Cleanup ============================================================ */ clear_flashes();