Compare commits
34 Commits
772779e78f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 67df059f59 | |||
| 82f56f70a9 | |||
| b8c21ebbde | |||
| 9a684ae93d | |||
| 2256c633d3 | |||
| 7c92cde143 | |||
| 88d4695596 | |||
| 55f12fcd69 | |||
| 27f50ffb35 | |||
| ab6841ab85 | |||
| a06140187d | |||
| ce9ea6752d | |||
| 58c69e4aa5 | |||
| beab60fcd2 | |||
| aafd2c05d2 | |||
| b18e1b9970 | |||
| a19776b930 | |||
| 30cbc7cd1c | |||
| cecea2eaff | |||
| e185c77cb8 | |||
| 084eb8266b | |||
| c388f20429 | |||
| 5112f2a3a3 | |||
| 7fd71ab0ac | |||
| 73ddc307eb | |||
| ffe6dd83ca | |||
| 07c9ed49c7 | |||
| 2326ddfee7 | |||
| fdb84b30b7 | |||
| af8b68ca46 | |||
| 0014ce0602 | |||
| 09b58fe1f5 | |||
| 4eb846f215 | |||
| 3d17902844 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -165,3 +165,6 @@ build-iPhoneSimulator/
|
|||||||
# Custom
|
# Custom
|
||||||
# For personal testing cases
|
# For personal testing cases
|
||||||
test/
|
test/
|
||||||
|
|
||||||
|
# clangd
|
||||||
|
.cache/
|
||||||
|
|||||||
@@ -6,33 +6,98 @@
|
|||||||
# obtain one at http://mozilla.org/MPL/2.0/.
|
# obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
cmake_policy(SET CMP0135 NEW)
|
||||||
|
|
||||||
project(Tourmaline VERSION 1)
|
project(Tourmaline VERSION 1)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
# Original - https://github.com/novelrt/NovelRT/blob/c877c1e870d62df98935489e9682d93b009fb2fd/ThirdParty/CMakeLists.txt#L6
|
||||||
|
# Modified version by williamjcm
|
||||||
|
macro(external_dependency name)
|
||||||
|
FetchContent_Declare(${name}
|
||||||
|
${ARGN}
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
PREFIX "${CMAKE_CURRENT_BINARY_DIR}/${name}"
|
||||||
|
TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/${name}/tmp"
|
||||||
|
STAMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/${name}/stamp"
|
||||||
|
DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${name}/dl"
|
||||||
|
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${name}/src"
|
||||||
|
SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${name}/build"
|
||||||
|
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${name}/bin"
|
||||||
|
INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/${name}/inst"
|
||||||
|
LOG_DIR "${CMAKE_CURRENT_BINARY_DIR}/${name}/log"
|
||||||
|
)
|
||||||
|
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}/cmake/${name}")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Third Party Libraries
|
||||||
|
external_dependency(Corrade
|
||||||
|
URL https://github.com/mosra/corrade/archive/2b7251d8bd8833a12f0d9b8deffca7a290340d3c.zip
|
||||||
|
URL_HASH SHA256=77ed07d373792ce05a64b87c84e7d4687965d6040df4e17b6e9922ca1cbd88c8
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(dep
|
||||||
|
Corrade
|
||||||
|
)
|
||||||
|
message(STATUS "Fetching ${dep}...")
|
||||||
|
add_subdirectory(external/${dep})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Building
|
||||||
add_library(${PROJECT_NAME} SHARED
|
add_library(${PROJECT_NAME} SHARED
|
||||||
"source/Systems/ECS/Components.cpp"
|
"source/Systems/ECS/Components.cpp"
|
||||||
"source/Systems/ECS/World.cpp"
|
"source/Systems/ECS/World.cpp"
|
||||||
"source/Systems/Logging.cpp"
|
"source/Systems/Logging.cpp"
|
||||||
"source/Systems/Random.cpp"
|
"source/Systems/Random.cpp"
|
||||||
"source/Types/UUID.cpp")
|
"source/Types/UUID.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Actual linking
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||||
|
Corrade::Main
|
||||||
|
Corrade::Containers
|
||||||
|
Corrade::Utility
|
||||||
|
Corrade::PluginManager
|
||||||
|
)
|
||||||
|
|
||||||
|
# Module stuff
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION})
|
set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION})
|
||||||
|
|
||||||
# Nothing to link right now
|
|
||||||
target_link_libraries(${PROJECT_NAME})
|
|
||||||
target_include_directories(${PROJECT_NAME}
|
target_include_directories(${PROJECT_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/headers>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/headers>
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>
|
||||||
|
$<BUILD_INTERFACE:${corrade_SOURCE_DIR}/src>
|
||||||
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>
|
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>
|
||||||
|
$<INSTALL_INTERFACE:include/${PROJECT_NAME}External>
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_GetProperties(Corrade SOURCE_DIR corrade_SOURCE_DIR)
|
||||||
|
FetchContent_GetProperties(Corrade BINARY_DIR corrade_BINARY_DIR)
|
||||||
|
|
||||||
|
install(
|
||||||
|
DIRECTORY
|
||||||
|
"${corrade_SOURCE_DIR}/src/Corrade/"
|
||||||
|
"${corrade_BINARY_DIR}/src/Corrade/"
|
||||||
|
DESTINATION "include/${PROJECT_NAME}External/Corrade"
|
||||||
|
FILES_MATCHING
|
||||||
|
PATTERN "*.h"
|
||||||
|
PATTERN "*.hpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
# A way to live forever
|
||||||
|
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
|
COMMAND printf "Dedicated to my beloved Goma, I love you. - Dora" >> $<TARGET_FILE:${PROJECT_NAME}>
|
||||||
)
|
)
|
||||||
|
|
||||||
install(
|
install(
|
||||||
@@ -40,7 +105,14 @@ install(
|
|||||||
EXPORT ${PROJECT_NAME}Targets
|
EXPORT ${PROJECT_NAME}Targets
|
||||||
LIBRARY DESTINATION lib
|
LIBRARY DESTINATION lib
|
||||||
ARCHIVE DESTINATION lib
|
ARCHIVE DESTINATION lib
|
||||||
INCLUDES DESTINATION include
|
INCLUDES DESTINATION include/${PROJECT_NAME}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS CorradeMain CorradeUtility CorradeContainers CorradePluginManager
|
||||||
|
LIBRARY DESTINATION lib
|
||||||
|
ARCHIVE DESTINATION lib
|
||||||
|
INCLUDES DESTINATION include/${PROJECT_NAME}External
|
||||||
)
|
)
|
||||||
|
|
||||||
install(DIRECTORY headers/ DESTINATION include/${PROJECT_NAME})
|
install(DIRECTORY headers/ DESTINATION include/${PROJECT_NAME})
|
||||||
|
|||||||
24
README.md
24
README.md
@@ -1,5 +1,5 @@
|
|||||||
# Tourmaline Engine
|
# Tourmaline Engine
|
||||||
Tormaline Engine is a game engine created for game development with C++23.
|
Tormaline Engine is a game engine created for C++23. [Source Code](https://git.thenight.club/cat/Tourmaline-Engine/).
|
||||||
|
|
||||||
### Currently the project is still trying to incorporate following parts (in no particular order):
|
### Currently the project is still trying to incorporate following parts (in no particular order):
|
||||||
- [ ] ECS (Builtin)
|
- [ ] ECS (Builtin)
|
||||||
@@ -17,17 +17,20 @@ Tormaline Engine is a game engine created for game development with C++23.
|
|||||||
# Usability Status
|
# Usability Status
|
||||||
Tourmaline is by no means currently usable. The project is incredible volatile with constant changes and improvements. Please wait until a release is made.
|
Tourmaline is by no means currently usable. The project is incredible volatile with constant changes and improvements. Please wait until a release is made.
|
||||||
|
|
||||||
|
However if you cannot just help yourself you can compile a tourmaline demo by running
|
||||||
|
```
|
||||||
|
g++ program.cpp -std=c++23 -lTourmaline -lCorradeUtility -lCorradePluginManager -I/usr/local/include/TourmalineExternal -o program
|
||||||
|
```
|
||||||
|
|
||||||
# 3rd Party Libraries Credits
|
# 3rd Party Libraries Credits
|
||||||
- [Corrade/Magnum](https://magnum.graphics/) - graphics middleware by Vladimír "Mosra" Vondruš.
|
- [Corrade/Magnum](https://magnum.graphics/) - graphics middleware by Vladimír "Mosra" Vondruš.
|
||||||
- [miniaudio](https://miniaud.io/) - audio playback and capture library by David "Mackron" Reid.
|
- [miniaudio](https://miniaud.io/) - audio playback and capture library by David "Mackron" Reid.
|
||||||
- [Xohiro](https://github.com/david-cortes/xoshiro_cpp/blob/master/xoshiro.h) implementation by David Blackman and Sebastiano Vigna
|
- [Xohiro](https://github.com/david-cortes/xoshiro_cpp/blob/master/xoshiro.h) implementation by David Blackman and Sebastiano Vigna.
|
||||||
|
|
||||||
# Special Thanks
|
# Special Thanks
|
||||||
- [Lars "harmonyisdead"](https://github.com/larsl2005) for hosting the thenight.club services.
|
- [Lars "harmonyisdead"](https://github.com/larsl2005) for hosting the thenight.club services.
|
||||||
- [Vladimír "Mosra" Vondruš](https://github.com/mosra) for their mentorship of my C++ knowledge/projects, and the people at the [magnum gitter channel](https://matrix.to/#/#mosra_magnum:gitter.im), for their continous support and feedback of this project.
|
- [Vladimír "Mosra" Vondruš](https://github.com/mosra) for their mentorship of my C++ knowledge/projects, and the people at the [magnum gitter channel](https://matrix.to/#/#mosra_magnum:gitter.im), for their continous support and feedback of this project.
|
||||||
- [Kae "voxelfoxkae"](https://voxelfox.co.uk/) for getting me into programming seriously and mentorship.
|
- [Kae "voxelfoxkae"](https://voxelfox.co.uk/) for mentorship.
|
||||||
- The members of The Night Club discord server, for being there for me.
|
|
||||||
|
|
||||||
|
|
||||||
# Not-so frequently asked questions
|
# Not-so frequently asked questions
|
||||||
### Version Scheming
|
### Version Scheming
|
||||||
@@ -41,14 +44,3 @@ From [Find Gemstone](https://www.findgemstone.com/blog/what-is-tourmaline-used-f
|
|||||||
> Tourmaline has unique electric properties, including the ability to generate an electric charge as well as maintain electromagnetic radiation. These characteristics make tourmaline useful in the production of a range of electronics.
|
> Tourmaline has unique electric properties, including the ability to generate an electric charge as well as maintain electromagnetic radiation. These characteristics make tourmaline useful in the production of a range of electronics.
|
||||||
|
|
||||||
In short Tourmaline is a very versatile "Gem". This name is given to the third iteration of a project that once only concern itself with Video playback on Magnum graphics.
|
In short Tourmaline is a very versatile "Gem". This name is given to the third iteration of a project that once only concern itself with Video playback on Magnum graphics.
|
||||||
|
|
||||||
## Short History
|
|
||||||
Initially this project was called **MagnumVideo**, as goal was to allow video playback on [Magnum Graphics](https://magnum.graphics/). Due to Magnum Graphic's OpenAL wrapper being very poorly, made the project required me to include an audio engine with the video playback.
|
|
||||||
> Original MagnumVideo was able to implement basic video playback with static frame time
|
|
||||||
|
|
||||||
So the project grew into **Overcharged Toolset** (MagnumVideo -> ChargeVideo), with goal to extend Magnum with Seamless Video, Audio, ECS, Shader, Particle, etc... systems. Each package aimed to be independent out of the box and only require each other if desired. However this was incredibly unsustainable and made development enjoyability and API quality very poor.
|
|
||||||
> ChargeVideo improved upon MagnumVideo with more supported types and PTS based timing. ChargeAudio was added to allow audio playback with video synced.
|
|
||||||
|
|
||||||
Around the time that Overcharged has been having an identity crisis, a dear friend in a libera IRC chat has introduced me to the wonderful world of [Ruby](https://www.ruby-lang.org/en/). I spent few days trying to combine two of my favourite things ever (Ruby's ease, and C++'s speed & control), this has led me to finally ditch Overcharged and start with a clean slate.
|
|
||||||
|
|
||||||
Therefore the name is basically "Overcharged + Ruby (a gem) = Tourmaline (Gem used to make electronics)" )
|
|
||||||
|
|||||||
16
external/Corrade/CMakeLists.txt
vendored
Normal file
16
external/Corrade/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
# Building options
|
||||||
|
set(CORRADE_BUILD_STATIC OFF)
|
||||||
|
|
||||||
|
# Feature options
|
||||||
|
set(CORRADE_WITH_MAIN ON)
|
||||||
|
set(CORRADE_WITH_UTILITY ON)
|
||||||
|
set(CORRADE_WITH_PLUGINMANAGER ON)
|
||||||
|
set(CORRADE_WITH_INTERCONNECT OFF)
|
||||||
|
set(CORRADE_WITH_TESTSUITE OFF)
|
||||||
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
|
set(CORRADE_WITH_RC ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(Corrade)
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
#define GUARD_TOURMALINE_CONCEPTS_H
|
#define GUARD_TOURMALINE_CONCEPTS_H
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
namespace Tourmaline::Concepts {
|
namespace Tourmaline::Concepts {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -30,5 +32,29 @@ template <typename Base, typename Type1, typename Type2>
|
|||||||
requires Either<Base, Type1, Type2>
|
requires Either<Base, Type1, Type2>
|
||||||
using OppositeOf = _opposite_of<Base, Type1, Type2>::type;
|
using OppositeOf = _opposite_of<Base, Type1, Type2>::type;
|
||||||
|
|
||||||
|
// heavily inspired by
|
||||||
|
// https://github.com/aminroosta/sqlite_modern_cpp/blob/master/hdr/sqlite_modern_cpp/utility/function_traits.h
|
||||||
|
template <typename> struct FunctionTraits;
|
||||||
|
template <typename Function>
|
||||||
|
struct FunctionTraits
|
||||||
|
: public FunctionTraits<
|
||||||
|
decltype(&std::remove_reference_t<Function>::operator())> {};
|
||||||
|
|
||||||
|
template <typename Return, typename Class, typename... Arguments>
|
||||||
|
struct FunctionTraits<Return (Class::*)(Arguments...) const>
|
||||||
|
: FunctionTraits<Return (*)(Arguments...)> {};
|
||||||
|
template <typename Return, typename Class, typename... Arguments>
|
||||||
|
struct FunctionTraits<Return (Class::*)(Arguments...)>
|
||||||
|
: FunctionTraits<Return (*)(Arguments...)> {};
|
||||||
|
|
||||||
|
template <typename Return, typename... Arguments>
|
||||||
|
struct FunctionTraits<Return (*)(Arguments...)> {
|
||||||
|
using returnType = Return;
|
||||||
|
using arguments = std::tuple<Arguments...>;
|
||||||
|
|
||||||
|
template <std::size_t index>
|
||||||
|
using argument = std::tuple_element_t<index, arguments>;
|
||||||
|
static constexpr std::size_t argumentCount = sizeof...(Arguments);
|
||||||
|
};
|
||||||
} // namespace Tourmaline::Concepts
|
} // namespace Tourmaline::Concepts
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: Dora "cat" <cat@thenight.club>
|
|
||||||
* SPDX-License-Identifier: MPL-2.0
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef GUARD_TOURMALINE_CONCEPTS_H
|
|
||||||
#define GUARD_TOURMALINE_CONCEPTS_H
|
|
||||||
#include <concepts>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace Tourmaline::Containers {
|
|
||||||
template <typename T>
|
|
||||||
concept Hashable = std::equality_comparable<T> && requires(T x) {
|
|
||||||
{ std::hash<T>{}(x) } -> std::convertible_to<std::size_t>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Base, typename Type1, typename Type2>
|
|
||||||
concept Either = std::same_as<Base, Type1> || std::same_as<Base, Type2>;
|
|
||||||
} // namespace Tourmaline::Containers
|
|
||||||
#endif
|
|
||||||
28
headers/Containers/ContainerOptions.hpp
Normal file
28
headers/Containers/ContainerOptions.hpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: Dora "cat" <cat@thenight.club>
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
|
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
#ifndef GUARD_TOURMALINE_CONTAINEROPTIONS_H
|
||||||
|
#define GUARD_TOURMALINE_CONTAINEROPTIONS_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Tourmaline::Containers {
|
||||||
|
struct HashmapOptions {
|
||||||
|
float loadFactor = 0.75f;
|
||||||
|
float minimizeFactor = 0.20f;
|
||||||
|
float leaningFactor = 2.5f;
|
||||||
|
std::size_t minimumBucketCount = 256;
|
||||||
|
std::size_t reservedBucketSpace = 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DualKeyMapOptions {
|
||||||
|
std::uint64_t baseReservation = 2048;
|
||||||
|
};
|
||||||
|
} // namespace Tourmaline::Containers
|
||||||
|
#endif
|
||||||
@@ -10,8 +10,12 @@
|
|||||||
#define GUARD_TOURMALINE_DUALKEYMAP_H
|
#define GUARD_TOURMALINE_DUALKEYMAP_H
|
||||||
#include "../Concepts.hpp"
|
#include "../Concepts.hpp"
|
||||||
#include "../Systems/Logging.hpp"
|
#include "../Systems/Logging.hpp"
|
||||||
|
#include "ContainerOptions.hpp"
|
||||||
|
#include "Hashmap.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include "Corrade/Containers/Array.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -26,15 +30,20 @@
|
|||||||
|
|
||||||
namespace Tourmaline::Containers {
|
namespace Tourmaline::Containers {
|
||||||
template <Concepts::Hashable AKey, Concepts::Hashable BKey, typename Value,
|
template <Concepts::Hashable AKey, Concepts::Hashable BKey, typename Value,
|
||||||
uint64_t baseReservation = 2048>
|
DualKeyMapOptions Options = {}>
|
||||||
class DualkeyMap {
|
class DualkeyMap {
|
||||||
public:
|
public:
|
||||||
// Return Types
|
// Return Types
|
||||||
template <typename OppositeKey, std::size_t Count>
|
template <typename OppositeKey>
|
||||||
requires Concepts::Either<OppositeKey, AKey, BKey>
|
requires Concepts::Either<OppositeKey, AKey, BKey>
|
||||||
using MultiQueryResult =
|
struct MultiQueryResult {
|
||||||
std::pair<const OppositeKey &,
|
// Having to use pointers here over references was not fun
|
||||||
std::array<std::reference_wrapper<Value>, Count>>;
|
// but it was for greater good
|
||||||
|
const OppositeKey *oppositeKey;
|
||||||
|
Corrade::Containers::Array<Value *> valueQueryResults;
|
||||||
|
std::size_t howManyFound = 1;
|
||||||
|
};
|
||||||
|
|
||||||
using QueryResult =
|
using QueryResult =
|
||||||
std::pair<std::variant<std::monostate, std::reference_wrapper<const AKey>,
|
std::pair<std::variant<std::monostate, std::reference_wrapper<const AKey>,
|
||||||
std::reference_wrapper<const BKey>>,
|
std::reference_wrapper<const BKey>>,
|
||||||
@@ -42,7 +51,7 @@ public:
|
|||||||
using Entry = std::tuple<const AKey &, const BKey &, Value &>;
|
using Entry = std::tuple<const AKey &, const BKey &, Value &>;
|
||||||
|
|
||||||
// Construct/Destruct
|
// Construct/Destruct
|
||||||
DualkeyMap() { hashList.reserve(baseReservation); }
|
DualkeyMap() { hashList.reserve(Options.baseReservation); }
|
||||||
~DualkeyMap() {
|
~DualkeyMap() {
|
||||||
// I'm sure there is a better way to do this
|
// I'm sure there is a better way to do this
|
||||||
for (DualkeyHash *hash : hashList) {
|
for (DualkeyHash *hash : hashList) {
|
||||||
@@ -197,23 +206,20 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Key,
|
template <typename Key,
|
||||||
typename OppositeKey = Concepts::OppositeOf<Key, AKey, BKey>,
|
typename OppositeKey = Concepts::OppositeOf<Key, AKey, BKey>>
|
||||||
std::size_t keyCount>
|
|
||||||
requires Concepts::Either<Key, AKey, BKey>
|
requires Concepts::Either<Key, AKey, BKey>
|
||||||
[[nodiscard("Discarding a very expensive query!")]]
|
[[nodiscard("Discarding a very expensive query!")]]
|
||||||
int QueryWithAll(const Key (&keys)[keyCount]) {
|
std::vector<MultiQueryResult<OppositeKey>>
|
||||||
std::vector<unprocessedMultiQueryResult<OppositeKey, keyCount>>
|
QueryWithAll(const Corrade::Containers::Array<Key> &keys) {
|
||||||
queryResults = queryWithMany<Key>(keys);
|
std::vector<MultiQueryResult<OppositeKey>> queryResult =
|
||||||
|
queryWithMany<Key>(keys);
|
||||||
|
|
||||||
// You could very well use auto here but this helps
|
std::erase_if(queryResult,
|
||||||
// with LSP hints
|
[keyCount = keys.size()](
|
||||||
for (const unprocessedMultiQueryResult<OppositeKey, keyCount> &queryRecord :
|
const MultiQueryResult<OppositeKey> &queryRecord) {
|
||||||
queryResults) {
|
return queryRecord.howManyFound != keyCount;
|
||||||
if (queryRecord.howManyFound == keyCount) {
|
});
|
||||||
}
|
return queryResult;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scan(std::function<bool(const std::size_t firstKeyHash,
|
void Scan(std::function<bool(const std::size_t firstKeyHash,
|
||||||
@@ -244,13 +250,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// Interal data structures
|
// Interal data structures
|
||||||
template <typename OppositeKey, std::size_t keyCount>
|
|
||||||
struct unprocessedMultiQueryResult {
|
|
||||||
OppositeKey *resultKey = nullptr;
|
|
||||||
std::size_t howManyFound = 1;
|
|
||||||
std::array<Value *, keyCount> valueQueryResults;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DualkeyHash {
|
struct DualkeyHash {
|
||||||
DualkeyHash(AKey &&firstKey, BKey &&secondKey, Value &&value)
|
DualkeyHash(AKey &&firstKey, BKey &&secondKey, Value &&value)
|
||||||
: firstKey(std::move(firstKey)), secondKey(std::move(secondKey)),
|
: firstKey(std::move(firstKey)), secondKey(std::move(secondKey)),
|
||||||
@@ -271,14 +270,14 @@ private:
|
|||||||
|
|
||||||
// Interal querying
|
// Interal querying
|
||||||
template <typename Key,
|
template <typename Key,
|
||||||
typename OppositeKey = Concepts::OppositeOf<Key, AKey, BKey>,
|
typename OppositeKey = Concepts::OppositeOf<Key, AKey, BKey>>
|
||||||
std::size_t keyCount>
|
inline std::vector<MultiQueryResult<OppositeKey>>
|
||||||
inline std::vector<unprocessedMultiQueryResult<OppositeKey, keyCount>>
|
queryWithMany(const Corrade::Containers::Array<Key> &keys) {
|
||||||
queryWithMany(const Key (&keys)[keyCount]) {
|
|
||||||
constexpr bool searchingInFirstKey = std::is_same_v<Key, AKey>;
|
constexpr bool searchingInFirstKey = std::is_same_v<Key, AKey>;
|
||||||
|
std::size_t keyCount = keys.size();
|
||||||
|
|
||||||
// I really can't wait for C++26 contracts
|
// I really can't wait for C++26 contracts
|
||||||
if constexpr (keyCount == 0) {
|
if (keyCount == 0) {
|
||||||
Systems::Logging::Log("Failed to Query! QueryWithAll require at least 2 "
|
Systems::Logging::Log("Failed to Query! QueryWithAll require at least 2 "
|
||||||
"key to be given, zero was given! Terminating",
|
"key to be given, zero was given! Terminating",
|
||||||
"Dualkey Map",
|
"Dualkey Map",
|
||||||
@@ -286,7 +285,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hoping this never ever gets triggered :sigh:
|
// Hoping this never ever gets triggered :sigh:
|
||||||
if constexpr (keyCount == 1) {
|
if (keyCount == 1) {
|
||||||
Systems::Logging::Log("QueryWithAll should not be used for single key "
|
Systems::Logging::Log("QueryWithAll should not be used for single key "
|
||||||
"entry! Please use Query for this instead.",
|
"entry! Please use Query for this instead.",
|
||||||
"Dualkey Map", Systems::Logging::LogLevel::Error);
|
"Dualkey Map", Systems::Logging::LogLevel::Error);
|
||||||
@@ -294,59 +293,57 @@ private:
|
|||||||
|
|
||||||
// While we don't necessary need the hashes,
|
// While we don't necessary need the hashes,
|
||||||
// it just helps us tremendously benefit from short circuit checks
|
// it just helps us tremendously benefit from short circuit checks
|
||||||
std::array<std::size_t, keyCount> keyHashes;
|
Corrade::Containers::Array<std::size_t> keyHashes{keyCount};
|
||||||
for (uint64_t index = 0; index < keyCount; index++) {
|
for (uint64_t index = 0; index < keyCount; index++) {
|
||||||
keyHashes[index] = std::hash<Key>{}(keys[index]);
|
keyHashes[index] = std::hash<Key>{}(keys[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t hashToCompare;
|
uint64_t hashToCompare;
|
||||||
Key *keyToCompare;
|
Key *keyToCompare;
|
||||||
OppositeKey *resultKey;
|
OppositeKey *oppositeKey;
|
||||||
std::vector<unprocessedMultiQueryResult<OppositeKey, keyCount>>
|
|
||||||
|
Containers::Hashmap<OppositeKey, MultiQueryResult<OppositeKey>,
|
||||||
|
{8.0f, 0.01f, 2.5f, 2048, 8}> // Aggressive hashmap :o
|
||||||
queryResults;
|
queryResults;
|
||||||
|
|
||||||
for (DualkeyHash *hash : hashList) {
|
for (DualkeyHash *hash : hashList) {
|
||||||
|
// Tombstone
|
||||||
|
if (hash == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// The hell of doing 2 conditions with similar logics in
|
// The hell of doing 2 conditions with similar logics in
|
||||||
// the same logical block
|
// the same logical block
|
||||||
if constexpr (searchingInFirstKey) {
|
if constexpr (searchingInFirstKey) {
|
||||||
hashToCompare = hash->firstKeyHash;
|
hashToCompare = hash->firstKeyHash;
|
||||||
keyToCompare = const_cast<AKey *>(&hash->firstKey);
|
keyToCompare = const_cast<AKey *>(&hash->firstKey);
|
||||||
resultKey = const_cast<BKey *>(&hash->secondKey);
|
oppositeKey = const_cast<BKey *>(&hash->secondKey);
|
||||||
} else {
|
} else {
|
||||||
hashToCompare = hash->secondKeyHash;
|
hashToCompare = hash->secondKeyHash;
|
||||||
keyToCompare = const_cast<BKey *>(&hash->secondKey);
|
keyToCompare = const_cast<BKey *>(&hash->secondKey);
|
||||||
resultKey = const_cast<AKey *>(&hash->firstKey);
|
oppositeKey = const_cast<AKey *>(&hash->firstKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The code above was done to make this code more uniform
|
// The code above was done to make this code more uniform
|
||||||
for (uint64_t index = 0; index < keyCount; index++) {
|
for (uint64_t index = 0; index < keyCount; index++) {
|
||||||
if (keyHashes[index] == hashToCompare && keys[index] == *keyToCompare) {
|
if (keyHashes[index] == hashToCompare && keys[index] == *keyToCompare) {
|
||||||
|
if (queryResults.Has(*oppositeKey)) [[likely]] {
|
||||||
bool doesExist = false;
|
auto &entry = queryResults.Get(*oppositeKey);
|
||||||
for (auto &queryRecord : queryResults) {
|
entry.valueQueryResults[index] = &hash->value;
|
||||||
if (*queryRecord.resultKey == *resultKey) {
|
++entry.howManyFound;
|
||||||
queryRecord.valueQueryResults[index] = &hash->value;
|
|
||||||
++queryRecord.howManyFound;
|
|
||||||
doesExist = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doesExist) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since the result record is not present
|
queryResults
|
||||||
// we have to make it
|
.Insert(
|
||||||
queryResults.emplace_back();
|
*oppositeKey,
|
||||||
auto &newRecord = queryResults.back();
|
{oppositeKey, Corrade::Containers::Array<Value *>{keyCount}})
|
||||||
newRecord.resultKey = resultKey;
|
.valueQueryResults[index] = &hash->value;
|
||||||
newRecord.valueQueryResults[index] = &hash->value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryResults;
|
return queryResults.ExtractValuesToArray();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace Tourmaline::Containers
|
} // namespace Tourmaline::Containers
|
||||||
|
|||||||
192
headers/Containers/Hashmap.hpp
Normal file
192
headers/Containers/Hashmap.hpp
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: Dora "cat" <cat@thenight.club>
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
|
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
#ifndef GUARD_TOURMALINE_HASHMAP_H
|
||||||
|
#define GUARD_TOURMALINE_HASHMAP_H
|
||||||
|
#include "../Concepts.hpp"
|
||||||
|
#include "../Systems/Logging.hpp"
|
||||||
|
#include "ContainerOptions.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Tourmaline::Containers {
|
||||||
|
template <Concepts::Hashable Key, typename Value, HashmapOptions Options = {}>
|
||||||
|
class Hashmap {
|
||||||
|
public:
|
||||||
|
Hashmap() { storage.resize(Options.minimumBucketCount); }
|
||||||
|
~Hashmap() { Clear(); }
|
||||||
|
|
||||||
|
Value &Insert(Key key, Value value) {
|
||||||
|
if (currentLoadFactor >= Options.loadFactor &&
|
||||||
|
currentlyRehashing == false) {
|
||||||
|
rehash();
|
||||||
|
}
|
||||||
|
std::size_t keyHash = std::hash<Key>{}(key),
|
||||||
|
keyHashPosition = keyHash % storage.size();
|
||||||
|
|
||||||
|
// Empty bucket
|
||||||
|
if (!storage[keyHashPosition].empty()) {
|
||||||
|
// Throws
|
||||||
|
Systems::Logging::Log("Trying to insert the same key twice! Throwing...",
|
||||||
|
"Hashmap", Systems::Logging::Error, Has(key));
|
||||||
|
} else {
|
||||||
|
storage[keyHashPosition].reserve(Options.reservedBucketSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
storage[keyHashPosition].emplace_back(key, std::move(value), keyHash);
|
||||||
|
currentLoadFactor = (++count) / static_cast<float>(bucketCount);
|
||||||
|
return storage[keyHashPosition].back().value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Remove(const Key &key) {
|
||||||
|
std::size_t keyHash = std::hash<Key>{}(key),
|
||||||
|
keyHashPosition = keyHash % storage.size();
|
||||||
|
|
||||||
|
// Throws
|
||||||
|
Systems::Logging::Log("Trying to remove a non-existant key! Throwing...",
|
||||||
|
"Hashmap", Systems::Logging::Error,
|
||||||
|
storage[keyHashPosition].empty());
|
||||||
|
std::erase_if(storage[keyHashPosition],
|
||||||
|
[keyHash, &key](const hashStorage &hash) {
|
||||||
|
return hash.hash == keyHash && hash.key == key;
|
||||||
|
});
|
||||||
|
|
||||||
|
currentLoadFactor = (--count) / static_cast<float>(bucketCount);
|
||||||
|
if (currentLoadFactor <= Options.minimizeFactor) {
|
||||||
|
rehash();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard("Unnecessary call of Has function")]]
|
||||||
|
bool Has(const Key &key) noexcept {
|
||||||
|
std::size_t keyHash = std::hash<Key>{}(key),
|
||||||
|
keyHashPosition = keyHash % storage.size();
|
||||||
|
|
||||||
|
// Empty bucket
|
||||||
|
if (storage[keyHashPosition].empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const hashStorage &hash : storage[keyHashPosition]) {
|
||||||
|
if (hash.hash == keyHash && hash.key == key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard("Unnecessary call of Get function")]]
|
||||||
|
Value &Get(const Key &key) {
|
||||||
|
std::size_t keyHash = std::hash<Key>{}(key),
|
||||||
|
keyHashPosition = keyHash % storage.size();
|
||||||
|
|
||||||
|
Systems::Logging::Log(
|
||||||
|
"Trying to access a non-existant bucket for a key! Throwing...",
|
||||||
|
"Hashmap", Systems::Logging::Error, storage[keyHashPosition].empty());
|
||||||
|
|
||||||
|
for (hashStorage &hash : storage[keyHashPosition]) {
|
||||||
|
if (hash.hash == keyHash && hash.key == key) {
|
||||||
|
return hash.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Systems::Logging::Log("Trying to access a non-existant key! Throwing...",
|
||||||
|
"Hashmap", Systems::Logging::Error);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard("Discarding an expensive operation!")]]
|
||||||
|
std::vector<Value> ExtractValuesToArray() {
|
||||||
|
std::vector<Value> result;
|
||||||
|
result.reserve(count);
|
||||||
|
|
||||||
|
for (bucket &entry : storage) {
|
||||||
|
for (hashStorage &hash : entry) {
|
||||||
|
result.emplace_back(std::move(hash.value));
|
||||||
|
}
|
||||||
|
entry.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
bucketCount = Options.minimumBucketCount;
|
||||||
|
std::vector<bucket> newStorage;
|
||||||
|
storage.swap(newStorage);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear() noexcept {
|
||||||
|
storage.clear();
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard("Unnecessary call of Count function")]]
|
||||||
|
std::size_t Count() noexcept {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool rehash(std::size_t goalSize = 0) {
|
||||||
|
// Minimum
|
||||||
|
goalSize = goalSize == 0 ? count : goalSize;
|
||||||
|
float wouldBeLoadFactor = goalSize / static_cast<float>(bucketCount);
|
||||||
|
if (wouldBeLoadFactor < Options.loadFactor &&
|
||||||
|
wouldBeLoadFactor > Options.minimizeFactor) [[unlikely]] {
|
||||||
|
return false; // No rehashing is required
|
||||||
|
}
|
||||||
|
|
||||||
|
// Putting it closer to minimizeFactor
|
||||||
|
std::size_t goalBucketCount = goalSize / preferredLoadFactor;
|
||||||
|
if (goalBucketCount < Options.minimumBucketCount) [[unlikely]] {
|
||||||
|
goalBucketCount = Options.minimumBucketCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to reallocate
|
||||||
|
if (goalBucketCount == bucketCount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentlyRehashing = true;
|
||||||
|
std::vector<bucket> oldStorage = std::move(storage);
|
||||||
|
storage = std::vector<bucket>();
|
||||||
|
storage.resize(goalBucketCount);
|
||||||
|
|
||||||
|
// Repopulate and cleanup
|
||||||
|
for (bucket &entry : oldStorage) {
|
||||||
|
for (hashStorage &hash : entry) {
|
||||||
|
Insert(std::move(hash.key), std::move(hash.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's necessary to write these again due to insert above
|
||||||
|
currentLoadFactor = goalSize / static_cast<float>(goalBucketCount);
|
||||||
|
bucketCount = goalBucketCount;
|
||||||
|
count = goalSize;
|
||||||
|
currentlyRehashing = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hashStorage {
|
||||||
|
Key key;
|
||||||
|
Value value;
|
||||||
|
std::size_t hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
using bucket = std::vector<hashStorage>;
|
||||||
|
std::vector<bucket> storage;
|
||||||
|
std::size_t count = 0, bucketCount = Options.minimumBucketCount;
|
||||||
|
float currentLoadFactor = 0,
|
||||||
|
preferredLoadFactor = (Options.loadFactor + Options.minimizeFactor) /
|
||||||
|
Options.leaningFactor;
|
||||||
|
bool currentlyRehashing = false; // Lock for Insert in rehash
|
||||||
|
};
|
||||||
|
} // namespace Tourmaline::Containers
|
||||||
|
#endif
|
||||||
@@ -10,64 +10,60 @@
|
|||||||
#ifndef GUARD_TOURMALINE_ECS_H
|
#ifndef GUARD_TOURMALINE_ECS_H
|
||||||
#define GUARD_TOURMALINE_ECS_H
|
#define GUARD_TOURMALINE_ECS_H
|
||||||
#include <any>
|
#include <any>
|
||||||
#include <format>
|
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
|
|
||||||
#include "../Containers/DualkeyMap.hpp"
|
#include "../Containers/DualkeyMap.hpp"
|
||||||
|
#include "../Containers/Hashmap.hpp"
|
||||||
#include "../Types.hpp"
|
#include "../Types.hpp"
|
||||||
#include "ECS/BuiltinComponents.hpp"
|
#include "ECS/BuiltinComponents.hpp"
|
||||||
#include "Logging.hpp"
|
#include "Logging.hpp"
|
||||||
|
|
||||||
namespace Tourmaline::Systems::ECS {
|
namespace Tourmaline::Systems::ECS {
|
||||||
using Entity = Tourmaline::Type::UUID;
|
using Entity = Tourmaline::Type::UUID;
|
||||||
class World;
|
using System = Tourmaline::Type::UUID;
|
||||||
|
|
||||||
class World {
|
class World {
|
||||||
public:
|
public:
|
||||||
|
World() {}
|
||||||
// ====== World controls ======
|
// ====== World controls ======
|
||||||
void Step();
|
void Step();
|
||||||
|
|
||||||
// ======== Entities ========
|
// ======== Entities ========
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Entity CreateEntity();
|
Entity CreateEntity();
|
||||||
|
[[nodiscard("Pointless call of EntityExists")]]
|
||||||
bool EntityExists(const Entity &entity) noexcept;
|
bool EntityExists(const Entity &entity) noexcept;
|
||||||
[[nodiscard("It is not guaranteed that an entity can always be destroyed, "
|
|
||||||
"please make sure by checking the returned bool")]]
|
|
||||||
bool DestroyEntity(Entity entity);
|
bool DestroyEntity(Entity entity);
|
||||||
|
|
||||||
// ======== Components ========
|
// ======== Components ========
|
||||||
template <isAComponent component, typename... Args>
|
template <isAComponent Component, typename... ComponentArgs>
|
||||||
component &AddComponent(const Entity &entity,
|
Component &AddComponent(const Entity &entity, ComponentArgs &&...args) {
|
||||||
Args &&...constructionArguments) {
|
auto newComponent = entityComponentMap.Insert(entity, typeid(Component),
|
||||||
auto newComponent = entityComponentMap.Insert(
|
Component(args...));
|
||||||
entity, typeid(component), component(constructionArguments...));
|
|
||||||
|
|
||||||
return std::any_cast<component &>(std::get<2>(newComponent));
|
return std::any_cast<Component &>(std::get<2>(newComponent));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <isAComponent component>
|
template <isAComponent Component>
|
||||||
[[nodiscard("Discarding an expensive operation's result!")]]
|
[[nodiscard("Pointless call of GetComponent")]]
|
||||||
component &GetComponent(const Entity &entity) {
|
Component &GetComponent(const Entity &entity) {
|
||||||
auto result = entityComponentMap.Query(entity, typeid(component));
|
auto result = entityComponentMap.Query(entity, typeid(Component));
|
||||||
if (result.empty()) {
|
if (result.empty()) {
|
||||||
Logging::Log(std::format("Entity {} does not have component {}!",
|
Logging::LogFormatted("Entity {} does not have component {}!",
|
||||||
entity.asString(), typeid(component).name()),
|
"ECS/GetComponent", Logging::LogLevel::Error,
|
||||||
"ECS/GetComponent", Logging::LogLevel::Error);
|
entity.asString(), typeid(Component).name());
|
||||||
}
|
}
|
||||||
return std::any_cast<component &>(result.begin()->second);
|
return std::any_cast<Component &>(result.begin()->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <isAComponent component>
|
template <isAComponent Component>
|
||||||
[[nodiscard("Discarding an expensive operation's result!")]]
|
[[nodiscard("Pointless call of HasComponent")]]
|
||||||
bool HasComponent(const Entity &entity) {
|
bool HasComponent(const Entity &entity) {
|
||||||
return entityComponentMap.Query(entity, typeid(component)).size();
|
return entityComponentMap.Query(entity, typeid(Component)).size();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <isAComponent component>
|
template <isAComponent Component> bool RemoveComponent(const Entity &entity) {
|
||||||
[[nodiscard("It is not guaranteed that a component can always be removed, "
|
return entityComponentMap.Remove(entity, typeid(Component));
|
||||||
"please make sure by checking the returned bool")]]
|
|
||||||
bool RemoveComponent(const Entity &entity) {
|
|
||||||
return entityComponentMap.Remove(entity, typeid(component));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copying is not allowed since the ECS world is meant to be
|
// Copying is not allowed since the ECS world is meant to be
|
||||||
@@ -76,8 +72,11 @@ public:
|
|||||||
World &operator=(const World &) = delete;
|
World &operator=(const World &) = delete;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Tourmaline::Containers::DualkeyMap<Entity, std::type_index, std::any>
|
using systemFunction =
|
||||||
|
std::function<void(const Entity &, std::span<std::any *>)>;
|
||||||
|
Containers::DualkeyMap<Entity, std::type_index, std::any>
|
||||||
entityComponentMap{};
|
entityComponentMap{};
|
||||||
|
Containers::Hashmap<System, systemFunction> registeredSystems{};
|
||||||
|
|
||||||
// ======== Life-cycle ========
|
// ======== Life-cycle ========
|
||||||
void preSystems();
|
void preSystems();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ concept isAComponent = std::derived_from<T, ECS::Component>;
|
|||||||
namespace Tourmaline::Systems::Components {
|
namespace Tourmaline::Systems::Components {
|
||||||
// Builtin
|
// Builtin
|
||||||
struct Base : public ECS::Component {
|
struct Base : public ECS::Component {
|
||||||
Base() {}
|
double x = 0, y = 0, z = 0;
|
||||||
};
|
};
|
||||||
} // namespace Tourmaline::Systems::Components
|
} // namespace Tourmaline::Systems::Components
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -8,31 +8,36 @@
|
|||||||
*/
|
*/
|
||||||
#ifndef GUARD_TOURMALINE_LOGGING_H
|
#ifndef GUARD_TOURMALINE_LOGGING_H
|
||||||
#define GUARD_TOURMALINE_LOGGING_H
|
#define GUARD_TOURMALINE_LOGGING_H
|
||||||
#include <array>
|
|
||||||
|
#include "Corrade/Containers/String.h"
|
||||||
|
#include "Corrade/Containers/StringView.h"
|
||||||
|
#include "Corrade/Utility/Format.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace Tourmaline::Systems {
|
namespace Tourmaline::Systems {
|
||||||
class Logging {
|
class Logging {
|
||||||
public:
|
public:
|
||||||
enum class LogLevel {
|
enum LogLevel { Critical, Error, Warning, Info, Debug, Trace };
|
||||||
Critical = 0,
|
|
||||||
Error = 1,
|
|
||||||
Warning = 2,
|
|
||||||
Info = 3,
|
|
||||||
Debug = 4,
|
|
||||||
Trace = 5
|
|
||||||
};
|
|
||||||
|
|
||||||
static void LogToFile(std::string File = "");
|
static void LogToFile(Corrade::Containers::String File = "");
|
||||||
static void Log(const std::string &message,
|
static void Log(Corrade::Containers::StringView message,
|
||||||
const std::string &position = "Unknown",
|
Corrade::Containers::StringView position = "Unknown",
|
||||||
LogLevel severity = LogLevel::Info, bool assertion = true);
|
LogLevel severity = LogLevel::Info, bool assertion = true);
|
||||||
|
|
||||||
|
template <class... Args>
|
||||||
|
static void LogFormatted(const char *format, const char *position,
|
||||||
|
LogLevel severity, const Args &...args) {
|
||||||
|
static Corrade::Containers::String output{Corrade::ValueInit, 4096};
|
||||||
|
std::size_t size = Corrade::Utility::formatInto(output, format, args...);
|
||||||
|
Log(Corrade::Containers::StringView{output.begin(), size}, position,
|
||||||
|
severity);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::fstream File;
|
static std::fstream File;
|
||||||
static std::array<std::pair<const std::string, const std::string>, 6>
|
static const char *LogLevelToColour[Trace + 1];
|
||||||
LogLevelToString;
|
static const char *LogLevelToString[Trace + 1];
|
||||||
};
|
};
|
||||||
} // namespace Tourmaline::Systems
|
} // namespace Tourmaline::Systems
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#ifndef GUARD_TOURMALINE_RANDOM_H
|
#ifndef GUARD_TOURMALINE_RANDOM_H
|
||||||
#define GUARD_TOURMALINE_RANDOM_H
|
#define GUARD_TOURMALINE_RANDOM_H
|
||||||
#include "../Types.hpp"
|
#include "../Types.hpp"
|
||||||
#include <TourmalineExternal/random/xoshiro.h>
|
#include "TourmalineExternal/random/xoshiro.h"
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
#ifndef GUARD_TOURMALINE_TYPES_H
|
#ifndef GUARD_TOURMALINE_TYPES_H
|
||||||
#define GUARD_TOURMALINE_TYPES_H
|
#define GUARD_TOURMALINE_TYPES_H
|
||||||
|
|
||||||
|
#include "Corrade/Containers/String.h"
|
||||||
#include "TourmalineExternal/random/xoshiro.h"
|
#include "TourmalineExternal/random/xoshiro.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -19,7 +21,7 @@ namespace Tourmaline::Type {
|
|||||||
class UUID {
|
class UUID {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
std::string asString() const;
|
Corrade::Containers::String asString() const;
|
||||||
bool operator==(const UUID &rhs) const;
|
bool operator==(const UUID &rhs) const;
|
||||||
|
|
||||||
UUID(uint64_t firstHalf, uint64_t secondHalf);
|
UUID(uint64_t firstHalf, uint64_t secondHalf);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Systems/ECS.hpp>
|
#include "Systems/ECS.hpp"
|
||||||
#include <Systems/ECS/BuiltinComponents.hpp>
|
#include "Systems/ECS/BuiltinComponents.hpp"
|
||||||
|
|
||||||
// Empty until future use
|
// Empty until future use
|
||||||
|
|||||||
@@ -7,10 +7,9 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Systems/ECS.hpp>
|
#include "Systems/ECS.hpp"
|
||||||
#include <Systems/ECS/BuiltinComponents.hpp>
|
#include "Systems/ECS/BuiltinComponents.hpp"
|
||||||
#include <Systems/Random.hpp>
|
#include "Systems/Random.hpp"
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
using namespace Tourmaline::Systems;
|
using namespace Tourmaline::Systems;
|
||||||
using namespace ECS;
|
using namespace ECS;
|
||||||
|
|||||||
@@ -7,7 +7,12 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Systems/Logging.hpp>
|
#include "Systems/Logging.hpp"
|
||||||
|
|
||||||
|
#include "Corrade/Containers/String.h"
|
||||||
|
#include "Corrade/Containers/StringView.h"
|
||||||
|
#include "Corrade/Tags.h"
|
||||||
|
#include "Corrade/Utility/Format.h"
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -18,51 +23,57 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string_view>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
using namespace Tourmaline::Systems;
|
using namespace Tourmaline::Systems;
|
||||||
|
using namespace Corrade::Containers;
|
||||||
|
using namespace Corrade::Utility;
|
||||||
|
|
||||||
// This is what happens when it takes you 50 years to implement
|
// This is what happens when it takes you 50 years to implement
|
||||||
// reflections to a language
|
// reflections to a language
|
||||||
std::array<std::pair<const std::string, const std::string>, 6>
|
const char *Logging::LogLevelToColour[Logging::Trace + 1]{
|
||||||
Logging::LogLevelToString{std::pair{"Critical", "[0;31m"},
|
"[0;31m", "[0;91m", "[0;33m", "[0;37m", "[0;92m", "[0;36m"};
|
||||||
{"Error", "[0;91m"},
|
const char *Logging::LogLevelToString[Logging::Trace + 1]{
|
||||||
{"Warning", "[0;33m"},
|
"Critical", "Error", "Warning", "Info", "Debug", "Trace"};
|
||||||
{"Info", "[0;37m"},
|
|
||||||
{"Debug", "[0;92m"},
|
|
||||||
{"Trace", "[0;36m"}};
|
|
||||||
std::fstream Logging::File;
|
std::fstream Logging::File;
|
||||||
|
|
||||||
void Logging::LogToFile(std::string File) {
|
void Logging::LogToFile(String File) {
|
||||||
if (File == "") {
|
if (File == "") {
|
||||||
const auto now = std::chrono::system_clock::now();
|
const auto now = std::chrono::system_clock::now();
|
||||||
File = std::format("Tourmaline-{:%Y-%j}.txt", now);
|
std::chrono::year_month_day ymd{std::chrono::floor<std::chrono::days>(now)};
|
||||||
|
File = String{Corrade::ValueInit, 128};
|
||||||
|
formatInto(File, "Tourmaline-{}-{}-{}.txt", static_cast<int>(ymd.year()),
|
||||||
|
static_cast<unsigned>(ymd.month()),
|
||||||
|
static_cast<unsigned>(ymd.day()));
|
||||||
}
|
}
|
||||||
Logging::File.open(File, std::fstream::out);
|
Logging::File.open(File.data(), std::fstream::out);
|
||||||
|
|
||||||
if (Logging::File.fail()) {
|
if (Logging::File.fail()) {
|
||||||
throw std::runtime_error("FAILED! Could not open or create the file: " +
|
String error =
|
||||||
File + "!\n" + strerror(errno));
|
format("FAILED! Could not open or create the file: {}! Error: {}", File,
|
||||||
|
strerror(errno));
|
||||||
|
throw std::runtime_error(error.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logging::Log(const std::string &message, const std::string &position,
|
void Logging::Log(StringView message, StringView position,
|
||||||
Logging::LogLevel severity, bool assertion) {
|
Logging::LogLevel severity, bool assertion) {
|
||||||
if (assertion) [[likely]] {
|
if (assertion) {
|
||||||
auto loglevelData =
|
static String output{Corrade::ValueInit,
|
||||||
Logging::LogLevelToString[static_cast<size_t>(severity)];
|
4096}; // This is done to stop allocations
|
||||||
std::string output =
|
std::size_t formattedSize = formatInto(
|
||||||
std::format("[{}@{}] {}\n", loglevelData.first, position, message);
|
output, "[{}@{}] {}\n", LogLevelToString[severity], position, message);
|
||||||
|
|
||||||
std::print("\033{} {}\033[0m", loglevelData.second, output);
|
std::print(
|
||||||
|
"\033{} {}\033[0m", LogLevelToColour[severity],
|
||||||
|
std::string_view{output.begin(), output.begin() + formattedSize});
|
||||||
if (Logging::File.is_open()) {
|
if (Logging::File.is_open()) {
|
||||||
Logging::File.write(output.c_str(), output.size());
|
Logging::File.write(output.data(), formattedSize);
|
||||||
Logging::File.flush(); // Terrible but necessary sadly
|
Logging::File.flush(); // Terrible but necessary sadly
|
||||||
}
|
}
|
||||||
|
|
||||||
if (severity == Logging::LogLevel::Error) {
|
if (severity == Logging::LogLevel::Error) {
|
||||||
throw std::runtime_error(output);
|
throw std::runtime_error(output.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (severity == Logging::LogLevel::Critical) {
|
if (severity == Logging::LogLevel::Critical) {
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
#include <Systems/Random.hpp>
|
|
||||||
|
#include "Systems/Random.hpp"
|
||||||
|
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|||||||
@@ -7,17 +7,19 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Types.hpp>
|
#include "Corrade/Utility/Format.h"
|
||||||
|
#include "Types.hpp"
|
||||||
|
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <format>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace Tourmaline::Type;
|
using namespace Tourmaline::Type;
|
||||||
std::string UUID::asString() const {
|
using namespace Corrade::Containers;
|
||||||
return std::format("{:016X}{:016X}", firstHalf, secondHalf);
|
using namespace Corrade::Utility;
|
||||||
|
String UUID::asString() const {
|
||||||
|
return format("{:.16X}{:.16X}", firstHalf, secondHalf);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UUID::operator==(const UUID &rhs) const {
|
bool UUID::operator==(const UUID &rhs) const {
|
||||||
|
|||||||
Reference in New Issue
Block a user