Skip to content

Commit

Permalink
refactor: Change TryParse implementation (DarkflameUniverse#1442)
Browse files Browse the repository at this point in the history
* Changed how the TryParse function works (and also did some general cleanup along the way)

* Update noexcept attributes (verified these are correct)

* Add fp overload for MacOS functionality

* resolving some feedback

* Split out unrelated changes to CleanupRoundup branch

* Update in response to feedback

* the consequences of emo's member variable renaming request

* Revert "the consequences of emo's member variable renaming request"

This reverts commit bf318ca.

* Fully revert renaming attempt

* Revert "the consequences of emo's member variable renaming request"

This reverts commit bf318ca.

Fully revert renaming attempt

* Created ClientVersion.h and moved the client version defaults to it

* Fix partial parsing and MacOS floating point errors

* attempting fix to MacOS compiler error

* syntax pass (should be the last commit unless the CI fails)

* ah, wait, forgot to uncomment the preprocessor statements for MacOS. THIS should be the last commit pending CI

* Okay, one last thing I noticed: We were including C headers here. Now they're C++ headers. Pinky swear this is it!

* typo and I am OCD. please let this be the last

* hash is usally but not always noexcept, so the specifier should go

* Address MOST of the feedback

* address the claim codes issue
  • Loading branch information
jadebenn authored Feb 10, 2024
1 parent 62b670d commit 0c1ee05
Show file tree
Hide file tree
Showing 33 changed files with 451 additions and 441 deletions.
8 changes: 4 additions & 4 deletions dAuthServer/AuthServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ int main(int argc, char** argv) {
Game::randomEngine = std::mt19937(time(0));

//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
uint32_t maxClients = 999;
uint32_t ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default.
std::string ourIP = "localhost";
GeneralUtils::TryParse(Game::config->GetValue("max_clients"), maxClients);
GeneralUtils::TryParse(Game::config->GetValue("auth_server_port"), ourPort);
const uint32_t maxClients = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_clients")).value_or(999);

//LU client is hardcoded to use this for auth port, so I'm making it the default.
const uint32_t ourPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("auth_server_port")).value_or(1001);
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;

Expand Down
9 changes: 3 additions & 6 deletions dChatServer/ChatServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,15 @@ int main(int argc, char** argv) {
masterPort = masterInfo->port;
}
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
uint32_t maxClients = 999;
uint32_t ourPort = 1501;
std::string ourIP = "localhost";
GeneralUtils::TryParse(Game::config->GetValue("max_clients"), maxClients);
GeneralUtils::TryParse(Game::config->GetValue("chat_server_port"), ourPort);
const uint32_t maxClients = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_clients")).value_or(999);
const uint32_t ourPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("chat_server_port")).value_or(1501);
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;

Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);

bool dontGenerateDCF = false;
GeneralUtils::TryParse(Game::config->GetValue("dont_generate_dcf"), dontGenerateDCF);
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);

Game::randomEngine = std::mt19937(time(0));
Expand Down
6 changes: 4 additions & 2 deletions dChatServer/PlayerContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
#include "dConfig.h"

void PlayerContainer::Initialize() {
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_best_friends"), m_MaxNumberOfBestFriends);
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_friends"), m_MaxNumberOfFriends);
m_MaxNumberOfBestFriends =
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_best_friends")).value_or(m_MaxNumberOfBestFriends);
m_MaxNumberOfFriends =
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_friends")).value_or(m_MaxNumberOfFriends);
}

PlayerContainer::~PlayerContainer() {
Expand Down
4 changes: 0 additions & 4 deletions dCommon/GeneralUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,3 @@ std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::stri

return sortedFiles;
}

bool GeneralUtils::TryParse(const std::string& x, const std::string& y, const std::string& z, NiPoint3& dst) {
return TryParse<float>(x.c_str(), dst.x) && TryParse<float>(y.c_str(), dst.y) && TryParse<float>(z.c_str(), dst.z);
}
160 changes: 98 additions & 62 deletions dCommon/GeneralUtils.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
#pragma once

// C++
#include <stdint.h>
#include <charconv>
#include <cstdint>
#include <random>
#include <time.h>
#include <ctime>
#include <string>
#include <type_traits>
#include <string_view>
#include <optional>
#include <functional>
#include <type_traits>
#include <stdexcept>
#include "BitStream.h"
#include "NiPoint3.h"

#include "dPlatforms.h"
#include "Game.h"
#include "Logger.h"

