[Add] Implement system boot sequence screen.

This commit is contained in:
Ritchie Cunningham 2025-09-28 03:10:12 +01:00
parent e72cc987ff
commit 6876cbff95
5 changed files with 123 additions and 14 deletions

24
assets/boot_messages.txt Normal file
View File

@ -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]: <info> [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...

View File

@ -12,6 +12,7 @@
#include "ui/desktop.h" #include "ui/desktop.h"
#include "ui/ui_window.h" #include "ui/ui_window.h"
#include <ui/main_menu.h> #include <ui/main_menu.h>
#include <ui/boot_sequence.h>
void GameState::_init_desktop(void) { void GameState::_init_desktop(void) {
_desktop = std::make_unique<Desktop>(); _desktop = std::make_unique<Desktop>();
@ -92,24 +93,29 @@ void GameState::update(void) {
_main_menu.reset(); /* Free mem. */ _main_menu.reset(); /* Free mem. */
if(_current_screen == Screen::BOOTING) { if(_current_screen == Screen::BOOTING) {
fprintf(stdout, "Starting in single-player mode...\n"); _boot_sequence = std::make_unique<BootSequence>();
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();
} }
} }
break; break;
} }
case Screen::BOOTING: case Screen::BOOTING: {
/* TODO: */ 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; break;
}
case Screen::DESKTOP: { case Screen::DESKTOP: {
std::string server_msg; std::string server_msg;
while(_network->poll_message(server_msg)) { while(_network->poll_message(server_msg)) {
@ -157,7 +163,9 @@ void GameState::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer
} }
break; break;
case Screen::BOOTING: case Screen::BOOTING:
/* TODO: */ if(_boot_sequence) {
_boot_sequence->render(txt_renderer, screen_height);
}
break; break;
case Screen::DESKTOP: case Screen::DESKTOP:
if(_desktop) { if(_desktop) {

View File

@ -4,6 +4,7 @@
class ClientNetwork; class ClientNetwork;
class Desktop; class Desktop;
class BootSequence;
class MainMenu; class MainMenu;
class ShapeRenderer; class ShapeRenderer;
class TextRenderer; class TextRenderer;
@ -30,6 +31,7 @@ public:
private: private:
std::unique_ptr<ClientNetwork> _network; std::unique_ptr<ClientNetwork> _network;
std::unique_ptr<Desktop> _desktop; std::unique_ptr<Desktop> _desktop;
std::unique_ptr<BootSequence> _boot_sequence;
std::unique_ptr<MainMenu> _main_menu; std::unique_ptr<MainMenu> _main_menu;
Screen _current_screen; Screen _current_screen;

View File

@ -0,0 +1,54 @@
#include <fstream>
#include <string>
#include <vector>
#include <cstdio>
#include "gfx/txt_renderer.h"
#include "boot_sequence.h"
#include <SDL3/SDL_timer.h>
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);
}
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <vector>
#include <string>
#include <SDL3/SDL.h>
#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<std::string> _messages;
Uint32 _start_time;
Uint32 _line_interval_ms; /* Time for each line to appear. */
Uint32 _total_duration_ms; /* Total time for the whole sequence. */
};