From 6876cbff9545468c300d9861f52736125815805c Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Sun, 28 Sep 2025 03:10:12 +0100 Subject: [PATCH] [Add] Implement system boot sequence screen. --- assets/boot_messages.txt | 24 +++++++++++++++ client/src/game_state.cpp | 36 +++++++++++++--------- client/src/game_state.h | 2 ++ client/src/ui/boot_sequence.cpp | 54 +++++++++++++++++++++++++++++++++ client/src/ui/boot_sequence.h | 21 +++++++++++++ 5 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 assets/boot_messages.txt create mode 100644 client/src/ui/boot_sequence.cpp create mode 100644 client/src/ui/boot_sequence.h diff --git a/assets/boot_messages.txt b/assets/boot_messages.txt new file mode 100644 index 0000000..9854a0f --- /dev/null +++ b/assets/boot_messages.txt @@ -0,0 +1,24 @@ +[ 0.000000] Bettola version 6.1.0-bettola (dev@bettola) +[ 0.000000] Command line: BOOT_IMAGE=/vmbettola-6.1.0 ro quiet +[ 0.134589] ACPI: PM-Timer IO Port: 0x808 +[ 0.345821] pci 0000:00:02.0: vgaarb: setting as boot VGA device +[ 0.345911] pci 0000:00:03.0: enp0s3: identified as [B77A:1337] +[ 0.582190] systemd[1]: Starting systemd-journald.service... +[ 0.621337] systemd-journald[218]: Journal started. +[ 1.123456] EXT4-fs (sda1): mounted filesystem with ordered data mode. +[ 1.567890] systemd[1]: Reached target Local File Systems. +[ 1.890123] systemd[1]: Starting systemd-logind.service... +[ 2.101122] systemd[1]: Starting NetworkManager.service... +[ 2.334455] NetworkManager[310]: [1678886400.123] NetworkManager (version 1.40.0) is +starting... +[ 2.800100] enp0s3: Link is up at 1000 Mbps, full duplex. +[ 3.123456] systemd[1]: Reached target Network. +[ 3.500000] systemd[1]: Starting Bettola Daemon... +[ 3.600000] bettolad[420]: Initializing VFS... +[ 3.700000] bettolad[420]: Generating world... +[ 4.100000] bettolad[420]: World generation complete. Seed: 0xDEADBEEF +[ 4.200000] bettolad[420]: Listening on 0.0.0.0:1337 +[ 4.500000] systemd[1]: Started Bettola Daemon. +[ 4.800000] systemd[1]: Reached target Multi-User System. +[ 5.000000] systemd[1]: Starting Graphical Interface. +[ 5.500000] bettolac-greeter: Starting display manager... diff --git a/client/src/game_state.cpp b/client/src/game_state.cpp index 8ad628b..1e80c9a 100644 --- a/client/src/game_state.cpp +++ b/client/src/game_state.cpp @@ -12,6 +12,7 @@ #include "ui/desktop.h" #include "ui/ui_window.h" #include +#include void GameState::_init_desktop(void) { _desktop = std::make_unique(); @@ -92,24 +93,29 @@ void GameState::update(void) { _main_menu.reset(); /* Free mem. */ if(_current_screen == Screen::BOOTING) { - fprintf(stdout, "Starting in single-player mode...\n"); - std::thread server_thread(&GameState::_run_server, this); - server_thread.detach(); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - - if(!_network->connect("127.0.0.1", 1337)) { - /* TODO: Handle connection failure. */ - } - /* TODO: transition to book screen. */ - _current_screen = Screen::DESKTOP; - _init_desktop(); + _boot_sequence = std::make_unique(); } } break; } - case Screen::BOOTING: - /* TODO: */ + case Screen::BOOTING: { + if(!_boot_sequence) break; /* Shouldn't happen. */ + if(_boot_sequence->is_finished()) { + /* Connect to server. */ + fprintf(stdout, "Starting in single-player mode...\n"); + std::thread server_thread(&GameState::_run_server, this); + server_thread.detach(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + if(!_network->connect("127.0.0.1", 1337)) { + /* TODO: Handle connection failure. */ + } + _current_screen = Screen::DESKTOP; + _init_desktop(); + _boot_sequence.reset(); /* Free mem. */ + } break; + } case Screen::DESKTOP: { std::string server_msg; while(_network->poll_message(server_msg)) { @@ -157,7 +163,9 @@ void GameState::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer } break; case Screen::BOOTING: - /* TODO: */ + if(_boot_sequence) { + _boot_sequence->render(txt_renderer, screen_height); + } break; case Screen::DESKTOP: if(_desktop) { diff --git a/client/src/game_state.h b/client/src/game_state.h index e7effb9..702f80d 100644 --- a/client/src/game_state.h +++ b/client/src/game_state.h @@ -4,6 +4,7 @@ class ClientNetwork; class Desktop; +class BootSequence; class MainMenu; class ShapeRenderer; class TextRenderer; @@ -30,6 +31,7 @@ public: private: std::unique_ptr _network; std::unique_ptr _desktop; + std::unique_ptr _boot_sequence; std::unique_ptr _main_menu; Screen _current_screen; diff --git a/client/src/ui/boot_sequence.cpp b/client/src/ui/boot_sequence.cpp new file mode 100644 index 0000000..4a1f8bd --- /dev/null +++ b/client/src/ui/boot_sequence.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include "gfx/txt_renderer.h" + +#include "boot_sequence.h" +#include + +BootSequence::BootSequence(void) { + /* Load boot messages. */ + std::ifstream boot_file("assets/boot_messages.txt"); + if(!boot_file.is_open()) { + printf("ERROR: Failed to open assets/boot_messages.txt\n"); + _messages.push_back("ERROR: boot_messages.txt not found."); + } else { + std::string line; + while(std::getline(boot_file, line)) { + if(!line.empty()) { + _messages.push_back(line); + } + } + } + + /* Init timings. */ + _start_time = SDL_GetTicks(); + _line_interval_ms = 150; /* 150ms between each line. */ + /* Total duration is time for all lines plus a 2-second pause at the end. */ + _total_duration_ms = (_messages.size() * _line_interval_ms) + 2000; +} + +BootSequence::~BootSequence(void) {} + +bool BootSequence::is_finished(void) { + return (SDL_GetTicks() - _start_time) >= _total_duration_ms; +} + +void BootSequence::render(TextRenderer* txt_renderer, int screen_height) { + const Color text_color = { 0.9f, 0.9f, 0.9f }; /* grey/white */ + const float line_height = 18.0f; + const float padding = 15.0f; + + Uint32 elapsed_time = SDL_GetTicks() - _start_time; + int lines_to_show = elapsed_time / _line_interval_ms; + + if(lines_to_show > _messages.size()) { + lines_to_show = _messages.size(); + } + + for(int i = 0; i < lines_to_show; ++i) { + float y_pos = (screen_height - padding) - (i * line_height); + txt_renderer->render_text(_messages[i].c_str(), padding, y_pos, 1.0f, text_color); + } +} diff --git a/client/src/ui/boot_sequence.h b/client/src/ui/boot_sequence.h new file mode 100644 index 0000000..a8aeeb1 --- /dev/null +++ b/client/src/ui/boot_sequence.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +#include "gfx/txt_renderer.h" + +class BootSequence { +public: + BootSequence(void); + ~BootSequence(void); + + bool is_finished(void); + void render(TextRenderer* txt_renderer, int screen_height); +private: + std::vector _messages; + Uint32 _start_time; + Uint32 _line_interval_ms; /* Time for each line to appear. */ + Uint32 _total_duration_ms; /* Total time for the whole sequence. */ +};