feat: Improve console output to show packet enum names (magic_enum) (#1344)

* add enum stringification functionality from third party source

* squashed commit

* Macros: Add test and improve speed

Space macros out
utilize cache locality
ensure no lost functionality

* moved stringify code to dCommon

* Rename #defines in stringify enum tests

* Revert "moved stringify code to dCommon"

This reverts commit 33fa5f8d2f.

* improve macro functionality

change function handle

formatting and function definition tweaks

* typo fixes

* moved code to dCommon/dEnums and tests to dCommonTests/dEnumsTests

* initial magic_enums alternate implementation of enum stringification

* deleted unused tests

* reverted compile flag oopsy and fixed output types

* fixed testing suite

* test formatting improvement

* formatting again :(

* added gm string to "aborting gm!" message

* Push my suggestion for CI tests.

* updated magic enum test

* fix test variable type

* added gm test

* making sure magic_enum is on a release branch

* tidying up console outputs

* re-implemented enum array access for performance

* now it is bugged :(

* nvm, working

* helping out the snowflake compilers

* changed return type too

* optimization too

* formatting too I guess because why not

* being even more painfully specific

* Update WorldServer.cpp to match emo's feedback

* Update MagicEnumTests.cpp to use srand(time(NULL))

* Update eGameMessageType.h - formatting

* Trying to fix the crash but can't actually compile the code to check on my own rn

* Update WorldServer.cpp - third try at this

* Update MagicEnumTests.cpp - use better macro definitions

* Update MagicEnumTests.cpp - c string comparison fix

* addressing all but the cmake feedback

* fixed cmake to the best of my very limited ability

* added tests to verify magic enum arrays are pre-sorted

* updated

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
Co-authored-by: Jettford <mrjettbradford@gmail.com>
This commit is contained in:
jadebenn 2023-12-23 10:51:59 -06:00 committed by GitHub
parent c1e8546d48
commit fcf4d6c6fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1808 additions and 1598 deletions

3
.gitmodules vendored
View File

@ -17,3 +17,6 @@
[submodule "thirdparty/AccountManager"]
path = thirdparty/AccountManager
url = https://github.com/DarkflameUniverse/AccountManager
[submodule "thirdparty/magic_enum"]
path = thirdparty/magic_enum
url = https://github.com/Neargye/magic_enum.git

View File

@ -293,6 +293,7 @@ set(INCLUDED_DIRECTORIES
"dScripts/zone/PROPERTY/GF"
"dScripts/zone/PROPERTY/NS"
"thirdparty/magic_enum/include/magic_enum"
"thirdparty/raknet/Source"
"thirdparty/tinyxml2"
"thirdparty/recastnavigation"
@ -374,7 +375,7 @@ add_subdirectory(dNavigation)
add_subdirectory(dPhysics)
# Create a list of common libraries shared between all binaries
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp")
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp" "magic_enum")
# Add platform specific common libraries
if (UNIX)

View File

@ -0,0 +1,29 @@
#ifndef __STRINGIFIEDENUM_H__
#define __STRINGIFIEDENUM_H__
#include <string>
#include "magic_enum.hpp"
namespace StringifiedEnum {
template<typename T>
const std::string_view ToString(const T e) {
constexpr auto sv = &magic_enum::enum_entries<T>();
std::string_view output;
const auto it = std::lower_bound(
sv->begin(), sv->end(), e,
[&](const std::pair<T, std::string_view>& lhs, const T rhs)
{ return lhs.first < rhs; }
);
if (it != sv->end() && it->first == e) {
output = it->second;
}
else {
output = "UNKNOWN";
}
return output;
}
}
#endif // !__STRINGIFIEDENUM_H__

View File

@ -3,6 +3,8 @@
#include <cstdint>
#include "magic_enum.hpp"
enum class eGameMessageType : uint16_t {
GET_POSITION = 0,
GET_ROTATION = 1,
@ -1602,4 +1604,10 @@ enum class eGameMessageType : uint16_t {
GET_IS_ON_RAIL = 1772
};
template <>
struct magic_enum::customize::enum_range<eGameMessageType> {
static constexpr int min = 0;
static constexpr int max = 1772;
};
#endif //!__EGAMEMESSAGETYPE__H__

View File

@ -3,6 +3,8 @@
#include <cstdint>
#include "magic_enum.hpp"
enum class eWorldMessageType : uint32_t {
VALIDATION = 1, // Session info
CHARACTER_LIST_REQUEST,
@ -40,4 +42,10 @@ enum class eWorldMessageType : uint32_t {
UI_HELP_TOP_5 = 91
};
template <>
struct magic_enum::customize::enum_range<eWorldMessageType> {
static constexpr int min = 0;
static constexpr int max = 91;
};
#endif //!__EWORLDMESSAGETYPE__H__

View File

@ -34,10 +34,10 @@
#include "eMissionTaskType.h"
#include "eReplicaComponentType.h"
#include "eConnectionType.h"
#include "eGameMessageType.h"
#include "ePlayerFlag.h"
#include "dConfig.h"
using namespace std;
#include "StringifiedEnum.h"
void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const SystemAddress& sysAddr, LWOOBJID objectID, eGameMessageType messageID) {
@ -49,11 +49,11 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
User* usr = UserManager::Instance()->GetUser(sysAddr);
if (!entity) {
LOG("Failed to find associated entity (%llu), aborting GM (%X)!", objectID, messageID);
LOG("Failed to find associated entity (%llu), aborting GM: %4i, %s!", objectID, messageID, StringifiedEnum::ToString(messageID).data());
return;
}
if (messageID != eGameMessageType::READY_FOR_UPDATES) LOG_DEBUG("received game message ID: %i", messageID);
if (messageID != eGameMessageType::READY_FOR_UPDATES) LOG_DEBUG("Received GM with ID and name: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
switch (messageID) {
@ -344,12 +344,12 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
SyncSkill sync = SyncSkill(inStream); // inStream replaced &bitStream
ostringstream buffer;
std::ostringstream buffer;
for (unsigned int k = 0; k < sync.sBitStream.size(); k++) {
char s;
s = sync.sBitStream.at(k);
buffer << setw(2) << hex << setfill('0') << (int)s << " ";
buffer << std::setw(2) << std::hex << std::setfill('0') << static_cast<int>(s) << " ";
}
if (usr != nullptr) {
@ -694,7 +694,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
GameMessages::SendVendorStatusUpdate(entity, sysAddr, true);
break;
default:
LOG_DEBUG("Unknown game message ID: %i", messageID);
LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
break;
}
}

View File

@ -19,8 +19,7 @@
#include "Logger.h"
#include "GameMessages.h"
#include "CDClientDatabase.h"
enum class eGameMessageType : uint16_t;
#include "eGameMessageType.h"
namespace GameMessageHandler {
void HandleMessage(RakNet::BitStream* inStream, const SystemAddress& sysAddr, LWOOBJID objectID, eGameMessageType messageID);

View File

@ -76,6 +76,7 @@
#include "EntityManager.h"
#include "CheatDetection.h"
#include "eGameMasterLevel.h"
#include "StringifiedEnum.h"
namespace Game {
Logger* logger = nullptr;
@ -1244,7 +1245,9 @@ void HandlePacket(Packet* packet) {
}
default:
LOG("Unknown world packet received: %i", int(packet->data[3]));
const auto messageId = *reinterpret_cast<eWorldMessageType*>(&packet->data[3]);
const std::string_view messageIdString = StringifiedEnum::ToString(messageId);
LOG("Unknown world packet received: %4i, %s", messageId, messageIdString.data());
}
}

View File

@ -11,6 +11,9 @@ set(DCOMMONTEST_SOURCES
"dCommonDependencies.cpp"
)
add_subdirectory(dEnumsTests)
list(APPEND DCOMMONTEST_SOURCES ${DENUMS_TESTS})
# Set our executable
add_executable(dCommonTests ${DCOMMONTEST_SOURCES})

View File

@ -0,0 +1,10 @@
set(DENUMS_TESTS
"MagicEnumTests.cpp"
)
# Get the folder name and prepend it to the files above
get_filename_component(thisFolderName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
list(TRANSFORM DENUMS_TESTS PREPEND "${thisFolderName}/")
# Export to parent scope
set(DENUMS_TESTS ${DENUMS_TESTS} PARENT_SCOPE)

View File

@ -0,0 +1,142 @@
#include <chrono>
#include <string>
#include <gtest/gtest.h>
#include "StringifiedEnum.h"
#include "Logger.h"
#include "Game.h"
#include "eGameMessageType.h"
#include "eWorldMessageType.h"
#include "magic_enum.hpp"
#define ENUM_EQ(e, y, z)\
LOG("%s %s", StringifiedEnum::ToString(static_cast<e>(y)).data(), #z);\
ASSERT_STREQ(StringifiedEnum::ToString(static_cast<e>(y)).data(), #z);
#define ENUM_NE(e, y)\
ENUM_EQ(e, y, UNKNOWN);
// Test World Message Enum Reflection
TEST(MagicEnumTest, eWorldMessageTypeTest) {
Game::logger = new Logger("./MagicEnumTest_eWorldMessageTypeTest.log", true, true);
ENUM_EQ(eWorldMessageType, 1, VALIDATION);
ENUM_EQ(eWorldMessageType, 2, CHARACTER_LIST_REQUEST);
ENUM_EQ(eWorldMessageType, 3, CHARACTER_CREATE_REQUEST);
ENUM_EQ(eWorldMessageType, 4, LOGIN_REQUEST);
ENUM_EQ(eWorldMessageType, 5, GAME_MSG);
ENUM_EQ(eWorldMessageType, 6, CHARACTER_DELETE_REQUEST);
ENUM_EQ(eWorldMessageType, 7, CHARACTER_RENAME_REQUEST);
ENUM_EQ(eWorldMessageType, 8, HAPPY_FLOWER_MODE_NOTIFY);
ENUM_EQ(eWorldMessageType, 9, SLASH_RELOAD_MAP);
ENUM_EQ(eWorldMessageType, 10, SLASH_PUSH_MAP_REQUEST);
ENUM_EQ(eWorldMessageType, 11, SLASH_PUSH_MAP);
ENUM_EQ(eWorldMessageType, 12, SLASH_PULL_MAP);
ENUM_EQ(eWorldMessageType, 13, LOCK_MAP_REQUEST);
ENUM_EQ(eWorldMessageType, 14, GENERAL_CHAT_MESSAGE);
ENUM_EQ(eWorldMessageType, 15, HTTP_MONITOR_INFO_REQUEST);
ENUM_EQ(eWorldMessageType, 16, SLASH_DEBUG_SCRIPTS);
ENUM_EQ(eWorldMessageType, 17, MODELS_CLEAR);
ENUM_EQ(eWorldMessageType, 18, EXHIBIT_INSERT_MODEL);
ENUM_EQ(eWorldMessageType, 19, LEVEL_LOAD_COMPLETE);
ENUM_EQ(eWorldMessageType, 20, TMP_GUILD_CREATE);
ENUM_EQ(eWorldMessageType, 21, ROUTE_PACKET);
ENUM_EQ(eWorldMessageType, 22, POSITION_UPDATE);
ENUM_EQ(eWorldMessageType, 23, MAIL);
ENUM_EQ(eWorldMessageType, 24, WORD_CHECK);
ENUM_EQ(eWorldMessageType, 25, STRING_CHECK);
ENUM_EQ(eWorldMessageType, 26, GET_PLAYERS_IN_ZONE);
ENUM_EQ(eWorldMessageType, 27, REQUEST_UGC_MANIFEST_INFO);
ENUM_EQ(eWorldMessageType, 28, BLUEPRINT_GET_ALL_DATA_REQUEST);
ENUM_EQ(eWorldMessageType, 29, CANCEL_MAP_QUEUE);
ENUM_EQ(eWorldMessageType, 30, HANDLE_FUNNESS);
ENUM_EQ(eWorldMessageType, 31, FAKE_PRG_CSR_MESSAGE);
ENUM_EQ(eWorldMessageType, 32, REQUEST_FREE_TRIAL_REFRESH);
ENUM_EQ(eWorldMessageType, 33, GM_SET_FREE_TRIAL_STATUS);
ENUM_EQ(eWorldMessageType, 91, UI_HELP_TOP_5);
ENUM_NE(eWorldMessageType, 37);
ENUM_NE(eWorldMessageType, 123);
srand(time(NULL));
auto begin = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000000; ++i) {
volatile auto f = StringifiedEnum::ToString(static_cast<eWorldMessageType>(i)).data();
// To ensure the compiler doesn't optimize out the call, I print it at random intervals
if (rand() % 100000 == 0) LOG("%i, %s", i, f);
}
auto end = std::chrono::high_resolution_clock::now();
LOG("Time: %lld", std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count());
delete Game::logger;
}
// Test Game Message Enum Reflection
TEST(MagicEnumTest, eGameMessageTypeTest) {
Game::logger = new Logger("./MagicEnumTest_eGameMessageTypeTest.log", true, true);
// Only doing the first and last 10 for the sake of my sanity
ENUM_EQ(eGameMessageType, 0, GET_POSITION);
ENUM_EQ(eGameMessageType, 1, GET_ROTATION);
ENUM_EQ(eGameMessageType, 2, GET_LINEAR_VELOCITY);
ENUM_EQ(eGameMessageType, 3, GET_ANGULAR_VELOCITY);
ENUM_EQ(eGameMessageType, 4, GET_FORWARD_VELOCITY);
ENUM_EQ(eGameMessageType, 5, GET_PLAYER_FORWARD);
ENUM_EQ(eGameMessageType, 6, GET_FORWARD_VECTOR);
ENUM_EQ(eGameMessageType, 7, SET_POSITION);
ENUM_EQ(eGameMessageType, 8, SET_LOCAL_POSITION);
ENUM_EQ(eGameMessageType, 9, SET_ROTATION);
ENUM_EQ(eGameMessageType, 10, SET_LINEAR_VELOCITY);
ENUM_EQ(eGameMessageType, 1762, USE_SKILL_SET);
ENUM_EQ(eGameMessageType, 1763, SET_SKILL_SET_POSSESSOR);
ENUM_EQ(eGameMessageType, 1764, POPULATE_ACTION_BAR);
ENUM_EQ(eGameMessageType, 1765, GET_COMPONENT_TEMPLATE_ID);
ENUM_EQ(eGameMessageType, 1766, GET_POSSESSABLE_SKILL_SET);
ENUM_EQ(eGameMessageType, 1767, MARK_INVENTORY_ITEM_AS_ACTIVE);
ENUM_EQ(eGameMessageType, 1768, UPDATE_FORGED_ITEM);
ENUM_EQ(eGameMessageType, 1769, CAN_ITEMS_BE_REFORGED);
ENUM_EQ(eGameMessageType, 1771, NOTIFY_CLIENT_RAIL_START_FAILED);
ENUM_EQ(eGameMessageType, 1772, GET_IS_ON_RAIL);
ENUM_NE(eGameMessageType, 32);
ENUM_NE(eGameMessageType, 1776);
srand(time(NULL));
auto begin = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000000; ++i) {
volatile auto f = StringifiedEnum::ToString(static_cast<eGameMessageType>(i)).data();
// To ensure the compiler doesn't optimize out the call, I print it at random intervals
if (rand() % 100000 == 0) LOG("%i, %s", i, f);
}
auto end = std::chrono::high_resolution_clock::now();
LOG("Time: %lld", std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count());
delete Game::logger;
}
#define ASSERT_EARRAY_SORTED(EARRAY_VAR)\
for (int i = 0; i < EARRAY_VAR->size(); i++) {\
const auto entryCurr = EARRAY_VAR->at(i).first;\
LOG_EARRAY(EARRAY_VAR, i, entryCurr);\
const auto entryNext = EARRAY_VAR->at(++i).first;\
LOG_EARRAY(EARRAY_VAR, i, entryNext);\
ASSERT_TRUE(entryCurr < entryNext);\
};\
#define LOG_EARRAY(EARRAY_VAR, INDICE, ENTRY)\
LOG(#EARRAY_VAR"[%i] = %i, %s", INDICE, ENTRY, magic_enum::enum_name(ENTRY).data());
// Test that the magic enum arrays are pre-sorted
TEST(MagicEnumTest, ArraysAreSorted) {
Game::logger = new Logger("./MagicEnumTest_ArraysAreSorted.log", true, true);
constexpr auto wmArray = &magic_enum::enum_entries<eWorldMessageType>();
ASSERT_EARRAY_SORTED(wmArray);
constexpr auto gmArray = &magic_enum::enum_entries<eGameMessageType>();
ASSERT_EARRAY_SORTED(gmArray);
delete Game::logger;
}

View File

@ -19,6 +19,9 @@ add_library(bcrypt ${SOURCES_LIBBCRYPT})
# Source code for sqlite
add_subdirectory(SQLite)
# Source code for magic_enum
add_subdirectory(magic_enum)
# MariaDB C++ Connector
include(CMakeMariaDBLists.txt)

1
thirdparty/magic_enum vendored Submodule

@ -0,0 +1 @@
Subproject commit e55b9b54d5cf61f8e117cafb17846d7d742dd3b4