merge reinici-real-servei: reinici real via execv + neteja fake reset
This commit is contained in:
commit
3db0ed4457
10 files changed
+103
-67
No files matched your search
@@ -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
|
||||||
|
|||||||
@@ -466,16 +466,6 @@ void Director::processMenuInput() {
|
|||||||
|
|
||||||
void Director::switchSceneIfRequested() {
|
void Director::switchSceneIfRequested() {
|
||||||
if (!current_scene_) { return; }
|
if (!current_scene_) { return; }
|
||||||
// Prioritat al menú: si un item del ServiceMenu ha encuat una
|
|
||||||
// transició (típicament "Reiniciar" → LOGO amb reset), s'aplica
|
|
||||||
// primer. Substitueix l'`ExitRequest` que pugui haver publicat
|
|
||||||
// l'escena en aquest mateix frame — l'usuari ha cancel·lat l'estat
|
|
||||||
// de joc explícitament i no ens interessa mantenir-ne la sortida.
|
|
||||||
if (auto menu_req = ServiceMenu::get().takePendingSceneRequest(); menu_req.has_value()) {
|
|
||||||
Log::info("Director", "scene transition (menu) → {}", toString(menu_req->next));
|
|
||||||
current_scene_ = buildScene(menu_req->next, menu_req->params);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// L'escena ha publicat un `ExitRequest` dins de `exit_request_`.
|
// L'escena ha publicat un `ExitRequest` dins de `exit_request_`.
|
||||||
// Director el desempaqueta i construeix la nova escena via
|
// Director el desempaqueta i construeix la nova escena via
|
||||||
// `buildScene`. Si la `std::any_cast` falla (tipus inesperat), el
|
// `buildScene`. Si la `std::any_cast` falla (tipus inesperat), el
|
||||||
@@ -498,10 +488,8 @@ auto Director::buildScene(SceneId id, const SceneParams& params) -> std::unique_
|
|||||||
// ctors antics no l'utilitzen directament. Quan totes estiguen
|
// ctors antics no l'utilitzen directament. Quan totes estiguen
|
||||||
// migrades a `ExitRequest`, aquestes lambdes desapareixeran.
|
// migrades a `ExitRequest`, aquestes lambdes desapareixeran.
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case SceneId::LOGO: {
|
case SceneId::LOGO:
|
||||||
const auto& p = std::get<LogoParams>(params);
|
return std::make_unique<LogoScene>(logo_full_surface_);
|
||||||
return std::make_unique<LogoScene>(logo_full_surface_, p.reset);
|
|
||||||
}
|
|
||||||
case SceneId::TITLE:
|
case SceneId::TITLE:
|
||||||
return std::make_unique<TitleScene>(placeholder_text_, copyright_text_, title_bg_surface_, game_logo_surface_, mini_logo_surface_);
|
return std::make_unique<TitleScene>(placeholder_text_, copyright_text_, title_bg_surface_, game_logo_surface_, mini_logo_surface_);
|
||||||
case SceneId::INTRO: {
|
case SceneId::INTRO: {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -26,15 +26,9 @@ namespace {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
LogoScene::LogoScene(std::shared_ptr<Surface> logo, bool reset)
|
LogoScene::LogoScene(std::shared_ptr<Surface> logo)
|
||||||
: logo_(std::move(logo)) {
|
: logo_(std::move(logo)) {
|
||||||
Log::info("scene", "{} start{}", SCENE_NAME, reset ? " (reset)" : "");
|
Log::info("scene", "{} start", SCENE_NAME);
|
||||||
if (reset) {
|
|
||||||
if (auto* audio = Audio::get(); audio != nullptr) {
|
|
||||||
audio->stopMusic();
|
|
||||||
audio->stopAllSounds();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Arrenquem en BLACK_INTRO: ni so ni efecte. El glitch sound i el
|
// Arrenquem en BLACK_INTRO: ni so ni efecte. El glitch sound i el
|
||||||
// begin() de scan_ es disparen quan entrem a SCAN.
|
// begin() de scan_ es disparen quan entrem a SCAN.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,9 @@ class Surface;
|
|||||||
// Skip: qualsevol tecla no reservada salta directament a la transició
|
// Skip: qualsevol tecla no reservada salta directament a la transició
|
||||||
// (sense fade-out) — el TitleScene té el seu propi `intro.fade_in_ms`,
|
// (sense fade-out) — el TitleScene té el seu propi `intro.fade_in_ms`,
|
||||||
// així que la transició queda visualment continua igualment.
|
// així que la transició queda visualment continua igualment.
|
||||||
//
|
|
||||||
// Si arriba amb `reset=true` (acció "Reiniciar" del menú de servei),
|
|
||||||
// atura música i tots els sons al constructor — el següent LOGO arrenca
|
|
||||||
// silenciós encara que veníssem del joc en marxa.
|
|
||||||
class LogoScene : public Scene {
|
class LogoScene : public Scene {
|
||||||
public:
|
public:
|
||||||
LogoScene(std::shared_ptr<Surface> logo, bool reset);
|
explicit LogoScene(std::shared_ptr<Surface> logo);
|
||||||
~LogoScene() override;
|
~LogoScene() override;
|
||||||
|
|
||||||
void update(int delta_ms) override;
|
void update(int delta_ms) override;
|
||||||
|
|||||||
@@ -30,15 +30,7 @@ enum class SceneId : std::uint8_t {
|
|||||||
// rep cap dada de l'anterior; en cas contrari, només els camps que
|
// rep cap dada de l'anterior; en cas contrari, només els camps que
|
||||||
// fluixen pel canal de transició (els recursos, defaults i config
|
// fluixen pel canal de transició (els recursos, defaults i config
|
||||||
// interna els resol Director quan construeix).
|
// interna els resol Director quan construeix).
|
||||||
struct LogoParams {
|
struct LogoParams {};
|
||||||
// Quan val `true`, indica que aquesta entrada al LOGO és una reinicialització
|
|
||||||
// completa (acció "Reiniciar" del menú de servei): l'escena haurà d'aturar
|
|
||||||
// música i sons abans de mostrar-se. En el flux normal LOGO→TITLE→… es
|
|
||||||
// queda a `false` perquè qualsevol música prèvia (p. ex. la pista de
|
|
||||||
// TITLE durant l'attract-out) continui sonant fins que la nova escena la
|
|
||||||
// substituïsca.
|
|
||||||
bool reset{false};
|
|
||||||
};
|
|
||||||
struct TitleParams {};
|
struct TitleParams {};
|
||||||
struct IntroParams {
|
struct IntroParams {
|
||||||
DebugConfig::StartingPlayers starting_players{DebugConfig::StartingPlayers::BOTH};
|
DebugConfig::StartingPlayers starting_players{DebugConfig::StartingPlayers::BOTH};
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -6,13 +6,11 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "core/input/define_buttons.hpp"
|
#include "core/input/define_buttons.hpp"
|
||||||
#include "core/rendering/surface.hpp" // Palette
|
#include "core/rendering/surface.hpp" // Palette
|
||||||
#include "game/scenes/scene_flow.hpp"
|
|
||||||
|
|
||||||
class Text;
|
class Text;
|
||||||
|
|
||||||
@@ -75,19 +73,6 @@ class ServiceMenu {
|
|||||||
// `define_buttons_->enable(...)` de coffee_crisis_arcade_edition.
|
// `define_buttons_->enable(...)` de coffee_crisis_arcade_edition.
|
||||||
void startDefineButtons(SDL_Gamepad* pad, const std::string& name, const std::string& path);
|
void startDefineButtons(SDL_Gamepad* pad, const std::string& name, const std::string& path);
|
||||||
|
|
||||||
// Bústia de transició d'escena des del menú: alguns items (p.ex.
|
|
||||||
// "Reiniciar" a Sistema) no es poden expressar com a `ExitRequest` de
|
|
||||||
// l'escena actual perquè el menú és global i no té handle a l'escena.
|
|
||||||
// Director consulta `takePendingSceneRequest()` cada frame i, si hi ha
|
|
||||||
// valor, força la transició — el mateix camí que per a `ExitRequest`
|
|
||||||
// d'una escena, però el dispara el menú directament.
|
|
||||||
void requestSceneTransition(ExitRequest req) { pending_request_ = req; }
|
|
||||||
[[nodiscard]] auto takePendingSceneRequest() -> std::optional<ExitRequest> {
|
|
||||||
auto out = pending_request_;
|
|
||||||
pending_request_.reset();
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Struct d'un item d'una pàgina ---
|
// --- Struct d'un item d'una pàgina ---
|
||||||
//
|
//
|
||||||
@@ -306,9 +291,4 @@ class ServiceMenu {
|
|||||||
// persisteix la nova configuració via `GamepadBindings`. Replica el
|
// persisteix la nova configuració via `GamepadBindings`. Replica el
|
||||||
// patró `define_buttons_` de coffee_crisis_arcade_edition.
|
// patró `define_buttons_` de coffee_crisis_arcade_edition.
|
||||||
DefineButtons define_buttons_;
|
DefineButtons define_buttons_;
|
||||||
|
|
||||||
// Sol·licitud de canvi d'escena pendent (vore `requestSceneTransition`).
|
|
||||||
// La consumeix Director al final del frame; si l'item no força la
|
|
||||||
// transició, queda buida.
|
|
||||||
std::optional<ExitRequest> pending_request_;
|
|
||||||
};
|
};
|
||||||
+8
-2
@@ -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!");
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user