From f2d9a02b6d71bfa0bb1f94de550f5be9119779cd Mon Sep 17 00:00:00 2001 From: tovjemam Date: Fri, 2 Jan 2026 22:19:29 +0100 Subject: [PATCH] Monstertruck racing --- CMakeLists.txt | 131 ++++++------- CMakePresets.json => _CMakePresets.json | 0 src/assets/vehiclemdl.cpp | 6 +- src/assets/vehiclemdl.hpp | 4 +- src/client/app.cpp | 13 +- src/client/app.hpp | 1 + src/client/main.cpp | 35 +++- src/collision/dynamicsworld.cpp | 13 ++ src/collision/dynamicsworld.hpp | 9 + src/game/controllable.cpp | 9 + src/game/controllable.hpp | 30 +++ src/game/entity.cpp | 8 + src/game/entity.hpp | 12 ++ src/game/game.cpp | 21 +- src/game/game.hpp | 7 + src/game/openworld.cpp | 28 +++ src/game/openworld.hpp | 21 ++ src/game/player.cpp | 47 ++++- src/game/player.hpp | 18 +- src/game/vehicle.cpp | 250 ++++++++++++++++++++++-- src/game/vehicle.hpp | 19 +- src/game/world.cpp | 21 +- src/game/world.hpp | 9 + src/gameview/client_session.cpp | 4 + src/gameview/client_session.hpp | 3 +- src/gameview/entityview.hpp | 15 +- src/gameview/vehicleview.cpp | 95 +++++++++ src/gameview/vehicleview.hpp | 32 +++ src/gameview/worldview.cpp | 29 ++- src/net/defs.hpp | 4 + src/net/fixed_str.hpp | 2 + src/net/inmessage.hpp | 25 ++- src/net/outmessage.hpp | 21 +- src/net/quantized.hpp | 11 ++ src/server/client.cpp | 2 +- src/server/client.hpp | 1 + src/server/server.cpp | 43 +++- src/server/server.hpp | 3 + src/utils/allocnum.hpp | 4 +- src/utils/defs.hpp | 10 + src/utils/files_server.cpp | 20 ++ 41 files changed, 909 insertions(+), 127 deletions(-) rename CMakePresets.json => _CMakePresets.json (100%) create mode 100644 src/game/controllable.cpp create mode 100644 src/game/controllable.hpp create mode 100644 src/game/openworld.cpp create mode 100644 src/game/openworld.hpp create mode 100644 src/gameview/vehicleview.cpp create mode 100644 src/gameview/vehicleview.hpp create mode 100644 src/utils/files_server.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c904012..338ac29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,32 +5,56 @@ project(FekalniGtacko) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(SOURCES - "src/client/main.cpp" - "src/client/app.hpp" - "src/client/app.cpp" - "src/client/gl.hpp" - "src/client/utils.hpp" +set(COMMON_SOURCES "src/assets/animation.hpp" "src/assets/animation.cpp" "src/assets/cache.hpp" "src/assets/cache.cpp" "src/assets/cmdfile.hpp" "src/assets/cmdfile.cpp" - "src/assets/model.hpp" - "src/assets/model.cpp" - "src/assets/mesh_builder.hpp" - "src/assets/mesh_builder.cpp" "src/assets/map.hpp" "src/assets/map.cpp" + "src/assets/model.hpp" + "src/assets/model.cpp" "src/assets/skeleton.hpp" "src/assets/skeleton.cpp" + "src/assets/vehiclemdl.hpp" + "src/assets/vehiclemdl.cpp" + "src/collision/aabb.hpp" + "src/collision/dynamicsworld.hpp" + "src/collision/dynamicsworld.cpp" + "src/collision/motionstate.hpp" + "src/collision/trianglemesh.hpp" "src/collision/trianglemesh.cpp" "src/game/player_input.hpp" "src/game/transform_node.hpp" + "src/net/defs.hpp" + "src/net/fixed_str.hpp" + "src/net/inmessage.hpp" + "src/net/msg_producer.hpp" + "src/net/msg_producer.cpp" + "src/net/outmessage.hpp" + "src/net/quantized.hpp" + "src/utils/allocnum.hpp" + "src/utils/defs.hpp" + "src/utils/files.hpp" + "src/utils/transform.hpp" + "src/utils/validate.hpp" +) + +set(CLIENT_ONLY_SOURCES + "src/assets/mesh_builder.hpp" + "src/assets/mesh_builder.cpp" + "src/client/app.hpp" + "src/client/app.cpp" + "src/client/gl.hpp" + "src/client/main.cpp" + "src/client/utils.hpp" "src/gameview/client_session.hpp" "src/gameview/client_session.cpp" "src/gameview/entityview.hpp" + "src/gameview/vehicleview.hpp" + "src/gameview/vehicleview.cpp" "src/gameview/worldview.hpp" "src/gameview/worldview.cpp" "src/gfx/buffer_object.cpp" @@ -49,29 +73,43 @@ set(SOURCES "src/gfx/uniform_buffer.hpp" "src/gfx/vertex_array.cpp" "src/gfx/vertex_array.hpp" - "src/net/defs.hpp" - "src/net/fixed_str.hpp" - "src/net/inmessage.hpp" - "src/net/msg_producer.hpp" - "src/net/msg_producer.cpp" - "src/net/outmessage.hpp" - "src/net/quantized.hpp" - "src/utils/defs.hpp" - "src/utils/files.hpp" "src/utils/files.cpp" - "src/utils/transform.hpp" ) +set(SERVER_ONLY_SOURCES + "src/game/controllable.cpp" + "src/game/controllable.hpp" + "src/game/entity.hpp" + "src/game/entity.cpp" + "src/game/game.hpp" + "src/game/game.cpp" + "src/game/openworld.hpp" + "src/game/openworld.cpp" + "src/game/player.hpp" + "src/game/player.cpp" + "src/game/vehicle.hpp" + "src/game/vehicle.cpp" + "src/game/world.hpp" + "src/game/world.cpp" + "src/server/client.hpp" + "src/server/client.cpp" + "src/server/main.cpp" + "src/server/server.hpp" + "src/server/server.cpp" + "src/server/wsserver.hpp" + "src/server/wsserver.cpp" + "src/utils/files_server.cpp" +) if(ANDROID) # Android-specific setup set(MAIN_NAME "main") - add_library(${MAIN_NAME} SHARED ${SOURCES}) + add_library(${MAIN_NAME} SHARED ${COMMON_SOURCES} ${CLIENT_ONLY_SOURCES}) target_link_libraries(${MAIN_NAME} PRIVATE GLESv3 log android) else() # Desktop build set(MAIN_NAME "FekalniGtacko") - add_executable(${MAIN_NAME} ${SOURCES}) + add_executable(${MAIN_NAME} ${COMMON_SOURCES} ${CLIENT_ONLY_SOURCES}) endif() @@ -162,56 +200,11 @@ endif() add_subdirectory(external/Crow) set(SERVER_SOURCES - "src/assets/animation.hpp" - "src/assets/animation.cpp" - "src/assets/cache.hpp" - "src/assets/cache.cpp" - "src/assets/cmdfile.hpp" - "src/assets/cmdfile.cpp" - "src/assets/map.hpp" - "src/assets/map.cpp" - "src/assets/model.hpp" - "src/assets/model.cpp" - "src/assets/mesh_builder.cpp" - "src/assets/skeleton.hpp" - "src/assets/skeleton.cpp" - "src/collision/aabb.hpp" - "src/collision/dynamicsworld.hpp" - "src/collision/dynamicsworld.cpp" - "src/collision/trianglemesh.hpp" - "src/collision/trianglemesh.cpp" - "src/game/entity.hpp" - "src/game/entity.cpp" - "src/game/game.hpp" - "src/game/game.cpp" - "src/game/player_input.hpp" - "src/game/player.hpp" - "src/game/player.cpp" - "src/game/transform_node.hpp" - "src/game/world.hpp" - "src/game/world.cpp" - "src/net/defs.hpp" - "src/net/fixed_str.hpp" - "src/net/inmessage.hpp" - "src/net/msg_producer.hpp" - "src/net/msg_producer.cpp" - "src/net/outmessage.hpp" - "src/net/quantized.hpp" - "src/server/client.hpp" - "src/server/client.cpp" - "src/server/main.cpp" - "src/server/server.hpp" - "src/server/server.cpp" - "src/server/wsserver.hpp" - "src/server/wsserver.cpp" - "src/utils/defs.hpp" - "src/utils/files.hpp" - "src/utils/files.cpp" - "src/utils/transform.hpp" + ) set(SERVER_NAME server) - add_executable(${SERVER_NAME} ${SERVER_SOURCES}) + add_executable(${SERVER_NAME} ${COMMON_SOURCES} ${SERVER_ONLY_SOURCES}) target_link_libraries(${SERVER_NAME} PRIVATE Crow glm BulletCollision BulletDynamics LinearMath Bullet3Common) target_include_directories(${SERVER_NAME} PRIVATE "src" "external/bullet3/src") target_compile_definitions(${SERVER_NAME} PRIVATE SERVER) diff --git a/CMakePresets.json b/_CMakePresets.json similarity index 100% rename from CMakePresets.json rename to _CMakePresets.json diff --git a/src/assets/vehiclemdl.cpp b/src/assets/vehiclemdl.cpp index 0ad9d13..153b942 100644 --- a/src/assets/vehiclemdl.cpp +++ b/src/assets/vehiclemdl.cpp @@ -5,7 +5,7 @@ std::shared_ptr assets::VehicleModel::LoadFromFile(const std::string& filename) { - auto veh = std::shared_ptr(); + auto veh = std::make_shared(); LoadCMDFile(filename, [&](const std::string& command, std::istringstream& iss) { if (command == "basemodel") @@ -40,4 +40,6 @@ std::shared_ptr assets::VehicleModel::LoadFromFile(c veh->wheels_.emplace_back(wheel); } }); -} \ No newline at end of file + + return veh; +} diff --git a/src/assets/vehiclemdl.hpp b/src/assets/vehiclemdl.hpp index 2c6236c..c770a74 100644 --- a/src/assets/vehiclemdl.hpp +++ b/src/assets/vehiclemdl.hpp @@ -32,8 +32,8 @@ public: static std::shared_ptr LoadFromFile(const std::string& filename); - const std::shared_ptr& GetModel() { return basemodel_; } - const std::vector& GetWheels() { return wheels_; } + const std::shared_ptr& GetModel() const { return basemodel_; } + const std::vector& GetWheels() const { return wheels_; } private: std::shared_ptr basemodel_; diff --git a/src/client/app.cpp b/src/client/app.cpp index 20c4c65..fd1d04b 100644 --- a/src/client/app.cpp +++ b/src/client/app.cpp @@ -45,7 +45,7 @@ void App::Frame() { world->Draw(dlist_); - glm::mat4 view = glm::lookAt(glm::vec3(80.0f, 0.0f, 10.0f), glm::vec3(40.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + glm::mat4 view = glm::lookAt(glm::vec3(15.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, -13.0f), glm::vec3(0.0f, 0.0f, 1.0f)); glm::mat4 proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 3000.0f); gfx::DrawListParams params; @@ -53,6 +53,15 @@ void App::Frame() renderer_.DrawList(dlist_, params); } + + if (time_ - last_send_time_ > 0.040f) + { + auto msg = BeginMsg(net::MSG_IN); + msg.Write(input_); + + last_send_time_ = time_; + + } } void App::Connected() @@ -60,7 +69,7 @@ void App::Connected() std::cout << "WS connected" << std::endl; // init session - session_ = std::make_unique(); + session_ = std::make_unique(*this); // send login auto msg = BeginMsg(net::MSG_ID); diff --git a/src/client/app.hpp b/src/client/app.hpp index 264948f..4a7b76e 100644 --- a/src/client/app.hpp +++ b/src/client/app.hpp @@ -33,6 +33,7 @@ private: private: float time_ = 0.0f; + float last_send_time_ = 0.0f; glm::ivec2 viewport_size_ = {800, 600}; game::PlayerInputFlags input_ = 0; game::PlayerInputFlags prev_input_ = 0; diff --git a/src/client/main.cpp b/src/client/main.cpp index f7072f1..a01a5d1 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -10,6 +10,12 @@ #include #endif // EMSCRIPTEN +#ifdef _WIN32 +#define NOMINMAX +#pragma comment(lib, "ws2_32") +#include +#endif + #include "app.hpp" #include "gl.hpp" @@ -47,7 +53,9 @@ static void InitSDL() SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); std::cout << "Creating SDL window..." << std::endl; - s_window = SDL_CreateWindow("PortalGame", 100, 100, 640, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_MAXIMIZED | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + s_window = + SDL_CreateWindow("PortalGame", 100, 100, 640, 480, + SDL_WINDOW_SHOWN /* | SDL_WINDOW_MAXIMIZED */| SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if (!s_window) { ThrowSDLError("SDL_CreateWindow"); @@ -219,15 +227,29 @@ static void Main() { #ifdef EMSCRIPTEN emscripten_set_main_loop(Frame, 0, true); #else + +#ifdef _WIN32 + INT rc; + WSADATA wsaData; + + rc = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (rc) + { + printf("WSAStartup Failed.\n"); + return; + } +#endif + { using namespace easywsclient; - auto ws = std::unique_ptr(WebSocket::from_url("ws://127.0.0.1:8080/ws")); + auto ws = std::unique_ptr(WebSocket::from_url("ws://127.0.0.1:11200/ws")); bool connected = false; + std::vector data; - while (!s_quit) + while (ws && !s_quit) { ws->poll(); @@ -237,7 +259,7 @@ static void Main() { connected = true; s_app->Connected(); } - else if (ws_state != WebSocket::CLOSED && connected) + else if (ws_state != WebSocket::OPEN && connected) { connected = false; s_app->Disconnected("WS closed"); @@ -270,6 +292,11 @@ static void Main() { ShutdownGL(); ShutdownSDL(); + +#ifdef _WIN32 + WSACleanup(); +#endif + #endif // EMSCRIPTEN } diff --git a/src/collision/dynamicsworld.cpp b/src/collision/dynamicsworld.cpp index c4d38d8..8b5909d 100644 --- a/src/collision/dynamicsworld.cpp +++ b/src/collision/dynamicsworld.cpp @@ -6,7 +6,20 @@ collision::DynamicsWorld::DynamicsWorld(std::shared_ptr map) : map_(std::move(map)), bt_dispatcher_(&bt_cfg_), bt_world_(&bt_dispatcher_, &bt_broadphase_, &bt_solver_, &bt_cfg_), bt_veh_raycaster_(&bt_world_) { + bt_world_.setGravity(btVector3(0, 0, -9.81f)); + AddMapCollision(); + + btTransform t; + t.setIdentity(); + t.setOrigin(btVector3(0,0,-12)); + + // TODO: remove + static btDefaultMotionState motion(t); + static btBoxShape box(btVector3(100, 100, 2)); + btRigidBody::btRigidBodyConstructionInfo rbInfo(0.0f, &motion, &box, btVector3(0,0,0)); + static btRigidBody body(rbInfo); + bt_world_.addRigidBody(&body); } void collision::DynamicsWorld::AddMapCollision() diff --git a/src/collision/dynamicsworld.hpp b/src/collision/dynamicsworld.hpp index 0a6664f..c41fe7e 100644 --- a/src/collision/dynamicsworld.hpp +++ b/src/collision/dynamicsworld.hpp @@ -9,6 +9,15 @@ namespace collision { +// struct StaticObjectInstance +// { +// std::unique_ptr shape; +// btRigidBody body; + +// StaticObjectInstance(std::unique_ptr shape) : body() + +// } + class DynamicsWorld { public: diff --git a/src/game/controllable.cpp b/src/game/controllable.cpp new file mode 100644 index 0000000..c10bb10 --- /dev/null +++ b/src/game/controllable.cpp @@ -0,0 +1,9 @@ +#include "controllable.hpp" + +#include "player.hpp" + +game::Controllable::~Controllable() +{ + if (controller_) + controller_->Control(nullptr); +} diff --git a/src/game/controllable.hpp b/src/game/controllable.hpp new file mode 100644 index 0000000..ff9383a --- /dev/null +++ b/src/game/controllable.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "utils/defs.hpp" + +namespace game +{ + +class Entity; +class Player; + +class Controllable +{ +public: + Controllable() = default; + DELETE_COPY_MOVE(Controllable) + + virtual Entity& GetEntity() = 0; + + Player* GetController() { return controller_; } + const Player* GetController() const { return controller_; } + + ~Controllable(); + +private: + Player* controller_ = nullptr; + friend class Player; + +}; + +} \ No newline at end of file diff --git a/src/game/entity.cpp b/src/game/entity.cpp index 34f558e..3a88b17 100644 --- a/src/game/entity.cpp +++ b/src/game/entity.cpp @@ -3,3 +3,11 @@ #include "world.hpp" game::Entity::Entity(World& world, net::EntType viewtype) : world_(world), entnum_(world.GetNewEntnum()), viewtype_(viewtype) {} + +net::OutMessage game::Entity::BeginEntMsg(net::EntMsgType type) +{ + auto msg = BeginMsg(net::MSG_ENTMSG); + msg.Write(entnum_); + msg.Write(type); + return msg; +} diff --git a/src/game/entity.hpp b/src/game/entity.hpp index c158202..0e71280 100644 --- a/src/game/entity.hpp +++ b/src/game/entity.hpp @@ -4,6 +4,7 @@ #include "net/msg_producer.hpp" #include "transform_node.hpp" +#include "utils/defs.hpp" namespace game { @@ -15,6 +16,7 @@ class Entity : public net::MsgProducer { public: Entity(World& world, net::EntType viewtype); + DELETE_COPY_MOVE(Entity) net::EntNum GetEntNum() const { return entnum_; } net::EntType GetViewType() const { return viewtype_; } @@ -22,12 +24,22 @@ public: virtual void Update() { ResetMsg(); } virtual void SendInitData(Player& player, net::OutMessage& msg) const {} + void Remove() { removed_ = true; } + bool IsRemoved() const { return removed_; } + + virtual ~Entity() = default; + +protected: + net::OutMessage BeginEntMsg(net::EntMsgType type); + protected: World& world_; const net::EntNum entnum_; const net::EntType viewtype_; TransformNode root_; + + bool removed_ = false; }; } \ No newline at end of file diff --git a/src/game/game.cpp b/src/game/game.cpp index c60ccd0..a8eafb7 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -1,8 +1,25 @@ #include "game.hpp" +#include "player.hpp" +#include "openworld.hpp" + game::Game::Game() { - default_world_ = std::make_shared("openworld"); + default_world_ = std::make_shared(); +} + +void game::Game::Update() +{ + default_world_->Update(40); +} + +void game::Game::PlayerJoined(Player& player) +{ + player.SetWorld(default_world_.get()); +} + +void game::Game::PlayerLeft(Player& player) +{ -} \ No newline at end of file +} diff --git a/src/game/game.hpp b/src/game/game.hpp index 07d86b8..300726f 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -7,11 +7,18 @@ namespace game { +class Player; + class Game { public: Game(); + void Update(); + + void PlayerJoined(Player& player); + void PlayerLeft(Player& player); + private: std::shared_ptr default_world_; diff --git a/src/game/openworld.cpp b/src/game/openworld.cpp new file mode 100644 index 0000000..97b49c3 --- /dev/null +++ b/src/game/openworld.cpp @@ -0,0 +1,28 @@ +#include "openworld.hpp" + +#include "vehicle.hpp" +#include "player.hpp" + +game::OpenWorld::OpenWorld() : World("openworld") {} + +void game::OpenWorld::PlayerJoined(Player& player) +{ + // spawn him car + auto& vehicle = Spawn("pickup"); + player.Control(&vehicle); + + player_vehicles_[&player] = vehicle.GetEntNum(); +} + +void game::OpenWorld::PlayerLeft(Player& player) +{ + auto it = player_vehicles_.find(&player); + + Entity* ent = GetEntity(it->second); + if (ent) + ent->Remove(); + + player_vehicles_.erase(it); + + +} diff --git a/src/game/openworld.hpp b/src/game/openworld.hpp new file mode 100644 index 0000000..92cd8dd --- /dev/null +++ b/src/game/openworld.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "world.hpp" + +namespace game +{ + +class OpenWorld : public World +{ +public: + OpenWorld(); + + virtual void PlayerJoined(Player& player) override; + virtual void PlayerLeft(Player& player) override; + +private: + std::map player_vehicles_; + +}; + +} \ No newline at end of file diff --git a/src/game/player.cpp b/src/game/player.cpp index 46e1e93..59cdc45 100644 --- a/src/game/player.cpp +++ b/src/game/player.cpp @@ -2,7 +2,12 @@ #include "world.hpp" -game::Player::Player(Game& game, std::string name) : game_(game), name_(std::move(name)) {} +#include + +game::Player::Player(Game& game, std::string name) : game_(game), name_(std::move(name)) +{ + game_.PlayerJoined(*this); +} bool game::Player::ProcessMsg(net::MessageType type, net::InMessage& msg) { @@ -28,8 +33,45 @@ void game::Player::Update() SyncEntities(); } +void game::Player::SetWorld(World* world) +{ + if (world == world_) + return; + + Control(nullptr); + + if (world_) + world_->PlayerLeft(*this); + + world_ = world; + + if (world_) + world_->PlayerJoined(*this); +} + +void game::Player::Control(Controllable* ctl) +{ + if (ctl == ctl_) + return; + + if (ctl_) + ctl_->controller_ = nullptr; // clear old + + ctl_ = ctl; + + if (ctl_) + ctl_->controller_ = this; +} + +game::Player::~Player() +{ + SetWorld(nullptr); + game_.PlayerLeft(*this); +} + void game::Player::SendWorldMsg() { + MSGDEBUG(std::cout << "seding CHWORLD" << std::endl;) auto msg = BeginMsg(net::MSG_CHWORLD); msg.Write(net::MapName(world_->GetMapName())); } @@ -87,6 +129,7 @@ bool game::Player::ShouldSeeEntity(const Entity& entity) const void game::Player::SendInitEntity(const Entity& entity) { + MSGDEBUG(std::cout << "seding ENTSPAWN " << entity.GetEntNum() << std::endl;) auto msg = BeginMsg(net::MSG_ENTSPAWN); msg.Write(entity.GetEntNum()); msg.Write(entity.GetViewType()); @@ -95,12 +138,14 @@ void game::Player::SendInitEntity(const Entity& entity) void game::Player::SendUpdateEntity(const Entity& entity) { + MSGDEBUG(std::cout << "seding update ent " << entity.GetEntNum() << std::endl;) auto msg = BeginMsg(); // no CMD here, these are already included in entity message payload! msg.Write(entity.GetMsg()); } void game::Player::SendDestroyEntity(net::EntNum entnum) { + MSGDEBUG(std::cout << "seding ENTDESTROY " << entnum << std::endl;) auto msg = BeginMsg(net::MSG_ENTDESTROY); msg.Write(entnum); } diff --git a/src/game/player.hpp b/src/game/player.hpp index 5bc3e8f..b39f956 100644 --- a/src/game/player.hpp +++ b/src/game/player.hpp @@ -2,17 +2,20 @@ #include -#include "server/client.hpp" +#include "net/defs.hpp" +#include "net/inmessage.hpp" #include "net/msg_producer.hpp" #include "player_input.hpp" #include "game.hpp" +#include "controllable.hpp" namespace game { class World; class Entity; +class Vehicle; enum PlayerState { @@ -25,10 +28,18 @@ class Player : public net::MsgProducer { public: Player(Game& game, std::string name); + DELETE_COPY_MOVE(Player) bool ProcessMsg(net::MessageType type, net::InMessage& msg); void Update(); + void SetWorld(World* world); + void Control(Controllable* ctl); + + PlayerInputFlags GetInput() const { return in_; } + + ~Player(); + private: void SendWorldMsg(); @@ -50,9 +61,10 @@ private: World* known_world_ = nullptr; std::set known_ents_; - PlayerInputFlags in_; + PlayerInputFlags in_ = 0; - PlayerState state_; + PlayerState state_ = PS_NONE; + Controllable* ctl_ = nullptr; }; diff --git a/src/game/vehicle.cpp b/src/game/vehicle.cpp index 27a8b19..54c4889 100644 --- a/src/game/vehicle.cpp +++ b/src/game/vehicle.cpp @@ -1,34 +1,256 @@ #include "vehicle.hpp" #include "assets/cache.hpp" +#include "player.hpp" +#include "player_input.hpp" static std::shared_ptr LoadVehicleModelByName(const std::string& model_name) { - return assets::CacheManager::GetVehicleModel("data/" + model_name + ".map"); + return assets::CacheManager::GetVehicleModel("data/" + model_name + ".veh"); } - - -game::Vehicle::Vehicle(World& world, std::string model_name) : - Entity(world, net::ET_VEHICLE), - model_name_(model_name), - model_(LoadVehicleModelByName(model_name)), - motion_(root_.local) +struct Shape { + btBoxShape box; + btCompoundShape compound; + + Shape() : box(btVector3(1, 1, 1)) + { + btTransform t(btQuaternion(0, 0, 0), btVector3(0, 0, 2)); + compound.addChildShape(t, &box); + } +}; + +game::Vehicle::Vehicle(World& world, std::string model_name) + : Entity(world, net::ET_VEHICLE), model_name_(model_name), model_(LoadVehicleModelByName(model_name)), + motion_(root_.local) +{ + root_.local.position.z = 10.0f; + // setup chassis rigidbody float mass = 300.0f; - static btBoxShape shape(btVector3(1, 1, 1)); - - btVector3 local_inertia(0, 0, 0); - shape.calculateLocalInertia(mass, local_inertia); + static Shape shape; - btRigidBody::btRigidBodyConstructionInfo rb_info(mass, &motion_, &shape, local_inertia); + btVector3 local_inertia(0, 0, 0); + shape.compound.calculateLocalInertia(mass, local_inertia); + + btRigidBody::btRigidBodyConstructionInfo rb_info(mass, &motion_, &shape.compound, local_inertia); body_ = std::make_unique(rb_info); + body_->setActivationState(DISABLE_DEACTIVATION); // setup vehicle btRaycastVehicle::btVehicleTuning tuning; vehicle_ = std::make_unique(tuning, body_.get(), &world_.GetVehicleRaycaster()); vehicle_->setCoordinateSystem(0, 2, 1); - + + // setup wheels + // btVector3 wheelDirectionCS0(0, -1, 0); + // btVector3 wheelAxleCS(-1, 0, 0); + btVector3 wheelDirectionCS0(0, 0, -1); + btVector3 wheelAxleCS(1, 0, 0); + + for (const auto& wheels = model_->GetWheels(); const auto& wheeldef : wheels) + { + float suspension_rest_length = 0.6f; + + float wheelRadius = .35f; + + float friction = 5.0f; + float suspensionStiffness = 60.0f; + //float suspensionDamping = 2.3f; + //float suspensionCompression = 4.4f; + float suspensionRestLength = 0.6f; + float rollInfluence = 0.01f; + + float maxSuspensionForce = 100000.0f; + float maxSuspensionTravelCm = 5000.0f; + + float k = 0.2; + + const bool is_front = !(wheeldef.type & assets::WHEEL_REAR); + + btVector3 wheel_pos(wheeldef.position.x, wheeldef.position.y, wheeldef.position.z); + auto& wi = vehicle_->addWheel(wheel_pos, wheelDirectionCS0, wheelAxleCS, suspension_rest_length, + wheelRadius, tuning, is_front); + + wi.m_suspensionStiffness = suspensionStiffness; + + wi.m_wheelsDampingCompression = k * 2.0 * btSqrt(suspensionStiffness); //vehicleTuning.suspensionCompression; + wi.m_wheelsDampingRelaxation = k * 3.3 * btSqrt(suspensionStiffness);//vehicleTuning.suspensionDamping; + + wi.m_frictionSlip = friction; + //if (wi.m_bIsFrontWheel) wi.m_frictionSlip = vehicleTuning.friction * 1.4f; + + wi.m_rollInfluence = rollInfluence; + wi.m_maxSuspensionForce = maxSuspensionForce; + wi.m_maxSuspensionTravelCm = maxSuspensionTravelCm; + } + + auto& bt_world = world_.GetBtWorld(); + bt_world.addRigidBody(body_.get()); + bt_world.addAction(vehicle_.get()); + } +void game::Vehicle::Update() +{ + Super::Update(); + + ProcessInput(); + + for (int j = 0; j < vehicle_->getNumWheels(); j++) + { + auto& wheel = vehicle_->getWheelInfo(j); + float sus_length = wheel.m_raycastInfo.m_suspensionLength; + // TODO: sync wheels + } + + SendUpdateMsg(); +} + +void game::Vehicle::SendInitData(Player& player, net::OutMessage& msg) const +{ + net::ModelName name(model_name_); + msg.Write(name); +} + +game::Vehicle::~Vehicle() +{ + auto& bt_world = world_.GetBtWorld(); + bt_world.removeRigidBody(body_.get()); + bt_world.removeAction(vehicle_.get()); +} + +void game::Vehicle::ProcessInput() +{ + // TODO: totally fix + + float steeringIncrement = .04 * 60; + // float steeringClamp = .5; + float maxEngineForce = 7000; + float maxBreakingForce = 300; + + float engineForce = 0; + float breakingForce = 0; + + // process input + PlayerInputFlags in = GetController() ? GetController()->GetInput() : 0; + + if (in & IN_DEBUG1) + { + auto t = body_->getWorldTransform(); + t.setOrigin(btVector3(0, 0, 5)); + body_->setWorldTransform(t); + } + + float speed = vehicle_->getCurrentSpeedKmHour(); + + float maxsc = .5f; + float minsc = .08f; + float sl = 130.f; + + float steeringClamp = std::max(minsc, (1.f - (std::abs(speed) / sl)) * maxsc); + // steeringClamp = .5f; + + float t_delta = 1.0f / 25.0f; + + if (in & IN_FORWARD) + { + if (speed < -1) + breakingForce = maxBreakingForce; + else + engineForce = maxEngineForce; + } + if (in & IN_BACKWARD) + { + if (speed > 1) + breakingForce = maxBreakingForce; + else + engineForce = -maxEngineForce / 2; + } + + // idle breaking + // if (in & (IN_FORWARD | IN_BACKWARD) == 0) + // { + // breakingForce = maxBreakingForce * 0.5f; + // } + + if (in & IN_LEFT) + { + if (steering_ < steeringClamp) + steering_ += steeringIncrement * t_delta; + } + else + { + if (in & IN_RIGHT) + { + if (steering_ > -steeringClamp) + steering_ -= steeringIncrement * t_delta; + } + else + { + if (steering_ < -steeringIncrement * t_delta) + steering_ += steeringIncrement * t_delta; + else + { + if (steering_ > steeringIncrement * t_delta) + steering_ -= steeringIncrement * t_delta; + else + { + steering_ = 0.0f; + } + } + } + } + + vehicle_->applyEngineForce(engineForce, 2); + vehicle_->applyEngineForce(engineForce, 3); + + vehicle_->setBrake(breakingForce * 0.5, 0); + vehicle_->setBrake(breakingForce * 0.5, 1); + vehicle_->setBrake(breakingForce, 2); + vehicle_->setBrake(breakingForce, 3); + + vehicle_->setSteeringValue(steering_, 0); + vehicle_->setSteeringValue(steering_, 1); +} + +void game::Vehicle::SendUpdateMsg() +{ + const auto& trans = root_.local; + + auto umsg = BeginEntMsg(net::EMSG_UPDATE); + + // write position + umsg.Write(trans.position.x); + umsg.Write(trans.position.y); + umsg.Write(trans.position.z); + + // write angles + glm::vec3 angles = glm::eulerAngles(trans.rotation); + umsg.Write(angles.x); + umsg.Write(angles.y); + umsg.Write(angles.z); + + // TEMP wheels + // TODO: REMOVE + for (size_t i =0; i < vehicle_->getNumWheels(); ++i) + { + auto& wheel = vehicle_->getWheelInfo(i); + vehicle_->updateWheelTransformsWS(wheel); + + Transform trans; + trans.SetBtTransform(wheel.m_worldTransform); + + // write position + umsg.Write(trans.position.x); + umsg.Write(trans.position.y); + umsg.Write(trans.position.z); + + // write angles + glm::vec3 angles = glm::eulerAngles(trans.rotation); + umsg.Write(angles.x); + umsg.Write(angles.y); + umsg.Write(angles.z); + } +} diff --git a/src/game/vehicle.hpp b/src/game/vehicle.hpp index 1da0ed5..0b1804d 100644 --- a/src/game/vehicle.hpp +++ b/src/game/vehicle.hpp @@ -2,17 +2,32 @@ #include "entity.hpp" #include "world.hpp" +#include "controllable.hpp" #include "assets/vehiclemdl.hpp" #include "collision/motionstate.hpp" namespace game { -class Vehicle : public Entity +class Vehicle : public Entity, public Controllable { public: + using Super = Entity; + Vehicle(World& world, std::string model_name); + virtual void Update() override; + virtual void SendInitData(Player& player, net::OutMessage& msg) const override; + + // Controllable + Entity& GetEntity() override { return *this; }; + + virtual ~Vehicle(); + +private: + void ProcessInput(); + void SendUpdateMsg(); + private: std::string model_name_; std::shared_ptr model_; @@ -20,6 +35,8 @@ private: collision::MotionState motion_; std::unique_ptr body_; std::unique_ptr vehicle_; + + float steering_ = 0.0f; }; } \ No newline at end of file diff --git a/src/game/world.cpp b/src/game/world.cpp index 1d31152..90f3514 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -35,10 +35,25 @@ void game::World::RegisterEntity(std::unique_ptr ent) void game::World::Update(int64_t delta_time) { time_ms_ += delta_time; - GetBtWorld().stepSimulation(static_cast(delta_time) * 1000.0f); + GetBtWorld().stepSimulation(static_cast(delta_time) * 1000.0f, 2, 1.0f / 100.0f); - for (auto& [entnum, ent] : ents_) + // update entities + for (auto it = ents_.begin(); it != ents_.end();) { - ent->Update(); + it->second->Update(); + + if (it->second->IsRemoved()) + it = ents_.erase(it); + else + ++it; } } + +game::Entity* game::World::GetEntity(net::EntNum entnum) +{ + auto it = ents_.find(entnum); + if (it == ents_.end()) + return nullptr; + + return it->second.get(); +} diff --git a/src/game/world.hpp b/src/game/world.hpp index 0e4f63e..31c0aa3 100644 --- a/src/game/world.hpp +++ b/src/game/world.hpp @@ -14,6 +14,7 @@ class World : public collision::DynamicsWorld { public: World(std::string mapname); + DELETE_COPY_MOVE(World) // spawn entity of type T template T, typename... TArgs> @@ -30,10 +31,18 @@ public: void Update(int64_t delta_time); + // events + virtual void PlayerJoined(Player& player) {} + virtual void PlayerLeft(Player& player) {} + + Entity* GetEntity(net::EntNum entnum); + const std::string& GetMapName() const { return mapname_; } const std::map>& GetEntities() const { return ents_; } int64_t GetTime() const { return time_ms_; } + virtual ~World() = default; + private: std::string mapname_; std::map> ents_; diff --git a/src/gameview/client_session.cpp b/src/gameview/client_session.cpp index dbe30a0..76be15b 100644 --- a/src/gameview/client_session.cpp +++ b/src/gameview/client_session.cpp @@ -1,5 +1,7 @@ #include "client_session.hpp" +#include + game::view::ClientSession::ClientSession(App& app) : app_(app) {} bool game::view::ClientSession::ProcessMessage(net::InMessage& msg) @@ -19,6 +21,8 @@ bool game::view::ClientSession::ProcessMessage(net::InMessage& msg) bool game::view::ClientSession::ProcessSingleMessage(net::MessageType type, net::InMessage& msg) { + MSGDEBUG(std::cout << "[MSG] received " << (uint32_t)type << std::endl;) + switch (type) { case net::MSG_CHWORLD: diff --git a/src/gameview/client_session.hpp b/src/gameview/client_session.hpp index 8432a7f..257d8dc 100644 --- a/src/gameview/client_session.hpp +++ b/src/gameview/client_session.hpp @@ -4,11 +4,12 @@ #include "worldview.hpp" -#include "client/app.hpp" #include "gfx/draw_list.hpp" #include "net/defs.hpp" #include "net/inmessage.hpp" +class App; + namespace game::view { diff --git a/src/gameview/entityview.hpp b/src/gameview/entityview.hpp index dd01333..79f8664 100644 --- a/src/gameview/entityview.hpp +++ b/src/gameview/entityview.hpp @@ -8,24 +8,27 @@ #include "net/defs.hpp" #include "net/inmessage.hpp" -class World; +#include "utils/defs.hpp" namespace game::view { +class WorldView; + class EntityView { public: - EntityView(World& world) : world_(world) {} - - virtual bool ProcessMsg( net::InMessage& msg) { return false; } + EntityView(WorldView& world) : world_(world) {} + DELETE_COPY_MOVE(EntityView) + virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) { return false; } virtual void Update() {} - virtual void Draw(gfx::DrawList& dlist) {} + virtual ~EntityView() = default; + protected: - World& world_; + WorldView& world_; TransformNode root_; bool visible_ = false; diff --git a/src/gameview/vehicleview.cpp b/src/gameview/vehicleview.cpp new file mode 100644 index 0000000..07122ae --- /dev/null +++ b/src/gameview/vehicleview.cpp @@ -0,0 +1,95 @@ +#include "vehicleview.hpp" + +#include "assets/cache.hpp" + +#include + +game::view::VehicleView::VehicleView(WorldView& world, std::shared_ptr model) + : EntityView(world), model_(std::move(model)) +{ +} + +std::unique_ptr game::view::VehicleView::InitFromMsg(WorldView& world, net::InMessage& msg) +{ + net::ModelName modelname; + if (!msg.Read(modelname)) + return nullptr; + + auto model = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh"); + + return std::make_unique(world, std::move(model)); +} + +bool game::view::VehicleView::ProcessMsg(net::EntMsgType type, net::InMessage& msg) +{ + switch (type) + { + case net::EMSG_UPDATE: + return ProcessUpdateMsg(msg); + + default: + return false; + } +} + +void game::view::VehicleView::Update() {} + +void game::view::VehicleView::Draw(gfx::DrawList& dlist) +{ + root_.UpdateMatrix(); + + // TOOD: chceck and fix + const auto& model = *model_->GetModel(); + const auto& mesh = *model.GetMesh(); + + for (const auto& surface : mesh.surfaces) + { + gfx::DrawSurfaceCmd cmd; + cmd.surface = &surface; + cmd.matrices = &root_.matrix; + dlist.AddSurface(cmd); + } + + const auto& wheels = model_->GetWheels(); + for (size_t i = 0; i < 4; ++i) + { + wheels_[i].UpdateMatrix(); + + const auto& mesh = *wheels[i].model->GetMesh(); + + for (const auto& surface : mesh.surfaces) + { + gfx::DrawSurfaceCmd cmd; + cmd.surface = &surface; + cmd.matrices = &wheels_[i].matrix; + dlist.AddSurface(cmd); + } + } +} + +bool game::view::VehicleView::ProcessUpdateMsg(net::InMessage& msg) +{ + auto& trans = root_.local; + glm::vec3 angles; + + if (!msg.Read(trans.position.x) || !msg.Read(trans.position.y) || + !msg.Read(trans.position.z) || !msg.Read(angles.x) || + !msg.Read(angles.y) || !msg.Read(angles.z)) + return false; + + trans.rotation = glm::quat(angles); + + for (size_t i = 0; i < 4; ++i) + { + auto& trans = wheels_[i].local; + glm::vec3 angles; + + if (!msg.Read(trans.position.x) || !msg.Read(trans.position.y) || + !msg.Read(trans.position.z) || !msg.Read(angles.x) || + !msg.Read(angles.y) || !msg.Read(angles.z)) + return false; + + trans.rotation = glm::quat(angles); + } + +} diff --git a/src/gameview/vehicleview.hpp b/src/gameview/vehicleview.hpp new file mode 100644 index 0000000..1084405 --- /dev/null +++ b/src/gameview/vehicleview.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "entityview.hpp" + +#include "assets/vehiclemdl.hpp" + +#include + +namespace game::view +{ + +class VehicleView : public EntityView +{ +public: + VehicleView(WorldView& world, std::shared_ptr model); + static std::unique_ptr InitFromMsg(WorldView& world, net::InMessage& msg); + + virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) override; + virtual void Update() override; + virtual void Draw(gfx::DrawList& dlist) override; + +private: + bool ProcessUpdateMsg(net::InMessage& msg); + +private: + std::shared_ptr model_; + + TransformNode wheels_[4]; + +}; + +} \ No newline at end of file diff --git a/src/gameview/worldview.cpp b/src/gameview/worldview.cpp index 47c42c1..db71c59 100644 --- a/src/gameview/worldview.cpp +++ b/src/gameview/worldview.cpp @@ -2,6 +2,8 @@ #include "assets/cache.hpp" +#include "vehicleview.hpp" + game::view::WorldView::WorldView() { map_ = assets::CacheManager::GetMap("data/openworld.map"); @@ -29,6 +31,11 @@ void game::view::WorldView::Draw(gfx::DrawList& dlist) const { if (map_) map_->Draw(dlist); + + for (const auto& [entnum, ent] : ents_) + { + ent->Draw(dlist); + } } bool game::view::WorldView::ProcessEntSpawnMsg(net::InMessage& msg) @@ -45,17 +52,37 @@ bool game::view::WorldView::ProcessEntSpawnMsg(net::InMessage& msg) switch (type) { + case net::ET_VEHICLE: + entslot = VehicleView::InitFromMsg(*this, msg); + break; default: return false; } + if (!entslot) // init failed + { + ents_.erase(entnum); + return false; + } + return true; } bool game::view::WorldView::ProcessEntMsgMsg(net::InMessage& msg) { - return false; + net::EntNum entnum; + net::EntMsgType type; + + if (!msg.Read(entnum) || !msg.Read(type)) + return false; + + auto ent_it = ents_.find(entnum); + + if (ent_it == ents_.end()) + return false; + + return ent_it->second->ProcessMsg(type, msg); } bool game::view::WorldView::ProcessEntDestroyMsg(net::InMessage& msg) diff --git a/src/net/defs.hpp b/src/net/defs.hpp index e85b5f4..aa2a494 100644 --- a/src/net/defs.hpp +++ b/src/net/defs.hpp @@ -38,6 +38,7 @@ enum MessageType : uint8_t using PlayerName = FixedStr<24>; using MapName = FixedStr<32>; +using ModelName = FixedStr<64>; // pi approx fraction constexpr long long PI_N = 245850922; @@ -65,4 +66,7 @@ enum EntMsgType : uint8_t EMSG_UPDATE, }; +using PositionQ = Quantized; +using AngleQ = Quantized; + } // namespace net \ No newline at end of file diff --git a/src/net/fixed_str.hpp b/src/net/fixed_str.hpp index 102b73d..7a615c4 100644 --- a/src/net/fixed_str.hpp +++ b/src/net/fixed_str.hpp @@ -26,6 +26,8 @@ struct FixedStr size_t putsize = std::min(N, stdstr.size()); len = putsize; memcpy(str, stdstr.data(), putsize); + + return *this; } size_t MaxLen() const { return N; } diff --git a/src/net/inmessage.hpp b/src/net/inmessage.hpp index 22b518b..17dc251 100644 --- a/src/net/inmessage.hpp +++ b/src/net/inmessage.hpp @@ -57,7 +57,7 @@ public: if (!Read(und)) return false; - value = und; + value = static_cast(und); return true; } @@ -73,14 +73,27 @@ public: return true; } - template - bool Read(Quantized& quant) + template + bool Read(T& quant) { - T value; - if (!Read(value)) + return Read(quant.value); + + //T value; + //if (!Read(value)) + // return false; + + //quant.value = value; + //return true; + } + + template + bool Read(float& f) + { + T q; + if (!Read(q)) return false; - quant.value = value; + f = q.Decode(); return true; } diff --git a/src/net/outmessage.hpp b/src/net/outmessage.hpp index 9edbf3f..4082414 100644 --- a/src/net/outmessage.hpp +++ b/src/net/outmessage.hpp @@ -13,7 +13,7 @@ namespace net class OutMessage { public: - OutMessage(std::vector& buffer) : buffer_(buffer) { buffer.clear(); } + OutMessage(std::vector& buffer) : buffer_(buffer) { } template size_t Reserve() @@ -43,6 +43,9 @@ public: void Write(const char* str, size_t n) { + if (n == 0) + return; + size_t pos = buffer_.size(); buffer_.resize(pos + n); memcpy(&buffer_[pos], str, n); @@ -60,10 +63,18 @@ public: Write(str.str, str.len); } - template - void Write(Quantized quant) + // template + // void Write(Quantized quant) + // { + // Write(quant.value); + // } + + template + void Write(float f) { - Write(quant.value); + T q; + q.Encode(f); + Write(q.value); } void WriteVarInt(int64_t value) @@ -92,4 +103,4 @@ private: std::vector& buffer_; }; -} // namespace net \ No newline at end of file +} // namespace net diff --git a/src/net/quantized.hpp b/src/net/quantized.hpp index 976499e..01418a1 100644 --- a/src/net/quantized.hpp +++ b/src/net/quantized.hpp @@ -21,6 +21,7 @@ struct Quantized public: T value; + Quantized() = default; Quantized(T value) : value(value) {} Quantized(float fvalue) { Encode(value); } @@ -37,4 +38,14 @@ public: static constexpr float MaxError() noexcept { return inv_scale * 0.5f; } }; +template +concept AnyQuantized = requires(T t, float f) +{ + { T(f) }; + { t.Encode(f) }; + { t.Decode() } -> std::convertible_to; + // { t.value } -> std::unsigned_integral; + { t.value }; +}; + } // namespace net \ No newline at end of file diff --git a/src/server/client.cpp b/src/server/client.cpp index 0f54422..d75e0c5 100644 --- a/src/server/client.cpp +++ b/src/server/client.cpp @@ -11,7 +11,7 @@ bool sv::Client::ProcessMessage(net::InMessage& msg) { net::MessageType type = net::MSG_NONE; if (!msg.Read(type)) - break; + return true; if (type == net::MSG_NONE || type >= net::MSG_COUNT) return false; diff --git a/src/server/client.hpp b/src/server/client.hpp index 28c3962..e4e158d 100644 --- a/src/server/client.hpp +++ b/src/server/client.hpp @@ -1,6 +1,7 @@ #pragma once #include "wsserver.hpp" +#include "net/defs.hpp" #include "net/inmessage.hpp" #include "game/player.hpp" diff --git a/src/server/server.cpp b/src/server/server.cpp index 8e5f749..4ab354f 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -1,13 +1,52 @@ #include "server.hpp" +#include +#include + +#include + +#ifdef _WIN32 +#define NOMINMAX +#include +#pragma comment(lib, "winmm.lib") +#endif + sv::Server::Server(uint16_t port) : ws_(port) {} void sv::Server::Run() { + using namespace std::chrono_literals; + + auto t_start = std::chrono::steady_clock::now(); + auto t_next = t_start; + auto t_prev = t_start; + +#ifdef _WIN32 + timeBeginPeriod(1); +#endif + bool exit = false; while (!exit) { + time_ += 40; + PollWSEvents(); + Update(); + + t_next += 40ms; + + auto t_now = std::chrono::steady_clock::now(); + + while (t_now < t_next) + { + std::this_thread::sleep_for(t_next - t_now); + t_now = std::chrono::steady_clock::now(); + } + + auto t_diff = t_now - t_prev; + t_prev = t_now; + + std::cout << std::chrono::duration_cast(t_diff).count() <(); + clients_[conn] = std::make_unique(*this, conn); } void sv::Server::HandleWSMessage(WSConnId conn, const std::string& data) @@ -67,7 +106,7 @@ void sv::Server::HandleWSDisconnect(WSConnId conn) void sv::Server::Update() { // update game - + game_.Update(); // update players for (const auto& [conn, client] : clients_) diff --git a/src/server/server.hpp b/src/server/server.hpp index 52c6402..1a4c689 100644 --- a/src/server/server.hpp +++ b/src/server/server.hpp @@ -23,6 +23,8 @@ public: game::Game& GetGame() { return game_; } + int64_t GetTime() const { return time_; } + private: void PollWSEvents(); void HandleWSConnect(WSConnId conn); @@ -38,6 +40,7 @@ private: game::Game game_; std::unordered_map> clients_; + int64_t time_ = 0; }; diff --git a/src/utils/allocnum.hpp b/src/utils/allocnum.hpp index e775bd8..2b50676 100644 --- a/src/utils/allocnum.hpp +++ b/src/utils/allocnum.hpp @@ -9,14 +9,14 @@ template TNum AllocNum(const TMap& map, TNum& num) { constexpr auto MAX_NUM = std::numeric_limits().max(); - if (ents_.size() >= MAX_NUM - 2) // 0 & MAX reserved + if (map.size() >= MAX_NUM - 2) // 0 & MAX reserved return 0; // this is stupid but whatever do { ++num; - } while (num == 0 || num == MAX_ENTNUM || ents_.find(num) != ents_.end()); + } while (num == 0 || num == MAX_NUM || map.find(num) != map.end()); return num; } diff --git a/src/utils/defs.hpp b/src/utils/defs.hpp index 045ef10..e54bfb9 100644 --- a/src/utils/defs.hpp +++ b/src/utils/defs.hpp @@ -11,3 +11,13 @@ #else #define CLIENT_ONLY(...) #endif + +//#define MSGDEBUG(...) __VA_ARGS__ +#define MSGDEBUG(...) + +#define DELETE_COPY_MOVE(classname) \ + classname(const classname& other) = delete; \ + classname(classname&& other) = delete; \ + classname& operator=(const classname& other) = delete; \ + classname& operator=(classname&& other) = delete; + diff --git a/src/utils/files_server.cpp b/src/utils/files_server.cpp new file mode 100644 index 0000000..61bc523 --- /dev/null +++ b/src/utils/files_server.cpp @@ -0,0 +1,20 @@ +#include "files.hpp" + +#include + +std::string fs::ReadFileAsString(const std::string& path) +{ + std::ifstream t(path, std::ios::binary); + t.seekg(0, std::ios::end); + size_t size = t.tellg(); + std::string buffer(size, ' '); + t.seekg(0); + t.read(&buffer[0], size); + return buffer; +} + +std::istringstream fs::ReadFileAsStream(const std::string& path) +{ + std::string content = ReadFileAsString(path); + return std::istringstream(content); +}