Expand Down Expand Up @@ -123,90 +126,125 @@ namespace GeneralUtils {

std::vector<std::string> GetSqlFileNamesFromFolder(const std::string& folder);

// Concept constraining to enum types
template <typename T>
T Parse(const char* value);
concept Enum = std::is_enum_v<T>;

template <>
inline bool Parse(const char* value) {
return std::stoi(value);
}
// Concept constraining to numeric types
template <typename T>
concept Numeric = std::integral<T> || Enum<T> || std::floating_point<T>;

template <>
inline int32_t Parse(const char* value) {
return std::stoi(value);
}
// Concept trickery to enable parsing underlying numeric types
template <Numeric T>
struct numeric_parse { using type = T; };

template <>
inline int64_t Parse(const char* value) {
return std::stoll(value);
}
// 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>; };

template <>
inline float Parse(const char* value) {
return std::stof(value);
}
// 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 = uint32_t; };

template <>
inline double Parse(const char* value) {
return std::stod(value);
}
// Shorthand type alias
template <Numeric T>
using numeric_parse_t = numeric_parse<T>::type;

template <>
inline uint16_t Parse(const char* value) {
return std::stoul(value);
}
/**
* 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(const std::string_view str) {
numeric_parse_t<T> result;

template <>
inline uint32_t Parse(const char* value) {
return std::stoul(value);
}
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{};

template <>
inline uint64_t Parse(const char* value) {
return std::stoull(value);
return isParsed ? static_cast<T>(result) : std::optional<T>{};
}

template <>
inline eInventoryType Parse(const char* value) {
return static_cast<eInventoryType>(std::stoul(value));
}
#ifdef DARKFLAME_PLATFORM_MACOS

template <>
inline eReplicaComponentType Parse(const char* value) {
return static_cast<eReplicaComponentType>(std::stoul(value));
}
// Anonymous namespace containing MacOS floating-point parse function specializations
namespace {
template <std::floating_point T>
[[nodiscard]] T Parse(const std::string_view str, size_t* parseNum);

template <typename T>
bool TryParse(const char* value, T& dst) {
try {
dst = Parse<T>(value);
template <>
[[nodiscard]] float Parse<float>(const std::string_view str, size_t* parseNum) {
return std::stof(std::string{ str }, parseNum);
}

return true;
} catch (...) {
return false;
template <>
[[nodiscard]] double Parse<double>(const std::string_view str, size_t* parseNum) {
return std::stod(std::string{ str }, parseNum);
}

template <>
[[nodiscard]] long double Parse<long double>(const std::string_view str, size_t* parseNum) {
return std::stold(std::string{ str }, parseNum);
}
}

template <typename T>
T Parse(const std::string& value) {
return Parse<T>(value.c_str());
/**
* 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(const std::string_view str) noexcept try {
size_t parseNum;
const T result = 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>
bool TryParse(const std::string& value, T& dst) {
return TryParse<T>(value.c_str(), dst);
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string& strX, const std::string& strY, const std::string& 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;
}

bool TryParse(const std::string& x, const std::string& y, const std::string& z, NiPoint3& dst);
/**
* The TryParse overload for handling NiPoint3 by passingn a reference to a vector of three strings
* @param str The string vector representing the X, Y, and Xcoordinates
* @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::vector<std::string>& str) {
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
}

template<typename T>
template <typename T>
std::u16string to_u16string(T value) {
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
}

// From boost::hash_combine
template <class T>
void hash_combine(std::size_t& s, const T& v) {
constexpr void hash_combine(std::size_t& s, const T& v) {
std::hash<T> h;
s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
}
Expand Down Expand Up @@ -239,10 +277,8 @@ namespace GeneralUtils {
* @param entry Enum entry to cast
* @returns The enum entry's value in its underlying type
*/
template <typename eType>
inline constexpr typename std::underlying_type_t<eType> CastUnderlyingType(const eType entry) {
static_assert(std::is_enum_v<eType>, "Not an enum");

template <Enum eType>
constexpr typename std::underlying_type_t<eType> CastUnderlyingType(const eType entry) noexcept {
return static_cast<typename std::underlying_type_t<eType>>(entry);
}

Expand Down
38 changes: 21 additions & 17 deletions dCommon/LDFFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,33 +61,33 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
}

case LDF_TYPE_S32: {
int32_t data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
const auto data = GeneralUtils::TryParse<int32_t>(ldfTypeAndValue.second);
if (!data) {
LOG("Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<int32_t>(key, data);
returnValue = new LDFData<int32_t>(key, data.value());

break;
}

case LDF_TYPE_FLOAT: {
float data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
const auto data = GeneralUtils::TryParse<float>(ldfTypeAndValue.second);
if (!data) {
LOG("Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<float>(key, data);
returnValue = new LDFData<float>(key, data.value());
break;
}

case LDF_TYPE_DOUBLE: {
double data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
const auto data = GeneralUtils::TryParse<double>(ldfTypeAndValue.second);
if (!data) {
LOG("Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<double>(key, data);
returnValue = new LDFData<double>(key, data.value());
break;
}

Expand All @@ -100,10 +100,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") {
data = 0;
} else {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
const auto dataOptional = GeneralUtils::TryParse<uint32_t>(ldfTypeAndValue.second);
if (!dataOptional) {
LOG("Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
data = dataOptional.value();
}

returnValue = new LDFData<uint32_t>(key, data);
Expand All @@ -118,33 +120,35 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") {
data = false;
} else {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
const auto dataOptional = GeneralUtils::TryParse<bool>(ldfTypeAndValue.second);
if (!dataOptional) {
LOG("Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
data = dataOptional.value();
}

returnValue = new LDFData<bool>(key, data);
break;
}

case LDF_TYPE_U64: {
uint64_t data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
const auto data = GeneralUtils::TryParse<uint64_t>(ldfTypeAndValue.second);
if (!data) {
LOG("Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<uint64_t>(key, data);
returnValue = new LDFData<uint64_t>(key, data.value());
break;
}

case LDF_TYPE_OBJID: {
LWOOBJID data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
const auto data = GeneralUtils::TryParse<LWOOBJID>(ldfTypeAndValue.second);
if (!data) {
LOG("Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<LWOOBJID>(key, data);
returnValue = new LDFData<LWOOBJID>(key, data.value());
break;
}

Expand Down
2 changes: 1 addition & 1 deletion dCommon/dClient/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
set(DCOMMON_DCLIENT_SOURCES
"AssetManager.cpp"
"PackIndex.cpp"
"Pack.cpp"
"AssetManager.cpp"
PARENT_SCOPE
)
12 changes: 12 additions & 0 deletions dCommon/dClient/ClientVersion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef __CLIENTVERSION_H__
#define __CLIENTVERSION_H__

#include <cstdint>

namespace ClientVersion {
constexpr uint16_t major = 1;
constexpr uint16_t current = 10;
constexpr uint16_t minor = 64;
}

#endif // !__CLIENTVERSION_H__
Loading

0 comments on commit 0c1ee05

Please sign in to comment.