mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-25 06:57:28 +00:00
315 lines
10 KiB
C++
315 lines
10 KiB
C++
#pragma once
|
|
|
|
// C++
|
|
#include <charconv>
|
|
#include <cstdint>
|
|
#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"
|
|
|
|
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 {
|
|
//! 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 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>{};
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
#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<NiPoint3> 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<NiPoint3>(x.value(), y.value(), z.value()) : std::nullopt;
|
|
}
|
|
|
|
/**
|
|
* 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<NiPoint3> TryParse(const std::span<const std::string> str) {
|
|
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
/**
|
|
* 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());
|
|
}
|
|
}
|