mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-10-11 09:58:05 +00:00
feat: Mission Component debug (#1901)
* feat: Mission Component debug * Add player argument to inspect command * Add completion details * Remove unlocalized server string done on client instead
This commit is contained in:
@@ -50,7 +50,10 @@ enum class eMissionState : int {
|
||||
/**
|
||||
* The mission has been completed before and has now been completed again. Used for daily missions.
|
||||
*/
|
||||
COMPLETE_READY_TO_COMPLETE = 12
|
||||
COMPLETE_READY_TO_COMPLETE = 12,
|
||||
|
||||
// The mission is failed (don't know where this is used)
|
||||
FAILED = 16,
|
||||
};
|
||||
|
||||
#endif //!__MISSIONSTATE__H__
|
||||
|
@@ -2247,6 +2247,7 @@ bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||
response.Insert("objectID", std::to_string(m_ObjectID));
|
||||
response.Insert("serverInfo", true);
|
||||
GameMessages::GetObjectReportInfo info{};
|
||||
info.bVerbose = requestInfo.bVerbose;
|
||||
info.info = response.InsertArray("data");
|
||||
auto& objectInfo = info.info->PushDebug("Object Details");
|
||||
auto* table = CDClientManager::GetTable<CDObjectsTable>();
|
||||
@@ -2260,14 +2261,14 @@ bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||
|
||||
auto& componentDetails = objectInfo.PushDebug("Component Information");
|
||||
for (const auto [id, component] : m_Components) {
|
||||
componentDetails.PushDebug<AMFStringValue>(StringifiedEnum::ToString(id)) = "";
|
||||
componentDetails.PushDebug(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* client = Game::entityManager->GetEntity(requestInfo.clientId);
|
||||
|
@@ -70,7 +70,7 @@ bool CharacterComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
for (const auto zoneID : m_VisitedLevels) {
|
||||
std::stringstream sstream;
|
||||
sstream << "MapID: " << zoneID.GetMapID() << " CloneID: " << zoneID.GetCloneID();
|
||||
vl.PushDebug<AMFStringValue>(sstream.str()) = "";
|
||||
vl.PushDebug(sstream.str());
|
||||
}
|
||||
|
||||
// visited locations
|
||||
@@ -95,7 +95,7 @@ bool CharacterComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
const int32_t flagId = base + i;
|
||||
std::stringstream stream;
|
||||
stream << "Flag: " << flagId;
|
||||
allFlags.PushDebug<AMFStringValue>(stream.str()) = "";
|
||||
allFlags.PushDebug(stream.str());
|
||||
}
|
||||
flagChunkCopy >>= 1;
|
||||
}
|
||||
|
@@ -27,7 +27,10 @@ std::unordered_map<AchievementCacheKey, std::vector<uint32_t>> MissionComponent:
|
||||
|
||||
//! Initializer
|
||||
MissionComponent::MissionComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
using namespace GameMessages;
|
||||
m_LastUsedMissionOrderUID = Game::zoneManager->GetUniqueMissionIdStartingValue();
|
||||
|
||||
RegisterMsg<GetObjectReportInfo>(this, &MissionComponent::OnGetObjectReportInfo);
|
||||
}
|
||||
|
||||
//! Destructor
|
||||
@@ -622,3 +625,111 @@ void MissionComponent::ResetMission(const int32_t missionId) {
|
||||
m_Missions.erase(missionId);
|
||||
GameMessages::SendResetMissions(m_Parent, m_Parent->GetSystemAddress(), missionId);
|
||||
}
|
||||
|
||||
void PushMissions(const std::map<uint32_t, Mission*>& missions, AMFArrayValue& V, bool verbose) {
|
||||
for (const auto& [id, mission] : missions) {
|
||||
std::stringstream ss;
|
||||
if (!mission) {
|
||||
ss << "Mission ID: " << id;
|
||||
V.PushDebug(ss.str());
|
||||
} else if (!verbose) {
|
||||
ss << "%[Missions_" << id << "_name]" << ", Mission ID";
|
||||
V.PushDebug<AMFIntValue>(ss.str()) = id;
|
||||
} else {
|
||||
ss << "%[Missions_" << id << "_name]" << ", Mission ID: " << id;
|
||||
auto& missionV = V.PushDebug(ss.str());
|
||||
auto& missionInformation = missionV.PushDebug("Mission Information");
|
||||
|
||||
if (mission->IsComplete()) {
|
||||
missionInformation.PushDebug<AMFStringValue>("Time mission last completed") = std::to_string(mission->GetTimestamp());
|
||||
missionInformation.PushDebug<AMFIntValue>("Number of times completed") = mission->GetCompletions();
|
||||
}
|
||||
// Expensive to network this especially when its read from the client anyways
|
||||
// missionInformation.PushDebug("Description").PushDebug("None");
|
||||
// missionInformation.PushDebug("Text").PushDebug("None");
|
||||
|
||||
auto& statusInfo = missionInformation.PushDebug("Mission statuses for local player");
|
||||
if (mission->IsAvalible()) statusInfo.PushDebug("Available");
|
||||
if (mission->IsActive()) statusInfo.PushDebug("Active");
|
||||
if (mission->IsReadyToComplete()) statusInfo.PushDebug("Ready To Complete");
|
||||
if (mission->IsComplete()) statusInfo.PushDebug("Completed");
|
||||
if (mission->IsFailed()) statusInfo.PushDebug("Failed");
|
||||
const auto& clientInfo = mission->GetClientInfo();
|
||||
|
||||
statusInfo.PushDebug<AMFBoolValue>("Is an achievement mission") = mission->IsAchievement();
|
||||
statusInfo.PushDebug<AMFBoolValue>("Is an timed mission") = clientInfo.time_limit > 0;
|
||||
auto& taskInfo = statusInfo.PushDebug("Task Info");
|
||||
taskInfo.PushDebug<AMFIntValue>("Number of tasks in this mission") = mission->GetTasks().size();
|
||||
int32_t i = 0;
|
||||
for (const auto* task : mission->GetTasks()) {
|
||||
auto& thisTask = taskInfo.PushDebug("Task " + std::to_string(i));
|
||||
// Expensive to network this especially when its read from the client anyways
|
||||
// thisTask.PushDebug("Description").PushDebug("%[MissionTasks_" + taskUidStr + "_description]");
|
||||
thisTask.PushDebug<AMFIntValue>("Number done") = std::min(task->GetProgress(), static_cast<uint32_t>(task->GetClientInfo().targetValue));
|
||||
thisTask.PushDebug<AMFIntValue>("Number total needed") = task->GetClientInfo().targetValue;
|
||||
thisTask.PushDebug<AMFIntValue>("Task Type") = task->GetClientInfo().taskType;
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
// auto& chatText = missionInformation.PushDebug("Chat Text for Mission States");
|
||||
// Expensive to network this especially when its read from the client anyways
|
||||
// chatText.PushDebug("Available Text").PushDebug("%[MissionText_" + idStr + "_chat_state_1]");
|
||||
// chatText.PushDebug("Active Text").PushDebug("%[MissionText_" + idStr + "_chat_state_2]");
|
||||
// chatText.PushDebug("Ready-to-Complete Text").PushDebug("%[MissionText_" + idStr + "_chat_state_3]");
|
||||
// chatText.PushDebug("Complete Text").PushDebug("%[MissionText_" + idStr + "_chat_state_4]");
|
||||
|
||||
if (clientInfo.time_limit > 0) {
|
||||
missionInformation.PushDebug<AMFIntValue>("Time Limit") = clientInfo.time_limit;
|
||||
missionInformation.PushDebug<AMFDoubleValue>("Time Remaining") = 0;
|
||||
}
|
||||
|
||||
if (clientInfo.offer_objectID != -1) {
|
||||
missionInformation.PushDebug<AMFIntValue>("Offer Object LOT") = clientInfo.offer_objectID;
|
||||
}
|
||||
|
||||
if (clientInfo.target_objectID != -1) {
|
||||
missionInformation.PushDebug<AMFIntValue>("Complete Object LOT") = clientInfo.target_objectID;
|
||||
}
|
||||
|
||||
if (!clientInfo.prereqMissionID.empty()) {
|
||||
missionInformation.PushDebug<AMFStringValue>("Requirement Mission IDs") = clientInfo.prereqMissionID;
|
||||
}
|
||||
|
||||
missionInformation.PushDebug<AMFBoolValue>("Is Repeatable") = clientInfo.repeatable;
|
||||
const bool hasNoOfferer = clientInfo.offer_objectID == -1 || clientInfo.offer_objectID == 0;
|
||||
const bool hasNoCompleter = clientInfo.target_objectID == -1 || clientInfo.target_objectID == 0;
|
||||
missionInformation.PushDebug<AMFBoolValue>("Is Achievement") = hasNoOfferer && hasNoCompleter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MissionComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
auto& missionInfo = reportMsg.info->PushDebug("Mission (Laggy)");
|
||||
missionInfo.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||
// Sort the missions so they are easier to parse and present to the end user
|
||||
std::map<uint32_t, Mission*> achievements;
|
||||
std::map<uint32_t, Mission*> missions;
|
||||
std::map<uint32_t, Mission*> doneMissions;
|
||||
for (const auto [id, mission] : m_Missions) {
|
||||
if (!mission) continue;
|
||||
else if (mission->IsComplete()) doneMissions[id] = mission;
|
||||
else if (mission->IsAchievement()) achievements[id] = mission;
|
||||
else if (mission->IsMission()) missions[id] = mission;
|
||||
}
|
||||
|
||||
// None of these should be empty, but if they are dont print the field
|
||||
if (!achievements.empty() || !missions.empty()) {
|
||||
auto& incompleteMissions = missionInfo.PushDebug("Incomplete Missions");
|
||||
PushMissions(achievements, incompleteMissions, reportMsg.bVerbose);
|
||||
PushMissions(missions, incompleteMissions, reportMsg.bVerbose);
|
||||
}
|
||||
|
||||
if (!doneMissions.empty()) {
|
||||
auto& completeMissions = missionInfo.PushDebug("Completed Missions");
|
||||
PushMissions(doneMissions, completeMissions, reportMsg.bVerbose);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -171,6 +171,7 @@ public:
|
||||
|
||||
void ResetMission(const int32_t missionId);
|
||||
private:
|
||||
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
/**
|
||||
* All the missions owned by this entity, mapped by mission ID
|
||||
*/
|
||||
|
@@ -6415,6 +6415,7 @@ namespace GameMessages {
|
||||
void RequestServerObjectInfo::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
||||
auto* handlingEntity = Game::entityManager->GetEntity(targetForReport);
|
||||
if (handlingEntity) handlingEntity->HandleMsg(*this);
|
||||
else LOG("Failed to find target %llu", targetForReport);
|
||||
}
|
||||
|
||||
bool RequestUse::Deserialize(RakNet::BitStream& stream) {
|
||||
|
@@ -270,6 +270,12 @@ bool Mission::IsReadyToComplete() const {
|
||||
return m_State == eMissionState::READY_TO_COMPLETE || m_State == eMissionState::COMPLETE_READY_TO_COMPLETE;
|
||||
}
|
||||
|
||||
bool Mission::IsFailed() const {
|
||||
const auto underlying = GeneralUtils::ToUnderlying(m_State);
|
||||
const auto target = GeneralUtils::ToUnderlying(eMissionState::FAILED);
|
||||
return (underlying & target) != 0;
|
||||
}
|
||||
|
||||
void Mission::MakeReadyToComplete() {
|
||||
SetMissionState(m_Completions == 0 ? eMissionState::READY_TO_COMPLETE : eMissionState::COMPLETE_READY_TO_COMPLETE);
|
||||
}
|
||||
|
@@ -244,6 +244,8 @@ public:
|
||||
|
||||
const std::set<uint32_t>& GetTestedMissions() const;
|
||||
|
||||
bool IsFailed() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Progresses all the newly accepted tasks for this mission after it has been accepted to reflect the state of the
|
||||
|
@@ -1475,11 +1475,14 @@ namespace DEVGMCommands {
|
||||
if (splitArgs.empty()) return;
|
||||
|
||||
Entity* closest = nullptr;
|
||||
float closestDistance = 0.0f;
|
||||
|
||||
std::u16string ldf;
|
||||
|
||||
bool isLDF = false;
|
||||
|
||||
closest = PlayerManager::GetPlayer(splitArgs[0]);
|
||||
if (!closest) {
|
||||
auto component = GeneralUtils::TryParse<eReplicaComponentType>(splitArgs[0]);
|
||||
if (!component) {
|
||||
component = eReplicaComponentType::INVALID;
|
||||
@@ -1491,7 +1494,6 @@ namespace DEVGMCommands {
|
||||
|
||||
auto reference = entity->GetPosition();
|
||||
|
||||
auto closestDistance = 0.0f;
|
||||
|
||||
const auto candidates = Game::entityManager->GetEntitiesByComponent(component.value());
|
||||
|
||||
@@ -1520,6 +1522,9 @@ namespace DEVGMCommands {
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
closestDistance = NiPoint3::Distance(entity->GetPosition(), closest->GetPosition());
|
||||
}
|
||||
|
||||
if (!closest) return;
|
||||
|
||||
|
@@ -80,7 +80,7 @@ These commands are primarily for development and testing. The usage of many of t
|
||||
|getnavmeshheight|`/getnavmeshheight`|Displays the navmesh height at your current position.|8|
|
||||
|giveuscore|`/giveuscore <uscore>`|Gives uscore.|8|
|
||||
|gmadditem|`/gmadditem <id> (count)`|Adds the given item to your inventory by id.|8|
|
||||
|inspect|`/inspect <component> (-m <waypoint> \| -a <animation> \| -s \| -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. See [Detailed `/inspect` Usage](#detailed-inspect-usage) below.|8|
|
||||
|inspect|`/inspect <component or ldf variable or player name> (-m <waypoint> \| -a <animation> \| -s \| -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. See [Detailed `/inspect` Usage](#detailed-inspect-usage) below.|8|
|
||||
|list-spawns|`/list-spawns`|Lists all the character spawn points in the zone. Additionally, this command will display the current scene that plays when the character lands in the next zone, if there is one.|8|
|
||||
|locrow|`/locrow`|Prints the your current position and rotation information to the console.|8|
|
||||
|lookup|`/lookup <query>`|Searches through the Objects table in the client SQLite database for items whose display name, name, or description contains the query. Query can be multiple words delimited by spaces.|8|
|
||||
|
Reference in New Issue
Block a user