[Refactor] One GameState to rule them all!
Introduces a central GameState class to manage the client's lifecycle, network connection and UI components. The Terminal has been demoted to a pure UI widget and main has been simplified to a basic entrypoint as they should be!
This commit is contained in:
parent
59783d2408
commit
00e78cc2ba
81
client/src/game_state.cpp
Normal file
81
client/src/game_state.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "game_state.h"
|
||||
#include "gfx/shape_renderer.h"
|
||||
#include "gfx/txt_renderer.h"
|
||||
#include "terminal.h"
|
||||
#include "ui/desktop.h"
|
||||
#include "ui/ui_window.h"
|
||||
|
||||
GameState::GameState(void) = default;
|
||||
|
||||
GameState::~GameState(void) = default;
|
||||
|
||||
void GameState::init(void) {
|
||||
/* Create and connect the network client. */
|
||||
_network = std::make_unique<ClientNetwork>();
|
||||
if(_network->connect("127.0.0.1", 1337)) {
|
||||
/* TODO: handle connection success message. */
|
||||
} else {
|
||||
/* TODO: handle connection failure. */
|
||||
}
|
||||
|
||||
/* Create the desktop. */
|
||||
_desktop = std::make_unique<Desktop>();
|
||||
|
||||
/*
|
||||
* Create initial terminal window.
|
||||
*/
|
||||
auto term = std::make_unique<Terminal>(_network.get()); /* Pass network connection. */
|
||||
auto term_window = std::make_unique<UIWindow>("Terminal", 100, 100, 800, 500);
|
||||
term_window->set_content(std::move(term));
|
||||
_desktop->add_window(std::move(term_window));
|
||||
}
|
||||
|
||||
void GameState::handle_event(SDL_Event* event) {
|
||||
if(_desktop) {
|
||||
_desktop->handle_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::update(void) {
|
||||
std::string server_msg;
|
||||
while(_network->poll_message(server_msg)) {
|
||||
UIWindow* focused_window = _desktop->get_focused_window();
|
||||
if(!focused_window) continue;
|
||||
|
||||
Terminal* terminal = focused_window->get_content();
|
||||
if(!terminal) continue;
|
||||
|
||||
/* Server sends "output\nprompt", split them. */
|
||||
size_t last_newline = server_msg.find_last_of("\n");
|
||||
if(last_newline != std::string::npos) {
|
||||
std::string prompt = server_msg.substr(last_newline+1);
|
||||
terminal->set_prompt(prompt);
|
||||
|
||||
std::string output = server_msg.substr(0, last_newline);
|
||||
if(!output.empty()) {
|
||||
/* split the multiline output and push each line to history. */
|
||||
std::stringstream ss(output);
|
||||
std::string line;
|
||||
while(std::getline(ss, line, '\n')) {
|
||||
terminal->add_history(line);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
terminal->add_history(server_msg);
|
||||
}
|
||||
}
|
||||
if(_desktop) {
|
||||
_desktop->update();
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
|
||||
int screen_height, bool show_cursor) {
|
||||
if(_desktop) {
|
||||
_desktop->render(shape_renderer, txt_renderer, screen_height, show_cursor);
|
||||
}
|
||||
}
|
||||
25
client/src/game_state.h
Normal file
25
client/src/game_state.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
class ClientNetwork;
|
||||
class Desktop;
|
||||
class ShapeRenderer;
|
||||
class TextRenderer;
|
||||
union SDL_Event;
|
||||
|
||||
class GameState {
|
||||
public:
|
||||
GameState(void);
|
||||
~GameState(void);
|
||||
|
||||
void init(void);
|
||||
void handle_event(SDL_Event* event);
|
||||
void update(void);
|
||||
void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
|
||||
int screen_height, bool show_cursor);
|
||||
|
||||
private:
|
||||
std::unique_ptr<ClientNetwork> _network;
|
||||
std::unique_ptr<Desktop> _desktop;
|
||||
};
|
||||
@ -10,10 +10,9 @@
|
||||
#include <chrono>
|
||||
#include "../../server/src/network_manager.h"
|
||||
|
||||
#include "terminal.h"
|
||||
#include "gfx/shape_renderer.h"
|
||||
#include "ui/desktop.h"
|
||||
#include "ui/ui_window.h"
|
||||
#include "gfx/txt_renderer.h"
|
||||
#include "game_state.h"
|
||||
|
||||
void run_server(void) {
|
||||
try {
|
||||
@ -104,16 +103,8 @@ int main(int argc, char** argv) {
|
||||
/* Init shape renderer. */
|
||||
ShapeRenderer* shape_renderer_instance = new ShapeRenderer(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
/* Create terminal. */
|
||||
auto term = std::make_unique<Terminal>();
|
||||
|
||||
/* Create UI window and pass it a terminal. */
|
||||
auto test_window = std::make_unique<UIWindow>("Terminal", 100, 100, 800, 500);
|
||||
test_window->set_content(std::move(term));
|
||||
|
||||
/* Create desktop and add the window. */
|
||||
auto desktop = std::make_unique<Desktop>();
|
||||
desktop->add_window(std::move(test_window));
|
||||
auto game_state = std::make_unique<GameState>();
|
||||
game_state->init();
|
||||
|
||||
/* timer for cursor blink. */
|
||||
Uint32 last_blink_time = 0;
|
||||
@ -125,10 +116,10 @@ int main(int argc, char** argv) {
|
||||
SDL_Event event;
|
||||
while(SDL_PollEvent(&event)) {
|
||||
if(event.type == SDL_EVENT_QUIT) { running = false; }
|
||||
desktop.get()->handle_event(&event);
|
||||
game_state->handle_event(&event);
|
||||
}
|
||||
|
||||
desktop.get()->update();
|
||||
game_state->update();
|
||||
|
||||
Uint32 current_time = SDL_GetTicks();
|
||||
if(current_time - last_blink_time > 500) { /* Every 500ms. */
|
||||
@ -140,14 +131,14 @@ int main(int argc, char** argv) {
|
||||
glClearColor(0.1f, 0.1f, 0.1, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
desktop.get()->render(shape_renderer_instance, txt_render_instance, SCREEN_HEIGHT, show_cursor);
|
||||
game_state->render(shape_renderer_instance, txt_render_instance, SCREEN_HEIGHT, show_cursor);
|
||||
|
||||
/* It's really odd to call it SwapWindow now, rather than SwapBuffer. */
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
/* Cleanup. */
|
||||
desktop.reset();
|
||||
game_state.reset();
|
||||
delete shape_renderer_instance;
|
||||
delete txt_render_instance;
|
||||
SDL_GL_DestroyContext(context);
|
||||
|
||||
@ -1,73 +1,44 @@
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <GL/glew.h>
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "terminal.h"
|
||||
#include "client_network.h"
|
||||
#include "gfx/txt_renderer.h"
|
||||
|
||||
Terminal::Terminal(void) {
|
||||
Terminal::Terminal(ClientNetwork* network) : _network(network) {
|
||||
/* Placeholder welcome message to history. */
|
||||
_history.push_back("Welcome to Bettola");
|
||||
_history.push_back("Connecting to server...");
|
||||
|
||||
_network = std::make_unique<ClientNetwork>();
|
||||
_should_close = false;
|
||||
_scroll_offset = 0;
|
||||
_prompt = "";
|
||||
|
||||
/* TODO: Move network to main.cpp, or better yet, a dedicatated game state file */
|
||||
if(_network->connect("127.0.0.1", 1337)) {
|
||||
_history.push_back("Connection successful.");
|
||||
} else {
|
||||
_history.push_back("Connection failed. Please restart.");
|
||||
_prompt = "error>";
|
||||
}
|
||||
}
|
||||
|
||||
Terminal::~Terminal(void) {}
|
||||
|
||||
void Terminal::update(void) {
|
||||
std::string server_msg;
|
||||
while(_network->poll_message(server_msg)) {
|
||||
if(server_msg == "__CLOSE_CONNECTION__") {
|
||||
_history.push_back("Connection closed by server.");
|
||||
_should_close = true;
|
||||
return;
|
||||
}
|
||||
/* Server will send "output\nprompt". Split them. */
|
||||
size_t last_newline = server_msg.find_last_of('\n');
|
||||
if(last_newline != std::string::npos) {
|
||||
_prompt = server_msg.substr(last_newline+1);
|
||||
std::string output = server_msg.substr(0, last_newline);
|
||||
if(!output.empty()) {
|
||||
/* Split multiline output and push each line to history. */
|
||||
std::stringstream ss(output);
|
||||
std::string line;
|
||||
while(std::getline(ss, line, '\n')) {
|
||||
/* Replace tabs. */
|
||||
std::string line_with_spaces;
|
||||
for(char ch : line) {
|
||||
if(ch == '\t') {
|
||||
line_with_spaces += " ";
|
||||
} else {
|
||||
line_with_spaces += ch;
|
||||
}
|
||||
}
|
||||
_history.push_back(line_with_spaces);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Terminal::add_history(const std::string& line) {
|
||||
std::string line_with_spaces;
|
||||
for(char ch : line) {
|
||||
if(ch == '\t') {
|
||||
line_with_spaces += " ";
|
||||
} else {
|
||||
/*
|
||||
* If there's no newline, it might be the initial welcome message,
|
||||
* or some other non-standard message, just it to history.
|
||||
*/
|
||||
_history.push_back(server_msg);
|
||||
line_with_spaces += ch;
|
||||
}
|
||||
}
|
||||
_history.push_back(line_with_spaces);
|
||||
|
||||
if(line == "__CLOSE_CONNECTION__") {
|
||||
_history.back() = "Connection closed by server.";
|
||||
_should_close = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Terminal::set_prompt(const std::string& prompt) {
|
||||
_prompt = prompt;
|
||||
}
|
||||
|
||||
bool Terminal::close(void) { return _should_close; }
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <SDL3/SDL.h>
|
||||
@ -9,13 +8,15 @@
|
||||
|
||||
class Terminal {
|
||||
public:
|
||||
Terminal(void);
|
||||
Terminal(ClientNetwork* network);
|
||||
~Terminal(void);
|
||||
|
||||
void update(void);
|
||||
void handle_input(SDL_Event* event);
|
||||
void render(TextRenderer* renderer, int x, int y, int width, int height, bool show_cursor);
|
||||
void scroll(int amount, int content_height);
|
||||
void add_history(const std::string& line);
|
||||
void set_prompt(const std::string& prompt);
|
||||
bool close(void);
|
||||
|
||||
private:
|
||||
@ -26,5 +27,5 @@ private:
|
||||
std::vector<std::string> _history;
|
||||
int _scroll_offset;
|
||||
std::string _prompt;
|
||||
std::unique_ptr<ClientNetwork> _network;
|
||||
ClientNetwork* _network;
|
||||
};
|
||||
|
||||
@ -55,14 +55,7 @@ void Desktop::handle_event(SDL_Event* event) {
|
||||
}
|
||||
|
||||
void Desktop::update(void) {
|
||||
/* Poll all windows for network updates. */
|
||||
for(auto& window : _windows) {
|
||||
Terminal* term = window->get_content();
|
||||
if(term) {
|
||||
term->update();
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove closed windows. */
|
||||
_windows.erase(std::remove_if(_windows.begin(), _windows.end(),
|
||||
[](const std::unique_ptr<UIWindow>& window) {
|
||||
Terminal* term = window->get_content();
|
||||
@ -71,6 +64,10 @@ void Desktop::update(void) {
|
||||
_windows.end());
|
||||
}
|
||||
|
||||
UIWindow* Desktop::get_focused_window(void) {
|
||||
return _focused_window;
|
||||
}
|
||||
|
||||
void Desktop::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
|
||||
int screen_height, bool show_cursor) {
|
||||
for(const auto& win : _windows) {
|
||||
|
||||
@ -19,6 +19,8 @@ public:
|
||||
void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height,
|
||||
bool show_cursor);
|
||||
|
||||
UIWindow* get_focused_window(void);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<UIWindow>> _windows;
|
||||
UIWindow* _focused_window;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user