feat(servei): reinici real via execv

This commit is contained in:
JailDesigner committed 2026-05-25 17:29:42 +02:00
1 parent 774874deb0
commit a8ccb3fae1
5 files changed
+97 -11

No files matched your search

+1
View File
@@ -65,6 +65,7 @@ set(ENTITY_SOURCES
set(APP_SOURCES set(APP_SOURCES
source/main.cpp source/main.cpp
source/core/system/director.cpp source/core/system/director.cpp
source/core/system/relaunch.cpp
source/core/audio/audio.cpp source/core/audio/audio.cpp
source/core/audio/audio_adapter.cpp source/core/audio/audio_adapter.cpp
source/core/audio/audio_effects.cpp source/core/audio/audio_effects.cpp
+51
View File
@@ -0,0 +1,51 @@
// relaunch.cpp - Implementació del reinici en calent
#include "core/system/relaunch.hpp"
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <iostream>
#ifdef _WIN32
#include <process.h> // _execv
#else
#include <unistd.h> // execv
#endif
namespace {
// Estat global (process-scope). Aquesta TU és l'única que gestiona el
// reinici, així que els statics interns no s'escapen.
char** g_argv = nullptr;
bool g_requested = false;
} // namespace
namespace System::Relaunch {
void setArgv(int /*argc*/, char** argv) {
g_argv = argv;
}
void request() {
g_requested = true;
}
void execIfRequested() {
if (!g_requested || g_argv == nullptr || g_argv[0] == nullptr) {
return;
}
std::cout << "[Relaunch] Reiniciant " << g_argv[0] << "...\n";
#ifdef _WIN32
_execv(g_argv[0], g_argv);
#else
execv(g_argv[0], g_argv);
#endif
// Si arribem aquí, execv ha fallat. Tots els subsistemes ja estan
// destruïts; sortim amb error i el shell rebrà el codi.
std::cerr << "[Relaunch] Ha fallat: " << std::strerror(errno) << '\n';
std::exit(EXIT_FAILURE);
}
} // namespace System::Relaunch
+28
View File
@@ -0,0 +1,28 @@
// relaunch.hpp - Reinici en calent del procés (execv)
//
// Helper desacoblat perquè el menú de servei demani un reinici sense conèixer
// ni Director ni main.cpp. Patró d'ús:
//
// main() → Relaunch::setArgv(argc, argv) (a l'arrencada)
// ServiceMenu → Relaunch::request() (en activar "Reiniciar")
// main() → Relaunch::execIfRequested() (al final de SDL_AppQuit)
//
// L'execv() reemplaça el procés actual: si torna, ha fallat. Es crida després
// de destruir el Director per garantir que GPU/àudio/SDL ja són morts.
#pragma once
namespace System::Relaunch {
// Emmagatzema l'argv original. Cal cridar-ho una vegada des de main.
void setArgv(int argc, char** argv);
// Demana un reinici (no actua immediatament; només marca el flag).
void request();
// Si hi ha reinici demanat i tenim argv vàlid, fa execv. En cas d'èxit
// no torna. Si execv falla, registra l'error per stderr i surt amb
// EXIT_FAILURE (no podem reprendre — els subsistemes ja estan destruïts).
void execIfRequested();
} // namespace System::Relaunch
+9 -9
View File
@@ -15,6 +15,7 @@
#include "core/rendering/screen.hpp" #include "core/rendering/screen.hpp"
#include "core/rendering/text.hpp" #include "core/rendering/text.hpp"
#include "core/system/event_buffer.hpp" #include "core/system/event_buffer.hpp"
#include "core/system/relaunch.hpp"
#include "core/ui/ui_config.hpp" #include "core/ui/ui_config.hpp"
#include "game/gameplay_config.hpp" #include "game/gameplay_config.hpp"
#include "game/options.hpp" #include "game/options.hpp"
@@ -1228,18 +1229,17 @@ auto ServiceMenu::buildSystem() -> Page {
page.items.push_back(makeLabel( page.items.push_back(makeLabel(
std::string("ver.") + Texts::VERSION + " (" + Version::GIT_HASH + ")")); std::string("ver.") + Texts::VERSION + " (" + Version::GIT_HASH + ")"));
// Reiniciar el joc: tornem al LOGO amb `reset=true` perquè LogoScene // Reinici real del procés: marquem el flag i empenyem QUIT. SDL_AppQuit
// ature música i sons abans de mostrar-se. Tanquem el menú a la // destruirà tots els subsistemes i llavors farà execv (al final, amb tot
// mateixa acció — Director recollirà la transició al final del frame // net) per substituir el binari per ell mateix. Si execv falla, el procés
// via `takePendingSceneRequest()` i reconstruirà l'escena. // surt amb error.
page.items.push_back(makeAction( page.items.push_back(makeAction(
"menu.items.restart", "menu.items.restart",
[]() { []() {
ServiceMenu::get().requestSceneTransition(ExitRequest{ System::Relaunch::request();
.next = SceneId::LOGO, SDL_Event quit_event{};
.params = LogoParams{.reset = true}, quit_event.type = SDL_EVENT_QUIT;
}); EventBuffer::events.push_back(quit_event);
ServiceMenu::get().close();
})); }));
page.items.push_back(makeAction( page.items.push_back(makeAction(
+8 -2
View File
@@ -7,11 +7,13 @@
#include "core/system/director.hpp" #include "core/system/director.hpp"
#include "core/system/event_buffer.hpp" #include "core/system/event_buffer.hpp"
#include "core/system/relaunch.hpp"
#include "utils/log.hpp" #include "utils/log.hpp"
auto SDL_AppInit(void** appstate, int argc, char* argv[]) -> SDL_AppResult { auto SDL_AppInit(void** appstate, int argc, char* argv[]) -> SDL_AppResult {
(void)argc; // Desem argv perquè el menú de servei pugui demanar un reinici en calent
(void)argv; // (execv) sense haver de conèixer ni Director ni main.
System::Relaunch::setArgv(argc, argv);
// Logger primer de tot perquè l'arrencada del Director ja emet logs. // Logger primer de tot perquè l'arrencada del Director ja emet logs.
Log::init(); Log::init();
@@ -45,6 +47,10 @@ void SDL_AppQuit(void* appstate, SDL_AppResult result) {
{ {
std::unique_ptr<Director> director(static_cast<Director*>(appstate)); std::unique_ptr<Director> director(static_cast<Director*>(appstate));
} }
// Si el menú va demanar reinici, fem execv ara que tot està net. En èxit
// no torna (el procés és reemplaçat) i el "Bye!" no s'imprimeix — correcte,
// no és una sortida.
System::Relaunch::execIfRequested();
// Marcador de fi: ha de ser l'ÚLTIMA línia de log del programa. // Marcador de fi: ha de ser l'ÚLTIMA línia de log del programa.
Log::raw(Log::Level::INFO, "Bye!"); Log::raw(Log::Level::INFO, "Bye!");
} }