Compare commits
1 Commits
main
...
fbbba96705
| Author | SHA1 | Date | |
|---|---|---|---|
| fbbba96705 |
16
.clangd
16
.clangd
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"CompileFlags": {
|
||||
"Add": [
|
||||
"-std=c++23",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wpedantic",
|
||||
]
|
||||
},
|
||||
"Index": {
|
||||
"Background": "Build"
|
||||
},
|
||||
"Completion": {
|
||||
"AllScopes": true
|
||||
}
|
||||
}
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -162,9 +162,3 @@ build-iPhoneSimulator/
|
||||
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
||||
# .rubocop-https?--*
|
||||
|
||||
# Custom
|
||||
# For personal testing cases
|
||||
test/
|
||||
|
||||
# clangd
|
||||
.cache/
|
||||
|
||||
120
CMakeLists.txt
120
CMakeLists.txt
@@ -1,120 +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/.
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
|
||||
project(Tourmaline VERSION 1)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address")
|
||||
endif()
|
||||
|
||||
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
|
||||
"source/Systems/ECS/Components.cpp"
|
||||
"source/Systems/ECS/World.cpp"
|
||||
"source/Systems/Logging.cpp"
|
||||
"source/Systems/Random.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})
|
||||
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/headers>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>
|
||||
$<BUILD_INTERFACE:${corrade_SOURCE_DIR}/src>
|
||||
$<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(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${PROJECT_NAME}Targets
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
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 ${PROJECT_NAME}External/ DESTINATION
|
||||
include/${PROJECT_NAME}External)
|
||||
45
README.md
45
README.md
@@ -1,46 +1,9 @@
|
||||
# Tourmaline Engine
|
||||
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):
|
||||
- [ ] ECS (Builtin)
|
||||
- [X] Logging (Builtin)
|
||||
- [X] Random Generation (Builtin)
|
||||
- [ ] Graphics (Magnum Graphics)
|
||||
- [ ] Physics Integration (ReactPhysics3D)
|
||||
- [ ] Audio (Builtin + miniaudio)
|
||||
- [ ] Particles (Builtin)
|
||||
- [ ] Video playback (Builtin + FFmpeg(subject to change))
|
||||
- [ ] Asset Archiving (Searching for a library)
|
||||
- [ ] UI (Magnum UI)
|
||||
- [ ] Shaders (Builtin)
|
||||
Tormaline Engine is a game engine created for game development with C++23. For its stack it uses Corrade as its STD replacement (when applicable), Magnum Graphics as its graphics engine, miniaudio as its sound engine, FLECS as its main ECS controller.
|
||||
|
||||
# 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.
|
||||
The engine aims to incorporate mruby to make ruby its optional scripting language. The goal is to make 1 to 1 parity with C++ and Ruby scirpting. So a game for example can use Ruby for its basic player controls or networking, while using C++ for its complex AI logic. A game is not strictly required to use C++ and Ruby. A game can choose how much of either language it needs.
|
||||
|
||||
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
|
||||
```
|
||||
Tourmaline Engine will be ECS only by default. ECS will be strictly imposed on engine level. Tourmaline Engine aside from ECS tries not to force the developer into its (my own) opinionated way of doing things. This is achieved by allowing the programmer to easily build modules to the engine. We expose module developers majority of the low level aspects of the project.
|
||||
|
||||
# 3rd Party Libraries Credits
|
||||
- [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.
|
||||
- [Xohiro](https://github.com/david-cortes/xoshiro_cpp/blob/master/xoshiro.h) implementation by David Blackman and Sebastiano Vigna.
|
||||
|
||||
# Special Thanks
|
||||
- [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.
|
||||
- [Kae "voxelfoxkae"](https://voxelfox.co.uk/) for mentorship.
|
||||
|
||||
# Not-so frequently asked questions
|
||||
### Version Scheming
|
||||
Tourmaline uses a single digit that ticks up by 1 for each version. There are no major or minor updates or versions. The project can be seen as a rolling release project.
|
||||
|
||||
### What is Tourmaline and Why is that the name?
|
||||
From [Wikipedia](https://en.wikipedia.org/wiki/Tourmaline)
|
||||
> Tourmaline is a crystalline silicate mineral group in which boron is compounded with elements such as aluminium, iron, magnesium, sodium, lithium, or potassium.
|
||||
|
||||
From [Find Gemstone](https://www.findgemstone.com/blog/what-is-tourmaline-used-for-in-industry/)
|
||||
> 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.
|
||||
Tourmaline Engine places an emphasis on good documentation, plenty of examples, and tutorials. Additionally Tourmaline Engine targets to have a large standard implementation for most day to day game development needs. Physics, shadows, sounds, video playback, OS agnostic IO interactions, etc...
|
||||
16
external/Corrade/CMakeLists.txt
vendored
16
external/Corrade/CMakeLists.txt
vendored
@@ -1,16 +0,0 @@
|
||||
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)
|
||||
@@ -1,60 +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>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Tourmaline::Concepts {
|
||||
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>;
|
||||
|
||||
// Oh C++ and your jank
|
||||
template <typename Base, typename Type1, typename Type2> struct _opposite_of {
|
||||
using type = std::conditional_t<std::is_same_v<Base, Type1>, Type2, Type1>;
|
||||
};
|
||||
|
||||
template <typename Base, typename Type1, typename Type2>
|
||||
requires Either<Base, Type1, Type2>
|
||||
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
|
||||
#endif
|
||||
@@ -1,28 +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_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
|
||||
@@ -1,350 +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_DUALKEYMAP_H
|
||||
#define GUARD_TOURMALINE_DUALKEYMAP_H
|
||||
#include "../Concepts.hpp"
|
||||
#include "../Systems/Logging.hpp"
|
||||
#include "ContainerOptions.hpp"
|
||||
#include "Hashmap.hpp"
|
||||
|
||||
#include "Corrade/Containers/Array.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <stack>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace Tourmaline::Containers {
|
||||
template <Concepts::Hashable AKey, Concepts::Hashable BKey, typename Value,
|
||||
DualKeyMapOptions Options = {}>
|
||||
class DualkeyMap {
|
||||
public:
|
||||
// Return Types
|
||||
template <typename OppositeKey>
|
||||
requires Concepts::Either<OppositeKey, AKey, BKey>
|
||||
struct MultiQueryResult {
|
||||
// Having to use pointers here over references was not fun
|
||||
// but it was for greater good
|
||||
const OppositeKey *oppositeKey;
|
||||
Corrade::Containers::Array<Value *> valueQueryResults;
|
||||
std::size_t howManyFound = 1;
|
||||
};
|
||||
|
||||
using QueryResult =
|
||||
std::pair<std::variant<std::monostate, std::reference_wrapper<const AKey>,
|
||||
std::reference_wrapper<const BKey>>,
|
||||
Value &>;
|
||||
using Entry = std::tuple<const AKey &, const BKey &, Value &>;
|
||||
|
||||
// Construct/Destruct
|
||||
DualkeyMap() { hashList.reserve(Options.baseReservation); }
|
||||
~DualkeyMap() {
|
||||
// I'm sure there is a better way to do this
|
||||
for (DualkeyHash *hash : hashList) {
|
||||
if (hash != nullptr) [[likely]] {
|
||||
delete hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No copying due to the container expected to be the sole
|
||||
// owner of the data
|
||||
DualkeyMap(const DualkeyMap &) = delete;
|
||||
DualkeyMap &operator=(const DualkeyMap &) = delete;
|
||||
|
||||
// Public controls
|
||||
Entry Insert(AKey firstKey, BKey secondKey, Value value) {
|
||||
DualkeyHash *hash = new DualkeyHash(std::move(firstKey),
|
||||
std::move(secondKey), std::move(value));
|
||||
|
||||
if (graveyard.empty()) {
|
||||
hashList.push_back(hash);
|
||||
} else {
|
||||
hashList[graveyard.top()] = hash;
|
||||
graveyard.pop();
|
||||
}
|
||||
|
||||
return {hash->firstKey, hash->secondKey, hash->value};
|
||||
}
|
||||
|
||||
std::size_t Remove(std::optional<AKey> firstKey,
|
||||
std::optional<BKey> secondKey) {
|
||||
bool isFirstKeyGiven = firstKey.has_value();
|
||||
bool isSecondKeyGiven = secondKey.has_value();
|
||||
|
||||
if (!isFirstKeyGiven && !isSecondKeyGiven) [[unlikely]] {
|
||||
Systems::Logging::Log(
|
||||
"Failed to Delete! DualkeyMap::Delete require at least 1 "
|
||||
"key to be given! Terminating",
|
||||
"Dualkey Map", Systems::Logging::LogLevel::Critical);
|
||||
}
|
||||
|
||||
std::size_t firstKeyHash =
|
||||
isFirstKeyGiven ? std::hash<AKey>{}(firstKey.value()) : 0;
|
||||
std::size_t secondKeyHash =
|
||||
isSecondKeyGiven ? std::hash<BKey>{}(secondKey.value()) : 0;
|
||||
std::size_t index = 0, amountDeleted = 0;
|
||||
uint8_t stateOfIndexing = isFirstKeyGiven + (isSecondKeyGiven << 1);
|
||||
for (DualkeyHash *hash : hashList) {
|
||||
// Tombstone
|
||||
if (hash == nullptr) [[unlikely]] {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (stateOfIndexing) {
|
||||
case 1: // Only first key is given
|
||||
if (firstKeyHash == hash->firstKeyHash &&
|
||||
firstKey.value() == hash->firstKey) {
|
||||
delete hash;
|
||||
hashList[index] = nullptr;
|
||||
graveyard.push(index);
|
||||
++amountDeleted;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // Only second key is given
|
||||
if (secondKeyHash == hash->secondKeyHash &&
|
||||
secondKey.value() == hash->secondKey) {
|
||||
delete hash;
|
||||
hashList[index] = nullptr;
|
||||
graveyard.push(index);
|
||||
++amountDeleted;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // Both given
|
||||
if (firstKeyHash == hash->firstKeyHash &&
|
||||
secondKeyHash == hash->secondKeyHash &&
|
||||
firstKey.value() == hash->firstKey &&
|
||||
secondKey.value() == hash->secondKey) {
|
||||
delete hash;
|
||||
hashList[index] = nullptr;
|
||||
graveyard.push(index);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return amountDeleted;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::size_t Count() {
|
||||
return hashList.size() - graveyard.size();
|
||||
}
|
||||
|
||||
// Queries
|
||||
[[nodiscard("Discarding an expensive query!")]]
|
||||
std::vector<QueryResult> Query(std::optional<AKey> firstKey,
|
||||
std::optional<BKey> secondKey) {
|
||||
bool isFirstKeyGiven = firstKey.has_value();
|
||||
bool isSecondKeyGiven = secondKey.has_value();
|
||||
|
||||
if (!isFirstKeyGiven && !isSecondKeyGiven) [[unlikely]] {
|
||||
Systems::Logging::Log(
|
||||
"Failed to Query! DualkeyMap::Query require at least 1 "
|
||||
"key to be given! Terminating",
|
||||
"Dualkey Map", Systems::Logging::LogLevel::Critical);
|
||||
}
|
||||
std::size_t firstKeyHash =
|
||||
isFirstKeyGiven ? std::hash<AKey>{}(firstKey.value()) : 0;
|
||||
std::size_t secondKeyHash =
|
||||
isSecondKeyGiven ? std::hash<BKey>{}(secondKey.value()) : 0;
|
||||
|
||||
std::vector<QueryResult> finishedQuery{};
|
||||
|
||||
uint8_t stateOfIndexing = isFirstKeyGiven + (isSecondKeyGiven << 1);
|
||||
// Putting hash checks first to benefit from short circuits
|
||||
for (DualkeyHash *hash : hashList) {
|
||||
// Tombstone
|
||||
if (hash == nullptr) [[unlikely]] {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (stateOfIndexing) {
|
||||
case 1: // Only first key is given
|
||||
if (firstKeyHash == hash->firstKeyHash &&
|
||||
firstKey.value() == hash->firstKey) {
|
||||
finishedQuery.emplace_back(std::cref(hash->secondKey), hash->value);
|
||||
}
|
||||
continue;
|
||||
case 2: // Only second key is given
|
||||
if (secondKeyHash == hash->secondKeyHash &&
|
||||
secondKey.value() == hash->secondKey) {
|
||||
finishedQuery.emplace_back(std::cref(hash->firstKey), hash->value);
|
||||
}
|
||||
continue;
|
||||
case 3: // Both are given
|
||||
if (firstKeyHash == hash->firstKeyHash &&
|
||||
secondKeyHash == hash->secondKeyHash &&
|
||||
firstKey.value() == hash->firstKey &&
|
||||
secondKey.value() == hash->secondKey) {
|
||||
finishedQuery.emplace_back(std::monostate{}, hash->value);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return finishedQuery;
|
||||
}
|
||||
|
||||
template <typename Key,
|
||||
typename OppositeKey = Concepts::OppositeOf<Key, AKey, BKey>>
|
||||
requires Concepts::Either<Key, AKey, BKey>
|
||||
[[nodiscard("Discarding a very expensive query!")]]
|
||||
std::vector<MultiQueryResult<OppositeKey>>
|
||||
QueryWithAll(const Corrade::Containers::Array<Key> &keys) {
|
||||
std::vector<MultiQueryResult<OppositeKey>> queryResult =
|
||||
queryWithMany<Key>(keys);
|
||||
|
||||
std::erase_if(queryResult,
|
||||
[keyCount = keys.size()](
|
||||
const MultiQueryResult<OppositeKey> &queryRecord) {
|
||||
return queryRecord.howManyFound != keyCount;
|
||||
});
|
||||
return queryResult;
|
||||
}
|
||||
|
||||
void Scan(std::function<bool(const std::size_t firstKeyHash,
|
||||
const std::size_t secondKeyHash, Value &value)>
|
||||
scanFunction) {
|
||||
for (DualkeyHash *hash : hashList) {
|
||||
if (hash == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (scanFunction(hash->firstKeyHash, hash->secondKeyHash, hash->value)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scan(std::function<bool(const AKey &firstKey, const BKey &secondKey,
|
||||
Value &value)>
|
||||
scanFunction) {
|
||||
for (DualkeyHash *hash : hashList) {
|
||||
if (hash == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (scanFunction(hash->firstKey, hash->secondKey, hash->value)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Interal data structures
|
||||
struct DualkeyHash {
|
||||
DualkeyHash(AKey &&firstKey, BKey &&secondKey, Value &&value)
|
||||
: firstKey(std::move(firstKey)), secondKey(std::move(secondKey)),
|
||||
firstKeyHash(std::hash<AKey>{}(this->firstKey)),
|
||||
secondKeyHash(std::hash<BKey>{}(this->secondKey)),
|
||||
value(std::move(value)) {}
|
||||
|
||||
const AKey firstKey;
|
||||
const BKey secondKey;
|
||||
const std::size_t firstKeyHash;
|
||||
const std::size_t secondKeyHash;
|
||||
mutable Value value;
|
||||
};
|
||||
|
||||
// Actual data
|
||||
std::vector<DualkeyHash *> hashList;
|
||||
std::stack<std::size_t> graveyard;
|
||||
|
||||
// Interal querying
|
||||
template <typename Key,
|
||||
typename OppositeKey = Concepts::OppositeOf<Key, AKey, BKey>>
|
||||
inline std::vector<MultiQueryResult<OppositeKey>>
|
||||
queryWithMany(const Corrade::Containers::Array<Key> &keys) {
|
||||
constexpr bool searchingInFirstKey = std::is_same_v<Key, AKey>;
|
||||
std::size_t keyCount = keys.size();
|
||||
|
||||
// I really can't wait for C++26 contracts
|
||||
if (keyCount == 0) {
|
||||
Systems::Logging::Log("Failed to Query! QueryWithAll require at least 2 "
|
||||
"key to be given, zero was given! Terminating",
|
||||
"Dualkey Map",
|
||||
Systems::Logging::LogLevel::Critical);
|
||||
}
|
||||
|
||||
// Hoping this never ever gets triggered :sigh:
|
||||
if (keyCount == 1) {
|
||||
Systems::Logging::Log("QueryWithAll should not be used for single key "
|
||||
"entry! Please use Query for this instead.",
|
||||
"Dualkey Map", Systems::Logging::LogLevel::Error);
|
||||
}
|
||||
|
||||
// While we don't necessary need the hashes,
|
||||
// it just helps us tremendously benefit from short circuit checks
|
||||
Corrade::Containers::Array<std::size_t> keyHashes{keyCount};
|
||||
for (uint64_t index = 0; index < keyCount; index++) {
|
||||
keyHashes[index] = std::hash<Key>{}(keys[index]);
|
||||
}
|
||||
|
||||
uint64_t hashToCompare;
|
||||
Key *keyToCompare;
|
||||
OppositeKey *oppositeKey;
|
||||
|
||||
Containers::Hashmap<OppositeKey, MultiQueryResult<OppositeKey>,
|
||||
{8.0f, 0.01f, 2.5f, 2048, 8}> // Aggressive hashmap :o
|
||||
queryResults;
|
||||
|
||||
for (DualkeyHash *hash : hashList) {
|
||||
// Tombstone
|
||||
if (hash == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The hell of doing 2 conditions with similar logics in
|
||||
// the same logical block
|
||||
if constexpr (searchingInFirstKey) {
|
||||
hashToCompare = hash->firstKeyHash;
|
||||
keyToCompare = const_cast<AKey *>(&hash->firstKey);
|
||||
oppositeKey = const_cast<BKey *>(&hash->secondKey);
|
||||
} else {
|
||||
hashToCompare = hash->secondKeyHash;
|
||||
keyToCompare = const_cast<BKey *>(&hash->secondKey);
|
||||
oppositeKey = const_cast<AKey *>(&hash->firstKey);
|
||||
}
|
||||
|
||||
// The code above was done to make this code more uniform
|
||||
for (uint64_t index = 0; index < keyCount; index++) {
|
||||
if (keyHashes[index] == hashToCompare && keys[index] == *keyToCompare) {
|
||||
if (queryResults.Has(*oppositeKey)) [[likely]] {
|
||||
auto &entry = queryResults.Get(*oppositeKey);
|
||||
entry.valueQueryResults[index] = &hash->value;
|
||||
++entry.howManyFound;
|
||||
break;
|
||||
}
|
||||
|
||||
queryResults
|
||||
.Insert(
|
||||
*oppositeKey,
|
||||
{oppositeKey, Corrade::Containers::Array<Value *>{keyCount}})
|
||||
.valueQueryResults[index] = &hash->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return queryResults.ExtractValuesToArray();
|
||||
}
|
||||
};
|
||||
} // namespace Tourmaline::Containers
|
||||
#endif
|
||||
@@ -1,192 +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_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
|
||||
@@ -1,86 +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_ECS_H
|
||||
#define GUARD_TOURMALINE_ECS_H
|
||||
#include <any>
|
||||
#include <typeindex>
|
||||
|
||||
#include "../Containers/DualkeyMap.hpp"
|
||||
#include "../Containers/Hashmap.hpp"
|
||||
#include "../Types.hpp"
|
||||
#include "ECS/BuiltinComponents.hpp"
|
||||
#include "Logging.hpp"
|
||||
|
||||
namespace Tourmaline::Systems::ECS {
|
||||
using Entity = Tourmaline::Type::UUID;
|
||||
using System = Tourmaline::Type::UUID;
|
||||
|
||||
class World {
|
||||
public:
|
||||
World() {}
|
||||
// ====== World controls ======
|
||||
void Step();
|
||||
|
||||
// ======== Entities ========
|
||||
[[nodiscard]]
|
||||
Entity CreateEntity();
|
||||
[[nodiscard("Pointless call of EntityExists")]]
|
||||
bool EntityExists(const Entity &entity) noexcept;
|
||||
bool DestroyEntity(Entity entity);
|
||||
|
||||
// ======== Components ========
|
||||
template <isAComponent Component, typename... ComponentArgs>
|
||||
Component &AddComponent(const Entity &entity, ComponentArgs &&...args) {
|
||||
auto newComponent = entityComponentMap.Insert(entity, typeid(Component),
|
||||
Component(args...));
|
||||
|
||||
return std::any_cast<Component &>(std::get<2>(newComponent));
|
||||
}
|
||||
|
||||
template <isAComponent Component>
|
||||
[[nodiscard("Pointless call of GetComponent")]]
|
||||
Component &GetComponent(const Entity &entity) {
|
||||
auto result = entityComponentMap.Query(entity, typeid(Component));
|
||||
if (result.empty()) {
|
||||
Logging::LogFormatted("Entity {} does not have component {}!",
|
||||
"ECS/GetComponent", Logging::LogLevel::Error,
|
||||
entity.asString(), typeid(Component).name());
|
||||
}
|
||||
return std::any_cast<Component &>(result.begin()->second);
|
||||
}
|
||||
|
||||
template <isAComponent Component>
|
||||
[[nodiscard("Pointless call of HasComponent")]]
|
||||
bool HasComponent(const Entity &entity) {
|
||||
return entityComponentMap.Query(entity, typeid(Component)).size();
|
||||
}
|
||||
|
||||
template <isAComponent Component> bool RemoveComponent(const Entity &entity) {
|
||||
return entityComponentMap.Remove(entity, typeid(Component));
|
||||
}
|
||||
|
||||
// Copying is not allowed since the ECS world is meant to be
|
||||
// a session with its own private session sensitive variables
|
||||
World(const World &) = delete;
|
||||
World &operator=(const World &) = delete;
|
||||
|
||||
private:
|
||||
using systemFunction =
|
||||
std::function<void(const Entity &, std::span<std::any *>)>;
|
||||
Containers::DualkeyMap<Entity, std::type_index, std::any>
|
||||
entityComponentMap{};
|
||||
Containers::Hashmap<System, systemFunction> registeredSystems{};
|
||||
|
||||
// ======== Life-cycle ========
|
||||
void preSystems();
|
||||
void postSystems();
|
||||
};
|
||||
} // namespace Tourmaline::Systems::ECS
|
||||
#endif
|
||||
@@ -1,30 +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_BUILTIN_COMPONENTS_H
|
||||
#define GUARD_TOURMALINE_BUILTIN_COMPONENTS_H
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace Tourmaline::Systems::ECS {
|
||||
struct Component {
|
||||
public:
|
||||
virtual ~Component() = default;
|
||||
};
|
||||
template <typename T>
|
||||
concept isAComponent = std::derived_from<T, ECS::Component>;
|
||||
} // namespace Tourmaline::Systems::ECS
|
||||
|
||||
namespace Tourmaline::Systems::Components {
|
||||
// Builtin
|
||||
struct Base : public ECS::Component {
|
||||
double x = 0, y = 0, z = 0;
|
||||
};
|
||||
} // namespace Tourmaline::Systems::Components
|
||||
#endif
|
||||
@@ -1,43 +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_LOGGING_H
|
||||
#define GUARD_TOURMALINE_LOGGING_H
|
||||
|
||||
#include "Corrade/Containers/String.h"
|
||||
#include "Corrade/Containers/StringView.h"
|
||||
#include "Corrade/Utility/Format.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace Tourmaline::Systems {
|
||||
class Logging {
|
||||
public:
|
||||
enum LogLevel { Critical, Error, Warning, Info, Debug, Trace };
|
||||
|
||||
static void LogToFile(Corrade::Containers::String File = "");
|
||||
static void Log(Corrade::Containers::StringView message,
|
||||
Corrade::Containers::StringView position = "Unknown",
|
||||
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:
|
||||
static std::fstream File;
|
||||
static const char *LogLevelToColour[Trace + 1];
|
||||
static const char *LogLevelToString[Trace + 1];
|
||||
};
|
||||
} // namespace Tourmaline::Systems
|
||||
#endif
|
||||
@@ -1,47 +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_TYPES_H
|
||||
#define GUARD_TOURMALINE_TYPES_H
|
||||
|
||||
#include "Corrade/Containers/String.h"
|
||||
#include "TourmalineExternal/random/xoshiro.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace Tourmaline::Type {
|
||||
class UUID {
|
||||
public:
|
||||
[[nodiscard]]
|
||||
Corrade::Containers::String asString() const;
|
||||
bool operator==(const UUID &rhs) const;
|
||||
|
||||
UUID(uint64_t firstHalf, uint64_t secondHalf);
|
||||
UUID(const std::string &uuid);
|
||||
|
||||
private:
|
||||
uint64_t firstHalf = 0, secondHalf = 0;
|
||||
friend struct std::hash<Tourmaline::Type::UUID>;
|
||||
};
|
||||
} // namespace Tourmaline::Type
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<Tourmaline::Type::UUID> {
|
||||
size_t operator()(const Tourmaline::Type::UUID &uuid) const noexcept {
|
||||
uint64_t hash = Xoshiro::splitmix64(uuid.firstHalf);
|
||||
hash += uuid.secondHalf;
|
||||
return Xoshiro::splitmix64(hash);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
@@ -73,7 +73,7 @@ static inline void jump_state(const int_t jump_table[4], rng_t &rng)
|
||||
int_t s3 = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int b = 0; b < static_cast<int>(8*sizeof(int_t)); b++) // static_cast has been added by Tourmaline Engine!
|
||||
for (int b = 0; b < 8*sizeof(int_t); b++)
|
||||
{
|
||||
if (jump_table[i] & ((int_t)1) << b)
|
||||
{
|
||||
@@ -1,13 +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/.
|
||||
*/
|
||||
|
||||
#include "Systems/ECS.hpp"
|
||||
#include "Systems/ECS/BuiltinComponents.hpp"
|
||||
|
||||
// Empty until future use
|
||||
@@ -1,58 +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/.
|
||||
*/
|
||||
|
||||
#include "Systems/ECS.hpp"
|
||||
#include "Systems/ECS/BuiltinComponents.hpp"
|
||||
#include "Systems/Random.hpp"
|
||||
|
||||
using namespace Tourmaline::Systems;
|
||||
using namespace ECS;
|
||||
|
||||
void World::Step() {
|
||||
preSystems();
|
||||
// Actual systems will happen here
|
||||
postSystems();
|
||||
}
|
||||
|
||||
void World::preSystems() {
|
||||
// Defined for future use
|
||||
}
|
||||
|
||||
void World::postSystems() {
|
||||
// Defined for future use
|
||||
}
|
||||
|
||||
// Entities
|
||||
Entity World::CreateEntity() {
|
||||
auto newEntity = Random::GenerateUUID();
|
||||
|
||||
// Default components
|
||||
entityComponentMap.Insert(newEntity, typeid(Components::Base),
|
||||
Components::Base());
|
||||
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
bool World::EntityExists(const Entity &entity) noexcept {
|
||||
bool exists = false;
|
||||
entityComponentMap.Scan(
|
||||
[&exists, entity](const Tourmaline::Type::UUID ¤tEntity,
|
||||
const std::type_index &, std::any &) -> bool {
|
||||
if (currentEntity == entity) {
|
||||
exists = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return exists;
|
||||
}
|
||||
|
||||
bool World::DestroyEntity(Entity entity) {
|
||||
return entityComponentMap.Remove(entity, std::nullopt);
|
||||
}
|
||||
@@ -1,83 +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/.
|
||||
*/
|
||||
|
||||
#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 <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <print>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
|
||||
using namespace Tourmaline::Systems;
|
||||
using namespace Corrade::Containers;
|
||||
using namespace Corrade::Utility;
|
||||
|
||||
// This is what happens when it takes you 50 years to implement
|
||||
// reflections to a language
|
||||
const char *Logging::LogLevelToColour[Logging::Trace + 1]{
|
||||
"[0;31m", "[0;91m", "[0;33m", "[0;37m", "[0;92m", "[0;36m"};
|
||||
const char *Logging::LogLevelToString[Logging::Trace + 1]{
|
||||
"Critical", "Error", "Warning", "Info", "Debug", "Trace"};
|
||||
std::fstream Logging::File;
|
||||
|
||||
void Logging::LogToFile(String File) {
|
||||
if (File == "") {
|
||||
const auto now = std::chrono::system_clock::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.data(), std::fstream::out);
|
||||
|
||||
if (Logging::File.fail()) {
|
||||
String error =
|
||||
format("FAILED! Could not open or create the file: {}! Error: {}", File,
|
||||
strerror(errno));
|
||||
throw std::runtime_error(error.data());
|
||||
}
|
||||
}
|
||||
|
||||
void Logging::Log(StringView message, StringView position,
|
||||
Logging::LogLevel severity, bool assertion) {
|
||||
if (assertion) {
|
||||
static String output{Corrade::ValueInit,
|
||||
4096}; // This is done to stop allocations
|
||||
std::size_t formattedSize = formatInto(
|
||||
output, "[{}@{}] {}\n", LogLevelToString[severity], position, message);
|
||||
|
||||
std::print(
|
||||
"\033{} {}\033[0m", LogLevelToColour[severity],
|
||||
std::string_view{output.begin(), output.begin() + formattedSize});
|
||||
if (Logging::File.is_open()) {
|
||||
Logging::File.write(output.data(), formattedSize);
|
||||
Logging::File.flush(); // Terrible but necessary sadly
|
||||
}
|
||||
|
||||
if (severity == Logging::LogLevel::Error) {
|
||||
throw std::runtime_error(output.data());
|
||||
}
|
||||
|
||||
if (severity == Logging::LogLevel::Critical) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +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/.
|
||||
*/
|
||||
|
||||
#include "Corrade/Utility/Format.h"
|
||||
#include "Types.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
using namespace Tourmaline::Type;
|
||||
using namespace Corrade::Containers;
|
||||
using namespace Corrade::Utility;
|
||||
String UUID::asString() const {
|
||||
return format("{:.16X}{:.16X}", firstHalf, secondHalf);
|
||||
}
|
||||
|
||||
bool UUID::operator==(const UUID &rhs) const {
|
||||
// Since size may be increased
|
||||
return firstHalf == rhs.firstHalf && secondHalf == rhs.secondHalf;
|
||||
}
|
||||
|
||||
UUID::UUID(uint64_t firstHalf, uint64_t secondHalf)
|
||||
: firstHalf(firstHalf), secondHalf(secondHalf) {}
|
||||
|
||||
UUID::UUID(const std::string &uuid) {
|
||||
// We are assuming that it is a valid UUID, if not then somewhere else this
|
||||
// UUID should cause an error
|
||||
auto start = uuid.c_str(), half = start + 16,
|
||||
tail = half + 16; // Each UUID element is 16 characters padded
|
||||
|
||||
std::from_chars(start, half, firstHalf, 16);
|
||||
std::from_chars(half, tail, secondHalf, 16);
|
||||
}
|
||||
@@ -6,12 +6,11 @@
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
#include "Systems/Random.hpp"
|
||||
|
||||
#include "Random.hpp"
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <format>
|
||||
|
||||
using namespace Tourmaline::Systems;
|
||||
|
||||
@@ -24,5 +23,5 @@ Tourmaline::Type::UUID Random::GenerateUUID() {
|
||||
random_ab = std::rotl(hold, 12);
|
||||
random_c = ((random_c >> 2) << 2) + 2;
|
||||
|
||||
return Tourmaline::Type::UUID(random_ab, random_c);
|
||||
return std::format("{:X}{:X}", random_ab, random_c);
|
||||
}
|
||||
@@ -9,9 +9,8 @@
|
||||
|
||||
#ifndef GUARD_TOURMALINE_RANDOM_H
|
||||
#define GUARD_TOURMALINE_RANDOM_H
|
||||
#include "../../libraries/random/xoshiro.h"
|
||||
#include "../Types.hpp"
|
||||
#include "TourmalineExternal/random/xoshiro.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace Tourmaline::Systems {
|
||||
26
sourceCode/Types.hpp
Normal file
26
sourceCode/Types.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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_TYPES_H
|
||||
#define GUARD_TOURMALINE_TYPES_H
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
namespace Tourmaline::Type {
|
||||
struct UUID {
|
||||
[[nodiscard]]
|
||||
std::string asString() const;
|
||||
|
||||
UUID(uint64_t firstHalf, uint64_t secondHalf);
|
||||
UUID(const std::string &uuid);
|
||||
UUID(UUID &&uuid) noexcept;
|
||||
|
||||
private:
|
||||
std::unique_ptr<uint64_t[]> data = std::make_unique<uint64_t[]>(2);
|
||||
};
|
||||
} // namespace Tourmaline::Type
|
||||
#endif
|
||||
32
sourceCode/Types/UUID.cpp
Normal file
32
sourceCode/Types/UUID.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
#include "Types.hpp"
|
||||
#include <algorithm>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
using namespace Tourmaline::Type;
|
||||
std::string UUID::asString() const {
|
||||
return std::format("{:016X}{:016X}", data[0], data[1]);
|
||||
}
|
||||
|
||||
UUID::UUID(UUID &&uuid) noexcept { data.swap(uuid.data); }
|
||||
|
||||
UUID::UUID(uint64_t firstHalf, uint64_t secondHalf) {
|
||||
data[0] = firstHalf;
|
||||
data[1] = secondHalf;
|
||||
}
|
||||
|
||||
UUID::UUID(const std::string &uuid) {
|
||||
// We are assuming that it is a valid UUID, if not then somewhere else this
|
||||
// UUID should cause an error
|
||||
std::copy(uuid.begin(), uuid.begin() + 32, data.get());
|
||||
}
|
||||
Reference in New Issue
Block a user