From 9615c39e67473b3d318e59bc830e5db947f54e1f Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Sun, 14 Sep 2025 16:09:44 +0100 Subject: [PATCH] [Refac] Separate rendering, game and application. --- CMakeLists.txt | 1 + src/bettola.cpp | 329 ++------------------------------------ src/bettola.h | 43 +---- src/game/player.cpp | 8 + src/game/player.h | 10 ++ src/game_client.cpp | 172 ++++++++++++++++++++ src/game_client.h | 40 +++++ src/graphics/renderer.cpp | 116 ++++++++++++++ src/graphics/renderer.h | 26 +++ 9 files changed, 393 insertions(+), 352 deletions(-) create mode 100644 src/game_client.cpp create mode 100644 src/game_client.h create mode 100644 src/graphics/renderer.cpp create mode 100644 src/graphics/renderer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d626b9c..9cb63a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # include directories.. include_directories(libbettola/include) +include_directories(src) # Deps. find_package(SDL3 REQUIRED) diff --git a/src/bettola.cpp b/src/bettola.cpp index afad8a1..cc3aabd 100644 --- a/src/bettola.cpp +++ b/src/bettola.cpp @@ -1,6 +1,5 @@ #include #include -#include /* FINE LSP!! I'll play your games!!!! */ #include /* ~HJAPPY?!?!?! */ #include @@ -10,17 +9,9 @@ #include #include #include -#include #include -#include #include "bettola.h" -#include "bettola/network/net_common.h" -#include "game/remote_player.h" -#include "math/mat4.h" -#include "network/message.h" -#include "network/game_state_message.h" -#include "network/player_input_message.h" /* Dacav's resolution ;) */ const int SCREEN_WIDTH = 800; @@ -29,59 +20,39 @@ const int SCREEN_HEIGHT = 600; Bettola::Bettola(void) : _is_running(false), _window(nullptr), - _gl_context(nullptr), - _vao(0), - _vbo(0), - _our_player_id(0), - _input_sequence_number(0) { - - memset(&_server_addr, 0, sizeof(_server_addr)); -} + _gl_context(nullptr) {} Bettola::~Bettola(void) { if(_gl_context) { SDL_GL_DestroyContext(_gl_context); } - /* Explicitly close client socket. */ - _tcp_socket.close(); - _udp_socket.close(); - if(_window) { SDL_DestroyWindow(_window); } SDL_Quit(); +} - if(_gl_context) { - /* Should only be deleted if it was successfully created - * and if _gl_context is valid. - */ - glDeleteVertexArrays(1, &_vao); - glDeleteBuffers(1, &_vbo); +bool Bettola::init_sdl(void) { + if(!SDL_Init(SDL_INIT_VIDEO)) { + fprintf(stderr, "Failed to init SDL! SDL ERROR: %s\n", SDL_GetError()); + return false; } + return true; } int Bettola::run(void) { if(!init_sdl()) return -1; if(!create_window()) return -1; if(!create_gl_context()) return -1; - if(!init_client_connection()) return -1; - if(!init_glew()) return -1; - if(!_shader.load_from_files("assets/shaders/simple.vert", - "assets/shaders/simple.frag")) { + if(!_game_client.connect("127.0.0.1", 12345)) { + return -1; + } + if(!_renderer.init(SCREEN_WIDTH, SCREEN_HEIGHT)) { return -1; } - - _projection = BettolaMath::Mat4::orthographic(0.0f, 800.0f, 600.0f, 0.0f, -1.0f, 1.0f); - _shader.use(); - unsigned int proj_loc = glGetUniformLocation(_shader.get_id(), "projection"); - glUniformMatrix4fv(proj_loc, 1, GL_FALSE, _projection.get_ptr()); - - setup_quad(); - - glViewport(0,0,SCREEN_WIDTH, SCREEN_HEIGHT); _is_running = true; @@ -95,7 +66,6 @@ int Bettola::run(void) { last_count = current_counter; process_events(); - process_network(); update(delta_time); render(); } @@ -126,108 +96,10 @@ void Bettola::process_events(void) { } } -void Bettola::process_network(void) { - fd_set read_fds; - FD_ZERO(&read_fds); - - int tcp_fd = _tcp_socket.get_sockfd(); - int udp_fd = _udp_socket.get_sockfd(); - - FD_SET(tcp_fd, &read_fds); - FD_SET(udp_fd, &read_fds); - - int max_fd = std::max(tcp_fd, udp_fd); - - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 0; /* Non-blocking. */ - - int activity = select(max_fd+1, &read_fds, nullptr, nullptr, &tv); - - if(activity > 0) { - if(FD_ISSET(tcp_fd, &read_fds)) { - /* We only expect player ID assignment for now. */ - if(_our_player_id == 0) { - BettolaLib::Network::MessageHeader id_header; - ssize_t id_bytes_received = _tcp_socket.recv(&id_header, sizeof(id_header)); - if(id_bytes_received > 0 && id_header.type == BettolaLib::Network::MessageType::PlayerId) { - _tcp_socket.recv(&_our_player_id, sizeof(_our_player_id)); - printf("Bettola: Assigned player ID: %u\n", _our_player_id); - } - } - } - - if(FD_ISSET(udp_fd, &read_fds)) { - process_udp_messages(); - } - } -} - -void Bettola::process_udp_messages(void) { - char buffer[1024]; - sockaddr_in from_addr; - ssize_t id_bytes_received; - - while((id_bytes_received = _udp_socket.recv_from(buffer,sizeof(buffer),from_addr))>0) { - if(id_bytes_received < sizeof(BettolaLib::Network::MessageHeader)) { - continue; - } - - BettolaLib::Network::MessageHeader header; - memcpy(&header, buffer, sizeof(header)); - - const char* payload = buffer + sizeof(BettolaLib::Network::MessageHeader); - size_t payload_size = id_bytes_received-sizeof(BettolaLib::Network::MessageHeader); - - if(header.type == BettolaLib::Network::MessageType::GameState) { - if(payload_size >= sizeof(BettolaLib::Network::GameStateMessage)) { - BettolaLib::Network::GameStateMessage msg; - memcpy(&msg, payload, sizeof(msg)); - process_game_state(msg); - } - } - } -} - void Bettola::update(double dt) { - float dir_x = 0.0f; - float dir_y = 0.0f; - if(_input.up) dir_y -= 1.0f; - if(_input.down) dir_y += 1.0f; - if(_input.left) dir_x -= 1.0f; - if(_input.right) dir_x += 1.0f; - _player.set_velocity_direction(dir_x, dir_y); - - _player.update(dt); - - for(auto& remote_player : _remote_players) { - remote_player.update(dt); - } - - if(_our_player_id > 0) { - /* Sent our current input state to the server. */ - BettolaLib::Network::PlayerInputMessage input_msg; - input_msg.player_id = _our_player_id; - input_msg.sequence_number = ++_input_sequence_number; - input_msg.up = _input.up; - input_msg.down = _input.down; - input_msg.left = _input.left; - input_msg.right = _input.right; - input_msg.dt = dt; - - BettolaLib::Network::MessageHeader header; - header.type = BettolaLib::Network::MessageType::PlayerInput; - header.size = sizeof(input_msg); - - char buffer[sizeof(header) + sizeof(input_msg)]; - memcpy(buffer, &header, sizeof(header)); - memcpy(buffer+sizeof(header), &input_msg, sizeof(input_msg)); - - _udp_socket.send_to(buffer, sizeof(buffer), _server_addr); - - /* Store for reconciliation. */ - _pending_inputs.push_back(input_msg); - } + _game_client.process_network_messages(); + _game_client.send_input(_input, dt); + _game_client.update_players(dt); static char window_title[256]; static double time_since_title_update = 0.0; @@ -241,120 +113,10 @@ void Bettola::update(double dt) { } void Bettola::render(void) { - glClearColor(0.1f, 0.1f, 0.3f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - _shader.use(); - - BettolaMath::Mat4 trans_matrix = BettolaMath::Mat4::translation(_player.get_x(), - _player.get_y(), 0.0f); - - BettolaMath::Mat4 scale_matrix = BettolaMath::Mat4::scale(_player.get_width(), - _player.get_height(), 1.0f); - - BettolaMath::Mat4 model= trans_matrix.multiply(scale_matrix); - - unsigned int model_loc = glGetUniformLocation(_shader.get_id(), "model"); - glUniformMatrix4fv(model_loc, 1, GL_FALSE, model.get_ptr()); - - glBindVertexArray(_vao); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); - - /* Render remote players. */ - for(const auto& remote_player : _remote_players) { - BettolaMath::Mat4 remote_trans_matrix = - BettolaMath::Mat4::translation(remote_player.get_x(), - remote_player.get_y(), - 0.0f); - - BettolaMath::Mat4 remote_scale_matrix = - /* Assuming remote players have same size as local player. */ - BettolaMath::Mat4::scale(50.0f, 50.0f, 1.0f); - - BettolaMath::Mat4 remote_model = remote_trans_matrix.multiply(remote_scale_matrix); - - glUniformMatrix4fv(model_loc, 1, GL_FALSE, remote_model.get_ptr()); - - glBindVertexArray(_vao); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); - } - + _renderer.render(_game_client.get_player(), _game_client.get_remote_players()); SDL_GL_SwapWindow(_window); } -void Bettola::process_game_state(const BettolaLib::Network::GameStateMessage& msg) { - /* Track remote players each update. */ - std::set players_in_message; - - for(unsigned int i = 0; i < msg.num_players; ++i) { - const auto& player_state = msg.players[i]; - - if(player_state.player_id == _our_player_id) { - /* This is our player. Reconcile. */ - _player.set_position(player_state.x, player_state.y); - - /* Remove all inputs from our history that the server has processed. */ - while(!_pending_inputs.empty() && - _pending_inputs.front().sequence_number <= player_state.last_processed_sequence) { - - _pending_inputs.pop_front(); - } - - /* Now, re-apply any remaining inputs that the server hasn't - * seen yet. This'll bring the client up to date with most recent inputs. - */ - for(const auto& input : _pending_inputs) { - float dir_x = (input.right ? 1.0f : 0.0f) - (input.left ? 1.0f : 0.0f); - float dir_y = (input.down ? 1.0f : 0.0f) - (input.up ? 1.0f : 0.0f); - _player.set_velocity_direction(dir_x, dir_y); - _player.update(input.dt); - } - } else { - /* Remote player. Find if we already know about them.. */ - players_in_message.insert(player_state.player_id); - auto it = std::find_if(_remote_players.begin(), _remote_players.end(), - [&](const RemotePlayer& p) { - return p.get_id() == player_state.player_id; - }); - - if(it != _remote_players.end()) { - /* Found 'em! update their target pos. */ - it->set_target_position(player_state.x, player_state.y); - } else { - /* They are new, add them to our list. */ - _remote_players.emplace_back(player_state.player_id, player_state.x, player_state.y); - } - } - } - _remote_players.erase( - std::remove_if(_remote_players.begin(), _remote_players.end(), - [&](const RemotePlayer& p) { - return players_in_message.find(p.get_id()) == players_in_message.end(); - }), - - _remote_players.end()); -} - -bool Bettola::init_sdl(void) { - if(!SDL_Init(SDL_INIT_VIDEO)) { - fprintf(stderr, "Failed to iniit SDL! SDL ERROR: %s\n", SDL_GetError()); - return false; - } - return true; -} - -bool Bettola::init_glew(void) { - glewExperimental = GL_TRUE; - GLenum glew_error = glewInit(); - if(glew_error != GLEW_OK) { - fprintf(stderr, "Failed to init GLEW! %s\n", glewGetErrorString(glew_error)); - return false; - } - return true; -} - bool Bettola::create_window(void) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); @@ -381,64 +143,3 @@ bool Bettola::create_gl_context(void) { return true; } -void Bettola::setup_quad(void) { - float vertices[] = { - /* Should be a quad??!?? */ - -0.5f, -0.5f, 0.0f, - 0.5f, -0.5f, 0.0f, - 0.5f, 0.5f, 0.0f, - 0.5f, 0.5f, 0.0f, - -0.5f, 0.5f, 0.0f, - -0.5f, -0.5f, 0.0f, - }; - - glGenVertexArrays(1, &_vao); - glGenBuffers(1, &_vbo); - - glBindVertexArray(_vao); - - glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0); - glEnableVertexAttribArray(0); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} - -bool Bettola::init_client_connection(void) { - SDL_Delay(1000); /* Wait a second before trying to connect. */ - if(!_tcp_socket.create()) { - printf("Bettola client: Failed to create socket.\n"); - return false; - } - - if(!_tcp_socket.connect("127.0.0.1", BettolaLib::Network::DEFAULT_PORT)) { - perror("Bettola Client: Failed to connect to server.\n"); - return false; - } - - if(!_udp_socket.create()) { - printf("Bettola Client: Failed to create UDP socket.\n"); - return false; - } - - /* Setup server address for UDP. */ - _server_addr.sin_family = AF_INET; - _server_addr.sin_port = htons(BettolaLib::Network::DEFAULT_PORT); - inet_pton(AF_INET, "127.0.0.1", &_server_addr.sin_addr); - - /* Set the socket to non-blocking. */ - int flags = fcntl(_tcp_socket.get_sockfd(), F_GETFL, 0); - if(flags == -1) { - perror("fcntl F_GETFL failed."); - return false; - } - fcntl(_tcp_socket.get_sockfd(), F_SETFL, flags | O_NONBLOCK); - fcntl(_udp_socket.get_sockfd(), F_SETFL, flags | O_NONBLOCK); - - printf("Bettola Client: Connected to server at 127.0.01.:%hu\n", BettolaLib::Network::DEFAULT_PORT); - return true; -} - diff --git a/src/bettola.h b/src/bettola.h index 3143304..14e9542 100644 --- a/src/bettola.h +++ b/src/bettola.h @@ -5,14 +5,8 @@ #include #include -#include "graphics/shader.h" -#include "game/player.h" -#include "game/remote_player.h" -#include "math/mat4.h" -#include "network/tcpsocket.h" -#include "network/player_input_message.h" -#include "network/udpsocket.h" -namespace BettolaLib { namespace Network { struct GameStateMessage; } } +#include "graphics/renderer.h" +#include "game_client.h" class Bettola { public: @@ -23,46 +17,19 @@ public: private: void process_events(void); - void process_network(void); - void process_udp_messages(void); void update(double dt); void render(void); - void process_game_state(const BettolaLib::Network::GameStateMessage& msg); bool init_sdl(void); - bool init_glew(void); bool create_window(void); bool create_gl_context(void); - bool init_client_connection(void); - - void setup_quad(void); - - struct InputState { - bool up = false; - bool down = false; - bool left = false; - bool right = false; - }; - bool _is_running; - unsigned int _our_player_id; SDL_Window* _window; SDL_GLContext _gl_context; - Shader _shader; - unsigned int _vao; - unsigned int _vbo; - - BettolaMath::Mat4 _projection; - Player _player; - InputState _input; - std::vector _remote_players; - BettolaLib::Network::TCPSocket _tcp_socket; - BettolaLib::Network::UDPSocket _udp_socket; - sockaddr_in _server_addr; - - unsigned int _input_sequence_number; - std::deque _pending_inputs; + Renderer _renderer; + GameClient _game_client; + Player::InputState _input; }; diff --git a/src/game/player.cpp b/src/game/player.cpp index 81a2d0d..fb2bec8 100644 --- a/src/game/player.cpp +++ b/src/game/player.cpp @@ -1,5 +1,6 @@ #include "player.h" #include "bettola/math/vec2.h" +#include "network/player_input_message.h" Player::Player(void) : _x(400.0f), @@ -19,6 +20,13 @@ void Player::update(double dt) { _y += _vy * dt; } +void Player::apply_input(const BettolaLib::Network::PlayerInputMessage& input) { + float dir_x = (input.right ? 1.0f : 0.0f) - (input.left ? 1.0f : 0.0f); + float dir_y = (input.down ? 1.0f : 0.0f) - (input.up ? 1.0f : 0.0f); + set_velocity_direction(dir_x, dir_y); + update(input.dt); +} + void Player::set_velocity_direction(float dir_x, float dir_y) { BettolaMath::Vec2 direction = { dir_x, dir_y }; diff --git a/src/game/player.h b/src/game/player.h index 7a04119..887d605 100644 --- a/src/game/player.h +++ b/src/game/player.h @@ -1,11 +1,21 @@ #pragma once +namespace BettolaLib { namespace Network { struct PlayerInputMessage; } } + class Player { public: + struct InputState { + bool up =false; + bool down =false; + bool left =false; + bool right =false; + }; + Player(void); void update(double dt); void set_position(float x, float y); + void apply_input(const BettolaLib::Network::PlayerInputMessage& input); void set_velocity_direction(float dir_x, float dir_y); diff --git a/src/game_client.cpp b/src/game_client.cpp new file mode 100644 index 0000000..856a35f --- /dev/null +++ b/src/game_client.cpp @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include + +#include "game_client.h" +#include "game/remote_player.h" +#include "network/game_state_message.h" +#include "network/message.h" + +GameClient::GameClient(void) : _our_player_id(0), _input_sequence_number(0) { + memset(&_server_addr, 0, sizeof(_server_addr)); +} + +bool GameClient::connect(const char* host, unsigned short port) { + if(!_tcp_socket.create() || !_tcp_socket.connect(host,port)) { + perror("BettolaClient: Failed to connect TCP socket.\n"); + return false; + } + + if(!_udp_socket.create()) { + perror("BettolaClient: Failed to create UDP socket.\n"); + return false; + } + + /* Setup server address. */ + _server_addr.sin_family = AF_INET; + _server_addr.sin_port = htons(port); + inet_pton(AF_INET, host, &_server_addr.sin_addr); + + /* Set the socket to non-blocking. */ + int tcp_flags = fcntl(_tcp_socket.get_sockfd(), F_GETFL, 0); + fcntl(_tcp_socket.get_sockfd(), F_SETFL, tcp_flags | O_NONBLOCK); + + int udp_flags = fcntl(_udp_socket.get_sockfd(), F_GETFL, 0); + fcntl(_udp_socket.get_sockfd(), F_SETFL, udp_flags | O_NONBLOCK); + + printf("BettolaClient: Connected to server at %s:%hu\n", host, port); + return true; +} + +void GameClient::process_network_messages(void) { + fd_set read_fds; + FD_ZERO(&read_fds); + + int tcp_fd = _tcp_socket.get_sockfd(); + int udp_fd = _udp_socket.get_sockfd(); + + FD_SET(tcp_fd, &read_fds); + FD_SET(udp_fd, &read_fds); + + int max_fd = std::max(tcp_fd, udp_fd); + + struct timeval tv = {0,0}; /* Non-blocking. */ + + if(select(max_fd+1, &read_fds, nullptr, nullptr, &tv) > 0) { + if(FD_ISSET(tcp_fd, &read_fds) && _our_player_id == 0) { + BettolaLib::Network::MessageHeader header; + if(_tcp_socket.recv(&header, sizeof(header)) > 0 && header.type == + BettolaLib::Network::MessageType::PlayerId) { + _tcp_socket.recv(&_our_player_id, sizeof(_our_player_id)); + printf("BetollaClient: Assigned player ID: %u\n", _our_player_id); + } + } + if(FD_ISSET(udp_fd, &read_fds)) { + _process_udp_messages(); + } + } +} + +void GameClient::_process_udp_messages(void) { + char buffer[1024]; + sockaddr_in from_addr; + ssize_t bytes_received; + + while((bytes_received = _udp_socket.recv_from(buffer,sizeof(buffer),from_addr))>0) { + if(bytes_received < sizeof(BettolaLib::Network::MessageHeader)) continue; + + BettolaLib::Network::MessageHeader header; + memcpy(&header, buffer, sizeof(header)); + + const char* payload = buffer + sizeof(BettolaLib::Network::MessageHeader); + size_t payload_size = bytes_received-sizeof(BettolaLib::Network::MessageHeader); + + if(header.type == BettolaLib::Network::MessageType::GameState) { + if(payload_size >= sizeof(BettolaLib::Network::GameStateMessage)) { + BettolaLib::Network::GameStateMessage msg; + memcpy(&msg, payload, sizeof(msg)); + _process_game_state(msg); + } + } + } +} + +void GameClient::_process_game_state(const BettolaLib::Network::GameStateMessage& msg) { + /* Track remote players each update. */ + std::set players_in_message; + + for(unsigned int i = 0; i < msg.num_players; ++i) { + const auto& ps = msg.players[i]; + + if(ps.player_id == _our_player_id) { + /* This is our player. Reconcile. */ + _player.set_position(ps.x, ps.y); + + /* Remove all inputs from our history that the server has processed. */ + while(!_pending_inputs.empty() && + _pending_inputs.front().sequence_number <= ps.last_processed_sequence) { + _pending_inputs.pop_front(); + } + + /* Now, re-apply any remaining inputs that the server hasn't + * seen yet. This'll bring the client up to date with most recent inputs. + */ + for(const auto& input : _pending_inputs) { + _player.apply_input(input); + } + } else { + /* Remote player. Find if we already know about them.. */ + players_in_message.insert(ps.player_id); + auto it = std::find_if(_remote_players.begin(), _remote_players.end(), [&](const + RemotePlayer&p) {return p.get_id() == ps.player_id; }); + + if(it != _remote_players.end()) { + /* Found 'em! update their target pos. */ + it->set_target_position(ps.x, ps.y); + } else { + /* They are new, add them to our list. */ + _remote_players.emplace_back(ps.player_id, ps.x, ps.y); + } + } + } + + _remote_players.erase(std::remove_if(_remote_players.begin(), _remote_players.end(), + [&](const RemotePlayer& p) { return players_in_message.find(p.get_id()) == + players_in_message.end(); }), _remote_players.end()); +} + +void GameClient::send_input(const Player::InputState& input, float dt) { + if(_our_player_id > 0) { + /* Sent our current input state to the server. */ + BettolaLib::Network::PlayerInputMessage input_msg; + input_msg.player_id = _our_player_id; + input_msg.sequence_number = ++_input_sequence_number; + input_msg.up = input.up; + input_msg.down = input.down; + input_msg.left = input.left; + input_msg.right = input.right; + input_msg.dt = dt; + + BettolaLib::Network::MessageHeader header; + header.type = BettolaLib::Network::MessageType::PlayerInput; + header.size = sizeof(input_msg); + + char buffer[sizeof(header) + sizeof(input_msg)]; + memcpy(buffer, &header, sizeof(header)); + memcpy(buffer+sizeof(header), &input_msg, sizeof(input_msg)); + + _udp_socket.send_to(buffer, sizeof(buffer), _server_addr); + _pending_inputs.push_back(input_msg); /* Store for reconciliation. */ + } +} + +void GameClient::update_players(float dt) { + _player.update(dt); + for(auto& p : _remote_players) { + p.update(dt); + } +} + diff --git a/src/game_client.h b/src/game_client.h new file mode 100644 index 0000000..fe10a15 --- /dev/null +++ b/src/game_client.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +#include "game/player.h" +#include "game/remote_player.h" +#include "network/tcpsocket.h" +#include "network/udpsocket.h" +#include "network/player_input_message.h" +namespace BettolaLib { namespace Network { struct GameStateMessage; } } + +class GameClient { +public: + GameClient(void); + + bool connect(const char* host, unsigned short port); + void process_network_messages(void); + void send_input(const Player::InputState& input, float dt); + + const Player& get_player(void) const { return _player; } + const std::vector& get_remote_players(void) const { return _remote_players; } + void update_players(float dt); + +private: + void _process_udp_messages(void); + void _process_game_state(const BettolaLib::Network::GameStateMessage& msg); + + unsigned int _our_player_id; + Player _player; + std::vector _remote_players; + + BettolaLib::Network::TCPSocket _tcp_socket; + BettolaLib::Network::UDPSocket _udp_socket; + sockaddr_in _server_addr; + + unsigned int _input_sequence_number; + std::deque _pending_inputs; +}; diff --git a/src/graphics/renderer.cpp b/src/graphics/renderer.cpp new file mode 100644 index 0000000..8507c4f --- /dev/null +++ b/src/graphics/renderer.cpp @@ -0,0 +1,116 @@ +#include +#include +#include "renderer.h" + +Renderer::Renderer(void) : _vao(0), _vbo(0) {} + +Renderer::~Renderer(void) { + if(_vao != 0) { + glDeleteVertexArrays(1, &_vao); + } + if(_vbo != 0) { + glDeleteBuffers(1, &_vbo); + } +} + +bool Renderer::init(int screen_width, int screen_height) { + glewExperimental = GL_TRUE; + GLenum glew_error = glewInit(); + if(glew_error != GLEW_OK) { + fprintf(stderr, "Failed to init GLEW! %s\n", glewGetErrorString(glew_error)); + return false; + } + + if(!_init_shaders()) { + return false; + } + + _projection = BettolaMath::Mat4::orthographic(0.0f, (float)screen_width, (float)screen_height, 0.0f, -1.0f, 1.0f); + _shader.use(); + unsigned int proj_loc = glGetUniformLocation(_shader.get_id(), "projection"); + glUniformMatrix4fv(proj_loc, 1, GL_FALSE, _projection.get_ptr()); + + _setup_quad(); + + glViewport(0,0,screen_width, screen_height); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + return true; +} + +void Renderer::render(const Player& player, const std::vector& remote_players) { + glClearColor(0.1f, 0.1f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + _shader.use(); + + BettolaMath::Mat4 trans_matrix = BettolaMath::Mat4::translation(player.get_x(), + player.get_y(), 0.0f); + + BettolaMath::Mat4 scale_matrix = BettolaMath::Mat4::scale(player.get_width(), + player.get_height(), 1.0f); + + BettolaMath::Mat4 model= trans_matrix.multiply(scale_matrix); + + unsigned int model_loc = glGetUniformLocation(_shader.get_id(), "model"); + glUniformMatrix4fv(model_loc, 1, GL_FALSE, model.get_ptr()); + + glBindVertexArray(_vao); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + /* Render remote players. */ + for(const auto& remote_player : remote_players) { + BettolaMath::Mat4 remote_trans_matrix = + BettolaMath::Mat4::translation(remote_player.get_x(), + remote_player.get_y(), + 0.0f); + + BettolaMath::Mat4 remote_scale_matrix = + /* Assuming remote players have same size as local player. */ + BettolaMath::Mat4::scale(50.0f, 50.0f, 1.0f); + + BettolaMath::Mat4 remote_model = remote_trans_matrix.multiply(remote_scale_matrix); + + glUniformMatrix4fv(model_loc, 1, GL_FALSE, remote_model.get_ptr()); + + glBindVertexArray(_vao); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + } +} + +bool Renderer::_init_shaders(void) { + if(!_shader.load_from_files("assets/shaders/simple.vert", + "assets/shaders/simple.frag")) { + return false; + } + return true; +} + +void Renderer::_setup_quad(void) { + float vertices[] = { + /* Should be a quad??!?? */ + -0.5f, -0.5f, 0.0f, + 0.5f, -0.5f, 0.0f, + 0.5f, 0.5f, 0.0f, + 0.5f, 0.5f, 0.0f, + -0.5f, 0.5f, 0.0f, + -0.5f, -0.5f, 0.0f, + }; + + glGenVertexArrays(1, &_vao); + glGenBuffers(1, &_vbo); + + glBindVertexArray(_vao); + + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} diff --git a/src/graphics/renderer.h b/src/graphics/renderer.h new file mode 100644 index 0000000..2297266 --- /dev/null +++ b/src/graphics/renderer.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "shader.h" +#include "game/player.h" +#include "game/remote_player.h" +#include "math/mat4.h" + +class Renderer { +public: + Renderer(void); + ~Renderer(void); + + bool init(int screen_width, int screen_height); + void render(const Player& player, const std::vector& remote_players); + +private: + bool _init_shaders(); + void _setup_quad(); + + Shader _shader; + unsigned int _vao; + unsigned int _vbo; + BettolaMath::Mat4 _projection; +}; +