commit 78a25b305b00a88986ac396c8e249a5a67c55f46 Author: Jiga228 Date: Sun Sep 14 15:00:44 2025 +0700 Я пересоздал репозиторий из-за большого количества мусора в прошлом diff --git a/.github/workflows/ubuntu-test.yml b/.github/workflows/ubuntu-test.yml new file mode 100644 index 0000000..22b977f --- /dev/null +++ b/.github/workflows/ubuntu-test.yml @@ -0,0 +1,34 @@ +name: Ubuntu test + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + BUILD_TYPE: Release + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository with submodules + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 1 + + - name: Проверка содержимого glfw + run: ls -la glfw + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + #- name: Test + # working-directory: ${{github.workspace}}/build + # run: ctest -C ${{env.BUILD_TYPE}} diff --git a/.github/workflows/windows-test.yml b/.github/workflows/windows-test.yml new file mode 100644 index 0000000..a379efd --- /dev/null +++ b/.github/workflows/windows-test.yml @@ -0,0 +1,34 @@ +name: Windows test + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + BUILD_TYPE: Release + +jobs: + build: + runs-on: windows-latest + + steps: + - name: Checkout repository with submodules + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 1 + + - name: Проверка содержимого glfw + run: dir glfw + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + +# - name: Test +# working-directory: ${{github.workspace}}/build +# run: ctest -C ${{env.BUILD_TYPE}} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9cf718c --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +CMakeUserPresets.json + +# CLion +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#cmake-build-* + +build/ +.dist/ +.vs/ +.vscode/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..728bde6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "glfw"] + path = glfw + url = https://github.com/glfw/glfw.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6d6e0a6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.14) +project("UwU Engine" VERSION 0.1.0) + +set(OUTPUT_DIR ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR}) + +if(NOT MSVC) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") +endif() + +if(MSVC) + add_compile_options(/GR-) +else() + add_compile_options(-fno-rtti) +endif() + +add_subdirectory(FastRTTI) +add_subdirectory(glfw) +add_subdirectory(Core) +add_subdirectory(ModuleLib) +add_subdirectory(TestGame) +add_subdirectory(ProjectGenerator) diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt new file mode 100644 index 0000000..83fcbdd --- /dev/null +++ b/Core/CMakeLists.txt @@ -0,0 +1,25 @@ +set(CORE_NAME Core) + +find_package(Vulkan REQUIRED) + +set(CORE_NAME Core) + +file(GLOB_RECURSE SRC "*.cpp" "*.c" "*.h" "*.hpp") + +add_library(${CORE_NAME} STATIC ${SRC}) +target_compile_features(${CORE_NAME} PRIVATE cxx_std_17) +target_include_directories(${CORE_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/FastRTTI + ${PROJECT_SOURCE_DIR}/Delegate + ${PROJECT_SOURCE_DIR}/glfw/Include + ${Vulkan_INCLUDE_DIRS} +) + +target_link_libraries(${CORE_NAME} PRIVATE + ${CMAKE_DL_LIBS} # For linux + FastRTTI + glfw + Vulkan::Vulkan +) \ No newline at end of file diff --git a/Core/Core/CoreInstance.cpp b/Core/Core/CoreInstance.cpp new file mode 100644 index 0000000..88036b2 --- /dev/null +++ b/Core/Core/CoreInstance.cpp @@ -0,0 +1,117 @@ +#include "CoreInstance.h" + +#include "SystemCalls.h" + +#include +#include +#include +#include + +std::shared_ptr CoreInstance::MainConfig::save() +{ + return std::make_shared("MainConfig"); +} + +void CoreInstance::MainConfig::load(std::shared_ptr save) +{ + game_name = save->GetString("game_name"); + base_world = save->GetString("base_world"); + modules_names = save->GetVectorString("modules_names"); +} + +CoreInstance::CoreInstance() +{ + countCPU = System::getCountCPU(); + memorySize = System::getMemorySize(); + + std::ifstream main_config_file(main_config_name); + if (main_config_file.fail()) + throw std::runtime_error("Fail open main config file"); + + std::string payload; + std::getline(main_config_file, payload); + std::shared_ptr load_main_config = std::make_shared(payload); + + main_config.load(load_main_config); +} + +CoreInstance::~CoreInstance() +{ + if (game != nullptr) + delete game; + + for (auto& module : modules) + System::QuitModule(module.handler); + modules.clear(); +} + +extern GameInstance* GameFactory(CoreInstance&); + +void CoreInstance::start() +{ + for (auto& name : main_config.modules_names) + { + try + { + void* handler = System::InitModule(name.c_str(), *this); + modules.push_back(Module {name.c_str(), handler}); + } catch (const std::exception& e) + { + std::cout << e.what() << '\n'; + } + } + + for (auto& module : modules) + { + try + { + System::StartModule(module.handler); + } catch (const std::exception& e) + { + std::cout << e.what() << '\n'; + } + } + + game = GameFactory(*this); + if (game == nullptr) + throw std::runtime_error("Fail create game instance!"); + game->start(); +} + +void* CoreInstance::getHandlerModule(const char* name) const +{ + for (const auto& module : modules) + { + if (std::strcmp(module.name, name) == 0) + return module.handler; + } + throw std::runtime_error("Module not found"); +} + +void* CoreInstance::enableModule(const char* name) +{ + for (const auto& module : modules) + { + if (std::strcmp(module.name, name) == 0) + throw std::runtime_error("Module already enabled"); + } + + void* module = System::InitModule(name, *this); + modules.push_back({ name, module }); + return module; +} + +void CoreInstance::disableModule(const char* name) +{ + for (auto i = modules.cbegin(); i != modules.cend(); ++i) + { + if (std::strcmp(i->name, name) == 0) + { + System::QuitModule(i->handler); + modules.erase(i); + break; + } + } + + throw std::runtime_error("Module not found"); +} diff --git a/Core/Core/CoreInstance.h b/Core/Core/CoreInstance.h new file mode 100644 index 0000000..8106fd3 --- /dev/null +++ b/Core/Core/CoreInstance.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include + +#include "Game/GameInstance.h" +#include "Game/SaveMap/SaveMap.h" + +class CoreInstance { + struct Module { + const char* name; + void* handler; + }; + + struct MainConfig final : ISave + { + std::string game_name; + std::string base_world; + std::vector modules_names; + + std::shared_ptr save() override; + void load(std::shared_ptr save) override; + }; + +#pragma region Key words for main config + const char* key_base_world= "base_world"; + const char* key_modules = "modules"; +#pragma endregion + +#pragma region config parameters + const char* main_config_name = "main_config.conf"; + // std::string base_world; + // std::vector modules_names; + // std::string game_name; + MainConfig main_config; +#pragma endregion + + int countCPU; + unsigned long long memorySize; + std::list modules; + + GameInstance* game = nullptr; +public: + CoreInstance(); + ~CoreInstance(); + + void start(); + + int getCountCPU() const { return countCPU; } + unsigned long long getMemorySize() const { return memorySize; } + + /** + * @throws std::runtime_error if module isn't enabled + * @throws std::runtime_error if module not found + * @return module handler + */ + void* getHandlerModule(const char* name) const noexcept(false); + /** + * @throws std::runtime_error if the module already enabled + * @throws std::runtime_error if the module isn't found + * @throws std::runtime_error if the module doesn't contain InitModule or StartModule + * @retuen module handler + */ + void* enableModule(const char* name) noexcept(false); + /** + * @throws std::runtime_error if the module isn't found + * @throws std::runtime_error module isn't contain QuitModule function + */ + void disableModule(const char* name) noexcept(false); + + const std::string& getBaseWorldName() const { return main_config.base_world; } + const std::string& getGameName() const { return main_config.game_name; } +}; diff --git a/Core/Core/Linux.cpp b/Core/Core/Linux.cpp new file mode 100644 index 0000000..7a2ad17 --- /dev/null +++ b/Core/Core/Linux.cpp @@ -0,0 +1,73 @@ +#ifdef __linux__ + +#include "SystemCalls.h" + +#include +#include +#include + +#include +#include + +int System::getCountCPU() +{ + int num_cores = sysconf(_SC_NPROCESSORS_ONLN); + if(num_cores <= 0) + throw std::runtime_error("Fail get number of cores"); + + return num_cores; +} + +unsigned long long System::getMemorySize() +{ + struct sysinfo info{}; + if(sysinfo(&info) == -1) + std::runtime_error("Fail get system info"); + return info.totalram; +} + +void* System::InitModule(const char* ModuleName, CoreInstance& core) +{ + std::string fileName = "./lib"; + fileName += ModuleName; + fileName += ".so"; + + void* handler = dlopen(fileName.c_str(), RTLD_NOW); + if(handler == nullptr) + throw std::runtime_error("Module not found"); + + void(*init)(CoreInstance&) = reinterpret_cast(dlsym(handler, "InitModule")); + if(init == nullptr) + throw std::runtime_error("Module not contains InitModule"); + init(core); + + return handler; +} + +void System::StartModule(void* handler) +{ + void(*start)() = reinterpret_cast(dlsym(handler, "StartModule")); + if(start == nullptr) + throw std::runtime_error("Module not contins StartModule"); + start(); +} + +void System::StopModule(void* handler) +{ + void(*stop)() = reinterpret_cast(dlsym(handler, "StopModule")); + if(stop == nullptr) + std::runtime_error("Module mot contins StopModule"); + stop(); + dlclose(handler); +} + +void System::QuitModule(void* handler) +{ + void(*quit)() = reinterpret_cast(dlsym(handler, "QuitModule")); + if(quit == nullptr) + throw std::runtime_error("Module not contains QuitModule"); + quit(); + dlclose(handler); +} + +#endif diff --git a/Core/Core/RenderEngine.cpp b/Core/Core/RenderEngine.cpp new file mode 100644 index 0000000..ea00aa8 --- /dev/null +++ b/Core/Core/RenderEngine.cpp @@ -0,0 +1,74 @@ +#include "RenderEngine.h" + +#include + +#ifdef _DEBUG +#include +#define VK_CHECK(res) assert(res == VK_SUCCESS) +#else +#define VK_CHECK(res) res +#endif + +void RenderEngine::render_thread_func() +{ + window = glfwCreateWindow(640, 480, "UwU Engine", NULL, NULL); + if (!window) + { + glfwTerminate(); + throw std::runtime_error("Fail create window"); + } + while (!glfwWindowShouldClose(window)) + { + glfwSwapBuffers(window); + glfwPollEvents(); + } + onCloseWindow.Call(); +} + +RenderEngine::RenderEngine(const std::string& app_name, + int major_app_version, int minor_app_version, int patch_app_version, + int major_engine_version, int minor_engine_version, int patch_engine_version) +{ + if (!glfwInit()) + throw std::runtime_error("Fail initialize GLFW"); + + { + uint32_t extensions_count = 0; + const char** extensions = glfwGetRequiredInstanceExtensions(&extensions_count); + + VkApplicationInfo app_info{}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.apiVersion = VK_API_VERSION_1_0; + app_info.applicationVersion = VK_MAKE_VERSION(major_app_version, minor_app_version, patch_app_version); + app_info.pApplicationName = app_name.c_str(); + app_info.pEngineName = "UwU Engine"; + app_info.engineVersion = VK_MAKE_VERSION(major_engine_version, minor_engine_version, patch_engine_version); + + VkInstanceCreateInfo instance_create_info{}; + instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_create_info.ppEnabledExtensionNames = extensions; + instance_create_info.enabledExtensionCount = extensions_count; + instance_create_info.pApplicationInfo = &app_info; + VK_CHECK(vkCreateInstance(&instance_create_info, nullptr, &instance_)); + } +} + +RenderEngine::~RenderEngine() +{ + if (window != nullptr && !glfwWindowShouldClose(window)) + glfwSetWindowShouldClose(window, true); + + if (render_thread != nullptr) + { + render_thread->join(); + delete render_thread; + } + if (window != nullptr) + glfwDestroyWindow(window); + glfwTerminate(); +} + +void RenderEngine::start() +{ + render_thread = new std::thread(&RenderEngine::render_thread_func, this); +} diff --git a/Core/Core/RenderEngine.h b/Core/Core/RenderEngine.h new file mode 100644 index 0000000..081831f --- /dev/null +++ b/Core/Core/RenderEngine.h @@ -0,0 +1,28 @@ +#pragma once + +#define GLFW_INCLUDE_VULKAN + +#include "Delegate/Delegate.h" +#include +#include + +#include "GLFW/glfw3.h" + +class RenderEngine +{ + std::thread* render_thread; + + GLFWwindow* window; + VkInstance instance_; + + void render_thread_func(); +public: + Delegate onCloseWindow; + + RenderEngine(const std::string& app_name, + int major_app_version, int minor_app_version, int patch_app_version, + int major_engine_version, int minor_engine_version, int patch_engine_version); + ~RenderEngine(); + + void start(); +}; \ No newline at end of file diff --git a/Core/Core/SystemCalls.h b/Core/Core/SystemCalls.h new file mode 100644 index 0000000..49302cb --- /dev/null +++ b/Core/Core/SystemCalls.h @@ -0,0 +1,37 @@ +#pragma once + +class CoreInstance; + +namespace System { + /** + * @throws std::exception if the operation finished with failed + * @return count CPU + */ + int getCountCPU(); + unsigned long long getMemorySize(); + + /** + * Load module and init handler + * @throws std::runtime_error if module not found + * @return module handler + */ + void* InitModule(const char* ModuleName, CoreInstance& core); + + /** + * Start module + * @throws std::runtime_error if module not contains StartModule function + */ + void StartModule(void* handler) noexcept(false); + + /** + * Stop the module when unload there + * @throws std::runtime_error if module not contains StopModule function + */ + void StopModule(void* handler) noexcept(false); + + /** + * Call stop module + * @throws std::runtime_error if module not contains QuitModule function + */ + void QuitModule(void* handler) noexcept(false); +} diff --git a/Core/Core/Windows.cpp b/Core/Core/Windows.cpp new file mode 100644 index 0000000..b926e23 --- /dev/null +++ b/Core/Core/Windows.cpp @@ -0,0 +1,87 @@ +#ifdef _WIN32 + +#include "SystemCalls.h" + +#include + +#include +#include +#include +#include + +int System::getCountCPU() { + DWORD len = 0; + GetLogicalProcessorInformation(nullptr, &len); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + + std::vector buffer(len); + if (!GetLogicalProcessorInformation(buffer.data(), &len)) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + + DWORD count = len / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + int physicalCoreCount = 0; + + for (DWORD i = 0; i < count; ++i) { + if (buffer[i].Relationship == RelationProcessorCore) { + physicalCoreCount++; + } + } + + return physicalCoreCount; +} + +unsigned long long System::getMemorySize() { + MEMORYSTATUSEX memStatus{}; + memStatus.dwLength = sizeof(memStatus); + + GlobalMemoryStatusEx(&memStatus); + return memStatus.ullTotalPhys; +} + +void* System::InitModule(const char* ModuleName, CoreInstance& core) { + std::string fileName = ModuleName; + fileName += ".dll"; + + HMODULE hModule = LoadLibraryA(fileName.c_str()); + if(hModule == nullptr) + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + + void(*load)(CoreInstance&) = reinterpret_cast(GetProcAddress(hModule, "InitModule")); + if(load == nullptr) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + load(core); + + return hModule; +} + +void System::StartModule(void* handler) { + void(*start)() = reinterpret_cast(GetProcAddress(static_cast(handler), "StartModule")); + if(start == nullptr) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + start(); +} + +void System::StopModule(void* handler) { + void(*stop)() = reinterpret_cast(GetProcAddress(static_cast(handler), "StopModule")); + if(stop == nullptr) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + stop(); + FreeLibrary(static_cast(handler)); +} + +void System::QuitModule(void* handler) { + void(*quit)() = reinterpret_cast(GetProcAddress(static_cast(handler), "QuitModule")); + if(quit == nullptr) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + quit(); + FreeLibrary(static_cast(handler)); +} + +#endif \ No newline at end of file diff --git a/Core/Core/main.cpp b/Core/Core/main.cpp new file mode 100644 index 0000000..e456cd3 --- /dev/null +++ b/Core/Core/main.cpp @@ -0,0 +1,16 @@ +#include + +#include "CoreInstance.h" + +int main(int argc, char* argv[]) { + try { + CoreInstance core; + std::cout << "CPU: " << core.getCountCPU() << std::endl; + std::cout << "Memory: " << core.getMemorySize() << std::endl; + core.start(); + } catch(std::exception& e) { + std::cout << "[!] Unknown error (" << e.what() << ")\n"; + return -1; + } + return 0; +} diff --git a/Core/CoreInstance/CoreInstance.cpp b/Core/CoreInstance/CoreInstance.cpp new file mode 100644 index 0000000..38ac852 --- /dev/null +++ b/Core/CoreInstance/CoreInstance.cpp @@ -0,0 +1,139 @@ +#include "CoreInstance.h" + +#include "SystemCalls.h" + +#include +#include +#include +#include + +CoreInstance::CoreInstance() +{ + countCPU = System::getCountCPU(); + memorySize = System::getMemorySize(); + + std::ifstream main_config_file(main_config); + if (main_config_file.fail()) + throw std::runtime_error("Fail open main config file"); + + std::string line; + while (std::getline(main_config_file, line)) + { + if (base_world.empty() && line.find(key_base_world) == 0) + { + size_t pos = line.find_first_of(':') + 1; + base_world = line.substr(pos, line.size() - pos); + // trim + while (base_world[0] == ' ') + base_world.erase(0, 1); + while (base_world[base_world.size() - 1] == ' ') + base_world.erase(base_world.size() - 2, base_world.size() - 1); + + } + else if (modules_names.empty() && line.find(key_modules) == 0) + { + size_t pos = line.find(':') + 1; + line.erase(0, pos); + + size_t end = line.find(','); + while (end != std::string::npos) + { + // trim + while (line[0] == ' ') + line.erase(0, 1); + std::string module_name = line.substr(0, end - 1); + // trim + while (module_name[module_name.size() - 1] == ' ') + module_name.erase(module_name.size() - 2, module_name.size() - 1); + + modules_names.push_back(module_name); + line.erase(0, end); + end = line.find(','); + } + } + } + + if (base_world.empty()) + throw std::runtime_error("Fail read base world name"); +} + +CoreInstance::~CoreInstance() +{ + if (game != nullptr) + delete game; + + for (auto& module : modules) + System::QuitModule(module.handler); + modules.clear(); +} + +extern GameInstance* GameFactory(CoreInstance&); + +void CoreInstance::start() +{ + for (auto& name : modules_names) + { + try + { + void* handler = System::InitModule(name.c_str(), *this); + modules.push_back(Module {name.c_str(), handler}); + } catch (const std::exception& e) + { + std::cout << e.what() << '\n'; + } + } + + for (auto& module : modules) + { + try + { + System::StartModule(module.handler); + } catch (const std::exception& e) + { + std::cout << e.what() << '\n'; + } + } + + game = GameFactory(*this); + if (game == nullptr) + throw std::runtime_error("Fail create game instance!"); + game->start(); +} + +void* CoreInstance::getHandlerModule(const char* name) const +{ + for (const auto& module : modules) + { + if (std::strcmp(module.name, name) == 0) + return module.handler; + } + throw std::runtime_error("Module not found"); +} + +void* CoreInstance::enableModule(const char* name) +{ + for (const auto& module : modules) + { + if (std::strcmp(module.name, name) == 0) + throw std::runtime_error("Module already enabled"); + } + + void* module = System::InitModule(name, *this); + modules.push_back({ name, module }); + return module; +} + +void CoreInstance::disableModule(const char* name) +{ + for (auto i = modules.cbegin(); i != modules.cend(); ++i) + { + if (std::strcmp(i->name, name) == 0) + { + System::QuitModule(i->handler); + modules.erase(i); + break; + } + } + + throw std::runtime_error("Module not found"); +} diff --git a/Core/CoreInstance/CoreInstance.h b/Core/CoreInstance/CoreInstance.h new file mode 100644 index 0000000..3776320 --- /dev/null +++ b/Core/CoreInstance/CoreInstance.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +#include "Game/GameInstance.h" + +class CoreInstance { + struct Module { + const char* name; + void* handler; + }; + +#pragma region Key words for main config + const char* key_base_world= "base_world"; + const char* key_modules = "modules"; +#pragma endregion + +#pragma region config parameters + const char* main_config = "main_config.conf"; + std::string base_world; + std::vector modules_names; +#pragma endregion + + int countCPU; + unsigned long long memorySize; + std::list modules; + + GameInstance* game = nullptr; +public: + CoreInstance(); + ~CoreInstance(); + + void start(); + + int getCountCPU() const { return countCPU; } + unsigned long long getMemorySize() const { return memorySize; } + + /** + * @throws std::runtime_error if module isn't enabled + * @throws std::runtime_error if module not found + * @return module handler + */ + void* getHandlerModule(const char* name) const noexcept(false); + /** + * @throws std::runtime_error if the module already enabled + * @throws std::runtime_error if the module isn't found + * @throws std::runtime_error if the module doesn't contain InitModule or StartModule + * @retuen module handler + */ + void* enableModule(const char* name) noexcept(false); + /** + * @throws std::runtime_error if the module isn't found + * @throws std::runtime_error module isn't contain QuitModule function + */ + void disableModule(const char* name) noexcept(false); + + const std::string& getBaseWorldName() const { return base_world; } +}; diff --git a/Core/CoreInstance/Linux.cpp b/Core/CoreInstance/Linux.cpp new file mode 100644 index 0000000..7a2ad17 --- /dev/null +++ b/Core/CoreInstance/Linux.cpp @@ -0,0 +1,73 @@ +#ifdef __linux__ + +#include "SystemCalls.h" + +#include +#include +#include + +#include +#include + +int System::getCountCPU() +{ + int num_cores = sysconf(_SC_NPROCESSORS_ONLN); + if(num_cores <= 0) + throw std::runtime_error("Fail get number of cores"); + + return num_cores; +} + +unsigned long long System::getMemorySize() +{ + struct sysinfo info{}; + if(sysinfo(&info) == -1) + std::runtime_error("Fail get system info"); + return info.totalram; +} + +void* System::InitModule(const char* ModuleName, CoreInstance& core) +{ + std::string fileName = "./lib"; + fileName += ModuleName; + fileName += ".so"; + + void* handler = dlopen(fileName.c_str(), RTLD_NOW); + if(handler == nullptr) + throw std::runtime_error("Module not found"); + + void(*init)(CoreInstance&) = reinterpret_cast(dlsym(handler, "InitModule")); + if(init == nullptr) + throw std::runtime_error("Module not contains InitModule"); + init(core); + + return handler; +} + +void System::StartModule(void* handler) +{ + void(*start)() = reinterpret_cast(dlsym(handler, "StartModule")); + if(start == nullptr) + throw std::runtime_error("Module not contins StartModule"); + start(); +} + +void System::StopModule(void* handler) +{ + void(*stop)() = reinterpret_cast(dlsym(handler, "StopModule")); + if(stop == nullptr) + std::runtime_error("Module mot contins StopModule"); + stop(); + dlclose(handler); +} + +void System::QuitModule(void* handler) +{ + void(*quit)() = reinterpret_cast(dlsym(handler, "QuitModule")); + if(quit == nullptr) + throw std::runtime_error("Module not contains QuitModule"); + quit(); + dlclose(handler); +} + +#endif diff --git a/Core/CoreInstance/SystemCalls.h b/Core/CoreInstance/SystemCalls.h new file mode 100644 index 0000000..b2a0715 --- /dev/null +++ b/Core/CoreInstance/SystemCalls.h @@ -0,0 +1,37 @@ +#pragma once + +class CoreInstance; + +namespace System { + /** + * @throws std::exception if the operation finished with failed + * @return count CPU + */ + int getCountCPU(); + unsigned long long getMemorySize(); + + /** + * Load module and init handler + * @throws std::runtime_error if module not found + * @return module handler + */ + void* InitModule(const char* ModuleName, CoreInstance& core); + + /** + * Start module + * @throws std::runtime_error if module not contains StartModule function + */ + void StartModule(void* handler) noexcept(false); + + /* + * Stop the module when unload there + * @throws std::runtime_error if module not contains StopModule function + */ + void StopModule(void* handler) noexcept(false); + + /** + * Call stop module + * @throws std::runtime_error if module not contains QuitModule function + */ + void QuitModule(void* handler) noexcept(false); +} diff --git a/Core/CoreInstance/Windows.cpp b/Core/CoreInstance/Windows.cpp new file mode 100644 index 0000000..b926e23 --- /dev/null +++ b/Core/CoreInstance/Windows.cpp @@ -0,0 +1,87 @@ +#ifdef _WIN32 + +#include "SystemCalls.h" + +#include + +#include +#include +#include +#include + +int System::getCountCPU() { + DWORD len = 0; + GetLogicalProcessorInformation(nullptr, &len); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + + std::vector buffer(len); + if (!GetLogicalProcessorInformation(buffer.data(), &len)) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + + DWORD count = len / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + int physicalCoreCount = 0; + + for (DWORD i = 0; i < count; ++i) { + if (buffer[i].Relationship == RelationProcessorCore) { + physicalCoreCount++; + } + } + + return physicalCoreCount; +} + +unsigned long long System::getMemorySize() { + MEMORYSTATUSEX memStatus{}; + memStatus.dwLength = sizeof(memStatus); + + GlobalMemoryStatusEx(&memStatus); + return memStatus.ullTotalPhys; +} + +void* System::InitModule(const char* ModuleName, CoreInstance& core) { + std::string fileName = ModuleName; + fileName += ".dll"; + + HMODULE hModule = LoadLibraryA(fileName.c_str()); + if(hModule == nullptr) + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + + void(*load)(CoreInstance&) = reinterpret_cast(GetProcAddress(hModule, "InitModule")); + if(load == nullptr) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + load(core); + + return hModule; +} + +void System::StartModule(void* handler) { + void(*start)() = reinterpret_cast(GetProcAddress(static_cast(handler), "StartModule")); + if(start == nullptr) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + start(); +} + +void System::StopModule(void* handler) { + void(*stop)() = reinterpret_cast(GetProcAddress(static_cast(handler), "StopModule")); + if(stop == nullptr) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + stop(); + FreeLibrary(static_cast(handler)); +} + +void System::QuitModule(void* handler) { + void(*quit)() = reinterpret_cast(GetProcAddress(static_cast(handler), "QuitModule")); + if(quit == nullptr) { + throw std::runtime_error(std::to_string(GetLastError()).c_str()); + } + quit(); + FreeLibrary(static_cast(handler)); +} + +#endif \ No newline at end of file diff --git a/Core/CoreInstance/main.cpp b/Core/CoreInstance/main.cpp new file mode 100644 index 0000000..b00506f --- /dev/null +++ b/Core/CoreInstance/main.cpp @@ -0,0 +1,16 @@ +#include + +#include "CoreInstance/CoreInstance.h" + +int main(int argc, char* argv[]) { + try { + CoreInstance core; + std::cout << "CPU: " << core.getCountCPU() << std::endl; + std::cout << "Memory: " << core.getMemorySize() << std::endl; + core.start(); + } catch(std::exception& e) { + std::cout << "[!] Unknown error (" << e.what() << ")\n"; + return -1; + } + return 0; +} diff --git a/Core/Game/Actors/Actor.cpp b/Core/Game/Actors/Actor.cpp new file mode 100644 index 0000000..99d7f43 --- /dev/null +++ b/Core/Game/Actors/Actor.cpp @@ -0,0 +1,58 @@ +#include "Actor.h" + +#include "Game/SaveMap/SaveMap.h" +#include "Log/Log.h" + +Actor::Actor() +{ + SetType(Classes::Actor); +} + +std::shared_ptr Actor::save() noexcept +{ + std::shared_ptr save = std::make_shared("Actor"); + save->SaveObject("loc", &loc) + ->SaveObject("rot", &rot) + ->SaveObject("scale", &scale) + ->SaveListStrings("tags", std::move(tags)); + return save; +} + +void Actor::load(std::shared_ptr save) noexcept +{ + loc = *reinterpret_cast(save->GetObject("loc")); + rot = *reinterpret_cast(save->GetObject("rot")); + scale = *reinterpret_cast(save->GetObject("scale")); + tags = std::move(save->GetListString("tags")); +} + +void Actor::BeginPlay() +{ + Log("Actor::BeginPlay"); +} + +void Actor::Tick(double delta_time) +{ +} + +void Actor::SetActorLocate(const Vector3D& loc) noexcept +{ + this->loc = loc; + OnSetActorLocate.Call(loc); +} + +void Actor::SetActorRotate(const Vector3D& rot) noexcept +{ + this->rot = rot; + OnSetActorRotate.Call(rot); +} + +void Actor::AddTag(const std::string& tag) noexcept +{ + tags.push_back(tag); +} + +void Actor::RemoveTag(const std::string& tag) noexcept +{ + tags.remove(tag); +} diff --git a/Core/Game/Actors/Actor.h b/Core/Game/Actors/Actor.h new file mode 100644 index 0000000..6e5841b --- /dev/null +++ b/Core/Game/Actors/Actor.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include "RTTI.h" +#include "RTTI_Meta.h" +#include "Game/SaveMap/ISave.h" +#include "Delegate/Delegate.h" +#include "Math/Vector.h" + +GENERATE_META(Actor) + +class Actor : public ISave, public IRTTI +{ + Vector3D loc; + Vector3D rot; + Vector3D scale; + + std::list tags; +public: + Actor(); + + Delegate OnSetActorLocate; + Delegate OnSetActorRotate; + +#pragma region ISave + virtual std::shared_ptr save() noexcept override; + virtual void load(std::shared_ptr save) noexcept override; +#pragma endregion + + virtual void BeginPlay(); + virtual void Tick(double delta_time); + + /* + * Изменяет положение в пространстве + * Вызывает делегат OnSetActorLocate + */ + void SetActorLocate(const Vector3D& loc) noexcept; + inline const Vector3D& GetActorLocate() const { return loc; } + + /* + * Изменяет ориентацию в пространстве + * Вызывает делегат OnSetActorRotate + */ + void SetActorRotate(const Vector3D& rot) noexcept; + inline const Vector3D& GetActorRotate() const { return rot; } + + void AddTag(const std::string& tag) noexcept; + void RemoveTag(const std::string& tag) noexcept; + const std::list& GetTags() const { return tags; } +}; diff --git a/Core/Game/BaseObjectFactory.cpp b/Core/Game/BaseObjectFactory.cpp new file mode 100644 index 0000000..6edc75b --- /dev/null +++ b/Core/Game/BaseObjectFactory.cpp @@ -0,0 +1,10 @@ +#include "ObjectFactory.h" + +#include "Game/Actors/Actor.h" +#include "Math/Vector.h" + +std::vector base_object_factories = { + GENERATE_FACTORY_OBJECT(Actor) + GENERATE_FACTORY_OBJECT(Vector2D) + GENERATE_FACTORY_OBJECT(Vector3D) +}; \ No newline at end of file diff --git a/Core/Game/GameInstance.cpp b/Core/Game/GameInstance.cpp new file mode 100644 index 0000000..fd428be --- /dev/null +++ b/Core/Game/GameInstance.cpp @@ -0,0 +1,73 @@ +#include "GameInstance.h" + +#include +#include +#include +#include + +#include "Core/CoreInstance.h" +#include "Core/RenderEngine.h" +#include "Game/WorldFactory.h" +#include "SaveMap/SaveMap.h" +#include "Game/World/World.h" + +extern std::vector world_factories; + +GameInstance::GameInstance(CoreInstance& core) : core(core) +{ + render_engine_ = std::make_unique(core.getGameName(), 0, 0, 0, 0, 0, 0); + render_engine_->onCloseWindow.bind(this, &GameInstance::quit); + const std::string& base_world = core.getBaseWorldName(); + + // Init directories + std::filesystem::create_directories(std::filesystem::path("./Resources")); + + std::ifstream file("./Worlds/" + base_world + ".world"); + if (file.fail()) + throw std::runtime_error("Can't open world file"); + + std::string data; + std::getline(file, data); + + for (auto& factories : world_factories) + { + if (factories.world_name == base_world) + { + world = factories.factory(*this); + break; + } + } + + if (world == nullptr) + throw std::runtime_error("Can't find world"); + + world->load(std::make_shared(data)); +} + +GameInstance::~GameInstance() +{ + delete world; +} + +void GameInstance::start() +{ + render_engine_->start(); + world->BeginPlay(); + + auto first = std::chrono::steady_clock::now(); + + + while (is_running) + { + auto second = std::chrono::steady_clock::now(); + std::chrono::milliseconds delta_time = std::chrono::duration_cast(second - first); + world->Tick(delta_time.count() / 1000.0); + first = second; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } +} + +void GameInstance::quit() +{ + is_running = false; +} diff --git a/Core/Game/GameInstance.h b/Core/Game/GameInstance.h new file mode 100644 index 0000000..50415ea --- /dev/null +++ b/Core/Game/GameInstance.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +class CoreInstance; +class World; +class RenderEngine; + +#define GENERATE_FACTORY_GAME_INSTANCE(Class) \ +GameInstance* GameFactory(CoreInstance& core) { return new Class(core); } + +class GameInstance +{ + CoreInstance& core; + World* world = nullptr; + std::unique_ptr render_engine_; + + std::atomic is_running = true; + + // Create a window + // Render + // Pull events + void start(); + +public: + void quit(); + + // Init vulkan + GameInstance(CoreInstance& core); + virtual ~GameInstance(); + + World* GetWorld() const { return world; } + + friend class CoreInstance; +}; diff --git a/Core/Game/ObjectFactory.h b/Core/Game/ObjectFactory.h new file mode 100644 index 0000000..0025a4b --- /dev/null +++ b/Core/Game/ObjectFactory.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +class ISave; + +struct ObjectFactory +{ + const char* name; + ISave*(*factory)(); +}; + +// Создаёт список фабрик объектов +#define FACTORIES_LIST std::vector factories = + +// Создаёт фабрику для класса +#define GENERATE_FACTORY_OBJECT(Class) ObjectFactory{#Class, []()->ISave* { return new Class(); }}, + +/* + * Все классы или их родители в этом списке + * должны реализовывать инетфейс ISave. + * Пример использования: + +FACTORIES_LIST { + GENERATE_FACTORY_OBJECT(SometimeClass_One) + GENERATE_FACTORY_OBJECT(SometimeClass_Two) +}; + */ diff --git a/Core/Game/Resource/IResource.h b/Core/Game/Resource/IResource.h new file mode 100644 index 0000000..ac07b2d --- /dev/null +++ b/Core/Game/Resource/IResource.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "RTTI_Meta.h" + +GENERATE_META(IResource); +class IResource +{ +public: + virtual std::string serialize() = 0; + virtual void deserialize(const std::string& data) = 0; +}; \ No newline at end of file diff --git a/Core/Game/Resource/Resource.cpp b/Core/Game/Resource/Resource.cpp new file mode 100644 index 0000000..e96bbb6 --- /dev/null +++ b/Core/Game/Resource/Resource.cpp @@ -0,0 +1,142 @@ +#include "Resource.h" + +#include +#include +#include + +std::mutex Resource::mStringResource; +const char* Resource::StringResources = "./Resources/string.res"; + +std::mutex Resource::mIntResource; +const char* Resource::IntResources = "./Resources/int.res"; + +std::mutex Resource::mLongResource; +const char* Resource::LongResources = "./Resources/long.res"; + +std::mutex Resource::mDoubleResource; +const char* Resource::DoubleResources = "./Resources/double.res"; + +std::mutex Resource::mObjectResource; +const char* Resource::ObjectResources = "./Resources/object.res"; + +std::string Resource::GetResourceData(const char* name, const char* file_name) +{ + size_t size_name = strlen(name); + std::ifstream file(file_name); + if (!file.is_open()) + throw std::runtime_error("Can't open resource file"); + + std::string line; + while (std::getline(file, line)) + { + if (line.find(name) == 0 && line[size_name] == ':') + { + size_t begin = size_name + 2; + file.close(); + return line.substr(begin, line.length() - begin); + } + } + + file.close(); + throw std::runtime_error("Can't find resource"); +} + +void Resource::WriteResource(const char* name, const char* file_name, const std::string& value) +{ + size_t size_name = strlen(name); + bool isFind = false; + std::string buffer; + std::ifstream file(file_name); + + std::string line; + while (std::getline(file, line)) + { + if (line.find(name) == 0 && line[size_name] == ':') + { + isFind = true; + line.resize(size_name + 2 + value.length()); + line.replace(size_name + 2, value.length(), value); + } + buffer += line + '\n'; + } + file.close(); + + if (!isFind) + { + buffer += name; + buffer += ": "; + buffer += value; + buffer += '\n'; + } + + std::ofstream drop_file(file_name, std::ios::trunc); + drop_file << buffer; + drop_file.close(); +} + +void Resource::SetString(const char* name, const char* value) +{ + mStringResource.lock(); + WriteResource(name, StringResources, std::string(value)); + mStringResource.unlock(); +} + +void Resource::SetInt(const char* name, int value) +{ + mIntResource.lock(); + WriteResource(name, IntResources, std::to_string(value)); + mIntResource.unlock(); +} + +void Resource::SetLong(const char* name, long long value) +{ + mLongResource.lock(); + WriteResource(name, LongResources, std::to_string(value)); + mLongResource.unlock(); +} + +void Resource::SetDouble(const char* name, double value) +{ + mDoubleResource.lock(); + WriteResource(name, DoubleResources, std::to_string(value)); + mDoubleResource.unlock(); +} + +void Resource::SetObject(const char* name, IResource* object) +{ + mObjectResource.lock(); + WriteResource(name, ObjectResources, object->serialize()); + mObjectResource.unlock(); +} + +std::string Resource::GetString(const char* name) +{ + mStringResource.lock(); + std::string data = GetResourceData(name, StringResources); + mStringResource.unlock(); + return data; +} + +int Resource::GetInt(const char* name) +{ + mIntResource.lock(); + int data = std::stoi(GetResourceData(name, IntResources)); + mIntResource.unlock(); + return data; +} + +long long Resource::GetLong(const char* name) +{ + mLongResource.lock(); + long long data = std::stoll(GetResourceData(name, LongResources)); + mLongResource.unlock(); + return data; +} + +double Resource::GetDouble(const char* name) +{ + mDoubleResource.lock(); + double data = std::stod(GetResourceData(name, DoubleResources)); + mDoubleResource.unlock(); + return data; +} diff --git a/Core/Game/Resource/Resource.h b/Core/Game/Resource/Resource.h new file mode 100644 index 0000000..44d08e9 --- /dev/null +++ b/Core/Game/Resource/Resource.h @@ -0,0 +1,59 @@ +#pragma once + +#include "IResource.h" +#include +#include +#include + +class Resource final +{ + static std::mutex mStringResource; + static const char* StringResources; + + static std::mutex mIntResource; + static const char* IntResources; + + static std::mutex mLongResource; + static const char* LongResources; + + static std::mutex mDoubleResource; + static const char* DoubleResources; + + static std::mutex mObjectResource; + static const char* ObjectResources; + + // throw: std::runtime_error("Can't find resource") если ресурса не существует + static std::string GetResourceData(const char* name, const char* file_name); + + static void WriteResource(const char* name, const char* file_name, const std::string& value); + +public: + /* + * Устанавливает значение в ресурс + * Не бросают исключений + */ + static void SetString(const char* name, const char* value); + static void SetInt(const char* name, int value); + static void SetLong(const char* name, long long value); + static void SetDouble(const char* name, double value); + static void SetObject(const char* name, IResource* object); + + /* + * Метуды получения значений по ключу + * throw: std::runtime_error("Can't find resource") если ресурса не существует + */ + static std::string GetString(const char* name); + static int GetInt(const char* name); + static long long GetLong(const char* name); + static double GetDouble(const char* name); + template + static T* GetObject(const char* name) + { + mObjectResource.lock(); + std::string data = GetResourceData(name, ObjectResources); + mObjectResource.unlock(); + T* object = new T(); + object->deserialize(data); + return object; + } +}; diff --git a/Core/Game/SaveMap/ISave.h b/Core/Game/SaveMap/ISave.h new file mode 100644 index 0000000..02c3f95 --- /dev/null +++ b/Core/Game/SaveMap/ISave.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +class SaveMap; + +class ISave +{ +public: + virtual ~ISave() = default; + virtual std::shared_ptr save() = 0; + virtual void load(std::shared_ptr save) = 0; +}; \ No newline at end of file diff --git a/Core/Game/SaveMap/SaveMap.cpp b/Core/Game/SaveMap/SaveMap.cpp new file mode 100644 index 0000000..97373f7 --- /dev/null +++ b/Core/Game/SaveMap/SaveMap.cpp @@ -0,0 +1,587 @@ +#include "SaveMap.h" + +#include +#include +#include +#include "Game/ObjectFactory.h" + +extern std::vector factories; +extern std::vector base_object_factories; + +ISave* SaveMap::MakeObjectByName(const std::string& name) +{ + for (ObjectFactory& factory : base_object_factories) + { + if (name == factory.name) { + return factory.factory(); + } + } + for (ObjectFactory& factory : factories) + { + if (name == factory.name) { + return factory.factory(); + } + } + throw std::runtime_error("Object not found"); +} + +SaveMap::SaveMap(const char* class_name) : class_name(class_name) +{ + if (class_name == nullptr) + throw std::runtime_error("Can't create save map with null name"); +} + +SaveMap::SaveMap(const std::string& json_data) +{ + for (size_t i = 0; i < json_data.length() - 1;) + { + size_t key_begin = i = json_data.find_first_of('\"', i) + 1; + size_t key_end = i = json_data.find_first_of('\"', i); + + std::string name = json_data.substr(key_begin, key_end - key_begin); + + if (class_name.empty()) + { + size_t begin = json_data.find(':', i) + 2; + size_t end = i = json_data.find('\"', begin); + i++; + class_name = json_data.substr(begin, end - begin); + continue; + } + + if (name[0] == 'i') { + size_t begin = json_data.find(':', i) + 1; + size_t end = i = json_data.find_first_of(",}", i); + i++; + std::string value = json_data.substr(begin, end - begin); + save_long[name.c_str() + 1] = std::stoll(value); + } else if (name[0] == 'd') + { + size_t begin = json_data.find(':', i) + 1; + size_t end = i = json_data.find_first_of(",}", i); + i++; + std::string value = json_data.substr(begin, end - begin); + save_double[name.c_str() + 1] = std::stod(value); + } else if (name[0] == 's') + { + size_t begin = json_data.find(':', i) + 2; + size_t end = i = json_data.find('\"', begin); + i++; + std::string value = json_data.substr(begin, end - begin); + save_string[name.c_str() + 1] = value; + } else if (name[0] == 'o') + { + size_t begin = i = json_data.find('{', i); + int open = 1, close = 0; + while (close < open) + { + i++; + if (json_data[i] == '{') + open++; + else if (json_data[i] == '}') + close++; + } + + size_t end = i; + i++; + + std::string object_json = json_data.substr(begin, end - begin + 1); + + begin = object_json.find(':') + 2; + end = object_json.find('\"', begin); + + std::string object_name = object_json.substr(begin, end - begin); + ISave* object_ptr = MakeObjectByName(object_name); + + object_ptr->load(std::make_shared(object_json)); + save_objects[name.c_str() + 1] = object_ptr; + } else if (name[0] == 'v') + { + std::string key = name.substr(2, name.length() - 2); + + size_t begin_arr = i = json_data.find('[', i); + int open = 1, close = 0; + while (close < open) + { + i++; + if (json_data[i] == '[') + open++; + else if (json_data[i] == ']') + close++; + } + size_t end_arr = i; + i++; + + std::string arr_data = json_data.substr(begin_arr, end_arr - begin_arr + 1); + if (name[1] == 'i') + { + save_vector_integer[key] = std::vector(); + if (arr_data.length() == 2) + continue; + std::vector& vector_integer = save_vector_integer[key]; + + size_t j = 1; + while (j < arr_data.length()) + { + size_t end_val = arr_data.find_first_of(",]", j); + vector_integer.push_back(std::stoll(arr_data.substr(j, end_val - j))); + j = end_val + 1; + } + } else if (name[1] == 'd') + { + save_vector_double[key] = std::vector(); + if (arr_data.length() == 2) + continue; + std::vector& vector_double = save_vector_double[key]; + + size_t j = 1; + while (j < arr_data.length()) + { + size_t end_val = arr_data.find_first_of(",]", j); + vector_double.push_back(std::stod(arr_data.substr(j, end_val - j))); + j = end_val + 1; + } + } else if (name[1] == 's') + { + save_vector_strings[key] = std::vector(); + if (arr_data.length() == 2) + continue; + std::vector& vector_strings = save_vector_strings[key]; + + size_t j = 2; + while (j < arr_data.length()) + { + size_t begin = j; + size_t end = j = arr_data.find('\"', begin + 1); + std::string value = arr_data.substr(begin, end - begin); + vector_strings.push_back(value); + j += 3; + } + } else if (name[1] == 'o') + { + save_vector_objects[key] = std::vector(); + if (arr_data.length() == 2) + continue; + std::vector& vector_object = save_vector_objects[key]; + + size_t j = 1; + while (j < arr_data.length()) + { + size_t beg_val = j; + open = 1; + close = 0; + while (close < open) + { + j++; + if (arr_data[j] == '{') + open++; + else if (arr_data[j] == '}') + close++; + } + size_t end_val = j; + j++; + std::string object_json = arr_data.substr(beg_val, end_val - beg_val + 1); + + size_t begin_name = object_json.find(':') + 2; + size_t end_name = object_json.find('\"', begin_name); + + std::string object_name = object_json.substr(begin_name, end_name - begin_name); + ISave* object_ptr = MakeObjectByName(object_name); + + object_ptr->load(std::make_shared(object_json)); + vector_object.push_back(object_ptr); + + j = arr_data.find_first_of(",]", j) + 1; + } + } + } + else if (name[0] == 'l') + { + std::string key = name.substr(2, name.length() - 2); + + size_t begin_arr = i = json_data.find('[', i); + int open = 1, close = 0; + while (close < open) + { + i++; + if (json_data[i] == '[') + open++; + else if (json_data[i] == ']') + close++; + } + size_t end_arr = i; + i++; + + std::string arr_data = json_data.substr(begin_arr, end_arr - begin_arr + 1); + if (name[1] == 'i') + { + save_list_integer[key] = std::list(); + if (arr_data.length() == 2) + continue; + std::list& list_integer = save_list_integer[key]; + + size_t j = 1; + while (j < arr_data.length()) + { + size_t end_val = arr_data.find_first_of(",]", j); + list_integer.push_back(std::stoll(arr_data.substr(j, end_val - j))); + j = end_val + 1; + } + } + else if (name[1] == 'd') + { + save_list_double[key] = std::list(); + if (arr_data.length() == 2) + continue; + std::list& list_double = save_list_double[key]; + + size_t j = 1; + while (j < arr_data.length()) + { + size_t end_val = arr_data.find_first_of(",]", j); + list_double.push_back(std::stod(arr_data.substr(j, end_val - j))); + j = end_val + 1; + } + } else if (name[1] == 's') + { + save_list_strings[key] = std::list(); + if (arr_data.length() == 2) + continue; + std::list& list_strings = save_list_strings[key]; + + size_t j = 2; + while (j < arr_data.length()) + { + size_t begin = j; + size_t end = j = arr_data.find('\"', begin + 1); + std::string value = arr_data.substr(begin, end - begin); + list_strings.push_back(value); + j += 3; + } + } else if (name[1] == 'o') + { + save_list_objects[key] = std::list(); + if (arr_data.length() == 2) + continue; + std::list& list_object = save_list_objects[key]; + + size_t j = 1; + while (j < arr_data.length()) + { + size_t beg_val = j; + open = 1; + close = 0; + while (close < open) + { + j++; + if (arr_data[j] == '{') + open++; + else if (arr_data[j] == '}') + close++; + } + size_t end_val = j; + j++; + std::string object_json = arr_data.substr(beg_val, end_val - beg_val + 1); + + size_t begin_name = object_json.find(':') + 2; + size_t end_name = object_json.find('\"', begin_name); + + std::string object_name = object_json.substr(begin_name, end_name - begin_name); + ISave* object_ptr = MakeObjectByName(object_name); + + object_ptr->load(std::make_shared(object_json)); + list_object.push_back(object_ptr); + + j = arr_data.find_first_of(",]", j) + 1; + } + } + } + else if (name == "Parent parameters") { + size_t parent_begin = i = json_data.find('{', i); + int open = 1, close = 0; + while (close < open) + { + i++; + if (json_data[i] == '{') + open++; + else if (json_data[i] == '}') + close++; + } + + size_t parent_end = i; + i++; + + std::string str = json_data.substr(parent_begin, parent_end - parent_begin + 1); + parent = std::make_shared(str); + } + } +} + +std::shared_ptr SaveMap::SaveInteger(const char* name, int value) noexcept +{ + save_long[name] = value; + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveDouble(const char* name, double value) noexcept +{ + save_double[name] = value; + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveString(const char* name, const std::string& value) noexcept +{ + save_string[name] = value; + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveObject(const char* name, ISave* object) noexcept +{ + save_objects[name] = object; + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveVectorInteger(const char* name, std::vector&& value) noexcept +{ + save_vector_integer[name] = std::move(value); + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveVectorDouble(const char* name, std::vector&& value) noexcept +{ + save_vector_double[name] = std::move(value); + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveVectorStrings(const char* name, std::vector&& value) noexcept +{ + save_vector_strings[name] = std::move(value); + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveVectorObject(const char* name, std::vector&& value) noexcept +{ + save_vector_objects[name] = std::move(value); + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveListInteger(const char* name, std::list&& value) noexcept +{ + save_list_integer[name] = std::move(value); + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveListDouble(const char* name, std::list&& value) noexcept +{ + save_list_double[name] = std::move(value); + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveListStrings(const char* name, std::list&& value) noexcept +{ + save_list_strings[name] = std::move(value); + return std::shared_ptr(this); +} + +std::shared_ptr SaveMap::SaveListObject(const char* name, std::list&& value) noexcept +{ + save_list_objects[name] = std::move(value); + return std::shared_ptr(this); +} + +long long SaveMap::GetInteger(const char* name) +{ + return save_long[name]; +} + +double SaveMap::GetDouble(const char* name) +{ + return save_double[name]; +} + +const char* SaveMap::GetString(const char* name) +{ + return save_string[name].c_str(); +} + +ISave* SaveMap::GetObject(const char* name) +{ + return save_objects[name]; +} + +std::vector& SaveMap::GetVectorInteger(const char* name) +{ + return save_vector_integer[name]; +} + +std::vector& SaveMap::GetVectorDouble(const char* name) +{ + return save_vector_double[name]; +} + +std::vector SaveMap::GetVectorString(const char* name) +{ + return save_vector_strings[name]; +} + +std::vector& SaveMap::GetVectorObject(const char* name) +{ + return save_vector_objects[name]; +} + +std::list& SaveMap::GetListInteger(const char* name) +{ + return save_list_integer[name]; +} + +std::list& SaveMap::GetListDouble(const char* name) +{ + return save_list_double[name]; +} + +std::list& SaveMap::GetListString(const char* name) +{ + return save_list_strings[name]; +} + +std::list& SaveMap::GetListObject(const char* name) +{ + return save_list_objects[name]; +} + +std::string SaveMap::serialize() noexcept +{ + std::string buffer = R"({"Class name":")" + class_name + '\"'; + + for (auto& [key, value] : save_long) + buffer += ",\"i" + key + "\":" + std::to_string(value); + + for (auto& [key, value] : save_double) + buffer += ",\"d" + key + "\":" + std::to_string(value); + + for (auto& [key, value] : save_string) + buffer += ",\"s" + key + "\":\"" + value + '\"'; + + for (auto& [key, value] : save_objects) + buffer += ",\"o" + key + "\":\"" + value->save()->serialize(); + + for (auto& [key, value] : save_vector_integer) + { + if (value.empty()) + { + buffer += ",\"vi" + key + "\":[]"; + continue; + } + buffer += ",\"vi" + key + "\":["; + for (auto& item : value) + buffer += std::to_string(item) + ','; + buffer.replace(buffer.length() - 1, buffer.length() - 1, "]"); + } + + for (auto& [key, value] : save_vector_double) + { + if (value.empty()) + { + buffer += ",\"vd" + key + "\":[]"; + continue; + } + buffer += ",\"vd" + key + "\":["; + for (auto& item : value) + buffer += std::to_string(item) + ','; + buffer.replace(buffer.length() - 1, buffer.length() - 1, "]"); + } + + for (auto& [key, value] : save_vector_objects) + { + if (value.empty()) + { + buffer += ",\"vo" + key + "\":[]"; + continue; + } + buffer += ",\"vo" + key + "\":["; + for (auto& item : value) + buffer += item->save()->serialize() + ','; + buffer.replace(buffer.length() - 1, buffer.length() - 1, "]"); + } + + for (auto& [key, value] : save_vector_strings) + { + if (value.empty()) + { + buffer += ",\"vs" + key + "\":[]"; + continue; + } + buffer += ",\"vs" + key + "\":["; + for (auto& item : value) + buffer += '\"' + item + "\","; + buffer.replace(buffer.length() - 1, buffer.length() - 1, "]"); + } + + for (auto& [key, value] : save_list_integer) + { + if (value.empty()) + { + buffer += ",\"li" + key + "\":[]"; + continue; + } + buffer += ",\"li" + key + "\":["; + for (auto& item : value) + buffer += std::to_string(item) + ','; + buffer.replace(buffer.length() - 1, buffer.length() - 1, "]"); + } + + for (auto& [key, value] : save_list_double) + { + if (value.empty()) + { + buffer += ",\"ld" + key + "\":[]"; + continue; + } + buffer += ",\"ld" + key + "\":["; + for (auto& item : value) + buffer += std::to_string(item) + ','; + buffer.replace(buffer.length() - 1, buffer.length() - 1, "]"); + } + + for (auto& [key, value] : save_list_objects) + { + if (value.empty()) + { + buffer += ",\"lo" + key + "\":[]"; + continue; + } + buffer += ",\"lo" + key + "\":["; + for (auto& item : value) + buffer += item->save()->serialize() + ','; + buffer.replace(buffer.length() - 1, buffer.length() - 1, "]"); + } + + for (auto& [key, value] : save_list_strings) + { + if (value.empty()) + { + buffer += ",\"ls" + key + "\":[]"; + continue; + } + buffer += ",\"ls" + key + "\":["; + for (auto& item : value) + buffer += '\"' + item + "\","; + buffer.replace(buffer.length() - 1, buffer.length() - 1, "]"); + } + + if (parent != nullptr) { + buffer += ",\"Parent parameters\":" + parent->serialize() + '}'; + } else + buffer += '}'; + + return buffer; +} + +SaveMap* SaveMap::connect_to(std::shared_ptr parent) +{ + if (this->parent != nullptr) + throw std::runtime_error("Can't connect to second parent"); + + this->parent = parent; + return this; +} diff --git a/Core/Game/SaveMap/SaveMap.h b/Core/Game/SaveMap/SaveMap.h new file mode 100644 index 0000000..1614fc4 --- /dev/null +++ b/Core/Game/SaveMap/SaveMap.h @@ -0,0 +1,77 @@ +#pragma once + +#include "ISave.h" + +#include +#include +#include +#include +#include + +class SaveMap final +{ + std::string class_name; + std::shared_ptr parent; + + // Индивидуальные значения + std::map save_long; + std::map save_double; + std::map save_string; + std::map save_objects; + + // Массивы + std::map> save_vector_integer; + std::map> save_vector_double; + std::map> save_vector_objects; + std::map> save_vector_strings; + + // Связаные списки + std::map> save_list_integer; + std::map> save_list_double; + std::map> save_list_objects; + std::map> save_list_strings; + + static ISave* MakeObjectByName(const std::string& name); + +public: + // Создаёт пустой объект для заполнения + SaveMap(const char* class_name); + // Токенезирут JSON строку + SaveMap(const std::string& json_data); + + // Методы созранения значений + std::shared_ptr SaveInteger(const char* name, int value) noexcept; + std::shared_ptr SaveDouble(const char* name, double value) noexcept; + std::shared_ptr SaveString(const char* name, const std::string& value) noexcept; + std::shared_ptr SaveObject(const char* name, ISave* object) noexcept; + std::shared_ptr SaveVectorInteger(const char* name, std::vector&& value) noexcept; + std::shared_ptr SaveVectorDouble(const char* name, std::vector&& value) noexcept; + std::shared_ptr SaveVectorStrings(const char* name, std::vector&& value) noexcept; + std::shared_ptr SaveVectorObject(const char* name, std::vector&& value) noexcept; + std::shared_ptr SaveListInteger(const char* name, std::list&& value) noexcept; + std::shared_ptr SaveListDouble(const char* name, std::list&& value) noexcept; + std::shared_ptr SaveListStrings(const char* name, std::list&& value) noexcept; + std::shared_ptr SaveListObject(const char* name, std::list&& value) noexcept; + + // Методы получения значенй + long long GetInteger(const char* name); + double GetDouble(const char* name); + const char* GetString(const char* name); + ISave* GetObject(const char* name); + std::vector& GetVectorInteger(const char* name); + std::vector& GetVectorDouble(const char* name); + std::vector GetVectorString(const char* name); + std::vector& GetVectorObject(const char* name); + std::list& GetListInteger(const char* name); + std::list& GetListDouble(const char* name); + std::list& GetListString(const char* name); + std::list& GetListObject(const char* name); + + std::shared_ptr getParent() const { return parent; } + const std::string& getClassName() const { return class_name; } + + // Собирает все токены в JSON объект + std::string serialize() noexcept; + + SaveMap* connect_to(std::shared_ptr parent); +}; diff --git a/Core/Game/World/World.cpp b/Core/Game/World/World.cpp new file mode 100644 index 0000000..591b539 --- /dev/null +++ b/Core/Game/World/World.cpp @@ -0,0 +1,38 @@ +#include "World.h" + +#include "Game/SaveMap/SaveMap.h" + +World::World(GameInstance& game_instance) : game_instance(game_instance) +{ +} + +World::~World() +{ + for (auto& actor : actors) + delete actor; + actors.clear(); +} + +void World::BeginPlay() +{ + for (auto& i : actors) + i->BeginPlay(); +} + +void World::Tick(double delta_time) +{ + for (auto& i : actors) + i->Tick(delta_time); +} + +std::shared_ptr World::save() +{ + std::shared_ptr save = std::make_shared("World"); + save->SaveListObject("Actors", std::move(reinterpret_cast&>(actors))); + return save; +} + +void World::load(std::shared_ptr save) +{ + actors = std::move(reinterpret_cast&>(save->GetListObject("Actors"))); +} \ No newline at end of file diff --git a/Core/Game/World/World.h b/Core/Game/World/World.h new file mode 100644 index 0000000..1b5f402 --- /dev/null +++ b/Core/Game/World/World.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include + +#include "RTTI.h" +#include "Game/Actors/Actor.h" + +class GameInstance; + +class World : public ISave +{ + GameInstance& game_instance; + + // Actors only + std::list actors; + +public: + World(GameInstance& game_instance); + ~World() override; + + virtual void BeginPlay(); + virtual void Tick(double delta_time); + + GameInstance& GetGameInstance() const { return game_instance; } + +#pragma region ISave + std::shared_ptr save() override; + void load(std::shared_ptr save) override; +#pragma endregion + + template> + Container GetActorsByClass() + { + Container list; + for (auto i = actors.cbegin(); i != actors.cend(); ++i) + { + if (RTTI::IsA(*i)) + list.push_back(*i); + } + return list; + } + + template + T* SpawnActorFormClass(const Vector3D& loc = { 0, 0, 0 }, const Vector3D& rot = { 0, 0, 0 }) + { + T* object = new T(); + object->SetActorLocate(loc); + object->SetActorRotate(rot); + actors.push_back(object); + return object; + } + + template> + Container GetActorsByTag(std::string tag) + { + Container container; + + for (auto& i : actors) + { + const std::list& tags = i->GetTags(); + for (auto& j : tags) + { + if (j == tag) + container.push_back(i); + } + } + + return container; + } +}; + diff --git a/Core/Game/WorldFactory.h b/Core/Game/WorldFactory.h new file mode 100644 index 0000000..9f29920 --- /dev/null +++ b/Core/Game/WorldFactory.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +class GameInstance; +class World; + +struct WorldFactory +{ + const char* world_name; + World*(*factory)(GameInstance&); +}; + +// Создаёт ассоцеативный список +// В нём соспоставляются имина уровней и фабрики их классов +#define WORLDS_LIST std::vector world_factories = + +// Генерирует элемент списка +#define GENERATE_WORLD_FACTORY(Class, WorldName) WorldFactory{#WorldName, [](GameInstance& game_insance)->World* { return new Class(game_insance); }}, + +/* + * Пример использования: + +WORLDS_LIST { + GENERATE_WORLD_FACTORY(Sometime_class_world_One, WorldName_One) + GENERATE_WORLD_FACTORY(Sometime_class_world_Two, WorldName_Two) +}; + */ \ No newline at end of file diff --git a/Core/Log/Log.cpp b/Core/Log/Log.cpp new file mode 100644 index 0000000..2b0c4e7 --- /dev/null +++ b/Core/Log/Log.cpp @@ -0,0 +1,40 @@ +#include "Log.h" + +#include +#include + +std::mutex mOutput; + +#ifdef _DEBUG +void Log(const char* msg) +{ + std::lock_guard lock(mOutput); + std::cout << msg << '\n'; +} + +void Log(const std::string& msg) +{ + std::lock_guard lock(mOutput); + std::cout << msg << '\n'; +} +#else +void Log(const char* msg) +{ +} + +void Log(const std::string& msg) +{ +} +#endif + +void Message(const char* msg) +{ + std::lock_guard lock(mOutput); + std::cout << msg << '\n'; +} + +void Message(const std::string& msg) +{ + std::lock_guard lock(mOutput); + std::cout << msg << '\n'; +} \ No newline at end of file diff --git a/Core/Log/Log.h b/Core/Log/Log.h new file mode 100644 index 0000000..8027a10 --- /dev/null +++ b/Core/Log/Log.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +void Log(const char* msg); +void Log(const std::string& msg); + +void Message(const char* msg); +void Message(const std::string& msg); \ No newline at end of file diff --git a/Core/Math/Vector.cpp b/Core/Math/Vector.cpp new file mode 100644 index 0000000..bac260a --- /dev/null +++ b/Core/Math/Vector.cpp @@ -0,0 +1,62 @@ +#include "Vector.h" + +#include "Game/SaveMap/SaveMap.h" + +Vector2D::Vector2D(const double x, const double y) : x(x), y(y) +{} + +Vector2D::Vector2D(const Vector3D& vec3D) : x(vec3D.x), y(vec3D.y) +{} + +Vector2D Vector2D::operator+(const Vector2D& v) const +{ + return Vector2D{ x + v.x, y + v.y }; +} + +Vector2D Vector2D::operator-(const Vector2D& v) const +{ + return Vector2D{ x - v.x, y - v.y }; +} + +std::shared_ptr Vector2D::save() +{ + std::unique_ptr save = std::make_unique("Vector2D"); + save->SaveDouble("x", x)->SaveDouble("y", y); + return save; +} + +void Vector2D::load(std::shared_ptr save) +{ + x = save->GetDouble("x"); + y = save->GetDouble("y"); +} + +Vector3D::Vector3D(const double x, const double y, const double z) : x(x), y(y), z(z) +{} + +Vector3D::Vector3D(const Vector2D& vec2D) : x(vec2D.x), y(vec2D.y) +{} + +Vector3D Vector3D::operator+(const Vector3D& v) const +{ + return Vector3D{x + v.x, y + v.y, z + v.z}; +} + +Vector3D Vector3D::operator-(const Vector3D& v) const +{ + return Vector3D{ x - v.x, y - v.y, z - v.z }; +} + +std::shared_ptr Vector3D::save() +{ + std::unique_ptr save = std::make_unique("Vector3D"); + save->SaveDouble("x", x)->SaveDouble("y", y)->SaveDouble("z", z); + return save; +} + +void Vector3D::load(std::shared_ptr save) +{ + x = save->GetDouble("x"); + y = save->GetDouble("y"); + z = save->GetDouble("z"); +} diff --git a/Core/Math/Vector.h b/Core/Math/Vector.h new file mode 100644 index 0000000..18d1dd9 --- /dev/null +++ b/Core/Math/Vector.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Game/SaveMap/ISave.h" + +struct Vector3D; + +struct Vector2D : public ISave { + double x = 0, y = 0; + + Vector2D() = default; + Vector2D(const double x, const double y); + Vector2D(const Vector3D& vec3D); + + Vector2D operator+(const Vector2D& v) const; + Vector2D operator-(const Vector2D& v) const; + + std::shared_ptr save() override; + void load(std::shared_ptr save) override; +}; + +struct Vector3D : public ISave { + double x = 0, y = 0, z = 0; + + Vector3D() = default; + Vector3D(const double x, const double y, const double z); + Vector3D(const Vector2D& vec2D); + + Vector3D operator+(const Vector3D& v) const; + Vector3D operator-(const Vector3D& v) const; + + std::shared_ptr save() override; + void load(std::shared_ptr save) override; +}; \ No newline at end of file diff --git a/Delegate/Delegate/Delegate.h b/Delegate/Delegate/Delegate.h new file mode 100644 index 0000000..1548fb8 --- /dev/null +++ b/Delegate/Delegate/Delegate.h @@ -0,0 +1,333 @@ +#pragma once + +#include + +template +class Delegate +{ + struct DelegateDataBase + { + virtual ~DelegateDataBase() = default; + DelegateDataBase* next = nullptr; + long long Hash = 0; + virtual void Call(TypeParameter) = 0; + }; + + template + struct DelegateDataForClass : DelegateDataBase + { + T* object = nullptr; + + typedef void(T::*Method)(TypeParameter); + Method method = nullptr; + + void Call(TypeParameter data) override + { + (object->*method)(data); + } + }; + + struct DelegateDataForFunction : DelegateDataBase + { + typedef void(*Func)(TypeParameter); + Func func = nullptr; + + void Call(TypeParameter data) override + { + func(data); + } + }; + + DelegateDataBase* firstElement = nullptr; + + std::mutex mDelegateList; + + static constexpr unsigned int FNV1a(const char* str, unsigned int n, unsigned int hash = 2166136261U) { + return n == 0 ? hash : FNV1a(str + 1, n - 1, (hash ^ str[0]) * 19777619U); + } +public: + template + void bind(T* object, void(T::*method)(TypeParameter)) + { + char* str = new char[sizeof(T*) + sizeof(void(T::*)(TypeParameter))]; + const char* ptrToObjectRef = reinterpret_cast(&object); + for (long unsigned int i = 0; i < sizeof(T*); ++i) + str[i] = ptrToObjectRef[i]; + + const char* ptrToMethodRef = reinterpret_cast(&method); + for (long unsigned int i = sizeof(T*); i < sizeof(T*) + sizeof(void(T::*)(TypeParameter)); ++i) + str[i] = ptrToMethodRef[i - sizeof(T*)]; + + DelegateDataForClass* data = new DelegateDataForClass; + data->next = firstElement; + data->object = object; + data->method = method; + data->Hash = FNV1a(str, sizeof(T*) + sizeof(void(T::*)(TypeParameter))); + + delete[] str; + + mDelegateList.lock(); + firstElement = data; + mDelegateList.unlock(); + } + + void bind(void(*func)(TypeParameter)) + { + char* str = new char[sizeof(void(*)(TypeParameter))]; + const char* ptrToFuncRef = reinterpret_cast(&func); + for (int i = 0; i < sizeof(void(*)(TypeParameter)); ++i) + str[i] = ptrToFuncRef[i]; + + DelegateDataForFunction* data = new DelegateDataForFunction; + data->next = firstElement; + data->func = func; + data->Hash = FNV1a(str, sizeof(func)); + delete[] str; + + mDelegateList.lock(); + firstElement = data; + mDelegateList.unlock(); + } + + template + void unbind(T* object, void(T::*method)(TypeParameter)) + { + if (firstElement == nullptr) + return; + + char* str = new char[sizeof(T*) + sizeof(void(T::*)(TypeParameter))]; + const char* ptrToObjectRef = reinterpret_cast(&object); + for (long unsigned int i = 0; i < 8; ++i) + str[i] = ptrToObjectRef[i]; + + const char* ptrToMethodRef = reinterpret_cast(&method); + for (long unsigned int i = sizeof(T*); i < sizeof(T*) + sizeof(void(T::*)(TypeParameter)); ++i) + str[i] = ptrToMethodRef[i - sizeof(T*)]; + + unsigned int Hash = FNV1a(str, sizeof(T*) + sizeof(void(T::*)(TypeParameter))); + + DelegateDataBase* iterator = firstElement; + DelegateDataBase* temp = nullptr; + while (iterator != nullptr) + { + if (iterator->Hash == Hash) + { + if (temp != nullptr) + temp->next = iterator->next; + else + firstElement = iterator->next; + + delete static_cast*>(iterator); + break; + } + + temp = iterator; + iterator = iterator->next; + } + } + + void unbind(void(*func)(TypeParameter)) + { + char* str = new char[sizeof(void(*)(TypeParameter))]; + const char* ptrToFuncRef = reinterpret_cast(&func); + for (int i = 0; i < sizeof(void(*)(TypeParameter)); ++i) + str[i] = ptrToFuncRef[i]; + + unsigned int Hash = FNV1a(str, sizeof(void(*)(TypeParameter))); + delete[] str; + + DelegateDataBase* iterator = firstElement; + DelegateDataBase* temp = nullptr; + while (iterator != nullptr) + { + if (iterator->Hash == Hash) + { + if (temp != nullptr) + temp->next = iterator->next; + else + firstElement = iterator->next; + + delete static_cast(iterator); + break; + } + + temp = iterator; + iterator = iterator->next; + } + } + + void Call(TypeParameter data) const + { + DelegateDataBase* iterator = firstElement; + while (iterator != nullptr) + { + iterator->Call(data); + iterator = iterator->next; + } + } +}; + +template<> +class Delegate +{ + struct DelegateDataBase + { + virtual ~DelegateDataBase() = default; + DelegateDataBase* next = nullptr; + long long Hash = 0; + virtual void call() = 0; + }; + + template + struct DelegateDataForClass : DelegateDataBase + { + T* object = nullptr; + + typedef void(T::*Method)(); + Method method = nullptr; + + void call() override + { + (object->*method)(); + } + }; + + struct DelegateDataForFunction : DelegateDataBase + { + typedef void(*Func)(); + Func func = nullptr; + + void call() override + { + func(); + } + }; + + DelegateDataBase* firstElement = nullptr; + + std::mutex mDelegateList; + + static constexpr unsigned int FNV1a(const char* str, unsigned int n, unsigned int hash = 2166136261U) { + return n == 0 ? hash : FNV1a(str + 1, n - 1, (hash ^ str[0]) * 19777619U); + } +public: + template + void bind(T* object, void(T::*method)()) + { + char* str = new char[sizeof(T*) + sizeof(void(T::*)())]; + const char* ptrToObjectRef = reinterpret_cast(&object); + for (int i = 0; i < sizeof(T*); ++i) + str[i] = ptrToObjectRef[i]; + + const char* ptrToMethodRef = reinterpret_cast(&method); + for (int i = sizeof(T*); i < sizeof(T*) + sizeof(void(T::*)()); ++i) + str[i] = ptrToMethodRef[i - sizeof(T*)]; + + DelegateDataForClass* data = new DelegateDataForClass; + data->next = firstElement; + data->object = object; + data->method = method; + data->Hash = FNV1a(str, sizeof(T*) + sizeof(void(T::*)())); + + delete[] str; + + mDelegateList.lock(); + firstElement = data; + mDelegateList.unlock(); + } + + void bind(void(*func)()) + { + char* str = new char[sizeof(func)]; + const char* ptrToFuncRef = reinterpret_cast(&func); + for (int i = 0; i < sizeof(void(*)()); ++i) + str[i] = ptrToFuncRef[i]; + + DelegateDataForFunction* data = new DelegateDataForFunction; + data->next = firstElement; + data->func = func; + data->Hash = FNV1a(str, 8); + delete[] str; + + mDelegateList.lock(); + firstElement = data; + mDelegateList.unlock(); + } + + template + void unbind(T* object, void(T::*method)()) + { + if (firstElement == nullptr) + return; + + char* str = new char[sizeof(T*) + sizeof(void(T::*)())]; + const char* ptrToObjectRef = reinterpret_cast(&object); + for (int i = 0; i < sizeof(T*); ++i) + str[i] = ptrToObjectRef[i]; + + const char* ptrToMethodRef = reinterpret_cast(&method); + for (int i = sizeof(T*); i < sizeof(T*) + sizeof(void(T::*)()); ++i) + str[i] = ptrToMethodRef[i - sizeof(T*)]; + + unsigned int Hash = FNV1a(str, sizeof(T*) + sizeof(void(T::*)())); + + DelegateDataBase* iterator = firstElement; + DelegateDataBase* temp = nullptr; + while (iterator != nullptr) + { + if (iterator->Hash == Hash) + { + if (temp != nullptr) + temp->next = iterator->next; + else + firstElement = iterator->next; + + delete static_cast*>(iterator); + break; + } + + temp = iterator; + iterator = iterator->next; + } + } + + void unbind(void(*func)()) + { + char* str = new char[sizeof(void(*)())]; + const char* ptrToFuncRef = reinterpret_cast(&func); + for (int i = 0; i < sizeof(void(*)()); ++i) + str[i] = ptrToFuncRef[i]; + + unsigned int Hash = FNV1a(str, sizeof(void(*)())); + delete[] str; + + DelegateDataBase* iterator = firstElement; + DelegateDataBase* temp = nullptr; + while (iterator != nullptr) + { + if (iterator->Hash == Hash) + { + if (temp != nullptr) + temp->next = iterator->next; + else + firstElement = iterator->next; + + delete static_cast(iterator); + break; + } + + temp = iterator; + iterator = iterator->next; + } + } + + void Call() const + { + DelegateDataBase* iterator = firstElement; + while (iterator != nullptr) + { + iterator->call(); + iterator = iterator->next; + } + } +}; \ No newline at end of file diff --git a/Delegate/README.md b/Delegate/README.md new file mode 100644 index 0000000..1caad4f --- /dev/null +++ b/Delegate/README.md @@ -0,0 +1,26 @@ +# Delegate +That library implement delegate for C++. + +--------Exemple------- + +#include + +#include "Delegate.h" + +class A +{ +public: + void Foo1(int var) { std::cout << "A::Foo1(" << var <<");\n"; } + void Foo2(int var) { std::cout << "A::Foo2(" << var <<");\n"; } +}; + +int main(int argc, char* argv[]) +{ + Delegate delegate; + A a; + delegate.bind(&a, &A::Foo1); + delegate.bind(&a, &A::Foo2); + delegate.unbind(&a, &A::Foo1); + delegate.Call(321); + return 0; +} diff --git a/FastRTTI/CMakeLists.txt b/FastRTTI/CMakeLists.txt new file mode 100644 index 0000000..3d4db9a --- /dev/null +++ b/FastRTTI/CMakeLists.txt @@ -0,0 +1,29 @@ +file(GLOB SRC "*.cpp" "*.h") + +add_library(FastRTTI STATIC ${SRC}) + +target_include_directories(FastRTTI + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + + +if(CMAKE_BUILD_TYPE STREQUAL "Release") + if(MSVC) + target_compile_options(FastRTTI PRIVATE /GR-) + target_link_options(FastRTTI PRIVATE /LTCG) + else() + # GCC / Clang + target_compile_options(FastRTTI PRIVATE -fno-rtti) + target_link_options(FastRTTI PRIVATE -flto) + endif() + +elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") + if(MSVC) + target_compile_options(FastRTTI PRIVATE /GR-) + else() + target_compile_options(FastRTTI PRIVATE + -g + -fno-rtti) + endif() +endif() diff --git a/FastRTTI/RTTI.cpp b/FastRTTI/RTTI.cpp new file mode 100644 index 0000000..fe252b0 --- /dev/null +++ b/FastRTTI/RTTI.cpp @@ -0,0 +1,74 @@ +#include "RTTI.h" + +void IRTTI::FastContainer::push_back(const Classes newType) +{ + if (0 == _size) + { + ++_size; + array = new Classes; + *array = newType; + } + else + { + ++_size; + Classes* NewArray = new Classes[_size]; + + long long indexObject = _size - 1; + for (long long i = 0, j = 0; i < _size - 1; ++i, ++j) + { + if (newType < array[i] && indexObject == _size - 1) + { + indexObject = j; + ++j; + } + + NewArray[j] = array[i]; + } + NewArray[indexObject] = newType; + + delete[] array; + array = NewArray; + } +} + +bool IRTTI::FastContainer::find(const Classes& Type) const +{ + long long low = 0; + long long high = _size - 1; + + while (low <= high) + { + long long mid = (low + high) / 2; + if (static_cast(array[mid]) == static_cast(Type)) + return true; + + if (static_cast(array[mid]) > static_cast(Type)) + high = mid - 1; + else + low = mid + 1; + } + + return false; +} + +IRTTI::FastContainer::~FastContainer() +{ + _size = 0; + delete[] array; +} + +void IRTTI::SetType(const Classes Type) +{ + this->type = Type; + Tree.push_back(Type); +} + +IRTTI::IRTTI() +{ + SetType(Classes::IRTTI); +} + +Classes RTTI::GetType(const IRTTI* object) +{ + return object->type; +} diff --git a/FastRTTI/RTTI.h b/FastRTTI/RTTI.h new file mode 100644 index 0000000..6e909b2 --- /dev/null +++ b/FastRTTI/RTTI.h @@ -0,0 +1,61 @@ +#pragma once + +#include "RTTI_Meta.h" + +// Interface RTTI +GENERATE_META(IRTTI) +class IRTTI +{ + // Fast array + class FastContainer final + { + long long _size = 0; + Classes* array = nullptr; + public: + long long constexpr size() const { return _size; } + void push_back(const Classes object); + bool find(const Classes& Type) const; + + ~FastContainer(); + }; + FastContainer Tree; + Classes type; +protected: + // That method set type value and add type to inheritance tree + void SetType(Classes Type); + + IRTTI(); + ~IRTTI() = default; + + friend class RTTI; +}; + +// RTTI methods +class RTTI final +{ + RTTI() = default; + +public: + template + static bool IsA(const IRTTI* object) + { + return Meta::Type == object->type; + } + + // That method realized dynamic_cast + template + static To* dyn_cast(From* object) + { + IRTTI* InterfaceOfObject = static_cast(object); + if (IsA(object)) + return static_cast(object); + + if (InterfaceOfObject->Tree.find(Meta::Type)) + return static_cast(object); + + return nullptr; + } + + // That method return real type object in the form of enum value + static Classes GetType(const IRTTI* object); +}; \ No newline at end of file diff --git a/FastRTTI/RTTI_Meta.h b/FastRTTI/RTTI_Meta.h new file mode 100644 index 0000000..a222b70 --- /dev/null +++ b/FastRTTI/RTTI_Meta.h @@ -0,0 +1,17 @@ +#pragma once + +// This macro adds meta information to a type +#define GENERATE_META(Class) \ +class Class; \ +template<> struct Meta { \ +static constexpr Classes Type = Classes::Class; \ +}; + +// Predeclared struct for meta information +template struct Meta; + +// Enum type classes +enum class Classes +{ + IRTTI, IResource, Actor, TestActor +}; diff --git a/ModuleLib/CMakeLists.txt b/ModuleLib/CMakeLists.txt new file mode 100644 index 0000000..37f27f8 --- /dev/null +++ b/ModuleLib/CMakeLists.txt @@ -0,0 +1,14 @@ +set(MODULE_LIB_NAME ModuleLib) + +file(GLOB_RECURSE SRC "*.cpp" "*.c" "*.h" "*.hpp") + +add_library(${MODULE_LIB_NAME} STATIC ${SRC}) + +target_include_directories(${MODULE_LIB_NAME} PRIVATE + ${PROJECT_SOURCE_DIR}/Core + ${PROJECT_SOURCE_DIR}/Delegate +) + +if(UNIX AND NOT APPLE) + target_compile_options(${MODULE_LIB_NAME} PRIVATE -fPIC) +endif() diff --git a/ModuleLib/ModuleInstance.cpp b/ModuleLib/ModuleInstance.cpp new file mode 100644 index 0000000..4711f8a --- /dev/null +++ b/ModuleLib/ModuleInstance.cpp @@ -0,0 +1,68 @@ +#include "ModuleInstance.h" + +#include +#include + +ModuleInstance::ModuleInstance(CoreInstance& core) : core_(core) +{ + // Init instance +} + +void ModuleInstance::start() +{ + // Create thread +} + +void ModuleInstance::stop() +{ + onStop.Call(); +} + + +extern ModuleInstance* Factory(CoreInstance&); + +static std::mutex mModuleInstance; +static ModuleInstance* module = nullptr; +static std::thread* moduleThread; + +void InitModule(CoreInstance& core) +{ + mModuleInstance.lock(); + if(module == nullptr) + module = Factory(core); + mModuleInstance.unlock(); +} + +void StartModule() +{ + mModuleInstance.lock(); + if (module != nullptr) + moduleThread = new std::thread([&] {module->start(); }); + mModuleInstance.unlock(); +} + +void StopModule() +{ + mModuleInstance.lock(); + if(module != nullptr) + { + delete module; + module = nullptr; + moduleThread->detach(); + delete moduleThread; + } + mModuleInstance.unlock(); +} + +void QuitModule() +{ + mModuleInstance.lock(); + if(module != nullptr) + { + module->stop(); + moduleThread->join(); + delete module; + module = nullptr; + } + mModuleInstance.unlock(); +} diff --git a/ModuleLib/ModuleInstance.h b/ModuleLib/ModuleInstance.h new file mode 100644 index 0000000..4cbf820 --- /dev/null +++ b/ModuleLib/ModuleInstance.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Delegate/Delegate.h" + +#ifdef _WIN32 +#define EXPORT __declspec(dllexport) +#elif __linux__ +#define EXPORT +#else +#error message("Unknow platform") +#endif +/* +* This macro protects +* module callbacks from GB +*/ +#define PROTECTION_FROM_GB(Class) \ +extern "C" {\ +EXPORT void* protect_init() {return reinterpret_cast(&InitModule);} \ +EXPORT void* protect_start() {return reinterpret_cast(&StartModule);} \ +EXPORT void* protect_stop() {return reinterpret_cast(&StopModule);} \ +EXPORT void* protect_quit() {return reinterpret_cast(&QuitModule);} \ +}\ +ModuleInstance* Factory(CoreInstance& core) { return static_cast(new Class(core)); } + + +class CoreInstance; + +class ModuleInstance { + CoreInstance& core_; + +public: + Delegate onStop; + + ModuleInstance(CoreInstance& core); + virtual ~ModuleInstance() = default; + + CoreInstance& getCore() const { return core_; } + + virtual void start(); + void stop(); +}; + +extern "C" { + // Create module instance + EXPORT void InitModule(CoreInstance& core); + + // Start module instance + EXPORT void StartModule(); + + // Destroy module instance when core unloads there + EXPORT void StopModule(); + + // Stop module when core calls this + EXPORT void QuitModule(); +} diff --git a/ProjectGenerator/CMakeLists.txt b/ProjectGenerator/CMakeLists.txt new file mode 100644 index 0000000..bcc5db7 --- /dev/null +++ b/ProjectGenerator/CMakeLists.txt @@ -0,0 +1,5 @@ +set(CMAKE_CXX_STANDARD 20) + +file(GLOB_RECURSE SRC "*.cpp" "*.c" "*.h" "*.hpp") + +add_executable(ProjectGenerator ${SRC}) \ No newline at end of file diff --git a/ProjectGenerator/ProjectFile.txt b/ProjectGenerator/ProjectFile.txt new file mode 100644 index 0000000..151a78f --- /dev/null +++ b/ProjectGenerator/ProjectFile.txt @@ -0,0 +1,32 @@ +project(${GAME_NAME}) + +file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c" "src/*.h" "src/*.hpp") + +add_executable(${GAME_NAME} ${SRC}) + +target_link_directories(${GAME_NAME} PRIVATE + $<$:$ENV{UwU_Engine}/bin/Debug> + $<$:$ENV{UwU_Engine}/bin/Release> +) + +target_link_libraries(${GAME_NAME} PRIVATE Core FastRTTI) +target_include_directories(${GAME_NAME} PRIVATE + $ENV{UwU_Engine}/include/Core + $ENV{UwU_Engine}/include/FastRTTI + $ENV{UwU_Engine}/include/Delegate +) + +if(MSVC) + set(OUTPUT_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$) +else() + set(OUTPUT_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +endif() + +add_custom_command( + TARGET ${GAME_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/main_config.conf + ${OUTPUT_PATH}/main_config.conf + COMMENT "Copying main config file" +) \ No newline at end of file diff --git a/ProjectGenerator/main.cpp b/ProjectGenerator/main.cpp new file mode 100644 index 0000000..163e838 --- /dev/null +++ b/ProjectGenerator/main.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include + +int main() +{ + std::string project_name; + std::cout << "Enter project name: "; + std::getline(std::cin, project_name); + + std::filesystem::create_directory(project_name); + std::filesystem::create_directory(project_name + "/Resources"); + std::filesystem::create_directory(project_name + "/Worlds"); + std::filesystem::create_directory(project_name + "/src"); + + std::ifstream data("ProjectFile.txt"); + std::string buffer = "cmake_minimum_required(VERSION 3.14)\nset(GAME_NAME " + project_name + ")\n"; + + std::string line; + while (std::getline(data, line)) + buffer += line + '\n'; + data.close(); + + std::ofstream out(project_name + "/CMakeLists.txt", std::ios::out); + out << buffer; + out.close(); + + std::cout << "Project created successfully!\n"; + system("pause"); +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..0affad0 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# UwU-Engine \ No newline at end of file diff --git a/TestGame/CMakeLists.txt b/TestGame/CMakeLists.txt new file mode 100644 index 0000000..bd5b200 --- /dev/null +++ b/TestGame/CMakeLists.txt @@ -0,0 +1,47 @@ +set(GAME_NAME TestGame) + +set(CMAKE_CXX_STANDARD 20) + +file(GLOB_RECURSE SRC "*.cpp" "*.c" "*.h" "*.hpp" "*.res" "*.world" "*.conf") + +add_executable(${GAME_NAME} ${SRC}) +target_link_libraries(${GAME_NAME} PRIVATE Core) +target_include_directories(${GAME_NAME} PRIVATE + ${PROJECT_SOURCE_DIR}/Core + ${PROJECT_SOURCE_DIR}/FastRTTI + ${PROJECT_SOURCE_DIR}/Delegate +) + +if(MSVC) + set(OUTPUT_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$) +else() + set(OUTPUT_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +endif() + +add_custom_command( + TARGET ${GAME_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/main_config.conf + ${OUTPUT_PATH}/main_config.conf + COMMENT "Copying main config file" +) + + +add_custom_command( + TARGET ${GAME_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/Worlds + ${OUTPUT_PATH}/Worlds + COMMENT "Copying Worlds directory" +) + +add_custom_command( + TARGET ${GAME_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/Resources + ${OUTPUT_PATH}/Resources + COMMENT "Copying Resources directory" +) diff --git a/TestGame/Resources/double.res b/TestGame/Resources/double.res new file mode 100644 index 0000000..baf74ea --- /dev/null +++ b/TestGame/Resources/double.res @@ -0,0 +1 @@ +test_double: 12345.700000 diff --git a/TestGame/Resources/int.res b/TestGame/Resources/int.res new file mode 100644 index 0000000..656b174 --- /dev/null +++ b/TestGame/Resources/int.res @@ -0,0 +1 @@ +test_int: 12345 diff --git a/TestGame/Resources/long.res b/TestGame/Resources/long.res new file mode 100644 index 0000000..46403e0 --- /dev/null +++ b/TestGame/Resources/long.res @@ -0,0 +1 @@ +test_long: 1234567890123456789 diff --git a/TestGame/Resources/string.res b/TestGame/Resources/string.res new file mode 100644 index 0000000..285c411 --- /dev/null +++ b/TestGame/Resources/string.res @@ -0,0 +1,2 @@ +test_string: Hello, Danil! +test_string2: Hello, User! diff --git a/TestGame/Worlds/TestWorld.world b/TestGame/Worlds/TestWorld.world new file mode 100644 index 0000000..7bdcf01 --- /dev/null +++ b/TestGame/Worlds/TestWorld.world @@ -0,0 +1 @@ +{"Class name":"TestWorld","dtime":5.000000,"Parent parameters":{"Class name":"World","loActors":[]}} \ No newline at end of file diff --git a/TestGame/main_config.conf b/TestGame/main_config.conf new file mode 100644 index 0000000..b49aa65 --- /dev/null +++ b/TestGame/main_config.conf @@ -0,0 +1 @@ +{"Class name":"MainConfig","sgame_name":"Test Game","sbase_world":"TestWorld","vsmodules_names":[]} \ No newline at end of file diff --git a/TestGame/src/Actors/TestActor.cpp b/TestGame/src/Actors/TestActor.cpp new file mode 100644 index 0000000..da903e9 --- /dev/null +++ b/TestGame/src/Actors/TestActor.cpp @@ -0,0 +1,9 @@ +#include "TestActor.h" + +#include "Log/Log.h" + +void TestActor::BeginPlay() +{ + Actor::BeginPlay(); + Log("TestActor::BeginPlay"); +} \ No newline at end of file diff --git a/TestGame/src/Actors/TestActor.h b/TestGame/src/Actors/TestActor.h new file mode 100644 index 0000000..ebecad9 --- /dev/null +++ b/TestGame/src/Actors/TestActor.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Game/Actors/Actor.h" + +GENERATE_META(TestActor) +class TestActor final : public Actor +{ +public: + void BeginPlay() override; +}; \ No newline at end of file diff --git a/TestGame/src/Factories/ObjectFactory.cpp b/TestGame/src/Factories/ObjectFactory.cpp new file mode 100644 index 0000000..4690e4b --- /dev/null +++ b/TestGame/src/Factories/ObjectFactory.cpp @@ -0,0 +1,7 @@ +#include "Game/ObjectFactory.h" + +#include "../Actors/TestActor.h" + +FACTORIES_LIST{ + GENERATE_FACTORY_OBJECT(TestActor) +}; diff --git a/TestGame/src/Factories/WorldFactory.cpp b/TestGame/src/Factories/WorldFactory.cpp new file mode 100644 index 0000000..74185dd --- /dev/null +++ b/TestGame/src/Factories/WorldFactory.cpp @@ -0,0 +1,7 @@ +#include "Game/WorldFactory.h" + +#include "../Worlds/TestWorld.h" + +WORLDS_LIST{ + GENERATE_WORLD_FACTORY(TestWorld, TestWorld) +}; \ No newline at end of file diff --git a/TestGame/src/TestGameInstance.cpp b/TestGame/src/TestGameInstance.cpp new file mode 100644 index 0000000..fe4677e --- /dev/null +++ b/TestGame/src/TestGameInstance.cpp @@ -0,0 +1,12 @@ +#include "TestGameInstance.h" + +#include "Game/Resource/Resource.h" +#include + +#include "Game/SaveMap/SaveMap.h" +#include "Game/World/World.h" +GENERATE_FACTORY_GAME_INSTANCE(TestGameInstance) + +TestGameInstance::TestGameInstance(CoreInstance& core) : GameInstance(core) +{ +} diff --git a/TestGame/src/TestGameInstance.h b/TestGame/src/TestGameInstance.h new file mode 100644 index 0000000..15f50f6 --- /dev/null +++ b/TestGame/src/TestGameInstance.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Game/GameInstance.h" + +class TestGameInstance : public GameInstance +{ +public: + TestGameInstance(CoreInstance& core); +}; \ No newline at end of file diff --git a/TestGame/src/Worlds/TestWorld.cpp b/TestGame/src/Worlds/TestWorld.cpp new file mode 100644 index 0000000..b40064b --- /dev/null +++ b/TestGame/src/Worlds/TestWorld.cpp @@ -0,0 +1,40 @@ +#include "TestWorld.h" + +#include "Game/GameInstance.h" +#include "Game/SaveMap/SaveMap.h" +#include "Log/Log.h" + +TestWorld::TestWorld(GameInstance& game_instance) : World(game_instance) +{} + +void TestWorld::BeginPlay() +{ + World::BeginPlay(); + Log("TestWorld::BeginPlay"); +} + +void TestWorld::Tick(double delta_time) +{ + World::Tick(delta_time); + + time += delta_time; + if (time >= 10.0) + { + Message("TestWorld: Time out"); + GetGameInstance().quit(); + } +} + +std::shared_ptr TestWorld::save() +{ + std::shared_ptr save = World::save(); + std::shared_ptr my_save = std::make_shared("TestWorld"); + my_save->SaveDouble("time", time)->connect_to(save); + return my_save; +} + +void TestWorld::load(std::shared_ptr save) +{ + World::load(save->getParent()); + time = save->GetDouble("time"); +} diff --git a/TestGame/src/Worlds/TestWorld.h b/TestGame/src/Worlds/TestWorld.h new file mode 100644 index 0000000..1e6081b --- /dev/null +++ b/TestGame/src/Worlds/TestWorld.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Game/World/World.h" + +class TestWorld final : public World +{ + double time = 0; +public: + TestWorld(GameInstance& game_instance); + + void BeginPlay() override; + void Tick(double delta_time) override; + + std::shared_ptr save() override; + void load(std::shared_ptr save) override; +}; \ No newline at end of file diff --git a/build Debug.bat b/build Debug.bat new file mode 100644 index 0000000..84a0ce0 --- /dev/null +++ b/build Debug.bat @@ -0,0 +1,4 @@ +@echo off +cd build +cmake --build . --config Debug +pause \ No newline at end of file diff --git a/build Release.bat b/build Release.bat new file mode 100644 index 0000000..f0bd362 --- /dev/null +++ b/build Release.bat @@ -0,0 +1,4 @@ +@echo off +cd build +cmake --build . --config Release +pause \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..696b90e --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd build +cmake --build . diff --git a/configurate Win32.bat b/configurate Win32.bat new file mode 100644 index 0000000..ff63dcc --- /dev/null +++ b/configurate Win32.bat @@ -0,0 +1,5 @@ +@echo off +mkdir build +cd build +cmake .. -A Win32 +pause \ No newline at end of file diff --git a/configurate Win64.bat b/configurate Win64.bat new file mode 100644 index 0000000..2a1279f --- /dev/null +++ b/configurate Win64.bat @@ -0,0 +1,5 @@ +@echo off +mkdir build +cd build +cmake .. -A x64 +pause \ No newline at end of file diff --git a/configurate_release.sh b/configurate_release.sh new file mode 100644 index 0000000..572e7e9 --- /dev/null +++ b/configurate_release.sh @@ -0,0 +1,4 @@ +#!/bin/sh +mkdir build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Release diff --git a/glfw b/glfw new file mode 160000 index 0000000..8e15281 --- /dev/null +++ b/glfw @@ -0,0 +1 @@ +Subproject commit 8e15281d34a8b9ee9271ccce38177a3d812456f8