C++20 migration and Client

- Migrated to C++20 modules
- Worker and ThreadedWorker base classes for interthread systems
- Client class for server transport and converting game data models to render data (views)
- Update provinces render data from Client
- Renderer handles commands now
This commit is contained in:
legatt 2026-02-10 04:12:04 +03:00
parent 9d5a41b02d
commit 3f965b2393
112 changed files with 1859 additions and 2607 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

13
.idea/.idea.EtrediaEmpires.dir/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/modules.xml
/.idea.EtrediaEmpires.iml
/contentModel.xml
/projectSettingsUpdater.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

2
.idea/EtrediaEmpires.iml generated Normal file
View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@ -0,0 +1,11 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ClangTidy" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

7
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakePythonSetting">
<option name="pythonIntegrationState" value="YES" />
</component>
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/EtrediaEmpires.iml" filepath="$PROJECT_DIR$/.idea/EtrediaEmpires.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -1,20 +1,25 @@
cmake_minimum_required (VERSION 3.8)
cmake_minimum_required (VERSION 4.2.3)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
set(CMAKE_POSITION_INDEPENDENT_CODE OFF)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project (EtrediaEmpires)
project (EtrediaEmpires LANGUAGES CXX)
add_subdirectory(sdk/map)
add_subdirectory(sdk/assets)
add_subdirectory(utils)
add_subdirectory(p7lib)
add_subdirectory(server)
add_subdirectory(utils)
add_subdirectory(server/lib)
add_subdirectory(engine-api)
add_subdirectory(renderers/magnum-renderer)
add_subdirectory(engine)

BIN
assets/map/regions.map Normal file

Binary file not shown.

Binary file not shown.

52
cmake/executable.cmake Normal file
View File

@ -0,0 +1,52 @@
function(add_executable_target TARGET_NAME)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(${TARGET_NAME} LANGUAGES CXX)
add_executable(${TARGET_NAME})
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS
src/*.cpp
src/*.c
)
file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS
include/*.hpp
include/*.h
)
file(GLOB_RECURSE MODULES CONFIGURE_DEPENDS
modules/*.cppm
modules/*.ixx
)
target_sources(${TARGET_NAME} PUBLIC FILE_SET modules TYPE CXX_MODULES BASE_DIRS ${PROJECT_SOURCE_DIR} FILES ${MODULES})
target_sources(${TARGET_NAME} PUBLIC FILE_SET headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES ${HEADERS})
target_sources(${TARGET_NAME} PRIVATE ${SOURCES})
# MSVC STD module linking
# target_compile_options(${TARGET_NAME} PRIVATE
# /EHsc
# "SHELL:/reference \"std=$ENV{STD_MODULE_DIR}/std.ifc\""
# "SHELL:/reference \"std.compat=$ENV{STD_MODULE_DIR}/std.compat.ifc\""
#)
#target_link_libraries(${TARGET_NAME} PRIVATE "$ENV{STD_MODULE_DIR}/std.obj")
# target_link_libraries(${TARGET_NAME} PRIVATE "$ENV{STD_MODULE_DIR}/std.compat.obj")
#target_compile_options(${TARGET_NAME} PRIVATE
# /wd5050
#)
#target_compile_features(${TARGET_NAME} PRIVATE cxx_std_23 INTERFACE cxx_std_23)
# Clang modules
# target_compile_options(${TARGET_NAME} PRIVATE
# -fmodules
# -fcxx-modules
# -fbuiltin-module-map
# -fimplicit-module-maps
# )
target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
endfunction()

52
cmake/library.cmake Normal file
View File

@ -0,0 +1,52 @@
function(add_library_target TARGET_NAME)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(${TARGET_NAME} LANGUAGES CXX)
add_library(${TARGET_NAME})
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS
src/*.cpp
src/*.c
)
file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS
include/*.hpp
include/*.h
)
file(GLOB_RECURSE MODULES CONFIGURE_DEPENDS
modules/*.cppm
modules/*.ixx
)
target_sources(${TARGET_NAME} PUBLIC FILE_SET modules TYPE CXX_MODULES BASE_DIRS ${PROJECT_SOURCE_DIR} FILES ${MODULES})
target_sources(${TARGET_NAME} PUBLIC FILE_SET headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES ${HEADERS})
target_sources(${TARGET_NAME} PRIVATE ${SOURCES})
# MSVC STD module linking
# target_compile_options(${TARGET_NAME} PRIVATE
# /EHsc
# "SHELL:/reference \"std=$ENV{STD_MODULE_DIR}/std.ifc\""
# "SHELL:/reference \"std.compat=$ENV{STD_MODULE_DIR}/std.compat.ifc\""
#)
#target_link_libraries(${TARGET_NAME} PRIVATE "$ENV{STD_MODULE_DIR}/std.obj")
# target_link_libraries(${TARGET_NAME} PRIVATE "$ENV{STD_MODULE_DIR}/std.compat.obj")
#target_compile_options(${TARGET_NAME} PRIVATE
# /wd5050
#)
#target_compile_features(${TARGET_NAME} PRIVATE cxx_std_23 INTERFACE cxx_std_23)
# Clang modules
# target_compile_options(${TARGET_NAME} PRIVATE
# -fmodules
# -fcxx-modules
# -fbuiltin-module-map
# -fimplicit-module-maps
# )
target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
endfunction()

View File

@ -1,36 +1,18 @@
if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(engine-api)
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS
"src/*.cpp"
"src/*.c"
"include/*.hpp"
"include/*.h"
)
include(${CMAKE_SOURCE_DIR}/cmake/library.cmake)
# Find dependencies
find_package(glfw3 CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
# Link library
add_library(${PROJECT_NAME} ${SOURCES})
add_library_target(${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} PRIVATE p7)
target_link_libraries(${PROJECT_NAME} PUBLIC server)
target_link_libraries(${PROJECT_NAME} PUBLIC utils)
target_link_libraries(${PROJECT_NAME} PRIVATE glfw)
target_include_directories(${PROJECT_NAME} PUBLIC include/)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE glm::glm)

View File

@ -1,59 +0,0 @@
#pragma once
#include "GameData.hpp"
#include "Map.hpp"
#include <concepts>
#include <cstdint>
#include <vector>
namespace ee_assets
{
struct AssetData {};
template<typename T> concept Asset = std::derived_from<T, AssetData>;
enum AssetType
{
BINARY,
MAP_MESH,
SAVE,
SHADER,
MATERIAL,
IMAGE
};
struct AssetBinary : public AssetData
{
uint8_t* content = nullptr;
size_t size = 0;
AssetBinary(uint8_t* &source, const size_t sourceSize);
~AssetBinary();
};
struct AssetMapMesh : public AssetData
{
ee_engine::MapMesh content;
uint8_t* Serialize(size_t &size);
AssetMapMesh() {};
AssetMapMesh(uint8_t* &source, const size_t sourceSize);
};
struct AssetSave : public AssetData
{
std::vector<ee_data::Subject> subjects;
uint8_t* Serialize(size_t &size);
AssetSave() {};
AssetSave(uint8_t* &source, const size_t sourceSize);
};
#define AssetShader AssetBinary
struct AssetMaterial : public AssetData
{
uint32_t vertexShaderAssetID;
uint32_t fragmentShaderAssetID;
};
}

View File

@ -1,13 +0,0 @@
#pragma once
#include <cstdint>
#include <vector>
namespace ee_data
{
struct Subject
{
uint32_t tag;
uint32_t color;
std::vector<uint32_t> regions;
};
}

View File

@ -1,24 +0,0 @@
#pragma once
#include <vector>
#include <unordered_map>
#include <glm/glm.hpp>
namespace ee_engine
{
struct MapMesh
{
struct Province
{
std::vector<glm::vec2> vertices;
std::vector<uint16_t> indices;
};
std::unordered_map<uint32_t, std::vector<Province>> states;
};
struct ProvinceSSBO
{
uint32_t stateId;
uint32_t color;
};
}

View File

@ -0,0 +1,63 @@
module;
#include <concepts>
#include <type_traits>
#include <cstdint>
#include <cstddef>
export module AssetData;
import GameData;
import MapView;
export namespace ee_assets
{
struct AssetData {};
template<typename T> concept Asset = std::is_base_of_v<AssetData, T>;
enum AssetType
{
BINARY,
MAP_MESH,
SAVE,
SHADER,
MATERIAL,
IMAGE
};
struct AssetBinary : public AssetData
{
std::uint8_t* content = nullptr;
std::size_t size = 0;
AssetBinary(std::uint8_t* &source, const std::size_t sourceSize);
~AssetBinary();
};
struct AssetMapMesh : public AssetData
{
ee_engine::MapMesh content;
std::uint8_t* Serialize(std::size_t &size);
AssetMapMesh() {};
AssetMapMesh(std::uint8_t* &source, const std::size_t sourceSize);
};
struct AssetSave : public AssetData
{
ee_data::GameState content;
std::uint8_t* Serialize(std::size_t &size);
AssetSave() {};
AssetSave(std::uint8_t* &source, const std::size_t sourceSize);
};
using AssetShader = AssetBinary;
struct AssetMaterial : public AssetData
{
std::uint32_t vertexShaderAssetID;
std::uint32_t fragmentShaderAssetID;
};
}

View File

@ -1,7 +1,10 @@
#pragma once
module;
#include <functional>
namespace ee_engine
export module Input;
export namespace ee_engine
{
struct InputHandlers
{

View File

@ -1,9 +1,18 @@
#pragma once
#include <GLFW/glfw3.h>
#include "Map.hpp"
#include "AssetData.hpp"
module;
namespace ee_engine
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vector>
#include <unordered_map>
#include <cstdint>
#include <cstddef>
export module Render;
import worker;
import MapView;
import AssetData;
export namespace ee_engine
{
struct ShaderRefs
{
@ -24,11 +33,31 @@ namespace ee_engine
float rotation;
};
class IRenderer
struct RendererCommand : public ee_utils::WorkerCommand
{
enum Type : std::uint8_t
{
NONE,
UPDATE_REGION,
COMPLETED
};
struct RegionPayload { std::uint32_t id; ProvinceSSBO data; };
union {
RegionPayload region;
};
};
class IRenderer : public ee_utils::Worker<RendererCommand, RendererCommand::COMPLETED>
{
public:
virtual ~IRenderer();
constexpr bool isReady() const noexcept {return _isReady;}
void RegisterMaterials(const ShaderRefs &shaders);
void RegisterMapMesh(MapMesh *mapMesh);
@ -39,9 +68,12 @@ namespace ee_engine
virtual void ResizeWindow(int width, int height);
void SetCamera(const CameraTransform &camera);
CameraTransform getCamera();
constexpr CameraTransform getCamera() const noexcept {return camera;};
protected:
bool _isReady;
int screenWidth, screenHeight;
float screenAspect;
CameraTransform camera;
@ -51,6 +83,9 @@ namespace ee_engine
MapMesh *mapMesh;
ShaderRefs shaders;
struct RegionIndecies {std::size_t startIndex; std::size_t provincesCount;};
std::unordered_map<std::uint32_t /*id*/, RegionIndecies> regionsMapping;
explicit IRenderer(GLFWwindow *window);
virtual void Clear() = 0;
@ -58,5 +93,10 @@ namespace ee_engine
virtual void DrawUI() = 0;
virtual void ApplyCamera() = 0;
virtual void UpdateProvinces(const std::size_t index, const std::vector<ee_engine::ProvinceSSBO> &data) = 0;
private:
void UpdateRegionCmd(RendererCommand &command);
};
}

View File

@ -0,0 +1,28 @@
module;
#include <glm/glm.hpp>
#include <vector>
#include <unordered_map>
#include <cstdint>
//TODO: module Views
export module MapView;
export namespace ee_engine
{
struct MapMesh
{
struct Province
{
std::vector<glm::vec2> vertices;
std::vector<std::uint16_t> indices;
};
std::unordered_map<std::uint32_t, std::vector<Province>> regions;
};
struct ProvinceSSBO
{
std::uint32_t regionId;
std::uint32_t color;
};
}

View File

