diff --git a/.gitmodules b/.gitmodules index c319e98..952393c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "deps/libpqxx"] path = deps/libpqxx url = https://github.com/jtv/libpqxx +[submodule "deps/cpp-httplib"] + path = deps/cpp-httplib + url = https://github.com/yhirose/cpp-httplib diff --git a/CMakeLists.txt b/CMakeLists.txt index 928f804..b76a67f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,13 @@ add_subdirectory(${DIRECTORY_PQXX}) message(STATUS "PostgreSQL include: ${INCLUDE_PQXX}") message(STATUS "PostgreSQL library: ${LIBRARY_PQXX}") +# HTTP library +set(DIRECTORY_HTTP ${PROJECT_SOURCE_DIR}/deps/cpp-httplib) +set(INCLUDE_HTTP ${DIRECTORY_HTTP}) + +add_subdirectory(${DIRECTORY_HTTP}) +message(STATUS "HTTP include: ${INCLUDE_HTTP}") + # Core library set(DIRECTORY_CORE ${PROJECT_SOURCE_DIR}/core) set(INCLUDE_CORE ${DIRECTORY_CORE}) @@ -100,11 +107,21 @@ set(DIRECTORY_GAME ${PROJECT_SOURCE_DIR}/game) set(EXECUTABLE_GAME example) set(EXECUTABLE_GAME_RUNNER game_runner_example) set(EXECUTABLE_GAME_DATABASE game_database_example) +set(EXECUTABLE_LOADER_FROM_SERVER game_loader_from_server_example) add_subdirectory(${DIRECTORY_GAME}) message(STATUS "Game executable: ${EXECUTABLE_GAME}") message(STATUS "Game-runner executable ${EXECUTABLE_GAME_RUNNER}") +# Network binaries +set(DIRECTORY_NETWORK ${PROJECT_SOURCE_DIR}/network) +set(EXECUTABLE_SIMPLE_SERVER simple-server) +set(EXECUTABLE_SIMPLE_CLIENT simple-client) + +add_subdirectory(${DIRECTORY_NETWORK}) +message(STATUS "Server executable: ${EXECUTABLE_SERVER}") +message(STATUS "Client executable ${EXECUTABLE_GAME_CLIENT}") + # Tests set(DIRECTORY_TEST ${PROJECT_SOURCE_DIR}/test) set(EXECUTABLE_TEST test) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index f0c512f..a3599f0 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -78,17 +78,22 @@ set( runner/GameRunner.hpp runner/GameRunner.cpp loader/LevelLoader.hpp loader/LevelLoaderFromFile.cpp loader/LevelLoaderFromFile.hpp + loader/LevelLoaderFromServer.hpp loader/LevelLoaderFromServer.cpp visual/image/Image.hpp visual/image/static/RenderableStatic.cpp visual/image/static/RenderableStatic.hpp visual/image/shape/square/RenderableSquare.cpp visual/image/shape/square/RenderableSquare.hpp visual/image/animated/RenderableAnimatedOneFile.cpp visual/image/animated/RenderableAnimatedOneFile.hpp visual/image/animated/RenderableAnimatedSeveralFiles.cpp visual/image/animated/RenderableAnimatedSeveralFiles.hpp + visual/image/background/RenderableBackground.cpp visual/image/background/RenderableBackground.hpp + visual/image/shape/Shape.cpp visual/image/shape/Shape.hpp visual/image/shape/square/Square.cpp visual/image/shape/square/Square.hpp visual/image/static/StaticImage.cpp visual/image/static/StaticImage.hpp visual/image/animated/AnimatedImageOneFile.cpp visual/image/animated/AnimatedImageOneFile.hpp visual/image/animated/AnimatedImageSeveralFiles.cpp visual/image/animated/AnimatedImageSeveralFiles.hpp + visual/image/background/BackgroundImage.cpp visual/image/background/BackgroundImage.hpp + visual/image/storage/ImageStorage.cpp visual/image/storage/ImageStorage.hpp visual/Renderable.hpp visual/Camera.cpp visual/Camera.hpp diff --git a/core/database/database/Database.cpp b/core/database/database/Database.cpp index 98eb45c..8009717 100644 --- a/core/database/database/Database.cpp +++ b/core/database/database/Database.cpp @@ -25,6 +25,11 @@ namespace mad::core { "levels_completed SMALLINT NOT NULL);"; w.exec(m_query); + m_query = "CREATE TABLE IF NOT EXISTS levels(" + "id SERIAL PRIMARY KEY," + "name TEXT NOT NULL UNIQUE);"; + w.exec(m_query); + w.commit(); SPDLOG_DEBUG("Tables created successfully"); @@ -40,10 +45,17 @@ namespace mad::core { return !rows_found.empty(); } + bool Database::is_level_exists(const std::string &levelname) { + pqxx::work W(m_connector); + m_query = "SELECT * FROM levels WHERE name = '" + W.esc(levelname) + "';"; + pqxx::result rows_found = W.exec(m_query); + return !rows_found.empty(); + } + void Database::registry_user(const std::string &username) { pqxx::work W(m_connector); - m_query = "SELECT id FROM users"; + m_query = "SELECT id FROM users;"; pqxx::result total_rows = W.exec(m_query); std::size_t id = total_rows.size(); @@ -56,6 +68,15 @@ namespace mad::core { W.commit(); } + void Database::append_level(const std::string &levelname) { + pqxx::work W(m_connector); + + m_query = "INSERT INTO levels(name) VALUES('" + levelname + "');"; + W.exec(m_query); + + W.commit(); + } + std::size_t Database::get_id(const std::string &username) { pqxx::work W(m_connector); m_query = "SELECT * FROM users WHERE name = '" + W.esc(username) + "';"; @@ -99,4 +120,18 @@ namespace mad::core { void Database::reset_progress(const std::string &username) { reset_progress(get_id(username)); } + + std::string Database::get_levelname(std::size_t id) { + pqxx::work W(m_connector); + m_query = "SELECT * FROM levels WHERE id = " + std::to_string(id) + ";"; + auto found_row = W.exec1(m_query); + return found_row["name"].as(); + } + + std::size_t Database::get_levels_total() { + pqxx::work W(m_connector); + m_query = "SELECT id FROM levels;"; + auto total_rows = W.exec(m_query); + return total_rows.size(); + } } diff --git a/core/database/database/Database.hpp b/core/database/database/Database.hpp index 5db0168..d1fb612 100644 --- a/core/database/database/Database.hpp +++ b/core/database/database/Database.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace mad::core { @@ -13,13 +14,21 @@ namespace mad::core { bool is_user_exists(const std::string &username); - void registry_user(const std::string &username); + bool is_level_exists(const std::string &levelname); std::size_t get_id(const std::string &username); std::size_t get_progress(std::size_t id); std::size_t get_progress(const std::string &username); + std::string get_levelname(std::size_t id); + + std::size_t get_levels_total(); + + void registry_user(const std::string &username); + + void append_level(const std::string &levelname); + void increment_progress(std::size_t id); void increment_progress(const std::string &username); diff --git a/core/loader/LevelLoaderFromFile.cpp b/core/loader/LevelLoaderFromFile.cpp index 2201916..460f637 100644 --- a/core/loader/LevelLoaderFromFile.cpp +++ b/core/loader/LevelLoaderFromFile.cpp @@ -1,10 +1,7 @@ #include "LevelLoaderFromFile.hpp" #include -#include "event/management/condition/KeyDownCondition.hpp" -#include "event/management/condition/KeyPressedCondition.hpp" -#include "event/management/condition/TimerCondition.hpp" -#include "event/management/condition/TrueCondition.hpp" -#include "event/management/controller/statemachine/StateMachine.hpp" +#include +#include #include @@ -25,7 +22,7 @@ namespace mad::core { Vec2d camera_position = {m_config_json["camera"]["position"]["x"], m_config_json["camera"]["position"]["y"]}; - auto camera = std::make_shared(camera_position, world, true); + auto camera = std::make_shared(camera_position, world); auto keys = create_world(world); @@ -82,6 +79,9 @@ namespace mad::core { float current_position_y = object_size / 2; std::unordered_map keys; std::string map_line; + + create_background(world); + while (std::getline(m_level_map, map_line)) { for (char object: map_line) { switch (m_objects[object]) { @@ -133,7 +133,7 @@ namespace mad::core { void LevelLoaderFromFile::create_block(std::shared_ptr world, Vec2d position, float block_size, bool is_stable) { - std::filesystem::path source("../../game/resources/static/"); + std::filesystem::path source("../../resources/static/"); if (is_stable) { source /= static_cast(m_config_json["texture"]["stable"]); } else { @@ -203,7 +203,7 @@ namespace mad::core { } Entity::Id LevelLoaderFromFile::create_finish_block(std::shared_ptr world, Vec2d position, float block_size) { - std::filesystem::path source("../../game/resources/static/"); + std::filesystem::path source("../../resources/static/"); source /= static_cast(m_config_json["texture"]["finish"]); auto image_storage = std::make_shared( @@ -223,4 +223,29 @@ namespace mad::core { ); } + void LevelLoaderFromFile::create_background(std::shared_ptr world) { + std::filesystem::path source(m_config_json["background"]["source"]); + + std::shared_ptr image_storage; + std::vector parallax_ratios = m_config_json["background"]["parallax_ratios"]; + + image_storage = std::make_shared( + std::unordered_map>( + {{ImageStorage::TypeAction::Idle, + std::make_shared( + source, + parallax_ratios, + m_config_json["background"]["scale"] + ) + }} + ) + ); + world->create_viewable_entity( + -1, + {0, 0}, + 0, + image_storage + ); + } + } \ No newline at end of file diff --git a/core/loader/LevelLoaderFromFile.hpp b/core/loader/LevelLoaderFromFile.hpp index feb405e..4994a81 100644 --- a/core/loader/LevelLoaderFromFile.hpp +++ b/core/loader/LevelLoaderFromFile.hpp @@ -106,6 +106,8 @@ namespace mad::core { Entity::Id create_hero(std::shared_ptr world, Vec2d position); + void create_background(std::shared_ptr world); + Entity::Id create_finish_block(std::shared_ptr world, Vec2d position, float block_size); private: @@ -116,7 +118,7 @@ namespace mad::core { Hero, Enemy1, Enemy2, - Empty, + Empty }; std::filesystem::path m_level_directory; diff --git a/core/loader/LevelLoaderFromServer.cpp b/core/loader/LevelLoaderFromServer.cpp new file mode 100644 index 0000000..7b143c1 --- /dev/null +++ b/core/loader/LevelLoaderFromServer.cpp @@ -0,0 +1,247 @@ +#include "LevelLoaderFromServer.hpp" + +#include +#include +#include + +#include + +namespace mad::core { + + LevelLoaderFromServer::LevelLoaderFromServer(std::string map, json config) : m_level_map(std::move(map)), m_config_json(std::move(config)) { } + + std::unique_ptr LevelLoaderFromServer::load(std::shared_ptr global_dispatcher, + std::shared_ptr system_listener) { + auto level_dispatcher = std::make_shared(); + + auto world = std::make_shared(*level_dispatcher); + + Vec2d camera_position = {m_config_json["camera"]["position"]["x"], + m_config_json["camera"]["position"]["y"]}; + auto camera = std::make_shared(camera_position, world); + + auto keys = create_world(world); + + camera->turn_on(*level_dispatcher, keys[LevelLoaderFromServer::IdKeys::Hero]); + level_dispatcher->registry(camera); + level_dispatcher->registry(std::make_shared(world, keys[LevelLoaderFromServer::IdKeys::Hero])); + + /* std::vector> controllers { + std::make_shared(camera) + };*/ + + ///State Machine + struct C1 : mad::core::Controller { + void control() override { + //SPDLOG_DEBUG("controller 1"); + }; + }; + struct C2 : mad::core::Controller { + void control() override { + //SPDLOG_DEBUG("controller 2"); + }; + }; + auto machine = std::make_shared( + std::shared_ptr(level_dispatcher)); + machine->add_state(std::make_shared()); + machine->add_state(std::make_shared()); + machine->add_transition(0, 1, std::make_shared(1)); + machine->add_transition(1, 0, std::make_shared(2)); + machine->set_initial_state(0); + std::vector> controllers{machine, + std::make_shared( + camera)}; + + auto level_runner = std::make_unique( + system_listener, + std::make_unique(), + camera, + global_dispatcher, + level_dispatcher, + world, + controllers + ); + + level_dispatcher->registry(std::make_shared(*level_runner, std::make_shared(keys[LevelLoaderFromServer::IdKeys::Hero], keys[LevelLoaderFromServer::IdKeys::FinishBlock]))); + level_dispatcher->registry(std::make_shared(*level_runner)); + + return level_runner; + } + + std::unordered_map LevelLoaderFromServer::create_world(std::shared_ptr world) { + float object_size = m_config_json["block"]; + float current_position_x = object_size / 2; + float current_position_y = object_size / 2; + std::size_t width = m_config_json["width"]; + std::unordered_map keys; + + create_background(world); + + for (std::size_t i = 0; i < m_level_map.size();) { + for (std::size_t j = 0; j < width; ++j, ++i) { + char object = m_level_map[i]; + switch (m_objects[object]) { + case Objects::UnstableBlock: { + create_block(world, + {current_position_x, + current_position_y}, + object_size, false); + break; + } + case Objects::StableBlock: { + create_block(world, + {current_position_x, + current_position_y}, + object_size, true); + break; + } + case Objects::FinishBlock: { + keys[LevelLoaderFromServer::IdKeys::FinishBlock] = create_finish_block( + world, + {current_position_x, current_position_y}, + object_size); + break; + } + case Objects::Hero: { + keys[LevelLoaderFromServer::IdKeys::Hero] = create_hero(world, + {current_position_x, + current_position_y}); + break; + } + case Objects::Enemy1: { + break; + } + case Objects::Enemy2: { + break; + } + case Objects::Empty: { + break; + } + } + current_position_x += object_size; + } + current_position_y += object_size; + current_position_x = object_size / 2; + } + return keys; + } + + void LevelLoaderFromServer::create_block(std::shared_ptr world, + Vec2d position, float block_size, bool is_stable) { + + std::filesystem::path source("../../resources/static/"); + if (is_stable) { + source /= static_cast(m_config_json["texture"]["stable"]); + } else { + source /= static_cast(m_config_json["texture"]["unstable"]); + } + + auto image_storage = std::make_shared( + std::unordered_map>( + {{ImageStorage::TypeAction::Idle, + std::make_shared(source, block_size, + block_size, + StaticImage::TransformType::Tile) + }})); + + Entity::Id square_id = world->create_physical_entity( + 0, + position, + 0, + image_storage, + is_stable + ); + } + + Entity::Id LevelLoaderFromServer::create_hero(std::shared_ptr world, Vec2d position) { + std::filesystem::path source(m_config_json["animated_resources"]); + source /= m_config_json["hero"]["source"]; + + Entity::Id hero_id = 0; + + std::shared_ptr image_storage; + + float physical_size_width = m_config_json["hero"]["animated"]["size_width"]; + float physical_size_height = m_config_json["hero"]["animated"]["size_height"]; + float size_scale = m_config_json["hero"]["animated"]["size_scale"]; + float delta_x = m_config_json["hero"]["animated"]["delta_x"]; + float delta_y = m_config_json["hero"]["animated"]["delta_y"]; + + + image_storage = std::make_shared( + std::unordered_map>( + {{ImageStorage::TypeAction::Idle, + std::make_shared( + source / m_config_json["hero"]["animated"]["actions"]["idle"]["source"], + m_config_json["hero"]["animated"]["actions"]["idle"]["delta_time"], + physical_size_width, physical_size_height, size_scale, + delta_x, delta_y) + }, + {ImageStorage::TypeAction::Run, + std::make_shared( + source / m_config_json["hero"]["animated"]["actions"]["run"]["source"], + m_config_json["hero"]["animated"]["actions"]["run"]["delta_time"], + physical_size_width, physical_size_height, size_scale, + delta_x, delta_y) + }} + ) + ); + + hero_id = world->create_physical_entity( + 0, + position, + 0, + image_storage, + false, false + ); + + return hero_id; + } + + Entity::Id LevelLoaderFromServer::create_finish_block(std::shared_ptr world, Vec2d position, float block_size) { + std::filesystem::path source("../../resources/static/"); + source /= static_cast(m_config_json["texture"]["finish"]); + + auto image_storage = std::make_shared( + std::unordered_map>( + {{ImageStorage::TypeAction::Idle, + std::make_shared(source, block_size, + block_size, + StaticImage::TransformType::Fit) + }})); + + return world->create_physical_entity( + 0, + position, + 0, + image_storage, + true + ); + } + + void LevelLoaderFromServer::create_background(std::shared_ptr world) { + std::filesystem::path source(m_config_json["background"]["source"]); + + std::shared_ptr image_storage; + std::vector parallax_ratios = m_config_json["background"]["parallax_ratios"]; + + image_storage = std::make_shared( + std::unordered_map>( + {{ImageStorage::TypeAction::Idle, + std::make_shared( + source, + parallax_ratios, + m_config_json["background"]["scale"] + ) + }} + ) + ); + world->create_viewable_entity( + -1, + {0, 0}, + 0, + image_storage + ); + } + +} \ No newline at end of file diff --git a/core/loader/LevelLoaderFromServer.hpp b/core/loader/LevelLoaderFromServer.hpp new file mode 100644 index 0000000..04e310a --- /dev/null +++ b/core/loader/LevelLoaderFromServer.hpp @@ -0,0 +1,142 @@ +#ifndef MAD_LEVELLOADERFROMSERVER_HPP +#define MAD_LEVELLOADERFROMSERVER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +using json = nlohmann::json; + + +namespace mad::core { + + class ArrowController : public mad::core::EventHandler { + public: + explicit ArrowController(std::shared_ptr world, + mad::core::Entity::Id entity_id) + : m_world(std::move(world)), + m_entity_id(entity_id) {} + + void handle(const mad::core::Event &event) override { + //SPDLOG_INFO("handle arrow event"); + + auto make_move_intent = [](mad::core::Vec2d dir) { + return mad::core::LambdaIntent( + [=](mad::core::Entity &entity, mad::core::EventDispatcher &event_dispatcher) { + mad::core::cast_to(entity).move(dir); + }); + }; + + auto impulse = [](mad::core::Vec2d dir) { + return mad::core::LambdaIntent( + [=](mad::core::Entity &entity, mad::core::EventDispatcher &event_dispatcher) { + mad::core::cast_to(entity).apply_linear_impulse_to_center(dir, event_dispatcher); + }); + }; + + auto force = [](mad::core::Vec2d dir) { + return mad::core::LambdaIntent( + [=](mad::core::Entity &entity, mad::core::EventDispatcher &event_dispatcher) { + mad::core::cast_to(entity).apply_force_to_center(dir, event_dispatcher); + }); + }; + + if (event.type == mad::core::Event::Type::KeyPressed) { + const auto &keystroke = mad::core::const_cast_to(event); + if (keystroke.key_id == sf::Keyboard::Key::Space) { + m_world->manipulate_entity_id(m_entity_id, impulse(mad::core::Vec2d{0.0f, -200000.0f})); + } + } else if (event.type == mad::core::Event::Type::KeyHeld) { + const auto &keystroke = mad::core::const_cast_to(event); + if (keystroke.key_id == sf::Keyboard::Key::Right) { + m_world->manipulate_entity_id(m_entity_id, force(mad::core::Vec2d{100000.0f, 0.0f})); + } + if (keystroke.key_id == sf::Keyboard::Key::Left) { + m_world->manipulate_entity_id(m_entity_id, force(mad::core::Vec2d{-100000.0f, 0.0f})); + } + } + } + + std::unordered_set handled_types() override { + return {mad::core::Event::Type::KeyPressed, mad::core::Event::Type::KeyHeld}; + } + + private: + std::shared_ptr m_world; + mad::core::Entity::Id m_entity_id; + }; + + class LevelLoaderFromServer : public LevelLoader { + private: + enum class IdKeys { + Hero, + FinishBlock, + }; + + public: + explicit LevelLoaderFromServer(std::string map, json config); + + std::unique_ptr load( + std::shared_ptr global_dispatcher, + std::shared_ptr system_listener) override; + + std::unordered_map create_world(std::shared_ptr world); + + void create_block(std::shared_ptr world, Vec2d position, + float block_size, bool is_stable); + + Entity::Id create_hero(std::shared_ptr world, Vec2d position); + + void create_background(std::shared_ptr world); + + Entity::Id create_finish_block(std::shared_ptr world, Vec2d position, float block_size); + + private: + enum class Objects { + UnstableBlock, + StableBlock, + FinishBlock, + Hero, + Enemy1, + Enemy2, + Empty + }; + + json m_config_json; + + std::string m_level_map; + + std::unordered_map m_objects = { + {'.', Objects::Empty}, + {'#', Objects::StableBlock}, + {'@', Objects::UnstableBlock}, + {'F', Objects::FinishBlock}, + {'H', Objects::Hero}, + {'Z', Objects::Enemy1}, + {'E', Objects::Enemy2} + }; + + }; + +} + +#endif//MAD_LEVELLOADERFROMSERVER_HPP diff --git a/core/menu/PauseMenu.cpp b/core/menu/PauseMenu.cpp index f9b2c3a..f34ee62 100644 --- a/core/menu/PauseMenu.cpp +++ b/core/menu/PauseMenu.cpp @@ -23,6 +23,7 @@ namespace mad::core { } ImGui::End(); ImGui::SFML::Render(window); + return true; } } diff --git a/core/visual/Camera.cpp b/core/visual/Camera.cpp index 76173c7..e7a4e1d 100644 --- a/core/visual/Camera.cpp +++ b/core/visual/Camera.cpp @@ -93,6 +93,20 @@ namespace mad::core { }); break; } + case Image::Type::Background: { + std::shared_ptr background_image = + pointer_cast_to(image); + RenderableBackground renderable_background(background_image, + m_position, + positional_appearance.get_rotation()); + + insert_renderable_to_scene({positional_appearance.get_z_index(), + RenderableWithId(std::make_shared(renderable_background), + positional_appearance.get_entity_id()) + }); + + break; + } } } } @@ -102,7 +116,7 @@ namespace mad::core { } Camera::Camera(Vec2d initial_position, std::shared_ptr world, bool is_debug_mode) - : m_position(initial_position), + : m_position(std::make_shared(initial_position)), m_world(std::move(world)), m_view(initial_position, {640, 480}), m_is_debug_mode(is_debug_mode) { @@ -114,20 +128,20 @@ namespace mad::core { Vec2d position = entity.get_image_position(); if (!m_last_position.has_value()) { m_last_position = position; - m_position = position; - m_view.setCenter(m_position); + *m_position = position; + m_view.setCenter(*m_position); } switch (m_type) { case FollowType::Forward: { - float move_x = (position.get_x() - m_position.get_x()) * (2 - m_smoothness); - float move_y = (position - m_position).get_y(); + float move_x = (position.get_x() - m_position->get_x()) * (2 - m_smoothness); + float move_y = (position - *m_position).get_y(); m_view.move(move_x, move_y); - m_position += {move_x, move_y}; + *m_position += {move_x, move_y}; break; } case FollowType::Backward : { - m_view.move((position - m_position) * m_smoothness); - m_position += (position - m_position) * m_smoothness; + m_view.move((position - *m_position) * m_smoothness); + *m_position += (position - *m_position) * m_smoothness; break; } } @@ -140,8 +154,8 @@ namespace mad::core { } void Camera::set_position(const Vec2d &position) { - m_position = position; - m_view.setCenter(m_position); + *m_position = position; + m_view.setCenter(*m_position); } void Camera::set_rotation(float angle) { diff --git a/core/visual/Camera.hpp b/core/visual/Camera.hpp index 28b4b7a..5e5bf3f 100644 --- a/core/visual/Camera.hpp +++ b/core/visual/Camera.hpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -78,7 +80,7 @@ namespace mad::core { sf::View m_view; - Vec2d m_position; + std::shared_ptr m_position; std::optional m_last_position; diff --git a/core/visual/image/Image.hpp b/core/visual/image/Image.hpp index 37c7e02..9a78594 100644 --- a/core/visual/image/Image.hpp +++ b/core/visual/image/Image.hpp @@ -13,7 +13,8 @@ namespace mad::core { Shape, Static, AnimatedOneFile, - AnimatedSeveralFiles + AnimatedSeveralFiles, + Background }; enum class Orientation { diff --git a/core/visual/image/background/BackgroundImage.cpp b/core/visual/image/background/BackgroundImage.cpp new file mode 100644 index 0000000..13e5786 --- /dev/null +++ b/core/visual/image/background/BackgroundImage.cpp @@ -0,0 +1,31 @@ +#include "BackgroundImage.hpp" + +#include + +namespace mad::core { + + + BackgroundImage::BackgroundImage(std::filesystem::path path, std::vector parallax_ratios, float scale) + : Image(Image::Type::Background), m_path(std::move(path)), + m_parallax_ratios(std::move(parallax_ratios)), m_scale(scale) { + } + + std::filesystem::path BackgroundImage::get_path() const noexcept { + return m_path; + } + + std::vector BackgroundImage::get_parallax_ratios() const noexcept { + return m_parallax_ratios; + } + + b2PolygonShape BackgroundImage::as_fixture() { + return {}; + } + + float BackgroundImage::get_scale() const noexcept { + return m_scale; + } + + +} + diff --git a/core/visual/image/background/BackgroundImage.hpp b/core/visual/image/background/BackgroundImage.hpp new file mode 100644 index 0000000..88b5bbb --- /dev/null +++ b/core/visual/image/background/BackgroundImage.hpp @@ -0,0 +1,32 @@ +#ifndef MAD_BACKGROUNDIMAGE_HPP +#define MAD_BACKGROUNDIMAGE_HPP + +#include + +#include +#include + +namespace mad::core { + + class BackgroundImage : public Image { + public: + BackgroundImage(std::filesystem::path path, std::vector parallax_ratios, float scale); + + [[nodiscard]] std::filesystem::path get_path() const noexcept; + + [[nodiscard]] std::vector get_parallax_ratios() const noexcept; + + [[nodiscard]] float get_scale() const noexcept; + + b2PolygonShape as_fixture() override; + private: + std::filesystem::path m_path; + + std::vector m_parallax_ratios; + + float m_scale; + }; + +} + +#endif //MAD_BACKGROUNDIMAGE_HPP diff --git a/core/visual/image/background/RenderableBackground.cpp b/core/visual/image/background/RenderableBackground.cpp new file mode 100644 index 0000000..65c91e5 --- /dev/null +++ b/core/visual/image/background/RenderableBackground.cpp @@ -0,0 +1,57 @@ +#include "RenderableBackground.hpp" + +#include +#include + +namespace mad::core { + + + RenderableBackground::RenderableBackground(const std::shared_ptr &background, std::shared_ptr position, + std::shared_ptr rotation) : + m_camera_position(std::move(position)), m_rotation(std::move(rotation)), + m_parallax_ratios(background->get_parallax_ratios()), + m_last_camera_position(*m_camera_position), + m_scale({background->get_scale(), background->get_scale()}) { + is_active = background->is_active; + + std::set sorted_files; + + for (const auto &file : std::filesystem::directory_iterator{background->get_path()}) { + sorted_files.insert(file.path()); + } + + for (const auto &layer : sorted_files) { + sf::Texture texture; + CHECK_THROW(texture.loadFromFile(layer), + FileDoesNotExist, "Background file does not exist"); + texture.setRepeated(true); + m_layers.push_back(texture); + } + } + + bool RenderableBackground::render(sf::RenderWindow &window) { + for (int i = 0; i < static_cast(m_layers.size()); i++) { + sf::Sprite background; + background.setTexture(m_layers[i]); + float ratio = m_parallax_ratios[i]; + background.setScale(m_scale); + if (m_layers_positions.size() == i) { + m_layers_positions.push_back(*m_camera_position); + background.setPosition(m_layers_positions[i]); + } else { + background.setPosition(m_layers_positions[i].get_x() + (*m_camera_position - m_last_camera_position).get_x() * ratio, m_camera_position->get_y()); + } + m_layers_positions[i] += {(*m_camera_position - m_last_camera_position).get_x() * ratio, (*m_camera_position - m_layers_positions[i]).get_y()}; + background.setTextureRect({0, 0, static_cast(background.getLocalBounds().width) * 100, static_cast(background.getLocalBounds().height)}); + background.setOrigin(background.getLocalBounds().width / 2, background.getLocalBounds().height / 2); //static_cast(window.getSize().y) / 2); + window.draw(background); + } + m_last_camera_position = *m_camera_position; + return true; + } + + sf::RectangleShape RenderableBackground::get_physical_shape() noexcept { + return sf::RectangleShape(); + } +} + diff --git a/core/visual/image/background/RenderableBackground.hpp b/core/visual/image/background/RenderableBackground.hpp new file mode 100644 index 0000000..df95e9c --- /dev/null +++ b/core/visual/image/background/RenderableBackground.hpp @@ -0,0 +1,41 @@ +#ifndef MAD_RENDERABLEBACKGROUND_HPP +#define MAD_RENDERABLEBACKGROUND_HPP + +#include +#include + +#include + +#include +#include + +namespace mad::core { + + class RenderableBackground : public Renderable { + public: + RenderableBackground(const std::shared_ptr &background, + std::shared_ptr position, std::shared_ptr rotation); + + bool render(sf::RenderWindow &window) override; + + sf::RectangleShape get_physical_shape() noexcept override; + + private: + std::vector m_layers; + + std::shared_ptr m_camera_position; + + std::vector m_layers_positions; + + Vec2d m_last_camera_position; + + std::shared_ptr m_rotation; + + Vec2d m_scale = {1, 1}; + + std::vector m_parallax_ratios; + }; + +} + +#endif //MAD_RENDERABLEBACKGROUND_HPP diff --git a/deps/cpp-httplib b/deps/cpp-httplib new file mode 160000 index 0000000..df20c27 --- /dev/null +++ b/deps/cpp-httplib @@ -0,0 +1 @@ +Subproject commit df20c27696c6dcb2b9ccecf3c4f9c3d06c1ecf8c diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 1bbf356..80ef2dc 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -37,6 +37,6 @@ function(game_executable EXECUTABLE_GAME_NAME EXECUTABLE_SOURCES) endfunction() game_executable(${EXECUTABLE_GAME} example.cpp) -#game_executable(${EXECUTABLE_GAME_RUNNER} game_runner_example.cpp) +game_executable(${EXECUTABLE_LOADER_FROM_SERVER} game_loader_from_server_example.cpp) game_executable(${EXECUTABLE_GAME_RUNNER} game_with_default_loader_example.cpp) game_executable(${EXECUTABLE_GAME_DATABASE} game_database_example.cpp) diff --git a/game/game_database_example.cpp b/game/game_database_example.cpp index 48e0fe2..586b030 100644 --- a/game/game_database_example.cpp +++ b/game/game_database_example.cpp @@ -45,8 +45,8 @@ int main() { auto database_storage_driver = std::make_shared(database); std::vector> level_loaders{ - std::make_shared("../../game/resources/levels/level_with_finish"), - std::make_shared("../../game/resources/levels/level_01") + std::make_shared("../../resources/levels/level_with_finish"), + std::make_shared("../../resources/levels/level_01") }; auto game_runner = std::make_unique( diff --git a/game/game_loader_from_server_example.cpp b/game/game_loader_from_server_example.cpp new file mode 100644 index 0000000..0d42e9d --- /dev/null +++ b/game/game_loader_from_server_example.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +int main() { +#ifndef NDEBUG + auto log_level = spdlog::level::trace; +#else + auto log_level = spdlog::level::info; +#endif + spdlog::set_level(log_level); + + auto window = std::make_shared(sf::VideoMode(640, 480), "MAD"); + ImGui::SFML::Init(*window); + window->setFramerateLimit(120); + + auto global_dispatcher = std::make_shared(); + + auto system_listener = std::make_shared(window); + + auto database = std::make_shared(); + auto database_storage_driver = std::make_shared(database); + + std::string level_map; + json level_config; + + std::ifstream input_config("../../resources/levels/level_with_finish/config.json"); + input_config >> level_config; + std::ifstream map_file("../../resources/levels/level_with_finish/map"); + std::string map_line; + while (std::getline(map_file, map_line)) { + level_map += map_line; + } + + std::vector> level_loaders{ + std::make_shared(level_map, level_config) + }; + + auto game_runner = std::make_unique( + level_loaders, + global_dispatcher, + std::make_unique(), + std::make_unique(database_storage_driver), + system_listener, + database_storage_driver + ); + + global_dispatcher->registry(std::make_shared(*window)); + global_dispatcher->registry(std::make_shared(*game_runner)); + global_dispatcher->registry(std::make_shared(*game_runner, database_storage_driver)); + global_dispatcher->registry(std::make_shared(*game_runner)); + + game_runner->run(*window); + + ImGui::SFML::Shutdown(); +} diff --git a/game/game_with_default_loader_example.cpp b/game/game_with_default_loader_example.cpp index 337426b..37cafe8 100644 --- a/game/game_with_default_loader_example.cpp +++ b/game/game_with_default_loader_example.cpp @@ -31,7 +31,7 @@ int main() { #endif spdlog::set_level(log_level); - auto window = std::make_shared(sf::VideoMode(640, 480), "MAD"); + auto window = std::make_shared(sf::VideoMode(2560, 1600), "MAD"); ImGui::SFML::Init(*window); window->setFramerateLimit(120); @@ -42,7 +42,7 @@ int main() { auto offline_storage_driver = std::make_shared(); std::vector> level_loaders{ - std::make_shared("../../game/resources/levels/level_01") + std::make_shared("../../resources/levels/level_01") }; auto game_runner = std::make_unique( diff --git a/network/CMakeLists.txt b/network/CMakeLists.txt new file mode 100644 index 0000000..69e16eb --- /dev/null +++ b/network/CMakeLists.txt @@ -0,0 +1,41 @@ +function(network_executable EXECUTABLE_NETWORK_NAME EXECUTABLE_SOURCES) + message(STATUS "Network executable: '${EXECUTABLE_NETWORK_NAME}' is built with ${EXECUTABLE_SOURCES}") + + add_executable( + ${EXECUTABLE_NETWORK_NAME} + ${EXECUTABLE_SOURCES} + ${SOURCES_IMGUI} + ${SOURCES_IMGUI_SFML} + ) + + if (CMAKE_BUILD_TYPE MATCHES Debug) + target_compile_definitions(${EXECUTABLE_NETWORK_NAME} PUBLIC SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) + message(STATUS "Enable debug logs for ${EXECUTABLE_NETWORK_NAME}") + endif() + + target_include_directories( + ${EXECUTABLE_NETWORK_NAME} PUBLIC + + ${PROJECT_SOURCE_DIR} + ${INCLUDE_SPDLOG} + ${INCLUDE_BOX2D} + ${INCLUDE_IMGUI} + ${INCLUDE_IMGUI_SFML} + ${INCLUDE_HTTP} + ) + + target_link_libraries( + ${EXECUTABLE_NETWORK_NAME} + + ${LIBRARY_SFML} + ${LIBRARY_CORE} + ${LIBRARY_SPDLOG} + ${LIBRARY_BOX2D} + ${LIBRARY_OPENGL} + ${LIBRARY_PQXX} + ) + +endfunction() + +network_executable(${EXECUTABLE_SIMPLE_SERVER} server/simple-server.cpp) +network_executable(${EXECUTABLE_SIMPLE_CLIENT} client/simple-client.cpp) diff --git a/network/client/simple-client.cpp b/network/client/simple-client.cpp new file mode 100644 index 0000000..a7977b3 --- /dev/null +++ b/network/client/simple-client.cpp @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace mad::core { + + class NetworkClientStorageDriver : public ClientStorageDriver { + public: + explicit NetworkClientStorageDriver() : m_client("localhost", 8080) { + auto res = m_client.Get("/connection"); + SPDLOG_DEBUG("Connection request result:\n\tStatus: " + std::to_string(res->status) + "\n\tMessage: " + res->body); + } + + bool log_in(const std::string &username) const override { + auto res = m_client.Post("/user/login", httplib::Params{ + {"username", username} + }); + SPDLOG_DEBUG("Login request result:\n\tStatus: " + std::to_string(res->status) + "\n\tMessage: " + res->body); + if (res->status == 200) { + m_username = username; + } + return res->status == 200; + } + + bool sign_up(const std::string &username) override { + auto res = m_client.Post("/user/signup", httplib::Params{ + {"username", username} + }); + SPDLOG_DEBUG("Sign up request result:\n\tStatus: " + std::to_string(res->status) + "\n\tMessage: " + res->body); + return res->status == 200; + } + + std::size_t get_progress() const override { + auto res = m_client.Post("/user/progress", httplib::Params{ + {"username", m_username} + }); + SPDLOG_DEBUG("Get progress request result:\n\tStatus: " + std::to_string(res->status) + "\n\tMessage: " + res->body); + return std::stoi(res->body); + } + + void update_progress() override { + auto res = m_client.Post("/user/increment-progress", httplib::Params{ + {"username", m_username} + }); + SPDLOG_DEBUG("Update request result:\n\tStatus: " + std::to_string(res->status) + "\n\tMessage: " + res->body); + } + + std::vector> get_levels() { + auto res = m_client.Get("/level/total"); + std::size_t level_count = std::stoi(res->body); + std::vector> loaders; + for (std::size_t i = 1; i <= level_count; ++i) { + auto res1 = m_client.Post("/level/load", httplib::Params{ + {"number", std::to_string(i)} + }); + json config; + std::stringstream ss; + ss << res1->body; + ss >> config; + loaders.push_back(std::make_shared(config["map"], config)); + } + return loaders; + } + + private: + mutable std::string m_username; + mutable httplib::Client m_client; + + }; + +} + +int main() { +#ifndef NDEBUG + auto log_level = spdlog::level::trace; +#else + auto log_level = spdlog::level::info; +#endif + spdlog::set_level(log_level); + + auto window = std::make_shared(sf::VideoMode(640, 480), "MAD"); + ImGui::SFML::Init(*window); + window->setFramerateLimit(120); + + auto global_dispatcher = std::make_shared(); + + auto system_listener = std::make_shared(window); + + auto network_storage_driver = std::make_shared(); + + std::vector> level_loaders = network_storage_driver->get_levels(); + + auto game_runner = std::make_unique( + level_loaders, + global_dispatcher, + std::make_unique(), + std::make_unique(network_storage_driver), + system_listener, + network_storage_driver + ); + + global_dispatcher->registry(std::make_shared(*window)); + global_dispatcher->registry(std::make_shared(*game_runner)); + global_dispatcher->registry(std::make_shared(*game_runner, network_storage_driver)); + global_dispatcher->registry(std::make_shared(*game_runner)); + + game_runner->run(*window); + + ImGui::SFML::Shutdown(); +} diff --git a/network/server/simple-server.cpp b/network/server/simple-server.cpp new file mode 100644 index 0000000..cc77ab4 --- /dev/null +++ b/network/server/simple-server.cpp @@ -0,0 +1,247 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +using json = nlohmann::json; + + +int main() { + httplib::Server svr; + mad::core::Database db; + std::filesystem::path levels_directory = "../../resources/levels"; + + std::mutex locker; + std::vector logs; + + svr.Get("/connection", [&logs, &locker](const httplib::Request &req, httplib::Response &res) { + std::unique_lock lock(locker); + res.status = 200; + res.body = "Connection successful"; + logs.push_back("Connected user port " + std::to_string(req.remote_port)); + }); + + svr.Get("/level/total", [&logs, &locker, &db](const httplib::Request &req, httplib::Response &res) { + std::unique_lock lock(locker); + res.status = 200; + res.body = std::to_string(db.get_levels_total()); + logs.push_back("Send total level counter to port " + std::to_string(req.remote_port)); + }); + + svr.Post("/level/load", [&logs, &locker, &db, &levels_directory](const httplib::Request &req, httplib::Response &res) { + std::unique_lock lock(locker); + if (req.has_param("number")) { + auto number = std::stoi(req.get_param_value("number")); + if (number <= db.get_levels_total()) { + auto levelname = db.get_levelname(number); + auto cur_level_directory = levels_directory / levelname; + std::ifstream input_config(cur_level_directory / "config.json"); + std::ifstream input_map(cur_level_directory / "map"); + json config; + std::string map, map_line; + input_config >> config; + while (input_map >> map_line) { + map += map_line; + } + config["map"] = map; + res.status = 200; + res.body = to_string(config); + logs.push_back("Send level " + levelname + " to port " + std::to_string(req.remote_port)); + } else { + res.status = 404; + res.body = "Invalid number of level"; + } + } else { + res.status = 404; + res.body = "Invalid params of request"; + } + }); + + svr.Post("/user/login", [&db, &logs, &locker](const httplib::Request &req, httplib::Response &res) { + std::unique_lock lock(locker); + if (req.has_param("username")) { + auto username = req.get_param_value("username"); + if (db.is_user_exists(username)) { + res.status = 200; + res.body = "OK"; + logs.push_back("User " + std::to_string(req.remote_port) + " login as " + username); + } else { + res.status = 404; + res.body = "User doesn\'t exists"; + } + } else { + res.status = 404; + res.body = "Invalid params of request"; + } + }); + + svr.Post("/user/signup", [&db, &logs, &locker](const httplib::Request &req, httplib::Response &res) { + std::unique_lock lock(locker); + if (req.has_param("username")) { + auto username = req.get_param_value("username"); + if (db.is_user_exists(username)) { + res.status = 404; + res.body = "User already exists"; + logs.push_back("Register new user " + username + " from port " + std::to_string(req.remote_port)); + } else { + db.registry_user(username); + res.status = 200; + res.body = "User " + username + " is registered"; + } + } else { + res.status = 404; + res.body = "Invalid params of request"; + } + }); + + svr.Post("/user/progress", [&db, &logs, &locker](const httplib::Request &req, httplib::Response &res) { + std::unique_lock lock(locker); + if (req.has_param("username")) { + auto username = req.get_param_value("username"); + if (db.is_user_exists(username)) { + res.status = 200; + res.body = std::to_string(db.get_progress(username)); + logs.push_back("Send progress to user " + username); + } else { + res.status = 404; + res.body = "User doesn\'t exists"; + } + } else { + res.status = 404; + res.body = "Invalid params of request"; + } + }); + + svr.Post("/user/increment-progress", [&db, &logs, &locker](const httplib::Request &req, httplib::Response &res) { + std::unique_lock lock(locker); + if (req.has_param("username")) { + auto username = req.get_param_value("username"); + if (db.is_user_exists(username)) { + res.status = 200; + res.body = "OK"; + db.increment_progress(username); + logs.push_back("Increment progress for user " + username); + } else { + res.status = 404; + res.body = "User doesn\'t exists"; + } + } else { + res.status = 404; + res.body = "Invalid params of request"; + } + }); + + sf::RenderWindow window(sf::VideoMode(640, 480), "MAD Server"); + ImGui::SFML::Init(window); + window.setFramerateLimit(120); + char input_levelname[255] = "\0"; + std::string text_hint = "new level name"; + sf::Clock clock; + while (window.isOpen()) { + sf::Event event; + while (window.pollEvent(event)) { + ImGui::SFML::ProcessEvent(event); + + if (event.type == sf::Event::Closed) { + window.close(); + if (svr.is_running()) { + svr.stop(); + } + } + } + + ImGui::SFML::Update(window, clock.restart()); + + ImGui::SetNextWindowSize(ImVec2(window.getSize().x, window.getSize().y)); + ImGui::SetNextWindowPos({0, 0}); + ImGui::Begin("Server util", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); + + { + std::unique_lock lock(locker); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; + ImGui::BeginChild("Tool buttons", ImVec2(ImGui::GetContentRegionAvail().x * 0.3f, 82), true, window_flags); + if (ImGui::Button("Start server")) { + if (!svr.is_running()) { + std::thread([&svr]() mutable { + svr.listen("localhost", 8080); + }).detach(); + logs.emplace_back("Server has started"); + } + } + + if (ImGui::Button("Stop server")) { + if (svr.is_running()) { + svr.stop(); + logs.emplace_back("Server has stopped"); + } + } + + if (ImGui::Button("Quit")) { + window.close(); + if (svr.is_running()) { + svr.stop(); + } + } + ImGui::EndChild(); + } + + ImGui::SameLine(); + { + std::unique_lock lock(locker); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; + ImGui::BeginChild("Input levelname", ImVec2(ImGui::GetContentRegionAvail().x, 82), true, window_flags); + ImGui::InputText("##", input_levelname, 255); + ImGui::Text(text_hint.c_str()); + if (ImGui::Button("Enter")) { + if (!std::string(input_levelname).empty()) { + std::filesystem::path level_container = levels_directory / input_levelname; + if (std::filesystem::is_directory(level_container) && + std::filesystem::exists(level_container / "config.json") && + std::filesystem::exists(level_container / "map")) { + if (!db.is_level_exists(input_levelname)) { + db.append_level(input_levelname); + text_hint = "new level name"; + } else { + text_hint = "level name is already used"; + } + } else { + text_hint = "wrong directory format"; + } + } else { + text_hint = "string is empty"; + } + } + ImGui::EndChild(); + } + + { + std::unique_lock lock(locker); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; + ImGui::BeginChild("Logs", ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y), true, window_flags); + for (int i = 0; i < logs.size(); ++i) { + ImGui::Text(logs[i].c_str(), i); + } + ImGui::EndChild(); + } + + ImGui::End(); + + window.clear(); + ImGui::SFML::Render(window); + window.display(); + } + ImGui::SFML::Shutdown(); +} diff --git a/game/resources/animated/helicopter.png b/resources/animated/helicopter.png similarity index 100% rename from game/resources/animated/helicopter.png rename to resources/animated/helicopter.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00000.png b/resources/animated/hero/idle/Chara - BlueIdle00000.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00000.png rename to resources/animated/hero/idle/Chara - BlueIdle00000.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00001.png b/resources/animated/hero/idle/Chara - BlueIdle00001.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00001.png rename to resources/animated/hero/idle/Chara - BlueIdle00001.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00002.png b/resources/animated/hero/idle/Chara - BlueIdle00002.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00002.png rename to resources/animated/hero/idle/Chara - BlueIdle00002.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00003.png b/resources/animated/hero/idle/Chara - BlueIdle00003.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00003.png rename to resources/animated/hero/idle/Chara - BlueIdle00003.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00004.png b/resources/animated/hero/idle/Chara - BlueIdle00004.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00004.png rename to resources/animated/hero/idle/Chara - BlueIdle00004.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00005.png b/resources/animated/hero/idle/Chara - BlueIdle00005.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00005.png rename to resources/animated/hero/idle/Chara - BlueIdle00005.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00006.png b/resources/animated/hero/idle/Chara - BlueIdle00006.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00006.png rename to resources/animated/hero/idle/Chara - BlueIdle00006.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00007.png b/resources/animated/hero/idle/Chara - BlueIdle00007.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00007.png rename to resources/animated/hero/idle/Chara - BlueIdle00007.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00008.png b/resources/animated/hero/idle/Chara - BlueIdle00008.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00008.png rename to resources/animated/hero/idle/Chara - BlueIdle00008.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00009.png b/resources/animated/hero/idle/Chara - BlueIdle00009.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00009.png rename to resources/animated/hero/idle/Chara - BlueIdle00009.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00010.png b/resources/animated/hero/idle/Chara - BlueIdle00010.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00010.png rename to resources/animated/hero/idle/Chara - BlueIdle00010.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00011.png b/resources/animated/hero/idle/Chara - BlueIdle00011.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00011.png rename to resources/animated/hero/idle/Chara - BlueIdle00011.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00012.png b/resources/animated/hero/idle/Chara - BlueIdle00012.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00012.png rename to resources/animated/hero/idle/Chara - BlueIdle00012.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00013.png b/resources/animated/hero/idle/Chara - BlueIdle00013.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00013.png rename to resources/animated/hero/idle/Chara - BlueIdle00013.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00014.png b/resources/animated/hero/idle/Chara - BlueIdle00014.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00014.png rename to resources/animated/hero/idle/Chara - BlueIdle00014.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00015.png b/resources/animated/hero/idle/Chara - BlueIdle00015.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00015.png rename to resources/animated/hero/idle/Chara - BlueIdle00015.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00016.png b/resources/animated/hero/idle/Chara - BlueIdle00016.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00016.png rename to resources/animated/hero/idle/Chara - BlueIdle00016.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00017.png b/resources/animated/hero/idle/Chara - BlueIdle00017.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00017.png rename to resources/animated/hero/idle/Chara - BlueIdle00017.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00018.png b/resources/animated/hero/idle/Chara - BlueIdle00018.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00018.png rename to resources/animated/hero/idle/Chara - BlueIdle00018.png diff --git a/game/resources/animated/hero/idle/Chara - BlueIdle00019.png b/resources/animated/hero/idle/Chara - BlueIdle00019.png similarity index 100% rename from game/resources/animated/hero/idle/Chara - BlueIdle00019.png rename to resources/animated/hero/idle/Chara - BlueIdle00019.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00000.png b/resources/animated/hero/run/Chara_BlueWalk00000.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00000.png rename to resources/animated/hero/run/Chara_BlueWalk00000.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00001.png b/resources/animated/hero/run/Chara_BlueWalk00001.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00001.png rename to resources/animated/hero/run/Chara_BlueWalk00001.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00002.png b/resources/animated/hero/run/Chara_BlueWalk00002.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00002.png rename to resources/animated/hero/run/Chara_BlueWalk00002.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00003.png b/resources/animated/hero/run/Chara_BlueWalk00003.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00003.png rename to resources/animated/hero/run/Chara_BlueWalk00003.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00004.png b/resources/animated/hero/run/Chara_BlueWalk00004.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00004.png rename to resources/animated/hero/run/Chara_BlueWalk00004.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00005.png b/resources/animated/hero/run/Chara_BlueWalk00005.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00005.png rename to resources/animated/hero/run/Chara_BlueWalk00005.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00006.png b/resources/animated/hero/run/Chara_BlueWalk00006.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00006.png rename to resources/animated/hero/run/Chara_BlueWalk00006.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00007.png b/resources/animated/hero/run/Chara_BlueWalk00007.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00007.png rename to resources/animated/hero/run/Chara_BlueWalk00007.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00008.png b/resources/animated/hero/run/Chara_BlueWalk00008.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00008.png rename to resources/animated/hero/run/Chara_BlueWalk00008.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00009.png b/resources/animated/hero/run/Chara_BlueWalk00009.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00009.png rename to resources/animated/hero/run/Chara_BlueWalk00009.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00010.png b/resources/animated/hero/run/Chara_BlueWalk00010.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00010.png rename to resources/animated/hero/run/Chara_BlueWalk00010.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00011.png b/resources/animated/hero/run/Chara_BlueWalk00011.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00011.png rename to resources/animated/hero/run/Chara_BlueWalk00011.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00012.png b/resources/animated/hero/run/Chara_BlueWalk00012.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00012.png rename to resources/animated/hero/run/Chara_BlueWalk00012.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00013.png b/resources/animated/hero/run/Chara_BlueWalk00013.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00013.png rename to resources/animated/hero/run/Chara_BlueWalk00013.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00014.png b/resources/animated/hero/run/Chara_BlueWalk00014.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00014.png rename to resources/animated/hero/run/Chara_BlueWalk00014.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00015.png b/resources/animated/hero/run/Chara_BlueWalk00015.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00015.png rename to resources/animated/hero/run/Chara_BlueWalk00015.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00016.png b/resources/animated/hero/run/Chara_BlueWalk00016.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00016.png rename to resources/animated/hero/run/Chara_BlueWalk00016.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00017.png b/resources/animated/hero/run/Chara_BlueWalk00017.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00017.png rename to resources/animated/hero/run/Chara_BlueWalk00017.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00018.png b/resources/animated/hero/run/Chara_BlueWalk00018.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00018.png rename to resources/animated/hero/run/Chara_BlueWalk00018.png diff --git a/game/resources/animated/hero/run/Chara_BlueWalk00019.png b/resources/animated/hero/run/Chara_BlueWalk00019.png similarity index 100% rename from game/resources/animated/hero/run/Chara_BlueWalk00019.png rename to resources/animated/hero/run/Chara_BlueWalk00019.png diff --git a/game/resources/animated/runner_new.png b/resources/animated/runner_new.png similarity index 100% rename from game/resources/animated/runner_new.png rename to resources/animated/runner_new.png diff --git a/resources/background/background_01.png b/resources/background/background_01.png new file mode 100644 index 0000000..a4a4805 Binary files /dev/null and b/resources/background/background_01.png differ diff --git a/resources/background/background_02.png b/resources/background/background_02.png new file mode 100644 index 0000000..745ef83 Binary files /dev/null and b/resources/background/background_02.png differ diff --git a/resources/background/background_03.png b/resources/background/background_03.png new file mode 100644 index 0000000..90ec816 Binary files /dev/null and b/resources/background/background_03.png differ diff --git a/resources/background/background_04.png b/resources/background/background_04.png new file mode 100644 index 0000000..66bbcae Binary files /dev/null and b/resources/background/background_04.png differ diff --git a/resources/background/background_05.png b/resources/background/background_05.png new file mode 100644 index 0000000..936c5f7 Binary files /dev/null and b/resources/background/background_05.png differ diff --git a/game/resources/levels/level_01/config.json b/resources/levels/level_01/config.json similarity index 77% rename from game/resources/levels/level_01/config.json rename to resources/levels/level_01/config.json index 4594a7c..d6ff91f 100644 --- a/game/resources/levels/level_01/config.json +++ b/resources/levels/level_01/config.json @@ -1,6 +1,7 @@ { "name" : "level_01", - "animated_resources" : "../../game/resources/animated/", + "animated_resources" : "../../resources/animated/", + "width" : 14, "block" : 50.0, "camera": { "position" : { @@ -11,6 +12,11 @@ "smoothness": 0.6, "zoom": 10.5 }, + "background" : { + "source" : "../../resources/background", + "parallax_ratios" : [0.95, 0.9, 0.85, 0.8, 0.75], + "scale" : 2.8 + }, "texture" : { "stable" : "brick.png", "unstable" : "brick.png", diff --git a/game/resources/levels/level_01/map b/resources/levels/level_01/map similarity index 100% rename from game/resources/levels/level_01/map rename to resources/levels/level_01/map diff --git a/game/resources/levels/level_with_finish/config.json b/resources/levels/level_with_finish/config.json similarity index 77% rename from game/resources/levels/level_with_finish/config.json rename to resources/levels/level_with_finish/config.json index 6482a2a..c0f4a17 100644 --- a/game/resources/levels/level_with_finish/config.json +++ b/resources/levels/level_with_finish/config.json @@ -1,6 +1,7 @@ { "name" : "level_with_finish", - "animated_resources" : "../../game/resources/animated/", + "animated_resources" : "../../resources/animated/", + "width" : 18, "block" : 50.0, "camera": { "position" : { @@ -11,6 +12,11 @@ "smoothness": 0.6, "zoom": 10.5 }, + "background" : { + "source" : "../../resources/background", + "parallax_ratios" : [0.95, 0.9, 0.85, 0.8, 0.75], + "scale" : 2.8 + }, "texture" : { "stable" : "brick.png", "unstable" : "brick.png", diff --git a/game/resources/levels/level_with_finish/map b/resources/levels/level_with_finish/map similarity index 100% rename from game/resources/levels/level_with_finish/map rename to resources/levels/level_with_finish/map diff --git a/game/resources/static/18plus.png b/resources/static/18plus.png similarity index 100% rename from game/resources/static/18plus.png rename to resources/static/18plus.png diff --git a/game/resources/static/brick.png b/resources/static/brick.png similarity index 100% rename from game/resources/static/brick.png rename to resources/static/brick.png diff --git a/game/resources/static/exit.png b/resources/static/exit.png similarity index 100% rename from game/resources/static/exit.png rename to resources/static/exit.png diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 07bb365..bc2aa5e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,6 +18,7 @@ function(test_executable EXECUTABLE_TEST_NAME EXECUTABLE_SOURCES) ${INCLUDE_IMGUI} ${INCLUDE_IMGUI_SFML} ${INCLUDE_PQXX} + ${INCLUDE_HTTP} ) target_link_libraries( diff --git a/test/deps/CMakeLists.txt b/test/deps/CMakeLists.txt index 3078da8..817960a 100644 --- a/test/deps/CMakeLists.txt +++ b/test/deps/CMakeLists.txt @@ -1,3 +1,5 @@ test_executable(test-box2d TestBox2d.cpp) test_executable(test-imgui TestImGui.cpp) test_executable(test-pqxx TestPQXX.cpp) +test_executable(test-server network/server.cpp) +test_executable(test-client network/client.cpp) diff --git a/test/deps/network/client.cpp b/test/deps/network/client.cpp new file mode 100644 index 0000000..198b44a --- /dev/null +++ b/test/deps/network/client.cpp @@ -0,0 +1,22 @@ +#include +#include + +int main() { + httplib::Client cli("localhost", 8080); + + auto res1 = cli.Post("/user/login", httplib::Params{ + {"username", "Denis"} + }); + std::cout << res1->status << ' ' << res1->body << '\n'; + + + auto res2 = cli.Post("/user/login", httplib::Params{ + {"username", "NoDenis"} + }); + std::cout << res2->status << ' ' << res2->body << '\n'; + + auto res3 = cli.Post("/user/login", httplib::Params{ + {"name", "Denis"} + }); + std::cout << res3->status << ' ' << res3->body << '\n'; +} diff --git a/test/deps/network/server.cpp b/test/deps/network/server.cpp new file mode 100644 index 0000000..e4038c3 --- /dev/null +++ b/test/deps/network/server.cpp @@ -0,0 +1,155 @@ +#include +#include +#include + +namespace mad::core { + class Database { + public: + Database(); + + bool is_user_exists(const std::string &username); + + void registry_user(const std::string &username); + + std::size_t get_id(const std::string &username); + + std::size_t get_progress(std::size_t id); + + std::size_t get_progress(const std::string &username); + + void increment_progress(std::size_t id); + + void increment_progress(const std::string &username); + + void reset_progress(std::size_t id); + + void reset_progress(const std::string &username); + + private: + pqxx::connection m_connector; + std::string m_query; + + }; +} + +namespace mad::core { + + Database::Database() : m_connector("dbname = test-network") { + try { + if (m_connector.is_open()) { + SPDLOG_DEBUG("Database mad opened successfully"); + } else { + SPDLOG_DEBUG("Can't open database mad"); + } + + pqxx::work w(m_connector); + + m_query = "CREATE TABLE IF NOT EXISTS users(" + "id SMALLINT PRIMARY KEY," + "name TEXT NOT NULL UNIQUE);"; + w.exec(m_query); + + m_query = "CREATE TABLE IF NOT EXISTS progress(" + "id SMALLINT PRIMARY KEY REFERENCES users (id)," + "levels_completed SMALLINT NOT NULL);"; + w.exec(m_query); + + w.commit(); + + SPDLOG_DEBUG("Tables created successfully"); + } catch (std::exception &exc) { + SPDLOG_INFO(exc.what()); + } + } + + bool Database::is_user_exists(const std::string &username) { + pqxx::work W(m_connector); + m_query = "SELECT * FROM users WHERE name = '" + W.esc(username) + "';"; + pqxx::result rows_found = W.exec(m_query); + return !rows_found.empty(); + } + + void Database::registry_user(const std::string &username) { + pqxx::work W(m_connector); + + m_query = "SELECT id FROM users"; + pqxx::result total_rows = W.exec(m_query); + std::size_t id = total_rows.size(); + + m_query = "INSERT INTO users(id, name) VALUES(" + std::to_string(id) + ", '" + W.esc(username) + "');"; + W.exec(m_query); + + m_query = "INSERT INTO progress(id, levels_completed) VALUES(" + std::to_string(id) + ", 0);"; + W.exec(m_query); + + W.commit(); + } + + std::size_t Database::get_id(const std::string &username) { + pqxx::work W(m_connector); + m_query = "SELECT * FROM users WHERE name = '" + W.esc(username) + "';"; + auto found_row = W.exec1(m_query); + return found_row["id"].as(); + } + + std::size_t Database::get_progress(std::size_t id) { + pqxx::work W(m_connector); + m_query = "SELECT * FROM progress WHERE id = " + std::to_string(id) + ";"; + auto found_row = W.exec1(m_query); + return found_row["levels_completed"].as(); + } + + std::size_t Database::get_progress(const std::string &username) { + return get_progress(get_id(username)); + } + + void Database::increment_progress(std::size_t id) { + pqxx::work W(m_connector); + m_query = "UPDATE progress " + "SET levels_completed = levels_completed + 1 " + "WHERE id = " + std::to_string(id) + ";"; + W.exec(m_query); + W.commit(); + } + + void Database::increment_progress(const std::string &username) { + increment_progress(get_id(username)); + } + + void Database::reset_progress(std::size_t id) { + pqxx::work W(m_connector); + m_query = "UPDATE progress " + "SET levels_completed = 0 " + "WHERE id = " + std::to_string(id) + ";"; + W.exec(m_query); + W.commit(); + } + + void Database::reset_progress(const std::string &username) { + reset_progress(get_id(username)); + } +} + +int main() { + httplib::Server svr; + mad::core::Database db; + + svr.Post("/user/login", [&db](const httplib::Request &req, httplib::Response &res) { + if (req.has_param("username")) { + auto username = req.get_param_value("username"); + if (db.is_user_exists(username)) { + res.status = 200; + res.body = "OK"; + } else { + res.status = 404; + res.body = "User doesn\'t exists"; + } + } else { + res.status = 404; + res.body = "Invalid params of request"; + } + }); + + + svr.listen("localhost", 8080); +} \ No newline at end of file