Zappy GUI
C++ graphic client: renderer, camera, network
Loading...
Searching...
No Matches
App.cpp
Go to the documentation of this file.
1#include "App.hpp"
2
3#include <chrono>
4#include <csignal>
5#include <iostream>
6#include <thread>
7
8extern volatile sig_atomic_t g_interrupted;
9
10#include "../audio/IAudioManager.hpp"
11#include "../audio/NullAudioManager.hpp"
12#include "../audio/RaylibAudioManager.hpp"
14#include "network/TcpSocket.hpp"
19
20static constexpr int MAX_RETRIES = 5;
21
22App::App(int argc, char** argv) : args(argc, argv) {}
23
24bool App::shouldRun() const { return args.isValid(); }
25
26int App::exitCode() const { return args.exitCode(); }
27
29{
30 AppConfig config = args.getConfig();
32
33 auto socket = std::make_unique<TcpSocket>();
34 if (!_connectWithRetry(*socket, config.machine, config.port)) return;
35
36 EventQueue eventQueue;
37 IRenderer* renderer;
38
39 if (config.headless) {
40 std::cout << "[INFO] Running in headless mode\n";
41 renderer = new HeadlessRenderer(std::cout);
42 _audioManager = std::make_unique<NullAudioManager>();
43 } else {
44 renderer = new RaylibRenderer();
45 _audioManager = std::make_unique<RaylibAudioManager>();
46 }
47
48 renderer->setDevMode(config.dev, config.port, config.machine);
49 renderer->init();
50 _audioManager->init();
51
52 _rendererActive = true;
53 while (!renderer->shouldClose() && !g_interrupted) {
54 try {
55 while (!renderer->shouldClose() && !g_interrupted) {
56 socket->send("mct\n");
57 _trySendStu(*socket);
58 pollAndEnqueue(*socket, eventQueue);
59
60 while (auto event = eventQueue.pop()) {
61 state.applyEvent(*event);
62 _audioManager->handleEvent(*event);
63 }
64
65 renderer->setState(state);
66 renderer->handleInput();
67 if (auto newSpeed = renderer->getPendingSpeedChange())
68 socket->send("sst " + std::to_string(*newSpeed) + "\n");
69 renderer->render();
70 }
71 } catch (const TcpException& e) {
72 std::cerr << "[Network] " << e.what() << "\n";
73 renderer->shutdown();
74 _audioManager->shutdown();
75 _rendererActive = false;
76 socket = std::make_unique<TcpSocket>();
77 if (!_connectWithRetry(*socket, config.machine, config.port)) break;
78 state = GameState{};
79 eventQueue.clear();
81 renderer->init();
82 _audioManager->init();
83 _rendererActive = true;
84 }
85 }
86
87 if (g_interrupted) std::cerr << "[GUI] Stopping\n";
88 if (_rendererActive) {
89 renderer->shutdown();
90 _audioManager->shutdown();
91 }
92 delete renderer;
93}
94
96{
97 while (socket.poll(0)) {
98 std::optional<std::string> line = socket.recvLine();
99 if (!line) break;
100 std::optional<Event> event = ProtocolParser::parse(*line);
101 if (event) queue.push(*event);
102 }
103}
104
106{
107 if (_stuSilenced) return;
108
109 auto now = std::chrono::steady_clock::now();
110 if (now - _lastStuSent < std::chrono::seconds(1)) return;
111
112 if (_lastStuSent.time_since_epoch().count() != 0) {
115 else if (++_stuMissedResponses >= 3) {
116 _stuSilenced = true;
117 std::cerr << "[Network] stu: no response after 3 attempts, disabling uptime polling\n";
118 return;
119 }
121 }
122
123 socket.send("stu\n");
124 _lastStuSent = now;
125}
126
128{
129 _lastStuSent = {};
131 _stuSilenced = false;
132}
133
134bool App::_connectWithRetry(TcpSocket& socket, const std::string& host, int port)
135{
136 int delay = 2;
137 for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
138 try {
139 socket.connect(host, port);
140 socket.send("GRAPHIC\n");
141 std::cerr << "[Network] Connected to " << host << ":" << port << "\n";
142 return true;
143 } catch (const TcpException& e) {
144 std::cerr << "[Network] " << e.what() << " (attempt " << attempt << "/" << MAX_RETRIES
145 << ")\n";
146 if (attempt == MAX_RETRIES) break;
147 for (int s = delay; s > 0 && !g_interrupted; s--) {
148 std::cerr << "[Network] Retrying in " << s << "s... \r";
149 std::cerr.flush();
150 std::this_thread::sleep_for(std::chrono::seconds(1));
151 }
152 if (g_interrupted) return false;
153 delay *= 2;
154 }
155 }
156 std::cerr << "\n[Network] Could not connect after " << MAX_RETRIES << " attempts.\n";
157 return false;
158}
volatile sig_atomic_t g_interrupted
Definition main.cpp:6
static constexpr int MAX_RETRIES
Definition App.cpp:20
bool _connectWithRetry(TcpSocket &socket, const std::string &host, int port)
Attempts to connect with exponential backoff. Returns true on success.
Definition App.cpp:134
std::unique_ptr< IAudioManager > _audioManager
Definition App.hpp:64
std::chrono::steady_clock::time_point _lastStuSent
Definition App.hpp:60
void _trySendStu(TcpSocket &socket)
Sends stu once per real second and silences it after 3 consecutive non-responses.
Definition App.cpp:105
bool shouldRun() const
Checks if the application should run based on the parsed arguments.
Definition App.cpp:24
bool _stuSilenced
Definition App.hpp:62
GameState state
Definition App.hpp:41
int exitCode() const
Gets the exit code to return if the application should not run.
Definition App.cpp:26
void _resetStuState()
Resets stu polling state, called on reconnect.
Definition App.cpp:127
void pollAndEnqueue(TcpSocket &socket, EventQueue &queue)
Polls the socket for new data and enqueues any received events.
Definition App.cpp:95
Args args
Definition App.hpp:40
bool _rendererActive
Definition App.hpp:42
App(int argc, char **argv)
Definition App.cpp:22
void run()
Runs the application.
Definition App.cpp:28
int _stuMissedResponses
Definition App.hpp:61
int exitCode() const
Definition Args.cpp:11
ServerConfig getConfig() const
Definition Args.cpp:19
bool isValid() const
Definition Args.cpp:7
Thread-safe queue for storing events received from the server. Allows for potential multi-threaded be...
void push(Event event)
Pushes an event to the queue. Thread safe.
Definition EventQueue.cpp:3
void clear()
Clears all pending events from the queue. Thread safe.
std::optional< Event > pop()
Pops an event from the queue. Thread safe.
Definition EventQueue.cpp:9
Represents the current state of the game. Knows the world state, some metadata, and how to apply even...
Definition GameState.hpp:15
void applyEvent(const Event &e)
Applies an event to the game state, modifying it accordingly and setting the dirty flag.
Definition GameState.cpp:17
bool receivedStuResponse
Definition GameState.hpp:24
A headless renderer that outputs the game state to a stream (e.g., console) instead of rendering it g...
static void setLanguage(Language lang)
Definition I18n.hpp:83
Pure interface for rendering the game state.
Definition IRenderer.hpp:11
virtual void render()=0
virtual std::optional< int > getPendingSpeedChange()=0
virtual bool shouldClose()=0
virtual void setState(const GameState &state)=0
virtual void init()=0
virtual void shutdown()=0
virtual void setDevMode(bool dev, int port, const std::string &machine)=0
virtual void handleInput()=0
static std::optional< Event > parse(std::string_view input)
Parses a raw input string from the server and converts it into an Event variant. This method stops at...
A renderer that uses Raylib to display the game state graphically.
Exception class for TCP errors.
Definition TcpSocket.hpp:10
const char * what() const noexcept override
Definition TcpSocket.hpp:13
Simple TCP socket wrapper.
Definition TcpSocket.hpp:22
std::optional< std::string > recvLine()
Receives a line of text from the socket.
Definition TcpSocket.cpp:95
void send(const std::string &data)
Sends data through the socket.
Definition TcpSocket.cpp:83
bool poll(int timeout_ms)
Polls the socket for incoming data.
void connect(const std::string &host, int port)
Connects to a remote host.
Definition TcpSocket.cpp:16
Represents the configuration for the application, including the port, machine, and headless mode.
Definition Args.hpp:11
bool headless
Definition Args.hpp:14
bool dev
Definition Args.hpp:15
I18n::Language language
Definition Args.hpp:16
int port
Definition Args.hpp:12
std::string machine
Definition Args.hpp:13