@ -1,7 +1,12 @@
#include <ee_engine_api/AssetData.hpp>
#include <ee_utils/allocator.hpp>
#include <ee_utils/buffers.hpp>
module;
#include <glm/glm.hpp>
#include <cstdint>
#include <cstddef>
module AssetData;
import allocator;
import buffers;
/*
@ -9,12 +14,11 @@ YOU CAN PREVENT SOURCE BUFFER FROM CLOSING.
IT CAN BE USED FOR STORING ASSET DATA WITHOUT COPY.
IN THIS CASE, YOU MUST SET SOURCE TO NULLPTR AND FREE BUFFER YOURSELF.
TODO: make binary format describing for common functions instead of individual serialize/deserialize functions
TODO: parallelize convertion loops
TODO: make universal binary format, think about flatbuffers or msgpacks
*/
ee_assets::AssetBinary::AssetBinary(uint8_t* &source, const size_t sourceSize)
ee_assets::AssetBinary::AssetBinary(std::uint8_t* &source, const std::size_t sourceSize)
{
content = source;
size = sourceSize;
@ -29,39 +33,39 @@ ee_assets::AssetBinary::~AssetBinary()
ee_assets::AssetMapMesh::AssetMapMesh(uint8_t* &source, const size_t sourceSize)
ee_assets::AssetMapMesh::AssetMapMesh(std::uint8_t* &source, const std::size_t sourceSize)
{
uint8_t* buffer = source;
uint8_t* pointer = source;
size_t leftoverSize = sourceSize;
std::uint8_t* buffer = source;
std::uint8_t* pointer = source;
std::size_t leftoverSize = sourceSize;
source = nullptr;
uint32_t statesCount = 0;
ee_utils::readNext(&statesCount, pointer, leftoverSize, sizeof(uint32_t));
std::uint32_t statesCount = 0;
ee_utils::readNext(&statesCount, pointer, leftoverSize, sizeof(std::uint32_t));
for (uint32_t state = 0; state < statesCount; state++)
for (std::uint32_t state = 0; state < statesCount; state++)
{
uint32_t id = 0;
std::uint32_t id = 0;
glm::u8vec3 color {};
if (!ee_utils::readNext(&color, pointer, leftoverSize, sizeof(glm::u8vec3)))
break;
id = static_cast<uint32_t>(color.r) << 16 |
static_cast<uint32_t>(color.g) << 8 |
static_cast<uint32_t>(color.b);
id = static_cast<std::uint32_t>(color.r) << 16 |
static_cast<std::uint32_t>(color.g) << 8 |
static_cast<std::uint32_t>(color.b);
uint32_t provincesCount = 0;
ee_utils::readNext(&provincesCount, pointer, leftoverSize, sizeof(uint32_t));
std::uint32_t provincesCount = 0;
ee_utils::readNext(&provincesCount, pointer, leftoverSize, sizeof(std::uint32_t));
for (uint32_t pi = 0; pi < provincesCount; pi++)
for (std::uint32_t pi = 0; pi < provincesCount; pi++)
{
ee_engine::MapMesh::Province prov;
uint32_t verticesCount = 0;
if (!ee_utils::readNext(&verticesCount, pointer, leftoverSize, sizeof(uint32_t)))
std::uint32_t verticesCount = 0;
if (!ee_utils::readNext(&verticesCount, pointer, leftoverSize, sizeof(std::uint32_t)))
break;
prov.vertices.reserve(verticesCount);
for (uint32_t vi = 0; vi < verticesCount; vi++)
for (std::uint32_t vi = 0; vi < verticesCount; vi++)
{
glm::vec2 vertex;
if (!ee_utils::readNext(&vertex, pointer, leftoverSize, sizeof(glm::vec2)))
@ -70,21 +74,21 @@ ee_assets::AssetMapMesh::AssetMapMesh(uint8_t* &source, const size_t sourceSize)
prov.vertices.push_back(vertex);
}
uint32_t indicesCount = 0;
if (!ee_utils::readNext(&indicesCount, pointer, leftoverSize, sizeof(uint32_t)))
std::uint32_t indicesCount = 0;
if (!ee_utils::readNext(&indicesCount, pointer, leftoverSize, sizeof(std::uint32_t)))
break;
prov.indices.reserve(indicesCount);
for(uint32_t ii = 0; ii < indicesCount; ii++)
for(std::uint32_t ii = 0; ii < indicesCount; ii++)
{
uint16_t index = 0;
if (!ee_utils::readNext(&index, pointer, leftoverSize, sizeof(uint16_t)))
std::uint16_t index = 0;
if (!ee_utils::readNext(&index, pointer, leftoverSize, sizeof(std::uint16_t)))
break;
prov.indices.push_back(index);
}
content.states[id].push_back(prov);
content.regions[id].push_back(prov);
}
}
@ -92,34 +96,34 @@ ee_assets::AssetMapMesh::AssetMapMesh(uint8_t* &source, const size_t sourceSize)
}
ee_assets::AssetSave::AssetSave(uint8_t* &source, const size_t sourceSize)
ee_assets::AssetSave::AssetSave(std::uint8_t* &source, const std::size_t sourceSize)
{
uint8_t* buffer = source;
uint8_t* pointer = source;
size_t leftoverSize = sourceSize;
std::uint8_t* buffer = source;
std::uint8_t* pointer = source;
std::size_t leftoverSize = sourceSize;
source = nullptr;
uint16_t subjectsCount = static_cast<uint16_t>(subjects.size());
ee_utils::readNext(&subjectsCount, pointer, leftoverSize, sizeof(uint16_t));
std::uint16_t subjectsCount = static_cast<std::uint16_t>(content.subjects.size());
ee_utils::readNext(&subjectsCount, pointer, leftoverSize, sizeof(std::uint16_t));
for (uint16_t i = 0; i < subjectsCount; i++)
for (std::uint16_t i = 0; i < subjectsCount; i++)
{
ee_data::Subject subject;
ee_utils::readNext(&subject.tag, pointer, leftoverSize, sizeof(subject.tag));
ee_utils::readNext(&subject.color, pointer, leftoverSize, sizeof(subject.color));
uint16_t regionsCount = 0;
ee_utils::readNext(&regionsCount, pointer, leftoverSize, sizeof(uint16_t));
std::uint16_t regionsCount = 0;
ee_utils::readNext(&regionsCount, pointer, leftoverSize, sizeof(std::uint16_t));
subject.regions.resize(regionsCount);
ee_utils::readNext(
subject.regions.data(),
pointer,
leftoverSize,
subject.regions.size() * sizeof(uint32_t)
subject.regions.size() * sizeof(std::uint32_t)
);
subjects.push_back(subject);
content.subjects.push_back(subject);
}
DeallocateArray(buffer);
@ -131,35 +135,35 @@ ee_assets::AssetSave::AssetSave(uint8_t* &source, const size_t sourceSize)
// SERIALIZERS
uint8_t* ee_assets::AssetSave::Serialize(size_t &size)
std::uint8_t* ee_assets::AssetSave::Serialize(std::size_t &size)
{
size = sizeof(uint16_t);
for (ee_data::Subject &subject : subjects)
size = sizeof(std::uint16_t);
for (ee_data::Subject &subject : content.subjects)
{
size += sizeof(subject.tag);
size += sizeof(subject.color);
size += sizeof(uint16_t) + subject.regions.size() * sizeof(uint32_t);
size += sizeof(std::uint16_t) + subject.regions.size() * sizeof(std::uint32_t);
}
size_t leftover = size;
uint8_t *result = AllocateArray<uint8_t>(size);
uint8_t *pointer = result;
std::size_t leftover = size;
std::uint8_t *result = AllocateArray<std::uint8_t>(size);
std::uint8_t *pointer = result;
uint16_t subjectsCount = static_cast<uint16_t>(subjects.size());
ee_utils::writeNext(pointer, &subjectsCount, leftover, sizeof(uint16_t));
std::uint16_t subjectsCount = static_cast<std::uint16_t>(content.subjects.size());
ee_utils::writeNext(pointer, &subjectsCount, leftover, sizeof(std::uint16_t));
for (ee_data::Subject &subject : subjects)
for (ee_data::Subject &subject : content.subjects)
{
ee_utils::writeNext(pointer, &subject.tag, leftover, sizeof(subject.tag));
ee_utils::writeNext(pointer, &subject.color, leftover, sizeof(subject.color));
uint16_t regionsCount = static_cast<uint16_t>(subject.regions.size());
ee_utils::writeNext(pointer, &regionsCount, leftover, sizeof(uint16_t));
std::uint16_t regionsCount = static_cast<std::uint16_t>(subject.regions.size());
ee_utils::writeNext(pointer, &regionsCount, leftover, sizeof(std::uint16_t));
ee_utils::writeNext(
pointer,
subject.regions.data(),
leftover,
subject.regions.size() * sizeof(uint32_t)
subject.regions.size() * sizeof(std::uint32_t)
);
}

104
engine-api/src/Render.cpp Normal file
View File

@ -0,0 +1,104 @@
module;
#include "Logger.h"
#include <GLFW/glfw3.h>
#include <glm/gtc/matrix_transform.hpp>
#include <ee_utils/utils.hpp>
#include <cstdint>
#include <cstddef>
#include <vector>
module Render;
ee_engine::IRenderer::IRenderer(GLFWwindow *window)
{
commandsMapping[RendererCommand::UPDATE_REGION] = MAP_WORKER_COMMAND(RendererCommand, UpdateRegionCmd);
this->window = window;
glfwGetFramebufferSize(window, &screenWidth, &screenHeight);
screenAspect = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
}
ee_engine::IRenderer::~IRenderer()
{
}
void ee_engine::IRenderer::RegisterMaterials(const ShaderRefs &shaders)
{
this->shaders = shaders;
}
void ee_engine::IRenderer::RegisterMapMesh(MapMesh* mapMesh)
{
this->mapMesh = mapMesh;
}
void ee_engine::IRenderer::Draw()
{
Clear();
DrawMap();
DrawUI();
}
void ee_engine::IRenderer::ResizeWindow(int width, int height)
{
screenWidth = width;
screenHeight = height;
screenAspect = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
}
void ee_engine::IRenderer::SetCamera(const CameraTransform &camera)
{
if (camera.zoom <= 0)
{
LOG(WARNING, "Camera zoom cannot be zero or less!");
return;
}
this->camera = camera;
float halfHeight = camera.zoom;
float halfWidth = camera.zoom * screenAspect;
glm::mat4 P = glm::ortho(
-halfWidth, +halfWidth,
-halfHeight, +halfHeight
);
glm::mat4 V = glm::translate(
glm::mat4(1.0f),
glm::vec3(-camera.position, 0.0f)
);
viewProjection = P * V;
ApplyCamera();
}
void ee_engine::IRenderer::UpdateRegionCmd(ee_engine::RendererCommand &command)
{
if (!regionsMapping.contains(command.region.id))
{
LOG(WARNING, "Updating unregistered region with id: %d", command.region.id);
return;
}
if (command.region.id != command.region.data.regionId)
{
LOG(ERROR, "Updating regions ID is not supported! Region: %d Changed ID: %d",
command.region.id, command.region.data.regionId);
}
RegionIndecies mapping = regionsMapping[command.region.id];
std::vector<ProvinceSSBO> buffer;
buffer.reserve(mapping.provincesCount);
for (std::size_t i = 0; i < mapping.provincesCount; i++)
{
buffer.push_back(command.region.data);
}
UpdateProvinces(mapping.startIndex, buffer);
command.type = ee_engine::RendererCommand::COMPLETED;
}

View File

@ -1,70 +0,0 @@
#include <ee_engine_api/RenderInterface.hpp>
#include <ee_utils/allocator.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "Logger.h"
ee_engine::IRenderer::IRenderer(GLFWwindow *window)
{
this->window = window;
glfwGetFramebufferSize(window, &screenWidth, &screenHeight);
screenAspect = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
}
ee_engine::IRenderer::~IRenderer()
{
}
void ee_engine::IRenderer::RegisterMaterials(const ShaderRefs &shaders)
{
this->shaders = shaders;
}
void ee_engine::IRenderer::RegisterMapMesh(MapMesh* mapMesh)
{
this->mapMesh = mapMesh;
}
void ee_engine::IRenderer::Draw()
{
Clear();
DrawMap();
DrawUI();
}
void ee_engine::IRenderer::ResizeWindow(int width, int height)
{
screenWidth = width;
screenHeight = height;
screenAspect = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
}
void ee_engine::IRenderer::SetCamera(const CameraTransform &camera)
{
if (camera.zoom <= 0)
{
LOG(WARNING, "Camera zoom cannot be zero or less!");
return;
}
this->camera = camera;
float halfHeight = camera.zoom;
float halfWidth = camera.zoom * screenAspect;
glm::mat4 P = glm::ortho(
-halfWidth, +halfWidth,
-halfHeight, +halfHeight
);
glm::mat4 V = glm::translate(
glm::mat4(1.0f),
glm::vec3(-camera.position, 0.0f)
);
viewProjection = P * V;
ApplyCamera();
}
ee_engine::CameraTransform ee_engine::IRenderer::getCamera() {return this->camera;}

View File

@ -1,12 +0,0 @@
{
"configurations": [
{
"type": "cmake",
"request": "launch",
"name": "Debug portfile(s)",
"cmakeDebugType": "external",
"pipeName": "\\\\.\\pipe\\vcpkg_ext_portfile_dbg",
"preLaunchTask": "Debug vcpkg commands"
}
]
}

View File

@ -1,24 +1,6 @@
cmake_minimum_required (VERSION 3.8)
include(${CMAKE_SOURCE_DIR}/cmake/executable.cmake)
if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project (engine)
# List sources
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS
"src/*.cpp"
"src/*.c"
"include/*.hpp"
"include/*.h"
)
set(EXE engine)
# Find dependencies
@ -29,23 +11,17 @@ find_package(glm CONFIG REQUIRED)
# Link library
add_executable(${PROJECT_NAME} ${SOURCES})
add_executable_target(${EXE})
target_link_libraries(${PROJECT_NAME} PUBLIC utils)
target_link_libraries(${PROJECT_NAME} PUBLIC engine-api)
target_link_libraries(${PROJECT_NAME} PUBLIC magnum-renderer)
target_link_libraries(${EXE} PUBLIC utils)
target_link_libraries(${EXE} PUBLIC engine-api)
target_link_libraries(${EXE} PUBLIC magnum-renderer)
target_link_libraries(${PROJECT_NAME} PRIVATE Boost::lockfree)
target_link_libraries(${PROJECT_NAME} PRIVATE xxHash::xxhash)
target_link_libraries(${PROJECT_NAME} PRIVATE p7)
target_link_libraries(${PROJECT_NAME} PRIVATE glfw)
target_link_libraries(${PROJECT_NAME} PRIVATE glm::glm)
target_include_directories(${PROJECT_NAME} PUBLIC include/)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
endif()
target_link_libraries(${EXE} PRIVATE Boost::lockfree)
target_link_libraries(${EXE} PRIVATE xxHash::xxhash)
target_link_libraries(${EXE} PRIVATE p7)
target_link_libraries(${EXE} PRIVATE glfw)
target_link_libraries(${EXE} PRIVATE glm::glm)
# Compile options
if (APP_VERSION)
@ -53,7 +29,7 @@ if (APP_VERSION)
list(GET APP_VERSION_LIST 0 MAJOR_VERSION)
list(GET APP_VERSION_LIST 1 MINOR_VERSION)
list(GET APP_VERSION_LIST 2 PATCH_VERSION)
target_compile_definitions(${PROJECT_NAME}
target_compile_definitions(${EXE}
PRIVATE APP_VERSION
PRIVATE MAJOR_VERSION=${MAJOR_VERSION}
PRIVATE MINOR_VERSION=${MINOR_VERSION}
@ -65,9 +41,8 @@ endif()
set(link_src "${CMAKE_SOURCE_DIR}/assets")
set(link_dst "${CMAKE_CURRENT_BINARY_DIR}/assets")
add_custom_command(
TARGET ${PROJECT_NAME}
TARGET ${EXE}
POST_BUILD COMMAND ${CMAKE_COMMAND}
-E create_symlink ${link_src} ${link_dst}
DEPENDS ${link_dst}
COMMENT "symbolic link ${link_src} => ${link_dst}"
)

View File

@ -1,14 +0,0 @@
#pragma once
#include <ee_engine_api/InputInterface.hpp>
class Actions
{
public:
static ee_engine::InputHandlers mappings;
private:
static void Zoom(float dx, float dy);
static void MapScroll(float dx, float dy);
Actions();
};

View File

@ -1,172 +0,0 @@
#pragma once
#include "FileSystem.hpp"
#include <ee_engine_api/AssetData.hpp>
#include "Logger.h"
#include <vector>
#include <unordered_map>
#include <filesystem>
#include <thread>
#include <atomic>
#include <cstdint>
#include <Boost/lockfree/spsc_queue.hpp>
#define INVALID_ASSET_ID UINT32_MAX
struct AssetAddress
{
std::atomic<void*> resource;
};
struct AssetHandle
{
size_t slot = 0;
uint32_t id = 0;
};
class AssetInfo
{
public:
ee_assets::AssetType type = ee_assets::BINARY;
std::atomic<bool> isReady;
AssetAddress* resourceAddress;
AssetInfo(void *resource);
AssetInfo(const AssetInfo&) = delete;
AssetInfo& operator=(const AssetInfo&) = delete;
AssetInfo(AssetInfo&& other) noexcept;
AssetInfo& operator=(AssetInfo&& other) noexcept;
};
struct AssetLoadingEntry
{
AssetHandle handle;
FileHandle* file;
IFileSystem* fileOwner;
};
struct AssetStreamCommand
{
enum Type
{
NONE,
LOAD,
FIND,
GET,
COMPLETED
} type = NONE;
AssetHandle target;
uint32_t targetId = INVALID_ASSET_ID;
std::filesystem::path targetPath;
ee_assets::AssetType targetType = ee_assets::BINARY;
std::atomic<void*>* out;
};
class AssetManager
{
public:
std::atomic<bool> isRunning;
AssetManager();
~AssetManager();
void Start();
void Shutdown();
void LoadDir(ee_assets::AssetType type, const std::filesystem::path &path);
void LoadAsync(ee_assets::AssetType type, const std::filesystem::path &path);
AssetHandle& Find(const std::filesystem::path &path);
AssetHandle& Find(const uint32_t &id);
template <ee_assets::Asset T> T& FindOnce(const std::filesystem::path &path);
template <ee_assets::Asset T> T& FindOnce(const uint32_t &id);
void Redirect(AssetHandle &handle, void *resource);
template <ee_assets::Asset T> T& Get(const AssetHandle &handle);
void Free(AssetHandle &handle);
private:
std::stop_token *stoken;
std::jthread streamingThread;
boost::lockfree::spsc_queue<AssetStreamCommand, boost::lockfree::capacity<512>> commands;
IFileSystem *diskSystem;
std::vector<AssetInfo> activeAssets;
std::unordered_map<std::filesystem::path, uint32_t> assetsIdMapping;
std::unordered_map<uint32_t, AssetHandle*> assetsMapping;
std::vector<size_t> freeAssetSlots;
std::vector<AssetLoadingEntry> loadingEntries;
std::vector<ee_assets::AssetBinary*> binaryStorage;
std::vector<ee_assets::AssetSave*> savesStorage;
std::vector<ee_assets::AssetMapMesh*> mapMeshesStorage;
std::vector<ee_assets::AssetShader*> shadersStorage;
std::vector<ee_assets::AssetMaterial*> materialsStorage;
void Threading();
void Request(const AssetStreamCommand &command);
void Request(const AssetStreamCommand::Type &type);
void Loop();
void ProcessCommand(AssetStreamCommand &command);
void Release();
template<ee_assets::Asset T> void FinishLoadConstruct(AssetLoadingEntry &loaded, std::vector<T*> &storage);
template<ee_assets::Asset T> void FinishLoadPlain(AssetLoadingEntry &loaded, std::vector<T*> &storage);
template<ee_assets::Asset T> void ClearStorage(std::vector<T*> &storage);
};
template <ee_assets::Asset T> T& AssetManager::Get(const AssetHandle &handle)
{
std::atomic<void*> result;
AssetStreamCommand command {
.type = AssetStreamCommand::GET,
.target = handle,
.out = &result
};
Request(command);
while (command.out->load() == nullptr);
return *reinterpret_cast<T*>(command.out->load());
}
template <ee_assets::Asset T> T& AssetManager::FindOnce(const std::filesystem::path &path)
{
return Get<T>(Find(path));
}
template <ee_assets::Asset T> T& AssetManager::FindOnce(const uint32_t &id)
{
return Get<T>(Find(id));
}
template<ee_assets::Asset T> void AssetManager::FinishLoadConstruct(AssetLoadingEntry &loaded, std::vector<T*> &storage)
{
T* resource = Allocate<T>(loaded.file->data, loaded.file->size);
storage.push_back(resource);
Redirect(loaded.handle, storage.back());
}
template<ee_assets::Asset T> void AssetManager::FinishLoadPlain(AssetLoadingEntry &loaded, std::vector<T*> &storage)
{
T* resource = Allocate<T>();
if (loaded.file->size < sizeof(T))
LOG(CRITICAL, "File size is less than needed for asset of type: %d", typeid(T).name());
std::memcpy(resource, loaded.file->data, sizeof(T));
storage.push_back(resource);
Redirect(loaded.handle, storage.back());
}
template<ee_assets::Asset T> void AssetManager::ClearStorage(std::vector<T*> &storage)
{
for (T* &resource : storage)
{
if (resource != nullptr)
Deallocate(resource);
}
storage.clear();
}

View File

@ -1,20 +0,0 @@
#pragma once
#include <GLFW/glfw3.h>
#include <ee_engine_api/RenderInterface.hpp>
#include <ee_engine_api/InputInterface.hpp>
#include "AssetManager.hpp"
class Engine
{
public:
static GLFWwindow *window;
static ee_engine::IInput *input;
static AssetManager *assetManager;
static ee_engine::IRenderer *renderer;
static void Shutdown();
private:
Engine();
};

View File

@ -1,69 +0,0 @@
#pragma once
#include <filesystem>
#include <vector>
#include <cstdint>
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef __linux__
#endif
#define FILE_OPEN_NONE 0
#define FILE_OPEN_READ (1 << 0)
#define FILE_OPEN_WRITE (1 << 1)
#define FILE_OPEN_MMAP (1 << 2)
#define FILE_READ_SEQUENTIAL 0
#define FILE_READ_RANDOM_ACCESS (1 << 0)
struct FileHandle
{
size_t id = 0;
size_t size = 0;
size_t bufferSize = 0;
uint8_t *data = nullptr;
uint8_t *buffer = nullptr;
#ifndef NDEBUG
std::filesystem::path debugPath;
#endif
#ifdef _WIN32
HANDLE win32Handle = INVALID_HANDLE_VALUE;
OVERLAPPED overlapped;
#endif
#ifdef __linux__
#endif
};
class IFileSystem
{
public:
virtual FileHandle& Open(const std::filesystem::path &path, const uint32_t flags) = 0;
virtual void ReadAsync(FileHandle &file, const uint32_t bufferSize, const uint32_t flags) = 0;
virtual bool PollReady(FileHandle &file) = 0;
virtual void Close(FileHandle &file) = 0;
~IFileSystem();
protected:
std::vector<FileHandle*> activeFiles;
std::vector<FileHandle*> failedFiles;
std::vector<size_t> freeFileSlots;
};
class NativeFileSystem : public IFileSystem
{
public:
FileHandle& Open(const std::filesystem::path &path, const uint32_t flags) override;
void ReadAsync(FileHandle &file, const uint32_t bufferSize, uint32_t flags) override;
bool PollReady(FileHandle &file) override;
void Close(FileHandle &file) override;
private:
};

View File

@ -1,10 +0,0 @@
#pragma once
#include <ee_engine_api/InputInterface.hpp>
#include <GLFW/glfw3.h>
class GLFWInput : public ee_engine::IInput
{
public:
GLFWInput(ee_engine::InputHandlers handlers);
void Poll() override;
};

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef ETREDIAEMPIRES_COMPILE_CONFIG_HPP
#define ETREDIAEMPIRES_COMPILE_CONFIG_HPP
#define P7_LOGS_PATH L" /P7.Dir=logs/game/"
@ -15,3 +16,5 @@
#endif
#define ASSETS_HASH_SPACE 0
#endif //ETREDIAEMPIRES_COMPILE_CONFIG_HPP

View File

@ -0,0 +1,17 @@
export module Actions;
import Input;
export
{
class Actions
{
public:
static ee_engine::InputHandlers mappings;
private:
static void Zoom(float dx, float dy);
static void MapScroll(float dx, float dy);
Actions();
};
}

View File

@ -0,0 +1,425 @@
module;
#include "Logger.h"
#include "compile_config.hpp"
#include <ee_utils/utils.hpp>
#include <vector>
#include <unordered_map>
#include <filesystem>
#include <thread>
#include <atomic>
#include <cstdint>
#include <cstddef>
#include <xxhash.h>
#include <cstring>
#include <Boost/lockfree/spsc_queue.hpp>
export module AssetManager;
export import AssetData;
import allocator;
import worker;
import FileSystem;
export
{
struct AssetAddress
{
std::atomic<void*> resource;
};
struct AssetHandle
{
inline static constexpr std::uint32_t INVALID_ASSET_ID = UINT32_MAX;
std::size_t slot = 0;
std::uint32_t id = 0;
};
class AssetInfo
{
public:
ee_assets::AssetType type = ee_assets::BINARY;
std::atomic<bool> isReady;
AssetAddress* resourceAddress;
AssetInfo(void *resource)
{
this->resourceAddress = Allocate<AssetAddress>();
this->resourceAddress->resource.store(resource);
}
AssetInfo(const AssetInfo&) = delete;
AssetInfo& operator=(const AssetInfo&) = delete;
AssetInfo(AssetInfo&& other) noexcept :
type(other.type),
resourceAddress(other.resourceAddress)
{
isReady.store(other.isReady.load(std::memory_order_relaxed), std::memory_order_relaxed);
other.resourceAddress = nullptr;
other.isReady.store(false, std::memory_order_relaxed);
}
AssetInfo& operator=(AssetInfo&& other) noexcept
{
if (this != &other)
{
type = other.type;
resourceAddress = other.resourceAddress;
isReady.store(other.isReady.load(std::memory_order_relaxed), std::memory_order_relaxed);
other.resourceAddress = nullptr;
other.isReady.store(false, std::memory_order_relaxed);
}
return *this;
}
};
struct AssetLoadingEntry
{
AssetHandle handle;
FileHandle* file;
IFileSystem* fileOwner;
};
struct AssetStreamCommand : ee_utils::WorkerCommand
{
enum Type : std::uint8_t
{
NONE,
LOAD,
FIND,
GET,
COMPLETED
};
AssetHandle target;
std::uint32_t targetId = AssetHandle::INVALID_ASSET_ID;
std::filesystem::path targetPath;
ee_assets::AssetType targetType = ee_assets::BINARY;
std::atomic<void*>* out = nullptr;
};
class AssetManager final : public ee_utils::ThreadedWorker<AssetStreamCommand, AssetStreamCommand::COMPLETED>
{
public:
AssetManager() : ee_utils::ThreadedWorker<AssetStreamCommand, AssetStreamCommand::COMPLETED>(L"Assets Stream")
{
commandsMapping[AssetStreamCommand::LOAD] = MAP_WORKER_COMMAND(AssetStreamCommand, LoadCmd);
commandsMapping[AssetStreamCommand::FIND] = MAP_WORKER_COMMAND(AssetStreamCommand, FindCmd);
commandsMapping[AssetStreamCommand::GET] = MAP_WORKER_COMMAND(AssetStreamCommand, GetCmd);
diskSystem = Allocate<NativeFileSystem>();
}
~AssetManager()
{
Deallocate(diskSystem);
}
void LoadDir(ee_assets::AssetType type, const std::filesystem::path &path)
{
for (const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(path))
{
if (entry.is_directory()) continue;
LoadAsync(type, entry.path());
}
}
void LoadAsync(ee_assets::AssetType type, const std::filesystem::path &path)
{
AssetStreamCommand command;
command.type = AssetStreamCommand::LOAD;
command.targetPath = path;
command.targetType = type;
Request(command);
}
AssetHandle& Find(const std::filesystem::path &path)
{
std::atomic<void*> result;
AssetStreamCommand command;
command.type = AssetStreamCommand::FIND;
command.targetPath = path;
command.out = &result;
Request(command);
while (command.out->load() == nullptr);
return *reinterpret_cast<AssetHandle*>(command.out->load());
}
AssetHandle& Find(const std::uint32_t &id)
{
std::atomic<void*> result;
AssetStreamCommand command;
command.type = AssetStreamCommand::FIND;
command.targetId = id;
command.out = &result;
Request(command);
while (command.out->load() == nullptr);
return *reinterpret_cast<AssetHandle*>(command.out->load());
}
// Returned resource reference should not be stored
template <ee_assets::Asset T> T& FindOnce(const std::filesystem::path &path)
{
return Get<T>(Find(path));
}
// Returned resource reference should not be stored
template <ee_assets::Asset T> T& FindOnce(const std::uint32_t &id)
{
return Get<T>(Find(id));
}
void Redirect(AssetHandle &handle, void *resource)
{
AssetInfo &asset = activeAssets[handle.slot];
if (asset.resourceAddress == nullptr)
{
LOG(WARNING, "Attempt to redirect empty handle");
return;
}
// TODO: LEAK! Free old resource after redirecting handler to other resource
if (asset.resourceAddress->resource.load() != nullptr)
{
void* oldResource = asset.resourceAddress->resource.exchange(nullptr, std::memory_order_acq_rel);
//if (oldResource)
//Deallocate(oldResource);
}
asset.resourceAddress->resource.store(resource);
}
// Returned resource reference should not be stored
template <ee_assets::Asset T> T& Get(const AssetHandle &handle)
{
std::atomic<void*> result;
AssetStreamCommand command;
command.type = AssetStreamCommand::GET;
command.target = handle;
command.out = &result;
Request(command);
while (command.out->load() == nullptr);
return *reinterpret_cast<T*>(command.out->load());
}
void Free(AssetHandle &handle)
{
AssetInfo &asset = activeAssets[handle.slot];
Redirect(handle, nullptr);
Deallocate(asset.resourceAddress);
asset.resourceAddress = nullptr;
freeAssetSlots.push_back(handle.slot);
}
protected:
void Update() override
{
// TODO: FinishLoadTasks()
for (AssetLoadingEntry &entry : loadingEntries)
{
bool isReady = entry.fileOwner->PollReady(*entry.file);
if (isReady)
{
AssetInfo& asset = activeAssets[entry.handle.slot];
switch (asset.type)
{
case ee_assets::MAP_MESH:
FinishLoadConstruct(entry, mapMeshesStorage);
break;
case ee_assets::SAVE:
FinishLoadConstruct(entry, savesStorage);
break;
case ee_assets::SHADER:
FinishLoadConstruct(entry, shadersStorage);
break;
case ee_assets::MATERIAL:
FinishLoadPlain(entry, materialsStorage);
break;
case ee_assets::IMAGE:
break;
case ee_assets::BINARY:
default:
FinishLoadConstruct(entry, binaryStorage);
break;
}
entry.fileOwner->Close(*entry.file);
#ifndef NDEBUG
LOG(INFO, "Asset loaded: [%hs]", entry.file->debugPath.generic_u8string().c_str());
#endif
entry.file = nullptr;
asset.isReady.store(true);
}
}
std::erase_if(loadingEntries, [](const AssetLoadingEntry &entry) {
return entry.file == nullptr;
});
}
void Release() override
{
for (AssetInfo &asset : activeAssets)
{
//TODO: LEAK! Free resource
if (asset.resourceAddress->resource.load() != nullptr)
{
void* resource = asset.resourceAddress->resource.exchange(nullptr, std::memory_order_acq_rel);
//if (resource)
//Deallocate(resource);
}
Deallocate(asset.resourceAddress);
}
activeAssets.clear();
for (auto &mapping : assetsMapping)
Deallocate(mapping.second);
assetsMapping.clear();
binaryStorage.clear();
mapMeshesStorage.clear();
shadersStorage.clear();
materialsStorage.clear();
LOG(INFO, "Asset Streaming is released");
isRunning.store(false);
}
private:
IFileSystem *diskSystem;
std::vector<AssetInfo> activeAssets;
std::unordered_map<std::filesystem::path, std::uint32_t> assetsIdMapping;
std::unordered_map<std::uint32_t, AssetHandle*> assetsMapping;
std::vector<std::size_t> freeAssetSlots;
std::vector<AssetLoadingEntry> loadingEntries;
std::vector<ee_assets::AssetBinary*> binaryStorage;
std::vector<ee_assets::AssetSave*> savesStorage;
std::vector<ee_assets::AssetMapMesh*> mapMeshesStorage;
std::vector<ee_assets::AssetShader*> shadersStorage;
std::vector<ee_assets::AssetMaterial*> materialsStorage;
void LoadCmd(AssetStreamCommand &command)
{
LOG(INFO, "LOAD command: [%hs]", command.targetPath.generic_u8string().c_str());
/* TODO: process path to select FS */
/* TODO: select between async load and sync job task */
//Async load of full file without mmap in one thread
FileHandle &file = diskSystem->Open(command.targetPath, IFileSystem::FILE_OPEN_READ);
diskSystem->ReadAsync(file, static_cast<std::uint32_t>(file.size), IFileSystem::FILE_READ_SEQUENTIAL);
AssetHandle* handle = Allocate<AssetHandle>();
AssetInfo asset(nullptr);
asset.type = command.targetType;
std::size_t assetPos = 0;
if (freeAssetSlots.size() > 0)
{
assetPos = freeAssetSlots.back();
Deallocate(activeAssets[assetPos].resourceAddress);
activeAssets[assetPos] = std::move(asset);
freeAssetSlots.pop_back();
}
else
{
activeAssets.push_back(std::move(asset));
assetPos = activeAssets.size() - 1;
}
handle->slot = assetPos;
auto assetPath = command.targetPath.generic_u8string();
std::uint32_t assetId = assetsIdMapping[command.targetPath] =
XXH32(assetPath.data(), assetPath.size(), ASSETS_HASH_SPACE);
handle->id = assetId;
assetsMapping[assetId] = handle;
AssetLoadingEntry loadingEntry {
.handle = *handle,
.file = &file,
.fileOwner = diskSystem
};
loadingEntries.push_back(loadingEntry);
command.type = AssetStreamCommand::COMPLETED;
}
void FindCmd(AssetStreamCommand &command)
{
std::uint32_t id = command.targetId;
if (id == AssetHandle::INVALID_ASSET_ID)
{
std::unordered_map<std::filesystem::path, std::uint32_t>::iterator idMapping = assetsIdMapping.find(command.targetPath);
if (idMapping == assetsIdMapping.end())
return;
id = idMapping->second;
}
std::unordered_map<std::uint32_t, AssetHandle*>::iterator assetMapping = assetsMapping.find(id);
if (assetMapping == assetsMapping.end())
return;
if (command.out != nullptr)
command.out->store(assetMapping->second);
command.type = AssetStreamCommand::COMPLETED;
}
void GetCmd(AssetStreamCommand &command)
{
if (activeAssets.size() <= command.target.slot)
return;
if (!activeAssets[command.target.slot].isReady)
return;
if (command.out != nullptr)
command.out->store(activeAssets[command.target.slot].resourceAddress->resource);
command.type = AssetStreamCommand::COMPLETED;
}
template<ee_assets::Asset T> void FinishLoadConstruct(AssetLoadingEntry &loaded, std::vector<T*> &storage)
{
T* resource = Allocate<T>(loaded.file->data, loaded.file->size);
storage.push_back(resource);
Redirect(loaded.handle, storage.back());
}
template<ee_assets::Asset T> void FinishLoadPlain(AssetLoadingEntry &loaded, std::vector<T*> &storage)
{
T* resource = Allocate<T>();
if (loaded.file->size < sizeof(T))
LOG(CRITICAL, "File size is less than needed for asset of type: %d", typeid(T).name());
std::memcpy(resource, loaded.file->data, sizeof(T));
storage.push_back(resource);
Redirect(loaded.handle, storage.back());
}
template<ee_assets::Asset T> void ClearStorage(std::vector<T*> &storage)
{
for (T* &resource : storage)
{
if (resource != nullptr)
Deallocate(resource);
}
storage.clear();
}
};
}

View File

@ -0,0 +1,51 @@
module;
#include <cstdint>
export module Client;
import worker;
import GameData;
import DataChannel;
import AssetManager;
export
{
struct ClientCommand : public ee_utils::WorkerCommand
{
enum Type : std::uint8_t
{
NONE,
SEND,
COMPLETED
};
};
class Client final : public ee_utils::ThreadedWorker<ClientCommand, ClientCommand::COMPLETED>
{
public:
Client();
~Client();
void Init();
//void Send(); -> Request(SEND)
const IDataChannel* GetConnection();
void ConnectServer();
void LoadSave(const AssetHandle save);
protected:
void Update() override;
void Release() override;
private:
NetworkChannel *networkConnection = nullptr;
AssetHandle save;
void SendCmd(ClientCommand &command);
void ViewModelSubject(ee_data::Subject subject);
};
}

View File

@ -0,0 +1,24 @@
module;
export module DataChannel;
export
{
class IDataChannel
{
public:
// virtual void Request() = 0;
// virtual void Listen() = 0;
// virtual void Mute() = 0;
};
class NetworkChannel final : public IDataChannel
{
};
class ThreadChannel final : public IDataChannel
{
};
}

View File

@ -0,0 +1,59 @@
module;
#include <GLFW/glfw3.h>
export module Engine;
export import Render;
export import Input;
export import AssetManager;
export import Client;
import allocator;
import worker;
export
{
class Engine
{
public:
inline static GLFWwindow *window;
inline static ee_engine::IInput *input;
inline static AssetManager *assetManager;
inline static ee_engine::IRenderer *renderer;
inline static Client *client;
static void Shutdown()
{
if (input != nullptr)
Deallocate(input);
if (renderer != nullptr)
{
renderer->Shutdown();
Deallocate(renderer);
}
renderer = nullptr;
WaitThreadShutdown(assetManager);
WaitThreadShutdown(client);
glfwDestroyWindow(window);
glfwTerminate();
}
template <ee_utils::IThreadedWorker T>
static void WaitThreadShutdown(T* &thread)
{
if (thread != nullptr)
{
thread->Shutdown();
while(thread->isRunning.load()) {}
Deallocate(thread);
}
thread = nullptr;
}
private:
Engine();
};
}

View File

@ -0,0 +1,72 @@
module;
#include <filesystem>
#include <vector>
#include <cstdint>
#include <cstddef>
#include <ee_utils/platform.hpp>
export module FileSystem;
export
{
struct FileHandle {
std::size_t id = 0;
std::size_t size = 0;
std::size_t bufferSize = 0;
std::uint8_t *data = nullptr;
std::uint8_t *buffer = nullptr;
#ifndef NDEBUG
std::filesystem::path debugPath;
#endif
#ifdef _WIN32
HANDLE win32Handle = INVALID_HANDLE_VALUE;
OVERLAPPED overlapped;
#endif
#ifdef __linux__
#endif
};
class IFileSystem {
public:
inline static constexpr std::uint32_t FILE_OPEN_NONE = 0;
inline static constexpr std::uint32_t FILE_OPEN_READ = (1 << 0);
inline static constexpr std::uint32_t FILE_OPEN_WRITE = (1 << 1);
inline static constexpr std::uint32_t FILE_OPEN_MMAP = (1 << 2);
inline static constexpr std::uint32_t FILE_READ_SEQUENTIAL = 0;
inline static constexpr std::uint32_t FILE_READ_RANDOM_ACCESS = (1 << 0);
virtual FileHandle &Open(const std::filesystem::path &path, const std::uint32_t flags) = 0;
virtual void ReadAsync(FileHandle &file, const std::uint32_t bufferSize, const std::uint32_t flags) = 0;
virtual bool PollReady(FileHandle &file) = 0;
virtual void Close(FileHandle &file) = 0;
virtual ~IFileSystem();
protected:
std::vector<FileHandle *> activeFiles;
std::vector<FileHandle *> failedFiles;
std::vector<std::size_t> freeFileSlots;
};
class NativeFileSystem : public IFileSystem {
public:
FileHandle &Open(const std::filesystem::path &path, const std::uint32_t flags) override;
void ReadAsync(FileHandle &file, const std::uint32_t bufferSize, std::uint32_t flags) override;
bool PollReady(FileHandle &file) override;
void Close(FileHandle &file) override;
private:
};
}

View File

@ -0,0 +1,16 @@
module;
#include <GLFW/glfw3.h>
export module GLFWInput;
import Input;
export {
class GLFWInput : public ee_engine::IInput
{
public:
GLFWInput(ee_engine::InputHandlers handlers);
void Poll() override;
};
}

View File

@ -1,7 +1,10 @@
#include "Actions.hpp"
#include "Engine.hpp"
module;
#include <cmath>
module Actions;
import Engine;
ee_engine::InputHandlers Actions::mappings {
.zooming = Zoom,
.mapScroll = MapScroll

View File

@ -1,344 +0,0 @@
#include "compile_config.hpp"
#include "AssetManager.hpp"
#include <ee_utils/allocator.hpp>
#include "Logger.h"
#include <stdexcept>
#include <xxhash.h>
#include <cstring>
AssetInfo::AssetInfo(void *resource)
{
this->resourceAddress = Allocate<AssetAddress>();
this->resourceAddress->resource.store(resource);
}
AssetInfo::AssetInfo(AssetInfo&& other) noexcept :
type(other.type),
resourceAddress(other.resourceAddress)
{
isReady.store(other.isReady.load(std::memory_order_relaxed), std::memory_order_relaxed);
other.resourceAddress = nullptr;
other.isReady.store(false, std::memory_order_relaxed);
}
AssetInfo& AssetInfo::operator=(AssetInfo&& other) noexcept
{
if (this != &other)
{
type = other.type;
resourceAddress = other.resourceAddress;
isReady.store(other.isReady.load(std::memory_order_relaxed), std::memory_order_relaxed);
other.resourceAddress = nullptr;
other.isReady.store(false, std::memory_order_relaxed);
}
return *this;
}
void AssetManager::Redirect(AssetHandle &handle, void* resource)
{
AssetInfo &asset = activeAssets[handle.slot];
if (asset.resourceAddress == nullptr)
{
LOG(WARNING, "Attempt to redirect empty handle");
return;
}
if (asset.resourceAddress->resource.load() != nullptr)
{
void* oldResource = asset.resourceAddress->resource.exchange(nullptr, std::memory_order_acq_rel);
if (oldResource)
//Deallocate(asset.resourceAddress->resource.load());
Deallocate(oldResource);
}
asset.resourceAddress->resource.store(resource);
}
void AssetManager::Free(AssetHandle &handle)
{
AssetInfo &asset = activeAssets[handle.slot];
Redirect(handle, nullptr);
Deallocate(asset.resourceAddress);
asset.resourceAddress = nullptr;
freeAssetSlots.push_back(handle.slot);
}
AssetManager::AssetManager()
{
diskSystem = Allocate<NativeFileSystem>();
}
AssetManager::~AssetManager()
{
Deallocate(diskSystem);
}
void AssetManager::Request(const AssetStreamCommand &command)
{
commands.push(command);
}
void AssetManager::Request(const AssetStreamCommand::Type &type)
{
commands.push(AssetStreamCommand {.type = type});
}
void AssetManager::LoadAsync(ee_assets::AssetType type, const std::filesystem::path &path)
{
AssetStreamCommand command {
.type = AssetStreamCommand::LOAD,
.targetPath = path,
.targetType = type
};
Request(command);
}
AssetHandle& AssetManager::Find(const std::filesystem::path &path)
{
std::atomic<void*> result;
AssetStreamCommand command {
.type = AssetStreamCommand::FIND,
.targetPath = path,
.out = &result
};
Request(command);
while (command.out->load() == nullptr);
return *reinterpret_cast<AssetHandle*>(command.out->load());
}
AssetHandle& AssetManager::Find(const uint32_t &id)
{
std::atomic<void*> result;
AssetStreamCommand command {
.type = AssetStreamCommand::FIND,
.targetId = id,
.out = &result
};
Request(command);
while (command.out->load() == nullptr);
return *reinterpret_cast<AssetHandle*>(command.out->load());
}
void AssetManager::Start()
{
if (isRunning) throw std::logic_error("Asset Streaming can be started only once");
isRunning.store(true);
streamingThread = std::jthread([this](std::stop_token stop) {
this->stoken = &stop;
Threading();
});
}
void AssetManager::Shutdown()
{
streamingThread.request_stop();
}
void AssetManager::Threading()
{
Logger::trace->Register_Thread(L"Asset Stream", 0);
LOG(INFO, "Asset Streaming thread started");
Loop();
Release();
}
void AssetManager::Loop()
{
AssetStreamCommand commandBuffer;
std::vector<AssetStreamCommand> incompleteCommands;
while (!stoken->stop_requested())
{
while (commands.pop(commandBuffer))
{
ProcessCommand(commandBuffer);
if (commandBuffer.type != AssetStreamCommand::COMPLETED)
incompleteCommands.push_back(commandBuffer);
}
for (AssetStreamCommand &command : incompleteCommands)
{
commands.push(command);
}
incompleteCommands.clear();
// TODO: FinishLoadTasks()
for (AssetLoadingEntry &entry : loadingEntries)
{
bool isReady = entry.fileOwner->PollReady(*entry.file);
if (isReady)
{
AssetInfo& asset = activeAssets[entry.handle.slot];
switch (asset.type)
{
case ee_assets::MAP_MESH:
FinishLoadConstruct(entry, mapMeshesStorage);
break;
case ee_assets::SAVE:
FinishLoadConstruct(entry, savesStorage);
break;
case ee_assets::SHADER:
FinishLoadConstruct(entry, shadersStorage);
break;
case ee_assets::MATERIAL:
FinishLoadPlain(entry, materialsStorage);
break;
case ee_assets::IMAGE:
break;
case ee_assets::BINARY:
default:
FinishLoadConstruct(entry, binaryStorage);
break;
}
entry.fileOwner->Close(*entry.file);
#ifndef NDEBUG
LOG(INFO, "Asset loaded: [%hs]", entry.file->debugPath.generic_u8string().c_str());
#endif
entry.file = nullptr;
asset.isReady.store(true);
}
}
std::erase_if(loadingEntries, [](const AssetLoadingEntry &entry) {
return entry.file == nullptr;
});
}
}
void AssetManager::ProcessCommand(AssetStreamCommand &command)
{
// TODO: method for each command
switch(command.type)
{
case AssetStreamCommand::LOAD:
{
LOG(INFO, "LOAD command: [%hs]", command.targetPath.generic_u8string().c_str());
/* TODO: process path to select FS */
/* TODO: select between async load and sync job task */
//Async load of full file without mmap in one thread
FileHandle &file = diskSystem->Open(command.targetPath, FILE_OPEN_READ);
diskSystem->ReadAsync(file, static_cast<uint32_t>(file.size), FILE_READ_SEQUENTIAL);
AssetHandle* handle = Allocate<AssetHandle>();
AssetInfo asset(nullptr);
asset.type = command.targetType;
size_t assetPos = 0;
if (freeAssetSlots.size() > 0)
{
assetPos = freeAssetSlots.back();
Deallocate(activeAssets[assetPos].resourceAddress);
activeAssets[assetPos] = std::move(asset);
freeAssetSlots.pop_back();
}
else
{
activeAssets.push_back(std::move(asset));
assetPos = activeAssets.size() - 1;
}
handle->slot = assetPos;
auto assetPath = command.targetPath.generic_u8string();
uint32_t assetId = assetsIdMapping[command.targetPath] =
XXH32(assetPath.data(), assetPath.size(), ASSETS_HASH_SPACE);
handle->id = assetId;
assetsMapping[assetId] = handle;
AssetLoadingEntry loadingEntry {
.handle = *handle,
.file = &file,
.fileOwner = diskSystem
};
loadingEntries.push_back(loadingEntry);
command.type = AssetStreamCommand::COMPLETED;
break;
}
case AssetStreamCommand::FIND:
{
uint32_t id = command.targetId;
if (id == INVALID_ASSET_ID)
{
std::unordered_map<std::filesystem::path, uint32_t>::iterator idMapping = assetsIdMapping.find(command.targetPath);
if (idMapping == assetsIdMapping.end())
break;
id = idMapping->second;
}
std::unordered_map<uint32_t, AssetHandle*>::iterator assetMapping = assetsMapping.find(id);
if (assetMapping == assetsMapping.end())
break;
command.out->store(assetMapping->second);
command.type = AssetStreamCommand::COMPLETED;
break;
}
case AssetStreamCommand::GET:
{
if (activeAssets.size() <= command.target.slot)
break;
if (!activeAssets[command.target.slot].isReady)
break;
command.out->store(activeAssets[command.target.slot].resourceAddress->resource);
command.type = AssetStreamCommand::COMPLETED;
break;
}
default:
{
LOG(WARNING, "Unhandled command. Type id: %d", command.type);
break;
}
}
}
void AssetManager::Release()
{
for (AssetInfo &asset : activeAssets)
{
if (asset.resourceAddress->resource.load() != nullptr)
{
void* resource = asset.resourceAddress->resource.exchange(nullptr, std::memory_order_acq_rel);
if (resource)
Deallocate(resource);
}
Deallocate(asset.resourceAddress);
}
activeAssets.clear();
for (auto &mapping : assetsMapping)
Deallocate(mapping.second);
assetsMapping.clear();
binaryStorage.clear();
mapMeshesStorage.clear();
shadersStorage.clear();
materialsStorage.clear();
LOG(INFO, "Asset Streaming is released");
isRunning.store(false);
}
void AssetManager::LoadDir(ee_assets::AssetType type, const std::filesystem::path &path)
{
for (const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(path))
{
if (entry.is_directory()) continue;
LoadAsync(type, entry.path());
}
}

93
engine/src/Client.cpp Normal file
View File

@ -0,0 +1,93 @@
module;
#include "Logger.h"
#include <cstdint>
#include <ee_utils/utils.hpp>
#include <stdexcept>
module Client;
import allocator;
import MapView;
import Engine;
Client::Client() : ee_utils::ThreadedWorker<ClientCommand, ClientCommand::COMPLETED>(L"Client")
{
commandsMapping[ClientCommand::SEND] = MAP_WORKER_COMMAND(ClientCommand, SendCmd);
}
void Client::Init()
{
if (Engine::renderer == nullptr || !Engine::renderer->isReady())
{
LOG(CRITICAL, "Renderer is not ready. Client cannot be created");
throw std::logic_error("Renderer is not ready");
}
// TEST
ee_assets::AssetSave &asset = Engine::assetManager->Get<ee_assets::AssetSave>(save);
ViewModelSubject(asset.content.subjects[0]);
ViewModelSubject(asset.content.subjects[1]);
}
const IDataChannel* Client::GetConnection()
{
return networkConnection;
}
void Client::ConnectServer()
{
if (networkConnection != nullptr)
Deallocate(networkConnection);
networkConnection = Allocate<NetworkChannel>();
}
void Client::LoadSave(const AssetHandle save)
{
this->save = save;
}
void Client::Update()
{
}
void Client::Release()
{
}
void Client::SendCmd(ClientCommand &command)
{
command.type = ClientCommand::COMPLETED;
}
void Client::ViewModelSubject(ee_data::Subject subject)
{
for (const std::uint32_t region : subject.regions)
{
ee_engine::ProvinceSSBO vm {
.regionId = region,
.color = subject.color
};
ee_engine::RendererCommand cmd;
cmd.type = ee_engine::RendererCommand::UPDATE_REGION;
cmd.region = {.id = region, .data = vm};
Engine::renderer->Request(cmd);
}
}
Client::~Client()
{
if (networkConnection != nullptr)
Deallocate(networkConnection);
}

View File

@ -1,32 +0,0 @@
#include "Engine.hpp"
#include <ee_utils/allocator.hpp>
GLFWwindow* Engine::window;
ee_engine::IInput* Engine::input;
AssetManager* Engine::assetManager;
ee_engine::IRenderer* Engine::renderer;
void Engine::Shutdown()
{
if (input != nullptr)
Deallocate(input);
if (renderer != nullptr)
{
renderer->Shutdown();
Deallocate(renderer);
}
renderer = nullptr;
if (assetManager != nullptr)
{ // Wait for asset manager to clean and shutdown
assetManager->Shutdown();
while(assetManager->isRunning.load()) {}
Deallocate(assetManager);
}
assetManager = nullptr;
glfwDestroyWindow(window);
glfwTerminate();
}

View File

@ -1,6 +1,13 @@
#include "FileSystem.hpp"
module;
#include "Logger.h"
#include <ee_utils/allocator.hpp>
#include <filesystem>
#include <cstdint>
#include <cstddef>
module FileSystem;
import allocator;
IFileSystem::~IFileSystem()
{
@ -12,7 +19,7 @@ IFileSystem::~IFileSystem()
#ifdef _WIN32
FileHandle& NativeFileSystem::Open(const std::filesystem::path &path, const uint32_t flags)
FileHandle& NativeFileSystem::Open(const std::filesystem::path &path, const std::uint32_t flags)
{
FileHandle* result = Allocate<FileHandle>();
@ -51,7 +58,7 @@ FileHandle& NativeFileSystem::Open(const std::filesystem::path &path, const uint
if (freeFileSlots.size() > 0)
{
size_t pos = freeFileSlots.back();
std::size_t pos = freeFileSlots.back();
result->id = pos;
activeFiles[pos] = result;
freeFileSlots.pop_back();
@ -65,12 +72,12 @@ FileHandle& NativeFileSystem::Open(const std::filesystem::path &path, const uint
return *result;
}
void NativeFileSystem::ReadAsync(FileHandle &file, const uint32_t bufferSize, const uint32_t flags)
void NativeFileSystem::ReadAsync(FileHandle &file, const std::uint32_t bufferSize, const std::uint32_t flags)
{
file.overlapped = {
.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)
};
file.buffer = AllocateArray<uint8_t>(bufferSize);
file.buffer = AllocateArray<std::uint8_t>(bufferSize);
file.bufferSize = bufferSize;
BOOL isSync = ReadFile(

View File

@ -1,6 +1,10 @@
#include "GLFWInput.hpp"
#include "Engine.hpp"
module;
#include "Logger.h"
#include <GLFW/glfw3.h>
module GLFWInput;
import Engine;
GLFWInput::GLFWInput(ee_engine::InputHandlers handlers) : ee_engine::IInput(handlers)
{

View File

@ -1,14 +1,14 @@
import allocator;
import Engine;
import Actions;
import MagnumRender;
import GLFWInput;
#include "Logger.h"
#include <ee_magnum/Renderer.hpp>
#include <ee_utils/allocator.hpp>
#include "compile_config.hpp"
#include "GLFWInput.hpp"
#include "Actions.hpp"
#include "Engine.hpp"
#include <GLFW/glfw3.h>
#include <vector>
IP7_Trace* Logger::trace;
@ -37,14 +37,14 @@ int main(int argc, char **argv)
glfwMakeContextCurrent(window);
Engine::window = window;
// Setup Asset Manager
// Setup Asset Manager TODO: builder
Engine::assetManager = Allocate<AssetManager>();
Engine::assetManager->Start();
Engine::assetManager->LoadDir(ee_assets::SHADER, "assets/shaders");
Engine::assetManager->LoadDir(ee_assets::MATERIAL, "assets/materials");
// Load assets
Engine::assetManager->LoadAsync(ee_assets::MAP_MESH, "assets/map/states.map");
Engine::assetManager->LoadAsync(ee_assets::MAP_MESH, "assets/map/regions.map");
Engine::assetManager->LoadAsync(ee_assets::SAVE, "assets/scenarios/empires.save");
// Setup input manager TODO: builder
@ -53,7 +53,7 @@ int main(int argc, char **argv)
// Setup renderer TODO: builder
Engine::renderer = reinterpret_cast<ee_engine::IRenderer*>(Allocate<ee_magnum::MagnumRenderer>(window));
ee_assets::AssetMapMesh &states = Engine::assetManager->FindOnce<ee_assets::AssetMapMesh>("assets/map/states.map");
ee_assets::AssetMapMesh &states = Engine::assetManager->FindOnce<ee_assets::AssetMapMesh>("assets/map/regions.map");
Engine::renderer->RegisterMapMesh(&states.content);
//TODO: ShaderRefs builder from set of AssetMaterial
@ -76,9 +76,18 @@ int main(int argc, char **argv)
Engine::renderer->ResizeWindow(width, height);
});
// Setup Client TODO: builder
Engine::client = Allocate<Client>();
AssetHandle save = Engine::assetManager->Find("assets/scenarios/empires.save");
Engine::client->LoadSave(save);
Engine::client->Init();
Engine::client->Start();
// Game loop
ee_engine::RendererCommand renderCmdBuffer; std::vector<ee_engine::RendererCommand> renderIncompletesBuffer;
while(!glfwWindowShouldClose(window))
{
Engine::renderer->ProcessQueue(renderCmdBuffer, renderIncompletesBuffer);
Engine::renderer->Draw();
glfwSwapBuffers(window);
@ -96,3 +105,10 @@ int main(int argc, char **argv)
return 0;
}
// TODO: Rename states to regions in old sources
// TODO: Remove unneccesary address providing like (const type &arg) for small primitives
// TODO: Use CRTP where possible in classes with virtual/override
// TODO: Use final in classes with override
// TODO: Implementations of API as partition modules
// TODO: Leaks detection

View File

@ -53,7 +53,7 @@ else()
enable_cxx_compiler_flag_if_supported("-Wno-long-long")
enable_cxx_compiler_flag_if_supported("-Wno-missing-field-initializers")
enable_cxx_compiler_flag_if_supported("-Wno-unknown-pragmas")
enable_cxx_compiler_flag_if_supported("-fPIC")
#enable_cxx_compiler_flag_if_supported("-fPIC")
macro(set_ide_folder _project_name)
endmacro()

View File

@ -36,11 +36,11 @@ if(P7_BUILD_SHARED)
target_link_libraries(${PROJECT_NAME}-shared ${P7_PLATFORM_LIBS})
endif()
set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON)
#set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON)
if(P7_BUILD_SHARED)
set_property(TARGET ${PROJECT_NAME}-shared PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()
#if(P7_BUILD_SHARED)
# set_property(TARGET ${PROJECT_NAME}-shared PROPERTY POSITION_INDEPENDENT_CODE ON)
#endif()
target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../Headers/")

View File

@ -1,23 +1,6 @@
cmake_minimum_required (VERSION 3.8)
include(${CMAKE_SOURCE_DIR}/cmake/library.cmake)
if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project (magnum-renderer)
# List sources
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS
"src/*.cpp"
"src/*.c"
"include/*.hpp"
"include/*.h"
)
set(LIB magnum-renderer)
# Find dependencies
@ -38,20 +21,14 @@ find_package(Magnum REQUIRED
# Link library
add_library(${PROJECT_NAME} ${SOURCES})
add_library_target(${LIB})
target_link_libraries(${PROJECT_NAME} PUBLIC engine-api)
target_link_libraries(${PROJECT_NAME} PUBLIC utils)
target_link_libraries(${LIB} PUBLIC engine-api)
target_link_libraries(${LIB} PUBLIC utils)
target_link_libraries(${PROJECT_NAME} PUBLIC p7)
target_link_libraries(${PROJECT_NAME} PRIVATE
target_link_libraries(${LIB} PUBLIC p7)
target_link_libraries(${LIB} PRIVATE
Magnum::GL
Magnum::GlfwApplication
Magnum::Shaders
Magnum::Magnum)
target_include_directories(${PROJECT_NAME} PUBLIC include/)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
endif()

View File

@ -1,27 +1,27 @@
#pragma once
module;
#undef min
#undef max
#define NOMINMAX
#include <Magnum/GL/AbstractShaderProgram.h>
#include <Magnum/GL/Shader.h>
#include <Magnum/GL/Version.h>
#include <Magnum/Math/Matrix4.h>
#include <glm/glm.hpp>
#include <string>
#include <cstdint>
#define PROVINCES_SSBO_BINDING 0
#define CAMERA_UNIFORM 1
//TODO: push constant
#define TRANSFORM_UNIFORM 2
#define PROVINCES_ID_UNIFORM 3
export module Materials;
using namespace Magnum;
namespace ee_magnum
export namespace ee_magnum
{
class BaseMaterial : public GL::AbstractShaderProgram
{
public:
inline static constexpr std::int32_t CAMERA_BINDING = 1;
//TODO: push constant
inline static constexpr std::int32_t TRANSFORM_BINDING = 2;
void setModel(const Matrix4 &model);
void setViewProjection(const Matrix4 &vewProjection);
@ -32,8 +32,11 @@ namespace ee_magnum
class ProvinceMaterial : public BaseMaterial
{
public:
inline static constexpr std::int32_t PROVINCES_DATA_BINDING = 0;
inline static constexpr std::int32_t PROVINCES_ID_BINDING = 3;
explicit ProvinceMaterial(const std::string &vertSrc, const std::string &fragSrc);
void setId(const uint32_t &id);
void setId(const std::uint32_t &id);
};
}

View File

@ -1,21 +1,23 @@
#pragma once
module;
#undef min
#undef max
#define NOMINMAX
#include <Magnum/Platform/GLContext.h>
#include <Magnum/GL/Mesh.h>
#include <Magnum/GL/Buffer.h>
#include <GLFW/glfw3.h>
#include <cstddef>
#include "Materials.hpp"
#include <ee_engine_api/RenderInterface.hpp>
export module MagnumRender;
import MapView;
import Materials;
import Render;
using namespace Magnum;
namespace ee_magnum
export namespace ee_magnum
{
class MagnumRenderer : public ee_engine::IRenderer
class MagnumRenderer final : public ee_engine::IRenderer
{
public:
MagnumRenderer(GLFWwindow *window);
@ -31,6 +33,8 @@ namespace ee_magnum
void ApplyCamera() override;
virtual void UpdateProvinces(const std::size_t index, const std::vector<ee_engine::ProvinceSSBO> &data) override;
private:
Platform::GLContext context;

View File

@ -1,5 +1,17 @@
#include <ee_magnum/Materials.hpp>
module;
#define NOMINMAX
#include <Magnum/GL/AbstractShaderProgram.h>
#include <Magnum/GL/Shader.h>
#include <Magnum/GL/Version.h>
#include <Magnum/Math/Matrix4.h>
#include "Logger.h"
#include <string>
#include <cstdint>
module Materials;
ee_magnum::BaseMaterial::BaseMaterial(const std::string &vertSrc, const std::string &fragSrc)
{
@ -22,12 +34,12 @@ ee_magnum::BaseMaterial::BaseMaterial(const std::string &vertSrc, const std::str
void ee_magnum::BaseMaterial::setModel(const Matrix4 &m)
{
setUniform(TRANSFORM_UNIFORM, m);
setUniform(TRANSFORM_BINDING, m);
}
void ee_magnum::BaseMaterial::setViewProjection(const Matrix4 &vp)
{
setUniform(CAMERA_UNIFORM, vp);
setUniform(CAMERA_BINDING, vp);
}
ee_magnum::ProvinceMaterial::ProvinceMaterial(const std::string &vertSrc, const std::string &fragSrc): BaseMaterial(vertSrc, fragSrc)
@ -37,5 +49,5 @@ ee_magnum::ProvinceMaterial::ProvinceMaterial(const std::string &vertSrc, const
void ee_magnum::ProvinceMaterial::setId(const uint32_t &id)
{
setUniform(PROVINCES_ID_UNIFORM, id);
setUniform(PROVINCES_ID_BINDING, id);
}

View File

@ -1,17 +1,26 @@
#include "Logger.h"
#include "ee_utils/allocator.hpp"
#include <ee_magnum/Renderer.hpp>
module;
#define NOMINMAX
#include "Logger.h"
#include <Magnum/Platform/GLContext.h>
#include <Magnum/GL/Mesh.h>
#include <Magnum/GL/Buffer.h>
#include <Magnum/GL/DefaultFramebuffer.h>
#include <Magnum/GL/Mesh.h>
#include <Magnum/Math/Color.h>
#include <Magnum/Shaders/VertexColor.h>
#include <Corrade/Utility/Debug.h>
#include <GLFW/glfw3.h>
#include <cstdint>
#include <cstddef>
#include <glm/gtc/type_ptr.hpp>
#include <string>
module MagnumRender;
import allocator;
using namespace Math::Literals;
ee_magnum::MagnumRenderer::MagnumRenderer(GLFWwindow *window) : IRenderer(window)
@ -28,6 +37,7 @@ void ee_magnum::MagnumRenderer::Init()
LoadShaders();
LoadProvinces();
_isReady = true;
LOG(INFO, "Renderer initialized");
}
@ -39,7 +49,7 @@ void ee_magnum::MagnumRenderer::Clear()
void ee_magnum::MagnumRenderer::DrawMap()
{
ApplyCamera();
for (uint32_t i = 0; i < provinceMeshes.size(); i++)
for (std::uint32_t i = 0; i < provinceMeshes.size(); i++)
{
provinceMaterial->setId(i);
provinceMaterial->draw(provinceMeshes[i]);
@ -80,7 +90,9 @@ void ee_magnum::MagnumRenderer::LoadShaders()
void ee_magnum::MagnumRenderer::LoadProvinces()
{
std::vector<ee_engine::ProvinceSSBO> provincesData;
for (auto& [state, provs]: mapMesh->states)
std::size_t regionIndex = 0;
for (auto& [region, provs]: mapMesh->regions)
{
for (ee_engine::MapMesh::Province &province: provs)
{
@ -94,22 +106,31 @@ void ee_magnum::MagnumRenderer::LoadProvinces()
provinceMeshes.push_back(std::move(stateMesh));
ee_engine::ProvinceSSBO data {
.stateId = state,
.color = state
.regionId = region,
//.color = region
.color = 0xFFFFFF
};
provincesData.push_back(data);
}
regionsMapping[region] = {.startIndex = regionIndex, .provincesCount = provs.size()};
regionIndex += provs.size() * sizeof(ee_engine::ProvinceSSBO);
}
LOG(INFO, "Registered %d province meshes in renderer", provinceMeshes.size());
provinceSSBO.setTargetHint(GL::Buffer::TargetHint::ShaderStorage);
provinceSSBO.setData(provincesData, GL::BufferUsage::DynamicDraw);
provinceSSBO.bind(GL::Buffer::Target::ShaderStorage, PROVINCES_SSBO_BINDING);
provinceSSBO.bind(GL::Buffer::Target::ShaderStorage, ProvinceMaterial::PROVINCES_DATA_BINDING);
}
void ee_magnum::MagnumRenderer::UpdateProvinces(const std::size_t index, const std::vector<ee_engine::ProvinceSSBO> &data)
{
provinceSSBO.setSubData(index, data);
}
void ee_magnum::MagnumRenderer::Shutdown()
{
provinceMeshes.clear();

View File

@ -13,10 +13,13 @@ project (compile_assets)
find_package(nlohmann_json CONFIG REQUIRED)
find_package(xxHash CONFIG REQUIRED)
add_executable(${PROJECT_NAME} compile_assets.cpp)
add_executable(${PROJECT_NAME} compile_assets.cpp define/regions.def)
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json)
target_link_libraries(${PROJECT_NAME} PRIVATE xxHash::xxhash)
target_link_libraries(${PROJECT_NAME} PUBLIC engine-api)
target_link_libraries(${PROJECT_NAME} PUBLIC server)
target_compile_options(${PROJECT_NAME} PRIVATE /Zc:preprocessor)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)

View File

@ -1,3 +1,6 @@
import AssetData;
import GameData;
#include <cstdlib>
#include <filesystem>
#include <fstream>
@ -7,7 +10,6 @@
#include <string>
#include <charconv>
#include <functional>
#include <ee_engine_api/AssetData.hpp>
using json = nlohmann::json;
@ -23,11 +25,19 @@ namespace ee_data
)
}
namespace ee_data
{
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
GameState,
subjects
)
}
namespace ee_assets
{
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
AssetSave,
subjects
content
)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(

View File

@ -2,18 +2,14 @@
#define MAP(name, color)
#endif
MAP(elnia, 0xFF0000)
MAP(south_hleria, 0xFFD800)
MAP(siburia_central, 0xFF00DC)
MAP(siburia, 0xFF0000)
MAP(savatia, 0xFF0000)
MAP(east_monfua, 0xFF0000)
MAP(pepene, 0xAA00FF)
MAP(scarna, 0xFF007F)
MAP(dineria, 0x0400FF)
MAP(perdania, 0xFF6A00)
MAP(dineria, 0xFFFF00)
MAP(perdania, 0x7FFF00)
MAP(arstinia, 0xFF0000)
MAP(ortostan, 0xFF0000)
MAP(zerandia, 0xFF0000)
MAP(mgahvatia, 0xFF0000)
MAP(alende, 0x0000FF)
MAP(zerandia, 0xFF5400)
MAP(mgahvatia, 0x5400FF)
#undef MAP

View File

@ -1,33 +1,26 @@
{
"content": {
"subjects": [
{
"tag": "ESH",
"tag": "FSC",
"color": "#FF0000",
"regions": [
"elnia",
"south_hleria",
"siburia_central",
"siburia",
"savatia",
"east_monfua"
"pepene",
"scarna"
]
},
{
"tag": "UCB",
"color": "#00FF00",
"color": "#0000FF",
"regions": [
"dineria",
"perdania",
"arstinia",
"ortostan",
"alende",
"zerandia",
"mgahvatia"
]
}
]
}
}

BIN
sdk/map/regions.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

View File

@ -1,2 +1,2 @@
@echo off
"%CD%/../../out/build/DEBUG/sdk/map/convert_map.exe" states.bmp ../../assets/map/states.map
"%CD%/../../out/build/DEBUG/sdk/map/convert_map.exe" regions.bmp ../../assets/map/regions.map

View File

@ -1,12 +0,0 @@
{
"configurations": [
{
"type": "cmake",
"request": "launch",
"name": "Debug portfile(s)",
"cmakeDebugType": "external",
"pipeName": "\\\\.\\pipe\\vcpkg_ext_portfile_dbg",
"preLaunchTask": "Debug vcpkg commands"
}
]
}

View File

@ -1,17 +0,0 @@
cmake_minimum_required (VERSION 3.8)
if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project ("server")
add_subdirectory ("economy")
add_subdirectory ("economy/test")
add_subdirectory ("ecs")
add_subdirectory ("ecs/test")

View File

@ -1,33 +0,0 @@
project(Economy)
# List sources
set(HEADERS "economy.hpp" "defines.hpp" "enterprise_defaults.hpp" "systems.hpp")
set(HEADERS_COMPONENTS "container.hpp" "enterprise.hpp" "state.hpp" "tags.hpp"
"miner.hpp" "producer.hpp" "deliverer.hpp")
set(SOURCES "Economy.cpp")
set(SOURCES_SYSTEMS "enterprise_routine.cpp" "mining.cpp" "delivering.cpp")
list(TRANSFORM HEADERS PREPEND "include/ee_economy/")
list(TRANSFORM HEADERS_COMPONENTS PREPEND "include/ee_economy/components/")
list(TRANSFORM SOURCES PREPEND "src/")
list(TRANSFORM SOURCES_SYSTEMS PREPEND "src/systems/")
# Find dependencies
find_package(EnTT CONFIG REQUIRED)
# Link library
add_library(${PROJECT_NAME} ${HEADERS} ${HEADERS_COMPONENTS} ${SOURCES} ${SOURCES_SYSTEMS})
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} PRIVATE EnTT::EnTT)
target_include_directories(${PROJECT_NAME} PUBLIC include/)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
endif()

View File

@ -1,19 +0,0 @@
#pragma once
#include "../defines.hpp"
#include <unordered_map>
namespace ee_economy
{
struct Container
{
double capacity = 0.;
double total = 0.;
std::unordered_map<RESOURCE_TYPE, double> content;
void add(RESOURCE_TYPE type, double delta)
{
this->content[type] += delta;
this->total += delta;
}
};
}

View File

@ -1,24 +0,0 @@
#pragma once
#include <ee_economy/economy.hpp>
#include <vector>
namespace ee_economy
{
struct DeliverContract
{
entt::entity from;
entt::entity to;
entt::entity sponsor;
double amount = 0.0;
double cost = 0.0;
double distance = 1.0;
};
struct Deliverer
{
RESOURCE_TYPE resource = RESOURCE_TYPE::NONE;
std::vector<DeliverContract> contracts;
};
}

View File

@ -1,55 +0,0 @@
#pragma once
#include <ee_economy/economy.hpp>
#include <entt/entt.hpp>
#include <vector>
namespace ee_economy
{
struct Enterprise
{
bool isBuilding = false;
entt::entity state;
ACTION_TYPE action = ACTION_TYPE::STORE;
/*
* Mining - Amount of mined resource
* Delivering - Amount of resource that can be delivered by one truck
* Production -
*/
double efficiency = 0.;
double baseEfficiency = 0.;
double factorEfficiency = 1.;
std::vector<entt::entity> linksFrom;
std::vector<entt::entity> linksTo;
uint16_t staffSize = 1;
// This can be splitted in percents between bank enterprises in future
double totalMoney = 0;
double salary = 0;
bool isBankrupt = false;
void setMoney(double value)
{
totalMoney = value;
}
inline void updateEfficiency()
{
this->efficiency = this->baseEfficiency * this->factorEfficiency;
}
void setBaseEfficiency(double value)
{
this->baseEfficiency = value;
updateEfficiency();
}
void setFactorEfficiency(double value)
{
this->factorEfficiency = value;
updateEfficiency();
}
};
}

View File

@ -1,11 +0,0 @@
#pragma once
#include <ee_economy/economy.hpp>
#include <entt/entt.hpp>
namespace ee_economy
{
struct Miner
{
RESOURCE_TYPE resource = RESOURCE_TYPE::NONE;
};
}

View File

@ -1,9 +0,0 @@
#pragma once
namespace ee_economy
{
struct Producer
{
};
}

View File

@ -1,9 +0,0 @@
#pragma once
#include <cstdint>
namespace ee_economy
{
struct State {
int16_t externalId = -1;
};
}

View File

@ -1,6 +0,0 @@
#pragma once
namespace ee_economy
{
}

View File

@ -1,86 +0,0 @@
#pragma once
#include <cstdint>
namespace ee_economy
{
const double TOLERANCE = 0.001;
const double BASE_FUEL_PER_KM = 1.0;
enum struct ACTION_TYPE : uint8_t
{
MINING,
PRODUCTION,
DELIVERY,
STORE,
DESTRUCTION,
BUILD,
TRADE
};
enum struct CONTAINABLE_TYPE : uint8_t
{
RESOURCE,
GOODS,
SERVICE
};
enum struct RESOURCE_TYPE : uint8_t
{
NONE,
AGRICULTURE,
MEAT,
COTTON,
LEATHER,
WOOL,
TIMBER,
COAL,
OIL,
GAS,
IRON,
COPPER,
LITHIUM,
URANIUM,
GOLD,
FERTILIZER,
FABRIC,
FURNITURE,
LUXURY,
CLOTHING,
ALCOHOL,
PAPER,
GLASS,
PLASTIC,
CEMENT,
STEEL,
FUEL,
AVIATION_FUEL,
ROCKET_FUEL,
ENGINES,
ELECTRICITY,
AUTOMOBILE,
AIRPLANE,
HELICOPTER,
PHONE,
TRUCK,
CONSTRUCTION_EQUIPMENT,
PROCESSOR,
COMPUTER,
ELECTRONIC,
SEMICONDUCTOR,
BATTERY,
SYNTHETIC_RUBBER,
SYNTHETIC_FABRIC,
MACHINE_TOOLS,
MODERN_MACHINE_TOOLS,
POLYMERS,
MILITARY_EQUIPMENT,
MEDICINES,
TACTICAL_MISSILES,
NUCLEAR_MISSILES,
OIL_GAS
};
}

View File

@ -1,14 +0,0 @@
#pragma once
#include "defines.hpp"
#include "enterprise_defaults.hpp"
#include "systems.hpp"
#include "components/tags.hpp"
#include "components/state.hpp"
#include "components/container.hpp"
#include "components/enterprise.hpp"
#include "components/miner.hpp"
#include "components/producer.hpp"
#include "components/deliverer.hpp"

View File

@ -1,15 +0,0 @@
#pragma once
#include "defines.hpp"
#include "components/container.hpp"
namespace ee_economy
{
struct EnterpriseDefaults
{
Container container;
RESOURCE_TYPE resourceTarget = RESOURCE_TYPE::NONE;
double baseEfficiency = 0.0;
double factorEfficiency = 1.0;
};
}

View File

@ -1,27 +0,0 @@
// Rules for resources exchange in base before modifiers applying
#pragma once
#include <unordered_map>
#include "defines.hpp"
namespace ee_economy
{
struct ExchangeRule
{
double outValue;
std::unordered_map<RESOURCE_TYPE, double> consume;
};
const std::unordered_map<RESOURCE_TYPE, exchange_rule> resourcesExchange = {
{
RESOURCE_TYPE::FABRIC,
exchange_rule {
.outValue = 2.,
.consume = {
{RESOURCE_TYPE::WOOL, 3.}
}
}
}
};
}

View File

@ -1,15 +0,0 @@
#pragma once
#include <entt/entt.hpp>
namespace ee_economy
{
void enterpriseRoutine(entt::registry& registry);
void mining(entt::registry& registry);
// void producing(entt::registry& registry);
void delivering(entt::registry& registry);
// void storaging(entt::registry& registry); // consume money for storage time
// void destructing(entt::registry& registry);
// void building(entt::registry& registry);
// void trading(entt::registry& registry);
}

View File

@ -1,56 +0,0 @@
#include <ee_economy/economy.hpp>
void ee_economy::delivering(entt::registry &registry)
{
auto deliverers = registry.view<Enterprise, Deliverer, Container>();
for (auto [entity, enterprise, deliverer, storage]: deliverers.each())
{
if (enterprise.isBankrupt) continue;
for (DeliverContract &contract: deliverer.contracts)
{
double paidAmount = contract.amount;
if (contract.cost > 0)
{
Enterprise &sponsor = registry.get<Enterprise>(contract.sponsor);
double costPerOne = contract.cost / contract.amount;
double payment = contract.cost;
if (payment > sponsor.totalMoney)
{
payment = (int)(sponsor.totalMoney / costPerOne) * costPerOne;
paidAmount = payment / costPerOne;
}
sponsor.setMoney(sponsor.totalMoney - payment);
enterprise.setMoney(enterprise.totalMoney + payment);
contract.cost -= payment;
}
Container &fromStorage = registry.get<Container>(contract.from);
Container &toStorage = registry.get<Container>(contract.to);
double deliverValue = enterprise.efficiency;
if (contract.amount < enterprise.efficiency)
deliverValue = contract.amount;
if (toStorage.total + deliverValue > toStorage.capacity)
deliverValue = toStorage.capacity - toStorage.total;
if (deliverValue > fromStorage.content[deliverer.resource])
deliverValue = fromStorage.content[deliverer.resource];
if (deliverValue > paidAmount)
deliverValue = paidAmount;
if (deliverValue < 0.0)
deliverValue = 0.0;
toStorage.add(deliverer.resource, deliverValue);
fromStorage.add(deliverer.resource, -deliverValue);
contract.amount -= deliverValue;
}
std::erase_if(deliverer.contracts, [](const DeliverContract &contract) {
return contract.amount <= 0.0;
});
}
}

View File

@ -1,20 +0,0 @@
#include <ee_economy/economy.hpp>
void ee_economy::enterpriseRoutine(entt::registry &registry)
{
auto enterprises = registry.view<Enterprise>();
for (auto [entity, enterprise]: enterprises.each())
{
double salaryDebt = enterprise.salary * enterprise.staffSize;
if (enterprise.totalMoney < salaryDebt)
{
enterprise.isBankrupt = true;
enterprise.setMoney(0.0);
}
else
{
enterprise.setMoney(enterprise.totalMoney - salaryDebt);
}
}
}

View File

@ -1,22 +0,0 @@
#include <ee_economy/economy.hpp>
void ee_economy::mining(entt::registry &registry)
{
auto miners = registry.view<Enterprise, Miner, Container>();
for (auto [entity, enterprise, miner, localStorage]: miners.each())
{
if (enterprise.isBankrupt) continue;
Container& state = registry.get<Container>(enterprise.state);
double minedValue = enterprise.efficiency;
if (localStorage.total + minedValue > localStorage.capacity)
minedValue = localStorage.capacity - localStorage.total;
if (minedValue > state.content[miner.resource])
minedValue = state.content[miner.resource];
localStorage.add(miner.resource, minedValue);
state.add(miner.resource, -minedValue);
}
}

View File

@ -1,26 +0,0 @@
project(economy_tests)
# List sources
set(SOURCES "test_conf.hpp" "main.cpp"
"systems/enterprise_routine.cpp" "systems/mining.cpp" "systems/delivering.cpp")
# Find dependencies
find_package(GTest CONFIG REQUIRED)
# Link
include(GoogleTest)
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest_main)
target_link_libraries(${PROJECT_NAME} PRIVATE Economy::Economy)
# Test
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
endif()
gtest_discover_tests(${PROJECT_NAME})

View File

@ -1,8 +0,0 @@
#include <gtest/gtest.h>
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,214 +0,0 @@
#include <gtest/gtest.h>
#include <ee_economy/economy.hpp>
#include "../test_conf.hpp"
TEST_F(SystemsTest, DeliverySingleNormal)
{
// [Miner -> Storage] by Deliverer
ee_economy::Enterprise &delivererEnterprise = registry.get<ee_economy::Enterprise>(deliverer);
ee_economy::Deliverer &delivererDeliverer = registry.get<ee_economy::Deliverer>(deliverer);
delivererDeliverer.resource = ee_economy::RESOURCE_TYPE::COAL;
delivererEnterprise.setBaseEfficiency(1.0);
delivererDeliverer.contracts.push_back({.from = miner, .to = storage, .amount = 10.0});
ee_economy::Container &fromContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Container &toContainer = registry.get<ee_economy::Container>(storage);
fromContainer.capacity = 15.0;
fromContainer.add(ee_economy::RESOURCE_TYPE::COAL, 15.0);
toContainer.capacity = 15.0;
ee_economy::delivering(registry);
ASSERT_DOUBLE_EQ(fromContainer.total, 14.0);
ASSERT_DOUBLE_EQ(fromContainer.content[ee_economy::RESOURCE_TYPE::COAL], 14.0);
ASSERT_DOUBLE_EQ(toContainer.total, 1.0);
ASSERT_DOUBLE_EQ(toContainer.content[ee_economy::RESOURCE_TYPE::COAL], 1.0);
ASSERT_EQ(delivererDeliverer.contracts.size(), 1);
}
TEST_F(SystemsTest, DeliverySingleEmpty)
{
// [Miner -> Storage] by Deliverer
ee_economy::Enterprise &delivererEnterprise = registry.get<ee_economy::Enterprise>(deliverer);
ee_economy::Deliverer &delivererDeliverer = registry.get<ee_economy::Deliverer>(deliverer);
delivererDeliverer.resource = ee_economy::RESOURCE_TYPE::COAL;
delivererEnterprise.setBaseEfficiency(1.0);
delivererDeliverer.contracts.push_back({.from = miner, .to = storage, .amount = 10.0});
ee_economy::Container &fromContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Container &toContainer = registry.get<ee_economy::Container>(storage);
fromContainer.capacity = 15.0;
toContainer.capacity = 15.0;
ee_economy::delivering(registry);
ASSERT_DOUBLE_EQ(fromContainer.total, 0.0);
ASSERT_DOUBLE_EQ(fromContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.0);
ASSERT_DOUBLE_EQ(toContainer.total, 0.0);
ASSERT_DOUBLE_EQ(toContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.0);
ASSERT_EQ(delivererDeliverer.contracts.size(), 1);
}
TEST_F(SystemsTest, DeliverySinglePoor)
{
// [Miner -> Storage] by Deliverer
ee_economy::Enterprise &delivererEnterprise = registry.get<ee_economy::Enterprise>(deliverer);
ee_economy::Deliverer &delivererDeliverer = registry.get<ee_economy::Deliverer>(deliverer);
delivererDeliverer.resource = ee_economy::RESOURCE_TYPE::COAL;
delivererEnterprise.setBaseEfficiency(1.0);
delivererDeliverer.contracts.push_back({.from = miner, .to = storage, .amount = 10.0});
ee_economy::Container &fromContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Container &toContainer = registry.get<ee_economy::Container>(storage);
fromContainer.capacity = 15.0;
fromContainer.add(ee_economy::RESOURCE_TYPE::COAL, 0.5);
toContainer.capacity = 15.0;
ee_economy::delivering(registry);
ASSERT_DOUBLE_EQ(fromContainer.total, 0.0);
ASSERT_DOUBLE_EQ(fromContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.0);
ASSERT_DOUBLE_EQ(toContainer.total, 0.5);
ASSERT_DOUBLE_EQ(toContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.5);
ASSERT_EQ(delivererDeliverer.contracts.size(), 1);
}
TEST_F(SystemsTest, DeliverySingleFinishContract)
{
// [Miner -> Storage] by Deliverer
ee_economy::Enterprise &delivererEnterprise = registry.get<ee_economy::Enterprise>(deliverer);
ee_economy::Deliverer &delivererDeliverer = registry.get<ee_economy::Deliverer>(deliverer);
delivererDeliverer.resource = ee_economy::RESOURCE_TYPE::COAL;
delivererEnterprise.setBaseEfficiency(15.0);
delivererDeliverer.contracts.push_back({.from = miner, .to = storage, .amount = 10.0});
ee_economy::Container &fromContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Container &toContainer = registry.get<ee_economy::Container>(storage);
fromContainer.capacity = 15.0;
fromContainer.add(ee_economy::RESOURCE_TYPE::COAL, 15.0);
toContainer.capacity = 15.0;
ee_economy::delivering(registry);
ASSERT_DOUBLE_EQ(fromContainer.total, 5.0);
ASSERT_DOUBLE_EQ(fromContainer.content[ee_economy::RESOURCE_TYPE::COAL], 5.0);
ASSERT_DOUBLE_EQ(toContainer.total, 10.0);
ASSERT_DOUBLE_EQ(toContainer.content[ee_economy::RESOURCE_TYPE::COAL], 10.0);
ASSERT_EQ(delivererDeliverer.contracts.size(), 0);
}
TEST_F(SystemsTest, DeliveryMultipleFinishPoorCycle)
{
// [Miner -> Storage | Storage -> Miner] by Deliverer
ee_economy::Enterprise &delivererEnterprise = registry.get<ee_economy::Enterprise>(deliverer);
ee_economy::Deliverer &delivererDeliverer = registry.get<ee_economy::Deliverer>(deliverer);
delivererDeliverer.resource = ee_economy::RESOURCE_TYPE::COAL;
delivererEnterprise.setBaseEfficiency(16.0);
delivererDeliverer.contracts.push_back({.from = miner, .to = storage, .amount = 20.0});
delivererDeliverer.contracts.push_back({.from = storage, .to = miner, .amount = 5.0});
ee_economy::Container &minerContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Container &storageContainer = registry.get<ee_economy::Container>(storage);
minerContainer.capacity = 15.0;
minerContainer.add(ee_economy::RESOURCE_TYPE::COAL, 15.0);
storageContainer.capacity = 15.0;
ee_economy::delivering(registry);
ASSERT_DOUBLE_EQ(minerContainer.total, 5.0);
ASSERT_DOUBLE_EQ(minerContainer.content[ee_economy::RESOURCE_TYPE::COAL], 5.0);
ASSERT_DOUBLE_EQ(storageContainer.total, 10.0);
ASSERT_DOUBLE_EQ(storageContainer.content[ee_economy::RESOURCE_TYPE::COAL], 10.0);
ASSERT_EQ(delivererDeliverer.contracts.size(), 1);
ASSERT_EQ(delivererDeliverer.contracts[0].from, miner);
}
TEST_F(SystemsTest, DeliveryPayment)
{
// [Miner -> Storage] by Deliverer for $Miner
// [Miner -> Storage] by Deliverer
ee_economy::Enterprise &delivererEnterprise = registry.get<ee_economy::Enterprise>(deliverer);
ee_economy::Deliverer &delivererDeliverer = registry.get<ee_economy::Deliverer>(deliverer);
delivererDeliverer.resource = ee_economy::RESOURCE_TYPE::COAL;
delivererEnterprise.setBaseEfficiency(10.0);
delivererDeliverer.contracts.push_back({
.from = miner,
.to = storage,
.sponsor = miner,
.amount = 10.0,
.cost = 100.0
});
ee_economy::Container &fromContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Enterprise &fromEnterprise = registry.get<ee_economy::Enterprise>(miner);
ee_economy::Container &toContainer = registry.get<ee_economy::Container>(storage);
fromContainer.capacity = 10.0;
fromContainer.add(ee_economy::RESOURCE_TYPE::COAL, 10.0);
fromEnterprise.setMoney(95.0);
toContainer.capacity = 10.0;
ee_economy::delivering(registry);
ASSERT_DOUBLE_EQ(fromContainer.total, 1.0);
ASSERT_DOUBLE_EQ(fromContainer.content[ee_economy::RESOURCE_TYPE::COAL], 1.0);
ASSERT_DOUBLE_EQ(toContainer.total, 9.0);
ASSERT_DOUBLE_EQ(toContainer.content[ee_economy::RESOURCE_TYPE::COAL], 9.0);
ASSERT_DOUBLE_EQ(fromEnterprise.totalMoney, 5.0);
ASSERT_DOUBLE_EQ(delivererEnterprise.totalMoney, 90.0);
ee_economy::DeliverContract contract = delivererDeliverer.contracts[0];
ASSERT_DOUBLE_EQ(contract.cost, 10.0);
ASSERT_DOUBLE_EQ(contract.amount, 1.0);
}
TEST_F(SystemsTest, DeliveryBankrupt)
{
// [Miner -> Storage] by Deliverer
ee_economy::Enterprise &delivererEnterprise = registry.get<ee_economy::Enterprise>(deliverer);
ee_economy::Deliverer &delivererDeliverer = registry.get<ee_economy::Deliverer>(deliverer);
delivererDeliverer.resource = ee_economy::RESOURCE_TYPE::COAL;
delivererDeliverer.contracts.push_back({.from = miner, .to = storage, .amount = 10.0});
delivererEnterprise.setBaseEfficiency(1.0);
delivererEnterprise.isBankrupt = true;
ee_economy::Container &fromContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Container &toContainer = registry.get<ee_economy::Container>(storage);
fromContainer.capacity = 15.0;
fromContainer.add(ee_economy::RESOURCE_TYPE::COAL, 15.0);
toContainer.capacity = 15.0;
ee_economy::delivering(registry);
ASSERT_DOUBLE_EQ(fromContainer.total, 15.0);
ASSERT_DOUBLE_EQ(fromContainer.content[ee_economy::RESOURCE_TYPE::COAL], 15.0);
ASSERT_DOUBLE_EQ(toContainer.total, 0.0);
ASSERT_DOUBLE_EQ(toContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.0);
ASSERT_EQ(delivererDeliverer.contracts.size(), 1);
}

View File

@ -1,29 +0,0 @@
#include <gtest/gtest.h>
#include <ee_economy/economy.hpp>
#include "../test_conf.hpp"
TEST_F(SystemsTest, RoutineNormal)
{
ee_economy::Enterprise &minerEnterprise = registry.get<ee_economy::Enterprise>(miner);
minerEnterprise.setMoney(1000000000.0);
minerEnterprise.salary = 100000.0;
minerEnterprise.staffSize = 10;
ee_economy::enterpriseRoutine(registry);
ASSERT_DOUBLE_EQ(minerEnterprise.totalMoney, 999000000.0);
ASSERT_FALSE(minerEnterprise.isBankrupt);
}
TEST_F(SystemsTest, RoutineBankruptcy)
{
ee_economy::Enterprise &minerEnterprise = registry.get<ee_economy::Enterprise>(miner);
minerEnterprise.setMoney(1000000.0);
minerEnterprise.salary = 100000.0;
minerEnterprise.staffSize = 11;
ee_economy::enterpriseRoutine(registry);
ASSERT_DOUBLE_EQ(minerEnterprise.totalMoney, 0.0);
ASSERT_TRUE(minerEnterprise.isBankrupt);
}

View File

@ -1,113 +0,0 @@
#include <gtest/gtest.h>
#include <ee_economy/economy.hpp>
#include "../test_conf.hpp"
TEST_F(SystemsTest, MiningNormal)
{
ee_economy::Container &stateContainer = registry.get<ee_economy::Container>(state);
ee_economy::Container &minerContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Enterprise &minerEnterprise = registry.get<ee_economy::Enterprise>(miner);
ee_economy::Miner &minerMiner = registry.get<ee_economy::Miner>(miner);
stateContainer.capacity = 15.0;
stateContainer.add(ee_economy::RESOURCE_TYPE::COAL, 10.0);
minerContainer.capacity = 5.0;
minerMiner.resource = ee_economy::RESOURCE_TYPE::COAL;
minerEnterprise.setBaseEfficiency(0.1);
ee_economy::mining(registry);
ASSERT_DOUBLE_EQ(stateContainer.total, 9.9);
ASSERT_DOUBLE_EQ(stateContainer.content[ee_economy::RESOURCE_TYPE::COAL], 9.9);
ASSERT_DOUBLE_EQ(minerContainer.total, 0.1);
ASSERT_DOUBLE_EQ(minerContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.1);
}
TEST_F(SystemsTest, MiningEmptyState)
{
ee_economy::Container &stateContainer = registry.get<ee_economy::Container>(state);
ee_economy::Container &minerContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Enterprise &minerEnterprise = registry.get<ee_economy::Enterprise>(miner);
ee_economy::Miner &minerMiner = registry.get<ee_economy::Miner>(miner);
stateContainer.capacity = 15.0;
minerContainer.capacity = 5.0;
minerMiner.resource = ee_economy::RESOURCE_TYPE::COAL;
minerEnterprise.setBaseEfficiency(0.1);
ee_economy::mining(registry);
ASSERT_DOUBLE_EQ(stateContainer.total, 0.0);
ASSERT_DOUBLE_EQ(stateContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.0);
ASSERT_DOUBLE_EQ(minerContainer.total, 0.0);
ASSERT_DOUBLE_EQ(minerContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.0);
}
TEST_F(SystemsTest, MiningPoorState)
{
ee_economy::Container &stateContainer = registry.get<ee_economy::Container>(state);
ee_economy::Container &minerContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Enterprise &minerEnterprise = registry.get<ee_economy::Enterprise>(miner);
ee_economy::Miner &minerMiner = registry.get<ee_economy::Miner>(miner);
stateContainer.capacity = 15.0;
stateContainer.add(ee_economy::RESOURCE_TYPE::COAL, 0.05);
minerContainer.capacity = 5.0;
minerMiner.resource = ee_economy::RESOURCE_TYPE::COAL;
minerEnterprise.setBaseEfficiency(0.1);
ee_economy::mining(registry);
ASSERT_DOUBLE_EQ(stateContainer.total, 0.0);
ASSERT_DOUBLE_EQ(stateContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.0);
ASSERT_DOUBLE_EQ(minerContainer.total, 0.05);
ASSERT_DOUBLE_EQ(minerContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.05);
}
TEST_F(SystemsTest, MiningReachCap)
{
ee_economy::Container &stateContainer = registry.get<ee_economy::Container>(state);
ee_economy::Container &minerContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Enterprise &minerEnterprise = registry.get<ee_economy::Enterprise>(miner);
ee_economy::Miner &minerMiner = registry.get<ee_economy::Miner>(miner);
stateContainer.capacity = 15.0;
stateContainer.add(ee_economy::RESOURCE_TYPE::COAL, 10.0);
minerContainer.capacity = 0.05;
minerMiner.resource = ee_economy::RESOURCE_TYPE::COAL;
minerEnterprise.setBaseEfficiency(0.1);
ee_economy::mining(registry);
ASSERT_DOUBLE_EQ(stateContainer.total, 9.95);
ASSERT_DOUBLE_EQ(stateContainer.content[ee_economy::RESOURCE_TYPE::COAL], 9.95);
ASSERT_DOUBLE_EQ(minerContainer.total, 0.05);
ASSERT_DOUBLE_EQ(minerContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.05);
}
TEST_F(SystemsTest, MiningBankrupt)
{
ee_economy::Container &stateContainer = registry.get<ee_economy::Container>(state);
ee_economy::Container &minerContainer = registry.get<ee_economy::Container>(miner);
ee_economy::Enterprise &minerEnterprise = registry.get<ee_economy::Enterprise>(miner);
ee_economy::Miner &minerMiner = registry.get<ee_economy::Miner>(miner);
stateContainer.capacity = 15.0;
stateContainer.add(ee_economy::RESOURCE_TYPE::COAL, 10.0);
minerContainer.capacity = 5.0;
minerMiner.resource = ee_economy::RESOURCE_TYPE::COAL;
minerEnterprise.setBaseEfficiency(0.1);
minerEnterprise.isBankrupt = true;
ee_economy::mining(registry);
ASSERT_DOUBLE_EQ(stateContainer.total, 10.0);
ASSERT_DOUBLE_EQ(stateContainer.content[ee_economy::RESOURCE_TYPE::COAL], 10.0);
ASSERT_DOUBLE_EQ(minerContainer.total, 0.0);
ASSERT_DOUBLE_EQ(minerContainer.content[ee_economy::RESOURCE_TYPE::COAL], 0.0);
}

View File

@ -1,45 +0,0 @@
#pragma once
#include <ee_economy/economy.hpp>
#include <entt/entt.hpp>
#include <gtest/gtest.h>
struct SystemsTest : testing::Test
{
entt::registry registry;
entt::entity state;
entt::entity miner;
entt::entity storage;
entt::entity deliverer;
void SetUp()
{
// Create state
state = registry.create();
registry.emplace<ee_economy::State>(state);
registry.emplace<ee_economy::Container>(state);
// Create miner enterprise
miner = registry.create();
registry.emplace<ee_economy::Enterprise>(miner, false, state);
registry.emplace<ee_economy::Miner>(miner);
registry.emplace<ee_economy::Container>(miner);
// Create storage enterprise
storage = registry.create();
registry.emplace<ee_economy::Enterprise>(storage, false, state);
registry.emplace<ee_economy::Container>(storage);
// Create delivering enterprise
deliverer = registry.create();
registry.emplace<ee_economy::Enterprise>(deliverer, false, state);
registry.emplace<ee_economy::Deliverer>(deliverer);
registry.emplace<ee_economy::Container>(deliverer);
}
void TearDown()
{
}
};

View File

@ -1,27 +0,0 @@
project(ECS)
# List sources
set(HEADERS "ecs.hpp" "api.hpp" "world.hpp")
set(SOURCES "api.cpp" "world.cpp")
list(TRANSFORM HEADERS PREPEND "include/ee_ecs/")
list(TRANSFORM SOURCES PREPEND "src/")
# Find dependencies
find_package(EnTT CONFIG REQUIRED)
# Link library
add_library(${PROJECT_NAME} ${HEADERS} ${SOURCES})
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} PUBLIC Economy::Economy)
target_link_libraries(${PROJECT_NAME} PRIVATE EnTT::EnTT)
target_include_directories(${PROJECT_NAME} PUBLIC include/)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
endif()

View File

@ -1,14 +0,0 @@
#pragma once
#include <vector>
#include "data.hpp"
namespace ee_ecs
{
void init(InitConfiguration config);
void start();
template<typename T>
const std::vector<EnterpriseInfo> getAllEnterprises();
//const EnterpriseInfo getEnterprise(uint32_t id);
}

View File

@ -1,24 +0,0 @@
#pragma once
#include <unordered_map>
#include <ee_economy/economy.hpp>
#include <variant>
namespace ee_ecs
{
struct InitConfiguration
{
std::unordered_map<int16_t, ee_economy::Container> states;
};
struct EnterpriseInfo
{
const ee_economy::Enterprise* info = nullptr;
const ee_economy::Container* storage = nullptr;
std::variant<
const ee_economy::Miner*,
const ee_economy::Deliverer*
> specialInfo;
};
}

View File

@ -1,4 +0,0 @@
#pragma once
#include "api.hpp"
#include "world.hpp"

View File

@ -1,33 +0,0 @@
#pragma once
#include "ee_economy/components/container.hpp"
#include <entt/entity/fwd.hpp>
#include <entt/entt.hpp>
#include <ee_economy/economy.hpp>
#include <unordered_map>
namespace ee_ecs
{
class World
{
private:
entt::registry registry;
std::unordered_map<int16_t, entt::entity> statesTable;
void updateEnterprises();
public:
// Run every registered system
void update();
entt::entity registerState(const int16_t id, const ee_economy::Container &resources);
entt::entity spawnEnterprise(
const bool isBuilding,
const ee_economy::ACTION_TYPE action,
const entt::entity state,
const ee_economy::EnterpriseDefaults &defaults
);
entt::registry& getRegistry();
};
}

View File

@ -1,44 +0,0 @@
#include "ee_ecs/world.hpp"
#include <ee_ecs/ecs.hpp>
static ee_ecs::World worldInstance;
void ee_ecs::init(ee_ecs::InitConfiguration config)
{
for (auto& [id, container]: config.states)
{
worldInstance.registerState(id, container);
}
}
void ee_ecs::start()
{
while (true) {}
}
template<typename T>
const std::vector<ee_ecs::EnterpriseInfo> ee_ecs::getAllEnterprises()
{
std::vector<EnterpriseInfo> result;
//TODO: REPLACE VIEWS WITH GROUPS!
auto enterprises = worldInstance.getRegistry().group<>(entt::get<
ee_economy::Enterprise,
ee_economy::Container,
T
>);
result.reserve(enterprises.size_hint());
for (auto [entity, enterprise, container, action]: enterprises.each())
{
EnterpriseInfo eninfo;
eninfo.info = &enterprise;
eninfo.storage = &container;
eninfo.specialInfo = &action;
result.push_back(eninfo);
}
return result;
}

View File

@ -1,87 +0,0 @@
#include <ee_economy/economy.hpp>
#include <ee_ecs/ecs.hpp>
#include <stdexcept>
void ee_ecs::World::updateEnterprises()
{
ee_economy::enterpriseRoutine(this->registry);
ee_economy::mining(this->registry);
ee_economy::delivering(this->registry);
}
void ee_ecs::World::update()
{
this->updateEnterprises();
}
entt::entity ee_ecs::World::registerState(const int16_t id, const ee_economy::Container &resources)
{
if (this->statesTable.contains(id))
throw std::invalid_argument("State with this Id is already registered");
entt::entity entity = this->registry.create();
this->registry.emplace<ee_economy::State>(entity, id);
this->registry.emplace<ee_economy::Container>(
entity,
resources.capacity,
resources.total,
resources.content
);
this->statesTable[id] = entity;
return entity;
}
entt::entity ee_ecs::World::spawnEnterprise(
const bool isBuilding,
const ee_economy::ACTION_TYPE action,
const entt::entity state,
const ee_economy::EnterpriseDefaults &defaults
)
{
entt::entity entity = this->registry.create();
this->registry.emplace<ee_economy::Enterprise>(
entity,
isBuilding,
state,
action,
defaults.baseEfficiency * defaults.factorEfficiency,
defaults.baseEfficiency,
defaults.factorEfficiency
);
this->registry.emplace<ee_economy::Container>(
entity,
defaults.container.capacity,
defaults.container.total,
defaults.container.content
);
switch (action)
{
case ee_economy::ACTION_TYPE::MINING:
{
this->registry.emplace<ee_economy::Miner>(entity, defaults.resourceTarget);
break;
}
case ee_economy::ACTION_TYPE::DELIVERY:
{
this->registry.emplace<ee_economy::Deliverer>(entity, defaults.resourceTarget);
break;
}
default:
break;
}
return entity;
}
entt::registry& ee_ecs::World::getRegistry() {return this->registry;}

View File

@ -1,25 +0,0 @@
project(ecs_tests)
# List sources
set(SOURCES "test_conf.hpp" "main.cpp" "world.cpp" "benchmark.cpp")
# Find dependencies
find_package(GTest CONFIG REQUIRED)
# Link
include(GoogleTest)
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest_main)
target_link_libraries(${PROJECT_NAME} PRIVATE ECS::ECS)
# Test
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
endif()
gtest_discover_tests(${PROJECT_NAME})

View File

@ -1,31 +0,0 @@
#include <ee_ecs/ecs.hpp>
#include "test_conf.hpp"
#include <gtest/gtest.h>
#include <chrono>
struct timer final {
timer()
: start{std::chrono::system_clock::now()} {}
void elapsed() {
auto now = std::chrono::system_clock::now();
std::cout << std::chrono::duration<double>(now - start).count() << " seconds" << std::endl;
}
private:
std::chrono::time_point<std::chrono::system_clock> start;
};
template<typename Func, typename... Args>
void genericWith(Func func) {
timer timer;
func();
timer.elapsed();
}
TEST_F(WorldBenchmark, UpdateSystems)
{
genericWith([&]() {
world.update();
});
}

View File

@ -1,8 +0,0 @@
#include <gtest/gtest.h>
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,36 +0,0 @@
#pragma once
#include <gtest/gtest.h>
#include <ee_ecs/ecs.hpp>
struct WorldBenchmark : testing::Test
{
ee_ecs::World world;
void SetUp()
{
for (int16_t i = 0; i < 1000; i++)
{
ee_economy::Container resources {.capacity = 100.0};
resources.add(ee_economy::RESOURCE_TYPE::COAL, 100.0);
entt::entity state = world.registerState(i, resources);
ee_economy::EnterpriseDefaults miner {
.container {.capacity = 100.0},
.resourceTarget = ee_economy::RESOURCE_TYPE::GOLD,
.baseEfficiency = 1.0
};
world.spawnEnterprise(false, ee_economy::ACTION_TYPE::MINING, state, miner);
world.spawnEnterprise(false, ee_economy::ACTION_TYPE::MINING, state, miner);
world.spawnEnterprise(false, ee_economy::ACTION_TYPE::MINING, state, miner);
world.spawnEnterprise(false, ee_economy::ACTION_TYPE::MINING, state, miner);
world.spawnEnterprise(false, ee_economy::ACTION_TYPE::MINING, state, miner);
}
}
void TearDown()
{
}
};

View File

@ -1,80 +0,0 @@
#include <gtest/gtest.h>
#include <ee_ecs/ecs.hpp>
#include <ee_economy/economy.hpp>
#include <stdexcept>
TEST(WorldTest, RegisterState)
{
ee_ecs::World world;
ee_economy::Container res {.capacity = 100.0};
res.add(ee_economy::RESOURCE_TYPE::COAL, 98.0);
entt::entity state = world.registerState(10, res);
ee_economy::State registeredState = world.getRegistry().get<ee_economy::State>(state);
ee_economy::Container registeredContainer = world.getRegistry().get<ee_economy::Container>(state);
ASSERT_EQ(registeredState.externalId, 10);
ASSERT_DOUBLE_EQ(registeredContainer.content[ee_economy::RESOURCE_TYPE::COAL], 98.0);
}
TEST(WorldTest, RegisterStateDuplicate)
{
ee_ecs::World world;
world.registerState(10, {});
ASSERT_THROW({
world.registerState(10, {});
}, std::invalid_argument);
}
TEST(WorldTest, SpawnMiner)
{
ee_ecs::World world;
entt::entity state = world.getRegistry().create();
world.getRegistry().emplace<ee_economy::State>(state, (int16_t)0);
world.getRegistry().emplace<ee_economy::Container>(state, 100.0);
ee_economy::EnterpriseDefaults defaults {
.container {.capacity = 100.0 },
.resourceTarget = ee_economy::RESOURCE_TYPE::COAL,
.baseEfficiency = 1.0,
};
entt::entity enterprise = world.spawnEnterprise(false, ee_economy::ACTION_TYPE::MINING, state, defaults);
ee_economy::Enterprise registeredEnterprise = world.getRegistry().get<ee_economy::Enterprise>(enterprise);
ee_economy::Miner registeredAction = world.getRegistry().get<ee_economy::Miner>(enterprise);
ee_economy::Container registeredContainer = world.getRegistry().get<ee_economy::Container>(enterprise);
ASSERT_DOUBLE_EQ(registeredEnterprise.baseEfficiency, 1.0);
ASSERT_EQ(registeredAction.resource, ee_economy::RESOURCE_TYPE::COAL);
ASSERT_DOUBLE_EQ(registeredContainer.capacity, 100.0);
}
TEST(WorldTest, SpawnDeliverer)
{
ee_ecs::World world;
entt::entity state = world.getRegistry().create();
world.getRegistry().emplace<ee_economy::State>(state, (int16_t)0);
world.getRegistry().emplace<ee_economy::Container>(state, 100.0);
ee_economy::EnterpriseDefaults defaults {
.container {.capacity = 100.0 },
.resourceTarget = ee_economy::RESOURCE_TYPE::COAL,
.baseEfficiency = 1.0,
};
entt::entity enterprise = world.spawnEnterprise(false, ee_economy::ACTION_TYPE::DELIVERY, state, defaults);
ee_economy::Enterprise registeredEnterprise = world.getRegistry().get<ee_economy::Enterprise>(enterprise);
ee_economy::Deliverer registeredAction = world.getRegistry().get<ee_economy::Deliverer>(enterprise);
ee_economy::Container registeredContainer = world.getRegistry().get<ee_economy::Container>(enterprise);
ASSERT_DOUBLE_EQ(registeredEnterprise.baseEfficiency, 1.0);
ASSERT_EQ(registeredAction.resource, ee_economy::RESOURCE_TYPE::COAL);
ASSERT_DOUBLE_EQ(registeredContainer.capacity, 100.0);
}

View File

@ -0,0 +1,5 @@
include(${CMAKE_SOURCE_DIR}/cmake/library.cmake)
set(LIB server)
add_library_target(${LIB})

Some files were not shown because too many files have changed in this diff Show More