diff --git a/assets/menu_background_snippets.txt b/assets/menu_background_snippets.txt new file mode 100644 index 0000000..846bb0d --- /dev/null +++ b/assets/menu_background_snippets.txt @@ -0,0 +1,34 @@ +map -sV 10.0.2.15 +ls -la /etc/ +ps aux | grep 'sshd' +gcc exploit.c -o exploit +SELECT * FROM users; +import requests +while(true){}; +}); +sudo rm -rf / +netstat -anp +arp -a +ssh root@192.168.1.1 +git commit -m 'Initial commit' +hydra -l user -P wordlist.txt ftp://... +Hello, world. +All your base are belong to us +// TODO: fix this later +42 +1337 +zerocool +acidburn +cereal_killer +ping -c 4 google.com +traceroute 8.8.8.8 + + +password123 +GOD +Have you tried turning it off and on again? +Connection established. +SYN/ACK +Firewall breach detected. +Decrypting /etc/shadow... +root::0:0:root:/root:/bin/bash diff --git a/client/src/game_state.cpp b/client/src/game_state.cpp index 15bf2f4..8ad628b 100644 --- a/client/src/game_state.cpp +++ b/client/src/game_state.cpp @@ -1,13 +1,17 @@ #include #include #include +#include +#include +#include "../../server/src/network_manager.h" #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" +#include void GameState::_init_desktop(void) { _desktop = std::make_unique(); @@ -18,15 +22,41 @@ void GameState::_init_desktop(void) { _desktop->add_window(std::move(term_window)); } +void GameState::_run_server(void) { + try { + NetworkManager server; + server.start(1337); + /* + * Server's start() method is non-blocking, but NetworkManager + * object must be kept alive. We'll just loop forever and let the OS + * clean up the thread when main exits ;) + */ + while(true) { + std::this_thread::sleep_for(std::chrono::seconds(5)); + } + } catch(const std::exception& e) { + fprintf(stderr, "Single-player server thread exception: %s\n", e.what()); + } +} + GameState::GameState(void) : _current_screen(Screen::MAIN_MENU) {} GameState::~GameState(void) = default; -void GameState::init(void) { +void GameState::init(int screen_width, int screen_height) { /* Create and connect the network client. */ _network = std::make_unique(); - /* TODO: For now, connect immediately, later, trigger by menu option. */ + _main_menu = std::make_unique(screen_width, screen_height); +} + +void GameState::start_single_player_now(int screen_width, int screen_height) { + 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)); + + _network = std::make_unique(); if(!_network->connect("127.0.0.1", 1337)) { /* TODO: Handle connection failure. */ } @@ -37,7 +67,9 @@ void GameState::init(void) { void GameState::handle_event(SDL_Event* event) { switch(_current_screen) { case Screen::MAIN_MENU: - /* TODO: */ + if(_main_menu) { + _main_menu->handle_event(event); + } break; case Screen::BOOTING: /* TODO: */ @@ -52,13 +84,33 @@ void GameState::handle_event(SDL_Event* event) { void GameState::update(void) { switch(_current_screen) { - case Screen::MAIN_MENU: - /* TODO: */ + case Screen::MAIN_MENU: { + if(!_main_menu) break; + Screen next_screen = _main_menu->update(); + if(next_screen != Screen::MAIN_MENU) { + _current_screen = next_screen; + _main_menu.reset(); /* Free mem. */ + + if(_current_screen == Screen::BOOTING) { + 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. */ + } + /* TODO: transition to book screen. */ + _current_screen = Screen::DESKTOP; + _init_desktop(); + } + } break; + } case Screen::BOOTING: /* TODO: */ break; - case Screen::DESKTOP: + case Screen::DESKTOP: { std::string server_msg; while(_network->poll_message(server_msg)) { UIWindow* focused_window = _desktop->get_focused_window(); @@ -92,6 +144,7 @@ void GameState::update(void) { _desktop->update(); } break; + } } } @@ -99,7 +152,9 @@ void GameState::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer int screen_height, bool show_cursor) { switch(_current_screen) { case Screen::MAIN_MENU: - /* TODO: */ + if(_main_menu) { + _main_menu->render(shape_renderer, txt_renderer); + } break; case Screen::BOOTING: /* TODO: */ diff --git a/client/src/game_state.h b/client/src/game_state.h index 40d6aa4..e7effb9 100644 --- a/client/src/game_state.h +++ b/client/src/game_state.h @@ -4,6 +4,7 @@ class ClientNetwork; class Desktop; +class MainMenu; class ShapeRenderer; class TextRenderer; union SDL_Event; @@ -19,7 +20,8 @@ public: GameState(void); ~GameState(void); - void init(void); + void init(int screen_width, int screen_height); + void start_single_player_now(int screen_width, int screen_height); void handle_event(SDL_Event* event); void update(void); void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, @@ -28,7 +30,9 @@ public: private: std::unique_ptr _network; std::unique_ptr _desktop; + std::unique_ptr _main_menu; Screen _current_screen; void _init_desktop(void); + void _run_server(void); }; diff --git a/client/src/main.cpp b/client/src/main.cpp index 77f000c..ad9bb08 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -1,47 +1,17 @@ #include #include +#include #include #include #include -/* For single player mode. */ -#include -#include -#include -#include "../../server/src/network_manager.h" - #include "gfx/shape_renderer.h" #include "gfx/txt_renderer.h" #include "game_state.h" -void run_server(void) { - try { - NetworkManager server; - server.start(1337); - /* - * Server's start() method is non-blocking, but NetworkManager - * object must be kept alive. We'll just loop forever and let the OS - * clean up the thread when main exits ;) - */ - while(true) { - std::this_thread::sleep_for(std::chrono::seconds(5)); - } - } catch(const std::exception& e) { - fprintf(stderr, "Single-player server thread exception: %s\n", e.what()); - } -} - const int SCREEN_WIDTH = 1280; const int SCREEN_HEIGHT = 720; int main(int argc, char** argv) { - if(argc > 1 && std::string(argv[1]) == "-sp") { - fprintf(stdout, "Starting in single-player mode...\n"); - std::thread server_thread(run_server); - server_thread.detach(); - fprintf(stdout, "Waiting for server initialise...\n"); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - } - /* Init SDL. */ if(!SDL_Init(SDL_INIT_VIDEO)) { printf("SDL could not initialise! SDL_ERROR: %s\n", SDL_GetError()); @@ -104,7 +74,11 @@ int main(int argc, char** argv) { ShapeRenderer* shape_renderer_instance = new ShapeRenderer(SCREEN_WIDTH, SCREEN_HEIGHT); auto game_state = std::make_unique(); - game_state->init(); + if(argc > 1 && std::string(argv[1]) == "-sp") { + game_state->start_single_player_now(SCREEN_WIDTH, SCREEN_HEIGHT); + } else { + game_state->init(SCREEN_WIDTH, SCREEN_HEIGHT); + } /* timer for cursor blink. */ Uint32 last_blink_time = 0; diff --git a/client/src/ui/main_menu.cpp b/client/src/ui/main_menu.cpp new file mode 100644 index 0000000..2c09b5f --- /dev/null +++ b/client/src/ui/main_menu.cpp @@ -0,0 +1,142 @@ +#include +#include +#include + +#include "gfx/shape_renderer.h" +#include "gfx/txt_renderer.h" + +#include "main_menu.h" + +/* Get a random snippet form loaded word list. */ +const std::string& get_random_snippet(const std::vector& snippets) { + /* If empty, return a default to avoid a crash? */ + if(snippets.empty()) { + static const std::string empty_str = "ERROR: snippets file not found"; + return empty_str; + } + int index = rand() % snippets.size(); + return snippets[index]; +} + +MainMenu::MainMenu(int screen_width, int screen_height) : + _screen_width(screen_width), _screen_height(screen_height), + _next_screen(Screen::MAIN_MENU) { + + /* Load snippets form file. */ + std::ifstream snippet_file("assets/menu_background_snippets.txt"); + if(!snippet_file.is_open()) { + printf("ERROR: Failed to open assets/menu_background_snippets.txt\n"); + } else { + std::string line; + while(std::getline(snippet_file, line)) { + if(!line.empty()) { + _snippets.push_back(line); + } + } + } + + /* Initialise buttons. */ + const int button_width = 200; + const int button_height = 50; + const int center_x = (screen_width/2) - (button_width/2); + const int center_y = (screen_height/2); + + _buttons.push_back({ + "Single-Player", + { center_x, center_y + 30, button_width, button_height }, + Screen::BOOTING, /* This will trigger the booting screen. */ + false + }); + _buttons.push_back({ + "Online", + { center_x, center_y - 30, button_width, button_height }, + Screen::BOOTING, + false + }); + + /* Initialise animated background. */ + srand(time(NULL)); + for(int i = 0; i < 100; ++i) { + _background_text.push_back({ + get_random_snippet(_snippets), + (float)(rand() % screen_width), + (float)(rand() % screen_height), + (float)(rand() % 50 + 20) / 100.0f /* Random speed. */ + }); + } +} + +MainMenu::~MainMenu(void) {} + +void MainMenu::handle_event(SDL_Event* event) { + if(event->type == SDL_EVENT_MOUSE_MOTION) { + int mouse_x = event->motion.x; + int mouse_y = event->motion.y; + for(auto& button : _buttons) { + /* Invert mouse_y for UI coordinate system. */ + int inverted_y = _screen_height - mouse_y; + button.is_hovered = (mouse_x >= button.rect.x + && mouse_x <= button.rect.x + button.rect.w + && inverted_y >= button.rect.y + && inverted_y <= button.rect.y + button.rect.h); + } + } else if(event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) { + for(const auto& button : _buttons) { + if(button.is_hovered) { + _next_screen = button.action; + break; /* Once clicked button found, exit. */ + } + } + } +} + +Screen MainMenu::update(void) { + _update_background(); + return _next_screen; +} + +void MainMenu::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer) { + _render_background(txt_renderer); + + /* Button colours. */ + const Color button_color = { 0.1f, 0.15f, 0.2f }; + const Color button_hover_color = { 0.2f, 0.25f, 0.3f }; + const Color text_color = { 0.8f, 0.8f, 0.8f }; + + for(const auto& button : _buttons) { + /* Draw button background. */ + if(button.is_hovered) { + shape_renderer->draw_rect(button.rect.x, button.rect.y, button.rect.w, + button.rect.h, button_hover_color); + } else { + shape_renderer->draw_rect(button.rect.x, button.rect.y, button.rect.w, + button.rect.h, button_color); + } + + /* + * Draw button text centered. + * TODO: Calculate text width for perfect centering. + */ + txt_renderer->render_text(button.label.c_str(), button.rect.x + 50, + button.rect.y + 18, 1.0f, text_color); + } +} + +void MainMenu::_update_background(void) { + for(auto& line : _background_text) { + line.y -= line.speed; + if(line.y < 0) { + line.y = _screen_height; + line.x = (float)(rand() % _screen_width); + line.text = get_random_snippet(_snippets); + } + } +} + +void MainMenu::_render_background(TextRenderer* txt_renderer) { + const Color background_text_color = { 0.0f, 0.35f, 0.15f }; /* Dark green. */ + for(const auto& line : _background_text) { + txt_renderer->render_text(line.text.c_str(), line.x, line.y, 1.0f, + background_text_color); + } +} diff --git a/client/src/ui/main_menu.h b/client/src/ui/main_menu.h new file mode 100644 index 0000000..014136c --- /dev/null +++ b/client/src/ui/main_menu.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include "gfx/txt_renderer.h" +#include "gfx/shape_renderer.h" +#include "gfx/types.h" +#include "game_state.h" + +union SDL_Event; + +struct MenuButton { + std::string label; + Rect rect; + Screen action; /* Change state. */ + bool is_hovered = false; +}; + +class MainMenu { +public: + MainMenu(int screen_width, int screen_height); + ~MainMenu(void); + + void handle_event(SDL_Event* event); + Screen update(void); + void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer); + +private: + void _update_background(void); + void _render_background(TextRenderer* txdt_renderer); + + /* For animated background. */ + struct ScrollingText { + std::string text; + float x; + float y; + float speed; + }; + + std::vector _background_text; + int _screen_height; + int _screen_width; + + /* Buttons. */ + std::vector _buttons; + std::vector _snippets; + Screen _next_screen; +};