Compare commits
18 Commits
3024539ef2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| dda6e25bf9 | |||
| 518995ddd1 | |||
| e8498f9e66 | |||
| 88f5d02b6a | |||
| 31ebef777b | |||
| cb9005bf80 | |||
| b457ecce47 | |||
| e64b8ed194 | |||
| c1efee5b4e | |||
| c36c3468bb | |||
| 6d9edc18b4 | |||
| 8174c19f00 | |||
| 89ebe63e4b | |||
| 3b99a9d913 | |||
| e968585f5b | |||
| 6b5213d605 | |||
| 7470f01dc4 | |||
| 198b095c0e |
16
.clangd
Normal file
16
.clangd
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"CompileFlags": {
|
||||
"Add": [
|
||||
"-std=c++23",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wpedantic",
|
||||
]
|
||||
},
|
||||
"Index": {
|
||||
"Background": "Build"
|
||||
},
|
||||
"Completion": {
|
||||
"AllScopes": true
|
||||
}
|
||||
}
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -162,3 +162,6 @@ build-iPhoneSimulator/
|
||||
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
||||
# .rubocop-https?--*
|
||||
|
||||
# Custom
|
||||
# For personal testing cases
|
||||
test/
|
||||
|
||||
39
CMakeLists.txt
Normal file
39
CMakeLists.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
# 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)
|
||||
|
||||
project(Tourmaline VERSION 1)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address")
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
include_directories(headers)
|
||||
add_library(${PROJECT_NAME} SHARED
|
||||
"source/Systems/ECS/Component.cpp"
|
||||
"source/Systems/ECS/World.cpp"
|
||||
"source/Systems/Logging.cpp"
|
||||
"source/Systems/Random.cpp"
|
||||
"source/Types/UUID.cpp")
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION})
|
||||
|
||||
# Nothing to link right now
|
||||
target_link_libraries(${PROJECT_NAME})
|
||||
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${PROJECT_NAME}Targets
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
RUNTIME DESTINATION bin)
|
||||
install(DIRECTORY headers/ DESTINATION include/${PROJECT_NAME})
|
||||
29
README.md
29
README.md
@@ -1,9 +1,30 @@
|
||||
# Tourmaline Engine
|
||||
|
||||
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.
|
||||
Tormaline Engine is a game engine created for game development with C++23.
|
||||
|
||||
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.
|
||||
A lot of the info regarding Tourmaline has been removed from here. This is due to project still being in early phases of development.
|
||||
|
||||
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 Used 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
|
||||
|
||||
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...
|
||||
# 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.
|
||||
|
||||
## 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)" )
|
||||
|
||||
146
headers/Systems/ECS.hpp
Normal file
146
headers/Systems/ECS.hpp
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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 <concepts>
|
||||
#include <format>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "../Types.hpp"
|
||||
#include "Logging.hpp"
|
||||
|
||||
namespace Tourmaline::Systems::ECS {
|
||||
using Entity = Tourmaline::Type::UUID;
|
||||
class World;
|
||||
|
||||
struct BaseComponent {
|
||||
public:
|
||||
virtual ~BaseComponent() = default;
|
||||
const Entity &GetOwner();
|
||||
|
||||
private:
|
||||
const Entity *owner;
|
||||
friend World;
|
||||
};
|
||||
|
||||
// Concepts
|
||||
template <typename T>
|
||||
concept Component = std::derived_from<T, BaseComponent>;
|
||||
|
||||
class World {
|
||||
public:
|
||||
// Entity
|
||||
Entity CreateEntity();
|
||||
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);
|
||||
|
||||
// Components
|
||||
template <Component T, typename... Args>
|
||||
T &AddComponent(const Entity &entity, Args &&...constructionArguments) {
|
||||
auto entityIter = GetEntityIterator(
|
||||
entity,
|
||||
std::format(
|
||||
"Cannot add component \"{}\"! Entity \"{}\" does not exist!",
|
||||
typeid(T).name(), entity.asString()),
|
||||
"AddComponent", Systems::Logging::LogLevel::Error);
|
||||
|
||||
auto [componentIter, success] = entityIter->second.try_emplace(
|
||||
typeid(T), T(std::forward<Args>(constructionArguments)...));
|
||||
Systems::Logging::Log(
|
||||
std::format("Cannot add component! Component \"{}\" already exists "
|
||||
"in entity \"{}\" ",
|
||||
typeid(T).name(), entity.asString()),
|
||||
"AddComponent", Systems::Logging::LogLevel::Error, !success);
|
||||
|
||||
T &component = std::any_cast<T &>(componentIter->second);
|
||||
component.owner = &entity;
|
||||
return component;
|
||||
}
|
||||
|
||||
template <Component T>
|
||||
[[nodiscard("Discarding an expensive operation's result!")]]
|
||||
T &GetComponent(const Entity &entity) {
|
||||
auto iter = GetEntityIterator(
|
||||
entity,
|
||||
std::format("Can't get entity \"{}\"'s component \"{}\", since "
|
||||
"entity does not exist!",
|
||||
entity.asString(), typeid(T).name()),
|
||||
"GetComponent", Systems::Logging::LogLevel::Error);
|
||||
|
||||
auto component = iter->second.find(typeid(T));
|
||||
Systems::Logging::Log(
|
||||
std::format(
|
||||
"Entity \"{}\" does not have component \"{}\", cannot get it!",
|
||||
entity.asString(), typeid(T).name()),
|
||||
"GetComponent", Systems::Logging::LogLevel::Error,
|
||||
component == iter->second.end());
|
||||
|
||||
return std::any_cast<T &>(component->second);
|
||||
}
|
||||
|
||||
template <Component T>
|
||||
[[nodiscard("Discarding an expensive operation's result!")]]
|
||||
bool HasComponent(const Entity &entity) {
|
||||
auto iter = GetEntityIterator(
|
||||
entity,
|
||||
std::format("Can't find if entity \"{}\" has component \"{}\", since "
|
||||
"entity does not exist!",
|
||||
entity.asString(), typeid(T).name()));
|
||||
|
||||
return iter != entityComponentList.end() &&
|
||||
(iter->second.find(typeid(T)) != iter->second.end());
|
||||
}
|
||||
|
||||
template <Component T>
|
||||
[[nodiscard("It is not guaranteed that a component can always be removed, "
|
||||
"please make "
|
||||
"sure by checking the returned bool")]]
|
||||
bool RemoveComponent(const Entity &entity) {
|
||||
auto entityIter = GetEntityIterator(
|
||||
entity,
|
||||
std::format("Cannot remove component {} from entity {}, since entity "
|
||||
"does not exist!",
|
||||
typeid(T).name(), entity.asString()),
|
||||
"RemoveComponent", Systems::Logging::LogLevel::Warning);
|
||||
if (entityIter == entityComponentList.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto componentIter = entityIter->second.find(typeid(T));
|
||||
if (componentIter == entityIter->second.end()) {
|
||||
Systems::Logging::Log(
|
||||
std::format("Cannot remove component {} from entity {}, since entity "
|
||||
"does not have that component",
|
||||
typeid(T).name(), entity.asString()),
|
||||
"RemoveComponent", Systems::Logging::LogLevel::Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
entityIter->second.erase(componentIter);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<Entity, std::unordered_map<std::type_index, std::any>>
|
||||
entityComponentList{};
|
||||
|
||||
decltype(entityComponentList)::iterator
|
||||
GetEntityIterator(const Entity &entity, const std::string &errorMessage = "",
|
||||
const std::string &position = "",
|
||||
Tourmaline::Systems::Logging::LogLevel severity =
|
||||
Systems::Logging::LogLevel::Warning);
|
||||
};
|
||||
} // namespace Tourmaline::Systems::ECS
|
||||
#endif
|
||||
38
headers/Systems/Logging.hpp
Normal file
38
headers/Systems/Logging.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 <array>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
namespace Tourmaline::Systems {
|
||||
class Logging {
|
||||
public:
|
||||
enum class LogLevel {
|
||||
Critical = 0,
|
||||
Error = 1,
|
||||
Warning = 2,
|
||||
Info = 3,
|
||||
Debug = 4,
|
||||
Trace = 5
|
||||
};
|
||||
|
||||
static void LogToFile(std::string File = "");
|
||||
static void Log(const std::string &message,
|
||||
const std::string &position = "Unknown",
|
||||
LogLevel severity = LogLevel::Info, bool assertion = true);
|
||||
|
||||
private:
|
||||
static std::fstream File;
|
||||
static std::array<std::pair<const std::string, const std::string>, 6>
|
||||
LogLevelToString;
|
||||
};
|
||||
} // namespace Tourmaline::Systems
|
||||
#endif
|
||||
30
headers/Systems/Random.hpp
Normal file
30
headers/Systems/Random.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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_RANDOM_H
|
||||
#define GUARD_TOURMALINE_RANDOM_H
|
||||
#include "../../libraries/random/xoshiro.h"
|
||||
#include "../Types.hpp"
|
||||
#include <type_traits>
|
||||
|
||||
namespace Tourmaline::Systems {
|
||||
class Random {
|
||||
public:
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
static T Generate(T max, T min = 0) {
|
||||
return (generator() % (max - min + 1)) + min;
|
||||
}
|
||||
static Tourmaline::Type::UUID GenerateUUID();
|
||||
|
||||
private:
|
||||
static Xoshiro::Xoshiro256PP generator;
|
||||
};
|
||||
} // namespace Tourmaline::Systems
|
||||
#endif
|
||||
52
headers/Types.hpp
Normal file
52
headers/Types.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 <functional>
|
||||
#include <memory>
|
||||
namespace Tourmaline::Type {
|
||||
class UUID {
|
||||
public:
|
||||
constexpr static uint8_t BitLength = 128;
|
||||
constexpr static uint8_t QWORDLength = BitLength / 64;
|
||||
constexpr static uint8_t ByteLength = BitLength / 8;
|
||||
|
||||
[[nodiscard]]
|
||||
std::string asString() const;
|
||||
bool operator==(const UUID &rhs) const;
|
||||
|
||||
UUID(uint64_t firstHalf, uint64_t secondHalf);
|
||||
UUID(const std::string &uuid);
|
||||
|
||||
UUID(const UUID &uuid);
|
||||
UUID(UUID &&uuid) noexcept;
|
||||
UUID &operator=(const UUID &uuid);
|
||||
UUID &operator=(UUID &&uuid);
|
||||
~UUID() = default;
|
||||
|
||||
private:
|
||||
std::unique_ptr<uint64_t[]> data = std::make_unique<uint64_t[]>(QWORDLength);
|
||||
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 {
|
||||
const auto data = uuid.data.get();
|
||||
size_t h1 = std::hash<uint64_t>{}(data[0]),
|
||||
h2 = std::hash<uint64_t>{}(data[1]);
|
||||
return h1 ^ (h2 << 1);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
428
libraries/random/xoshiro.h
Normal file
428
libraries/random/xoshiro.h
Normal file
@@ -0,0 +1,428 @@
|
||||
/* Code was taken from here:
|
||||
https://prng.di.unimi.it
|
||||
And wrapped as a bit generator for C++'s random module' */
|
||||
|
||||
/* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org)
|
||||
|
||||
To the extent possible under law, the author has dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
See <http://creativecommons.org/publicdomain/zero/1.0/>. */
|
||||
#ifndef XOSHIRO_H
|
||||
#define XOSHIRO_H
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Xoshiro {
|
||||
|
||||
|
||||
#if __cplusplus >= 202001L
|
||||
# define HAS_CPP20
|
||||
#endif
|
||||
|
||||
static inline std::uint64_t rotl64(const std::uint64_t x, const int k) {
|
||||
return (x << k) | (x >> (64 - k));
|
||||
}
|
||||
|
||||
static inline std::uint32_t rotl32(const std::uint32_t x, const int k) {
|
||||
return (x << k) | (x >> (32 - k));
|
||||
}
|
||||
|
||||
/* these are in order to avoid gcc warnings about 'strict aliasing rules' */
|
||||
static inline std::uint32_t extract_32bits_from64_left(const std::uint64_t x)
|
||||
{
|
||||
std::uint32_t out;
|
||||
std::memcpy(&out, reinterpret_cast<const std::uint32_t*>(&x), sizeof(std::uint32_t));
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline std::uint32_t extract_32bits_from64_right(const std::uint64_t x)
|
||||
{
|
||||
std::uint32_t out;
|
||||
std::memcpy(&out, reinterpret_cast<const std::uint32_t*>(&x) + 1, sizeof(std::uint32_t));
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline void assign_32bits_to64_left(std::uint64_t assign_to, const std::uint32_t take_from)
|
||||
{
|
||||
std::memcpy(reinterpret_cast<std::uint32_t*>(&assign_to), &take_from, sizeof(std::uint32_t));
|
||||
}
|
||||
|
||||
static inline void assign_32bits_to64_right(std::uint64_t assign_to, const std::uint32_t take_from)
|
||||
{
|
||||
std::memcpy(reinterpret_cast<std::uint32_t*>(&assign_to) + 1, &take_from, sizeof(std::uint32_t));
|
||||
}
|
||||
|
||||
constexpr static const uint64_t JUMP_X256PP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c };
|
||||
|
||||
constexpr static const uint64_t LONG_JUMP_X256PP[] = { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 };
|
||||
|
||||
constexpr static const uint32_t JUMP_X128PP[] = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b };
|
||||
|
||||
constexpr static const uint32_t LONG_JUMP_X128PP[] = { 0xb523952e, 0x0b6f099f, 0xccf5a0ef, 0x1c580662 };
|
||||
|
||||
template <class int_t, class rng_t>
|
||||
static inline void jump_state(const int_t jump_table[4], rng_t &rng)
|
||||
{
|
||||
int_t s0 = 0;
|
||||
int_t s1 = 0;
|
||||
int_t s2 = 0;
|
||||
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!
|
||||
{
|
||||
if (jump_table[i] & ((int_t)1) << b)
|
||||
{
|
||||
s0 ^= rng.state[0];
|
||||
s1 ^= rng.state[1];
|
||||
s2 ^= rng.state[2];
|
||||
s3 ^= rng.state[3];
|
||||
}
|
||||
rng();
|
||||
}
|
||||
}
|
||||
|
||||
rng.state[0] = s0;
|
||||
rng.state[1] = s1;
|
||||
rng.state[2] = s2;
|
||||
rng.state[3] = s3;
|
||||
}
|
||||
|
||||
/* This is a fixed-increment version of Java 8's SplittableRandom generator
|
||||
See http://dx.doi.org/10.1145/2714064.2660195 and
|
||||
http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html
|
||||
|
||||
It is a very fast generator passing BigCrush, and it can be useful if
|
||||
for some reason you absolutely want 64 bits of state. */
|
||||
static inline std::uint64_t splitmix64(const std::uint64_t seed)
|
||||
{
|
||||
std::uint64_t z = (seed + 0x9e3779b97f4a7c15);
|
||||
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
|
||||
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
|
||||
return z ^ (z >> 31);
|
||||
}
|
||||
|
||||
/* This is xoshiro256++ 1.0, one of our all-purpose, rock-solid generators.
|
||||
It has excellent (sub-ns) speed, a state (256 bits) that is large
|
||||
enough for any parallel application, and it passes all tests we are
|
||||
aware of.
|
||||
|
||||
For generating just floating-point numbers, xoshiro256+ is even faster.
|
||||
|
||||
The state must be seeded so that it is not everywhere zero. If you have
|
||||
a 64-bit seed, we suggest to seed a splitmix64 generator and use its
|
||||
output to fill s. */
|
||||
class Xoshiro256PP
|
||||
{
|
||||
public:
|
||||
using result_type = std::uint64_t;
|
||||
std::uint64_t state[4] = {0x3d23dce41c588f8c, 0x10c770bb8da027b0, 0xc7a4c5e87c63ba25, 0xa830f83239465a2e};
|
||||
|
||||
constexpr static result_type min()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr static result_type max()
|
||||
{
|
||||
return UINT64_MAX;
|
||||
}
|
||||
|
||||
Xoshiro256PP() = default;
|
||||
|
||||
~Xoshiro256PP() noexcept = default;
|
||||
|
||||
Xoshiro256PP(Xoshiro256PP &other) = default;
|
||||
|
||||
Xoshiro256PP& operator=(const Xoshiro256PP &other) = default;
|
||||
|
||||
Xoshiro256PP(Xoshiro256PP &&) noexcept = default;
|
||||
|
||||
Xoshiro256PP& operator=(Xoshiro256PP &&) noexcept = default;
|
||||
|
||||
void seed(const std::uint64_t seed)
|
||||
{
|
||||
this->state[0] = splitmix64(splitmix64(seed));
|
||||
this->state[1] = splitmix64(this->state[0]);
|
||||
this->state[2] = splitmix64(this->state[1]);
|
||||
this->state[3] = splitmix64(this->state[2]);
|
||||
}
|
||||
|
||||
void seed(const std::uint64_t seed[4])
|
||||
{
|
||||
std::memcpy(this->state, seed, 4*sizeof(std::uint64_t));
|
||||
}
|
||||
|
||||
template<class Sseq>
|
||||
void seed(Sseq& seq)
|
||||
{
|
||||
seq.generate(reinterpret_cast<std::uint32_t*>(&this->state[0]),
|
||||
reinterpret_cast<std::uint32_t*>(&this->state[0] + 4));
|
||||
}
|
||||
|
||||
Xoshiro256PP(const std::uint64_t seed)
|
||||
{
|
||||
this->seed(seed);
|
||||
}
|
||||
|
||||
Xoshiro256PP(const std::uint64_t seed[4])
|
||||
{
|
||||
this->seed(seed);
|
||||
}
|
||||
|
||||
template<class Sseq>
|
||||
Xoshiro256PP(Sseq& seq)
|
||||
{
|
||||
this->seed(seq);
|
||||
}
|
||||
|
||||
result_type operator()()
|
||||
{
|
||||
const std::uint64_t result = rotl64(this->state[0] + this->state[3], 23) + this->state[0];
|
||||
const std::uint64_t t = this->state[1] << 17;
|
||||
this->state[2] ^= this->state[0];
|
||||
this->state[3] ^= this->state[1];
|
||||
this->state[1] ^= this->state[2];
|
||||
this->state[0] ^= this->state[3];
|
||||
this->state[2] ^= t;
|
||||
this->state[3] = rotl64(this->state[3], 45);
|
||||
return result;
|
||||
}
|
||||
|
||||
void discard(unsigned long long z)
|
||||
{
|
||||
for (unsigned long long ix = 0; ix < z; ix++)
|
||||
this->operator()();
|
||||
}
|
||||
|
||||
Xoshiro256PP jump()
|
||||
{
|
||||
Xoshiro256PP new_gen = *this;
|
||||
jump_state(JUMP_X256PP, new_gen);
|
||||
return new_gen;
|
||||
}
|
||||
|
||||
Xoshiro256PP long_jump()
|
||||
{
|
||||
Xoshiro256PP new_gen = *this;
|
||||
jump_state(LONG_JUMP_X256PP, new_gen);
|
||||
return new_gen;
|
||||
}
|
||||
|
||||
bool operator==(const Xoshiro256PP &rhs)
|
||||
{
|
||||
return std::memcmp(this->state, rhs.state, 4*sizeof(std::uint64_t));
|
||||
}
|
||||
|
||||
#ifndef HAS_CPP20
|
||||
bool operator!=(const Xoshiro256PP &rhs)
|
||||
{
|
||||
return !std::memcmp(this->state, rhs.state, 4*sizeof(std::uint64_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
template< class CharT, class Traits >
|
||||
friend std::basic_ostream<CharT,Traits>&
|
||||
operator<<(std::basic_ostream<CharT,Traits>& ost, const Xoshiro256PP& e)
|
||||
{
|
||||
ost.write(reinterpret_cast<const char*>(&e.state[0]), sizeof(std::uint64_t));
|
||||
ost.put(' ');
|
||||
ost.write(reinterpret_cast<const char*>(&e.state[1]), sizeof(std::uint64_t));
|
||||
ost.put(' ');
|
||||
ost.write(reinterpret_cast<const char*>(&e.state[2]), sizeof(std::uint64_t));
|
||||
ost.put(' ');
|
||||
ost.write(reinterpret_cast<const char*>(&e.state[3]), sizeof(std::uint64_t));
|
||||
return ost;
|
||||
}
|
||||
template< class CharT, class Traits >
|
||||
friend std::basic_istream<CharT,Traits>&
|
||||
operator>>(std::basic_istream<CharT,Traits>& ist, Xoshiro256PP& e)
|
||||
{
|
||||
ist.read(reinterpret_cast<char*>(&e.state[0]), sizeof(std::uint64_t));
|
||||
ist.get();
|
||||
ist.read(reinterpret_cast<char*>(&e.state[1]), sizeof(std::uint64_t));
|
||||
ist.get();
|
||||
ist.read(reinterpret_cast<char*>(&e.state[2]), sizeof(std::uint64_t));
|
||||
ist.get();
|
||||
ist.read(reinterpret_cast<char*>(&e.state[3]), sizeof(std::uint64_t));
|
||||
return ist;
|
||||
}
|
||||
};
|
||||
|
||||
/* This is xoshiro128++ 1.0, one of our 32-bit all-purpose, rock-solid
|
||||
generators. It has excellent speed, a state size (128 bits) that is
|
||||
large enough for mild parallelism, and it passes all tests we are aware
|
||||
of.
|
||||
|
||||
For generating just single-precision (i.e., 32-bit) floating-point
|
||||
numbers, xoshiro128+ is even faster.
|
||||
|
||||
The state must be seeded so that it is not everywhere zero. */
|
||||
class Xoshiro128PP
|
||||
{
|
||||
public:
|
||||
using result_type = std::uint32_t;
|
||||
std::uint32_t state[4] = {0x1c588f8c, 0x3d23dce4, 0x8da027b0, 0x10c770bb};
|
||||
|
||||
constexpr static result_type min()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr static result_type max()
|
||||
{
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
Xoshiro128PP() = default;
|
||||
|
||||
~Xoshiro128PP() noexcept = default;
|
||||
|
||||
Xoshiro128PP(Xoshiro128PP &other) = default;
|
||||
|
||||
Xoshiro128PP& operator=(const Xoshiro128PP &other) = default;
|
||||
|
||||
Xoshiro128PP(Xoshiro128PP &&) noexcept = default;
|
||||
|
||||
Xoshiro128PP& operator=(Xoshiro128PP &&) noexcept = default;
|
||||
|
||||
void seed(const std::uint64_t seed)
|
||||
{
|
||||
const auto t1 = splitmix64(seed);
|
||||
const auto t2 = splitmix64(t1);
|
||||
this->state[0] = splitmix64(extract_32bits_from64_left(t1));
|
||||
this->state[1] = splitmix64(extract_32bits_from64_right(t1));
|
||||
this->state[2] = splitmix64(extract_32bits_from64_left(t2));
|
||||
this->state[3] = splitmix64(extract_32bits_from64_right(t2));
|
||||
}
|
||||
|
||||
void seed(const std::uint32_t seed)
|
||||
{
|
||||
std::uint64_t temp;
|
||||
assign_32bits_to64_left(temp, seed);
|
||||
assign_32bits_to64_right(temp, seed);
|
||||
this->seed(temp);
|
||||
}
|
||||
|
||||
void seed(const std::uint64_t seed[2])
|
||||
{
|
||||
std::memcpy(this->state, seed, 4*sizeof(std::uint32_t));
|
||||
}
|
||||
|
||||
void seed(const std::uint32_t seed[4])
|
||||
{
|
||||
std::memcpy(this->state, seed, 4*sizeof(std::uint32_t));
|
||||
}
|
||||
|
||||
template<class Sseq>
|
||||
void seed(Sseq& seq)
|
||||
{
|
||||
seq.generate(&this->state[0], &this->state[0] + 4);
|
||||
}
|
||||
|
||||
Xoshiro128PP(const std::uint32_t seed)
|
||||
{
|
||||
this->seed(seed);
|
||||
}
|
||||
|
||||
Xoshiro128PP(const std::uint64_t seed)
|
||||
{
|
||||
this->seed(seed);
|
||||
}
|
||||
|
||||
Xoshiro128PP(const std::uint32_t seed[4])
|
||||
{
|
||||
this->seed(seed);
|
||||
}
|
||||
|
||||
Xoshiro128PP(const std::uint64_t seed[2])
|
||||
{
|
||||
this->seed(seed);
|
||||
}
|
||||
|
||||
template<class Sseq>
|
||||
Xoshiro128PP(Sseq& seq)
|
||||
{
|
||||
this->seed(seq);
|
||||
}
|
||||
|
||||
result_type operator()()
|
||||
{
|
||||
const std::uint32_t result = rotl32(this->state[0] + this->state[3], 7) + this->state[0];
|
||||
const std::uint32_t t = this->state[1] << 9;
|
||||
this->state[2] ^= this->state[0];
|
||||
this->state[3] ^= this->state[1];
|
||||
this->state[1] ^= this->state[2];
|
||||
this->state[0] ^= this->state[3];
|
||||
this->state[2] ^= t;
|
||||
this->state[3] = rotl32(this->state[3], 11);
|
||||
return result;
|
||||
}
|
||||
|
||||
Xoshiro128PP jump()
|
||||
{
|
||||
Xoshiro128PP new_gen = *this;
|
||||
jump_state(JUMP_X128PP, new_gen);
|
||||
return new_gen;
|
||||
}
|
||||
|
||||
Xoshiro128PP long_jump()
|
||||
{
|
||||
Xoshiro128PP new_gen = *this;
|
||||
jump_state(LONG_JUMP_X128PP, new_gen);
|
||||
return new_gen;
|
||||
}
|
||||
|
||||
void discard(unsigned long long z)
|
||||
{
|
||||
for (unsigned long long ix = 0; ix < z; ix++)
|
||||
this->operator()();
|
||||
}
|
||||
|
||||
bool operator==(const Xoshiro128PP &rhs)
|
||||
{
|
||||
return std::memcmp(this->state, rhs.state, 4*sizeof(std::uint32_t));
|
||||
}
|
||||
|
||||
#ifndef HAS_CPP20
|
||||
bool operator!=(const Xoshiro128PP &rhs)
|
||||
{
|
||||
return !std::memcmp(this->state, rhs.state, 4*sizeof(std::uint32_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
template< class CharT, class Traits >
|
||||
friend std::basic_ostream<CharT,Traits>&
|
||||
operator<<(std::basic_ostream<CharT,Traits>& ost, const Xoshiro128PP& e)
|
||||
{
|
||||
ost.write(reinterpret_cast<const char*>(&e.state[0]), sizeof(std::uint32_t));
|
||||
ost.put(' ');
|
||||
ost.write(reinterpret_cast<const char*>(&e.state[1]), sizeof(std::uint32_t));
|
||||
ost.put(' ');
|
||||
ost.write(reinterpret_cast<const char*>(&e.state[2]), sizeof(std::uint32_t));
|
||||
ost.put(' ');
|
||||
ost.write(reinterpret_cast<const char*>(&e.state[3]), sizeof(std::uint32_t));
|
||||
return ost;
|
||||
}
|
||||
template< class CharT, class Traits >
|
||||
friend std::basic_istream<CharT,Traits>&
|
||||
operator>>(std::basic_istream<CharT,Traits>& ist, Xoshiro128PP& e)
|
||||
{
|
||||
ist.read(reinterpret_cast<char*>(&e.state[0]), sizeof(std::uint32_t));
|
||||
ist.get();
|
||||
ist.read(reinterpret_cast<char*>(&e.state[1]), sizeof(std::uint32_t));
|
||||
ist.get();
|
||||
ist.read(reinterpret_cast<char*>(&e.state[2]), sizeof(std::uint32_t));
|
||||
ist.get();
|
||||
ist.read(reinterpret_cast<char*>(&e.state[3]), sizeof(std::uint32_t));
|
||||
return ist;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
14
source/Systems/ECS/Component.cpp
Normal file
14
source/Systems/ECS/Component.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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 "../../../headers/Systems/ECS.hpp"
|
||||
|
||||
using namespace Tourmaline::Systems::ECS;
|
||||
|
||||
const Entity &BaseComponent::GetOwner() { return *this->owner; }
|
||||
55
source/Systems/ECS/World.cpp
Normal file
55
source/Systems/ECS/World.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 "../../../headers/Systems/ECS.hpp"
|
||||
#include "../../../headers/Systems/Random.hpp"
|
||||
|
||||
using namespace Tourmaline::Systems::ECS;
|
||||
|
||||
Entity World::CreateEntity() {
|
||||
auto [iterator, success] =
|
||||
entityComponentList.try_emplace(Systems::Random::GenerateUUID());
|
||||
|
||||
Systems::Logging::Log("Failed to create an entity! Possibly by incredible "
|
||||
"luck generated already existing UUID?",
|
||||
"CreateEntity", Systems::Logging::LogLevel::Critical,
|
||||
!success);
|
||||
return iterator->first;
|
||||
}
|
||||
|
||||
bool World::EntityExists(const Entity &entity) noexcept {
|
||||
return entityComponentList.find(entity) != entityComponentList.end();
|
||||
}
|
||||
|
||||
bool World::DestroyEntity(Entity entity) {
|
||||
auto entityIter = GetEntityIterator(
|
||||
entity,
|
||||
std::format("Cannot delete entity \"{}\", it does not exist!",
|
||||
entity.asString()),
|
||||
"DestroyEntity");
|
||||
|
||||
if (entityIter == entityComponentList.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entityComponentList.erase(entityIter);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Very repetitive code
|
||||
decltype(World::entityComponentList)::iterator
|
||||
World::GetEntityIterator(const Entity &entity, const std::string &errorMessage,
|
||||
const std::string &position,
|
||||
Tourmaline::Systems::Logging::LogLevel severity) {
|
||||
auto iter = entityComponentList.find(entity);
|
||||
|
||||
Systems::Logging::Log(errorMessage, "GetEntityIterator/" + position, severity,
|
||||
iter == entityComponentList.end());
|
||||
return iter;
|
||||
}
|
||||
68
source/Systems/Logging.cpp
Normal file
68
source/Systems/Logging.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 "../../headers/Systems/Logging.hpp"
|
||||
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <print>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
using namespace Tourmaline::Systems;
|
||||
|
||||
// This is what happens when it takes you 50 years to implement
|
||||
// reflections to a language
|
||||
std::array<std::pair<const std::string, const std::string>, 6>
|
||||
Logging::LogLevelToString{std::pair{"Critical", "[0;31m"},
|
||||
{"Error", "[0;91m"},
|
||||
{"Warning", "[0;33m"},
|
||||
{"Info", "[0;37m"},
|
||||
{"Debug", "[0;92m"},
|
||||
{"Trace", "[0;36m"}};
|
||||
std::fstream Logging::File;
|
||||
|
||||
void Logging::LogToFile(std::string File) {
|
||||
if (File == "") {
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
File = std::format("Tourmaline-{:%Y-%j}.txt", now);
|
||||
}
|
||||
Logging::File.open(File, std::fstream::out);
|
||||
|
||||
if (Logging::File.fail()) {
|
||||
throw std::runtime_error("FAILED! Could not open or create the file: " +
|
||||
File + "!\n" + strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
void Logging::Log(const std::string &message, const std::string &position,
|
||||
Logging::LogLevel severity, bool assertion) {
|
||||
if (assertion) [[likely]] {
|
||||
auto loglevelData =
|
||||
Logging::LogLevelToString[static_cast<size_t>(severity)];
|
||||
std::string output =
|
||||
std::format("[{}@{}] {}\n", loglevelData.first, position, message);
|
||||
|
||||
std::print("\033{} {}\033[0m", loglevelData.second, output);
|
||||
if (Logging::File.is_open()) {
|
||||
Logging::File.write(output.c_str(), output.size());
|
||||
Logging::File.flush(); // Terrible but necessary sadly
|
||||
}
|
||||
|
||||
// Error and Critical
|
||||
if (severity < Logging::LogLevel::Warning) {
|
||||
throw std::runtime_error(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
source/Systems/Random.cpp
Normal file
27
source/Systems/Random.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 "../../headers/Systems/Random.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
|
||||
using namespace Tourmaline::Systems;
|
||||
|
||||
Xoshiro::Xoshiro256PP Random::generator(static_cast<uint64_t>(time(NULL)));
|
||||
Tourmaline::Type::UUID Random::GenerateUUID() {
|
||||
uint64_t random_ab = generator(), random_c = generator(), hold = 0;
|
||||
hold = std::rotr(random_ab, 12);
|
||||
hold = hold >> 4;
|
||||
hold = (hold << 4) + 0b0100;
|
||||
random_ab = std::rotl(hold, 12);
|
||||
random_c = ((random_c >> 2) << 2) + 2;
|
||||
|
||||
return Tourmaline::Type::UUID(random_ab, random_c);
|
||||
}
|
||||
69
source/Types/UUID.cpp
Normal file
69
source/Types/UUID.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 "../../headers/Types.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
using namespace Tourmaline::Type;
|
||||
std::string UUID::asString() const {
|
||||
return std::format("{:016X}{:016X}", data[0], data[1]);
|
||||
}
|
||||
|
||||
bool UUID::operator==(const UUID &rhs) const {
|
||||
// Since size may be increased
|
||||
for (uint8_t index = 0; index < QWORDLength; index++) {
|
||||
if (this->data[index] != rhs.data[index]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
UUID::UUID(const UUID &uuid) {
|
||||
std::memcpy(data.get(), uuid.data.get(), UUID::ByteLength);
|
||||
}
|
||||
|
||||
UUID &UUID::operator=(const UUID &uuid) {
|
||||
if (this != &uuid) [[likely]] {
|
||||
std::memcpy(data.get(), uuid.data.get(), UUID::ByteLength);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
UUID &UUID::operator=(UUID &&uuid) {
|
||||
if (this != &uuid) [[likely]] {
|
||||
data.swap(uuid.data);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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
|
||||
auto start = uuid.c_str(), half = start + ByteLength,
|
||||
tail = half + ByteLength; // Each UUID element is 16 characters padded
|
||||
|
||||
std::from_chars(start, half, data[0], 16);
|
||||
std::from_chars(half, tail, data[1], 16);
|
||||
}
|
||||
Reference in New Issue
Block a user