mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-19 03:36:56 -06:00
Compare commits
10 Commits
dfgsdfg
...
ecs-experi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a36b611367 | ||
|
|
85eb5a7261 | ||
|
|
93dcfddac5 | ||
|
|
82f510c642 | ||
|
|
ae4d9c4bcb | ||
|
|
b9e4aa5344 | ||
|
|
eab57e4022 | ||
|
|
179f0cf32d | ||
|
|
427b7c1047 | ||
|
|
afc2966507 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ RelWithDebInfo/
|
|||||||
docker/configs
|
docker/configs
|
||||||
|
|
||||||
# Third party libraries
|
# Third party libraries
|
||||||
|
thirdparty/magic_enum
|
||||||
thirdparty/mysql/
|
thirdparty/mysql/
|
||||||
thirdparty/mysql_linux/
|
thirdparty/mysql_linux/
|
||||||
CMakeVariables.txt
|
CMakeVariables.txt
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -14,6 +14,3 @@
|
|||||||
path = thirdparty/mariadb-connector-cpp
|
path = thirdparty/mariadb-connector-cpp
|
||||||
url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git
|
url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git
|
||||||
ignore = dirty
|
ignore = dirty
|
||||||
[submodule "thirdparty/magic_enum"]
|
|
||||||
path = thirdparty/magic_enum
|
|
||||||
url = https://github.com/Neargye/magic_enum.git
|
|
||||||
|
|||||||
@@ -110,6 +110,23 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
|||||||
|
|
||||||
find_package(MariaDB)
|
find_package(MariaDB)
|
||||||
|
|
||||||
|
# Fetch third party dependencies
|
||||||
|
set(DLU_THIRDPARTY_SOURCE_DIR ${CMAKE_SOURCE_DIR}/thirdparty)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
magic_enum
|
||||||
|
SYSTEM
|
||||||
|
# SOURCE_DIR ${DLU_THIRDPARTY_SOURCE_DIR}/magic_enum
|
||||||
|
GIT_REPOSITORY https://github.com/Neargye/magic_enum.git
|
||||||
|
GIT_TAG v0.9.7
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(magic_enum)
|
||||||
|
|
||||||
|
include(CMakePrintHelpers)
|
||||||
|
cmake_print_properties(TARGETS magic_enum::magic_enum PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
|
||||||
# Create a /resServer directory
|
# Create a /resServer directory
|
||||||
make_directory(${CMAKE_BINARY_DIR}/resServer)
|
make_directory(${CMAKE_BINARY_DIR}/resServer)
|
||||||
|
|
||||||
@@ -252,7 +269,6 @@ include_directories(
|
|||||||
"tests/dGameTests/dComponentsTests"
|
"tests/dGameTests/dComponentsTests"
|
||||||
|
|
||||||
SYSTEM
|
SYSTEM
|
||||||
"thirdparty/magic_enum/include/magic_enum"
|
|
||||||
"thirdparty/raknet/Source"
|
"thirdparty/raknet/Source"
|
||||||
"thirdparty/tinyxml2"
|
"thirdparty/tinyxml2"
|
||||||
"thirdparty/recastnavigation"
|
"thirdparty/recastnavigation"
|
||||||
@@ -303,6 +319,7 @@ file(
|
|||||||
# Add our library subdirectories for creation of the library object
|
# Add our library subdirectories for creation of the library object
|
||||||
add_subdirectory(dCommon)
|
add_subdirectory(dCommon)
|
||||||
add_subdirectory(dDatabase)
|
add_subdirectory(dDatabase)
|
||||||
|
add_subdirectory(dECS)
|
||||||
add_subdirectory(dChatFilter)
|
add_subdirectory(dChatFilter)
|
||||||
add_subdirectory(dNet)
|
add_subdirectory(dNet)
|
||||||
add_subdirectory(dScripts) # Add for dGame to use
|
add_subdirectory(dScripts) # Add for dGame to use
|
||||||
@@ -313,7 +330,7 @@ add_subdirectory(dPhysics)
|
|||||||
add_subdirectory(dServer)
|
add_subdirectory(dServer)
|
||||||
|
|
||||||
# Create a list of common libraries shared between all binaries
|
# Create a list of common libraries shared between all binaries
|
||||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "magic_enum")
|
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "magic_enum::magic_enum")
|
||||||
|
|
||||||
# Add platform specific common libraries
|
# Add platform specific common libraries
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
|
|||||||
@@ -70,5 +70,6 @@ else ()
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_link_libraries(dCommon
|
target_link_libraries(dCommon
|
||||||
|
PUBLIC magic_enum::magic_enum
|
||||||
PRIVATE ZLIB::ZLIB bcrypt tinyxml2
|
PRIVATE ZLIB::ZLIB bcrypt tinyxml2
|
||||||
INTERFACE dDatabase)
|
INTERFACE dDatabase)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "magic_enum.hpp"
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
namespace MessageType {
|
namespace MessageType {
|
||||||
enum class Game : uint16_t {
|
enum class Game : uint16_t {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "magic_enum.hpp"
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
namespace MessageType {
|
namespace MessageType {
|
||||||
enum class World : uint32_t {
|
enum class World : uint32_t {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#define __STRINGIFIEDENUM_H__
|
#define __STRINGIFIEDENUM_H__
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "magic_enum.hpp"
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
namespace StringifiedEnum {
|
namespace StringifiedEnum {
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "magic_enum.hpp"
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
static const uint8_t NUMBER_OF_INVENTORIES = 17;
|
static const uint8_t NUMBER_OF_INVENTORIES = 17;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,12 +2,6 @@ add_subdirectory(CDClientDatabase)
|
|||||||
add_subdirectory(GameDatabase)
|
add_subdirectory(GameDatabase)
|
||||||
|
|
||||||
add_library(dDatabase STATIC "MigrationRunner.cpp")
|
add_library(dDatabase STATIC "MigrationRunner.cpp")
|
||||||
|
|
||||||
add_custom_target(conncpp_dylib
|
|
||||||
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${PROJECT_BINARY_DIR})
|
|
||||||
|
|
||||||
add_dependencies(dDatabase conncpp_dylib)
|
|
||||||
|
|
||||||
target_include_directories(dDatabase PUBLIC ".")
|
target_include_directories(dDatabase PUBLIC ".")
|
||||||
target_link_libraries(dDatabase
|
target_link_libraries(dDatabase
|
||||||
PUBLIC dDatabaseCDClient dDatabaseGame)
|
PUBLIC magic_enum::magic_enum dDatabaseCDClient dDatabaseGame)
|
||||||
|
|||||||
15
dECS/CMakeLists.txt
Normal file
15
dECS/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
|
||||||
|
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
|
||||||
|
|
||||||
|
add_library(dECS STATIC
|
||||||
|
"Core.h"
|
||||||
|
"Iter.h"
|
||||||
|
"Core.cpp"
|
||||||
|
"System.cpp"
|
||||||
|
)
|
||||||
|
target_include_directories(dECS PUBLIC .)
|
||||||
|
target_link_libraries(dECS PRIVATE dCommon magic_enum::magic_enum)
|
||||||
|
target_compile_options(dECS PRIVATE
|
||||||
|
"$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra>>"
|
||||||
|
"$<${msvc_cxx}:$<BUILD_INTERFACE:/W3>>"
|
||||||
|
)
|
||||||
70
dECS/Core.cpp
Normal file
70
dECS/Core.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#include <atomic>
|
||||||
|
#include <magic_enum/magic_enum_containers.hpp>
|
||||||
|
#include <eReplicaComponentType.h>
|
||||||
|
#include "Core.h"
|
||||||
|
|
||||||
|
namespace dECS {
|
||||||
|
struct WorldData {
|
||||||
|
using CompSignature = magic_enum::containers::bitset<eReplicaComponentType>;
|
||||||
|
using CompMap = std::unordered_map<LWOOBJID, CompSignature>;
|
||||||
|
using CompStorage = std::unordered_map<eReplicaComponentType, std::unique_ptr<IStorage>>;
|
||||||
|
|
||||||
|
std::atomic<LWOOBJID> nextId = 1;
|
||||||
|
CompMap map;
|
||||||
|
CompStorage data;
|
||||||
|
};
|
||||||
|
|
||||||
|
World::World() : m_World{ std::make_shared<WorldData>() } {};
|
||||||
|
|
||||||
|
Entity World::MakeEntity() {
|
||||||
|
return Entity{ m_World->nextId.fetch_add(1, std::memory_order::relaxed),
|
||||||
|
m_World };
|
||||||
|
}
|
||||||
|
|
||||||
|
void* Entity::AddComponent(const eReplicaComponentType kind, const StorageConstructor storageConstructor) {
|
||||||
|
if (auto w = m_World.lock()) {
|
||||||
|
// Add to kind signature
|
||||||
|
w->map[m_Id].set(kind, true);
|
||||||
|
|
||||||
|
// Get or add storage
|
||||||
|
auto storageIt = w->data.find(kind);
|
||||||
|
if (storageIt == w->data.cend()) {
|
||||||
|
bool inserted = false;
|
||||||
|
std::tie(storageIt, inserted) = w->data.try_emplace(kind, storageConstructor());
|
||||||
|
if (!inserted) throw "storage emplacement failure";
|
||||||
|
}
|
||||||
|
auto& storage = *storageIt->second;
|
||||||
|
|
||||||
|
// Return reference if already mapped, otherwise add component
|
||||||
|
auto compIt = storage.rowMap.find(m_Id);
|
||||||
|
if (compIt == storage.rowMap.cend()) {
|
||||||
|
const auto curSize = storage.rowMap.size();
|
||||||
|
storage.rowMap.emplace(m_Id, curSize);
|
||||||
|
return storage.emplace_back();
|
||||||
|
}
|
||||||
|
const auto row = compIt->second;
|
||||||
|
return storage.at(row);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* Entity::GetComponent(const eReplicaComponentType kind) const {
|
||||||
|
if (auto const w = m_World.lock()) {
|
||||||
|
// Check that the entity has this component
|
||||||
|
if (!w->map[m_Id].test(kind)) return nullptr;
|
||||||
|
|
||||||
|
// Get the location where it's stored
|
||||||
|
const auto& storage = *w->data.at(kind);
|
||||||
|
const auto it = storage.rowMap.find(m_Id);
|
||||||
|
if (it == storage.rowMap.cend()) return nullptr;
|
||||||
|
const auto row = it->second;
|
||||||
|
return storage.at(row);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* Entity::GetComponent(const eReplicaComponentType kind) {
|
||||||
|
// Casting away const for this overload is safe, if not at all pretty
|
||||||
|
return const_cast<void*>(std::as_const(*this).GetComponent(kind));
|
||||||
|
}
|
||||||
|
}
|
||||||
180
dECS/Core.h
Normal file
180
dECS/Core.h
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Component;
|
||||||
|
enum class eReplicaComponentType : uint32_t;
|
||||||
|
using LWOOBJID = int64_t;
|
||||||
|
|
||||||
|
namespace dECS {
|
||||||
|
// template <typename C>
|
||||||
|
// concept IsComponent = std::derived_from<C, Component>;
|
||||||
|
|
||||||
|
// Data structures
|
||||||
|
struct WorldData;
|
||||||
|
class World;
|
||||||
|
|
||||||
|
template <typename... Cs>
|
||||||
|
class System;
|
||||||
|
|
||||||
|
class Entity;
|
||||||
|
struct IStorage;
|
||||||
|
|
||||||
|
template <typename C>
|
||||||
|
class Storage;
|
||||||
|
|
||||||
|
using WorldPtr = std::shared_ptr<WorldData>;
|
||||||
|
using WeakWorldPtr = std::weak_ptr<WorldData>;
|
||||||
|
|
||||||
|
class World {
|
||||||
|
public:
|
||||||
|
World();
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Entity MakeEntity();
|
||||||
|
|
||||||
|
template <typename... Cs>
|
||||||
|
[[nodiscard]]
|
||||||
|
System<Cs...> MakeSystem() {
|
||||||
|
return System<Cs...>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Cs, typename S>
|
||||||
|
[[nodiscard]]
|
||||||
|
System<Cs...> MakeSystem(S&& name) {
|
||||||
|
return System<Cs...>{ std::forward<S>(name) };
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WorldPtr m_World;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Cs>
|
||||||
|
class System {
|
||||||
|
public:
|
||||||
|
friend System World::MakeSystem<Cs...>();
|
||||||
|
|
||||||
|
template <typename... Ts, typename S>
|
||||||
|
friend System<Ts...> World::MakeSystem(S&&);
|
||||||
|
|
||||||
|
/*template <typename Fn>
|
||||||
|
requires std::is_invocable_r_v<void, Fn(Cs...), ObjId, Cs...>
|
||||||
|
void ForEach(Fn&& f) {
|
||||||
|
for (ObjId i = 0; i < mT.size(); ++i) {
|
||||||
|
auto& c = mT[i];
|
||||||
|
f(i, std::get<Cs>(c)...);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
template <typename Fn>
|
||||||
|
requires std::is_invocable_r_v<void, Fn(Cs...), Cs...>
|
||||||
|
void ForEach(Fn&& fn) {
|
||||||
|
std::tuple<Cs...> comps; // some sort of iterator that returns a tuple each 'step?'
|
||||||
|
for (size_t i = 0; i < 5; ++i) {
|
||||||
|
fn(std::get<Cs>(comps)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
System() = default;
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
explicit System(S&& name)
|
||||||
|
: m_name{ std::forward<S>(name) }
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string m_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Entity {
|
||||||
|
public:
|
||||||
|
friend Entity World::MakeEntity();
|
||||||
|
|
||||||
|
using StorageConstructor = std::function<std::unique_ptr<IStorage>()>;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr LWOOBJID GetObjectID() const noexcept {
|
||||||
|
return m_Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]]
|
||||||
|
void* AddComponent(eReplicaComponentType, StorageConstructor);
|
||||||
|
|
||||||
|
template <typename C>
|
||||||
|
[[maybe_unused]]
|
||||||
|
C* AddComponent() {
|
||||||
|
return static_cast<C*>(AddComponent(C::ComponentType, std::make_unique<Storage<C>>));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const void* GetComponent(eReplicaComponentType) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
void* GetComponent(eReplicaComponentType);
|
||||||
|
|
||||||
|
template <typename C>
|
||||||
|
[[nodiscard]]
|
||||||
|
const C* GetComponent() const {
|
||||||
|
return static_cast<const C*>(GetComponent(C::ComponentType));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename C>
|
||||||
|
[[nodiscard]]
|
||||||
|
C* GetComponent() {
|
||||||
|
return static_cast<C*>(GetComponent(C::ComponentType));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Entity(const LWOOBJID id, const WeakWorldPtr world)
|
||||||
|
: m_Id{ id }
|
||||||
|
, m_World { world }
|
||||||
|
{}
|
||||||
|
|
||||||
|
LWOOBJID m_Id;
|
||||||
|
|
||||||
|
WeakWorldPtr m_World;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IStorage {
|
||||||
|
using RowMap = std::unordered_map<LWOOBJID, size_t>;
|
||||||
|
|
||||||
|
virtual ~IStorage() = default;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual void* at(size_t) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual const void* at(size_t) const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual void* emplace_back() = 0;
|
||||||
|
|
||||||
|
RowMap rowMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename C>
|
||||||
|
class Storage : public IStorage {
|
||||||
|
public:
|
||||||
|
[[nodiscard]]
|
||||||
|
void* at(const size_t index) override {
|
||||||
|
return static_cast<void*>(&m_Vec.at(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const void* at(const size_t index) const override {
|
||||||
|
return static_cast<const void*>(&m_Vec.at(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
void* emplace_back() override {
|
||||||
|
return static_cast<void*>(&m_Vec.emplace_back());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<C> m_Vec;
|
||||||
|
};
|
||||||
|
}
|
||||||
12
dECS/Iter.h
Normal file
12
dECS/Iter.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include "Core.h"
|
||||||
|
|
||||||
|
namespace dECS {
|
||||||
|
class Iter {
|
||||||
|
public:
|
||||||
|
[[nodiscard]]
|
||||||
|
bool Next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
WeakWorldPtr m_World;
|
||||||
|
};
|
||||||
|
}
|
||||||
52
dECS/System.cpp
Normal file
52
dECS/System.cpp
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#include "PetComponent.h"
|
||||||
|
#include "MovementAIComponent.h"
|
||||||
|
#include "MissionComponent.h"
|
||||||
|
#include "eMissionState.h"
|
||||||
|
|
||||||
|
using Pet = PetComponent;
|
||||||
|
using Mission = MissionComponent;
|
||||||
|
using MovementAI = MovementAIComponent;
|
||||||
|
|
||||||
|
struct Position : NiPoint3 {};
|
||||||
|
struct Treasure {};
|
||||||
|
|
||||||
|
namespace decs {
|
||||||
|
template <typename... Cs>
|
||||||
|
class System {
|
||||||
|
public:
|
||||||
|
template <typename Fn>
|
||||||
|
void each(Fn&& fn) {
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Scene {
|
||||||
|
public:
|
||||||
|
template <typename... Cs>
|
||||||
|
System<Cs...> system() {
|
||||||
|
return System<Cs...>{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
auto scene = decs::Scene{};
|
||||||
|
|
||||||
|
scene.system<Pet>()
|
||||||
|
.each([](Pet& pet) {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.system<Pet, MovementAI>()
|
||||||
|
.each([](Pet& pet, MovementAI& move) {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.system<Pet, const Mission, const Position>()
|
||||||
|
.each([](Pet& pet, Mission const& mission, Position const& pos) {
|
||||||
|
auto const digUnlocked = mission.GetMissionState(842) == eMissionState::COMPLETE;
|
||||||
|
auto const treasurePos = PetDigServer::GetClosestTreasure(pos);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -55,7 +55,7 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
|
|||||||
"VerifyBehavior.cpp")
|
"VerifyBehavior.cpp")
|
||||||
|
|
||||||
add_library(dBehaviors OBJECT ${DGAME_DBEHAVIORS_SOURCES})
|
add_library(dBehaviors OBJECT ${DGAME_DBEHAVIORS_SOURCES})
|
||||||
target_link_libraries(dBehaviors PUBLIC dDatabaseCDClient dPhysics)
|
target_link_libraries(dBehaviors PUBLIC dDatabaseCDClient dPhysics magic_enum::magic_enum)
|
||||||
target_include_directories(dBehaviors PUBLIC "."
|
target_include_directories(dBehaviors PUBLIC "."
|
||||||
"${PROJECT_SOURCE_DIR}/dGame/dGameMessages" # via BehaviorContext.h
|
"${PROJECT_SOURCE_DIR}/dGame/dGameMessages" # via BehaviorContext.h
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
|||||||
@@ -79,4 +79,4 @@ target_include_directories(dComponents PUBLIC "."
|
|||||||
)
|
)
|
||||||
target_precompile_headers(dComponents REUSE_FROM dGameBase)
|
target_precompile_headers(dComponents REUSE_FROM dGameBase)
|
||||||
|
|
||||||
target_link_libraries(dComponents INTERFACE dBehaviors)
|
target_link_libraries(dComponents PUBLIC magic_enum::magic_enum INTERFACE dBehaviors)
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
#include "Component.h"
|
#include "Component.h"
|
||||||
|
|
||||||
|
|
||||||
Component::Component(Entity* parent) {
|
|
||||||
m_Parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component::~Component() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity* Component::GetParent() const {
|
|
||||||
return m_Parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Component::Update(float deltaTime) {
|
void Component::Update(float deltaTime) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
#include "tinyxml2.h"
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
|
namespace RakNet {
|
||||||
|
class BitStream;
|
||||||
|
};
|
||||||
|
|
||||||
class Entity;
|
class Entity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,14 +13,15 @@ class Entity;
|
|||||||
*/
|
*/
|
||||||
class Component {
|
class Component {
|
||||||
public:
|
public:
|
||||||
Component(Entity* parent);
|
Component() = default;
|
||||||
virtual ~Component();
|
Component(Entity* parent) : m_Parent{ parent } {}
|
||||||
|
virtual ~Component() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the owner of this component
|
* Gets the owner of this component
|
||||||
* @return the owner of this component
|
* @return the owner of this component
|
||||||
*/
|
*/
|
||||||
Entity* GetParent() const;
|
Entity* GetParent() const { return m_Parent; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the component in the game loop
|
* Updates the component in the game loop
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ set(DGAME_DGAMEMESSAGES_SOURCES
|
|||||||
|
|
||||||
add_library(dGameMessages OBJECT ${DGAME_DGAMEMESSAGES_SOURCES})
|
add_library(dGameMessages OBJECT ${DGAME_DGAMEMESSAGES_SOURCES})
|
||||||
target_link_libraries(dGameMessages
|
target_link_libraries(dGameMessages
|
||||||
PUBLIC dDatabase
|
PUBLIC magic_enum::magic_enum dDatabase
|
||||||
INTERFACE dGameBase # TradingManager
|
INTERFACE dGameBase # TradingManager
|
||||||
)
|
)
|
||||||
target_include_directories(dGameMessages PUBLIC "."
|
target_include_directories(dGameMessages PUBLIC "."
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ target_include_directories(dInventory PUBLIC "."
|
|||||||
"${PROJECT_SOURCE_DIR}/dGame/dMission" # via MissionComponent.h
|
"${PROJECT_SOURCE_DIR}/dGame/dMission" # via MissionComponent.h
|
||||||
"${PROJECT_SOURCE_DIR}/dZoneManager" # via Item.cpp
|
"${PROJECT_SOURCE_DIR}/dZoneManager" # via Item.cpp
|
||||||
)
|
)
|
||||||
|
target_link_libraries(dInventory PUBLIC magic_enum::magic_enum)
|
||||||
target_precompile_headers(dInventory REUSE_FROM dGameBase)
|
target_precompile_headers(dInventory REUSE_FROM dGameBase)
|
||||||
# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible.
|
# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible.
|
||||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97185
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97185
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#define _VARIADIC_MAX 10
|
#define _VARIADIC_MAX 10
|
||||||
#include "dCommonVars.h"
|
#include "dCommonVars.h"
|
||||||
#include "dNetCommon.h"
|
#include "dNetCommon.h"
|
||||||
#include "magic_enum.hpp"
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
enum class ServerType : uint32_t;
|
enum class ServerType : uint32_t;
|
||||||
enum class eLoginResponse : uint8_t;
|
enum class eLoginResponse : uint8_t;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ set(DNET_SOURCES "AuthPackets.cpp"
|
|||||||
"ZoneInstanceManager.cpp")
|
"ZoneInstanceManager.cpp")
|
||||||
|
|
||||||
add_library(dNet STATIC ${DNET_SOURCES})
|
add_library(dNet STATIC ${DNET_SOURCES})
|
||||||
target_link_libraries(dNet PRIVATE bcrypt MD5)
|
target_link_libraries(dNet PUBLIC magic_enum::magic_enum PRIVATE bcrypt MD5)
|
||||||
target_include_directories(dNet PRIVATE
|
target_include_directories(dNet PRIVATE
|
||||||
"${PROJECT_SOURCE_DIR}/dCommon"
|
"${PROJECT_SOURCE_DIR}/dCommon"
|
||||||
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
|
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ target_include_directories(WorldServer PRIVATE
|
|||||||
"${PROJECT_SOURCE_DIR}/dServer" # BinaryPathFinder.h
|
"${PROJECT_SOURCE_DIR}/dServer" # BinaryPathFinder.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(WorldServer ${COMMON_LIBRARIES}
|
target_link_libraries(WorldServer PUBLIC ${COMMON_LIBRARIES}
|
||||||
dScripts
|
dScripts
|
||||||
dGameBase
|
dGameBase
|
||||||
dComponents
|
dComponents
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ include(GoogleTest)
|
|||||||
|
|
||||||
# Add the subdirectories
|
# Add the subdirectories
|
||||||
add_subdirectory(dCommonTests)
|
add_subdirectory(dCommonTests)
|
||||||
|
add_subdirectory(dECSTests)
|
||||||
add_subdirectory(dGameTests)
|
add_subdirectory(dGameTests)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "MessageType/Game.h"
|
#include "MessageType/Game.h"
|
||||||
#include "MessageType/World.h"
|
#include "MessageType/World.h"
|
||||||
#include "magic_enum.hpp"
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
#define ENUM_EQ(e, y, z)\
|
#define ENUM_EQ(e, y, z)\
|
||||||
LOG("%s %s", StringifiedEnum::ToString(static_cast<e>(y)).data(), #z);\
|
LOG("%s %s", StringifiedEnum::ToString(static_cast<e>(y)).data(), #z);\
|
||||||
|
|||||||
9
tests/dECSTests/CMakeLists.txt
Normal file
9
tests/dECSTests/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
add_executable(dECSTests
|
||||||
|
"TestECS.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Link needed libraries
|
||||||
|
target_link_libraries(dECSTests PRIVATE dCommon dGame dECS GTest::gtest_main)
|
||||||
|
|
||||||
|
# Discover the tests
|
||||||
|
gtest_discover_tests(dECSTests)
|
||||||
220
tests/dECSTests/TestECS.cpp
Normal file
220
tests/dECSTests/TestECS.cpp
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <span>
|
||||||
|
#include <optional>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "Core.h"
|
||||||
|
#include <dComponents/Component.h>
|
||||||
|
#include <eReplicaComponentType.h>
|
||||||
|
|
||||||
|
using namespace dECS;
|
||||||
|
|
||||||
|
namespace TestECS {
|
||||||
|
using LegacyComponent = ::Component;
|
||||||
|
|
||||||
|
namespace Component {
|
||||||
|
using enum eReplicaComponentType;
|
||||||
|
|
||||||
|
void* NULL_PARENT = nullptr;
|
||||||
|
|
||||||
|
struct Legacy : public LegacyComponent {
|
||||||
|
static constexpr eReplicaComponentType ComponentType = CHANGLING;
|
||||||
|
|
||||||
|
Legacy() = default;
|
||||||
|
|
||||||
|
void Update(float deltaTime) {
|
||||||
|
std::printf("Legacy updated!\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Invalid {
|
||||||
|
static constexpr eReplicaComponentType ComponentType = INVALID;
|
||||||
|
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Destroyable {
|
||||||
|
static constexpr eReplicaComponentType ComponentType = DESTROYABLE;
|
||||||
|
|
||||||
|
using FactionId = int32_t;
|
||||||
|
|
||||||
|
float health;
|
||||||
|
float maxHealth;
|
||||||
|
float armor;
|
||||||
|
float maxArmor;
|
||||||
|
float imag;
|
||||||
|
float maxImag;
|
||||||
|
uint32_t damageToAbsorb;
|
||||||
|
bool immune;
|
||||||
|
bool gmImmune;
|
||||||
|
bool shielded;
|
||||||
|
float actualMaxHealth;
|
||||||
|
float actualMaxArmor;
|
||||||
|
float actualMaxImagination;
|
||||||
|
std::vector<FactionId> factionIds;
|
||||||
|
bool smashable;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IFakeSystem {
|
||||||
|
virtual ~IFakeSystem() = default;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr virtual size_t Count() const noexcept = 0;
|
||||||
|
|
||||||
|
constexpr virtual void EmplaceBack() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Cs>
|
||||||
|
struct FakeSystem : public IFakeSystem {
|
||||||
|
template <typename C>
|
||||||
|
using Storage = std::vector<std::remove_const_t<C>>;
|
||||||
|
|
||||||
|
std::tuple<Storage<Cs>...> data;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr size_t Count() const noexcept override {
|
||||||
|
return std::get<0>(data).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void EmplaceBack() override {
|
||||||
|
(std::get<Storage<Cs>>(data).emplace_back(), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename C>
|
||||||
|
requires std::disjunction_v<std::is_same<C, Cs>...>
|
||||||
|
[[nodiscard]]
|
||||||
|
std::span<C> Get() {
|
||||||
|
return std::get<Storage<C>>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename C>
|
||||||
|
requires std::disjunction_v<std::is_same<C, Cs>...>
|
||||||
|
[[nodiscard]]
|
||||||
|
std::span<const C> Get() const {
|
||||||
|
return std::get<Storage<C>>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fn>
|
||||||
|
requires std::is_invocable_r_v<void, Fn(Cs...), Cs...>
|
||||||
|
void ForEach(Fn&& fn);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FakeIter {
|
||||||
|
public:
|
||||||
|
constexpr FakeIter(const IFakeSystem& fakeSys) noexcept
|
||||||
|
: m_System{ fakeSys }
|
||||||
|
{}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr bool Next() {
|
||||||
|
return m_Count++ > m_System.Count();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t m_Count;
|
||||||
|
const IFakeSystem& m_System;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Cs>
|
||||||
|
template <typename Fn>
|
||||||
|
requires std::is_invocable_r_v<void, Fn(Cs...), Cs...>
|
||||||
|
void FakeSystem<Cs...>::ForEach(Fn&& fn){
|
||||||
|
for (size_t i = 0; i < Count(); ++i) {
|
||||||
|
fn(Get<Cs>()[i]...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that entity IDs increment correctly
|
||||||
|
TEST(ECSTest, IncrementEntityIdsSingleThread) {
|
||||||
|
auto w = World{};
|
||||||
|
|
||||||
|
auto ea = w.MakeEntity();
|
||||||
|
ASSERT_EQ(ea.GetObjectID(), 1);
|
||||||
|
|
||||||
|
auto eb = w.MakeEntity();
|
||||||
|
ASSERT_EQ(eb.GetObjectID(), 2);
|
||||||
|
|
||||||
|
auto ec = w.MakeEntity();
|
||||||
|
ASSERT_EQ(ec.GetObjectID(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test adding and getting components
|
||||||
|
TEST(ECSTest, MakeOneEntityAndAddComponents) {
|
||||||
|
using namespace TestECS::Component;
|
||||||
|
|
||||||
|
auto w = World{};
|
||||||
|
auto e = w.MakeEntity();
|
||||||
|
ASSERT_EQ(e.GetObjectID(), 1);
|
||||||
|
|
||||||
|
// add component
|
||||||
|
auto* const testCompPtr = e.AddComponent<Invalid>();
|
||||||
|
ASSERT_NE(testCompPtr, nullptr);
|
||||||
|
ASSERT_EQ(testCompPtr->ComponentType, Invalid::ComponentType);
|
||||||
|
ASSERT_EQ(testCompPtr->value, 0);
|
||||||
|
testCompPtr->value = 15;
|
||||||
|
|
||||||
|
// try getting the same component we just added
|
||||||
|
auto* const gotTestCompPtr = e.GetComponent<Invalid>();
|
||||||
|
ASSERT_NE(gotTestCompPtr, nullptr);
|
||||||
|
ASSERT_EQ(gotTestCompPtr, testCompPtr);
|
||||||
|
ASSERT_NE(gotTestCompPtr->value, 0);
|
||||||
|
ASSERT_EQ(gotTestCompPtr->value, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test world scoping
|
||||||
|
TEST(ECSTest, WorldScope) {
|
||||||
|
using namespace TestECS::Component;
|
||||||
|
|
||||||
|
auto e = std::optional<dECS::Entity>{};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto w = World{};
|
||||||
|
e.emplace(w.MakeEntity());
|
||||||
|
ASSERT_EQ(e->GetObjectID(), 1);
|
||||||
|
|
||||||
|
// add component within scope
|
||||||
|
auto* const cPtr = e->AddComponent<Invalid>();
|
||||||
|
ASSERT_NE(cPtr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempting to access this component should return nullptr
|
||||||
|
// now that the world has gone out of scope
|
||||||
|
ASSERT_EQ(e->GetComponent<Invalid>(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and iterate over a system
|
||||||
|
TEST(ECSTest, CreateAndIterateOverSystem) {
|
||||||
|
using namespace TestECS::Component;
|
||||||
|
|
||||||
|
auto w = World{};
|
||||||
|
auto s = w.MakeSystem<Destroyable, const Invalid>("DestInvalid");
|
||||||
|
|
||||||
|
size_t count = 0;
|
||||||
|
s.ForEach([&](Destroyable& d, const Invalid& i) {
|
||||||
|
std::printf("i = %ld: d.health = %f\n", ++count, d.health);
|
||||||
|
d.health += 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ECSTest, FakeIterationForTestingPurposes) {
|
||||||
|
using namespace TestECS;
|
||||||
|
using namespace TestECS::Component;
|
||||||
|
|
||||||
|
auto s = FakeSystem<Legacy, Destroyable>{};
|
||||||
|
|
||||||
|
auto const r = 2 + std::rand() % 8;
|
||||||
|
for (size_t i = 0; i < r; ++i) {
|
||||||
|
s.EmplaceBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count = 0;
|
||||||
|
s.ForEach([&](Legacy& l, Destroyable& d) {
|
||||||
|
l.Update(0.0f);
|
||||||
|
std::printf("i = %ld: d.health = %f\n", ++count, d.health);
|
||||||
|
d.health += 1;
|
||||||
|
});
|
||||||
|
std::printf("Total count = %ld\n", count);
|
||||||
|
ASSERT_EQ(r, count);
|
||||||
|
}
|
||||||
3
thirdparty/CMakeLists.txt
vendored
3
thirdparty/CMakeLists.txt
vendored
@@ -34,9 +34,6 @@ target_include_directories(bcrypt PRIVATE "libbcrypt/src")
|
|||||||
# Source code for sqlite
|
# Source code for sqlite
|
||||||
add_subdirectory(SQLite)
|
add_subdirectory(SQLite)
|
||||||
|
|
||||||
# Source code for magic_enum
|
|
||||||
add_subdirectory(magic_enum)
|
|
||||||
|
|
||||||
# Create our third party library objects
|
# Create our third party library objects
|
||||||
add_subdirectory(raknet)
|
add_subdirectory(raknet)
|
||||||
|
|
||||||
|
|||||||
1
thirdparty/magic_enum
vendored
1
thirdparty/magic_enum
vendored
Submodule thirdparty/magic_enum deleted from e55b9b54d5
Reference in New Issue
Block a user