feat: debug features and implement ObjectDebugger (#1846)

Move the -s and base features of inspect to the object debugger (this file is present in an unmodified, live client)
This commit is contained in:
David Markowitz
2025-07-19 03:11:32 -07:00
committed by GitHub
parent 71f708f1b5
commit 6b52cf67a0
10 changed files with 96 additions and 58 deletions

View File

@@ -97,6 +97,8 @@
#include "CDSkillBehaviorTable.h"
#include "CDZoneTableTable.h"
#include "StringifiedEnum.h"
#include <ranges>
Observable<Entity*, const PositionUpdate&> Entity::OnPlayerPositionUpdate;
@@ -187,6 +189,7 @@ Entity::~Entity() {
}
void Entity::Initialize() {
RegisterMsg(MessageType::Game::REQUEST_SERVER_OBJECT_INFO, this, &Entity::MsgRequestServerObjectInfo);
/**
* Setup trigger
*/
@@ -920,7 +923,7 @@ void Entity::WriteLDFData(const std::vector<LDFBaseData*>& ldf, RakNet::BitStrea
numberOfValidKeys--;
}
}
// Now write it to the main bitstream
outBitStream.Write<uint32_t>(settingStream.GetNumberOfBytesUsed() + 1 + sizeof(uint32_t));
outBitStream.Write<uint8_t>(0); //no compression used
@@ -2209,3 +2212,38 @@ bool Entity::HandleMsg(GameMessages::GameMsg& msg) const {
void Entity::RegisterMsg(const MessageType::Game msgId, std::function<bool(GameMessages::GameMsg&)> handler) {
m_MsgHandlers.emplace(msgId, handler);
}
bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
auto& requestInfo = static_cast<GameMessages::RequestServerObjectInfo&>(msg);
AMFArrayValue response;
response.Insert("visible", true);
response.Insert("objectID", std::to_string(m_ObjectID));
response.Insert("serverInfo", true);
GameMessages::GetObjectReportInfo info{};
info.info = response.InsertArray("data");
auto& objectInfo = info.info->PushDebug("Object Details");
auto* table = CDClientManager::GetTable<CDObjectsTable>();
const auto& objTableInfo = table->GetByID(GetLOT());
objectInfo.PushDebug<AMFStringValue>("Name") = objTableInfo.name;
objectInfo.PushDebug<AMFIntValue>("Template ID(LOT)") = GetLOT();
objectInfo.PushDebug<AMFStringValue>("Object ID") = std::to_string(GetObjectID());
objectInfo.PushDebug<AMFStringValue>("Spawner's Object ID") = std::to_string(GetSpawnerID());
auto& componentDetails = objectInfo.PushDebug("Component Information");
for (const auto [id, component] : m_Components) {
componentDetails.PushDebug<AMFStringValue>(StringifiedEnum::ToString(id)) = "";
}
auto& configData = objectInfo.PushDebug("Config Data");
for (const auto config : m_Settings) {
configData.PushDebug<AMFStringValue>(GeneralUtils::UTF16ToWTF8(config->GetKey())) = config->GetValueAsString();
}
HandleMsg(info);
auto* targetForReport = Game::entityManager->GetEntity(requestInfo.targetForReport);
if (targetForReport) GameMessages::SendUIMessageServerToSingleClient("ToggleObjectDebugger", response, targetForReport->GetSystemAddress());
return true;
}

View File

@@ -175,6 +175,8 @@ public:
void AddComponent(eReplicaComponentType componentId, Component* component);
bool MsgRequestServerObjectInfo(GameMessages::GameMsg& msg);
// This is expceted to never return nullptr, an assert checks this.
CppScripts::Script* const GetScript() const;
@@ -336,6 +338,10 @@ public:
bool HandleMsg(GameMessages::GameMsg& msg) const;
void RegisterMsg(const MessageType::Game msgId, auto* self, const auto handler) {
RegisterMsg(msgId, std::bind(handler, self, std::placeholders::_1));
}
/**
* @brief The observable for player entity position updates.
*/

View File

@@ -49,19 +49,13 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character, con
m_LastUpdateTimestamp = std::time(nullptr);
m_SystemAddress = systemAddress;
RegisterMsg(MessageType::Game::REQUEST_SERVER_OBJECT_INFO, this, &CharacterComponent::OnRequestServerObjectInfo);
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &CharacterComponent::OnGetObjectReportInfo);
}
bool CharacterComponent::OnRequestServerObjectInfo(GameMessages::GameMsg& msg) {
auto& request = static_cast<GameMessages::RequestServerObjectInfo&>(msg);
AMFArrayValue response;
bool CharacterComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
auto& reportInfo = static_cast<GameMessages::GetObjectReportInfo&>(msg);
response.Insert("visible", true);
response.Insert("objectID", std::to_string(request.targetForReport));
response.Insert("serverInfo", true);
auto& data = *response.InsertArray("data");
auto& cmptType = data.PushDebug("Character");
auto& cmptType = reportInfo.info->PushDebug("Character");
cmptType.PushDebug<AMFIntValue>("Component ID") = GeneralUtils::ToUnderlying(ComponentType);
cmptType.PushDebug<AMFIntValue>("Character's account ID") = m_Character->GetParentUser()->GetAccountID();
@@ -72,6 +66,13 @@ bool CharacterComponent::OnRequestServerObjectInfo(GameMessages::GameMsg& msg) {
cmptType.PushDebug<AMFStringValue>("Total currency") = std::to_string(m_Character->GetCoins());
cmptType.PushDebug<AMFStringValue>("Currency able to be picked up") = std::to_string(m_DroppedCoins);
cmptType.PushDebug<AMFStringValue>("Tooltip flags value") = "0";
auto& vl = cmptType.PushDebug("Visited Levels");
for (const auto zoneID : m_VisitedLevels) {
std::stringstream sstream;
sstream << "MapID: " << zoneID.GetMapID() << " CloneID: " << zoneID.GetCloneID();
vl.PushDebug<AMFStringValue>(sstream.str()) = "";
}
// visited locations
cmptType.PushDebug<AMFBoolValue>("is a GM") = m_GMLevel > eGameMasterLevel::CIVILIAN;
cmptType.PushDebug<AMFBoolValue>("Has PVP flag turned on") = m_PvpEnabled;
@@ -83,9 +84,6 @@ bool CharacterComponent::OnRequestServerObjectInfo(GameMessages::GameMsg& msg) {
cmptType.PushDebug<AMFIntValue>("Current Activity Type") = GeneralUtils::ToUnderlying(m_CurrentActivity);
cmptType.PushDebug<AMFDoubleValue>("Property Clone ID") = m_Character->GetPropertyCloneID();
GameMessages::SendUIMessageServerToSingleClient("ToggleObjectDebugger", response, m_Parent->GetSystemAddress());
LOG("Handled!");
return true;
}

View File

@@ -331,7 +331,7 @@ public:
void LoadVisitedLevelsXml(const tinyxml2::XMLElement& doc);
private:
bool OnRequestServerObjectInfo(GameMessages::GameMsg& msg);
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
/**
* The map of active venture vision effects

View File

@@ -15,6 +15,7 @@
#include "eGameMasterLevel.h"
class AMFBaseValue;
class AMFArrayValue;
class Entity;
class Item;
class NiQuaternion;
@@ -788,6 +789,13 @@ namespace GameMessages {
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
};
struct GetObjectReportInfo : public GameMsg {
AMFArrayValue* info{};
bool bVerbose{};
GetObjectReportInfo() : GameMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, eGameMasterLevel::DEVELOPER) {}
};
struct RequestUse : public GameMsg {
RequestUse() : GameMsg(MessageType::Game::REQUEST_USE) {}

View File

@@ -1,5 +1,7 @@
#include "DEVGMCommands.h"
#include <ranges>
// Classes
#include "AssetManager.h"
#include "Character.h"
@@ -48,6 +50,7 @@
#include "MessageType/Master.h"
#include "eInventoryType.h"
#include "ePlayerFlag.h"
#include "StringifiedEnum.h"
namespace DEVGMCommands {
@@ -1514,7 +1517,12 @@ namespace DEVGMCommands {
}
if (!closest) return;
LOG("%llu", closest->GetObjectID());
GameMessages::RequestServerObjectInfo objectInfo;
objectInfo.bVerbose = true;
objectInfo.target = closest->GetObjectID();
objectInfo.targetForReport = entity->GetObjectID();
closest->HandleMsg(objectInfo);
Game::entityManager->SerializeEntity(closest);
@@ -1528,16 +1536,6 @@ namespace DEVGMCommands {
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(header.str()));
for (const auto& pair : closest->GetComponents()) {
auto id = pair.first;
std::stringstream stream;
stream << "Component [" << std::to_string(static_cast<uint32_t>(id)) << "]";
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(stream.str()));
}
if (splitArgs.size() >= 2) {
if (splitArgs[1] == "-m" && splitArgs.size() >= 3) {
auto* const movingPlatformComponent = closest->GetComponent<MovingPlatformComponent>();
@@ -1557,13 +1555,6 @@ namespace DEVGMCommands {
Game::entityManager->SerializeEntity(closest);
} else if (splitArgs[1] == "-a" && splitArgs.size() >= 3) {
RenderComponent::PlayAnimation(closest, splitArgs.at(2));
} else if (splitArgs[1] == "-s") {
for (auto* entry : closest->GetSettings()) {
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::UTF8ToUTF16(entry->GetString()));
}
ChatPackets::SendSystemMessage(sysAddr, u"------");
ChatPackets::SendSystemMessage(sysAddr, u"Spawner ID: " + GeneralUtils::to_u16string(closest->GetSpawnerID()));
} else if (splitArgs[1] == "-p") {
const auto postion = closest->GetPosition();