authlevel === 1) { $r->get('/admin', 'Admin\donothing'); $r->form('/admin/main', 'Admin\primary'); $r->get('/admin/items', 'Admin\items'); $r->form('/admin/items/:id', 'Admin\edit_item'); $r->get('/admin/drops', 'Admin\drops'); $r->form('/admin/drops/:id', 'Admin\edit_drop'); $r->get('/admin/towns', 'Admin\towns'); $r->form('/admin/towns/:id', 'Admin\edit_town'); $r->get('/admin/monsters', 'Admin\monsters'); $r->form('/admin/monsters/:id', 'Admin\edit_monster'); $r->get('/admin/levels', 'Admin\levels'); $r->post('/admin/levels', 'Admin\edit_level'); $r->get('/admin/spells', 'Admin\spells'); $r->form('/admin/spells/:id', 'Admin\edit_spell'); $r->get('/admin/users', 'Admin\users'); $r->form('/admin/users/:id', 'Admin\edit_user'); $r->form('/admin/news', 'Admin\add_news'); } return $r; } /** * Home page for the admin panel. */ function donothing(): string { page_title('Admin'); return <<
Please note that the control panel has been created mostly as a shortcut for certain individual settings. It is meant for use primarily with editing one thing at a time. If you need to completely replace an entire table (say, to replace all stock monsters with your own new ones), it is suggested that you use a more in-depth database tool such as SQLite Browser.

Also, you should be aware that certain portions of the DK code are dependent on the formatting of certain database results (for example, the special attributes on item drops). While I have attempted to point these out throughout the admin script, you should definitely pay attention and be careful when editing some fields, because mistakes in the database content may result in script errors or your game breaking completely. HTML; } /** * Main settings that get written to .env */ function primary(): string { if (is_post()) { $form = validate($_POST, [ 'gamename' => ['alphanum-spaces'], 'gamesize' => ['int', 'min:5'], 'class1name' => ['alpha-spaces'], 'class2name' => ['alpha-spaces'], 'class3name' => ['alpha-spaces'], 'gameopen' => ['bool'], 'verifyemail' => ['bool'], 'shownews' => ['bool'], 'showonline' => ['bool'], 'showbabble' => ['bool'] ]); if ($form['valid']) { $form = $form['data']; if (($form['gamesize'] % 5) != 0) exit('Map size must be divisible by five.'); // @todo // write changes to .env $page = 'Main settings updated.'; } else { $error_list = ul_from_validate_errors($form['errors']); $page = <<Errors:
{$error_list}

Please go back and try again. HTML; } } else { $page = render('admin/main_settings'); } page_title('Admin: Main Settings'); return $page; } /** * Show the full list of items that can be edited. */ function items(): string { $items = db()->query('SELECT * FROM items ORDER BY id;'); $page = "

Edit Items

Click an item's name or ID to edit it.

\n"; $page .= build_bulk_table($items, 'name', '/admin/items'); page_title('Admin: Items'); return $page; } /** * Shows the form for editing an item via GET, processes edits via POST */ function edit_item(int $id): string { $item = get_item($id); if (is_post()) { $page = handle_edit_form($id, 'items', validate($_POST, [ 'name' => [], 'type' => ['int', 'in:1,2,3'], 'buycost' => ['int', 'min:0'], 'attribute' => ['int', 'min:0'], 'special' => ['default:X'] ])); } else { $page = render('admin/edit_item', ['item' => $item]); } page_title('Admin: Editing '.$item['name']); return $page; } /** * Show the full list of drops that can be edited */ function drops() { $drops = db()->query('SELECT * FROM drops ORDER BY id;'); $page = "

Edit Drops

Click an item's name to edit it.

\n"; $page .= build_bulk_table($drops, 'name', '/admin/drops'); page_title('Admin: Drops'); return $page; } /** * Show the form to edit drops via GET, process those edits via POST */ function edit_drop(int $id): string { $drop = get_drop($id); if (is_post()) { $page = handle_edit_form($id, 'drops', validate($_POST, [ 'name' => [], 'mlevel' => ['int', 'min:1'], 'attribute1' => [], 'attribute2' => ['default:X'], ])); } else { $page = render('admin/edit_drop', ['drop' => $drop]); } page_title('Admin: Editing '.$drop['name']); return $page; } /** * Generate the list of towns that can be edited. */ function towns(): string { $towns = db()->query('SELECT * FROM towns ORDER BY id;'); $page = "

Edit Towns

Click an town's name or ID to edit it.

\n"; $page .= build_bulk_table($towns, 'name', '/admin/towns'); page_title('Admin: Towns'); return $page; } /** * Save any changes to the town made. */ function edit_town(int $id): string { $town = get_town_by_id($id); if (is_post()) { $page = handle_edit_form($id, 'towns', validate($_POST, [ 'name' => [], 'latitude' => ['int', 'min:0', 'max:'.env('game_size')], 'longitude' => ['int', 'min:0', 'max:'.env('game_size')], 'innprice' => ['int', 'min:0'], 'mapprice' => ['int', 'min:0'], 'travelpoints' => ['int', 'min:0'], 'itemslist' => ['optional'] ])); } else { $page = render('admin/edit_town', ['town' => $town]); } page_title('Admin: Editing '.$town['name']); return $page; } /** * List the monsters available to edit. */ function monsters() { $max_level = db()->query('SELECT level FROM monsters ORDER BY level DESC LIMIT 1;')->fetchArray(SQLITE3_ASSOC)['level']; $monsters = db()->query('SELECT * FROM monsters ORDER BY id;'); $page = "

Edit Monsters

"; if ((env('game_size') / 5) !== $max_level) { $page .= "Note: Your highest monster level does not match with your entered map size. Highest monster level should be ".(env('game_size') / 5).", yours is $max_level. Please fix this before opening the game to the public.
"; } else { $page .= "Monster level and map size match. No further actions are required for map compatibility.
"; } $page .= "Click an monster's name or ID to edit it.

\n"; $page .= build_bulk_table($monsters, 'name', '/admin/monsters'); page_title('Admin: Monsters'); return $page; } /** * Handle the actual editing of the monster. */ function edit_monster(int $id): string { $monster = get_monster($id); if (is_post()) { $page = handle_edit_form($id, 'monsters', validate($_POST, [ 'name' => [], 'maxhp' => ['int', 'min:1'], 'maxdam' => ['int', 'min:0'], 'armor' => ['int', 'min:0'], 'level' => ['int', 'min:1'], 'maxexp' => ['int', 'min:0'], 'maxgold' => ['int', 'min:0'], 'immune' => ['in:0,1,2'] ])); } else { $page = render('admin/edit_monster', ['monster' => $monster]); } page_title('Admin: Editing '.$monster['name']); return $page; } /** * List all spells available to edit. */ function spells(): string { $page = "

Edit Spells

Click an spell's name to edit it.

\n"; $spells = db()->query('SELECT * FROM spells ORDER BY id;'); $page .= build_bulk_table($spells, 'name', '/admin/spells'); page_title('Admin: Spells'); return $page; } /** * Handle the editing of an individual spell. */ function edit_spell(int $id): string { $spell = get_spell($id); if (is_post()) { $page = handle_edit_form($id, 'spells', validate($_POST, [ 'name' => [], 'mp' => ['int', 'min:0'], 'attribute' => ['int', 'min:0'], 'type' => ['in:1,2,3,4,5'] ])); } else { $page = render('admin/edit_spell', ['spell' => $spell]); } page_title('Admin: Editing '.$spell['name']); return $page; } /** * List all editable levels. */ function levels(): string { $max_level = db()->query('SELECT id FROM levels ORDER BY id DESC LIMIT 1;')->fetchArray(SQLITE3_ASSOC)['id']; $page = <<Edit Levels Select a level number from the dropdown box to edit it.


HTML; } page_title('Admin: Add News'); return $page; } /** * Build an HTML table containing all columns and rows of a given data structure. Takes a SQLiteResult3 of a SELECT * query. */ function build_bulk_table(SQLite3Result $query_data, string $edit_column, string $edit_link): string { $data = []; $data_count = 0; while ($row = $query_data->fetchArray(SQLITE3_ASSOC)) $data[$data_count++] = $row; if ($data_count === 0) return 'No data.'; $columns = array_diff(array_keys($data[0]), ['password']); $html_parts = [ '', str_repeat('', count($columns)), '' ]; foreach ($columns as $column) { $html_parts[] = ''; } $html_parts[] = ''; $is_edit_column = array_flip(['id', $edit_column]); foreach ($data as $row) { $html_parts[] = ''; foreach ($columns as $column) { $name = make_safe($row[$column]); $html_parts[] = isset($is_edit_column[$column]) ? "" : ""; } $html_parts[] = ''; } $html_parts[] = '
' . make_safe($column === 'id' ? 'ID' : ucfirst($column)) . '
{$name}{$name}
'; return implode('', $html_parts); } /** * Save a row of data to it's table from the data supplied. */ function save_data_row(string $table, array $data, int $id): SQLite3Result|false { $data = array_filter($data, fn($value) => $value !== null && $value !== ''); if (empty($data)) return false; $fields = implode(',', array_map(fn($key) => "`$key`=?", array_keys($data))); $values = array_values($data); $values[] = $id; return db()->query("UPDATE $table SET $fields WHERE id=?", $values); } /** * Handle the result of a generic edit form. */ function handle_edit_form(int $id, string $table, array $form, string $updated_message = ''): string { if ($form['valid']) { save_data_row($table, $form['data'], $id); $page = $updated_message ?: ''.$form['data']['name'].' updated.'; } else { $error_list = ul_from_validate_errors($form['errors']); $page = <<Errors:
{$error_list}

Please go back and try again. HTML; } return $page; }