mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
The NexusDashboard-parity dashboard (dDashboardServer) and everything built on it on the experimental branch: accounts, characters, properties and moderation tools, permissions shared with in-game slash commands, economy reports, World 3D and property 3D views with client scenery, scheduled events (features, vanity changes, live events, announcements, restarts), vanity files and events, the CDClient browser, the message inspector with saved captures, chat filter tools, community challenges, live ops, the AI moderator helper, and the server-side changes they need. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
395 lines
13 KiB
C++
395 lines
13 KiB
C++
#pragma once
|
|
|
|
// C++
|
|
#include <charconv>
|
|
#include <cstdint>
|
|
#include <cmath>
|
|
#include <ctime>
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <random>
|
|
#include <span>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
|
|
#include "BitStream.h"
|
|
#include "NiPoint3.h"
|
|
#include "dPlatforms.h"
|
|
#include "Game.h"
|
|
#include "Logger.h"
|
|
#include "DluAssert.h"
|
|
|
|
#include <glm/ext/vector_float3.hpp>
|
|
|
|
enum eInventoryType : uint32_t;
|
|
enum class eObjectBits : size_t;
|
|
enum class eReplicaComponentType : uint32_t;
|
|
|
|
/*!
|
|
\file GeneralUtils.hpp
|
|
\brief A namespace containing general utility functions
|
|
*/
|
|
|
|
//! The general utils namespace
|
|
namespace GeneralUtils {
|
|
// Text for a SQL LIKE pattern with its wildcards (% and _) and the escape character escaped; use with ESCAPE '<escape>'.
|
|
// '!' works the same in SQLite and MySQL (a backslash in a MySQL string literal depends on the sql_mode).
|
|
inline std::string LikeEscape(std::string_view text, char escape = '\\') {
|
|
std::string escaped;
|
|
escaped.reserve(text.size());
|
|
for (const char c : text) {
|
|
if (c == '%' || c == '_' || c == escape) escaped += escape;
|
|
escaped += c;
|
|
}
|
|
return escaped;
|
|
}
|
|
|
|
//! Converts a plain ASCII string to a UTF-16 string
|
|
/*!
|
|
\param string The string to convert
|
|
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
|
\return An UTF-16 representation of the string
|
|
*/
|
|
std::u16string ASCIIToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
|
|
|
|
//! Converts a UTF-8 String to a UTF-16 string
|
|
/*!
|
|
\param string The string to convert
|
|
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
|
\return An UTF-16 representation of the string
|
|
*/
|
|
std::u16string UTF8ToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
|
|
|
|
namespace details {
|
|
//! Internal, do not use
|
|
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
|
|
}
|
|
|
|
//! Converts a Latin1 string to a UTF-8 string
|
|
/*!
|
|
\param string The string to convert
|
|
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
|
\return An UTF-8 representation of the string
|
|
*/
|
|
std::string Latin1ToUTF8(const std::u8string_view string, const size_t size = SIZE_MAX);
|
|
|
|
//! Converts a UTF-16 string to a UTF-8 string
|
|
/*!
|
|
\param string The string to convert
|
|
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
|
\return An UTF-8 representation of the string
|
|
*/
|
|
std::string UTF16ToWTF8(const std::u16string_view string, const size_t size = SIZE_MAX);
|
|
|
|
/**
|
|
* Compares two basic strings but does so ignoring case sensitivity
|
|
* \param a the first string to compare against the second string
|
|
* \param b the second string to compare against the first string
|
|
* @return if the two strings are equal
|
|
*/
|
|
bool CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b);
|
|
|
|
// MARK: Bits
|
|
|
|
// MARK: Bits
|
|
|
|
//! Sets a bit on a numerical value
|
|
template <typename T>
|
|
inline void SetBit(T& value, const eObjectBits bits) {
|
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
|
const auto index = static_cast<size_t>(bits);
|
|
if (index > (sizeof(T) * 8) - 1) {
|
|
return;
|
|
}
|
|
|
|
value |= static_cast<T>(1) << index;
|
|
}
|
|
|
|
//! Clears a bit on a numerical value
|
|
template <typename T>
|
|
inline void ClearBit(T& value, const eObjectBits bits) {
|
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
|
const auto index = static_cast<size_t>(bits);
|
|
if (index > (sizeof(T) * 8 - 1)) {
|
|
return;
|
|
}
|
|
|
|
value &= ~(static_cast<T>(1) << index);
|
|
}
|
|
|
|
//! Sets a specific bit in a signed 64-bit integer
|
|
/*!
|
|
\param value The value to set the bit for
|
|
\param index The index of the bit
|
|
*/
|
|
int64_t SetBit(int64_t value, const uint32_t index);
|
|
|
|
//! Clears a specific bit in a signed 64-bit integer
|
|
/*!
|
|
\param value The value to clear the bit from
|
|
\param index The index of the bit
|
|
*/
|
|
int64_t ClearBit(int64_t value, const uint32_t index);
|
|
|
|
//! Checks a specific bit in a signed 64-bit integer
|
|
/*!
|
|
\parma value The value to check the bit in
|
|
\param index The index of the bit
|
|
\return Whether or not the bit is set
|
|
*/
|
|
bool CheckBit(int64_t value, const uint32_t index);
|
|
|
|
bool ReplaceInString(std::string& str, const std::string_view from, const std::string_view to);
|
|
|
|
std::u16string ReadWString(RakNet::BitStream& inStream);
|
|
|
|
std::vector<std::wstring> SplitString(const std::wstring_view str, const wchar_t delimiter);
|
|
|
|
std::vector<std::u16string> SplitString(const std::u16string_view str, const char16_t delimiter);
|
|
|
|
std::vector<std::string> SplitString(const std::string_view str, const char delimiter);
|
|
|
|
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
|
|
|
|
/**
|
|
* Transparent string hasher - used to allow string_view key lookups for maps storing std::string keys
|
|
* https://www.reddit.com/r/cpp_questions/comments/12xw3sn/find_stdstring_view_in_unordered_map_with/jhki225/
|
|
* https://godbolt.org/z/789xv8Eeq
|
|
*/
|
|
template <typename... Bases>
|
|
struct overload : Bases... {
|
|
using is_transparent = void;
|
|
using Bases::operator() ...;
|
|
};
|
|
|
|
struct char_pointer_hash {
|
|
auto operator()(const char* const ptr) const noexcept {
|
|
return std::hash<std::string_view>{}(ptr);
|
|
}
|
|
};
|
|
|
|
using transparent_string_hash = overload<
|
|
std::hash<std::string>,
|
|
std::hash<std::string_view>,
|
|
char_pointer_hash
|
|
>;
|
|
|
|
// Concept constraining to enum types
|
|
template <typename T>
|
|
concept Enum = std::is_enum_v<T>;
|
|
|
|
// Concept constraining to numeric types
|
|
template <typename T>
|
|
concept Numeric = std::integral<T> || Enum<T> || std::floating_point<T>;
|
|
|
|
// Concept trickery to enable parsing underlying numeric types
|
|
template <Numeric T>
|
|
struct numeric_parse { using type = T; };
|
|
|
|
// If an enum, present an alias to its underlying type for parsing
|
|
template <Numeric T> requires Enum<T>
|
|
struct numeric_parse<T> { using type = std::underlying_type_t<T>; };
|
|
|
|
// If a boolean, present an alias to an intermediate integral type for parsing
|
|
template <Numeric T> requires std::same_as<T, bool>
|
|
struct numeric_parse<T> { using type = uint8_t; };
|
|
|
|
// Shorthand type alias
|
|
template <Numeric T>
|
|
using numeric_parse_t = numeric_parse<T>::type;
|
|
|
|
/**
|
|
* For numeric values: Parses a string_view and returns an optional variable depending on the result.
|
|
* @param str The string_view to be evaluated
|
|
* @returns An std::optional containing the desired value if it is equivalent to the string
|
|
*/
|
|
template <Numeric T>
|
|
[[nodiscard]] std::optional<T> TryParse(std::string_view str) {
|
|
numeric_parse_t<T> result;
|
|
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
|
|
|
const char* const strEnd = str.data() + str.size();
|
|
const auto [parseEnd, ec] = std::from_chars(str.data(), strEnd, result);
|
|
const bool isParsed = parseEnd == strEnd && ec == std::errc{};
|
|
|
|
return isParsed ? static_cast<T>(result) : std::optional<T>{};
|
|
}
|
|
|
|
// A version of TryParse that will return `errorVal` if `str` failed to parse.
|
|
template <Numeric T>
|
|
[[nodiscard]] T TryParse(std::string_view str, const T errorVal) {
|
|
return TryParse<T>(str).value_or(errorVal);
|
|
}
|
|
|
|
template<typename T>
|
|
requires(!Numeric<T>)
|
|
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
|
|
|
|
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
|
|
|
// MacOS floating-point parse helper function specializations
|
|
namespace details {
|
|
template <std::floating_point T>
|
|
[[nodiscard]] T _parse(const std::string_view str, size_t& parseNum);
|
|
}
|
|
|
|
/**
|
|
* For floating-point values: Parses a string_view and returns an optional variable depending on the result.
|
|
* Note that this function overload is only included for MacOS, as from_chars will fulfill its purpose otherwise.
|
|
* @param str The string_view to be evaluated
|
|
* @returns An std::optional containing the desired value if it is equivalent to the string
|
|
*/
|
|
template <std::floating_point T>
|
|
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
|
|
try {
|
|
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
|
|
|
size_t parseNum;
|
|
const T result = details::_parse<T>(str, parseNum);
|
|
const bool isParsed = str.length() == parseNum;
|
|
|
|
return isParsed ? result : std::optional<T>{};
|
|
} catch (...) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// A version of TryParse that will return `errorVal` if `str` failed to parse.
|
|
template <std::floating_point T>
|
|
[[nodiscard]] T TryParse(std::string_view str, const T errorVal) {
|
|
return TryParse<T>(str).value_or(errorVal);
|
|
}
|
|
|
|
#endif
|
|
|
|
/**
|
|
* The TryParse overload for handling NiPoint3 by passing 3 seperate string references
|
|
* @param strX The string representing the X coordinate
|
|
* @param strY The string representing the Y coordinate
|
|
* @param strZ The string representing the Z coordinate
|
|
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
|
*/
|
|
template <typename T>
|
|
[[nodiscard]] std::optional<T> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) {
|
|
const auto x = TryParse<float>(strX);
|
|
if (!x) return std::nullopt;
|
|
|
|
const auto y = TryParse<float>(strY);
|
|
if (!y) return std::nullopt;
|
|
|
|
const auto z = TryParse<float>(strZ);
|
|
return z ? std::make_optional<T>(x.value(), y.value(), z.value()) : std::nullopt;
|
|
}
|
|
|
|
// Alternative overload of TryParse with a default value
|
|
[[nodiscard]] inline NiPoint3 TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ, const NiPoint3 errorVal) {
|
|
return TryParse<NiPoint3>(strX, strY, strZ).value_or(errorVal);
|
|
}
|
|
|
|
/**
|
|
* The TryParse overload for handling NiPoint3 by passing a span of three strings
|
|
* @param str The string vector representing the X, Y, and Z coordinates
|
|
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
|
*/
|
|
template <typename T>
|
|
[[nodiscard]] std::optional<T> TryParse(const std::span<const std::string> str) {
|
|
return (str.size() == 3) ? TryParse<T>(str[0], str[1], str[2]) : std::nullopt;
|
|
}
|
|
|
|
// Alternative overload of TryParse with a default value
|
|
[[nodiscard]] inline NiPoint3 TryParse(const std::span<const std::string> str, const NiPoint3 errorVal) {
|
|
return TryParse<NiPoint3>(str).value_or(errorVal);
|
|
}
|
|
|
|
template <typename T>
|
|
std::u16string to_u16string(const T value) {
|
|
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
|
|
}
|
|
|
|
// From boost::hash_combine
|
|
template <class T>
|
|
constexpr void hash_combine(std::size_t& s, const T& v) {
|
|
std::hash<T> h;
|
|
s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
|
|
}
|
|
|
|
// MARK: Random Number Generation
|
|
|
|
//! Generates a random number
|
|
/*!
|
|
\param min The minimum the generate from
|
|
\param max The maximum to generate to
|
|
*/
|
|
template <typename T>
|
|
inline T GenerateRandomNumber(const std::size_t min, const std::size_t max) {
|
|
// Make sure it is a numeric type
|
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
|
|
|
if constexpr (std::is_integral_v<T>) { // constexpr only necessary on first statement
|
|
std::uniform_int_distribution<T> distribution(min, max);
|
|
return distribution(Game::randomEngine);
|
|
} else if (std::is_floating_point_v<T>) {
|
|
std::uniform_real_distribution<T> distribution(min, max);
|
|
return distribution(Game::randomEngine);
|
|
}
|
|
|
|
return T();
|
|
}
|
|
|
|
template<typename Container>
|
|
inline Container::value_type GetRandomElement(const Container& container) {
|
|
DluAssert(!container.empty());
|
|
return container[GenerateRandomNumber<typename Container::size_type>(0, container.size() - 1)];
|
|
}
|
|
|
|
/**
|
|
* Casts the value of an enum entry to its underlying type
|
|
* @param entry Enum entry to cast
|
|
* @returns The enum entry's value in its underlying type
|
|
*/
|
|
template <Enum eType>
|
|
constexpr std::underlying_type_t<eType> ToUnderlying(const eType entry) noexcept {
|
|
return static_cast<std::underlying_type_t<eType>>(entry);
|
|
}
|
|
|
|
// on Windows we need to undef these or else they conflict with our numeric limits calls
|
|
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
|
|
#ifdef _WIN32
|
|
#undef min
|
|
#undef max
|
|
#endif
|
|
|
|
template <typename T>
|
|
inline T GenerateRandomNumber() {
|
|
// Make sure it is a numeric type
|
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
|
|
|
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
|
|
}
|
|
|
|
// https://www.quora.com/How-do-you-round-to-specific-increments-like-0-5-in-C
|
|
// Rounds to the nearest floating point value specified.
|
|
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
|
T RountToNearestEven(const T value, const T modulus) {
|
|
const auto modulo = std::fmod(value, modulus);
|
|
const auto abs_modulo_2 = std::abs(modulo * 2);
|
|
const auto abs_modulus = std::abs(modulus);
|
|
|
|
bool round_away_from_zero = false;
|
|
if (abs_modulo_2 > abs_modulus) {
|
|
round_away_from_zero = true;
|
|
} else if (abs_modulo_2 == abs_modulus) {
|
|
const auto trunc_quot = std::floor(std::abs(value / modulus));
|
|
const auto odd = std::fmod(trunc_quot, T{ 2 }) != 0;
|
|
round_away_from_zero = odd;
|
|
}
|
|
|
|
if (round_away_from_zero) {
|
|
return value + (std::copysign(modulus, value) - modulo);
|
|
} else {
|
|
return value - modulo;
|
|
}
|
|
}
|
|
}
|