diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 4b234341..749b6db0 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -97,6 +97,8 @@ #include "CDSkillBehaviorTable.h" #include "CDZoneTableTable.h" +#include "StringifiedEnum.h" + #include Observable 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& ldf, RakNet::BitStrea numberOfValidKeys--; } } - + // Now write it to the main bitstream outBitStream.Write(settingStream.GetNumberOfBytesUsed() + 1 + sizeof(uint32_t)); outBitStream.Write(0); //no compression used @@ -2209,3 +2212,38 @@ bool Entity::HandleMsg(GameMessages::GameMsg& msg) const { void Entity::RegisterMsg(const MessageType::Game msgId, std::function handler) { m_MsgHandlers.emplace(msgId, handler); } + +bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) { + auto& requestInfo = static_cast(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(); + + const auto& objTableInfo = table->GetByID(GetLOT()); + + objectInfo.PushDebug("Name") = objTableInfo.name; + objectInfo.PushDebug("Template ID(LOT)") = GetLOT(); + objectInfo.PushDebug("Object ID") = std::to_string(GetObjectID()); + objectInfo.PushDebug("Spawner's Object ID") = std::to_string(GetSpawnerID()); + + auto& componentDetails = objectInfo.PushDebug("Component Information"); + for (const auto [id, component] : m_Components) { + componentDetails.PushDebug(StringifiedEnum::ToString(id)) = ""; + } + + auto& configData = objectInfo.PushDebug("Config Data"); + for (const auto config : m_Settings) { + configData.PushDebug(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; +} diff --git a/dGame/Entity.h b/dGame/Entity.h index 5f5d1976..469ac5ea 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -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. */ diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index 0659e743..0559da7c 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -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(msg); - AMFArrayValue response; +bool CharacterComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) { + auto& reportInfo = static_cast(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("Component ID") = GeneralUtils::ToUnderlying(ComponentType); cmptType.PushDebug("Character's account ID") = m_Character->GetParentUser()->GetAccountID(); @@ -72,6 +66,13 @@ bool CharacterComponent::OnRequestServerObjectInfo(GameMessages::GameMsg& msg) { cmptType.PushDebug("Total currency") = std::to_string(m_Character->GetCoins()); cmptType.PushDebug("Currency able to be picked up") = std::to_string(m_DroppedCoins); cmptType.PushDebug("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(sstream.str()) = ""; + } + // visited locations cmptType.PushDebug("is a GM") = m_GMLevel > eGameMasterLevel::CIVILIAN; cmptType.PushDebug("Has PVP flag turned on") = m_PvpEnabled; @@ -83,9 +84,6 @@ bool CharacterComponent::OnRequestServerObjectInfo(GameMessages::GameMsg& msg) { cmptType.PushDebug("Current Activity Type") = GeneralUtils::ToUnderlying(m_CurrentActivity); cmptType.PushDebug("Property Clone ID") = m_Character->GetPropertyCloneID(); - GameMessages::SendUIMessageServerToSingleClient("ToggleObjectDebugger", response, m_Parent->GetSystemAddress()); - - LOG("Handled!"); return true; } diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index 6336a6d1..6d52da32 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -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 diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 91f6cd7c..20d0eb82 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -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) {} diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp index 264e9373..d0a799e7 100644 --- a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp +++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp @@ -1,5 +1,7 @@ #include "DEVGMCommands.h" +#include + // 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(id)) << "]"; - - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(stream.str())); - } - if (splitArgs.size() >= 2) { if (splitArgs[1] == "-m" && splitArgs.size() >= 3) { auto* const movingPlatformComponent = closest->GetComponent(); @@ -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(); diff --git a/dNet/WorldPackets.cpp b/dNet/WorldPackets.cpp index 512f2265..746c7fc9 100644 --- a/dNet/WorldPackets.cpp +++ b/dNet/WorldPackets.cpp @@ -12,7 +12,7 @@ #include -void HTTPMonitorInfo::Serialize(RakNet::BitStream &bitStream) const { +void HTTPMonitorInfo::Serialize(RakNet::BitStream& bitStream) const { bitStream.Write(port); bitStream.Write(openWeb); bitStream.Write(supportsSum); @@ -81,32 +81,29 @@ void WorldPackets::SendServerState(const SystemAddress& sysAddr) { SEND_PACKET; } -void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm) { +void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm, const LWOCLONEID cloneID) { + using namespace std; RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CREATE_CHARACTER); RakNet::BitStream data; - data.Write(7); //LDF key count - std::unique_ptr> objid(new LDFData(u"objid", player)); - std::unique_ptr> lot(new LDFData(u"template", 1)); - std::unique_ptr> xmlConfigData(new LDFData(u"xmlData", xmlData)); - std::unique_ptr> name(new LDFData(u"name", username)); - std::unique_ptr> gmlevel(new LDFData(u"gmlevel", static_cast(gm))); - std::unique_ptr> chatmode(new LDFData(u"chatmode", static_cast(gm))); - std::unique_ptr> reputationLdf(new LDFData(u"reputation", reputation)); - - objid->WriteToPacket(data); - lot->WriteToPacket(data); - name->WriteToPacket(data); - gmlevel->WriteToPacket(data); - chatmode->WriteToPacket(data); - xmlConfigData->WriteToPacket(data); - reputationLdf->WriteToPacket(data); + std::vector> ldfData; + ldfData.push_back(move(make_unique>(u"objid", player))); + ldfData.push_back(move(make_unique>(u"template", 1))); + ldfData.push_back(move(make_unique>(u"xmlData", xmlData))); + ldfData.push_back(move(make_unique>(u"name", username))); + ldfData.push_back(move(make_unique>(u"gmlevel", static_cast(gm)))); + ldfData.push_back(move(make_unique>(u"chatmode", static_cast(gm)))); + ldfData.push_back(move(make_unique>(u"reputation", reputation))); + ldfData.push_back(move(make_unique>(u"propertycloneid", cloneID))); + + data.Write(ldfData.size()); + for (const auto& toSerialize : ldfData) toSerialize->WriteToPacket(data); //Compress the data before sending: - const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed()); - uint8_t* compressedData = new uint8_t[reservedSize]; + const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed()); + uint8_t* compressedData = new uint8_t[reservedSize]; // TODO There should be better handling here for not enough memory... if (!compressedData) return; @@ -177,7 +174,7 @@ void WorldPackets::SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPM SEND_PACKET; } -void WorldPackets::SendDebugOuput(const SystemAddress& sysAddr, const std::string& data){ +void WorldPackets::SendDebugOuput(const SystemAddress& sysAddr, const std::string& data) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::DEBUG_OUTPUT); bitStream.Write(data.size()); diff --git a/dNet/WorldPackets.h b/dNet/WorldPackets.h index 0da98604..1a48f5df 100644 --- a/dNet/WorldPackets.h +++ b/dNet/WorldPackets.h @@ -31,7 +31,7 @@ namespace WorldPackets { void SendCharacterDeleteResponse(const SystemAddress& sysAddr, bool response); void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift); void SendServerState(const SystemAddress& sysAddr); - void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm); + void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm, const LWOCLONEID cloneID); void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::set> unacceptedItems); void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel); void SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info); diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 9423d5dc..4380edd7 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -1040,7 +1040,7 @@ void HandlePacket(Packet* packet) { auto* characterComponent = player->GetComponent(); if (!characterComponent) return; - WorldPackets::SendCreateCharacter(packet->systemAddress, player->GetComponent()->GetReputation(), player->GetObjectID(), c->GetXMLData(), username, c->GetGMLevel()); + WorldPackets::SendCreateCharacter(packet->systemAddress, player->GetComponent()->GetReputation(), player->GetObjectID(), c->GetXMLData(), username, c->GetGMLevel(), c->GetPropertyCloneID()); WorldPackets::SendServerState(packet->systemAddress); const auto respawnPoint = player->GetCharacter()->GetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID()); diff --git a/docs/Commands.md b/docs/Commands.md index d9bacf70..b9a5ee5c 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -121,15 +121,15 @@ These commands are primarily for development and testing. The usage of many of t ## Detailed `/inspect` Usage -`/inspect (-m | -a | -s | -p | -f (faction) | -t)` +`/inspect (-m | -a | -p | -f (faction) | -t)` Finds the closest entity with the given component or LDF variable (ignoring players and racing cars), printing its ID, distance from the player, and whether it is sleeping, as well as the the IDs of all components the entity has. +This info is then shown in a window on the client which the user can navigate ### `/inspect` Options * `-m`: If the entity has a moving platform component, sends it to the given waypoint, or stops the platform if `waypoint` is `-1`. * `-a`: Plays the given animation on the entity. -* `-s`: Prints the entity's settings and spawner ID. * `-p`: Prints the entity's position * `-f`: If the entity has a destroyable component, prints whether the entity is smashable and its friendly and enemy faction IDs; if `faction` is specified, adds that faction to the entity. * `-cf`: check if the entity is enemy or friend