diff --git a/headers/Systems/ECS.hpp b/headers/Systems/ECS.hpp index bae87ba..1a26f10 100644 --- a/headers/Systems/ECS.hpp +++ b/headers/Systems/ECS.hpp @@ -10,64 +10,60 @@ #ifndef GUARD_TOURMALINE_ECS_H #define GUARD_TOURMALINE_ECS_H #include -#include #include #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; -class World; +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; - [[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 &AddComponent(const Entity &entity, - Args &&...constructionArguments) { - auto newComponent = entityComponentMap.Insert( - entity, typeid(component), component(constructionArguments...)); + template + Component &AddComponent(const Entity &entity, ComponentArgs &&...args) { + auto newComponent = entityComponentMap.Insert(entity, typeid(Component), + Component(args...)); - return std::any_cast(std::get<2>(newComponent)); + return std::any_cast(std::get<2>(newComponent)); } - template - [[nodiscard("Discarding an expensive operation's result!")]] - component &GetComponent(const Entity &entity) { - auto result = entityComponentMap.Query(entity, typeid(component)); + template + [[nodiscard("Pointless call of GetComponent")]] + Component &GetComponent(const Entity &entity) { + auto result = entityComponentMap.Query(entity, typeid(Component)); if (result.empty()) { - Logging::Log(std::format("Entity {} does not have component {}!", - entity.asString(), typeid(component).name()), - "ECS/GetComponent", Logging::LogLevel::Error); + Logging::LogFormatted("Entity {} does not have component {}!", + "ECS/GetComponent", Logging::LogLevel::Error, + entity.asString(), typeid(Component).name()); } - return std::any_cast(result.begin()->second); + return std::any_cast(result.begin()->second); } - template - [[nodiscard("Discarding an expensive operation's result!")]] + template + [[nodiscard("Pointless call of HasComponent")]] bool HasComponent(const Entity &entity) { - return entityComponentMap.Query(entity, typeid(component)).size(); + return entityComponentMap.Query(entity, typeid(Component)).size(); } - template - [[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) { - return entityComponentMap.Remove(entity, typeid(component)); + template bool RemoveComponent(const Entity &entity) { + return entityComponentMap.Remove(entity, typeid(Component)); } // Copying is not allowed since the ECS world is meant to be @@ -76,8 +72,11 @@ public: World &operator=(const World &) = delete; private: - Tourmaline::Containers::DualkeyMap + using systemFunction = + std::function)>; + Containers::DualkeyMap entityComponentMap{}; + Containers::Hashmap registeredSystems{}; // ======== Life-cycle ======== void preSystems(); diff --git a/headers/Systems/ECS/BuiltinComponents.hpp b/headers/Systems/ECS/BuiltinComponents.hpp index 341c448..9585e3c 100644 --- a/headers/Systems/ECS/BuiltinComponents.hpp +++ b/headers/Systems/ECS/BuiltinComponents.hpp @@ -24,7 +24,7 @@ concept isAComponent = std::derived_from; namespace Tourmaline::Systems::Components { // Builtin struct Base : public ECS::Component { - Base() {} + double x = 0, y = 0, z = 0; }; } // namespace Tourmaline::Systems::Components #endif diff --git a/headers/Systems/Logging.hpp b/headers/Systems/Logging.hpp index 18522a7..8a9285f 100644 --- a/headers/Systems/Logging.hpp +++ b/headers/Systems/Logging.hpp @@ -9,33 +9,35 @@ #ifndef GUARD_TOURMALINE_LOGGING_H #define GUARD_TOURMALINE_LOGGING_H -#include "Corrade/Containers/Array.h" +#include "Corrade/Containers/String.h" +#include "Corrade/Containers/StringView.h" +#include "Corrade/Utility/Format.h" #include -#include namespace Tourmaline::Systems { class Logging { public: - enum class LogLevel { - Critical = 0, - Error = 1, - Warning = 2, - Info = 3, - Debug = 4, - Trace = 5 - }; + enum LogLevel { Critical, Error, Warning, Info, Debug, Trace }; - static void LogToFile(std::string File = ""); - static void Log(std::string_view message, - std::string_view position = "Unknown", + 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 + static void LogFormatted(const char *format, + Corrade::Containers::StringView position, + LogLevel severity, const Args &...args) { + Corrade::Containers::String formatted = + Corrade::Utility::format(format, args...); + Log(formatted, position, severity); + } + private: static std::fstream File; - static Corrade::Containers::Array< - std::pair> - LogLevelToString; + static const char *LogLevelToColour[LogLevel::Trace + 1]; + static const char *LogLevelToString[LogLevel::Trace + 1]; }; } // namespace Tourmaline::Systems #endif diff --git a/headers/Types.hpp b/headers/Types.hpp index 7cc87ef..458da84 100644 --- a/headers/Types.hpp +++ b/headers/Types.hpp @@ -10,7 +10,9 @@ #ifndef GUARD_TOURMALINE_TYPES_H #define GUARD_TOURMALINE_TYPES_H +#include "Corrade/Containers/String.h" #include "TourmalineExternal/random/xoshiro.h" + #include #include #include @@ -19,7 +21,7 @@ namespace Tourmaline::Type { class UUID { public: [[nodiscard]] - std::string asString() const; + Corrade::Containers::String asString() const; bool operator==(const UUID &rhs) const; UUID(uint64_t firstHalf, uint64_t secondHalf); diff --git a/source/Systems/Logging.cpp b/source/Systems/Logging.cpp index cf127d0..4215c20 100644 --- a/source/Systems/Logging.cpp +++ b/source/Systems/Logging.cpp @@ -8,9 +8,12 @@ */ #include "Systems/Logging.hpp" -#include "Corrade/Containers/Array.h" -#include +#include "Corrade/Containers/String.h" +#include "Corrade/Containers/StringView.h" +#include "Corrade/Tags.h" +#include "Corrade/Utility/Format.h" + #include #include #include @@ -18,65 +21,63 @@ #include #include #include -#include #include #include -#include #include -#include 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 -Array> - Logging::LogLevelToString{Corrade::InPlaceInit, - {std::pair{"Critical", "[0;31m"}, - {"Error", "[0;91m"}, - {"Warning", "[0;33m"}, - {"Info", "[0;37m"}, - {"Debug", "[0;92m"}, - {"Trace", "[0;36m"}}}; +const char *Logging::LogLevelToColour[Logging::LogLevel::Trace + 1]{ + "[0;31m", "[0;91m", "[0;33m", "[0;37m", "[0;92m", "[0;36m"}; +const char *Logging::LogLevelToString[Logging::LogLevel::Trace + 1]{ + "Critical", "Error", "Warning", "Info", "Debug", "Trace"}; std::fstream Logging::File; -void Logging::LogToFile(std::string File) { +void Logging::LogToFile(String File) { if (File == "") { 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(now)}; + File = String{Corrade::ValueInit, 128}; + formatInto(File, "Tourmaline-{}-{}-{}.txt", static_cast(ymd.year()), + static_cast(ymd.month()), + static_cast(ymd.day())); } - Logging::File.open(File, std::fstream::out); + Logging::File.open(File.data(), std::fstream::out); if (Logging::File.fail()) { - throw std::runtime_error("FAILED! Could not open or create the file: " + - File + "!\n" + strerror(errno)); + String error = + format("FAILED! Could not open or create the file: {}! Error: {}", File, + strerror(errno)); + throw std::runtime_error(error.data()); } } -void Logging::Log(std::string_view message, std::string_view position, +void Logging::Log(StringView message, StringView position, Logging::LogLevel severity, bool assertion) { - if (assertion) [[likely]] { - static std::string - output; // This is done to stop allocations per std::format - const auto &loglevelData = - Logging::LogLevelToString[static_cast(severity)]; - std::format_to(std::back_inserter(output), "[{}@{}] {}\n", - loglevelData.first, position, message); + 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", loglevelData.second, output); + std::print( + "\033{} {}\033[0m", LogLevelToColour[severity], + std::string_view{output.begin(), output.begin() + formattedSize}); 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 } if (severity == Logging::LogLevel::Error) { - throw std::runtime_error(output); + throw std::runtime_error(output.data()); } if (severity == Logging::LogLevel::Critical) { std::terminate(); } - - output.clear(); } } diff --git a/source/Types/UUID.cpp b/source/Types/UUID.cpp index 79c0422..1b5eaf6 100644 --- a/source/Types/UUID.cpp +++ b/source/Types/UUID.cpp @@ -7,17 +7,19 @@ * obtain one at http://mozilla.org/MPL/2.0/. */ +#include "Corrade/Utility/Format.h" #include "Types.hpp" #include #include #include -#include #include using namespace Tourmaline::Type; -std::string UUID::asString() const { - return std::format("{:016X}{:016X}", firstHalf, secondHalf); +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 {