Compare commits

..

1 Commits

Author SHA1 Message Date
David Markowitz
22eb92e570 fix: hardcore mode fixes
fixes hardcore modes uscore drops
adds config option for excluded item drops

f

feat: Add logging for config options on load and reload

feat: Add logging for config options on load and reload
2025-09-21 16:26:15 -07:00
221 changed files with 1830 additions and 5516 deletions

View File

@@ -1,29 +0,0 @@
# GitHub Copilot Instructions
* c++20 standard, please use the latest features except NO modules.
* use `.contains` for searching in associative containers
* use const as much as possible. If it can be const, it should be made const
* DO NOT USE const_cast EVER.
* use `cstdint` bitwidth types ALWAYS for integral types.
* NEVER use std::wstring. If wide strings are necessary, use std::u16string with conversion utilties in GeneralUtils.h.
* Functions are ALWAYS PascalCase.
* local variables are camelCase
* NEVER use snake case
* indentation is TABS, not SPACES.
* TABS are 4 spaces by default
* Use trailing braces ALWAYS
* global variables are prefixed with `g_`
* if global variables or functions are needed, they should be located in an anonymous namespace
* Use `GeneralUtils::TryParse` for ANY parsing of strings to integrals.
* Use brace initialization when possible.
* ALWAYS default initialize variables.
* Pointers should be avoided unless necessary. Use references when the pointer has been checked and should not be null
* headers should be as compact as possible. Do NOT include extra data that isnt needed.
* Remember to include logs (LOG macro uses printf style logging) while putting verbose logs under LOG_DEBUG.
* NEVER USE `RakNet::BitStream::ReadBit`
* NEVER assume pointers are good, always check if they are null. Once a pointer is checked and is known to be non-null, further accesses no longer need checking
* Be wary of TOCTOU. Prevent all possible issues relating to TOCTOU.
* new memory allocations should never be used unless absolutely necessary.
* new for reconstruction of objects is allowed
* Prefer following the format of the file over correct formatting. Consistency over correctness.
* When using auto, ALWAYS put a * for pointers.

View File

@@ -3,7 +3,7 @@
// C++
#include <charconv>
#include <cstdint>
#include <cmath>
#include <cmath>
#include <ctime>
#include <functional>
#include <optional>
@@ -19,7 +19,6 @@
#include "dPlatforms.h"
#include "Game.h"
#include "Logger.h"
#include "DluAssert.h"
#include <glm/ext/vector_float3.hpp>
@@ -306,7 +305,7 @@ namespace GeneralUtils {
template<typename Container>
inline Container::value_type GetRandomElement(const Container& container) {
DluAssert(!container.empty());
return container[GenerateRandomNumber<typename Container::size_type>(0, container.size() - 1)];
return container[GenerateRandomNumber<typename Container::value_type>(0, container.size() - 1)];
}
/**

View File

@@ -5,43 +5,13 @@
#include "TinyXmlUtils.h"
#include <ranges>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include <sstream>
namespace {
// The base LXFML xml file to use when creating new models.
std::string g_base = R"(<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<LXFML versionMajor="5" versionMinor="0">
<Meta>
<Application name="LEGO Universe" versionMajor="0" versionMinor="0"/>
<Brand name="LEGOUniverse"/>
<BrickSet version="457"/>
</Meta>
<Bricks>
</Bricks>
<RigidSystems>
</RigidSystems>
<GroupSystems>
<GroupSystem>
</GroupSystem>
</GroupSystems>
</LXFML>)";
}
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoint3& curPosition) {
Result toReturn;
// Handle empty or invalid input
if (data.empty()) {
return toReturn;
}
tinyxml2::XMLDocument doc;
// Use length-based parsing to avoid expensive string copy
const auto err = doc.Parse(data.data(), data.size());
const auto err = doc.Parse(data.data());
if (err != tinyxml2::XML_SUCCESS) {
LOG("Failed to parse xml %s.", StringifiedEnum::ToString(err).data());
return toReturn;
}
@@ -50,6 +20,7 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
auto lxfml = reader["LXFML"];
if (!lxfml) {
LOG("Failed to find LXFML element.");
return toReturn;
}
@@ -78,19 +49,16 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
// Calculate the lowest and highest points on the entire model
for (const auto& transformation : transformations | std::views::values) {
auto split = GeneralUtils::SplitString(transformation, ',');
if (split.size() < 12) continue;
auto xOpt = GeneralUtils::TryParse<float>(split[9]);
auto yOpt = GeneralUtils::TryParse<float>(split[10]);
auto zOpt = GeneralUtils::TryParse<float>(split[11]);
if (!xOpt.has_value() || !yOpt.has_value() || !zOpt.has_value()) continue;
auto x = xOpt.value();
auto y = yOpt.value();
auto z = zOpt.value();
if (x < lowest.x) lowest.x = x;
if (y < lowest.y) lowest.y = y;
if (split.size() < 12) {
LOG("Not enough in the split?");
continue;
}
auto x = GeneralUtils::TryParse<float>(split[9]).value();
auto y = GeneralUtils::TryParse<float>(split[10]).value();
auto z = GeneralUtils::TryParse<float>(split[11]).value();
if (x < lowest.x) lowest.x = x;
if (y < lowest.y) lowest.y = y;
if (z < lowest.z) lowest.z = z;
if (highest.x < x) highest.x = x;
@@ -119,19 +87,13 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
for (auto& transformation : transformations | std::views::values) {
auto split = GeneralUtils::SplitString(transformation, ',');
if (split.size() < 12) {
LOG("Not enough in the split?");
continue;
}
auto xOpt = GeneralUtils::TryParse<float>(split[9]);
auto yOpt = GeneralUtils::TryParse<float>(split[10]);
auto zOpt = GeneralUtils::TryParse<float>(split[11]);
if (!xOpt.has_value() || !yOpt.has_value() || !zOpt.has_value()) {
continue;
}
auto x = xOpt.value() - newRootPos.x + curPosition.x;
auto y = yOpt.value() - newRootPos.y + curPosition.y;
auto z = zOpt.value() - newRootPos.z + curPosition.z;
auto x = GeneralUtils::TryParse<float>(split[9]).value() - newRootPos.x + curPosition.x;
auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y + curPosition.y;
auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z + curPosition.z;
std::stringstream stream;
for (int i = 0; i < 9; i++) {
stream << split[i];
@@ -166,345 +128,3 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
toReturn.center = newRootPos;
return toReturn;
}
// Deep-clone an XMLElement (attributes, text, and child elements) into a target document
// with maximum depth protection to prevent infinite loops
static tinyxml2::XMLElement* CloneElementDeep(const tinyxml2::XMLElement* src, tinyxml2::XMLDocument& dstDoc, int maxDepth = 100) {
if (!src || maxDepth <= 0) return nullptr;
auto* dst = dstDoc.NewElement(src->Name());
// copy attributes
for (const tinyxml2::XMLAttribute* attr = src->FirstAttribute(); attr; attr = attr->Next()) {
dst->SetAttribute(attr->Name(), attr->Value());
}
// copy children (elements and text)
for (const tinyxml2::XMLNode* child = src->FirstChild(); child; child = child->NextSibling()) {
if (const tinyxml2::XMLElement* childElem = child->ToElement()) {
// Recursively clone child elements with decremented depth
auto* clonedChild = CloneElementDeep(childElem, dstDoc, maxDepth - 1);
if (clonedChild) dst->InsertEndChild(clonedChild);
} else if (const tinyxml2::XMLText* txt = child->ToText()) {
auto* n = dstDoc.NewText(txt->Value());
dst->InsertEndChild(n);
} else if (const tinyxml2::XMLComment* c = child->ToComment()) {
auto* n = dstDoc.NewComment(c->Value());
dst->InsertEndChild(n);
}
}
return dst;
}
std::vector<Lxfml::Result> Lxfml::Split(const std::string_view data, const NiPoint3& curPosition) {
std::vector<Result> results;
// Handle empty or invalid input
if (data.empty()) {
return results;
}
// Prevent processing extremely large inputs that could cause hangs
if (data.size() > 10000000) { // 10MB limit
return results;
}
tinyxml2::XMLDocument doc;
// Use length-based parsing to avoid expensive string copy
const auto err = doc.Parse(data.data(), data.size());
if (err != tinyxml2::XML_SUCCESS) {
return results;
}
auto* lxfml = doc.FirstChildElement("LXFML");
if (!lxfml) {
return results;
}
// Build maps: partRef -> Part element, partRef -> Brick element, boneRef -> partRef, brickRef -> Brick element
std::unordered_map<std::string, tinyxml2::XMLElement*> partRefToPart;
std::unordered_map<std::string, tinyxml2::XMLElement*> partRefToBrick;
std::unordered_map<std::string, std::string> boneRefToPartRef;
std::unordered_map<std::string, tinyxml2::XMLElement*> brickByRef;
auto* bricksParent = lxfml->FirstChildElement("Bricks");
if (bricksParent) {
for (auto* brick = bricksParent->FirstChildElement("Brick"); brick; brick = brick->NextSiblingElement("Brick")) {
const char* brickRef = brick->Attribute("refID");
if (brickRef) brickByRef.emplace(std::string(brickRef), brick);
for (auto* part = brick->FirstChildElement("Part"); part; part = part->NextSiblingElement("Part")) {
const char* partRef = part->Attribute("refID");
if (partRef) {
partRefToPart.emplace(std::string(partRef), part);
partRefToBrick.emplace(std::string(partRef), brick);
}
auto* bone = part->FirstChildElement("Bone");
if (bone) {
const char* boneRef = bone->Attribute("refID");
if (boneRef) boneRefToPartRef.emplace(std::string(boneRef), partRef ? std::string(partRef) : std::string());
}
}
}
}
// Collect RigidSystem elements
std::vector<tinyxml2::XMLElement*> rigidSystems;
auto* rigidSystemsParent = lxfml->FirstChildElement("RigidSystems");
if (rigidSystemsParent) {
for (auto* rs = rigidSystemsParent->FirstChildElement("RigidSystem"); rs; rs = rs->NextSiblingElement("RigidSystem")) {
rigidSystems.push_back(rs);
}
}
// Collect top-level groups (immediate children of GroupSystem)
std::vector<tinyxml2::XMLElement*> groupRoots;
auto* groupSystemsParent = lxfml->FirstChildElement("GroupSystems");
if (groupSystemsParent) {
for (auto* gs = groupSystemsParent->FirstChildElement("GroupSystem"); gs; gs = gs->NextSiblingElement("GroupSystem")) {
for (auto* group = gs->FirstChildElement("Group"); group; group = group->NextSiblingElement("Group")) {
groupRoots.push_back(group);
}
}
}
// Track used bricks and rigidsystems
std::unordered_set<std::string> usedBrickRefs;
std::unordered_set<tinyxml2::XMLElement*> usedRigidSystems;
// Track used groups to avoid processing them twice
std::unordered_set<tinyxml2::XMLElement*> usedGroups;
// Helper to create output document from sets of brick refs and rigidsystem pointers
auto makeOutput = [&](const std::unordered_set<std::string>& bricksToInclude, const std::vector<tinyxml2::XMLElement*>& rigidSystemsToInclude, const std::vector<tinyxml2::XMLElement*>& groupsToInclude = {}) {
tinyxml2::XMLDocument outDoc;
outDoc.Parse(g_base.c_str());
auto* outRoot = outDoc.FirstChildElement("LXFML");
auto* outBricks = outRoot->FirstChildElement("Bricks");
auto* outRigidSystems = outRoot->FirstChildElement("RigidSystems");
auto* outGroupSystems = outRoot->FirstChildElement("GroupSystems");
// clone and insert bricks
for (const auto& bref : bricksToInclude) {
auto it = brickByRef.find(bref);
if (it == brickByRef.end()) continue;
tinyxml2::XMLElement* cloned = CloneElementDeep(it->second, outDoc);
if (cloned) outBricks->InsertEndChild(cloned);
}
// clone and insert rigidsystems
for (auto* rsPtr : rigidSystemsToInclude) {
tinyxml2::XMLElement* cloned = CloneElementDeep(rsPtr, outDoc);
if (cloned) outRigidSystems->InsertEndChild(cloned);
}
// clone and insert group(s) if requested
if (outGroupSystems && !groupsToInclude.empty()) {
// clear default children
while (outGroupSystems->FirstChild()) outGroupSystems->DeleteChild(outGroupSystems->FirstChild());
// create a GroupSystem element and append requested groups
auto* newGS = outDoc.NewElement("GroupSystem");
for (auto* gptr : groupsToInclude) {
tinyxml2::XMLElement* clonedG = CloneElementDeep(gptr, outDoc);
if (clonedG) newGS->InsertEndChild(clonedG);
}
outGroupSystems->InsertEndChild(newGS);
}
// Print to string
tinyxml2::XMLPrinter printer;
outDoc.Print(&printer);
// Normalize position and compute center using existing helper
std::string xmlString = printer.CStr();
if (xmlString.size() > 5000000) { // 5MB limit for normalization
Result emptyResult;
emptyResult.lxfml = xmlString;
return emptyResult;
}
auto normalized = NormalizePosition(xmlString, curPosition);
return normalized;
};
// 1) Process groups (each top-level Group becomes one output; nested groups are included)
for (auto* groupRoot : groupRoots) {
// Skip if this group was already processed as part of another group
if (usedGroups.find(groupRoot) != usedGroups.end()) continue;
// Helper to collect all partRefs in a group's subtree
std::function<void(const tinyxml2::XMLElement*, std::unordered_set<std::string>&)> collectParts = [&](const tinyxml2::XMLElement* g, std::unordered_set<std::string>& partRefs) {
if (!g) return;
const char* partAttr = g->Attribute("partRefs");
if (partAttr) {
for (auto& tok : GeneralUtils::SplitString(partAttr, ',')) partRefs.insert(tok);
}
for (auto* child = g->FirstChildElement("Group"); child; child = child->NextSiblingElement("Group")) collectParts(child, partRefs);
};
// Collect all groups that need to be merged into this output
std::vector<tinyxml2::XMLElement*> groupsToInclude{ groupRoot };
usedGroups.insert(groupRoot);
// Build initial sets of bricks and boneRefs from the starting group
std::unordered_set<std::string> partRefs;
collectParts(groupRoot, partRefs);
std::unordered_set<std::string> bricksIncluded;
std::unordered_set<std::string> boneRefsIncluded;
for (const auto& pref : partRefs) {
auto pit = partRefToBrick.find(pref);
if (pit != partRefToBrick.end()) {
const char* bref = pit->second->Attribute("refID");
if (bref) bricksIncluded.insert(std::string(bref));
}
auto partIt = partRefToPart.find(pref);
if (partIt != partRefToPart.end()) {
auto* bone = partIt->second->FirstChildElement("Bone");
if (bone) {
const char* bref = bone->Attribute("refID");
if (bref) boneRefsIncluded.insert(std::string(bref));
}
}
}
// Iteratively include any RigidSystems that reference any boneRefsIncluded
// and check if those rigid systems' bricks span other groups
bool changed = true;
std::vector<tinyxml2::XMLElement*> rigidSystemsToInclude;
int maxIterations = 1000; // Safety limit to prevent infinite loops
int iteration = 0;
while (changed && iteration < maxIterations) {
changed = false;
iteration++;
// First, expand rigid systems based on current boneRefsIncluded
for (auto* rs : rigidSystems) {
if (usedRigidSystems.find(rs) != usedRigidSystems.end()) continue;
// parse boneRefs of this rigid system (from its <Rigid> children)
bool intersects = false;
std::vector<std::string> rsBoneRefs;
for (auto* rigid = rs->FirstChildElement("Rigid"); rigid; rigid = rigid->NextSiblingElement("Rigid")) {
const char* battr = rigid->Attribute("boneRefs");
if (!battr) continue;
for (auto& tok : GeneralUtils::SplitString(battr, ',')) {
rsBoneRefs.push_back(tok);
if (boneRefsIncluded.find(tok) != boneRefsIncluded.end()) intersects = true;
}
}
if (!intersects) continue;
// include this rigid system and all boneRefs it references
usedRigidSystems.insert(rs);
rigidSystemsToInclude.push_back(rs);
for (const auto& br : rsBoneRefs) {
boneRefsIncluded.insert(br);
auto bpIt = boneRefToPartRef.find(br);
if (bpIt != boneRefToPartRef.end()) {
auto partRef = bpIt->second;
auto pbIt = partRefToBrick.find(partRef);
if (pbIt != partRefToBrick.end()) {
const char* bref = pbIt->second->Attribute("refID");
if (bref && bricksIncluded.insert(std::string(bref)).second) changed = true;
}
}
}
}
// Second, check if the newly included bricks span any other groups
// If so, merge those groups into the current output
for (auto* otherGroup : groupRoots) {
if (usedGroups.find(otherGroup) != usedGroups.end()) continue;
// Collect partRefs from this other group
std::unordered_set<std::string> otherPartRefs;
collectParts(otherGroup, otherPartRefs);
// Check if any of these partRefs correspond to bricks we've already included
bool spansOtherGroup = false;
for (const auto& pref : otherPartRefs) {
auto pit = partRefToBrick.find(pref);
if (pit != partRefToBrick.end()) {
const char* bref = pit->second->Attribute("refID");
if (bref && bricksIncluded.find(std::string(bref)) != bricksIncluded.end()) {
spansOtherGroup = true;
break;
}
}
}
if (spansOtherGroup) {
// Merge this group into the current output
usedGroups.insert(otherGroup);
groupsToInclude.push_back(otherGroup);
changed = true;
// Add all partRefs, boneRefs, and bricks from this group
for (const auto& pref : otherPartRefs) {
auto pit = partRefToBrick.find(pref);
if (pit != partRefToBrick.end()) {
const char* bref = pit->second->Attribute("refID");
if (bref) bricksIncluded.insert(std::string(bref));
}
auto partIt = partRefToPart.find(pref);
if (partIt != partRefToPart.end()) {
auto* bone = partIt->second->FirstChildElement("Bone");
if (bone) {
const char* bref = bone->Attribute("refID");
if (bref) boneRefsIncluded.insert(std::string(bref));
}
}
}
}
}
}
if (iteration >= maxIterations) {
// Iteration limit reached, stop processing to prevent infinite loops
// The file is likely malformed, so just skip further processing
return results;
}
// include bricks from bricksIncluded into used set
for (const auto& b : bricksIncluded) usedBrickRefs.insert(b);
// make output doc and push result (include all merged groups' XML)
auto normalized = makeOutput(bricksIncluded, rigidSystemsToInclude, groupsToInclude);
results.push_back(normalized);
}
// 2) Process remaining RigidSystems (each becomes its own file)
for (auto* rs : rigidSystems) {
if (usedRigidSystems.find(rs) != usedRigidSystems.end()) continue;
std::unordered_set<std::string> bricksIncluded;
// collect boneRefs referenced by this rigid system
for (auto* rigid = rs->FirstChildElement("Rigid"); rigid; rigid = rigid->NextSiblingElement("Rigid")) {
const char* battr = rigid->Attribute("boneRefs");
if (!battr) continue;
for (auto& tok : GeneralUtils::SplitString(battr, ',')) {
auto bpIt = boneRefToPartRef.find(tok);
if (bpIt != boneRefToPartRef.end()) {
auto partRef = bpIt->second;
auto pbIt = partRefToBrick.find(partRef);
if (pbIt != partRefToBrick.end()) {
const char* bref = pbIt->second->Attribute("refID");
if (bref) bricksIncluded.insert(std::string(bref));
}
}
}
}
// mark used
for (const auto& b : bricksIncluded) usedBrickRefs.insert(b);
usedRigidSystems.insert(rs);
std::vector<tinyxml2::XMLElement*> rsVec{ rs };
auto normalized = makeOutput(bricksIncluded, rsVec);
results.push_back(normalized);
}
// 3) Any remaining bricks not included become their own files
for (const auto& [bref, brickPtr] : brickByRef) {
if (usedBrickRefs.find(bref) != usedBrickRefs.end()) continue;
std::unordered_set<std::string> bricksIncluded{ bref };
auto normalized = makeOutput(bricksIncluded, {});
results.push_back(normalized);
usedBrickRefs.insert(bref);
}
return results;
}

View File

@@ -6,7 +6,6 @@
#include <string>
#include <string_view>
#include <vector>
#include "NiPoint3.h"
@@ -19,7 +18,6 @@ namespace Lxfml {
// Normalizes a LXFML model to be positioned relative to its local 0, 0, 0 rather than a game worlds 0, 0, 0.
// Returns a struct of its new center and the updated LXFML containing these edits.
[[nodiscard]] Result NormalizePosition(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO);
[[nodiscard]] std::vector<Result> Split(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO);
// these are only for the migrations due to a bug in one of the implementations.
[[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data);

View File

@@ -1,34 +0,0 @@
#ifndef NICOLOR_H
#define NICOLOR_H
struct NiColor {
float m_Red;
float m_Green;
float m_Blue;
NiColor(float red, float green, float blue) : m_Red(red), m_Green(green), m_Blue(blue) {}
NiColor() : NiColor(0.0f, 0.0f, 0.0f) {}
/* reduce RGB files to grayscale, with or without alpha
* using the equation given in Poynton's ColorFAQ at
* <http://www.inforamp.net/~poynton/> // dead link
* Copyright (c) 1998-01-04 Charles Poynton poynton at inforamp.net
*
* Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
*
* We approximate this with
*
* Y = 0.21268 * R + 0.7151 * G + 0.07217 * B
*
* which can be expressed with integers as
*
* Y = (6969 * R + 23434 * G + 2365 * B)/32768
*
* The calculation is to be done in a linear colorspace.
*
* Other integer coefficents can be used via png_set_rgb_to_gray().
*/
float ToXYZ() const { return (m_Red * 0.212671f) + (m_Green * 0.71516f) + (m_Blue * 0.072169f); };
};
#endif // NICOLOR_H

View File

@@ -81,9 +81,6 @@ public:
[[nodiscard]]
AssetStream GetFile(const char* name) const;
[[nodiscard]]
AssetStream GetFile(const std::string& name) const { return GetFile(name.c_str()); };
private:
void LoadPackIndex();

View File

@@ -1,170 +0,0 @@
#ifndef SCENE_COLOR_H
#define SCENE_COLOR_H
#include "NiColor.h"
#include <vector>
class SceneColor {
public:
SceneColor() {
TEMPLATE_COLORS.resize(146);
// these are not random values, they are the actual template colors used by the game
TEMPLATE_COLORS[0] = NiColor(0.5019608f, 0.5019608f, 0.5019608f);
TEMPLATE_COLORS[1] = NiColor(1.0f, 0.0f, 0.0f);
TEMPLATE_COLORS[2] = NiColor(0.0f, 1.0f, 0.0f);
TEMPLATE_COLORS[3] = NiColor(0.0f, 0.0f, 1.0f);
TEMPLATE_COLORS[4] = NiColor(1.0f, 1.0f, 0.0f);
TEMPLATE_COLORS[5] = NiColor(1.0f, 0.0f, 1.0f);
TEMPLATE_COLORS[6] = NiColor(0.0f, 1.0f, 1.0f);
TEMPLATE_COLORS[7] = NiColor(0.5019608f, 0.0f, 1.0f);
TEMPLATE_COLORS[8] = NiColor(1.0f, 0.5019608f, 0.0f);
TEMPLATE_COLORS[9] = NiColor(1.0f, 0.5019608f, 0.5019608f);
TEMPLATE_COLORS[10] = NiColor(0.5019608f, 0.2509804f, 0.0f);
TEMPLATE_COLORS[11] = NiColor(0.5019608f, 0.0f, 0.2509804f);
TEMPLATE_COLORS[12] = NiColor(0.0f, 0.5019608f, 0.2509804f);
TEMPLATE_COLORS[13] = NiColor(0.2509804f, 0.0f, 0.5019608f);
TEMPLATE_COLORS[14] = NiColor(0.8745098f, 0.0f, 0.2509804f);
TEMPLATE_COLORS[15] = NiColor(0.2509804f, 0.8745098f, 0.5019608f);
TEMPLATE_COLORS[16] = NiColor(1.0f, 0.7490196f, 0.0f);
TEMPLATE_COLORS[17] = NiColor(1.0f, 0.2509804f, 0.0627451f);
TEMPLATE_COLORS[18] = NiColor(0.2509804f, 0.0f, 0.8745098f);
TEMPLATE_COLORS[19] = NiColor(0.7490196f, 0.0627451f, 0.0627451f);
TEMPLATE_COLORS[20] = NiColor(0.0627451f, 0.7490196f, 0.0627451f);
TEMPLATE_COLORS[21] = NiColor(1.0f, 0.5019608f, 1.0f);
TEMPLATE_COLORS[22] = NiColor(0.9372549f, 0.8705882f, 0.8039216f);
TEMPLATE_COLORS[23] = NiColor(0.8039216f, 0.5843138f, 0.4588235f);
TEMPLATE_COLORS[24] = NiColor(0.9921569f, 0.8509804f, 0.7098039f);
TEMPLATE_COLORS[25] = NiColor(0.4705882f, 0.8588235f, 0.8862745f);
TEMPLATE_COLORS[26] = NiColor(0.5294118f, 0.6627451f, 0.4196078f);
TEMPLATE_COLORS[27] = NiColor(1.0f, 0.6431373f, 0.454902f);
TEMPLATE_COLORS[28] = NiColor(0.9803922f, 0.9058824f, 0.7098039f);
TEMPLATE_COLORS[29] = NiColor(0.6235294f, 0.5058824f, 0.4392157f);
TEMPLATE_COLORS[30] = NiColor(0.9921569f, 0.4862745f, 0.4313726f);
TEMPLATE_COLORS[31] = NiColor(0.0f, 0.0f, 0.0f);
TEMPLATE_COLORS[32] = NiColor(0.6745098f, 0.8980392f, 0.9333333f);
TEMPLATE_COLORS[33] = NiColor(0.1215686f, 0.4588235f, 0.9960784f);
TEMPLATE_COLORS[34] = NiColor(0.6352941f, 0.6352941f, 0.8156863f);
TEMPLATE_COLORS[35] = NiColor(0.4f, 0.6f, 0.8f);
TEMPLATE_COLORS[36] = NiColor(0.05098039f, 0.5960785f, 0.7294118f);
TEMPLATE_COLORS[37] = NiColor(0.4509804f, 0.4f, 0.7411765f);
TEMPLATE_COLORS[38] = NiColor(0.8705882f, 0.3647059f, 0.5137255f);
TEMPLATE_COLORS[39] = NiColor(0.7960784f, 0.254902f, 0.3294118f);
TEMPLATE_COLORS[40] = NiColor(0.7058824f, 0.4039216f, 0.3019608f);
TEMPLATE_COLORS[41] = NiColor(1.0f, 0.4980392f, 0.2862745f);
TEMPLATE_COLORS[42] = NiColor(0.9176471f, 0.4941176f, 0.3647059f);
TEMPLATE_COLORS[43] = NiColor(0.6901961f, 0.7176471f, 0.7764706f);
TEMPLATE_COLORS[44] = NiColor(1.0f, 1.0f, 0.6f);
TEMPLATE_COLORS[45] = NiColor(0.1098039f, 0.827451f, 0.6352941f);
TEMPLATE_COLORS[46] = NiColor(1.0f, 0.6666667f, 0.8f);
TEMPLATE_COLORS[47] = NiColor(0.8666667f, 0.2666667f, 0.572549f);
TEMPLATE_COLORS[48] = NiColor(0.1137255f, 0.6745098f, 0.8392157f);
TEMPLATE_COLORS[49] = NiColor(0.7372549f, 0.3647059f, 0.345098f);
TEMPLATE_COLORS[50] = NiColor(0.8666667f, 0.5803922f, 0.4588235f);
TEMPLATE_COLORS[51] = NiColor(0.6039216f, 0.8078431f, 0.9215686f);
TEMPLATE_COLORS[52] = NiColor(1.0f, 0.7372549f, 0.8509804f);
TEMPLATE_COLORS[53] = NiColor(0.9921569f, 0.8588235f, 0.427451f);
TEMPLATE_COLORS[54] = NiColor(0.1686275f, 0.4235294f, 0.7686275f);
TEMPLATE_COLORS[55] = NiColor(0.9372549f, 0.8039216f, 0.7215686f);
TEMPLATE_COLORS[56] = NiColor(0.4313726f, 0.3176471f, 0.3764706f);
TEMPLATE_COLORS[57] = NiColor(0.8078431f, 1.0f, 0.1137255f);
TEMPLATE_COLORS[58] = NiColor(0.427451f, 0.682353f, 0.5058824f);
TEMPLATE_COLORS[59] = NiColor(0.7647059f, 0.3921569f, 0.772549f);
TEMPLATE_COLORS[60] = NiColor(0.8f, 0.4f, 0.4f);
TEMPLATE_COLORS[61] = NiColor(0.9058824f, 0.7764706f, 0.5921569f);
TEMPLATE_COLORS[62] = NiColor(0.9882353f, 0.8509804f, 0.4588235f);
TEMPLATE_COLORS[63] = NiColor(0.6588235f, 0.8941177f, 0.627451f);
TEMPLATE_COLORS[64] = NiColor(0.5843138f, 0.5686275f, 0.5490196f);
TEMPLATE_COLORS[65] = NiColor(0.1098039f, 0.6745098f, 0.4705882f);
TEMPLATE_COLORS[66] = NiColor(0.06666667f, 0.3921569f, 0.7058824f);
TEMPLATE_COLORS[67] = NiColor(0.9411765f, 0.9098039f, 0.5686275f);
TEMPLATE_COLORS[68] = NiColor(1.0f, 0.1137255f, 0.8078431f);
TEMPLATE_COLORS[69] = NiColor(0.6980392f, 0.9254902f, 0.3647059f);
TEMPLATE_COLORS[70] = NiColor(0.3647059f, 0.4627451f, 0.7960784f);
TEMPLATE_COLORS[71] = NiColor(0.7921569f, 0.2156863f, 0.4039216f);
TEMPLATE_COLORS[72] = NiColor(0.2313726f, 0.6901961f, 0.5607843f);
TEMPLATE_COLORS[73] = NiColor(0.9882353f, 0.7058824f, 0.8352941f);
TEMPLATE_COLORS[74] = NiColor(1.0f, 0.9568627f, 0.3098039f);
TEMPLATE_COLORS[75] = NiColor(1.0f, 0.7411765f, 0.5333334f);
TEMPLATE_COLORS[76] = NiColor(0.9647059f, 0.3921569f, 0.6862745f);
TEMPLATE_COLORS[77] = NiColor(0.6666667f, 0.9411765f, 0.8196079f);
TEMPLATE_COLORS[78] = NiColor(0.8039216f, 0.2901961f, 0.2980392f);
TEMPLATE_COLORS[79] = NiColor(0.9294118f, 0.8196079f, 0.6117647f);
TEMPLATE_COLORS[80] = NiColor(0.5921569f, 0.6039216f, 0.6666667f);
TEMPLATE_COLORS[81] = NiColor(0.7843137f, 0.2196078f, 0.3529412f);
TEMPLATE_COLORS[82] = NiColor(0.9372549f, 0.5960785f, 0.6666667f);
TEMPLATE_COLORS[83] = NiColor(0.9921569f, 0.7372549f, 0.7058824f);
TEMPLATE_COLORS[84] = NiColor(0.1019608f, 0.282353f, 0.4627451f);
TEMPLATE_COLORS[85] = NiColor(0.1882353f, 0.7294118f, 0.5607843f);
TEMPLATE_COLORS[86] = NiColor(0.772549f, 0.2941177f, 0.5490196f);
TEMPLATE_COLORS[87] = NiColor(0.09803922f, 0.454902f, 0.8235294f);
TEMPLATE_COLORS[88] = NiColor(0.7294118f, 0.7215686f, 0.4235294f);
TEMPLATE_COLORS[89] = NiColor(1.0f, 0.4588235f, 0.2196078f);
TEMPLATE_COLORS[90] = NiColor(1.0f, 0.1686275f, 0.1686275f);
TEMPLATE_COLORS[91] = NiColor(0.972549f, 0.8352941f, 0.4078431f);
TEMPLATE_COLORS[92] = NiColor(0.9019608f, 0.6588235f, 0.8431373f);
TEMPLATE_COLORS[93] = NiColor(0.254902f, 0.2901961f, 0.2980392f);
TEMPLATE_COLORS[94] = NiColor(1.0f, 0.4313726f, 0.2901961f);
TEMPLATE_COLORS[95] = NiColor(0.1098039f, 0.6627451f, 0.7882353f);
TEMPLATE_COLORS[96] = NiColor(1.0f, 0.8117647f, 0.6705883f);
TEMPLATE_COLORS[97] = NiColor(0.772549f, 0.8156863f, 0.9019608f);
TEMPLATE_COLORS[98] = NiColor(0.9921569f, 0.8666667f, 0.9019608f);
TEMPLATE_COLORS[99] = NiColor(0.08235294f, 0.5019608f, 0.4705882f);
TEMPLATE_COLORS[100] = NiColor(0.9882353f, 0.454902f, 0.9921569f);
TEMPLATE_COLORS[101] = NiColor(0.9686275f, 0.5607843f, 0.654902f);
TEMPLATE_COLORS[102] = NiColor(0.5568628f, 0.2705882f, 0.5215687f);
TEMPLATE_COLORS[103] = NiColor(0.454902f, 0.2588235f, 0.7843137f);
TEMPLATE_COLORS[104] = NiColor(0.6156863f, 0.5058824f, 0.7294118f);
TEMPLATE_COLORS[105] = NiColor(1.0f, 0.2862745f, 0.4235294f);
TEMPLATE_COLORS[106] = NiColor(0.8392157f, 0.5411765f, 0.3490196f);
TEMPLATE_COLORS[107] = NiColor(0.4431373f, 0.2941177f, 0.1372549f);
TEMPLATE_COLORS[108] = NiColor(1.0f, 0.282353f, 0.8156863f);
TEMPLATE_COLORS[109] = NiColor(0.9333333f, 0.1254902f, 0.3019608f);
TEMPLATE_COLORS[110] = NiColor(1.0f, 0.3254902f, 0.2862745f);
TEMPLATE_COLORS[111] = NiColor(0.7529412f, 0.2666667f, 0.5607843f);
TEMPLATE_COLORS[112] = NiColor(0.1215686f, 0.8078431f, 0.7960784f);
TEMPLATE_COLORS[113] = NiColor(0.4705882f, 0.3176471f, 0.6627451f);
TEMPLATE_COLORS[114] = NiColor(1.0f, 0.6078432f, 0.6666667f);
TEMPLATE_COLORS[115] = NiColor(0.9882353f, 0.1568628f, 0.2784314f);
TEMPLATE_COLORS[116] = NiColor(0.4627451f, 1.0f, 0.4784314f);
TEMPLATE_COLORS[117] = NiColor(0.6235294f, 0.8862745f, 0.7490196f);
TEMPLATE_COLORS[118] = NiColor(0.6470588f, 0.4117647f, 0.3098039f);
TEMPLATE_COLORS[119] = NiColor(0.5411765f, 0.4745098f, 0.3647059f);
TEMPLATE_COLORS[120] = NiColor(0.2705882f, 0.8078431f, 0.6352941f);
TEMPLATE_COLORS[121] = NiColor(0.8039216f, 0.772549f, 0.7607843f);
TEMPLATE_COLORS[122] = NiColor(0.5019608f, 0.854902f, 0.9215686f);
TEMPLATE_COLORS[123] = NiColor(0.9254902f, 0.9176471f, 0.7450981f);
TEMPLATE_COLORS[124] = NiColor(1.0f, 0.8117647f, 0.282353f);
TEMPLATE_COLORS[125] = NiColor(0.9921569f, 0.3686275f, 0.3254902f);
TEMPLATE_COLORS[126] = NiColor(0.9803922f, 0.654902f, 0.4235294f);
TEMPLATE_COLORS[127] = NiColor(0.09411765f, 0.654902f, 0.7098039f);
TEMPLATE_COLORS[128] = NiColor(0.9215686f, 0.7803922f, 0.8745098f);
TEMPLATE_COLORS[129] = NiColor(0.9882353f, 0.5372549f, 0.6745098f);
TEMPLATE_COLORS[130] = NiColor(0.8588235f, 0.8431373f, 0.8235294f);
TEMPLATE_COLORS[131] = NiColor(0.8705882f, 0.6666667f, 0.5333334f);
TEMPLATE_COLORS[132] = NiColor(0.4666667f, 0.8666667f, 0.9058824f);
TEMPLATE_COLORS[133] = NiColor(1.0f, 1.0f, 0.4f);
TEMPLATE_COLORS[134] = NiColor(0.572549f, 0.4313726f, 0.682353f);
TEMPLATE_COLORS[135] = NiColor(0.1960784f, 0.2901961f, 0.6980392f);
TEMPLATE_COLORS[136] = NiColor(0.9686275f, 0.3254902f, 0.5803922f);
TEMPLATE_COLORS[137] = NiColor(1.0f, 0.627451f, 0.5372549f);
TEMPLATE_COLORS[138] = NiColor(0.5607843f, 0.3137255f, 0.6156863f);
TEMPLATE_COLORS[139] = NiColor(1.0f, 1.0f, 1.0f);
TEMPLATE_COLORS[140] = NiColor(0.6352941f, 0.6784314f, 0.8156863f);
TEMPLATE_COLORS[141] = NiColor(0.9882353f, 0.4235294f, 0.5215687f);
TEMPLATE_COLORS[142] = NiColor(0.8039216f, 0.6431373f, 0.8705882f);
TEMPLATE_COLORS[143] = NiColor(0.9882353f, 0.9098039f, 0.5137255f);
TEMPLATE_COLORS[144] = NiColor(0.772549f, 0.8901961f, 0.5176471f);
TEMPLATE_COLORS[145] = NiColor(1.0f, 0.682353f, 0.2588235f);
}
const NiColor& Get(unsigned char index) const {
return (index < 146) ? TEMPLATE_COLORS[index] : FALLBACK_COLOR;
}
private:
const NiColor FALLBACK_COLOR = NiColor(1.0f, 1.0f, 1.0f);
std::vector<NiColor> TEMPLATE_COLORS;
};
#endif // SCENE_COLOR_H

View File

@@ -47,7 +47,6 @@ void dConfig::LoadConfig() {
void dConfig::ReloadConfig() {
this->m_ConfigValues.clear();
LoadConfig();
for (const auto& handler : m_ConfigHandlers) handler();
LogSettings();
}
@@ -60,10 +59,6 @@ const std::string& dConfig::GetValue(std::string key) {
return this->m_ConfigValues[key];
}
void dConfig::AddConfigHandler(std::function<void()> handler) {
m_ConfigHandlers.push_back(handler);
}
void dConfig::LogSettings() const {
LOG("Configuration settings:");
for (const auto& [key, value] : m_ConfigValues) {

View File

@@ -1,7 +1,5 @@
#pragma once
#include <fstream>
#include <functional>
#include <map>
#include <string>
@@ -32,14 +30,11 @@ public:
*/
void ReloadConfig();
// Adds a function to be called when the config is (re)loaded
void AddConfigHandler(std::function<void()> handler);
void LogSettings() const;
private:
void ProcessLine(const std::string& line);
std::map<std::string, std::string> m_ConfigValues;
std::vector<std::function<void()>> m_ConfigHandlers;
std::string m_ConfigFilePath;
};

View File

@@ -3,7 +3,9 @@
namespace MessageType {
enum class Master : uint32_t {
REQUEST_ZONE_TRANSFER = 1,
REQUEST_PERSISTENT_ID = 1,
REQUEST_PERSISTENT_ID_RESPONSE,
REQUEST_ZONE_TRANSFER,
REQUEST_ZONE_TRANSFER_RESPONSE,
SERVER_INFO,
REQUEST_SESSION_KEY,

View File

@@ -18,9 +18,7 @@ enum class eCharacterVersion : uint32_t {
SPEED_BASE,
// Fixes nexus force explorer missions
NJ_JAYMISSIONS,
NEXUS_FORCE_EXPLORER, // Fixes pet ids in player inventories
PET_IDS, // Fixes pet ids in player inventories
UP_TO_DATE, // will become INVENTORY_PERSISTENT_IDS
UP_TO_DATE, // will become NEXUS_FORCE_EXPLORER
};
#endif //!__ECHARACTERVERSION__H__

View File

@@ -50,10 +50,7 @@ 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,
// The mission is failed (don't know where this is used)
FAILED = 16,
COMPLETE_READY_TO_COMPLETE = 12
};
#endif //!__MISSIONSTATE__H__

View File

@@ -1,12 +1,13 @@
#ifndef EOBJECTBITS_H
#define EOBJECTBITS_H
#ifndef __EOBJECTBITS__H__
#define __EOBJECTBITS__H__
#include <cstdint>
enum class eObjectBits : size_t {
PERSISTENT = 32,
CLIENT = 46,
SPAWNED = 58,
CHARACTER = 60
};
#endif //!EOBJECTBITS_H
#endif //!__EOBJECTBITS__H__

View File

@@ -6,19 +6,14 @@
class IObjectIdTracker {
public:
// Only the first 48 bits of the ids are the id, the last 16 bits are reserved for flags.
struct Range {
uint64_t minID{}; // Only the first 48 bits are the id, the last 16 bits are reserved for flags.
uint64_t maxID{}; // Only the first 48 bits are the id, the last 16 bits are reserved for flags.
};
// Get the current persistent id.
virtual std::optional<uint64_t> GetCurrentPersistentId() = 0;
virtual std::optional<uint32_t> GetCurrentPersistentId() = 0;
// Insert the default persistent id.
virtual void InsertDefaultPersistentId() = 0;
virtual Range GetPersistentIdRange() = 0;
// Update the persistent id.
virtual void UpdatePersistentId(const uint32_t newId) = 0;
};
#endif //!__IOBJECTIDTRACKER__H__

View File

@@ -39,9 +39,6 @@ public:
std::vector<IProperty::Info> entries;
};
// Get the property info for the given property id.
virtual std::optional<IProperty::Info> GetPropertyInfo(const LWOOBJID id) = 0;
// Get the property info for the given property id.
virtual std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) = 0;

View File

@@ -16,14 +16,14 @@ public:
NiQuaternion rotation = QuatUtils::IDENTITY;
LWOOBJID id{};
LOT lot{};
LWOOBJID ugcId{};
uint32_t ugcId{};
std::array<LWOOBJID, 5> behaviors{};
};
// Inserts a new UGC model into the database.
virtual void InsertNewUgcModel(
std::stringstream& sd0Data,
const uint64_t blueprintId,
const uint32_t blueprintId,
const uint32_t accountId,
const LWOOBJID characterId) = 0;
@@ -45,6 +45,6 @@ public:
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
// Gets a model by ID
virtual std::optional<Model> GetModel(const LWOOBJID modelID) = 0;
virtual Model GetModel(const LWOOBJID modelID) = 0;
};
#endif //!__IPROPERTIESCONTENTS__H__

View File

@@ -29,7 +29,5 @@ public:
// Inserts a new UGC model into the database.
virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) = 0;
virtual std::optional<IUgc::Model> GetUgcModel(const LWOOBJID ugcId) = 0;
};
#endif //!__IUGC__H__

View File

@@ -83,7 +83,7 @@ public:
void InsertNewMail(const MailInfo& mail) override;
void InsertNewUgcModel(
std::stringstream& sd0Data,
const uint64_t blueprintId,
const uint32_t blueprintId,
const uint32_t accountId,
const LWOOBJID characterId) override;
std::vector<MailInfo> GetMailForPlayer(const LWOOBJID characterId, const uint32_t numberOfMail) override;
@@ -98,9 +98,9 @@ public:
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint64_t> GetCurrentPersistentId() override;
IObjectIdTracker::Range GetPersistentIdRange() override;
std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;
std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override;
std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override;
std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override;
@@ -127,9 +127,7 @@ public:
void DeleteUgcBuild(const LWOOBJID bigId) override;
uint32_t GetAccountCount() override;
bool IsNameInUse(const std::string_view name) override;
std::optional<IPropertyContents::Model> GetModel(const LWOOBJID modelID) override;
std::optional<IUgc::Model> GetUgcModel(const LWOOBJID ugcId) override;
std::optional<IProperty::Info> GetPropertyInfo(const LWOOBJID id) override;
IPropertyContents::Model GetModel(const LWOOBJID modelID) override;
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
private:
@@ -170,91 +168,91 @@ private:
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string_view param) {
LOG_DEBUG("%s", param.data());
// LOG("%s", param.data());
stmt->setString(index, param.data());
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const char* param) {
LOG_DEBUG("%s", param);
// LOG("%s", param);
stmt->setString(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string param) {
LOG_DEBUG("%s", param.c_str());
// LOG("%s", param.c_str());
stmt->setString(index, param.c_str());
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int8_t param) {
LOG_DEBUG("%u", param);
// LOG("%u", param);
stmt->setByte(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint8_t param) {
LOG_DEBUG("%d", param);
// LOG("%d", param);
stmt->setByte(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int16_t param) {
LOG_DEBUG("%u", param);
// LOG("%u", param);
stmt->setShort(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint16_t param) {
LOG_DEBUG("%d", param);
// LOG("%d", param);
stmt->setShort(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint32_t param) {
LOG_DEBUG("%u", param);
// LOG("%u", param);
stmt->setUInt(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int32_t param) {
LOG_DEBUG("%d", param);
// LOG("%d", param);
stmt->setInt(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int64_t param) {
LOG_DEBUG("%llu", param);
// LOG("%llu", param);
stmt->setInt64(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint64_t param) {
LOG_DEBUG("%llu", param);
// LOG("%llu", param);
stmt->setUInt64(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const float param) {
LOG_DEBUG("%f", param);
// LOG("%f", param);
stmt->setFloat(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const double param) {
LOG_DEBUG("%f", param);
// LOG("%f", param);
stmt->setDouble(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const bool param) {
LOG_DEBUG("%s", param ? "true" : "false");
// LOG("%d", param);
stmt->setBoolean(index, param);
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::istream* param) {
LOG_DEBUG("Blob");
// LOG("Blob");
// This is the one time you will ever see me use const_cast.
stmt->setBlob(index, const_cast<std::istream*>(param));
}
@@ -262,10 +260,10 @@ inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::istr
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::optional<uint32_t> param) {
if (param) {
LOG_DEBUG("%d", param.value());
// LOG("%d", param.value());
stmt->setInt(index, param.value());
} else {
LOG_DEBUG("Null");
// LOG("Null");
stmt->setNull(index, sql::DataType::SQLNULL);
}
}
@@ -273,10 +271,10 @@ inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::opti
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::optional<LWOOBJID> param) {
if (param) {
LOG_DEBUG("%d", param.value());
// LOG("%d", param.value());
stmt->setInt64(index, param.value());
} else {
LOG_DEBUG("Null");
// LOG("Null");
stmt->setNull(index, sql::DataType::SQLNULL);
}
}

View File

@@ -1,42 +1,17 @@
#include "MySQLDatabase.h"
std::optional<uint64_t> MySQLDatabase::GetCurrentPersistentId() {
std::optional<uint32_t> MySQLDatabase::GetCurrentPersistentId() {
auto result = ExecuteSelect("SELECT last_object_id FROM object_id_tracker");
if (!result->next()) {
return std::nullopt;
}
return result->getUInt64("last_object_id");
return result->getUInt("last_object_id");
}
void MySQLDatabase::InsertDefaultPersistentId() {
ExecuteInsert("INSERT INTO object_id_tracker VALUES (1);");
}
IObjectIdTracker::Range MySQLDatabase::GetPersistentIdRange() {
IObjectIdTracker::Range range;
auto prevCommit = GetAutoCommit();
SetAutoCommit(false);
// THIS MUST ABSOLUTELY NOT FAIL. These IDs are expected to be unique. As such a transactional select is used to safely get a range
// of IDs that will never be used again. A separate feature could track unused IDs and recycle them, but that is not implemented.
ExecuteCustomQuery("START TRANSACTION;");
// 200 seems like a good range to reserve at once. Only way this would be noticable is if a player
// added hundreds of items at once.
// Doing the update first ensures that all other connections are blocked from accessing this table until we commit.
auto result = ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = last_object_id + 200;");
// If no rows were updated, it means the table is empty, so we need to insert the default row first.
if (result == 0) {
InsertDefaultPersistentId();
result = ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = last_object_id + 200;");
}
// At this point all connections are waiting on us to finish the transaction, so we can safely select the ID we just set.
auto selectRes = ExecuteSelect("SELECT last_object_id FROM object_id_tracker;");
selectRes->next();
range.maxID = selectRes->getUInt64("last_object_id");
range.minID = range.maxID - 199;
ExecuteCustomQuery("COMMIT;");
SetAutoCommit(prevCommit);
return range;
void MySQLDatabase::UpdatePersistentId(const uint32_t newId) {
ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = ?;", newId);
}

View File

@@ -1,23 +1,6 @@
#include "MySQLDatabase.h"
#include "ePropertySortType.h"
IProperty::Info ReadPropertyInfo(UniqueResultSet& result) {
IProperty::Info info;
info.id = result->getUInt64("id");
info.ownerId = result->getInt64("owner_id");
info.cloneId = result->getUInt64("clone_id");
info.name = result->getString("name").c_str();
info.description = result->getString("description").c_str();
info.privacyOption = result->getInt("privacy_option");
info.rejectionReason = result->getString("rejection_reason").c_str();
info.lastUpdatedTime = result->getUInt("last_updated");
info.claimedTime = result->getUInt("time_claimed");
info.reputation = result->getUInt("reputation");
info.modApproved = result->getUInt("mod_approved");
info.performanceCost = result->getFloat("performance_cost");
return info;
}
std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(const IProperty::PropertyLookup& params) {
std::optional<IProperty::PropertyEntranceResult> result;
std::string query;
@@ -134,7 +117,19 @@ std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(co
while (properties->next()) {
if (!result) result = IProperty::PropertyEntranceResult();
result->entries.push_back(ReadPropertyInfo(properties));
auto& entry = result->entries.emplace_back();
entry.id = properties->getUInt64("id");
entry.ownerId = properties->getInt64("owner_id");
entry.cloneId = properties->getUInt64("clone_id");
entry.name = properties->getString("name").c_str();
entry.description = properties->getString("description").c_str();
entry.privacyOption = properties->getInt("privacy_option");
entry.rejectionReason = properties->getString("rejection_reason").c_str();
entry.lastUpdatedTime = properties->getUInt("last_updated");
entry.claimedTime = properties->getUInt("time_claimed");
entry.reputation = properties->getUInt("reputation");
entry.modApproved = properties->getUInt("mod_approved");
entry.performanceCost = properties->getFloat("performance_cost");
}
return result;
@@ -149,7 +144,21 @@ std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID map
return std::nullopt;
}
return ReadPropertyInfo(propertyEntry);
IProperty::Info toReturn;
toReturn.id = propertyEntry->getUInt64("id");
toReturn.ownerId = propertyEntry->getInt64("owner_id");
toReturn.cloneId = propertyEntry->getUInt64("clone_id");
toReturn.name = propertyEntry->getString("name").c_str();
toReturn.description = propertyEntry->getString("description").c_str();
toReturn.privacyOption = propertyEntry->getInt("privacy_option");
toReturn.rejectionReason = propertyEntry->getString("rejection_reason").c_str();
toReturn.lastUpdatedTime = propertyEntry->getUInt("last_updated");
toReturn.claimedTime = propertyEntry->getUInt("time_claimed");
toReturn.reputation = propertyEntry->getUInt("reputation");
toReturn.modApproved = propertyEntry->getUInt("mod_approved");
toReturn.performanceCost = propertyEntry->getFloat("performance_cost");
return toReturn;
}
void MySQLDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) {
@@ -186,15 +195,3 @@ void MySQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_
zoneId.GetMapID()
);
}
std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOOBJID id) {
auto propertyEntry = ExecuteSelect(
"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved, performance_cost "
"FROM properties WHERE id = ?;", id);
if (!propertyEntry->next()) {
return std::nullopt;
}
return ReadPropertyInfo(propertyEntry);
}

View File

@@ -64,27 +64,26 @@ void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId);
}
std::optional<IPropertyContents::Model> MySQLDatabase::GetModel(const LWOOBJID modelID) {
IPropertyContents::Model MySQLDatabase::GetModel(const LWOOBJID modelID) {
auto result = ExecuteSelect("SELECT * FROM properties_contents WHERE id = ?", modelID);
std::optional<IPropertyContents::Model> model = std::nullopt;
IPropertyContents::Model model{};
while (result->next()) {
model = IPropertyContents::Model{};
model->id = result->getUInt64("id");
model->lot = static_cast<LOT>(result->getUInt("lot"));
model->position.x = result->getFloat("x");
model->position.y = result->getFloat("y");
model->position.z = result->getFloat("z");
model->rotation.w = result->getFloat("rw");
model->rotation.x = result->getFloat("rx");
model->rotation.y = result->getFloat("ry");
model->rotation.z = result->getFloat("rz");
model->ugcId = result->getUInt64("ugc_id");
model->behaviors[0] = result->getUInt64("behavior_1");
model->behaviors[1] = result->getUInt64("behavior_2");
model->behaviors[2] = result->getUInt64("behavior_3");
model->behaviors[3] = result->getUInt64("behavior_4");
model->behaviors[4] = result->getUInt64("behavior_5");
model.id = result->getUInt64("id");
model.lot = static_cast<LOT>(result->getUInt("lot"));
model.position.x = result->getFloat("x");
model.position.y = result->getFloat("y");
model.position.z = result->getFloat("z");
model.rotation.w = result->getFloat("rw");
model.rotation.x = result->getFloat("rx");
model.rotation.y = result->getFloat("ry");
model.rotation.z = result->getFloat("rz");
model.ugcId = result->getUInt64("ugc_id");
model.behaviors[0] = result->getUInt64("behavior_1");
model.behaviors[1] = result->getUInt64("behavior_2");
model.behaviors[2] = result->getUInt64("behavior_3");
model.behaviors[3] = result->getUInt64("behavior_4");
model.behaviors[4] = result->getUInt64("behavior_5");
}
return model;

View File

@@ -1,17 +1,5 @@
#include "MySQLDatabase.h"
IUgc::Model ReadModel(UniqueResultSet& result) {
IUgc::Model model;
// blob is owned by the query, so we need to do a deep copy :/
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
model.lxfmlData << blob->rdbuf();
model.id = result->getUInt64("ugcID");
model.modelID = result->getUInt64("modelID");
return model;
}
std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) {
auto result = ExecuteSelect(
"SELECT lxfml, u.id as ugcID, pc.id as modelID FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;",
@@ -20,7 +8,14 @@ std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId)
std::vector<IUgc::Model> toReturn;
while (result->next()) {
toReturn.push_back(ReadModel(result));
IUgc::Model model;
// blob is owned by the query, so we need to do a deep copy :/
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
model.lxfmlData << blob->rdbuf();
model.id = result->getUInt64("ugcID");
model.modelID = result->getUInt64("modelID");
toReturn.push_back(std::move(model));
}
return toReturn;
@@ -32,7 +27,14 @@ std::vector<IUgc::Model> MySQLDatabase::GetAllUgcModels() {
std::vector<IUgc::Model> models;
models.reserve(result->rowsCount());
while (result->next()) {
models.push_back(ReadModel(result));
IUgc::Model model;
model.id = result->getInt64("ugcID");
model.modelID = result->getUInt64("modelID");
// blob is owned by the query, so we need to do a deep copy :/
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
model.lxfmlData << blob->rdbuf();
models.push_back(std::move(model));
}
return models;
@@ -43,8 +45,8 @@ void MySQLDatabase::RemoveUnreferencedUgcModels() {
}
void MySQLDatabase::InsertNewUgcModel(
std::stringstream& sd0Data, // cant be const sad
const uint64_t blueprintId,
std:: stringstream& sd0Data, // cant be const sad
const uint32_t blueprintId,
const uint32_t accountId,
const LWOOBJID characterId) {
const std::istream stream(sd0Data.rdbuf());
@@ -69,14 +71,3 @@ void MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstrea
const std::istream stream(lxfml.rdbuf());
ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId);
}
std::optional<IUgc::Model> MySQLDatabase::GetUgcModel(const LWOOBJID ugcId) {
auto result = ExecuteSelect("SELECT u.id AS ugcID, lxfml, pc.id AS modelID FROM ugc AS u JOIN properties_contents AS pc ON pc.ugc_id = u.id WHERE u.id = ?", ugcId);
std::optional<IUgc::Model> toReturn = std::nullopt;
if (result->next()) {
toReturn = ReadModel(result);
}
return toReturn;
}

View File

@@ -61,9 +61,9 @@ bool SQLiteDatabase::GetAutoCommit() {
void SQLiteDatabase::SetAutoCommit(bool value) {
if (value) {
if (!GetAutoCommit()) con->compileStatement("COMMIT;").execDML();
} else {
if (GetAutoCommit()) con->compileStatement("BEGIN;").execDML();
} else {
if (!GetAutoCommit()) con->compileStatement("COMMIT;").execDML();
}
}

View File

@@ -81,7 +81,7 @@ public:
void InsertNewMail(const MailInfo& mail) override;
void InsertNewUgcModel(
std::stringstream& sd0Data,
const uint64_t blueprintId,
const uint32_t blueprintId,
const uint32_t accountId,
const LWOOBJID characterId) override;
std::vector<MailInfo> GetMailForPlayer(const LWOOBJID characterId, const uint32_t numberOfMail) override;
@@ -96,9 +96,9 @@ public:
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint64_t> GetCurrentPersistentId() override;
IObjectIdTracker::Range GetPersistentIdRange() override;
std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;
std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override;
std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override;
std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override;
@@ -125,9 +125,7 @@ public:
void DeleteUgcBuild(const LWOOBJID bigId) override;
uint32_t GetAccountCount() override;
bool IsNameInUse(const std::string_view name) override;
std::optional<IPropertyContents::Model> GetModel(const LWOOBJID modelID) override;
std::optional<IUgc::Model> GetUgcModel(const LWOOBJID ugcId) override;
std::optional<IProperty::Info> GetPropertyInfo(const LWOOBJID id) override;
IPropertyContents::Model GetModel(const LWOOBJID modelID) override;
private:
CppSQLite3Statement CreatePreppedStmt(const std::string& query);

View File

@@ -1,40 +1,17 @@
#include "SQLiteDatabase.h"
std::optional<uint64_t> SQLiteDatabase::GetCurrentPersistentId() {
std::optional<uint32_t> SQLiteDatabase::GetCurrentPersistentId() {
auto [_, result] = ExecuteSelect("SELECT last_object_id FROM object_id_tracker");
if (result.eof()) {
return std::nullopt;
}
return result.getInt64Field("last_object_id");
return result.getIntField("last_object_id");
}
void SQLiteDatabase::InsertDefaultPersistentId() {
ExecuteInsert("INSERT INTO object_id_tracker VALUES (1);");
}
IObjectIdTracker::Range SQLiteDatabase::GetPersistentIdRange() {
IObjectIdTracker::Range range;
auto prevCommit = GetAutoCommit();
SetAutoCommit(false); // This begins the transaction for us if one is not already in progress
// THIS MUST ABSOLUTELY NOT FAIL. These IDs are expected to be unique. As such a transactional select is used to safely get a range
// of IDs that will never be used again. A separate feature could track unused IDs and recycle them, but that is not implemented.
// 200 seems like a good range to reserve at once. Only way this would be noticable is if a player
// added hundreds of items at once.
// Doing the update first ensures that all other connections are blocked from accessing this table until we commit.
auto result = ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = last_object_id + 200;");
if (result == 0) {
InsertDefaultPersistentId();
result = ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = last_object_id + 200;");
}
// At this point all connections are waiting on us to finish the transaction, so we can safely select the ID we just set.
auto [_, selectResult] = ExecuteSelect("SELECT last_object_id FROM object_id_tracker;");
range.maxID = selectResult.getInt64Field("last_object_id");
range.minID = range.maxID - 199;
// We must commit here manually, this will unlock the database for all other servers
ExecuteCustomQuery("COMMIT;");
SetAutoCommit(prevCommit);
return range;
void SQLiteDatabase::UpdatePersistentId(const uint32_t newId) {
ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = ?;", newId);
}

View File

@@ -1,23 +1,6 @@
#include "SQLiteDatabase.h"
#include "ePropertySortType.h"
IProperty::Info ReadPropertyInfo(CppSQLite3Query& propertyEntry) {
IProperty::Info toReturn;
toReturn.id = propertyEntry.getInt64Field("id");
toReturn.ownerId = propertyEntry.getInt64Field("owner_id");
toReturn.cloneId = propertyEntry.getInt64Field("clone_id");
toReturn.name = propertyEntry.getStringField("name");
toReturn.description = propertyEntry.getStringField("description");
toReturn.privacyOption = propertyEntry.getIntField("privacy_option");
toReturn.rejectionReason = propertyEntry.getStringField("rejection_reason");
toReturn.lastUpdatedTime = propertyEntry.getIntField("last_updated");
toReturn.claimedTime = propertyEntry.getIntField("time_claimed");
toReturn.reputation = propertyEntry.getIntField("reputation");
toReturn.modApproved = propertyEntry.getIntField("mod_approved");
toReturn.performanceCost = propertyEntry.getFloatField("performance_cost");
return toReturn;
}
std::optional<IProperty::PropertyEntranceResult> SQLiteDatabase::GetProperties(const IProperty::PropertyLookup& params) {
std::optional<IProperty::PropertyEntranceResult> result;
std::string query;
@@ -135,7 +118,19 @@ std::optional<IProperty::PropertyEntranceResult> SQLiteDatabase::GetProperties(c
auto& [_, properties] = propertiesRes;
if (!properties.eof() && !result.has_value()) result = IProperty::PropertyEntranceResult();
while (!properties.eof()) {
result->entries.push_back(ReadPropertyInfo(properties));
auto& entry = result->entries.emplace_back();
entry.id = properties.getInt64Field("id");
entry.ownerId = properties.getInt64Field("owner_id");
entry.cloneId = properties.getInt64Field("clone_id");
entry.name = properties.getStringField("name");
entry.description = properties.getStringField("description");
entry.privacyOption = properties.getIntField("privacy_option");
entry.rejectionReason = properties.getStringField("rejection_reason");
entry.lastUpdatedTime = properties.getIntField("last_updated");
entry.claimedTime = properties.getIntField("time_claimed");
entry.reputation = properties.getIntField("reputation");
entry.modApproved = properties.getIntField("mod_approved");
entry.performanceCost = properties.getFloatField("performance_cost");
properties.nextRow();
}
@@ -151,7 +146,21 @@ std::optional<IProperty::Info> SQLiteDatabase::GetPropertyInfo(const LWOMAPID ma
return std::nullopt;
}
return ReadPropertyInfo(propertyEntry);
IProperty::Info toReturn;
toReturn.id = propertyEntry.getInt64Field("id");
toReturn.ownerId = propertyEntry.getInt64Field("owner_id");
toReturn.cloneId = propertyEntry.getInt64Field("clone_id");
toReturn.name = propertyEntry.getStringField("name");
toReturn.description = propertyEntry.getStringField("description");
toReturn.privacyOption = propertyEntry.getIntField("privacy_option");
toReturn.rejectionReason = propertyEntry.getStringField("rejection_reason");
toReturn.lastUpdatedTime = propertyEntry.getIntField("last_updated");
toReturn.claimedTime = propertyEntry.getIntField("time_claimed");
toReturn.reputation = propertyEntry.getIntField("reputation");
toReturn.modApproved = propertyEntry.getIntField("mod_approved");
toReturn.performanceCost = propertyEntry.getFloatField("performance_cost");
return toReturn;
}
void SQLiteDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) {
@@ -188,15 +197,3 @@ void SQLiteDatabase::InsertNewProperty(const IProperty::Info& info, const uint32
zoneId.GetMapID()
);
}
std::optional<IProperty::Info> SQLiteDatabase::GetPropertyInfo(const LWOOBJID id) {
auto [_, propertyEntry] = ExecuteSelect(
"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved, performance_cost "
"FROM properties WHERE id = ?;", id);
if (propertyEntry.eof()) {
return std::nullopt;
}
return ReadPropertyInfo(propertyEntry);
}

View File

@@ -64,28 +64,27 @@ void SQLiteDatabase::RemoveModel(const LWOOBJID& modelId) {
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId);
}
std::optional<IPropertyContents::Model> SQLiteDatabase::GetModel(const LWOOBJID modelID) {
IPropertyContents::Model SQLiteDatabase::GetModel(const LWOOBJID modelID) {
auto [_, result] = ExecuteSelect("SELECT * FROM properties_contents WHERE id = ?", modelID);
std::optional<IPropertyContents::Model> model = std::nullopt;
IPropertyContents::Model model{};
if (!result.eof()) {
do {
model = IPropertyContents::Model{};
model->id = result.getInt64Field("id");
model->lot = static_cast<LOT>(result.getIntField("lot"));
model->position.x = result.getFloatField("x");
model->position.y = result.getFloatField("y");
model->position.z = result.getFloatField("z");
model->rotation.w = result.getFloatField("rw");
model->rotation.x = result.getFloatField("rx");
model->rotation.y = result.getFloatField("ry");
model->rotation.z = result.getFloatField("rz");
model->ugcId = result.getInt64Field("ugc_id");
model->behaviors[0] = result.getInt64Field("behavior_1");
model->behaviors[1] = result.getInt64Field("behavior_2");
model->behaviors[2] = result.getInt64Field("behavior_3");
model->behaviors[3] = result.getInt64Field("behavior_4");
model->behaviors[4] = result.getInt64Field("behavior_5");
model.id = result.getInt64Field("id");
model.lot = static_cast<LOT>(result.getIntField("lot"));
model.position.x = result.getFloatField("x");
model.position.y = result.getFloatField("y");
model.position.z = result.getFloatField("z");
model.rotation.w = result.getFloatField("rw");
model.rotation.x = result.getFloatField("rx");
model.rotation.y = result.getFloatField("ry");
model.rotation.z = result.getFloatField("rz");
model.ugcId = result.getInt64Field("ugc_id");
model.behaviors[0] = result.getInt64Field("behavior_1");
model.behaviors[1] = result.getInt64Field("behavior_2");
model.behaviors[2] = result.getInt64Field("behavior_3");
model.behaviors[3] = result.getInt64Field("behavior_4");
model.behaviors[4] = result.getInt64Field("behavior_5");
} while (result.nextRow());
}

View File

@@ -1,17 +1,5 @@
#include "SQLiteDatabase.h"
IUgc::Model ReadModel(CppSQLite3Query& result) {
IUgc::Model model;
int blobSize{};
const auto* blob = result.getBlobField("lxfml", blobSize);
model.lxfmlData << std::string(reinterpret_cast<const char*>(blob), blobSize);
model.id = result.getInt64Field("ugcID");
model.modelID = result.getInt64Field("modelID");
return model;
}
std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId) {
auto [_, result] = ExecuteSelect(
"SELECT lxfml, u.id AS ugcID, pc.id AS modelID FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;",
@@ -20,7 +8,14 @@ std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId
std::vector<IUgc::Model> toReturn;
while (!result.eof()) {
toReturn.push_back(ReadModel(result));
IUgc::Model model;
int blobSize{};
const auto* blob = result.getBlobField("lxfml", blobSize);
model.lxfmlData << std::string(reinterpret_cast<const char*>(blob), blobSize);
model.id = result.getInt64Field("ugcID");
model.modelID = result.getInt64Field("modelID");
toReturn.push_back(std::move(model));
result.nextRow();
}
@@ -32,7 +27,14 @@ std::vector<IUgc::Model> SQLiteDatabase::GetAllUgcModels() {
std::vector<IUgc::Model> models;
while (!result.eof()) {
models.push_back(ReadModel(result));
IUgc::Model model;
model.id = result.getInt64Field("ugcID");
model.modelID = result.getInt64Field("modelID");
int blobSize{};
const auto* blob = result.getBlobField("lxfml", blobSize);
model.lxfmlData << std::string(reinterpret_cast<const char*>(blob), blobSize);
models.push_back(std::move(model));
result.nextRow();
}
@@ -45,7 +47,7 @@ void SQLiteDatabase::RemoveUnreferencedUgcModels() {
void SQLiteDatabase::InsertNewUgcModel(
std::stringstream& sd0Data, // cant be const sad
const uint64_t blueprintId,
const uint32_t blueprintId,
const uint32_t accountId,
const LWOOBJID characterId) {
const std::istream stream(sd0Data.rdbuf());
@@ -70,14 +72,3 @@ void SQLiteDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstre
const std::istream stream(lxfml.rdbuf());
ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId);
}
std::optional<IUgc::Model> SQLiteDatabase::GetUgcModel(const LWOOBJID ugcId) {
auto [_, result] = ExecuteSelect("SELECT u.id AS ugcID, pc.id AS modelID, lxfml FROM ugc AS u JOIN properties_contents AS pc ON pc.id = u.id WHERE u.id = ?;", ugcId);
std::optional<IUgc::Model> toReturn = std::nullopt;
if (!result.eof()) {
toReturn = ReadModel(result);
}
return toReturn;
}

View File

@@ -192,7 +192,7 @@ void TestSQLDatabase::InsertNewMail(const MailInfo& mail) {
}
void TestSQLDatabase::InsertNewUgcModel(std::stringstream& sd0Data, const uint64_t blueprintId, const uint32_t accountId, const LWOOBJID characterId) {
void TestSQLDatabase::InsertNewUgcModel(std::stringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const LWOOBJID characterId) {
}
@@ -244,7 +244,7 @@ void TestSQLDatabase::SetMasterInfo(const IServers::MasterInfo& info) {
}
std::optional<uint64_t> TestSQLDatabase::GetCurrentPersistentId() {
std::optional<uint32_t> TestSQLDatabase::GetCurrentPersistentId() {
return {};
}
@@ -252,6 +252,10 @@ void TestSQLDatabase::InsertDefaultPersistentId() {
}
void TestSQLDatabase::UpdatePersistentId(const uint32_t id) {
}
std::optional<uint32_t> TestSQLDatabase::GetDonationTotal(const uint32_t activityId) {
return {};
}
@@ -300,6 +304,3 @@ void TestSQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGame
}
IObjectIdTracker::Range TestSQLDatabase::GetPersistentIdRange() {
return {};
}

View File

@@ -60,7 +60,7 @@ class TestSQLDatabase : public GameDatabase {
void InsertNewMail(const MailInfo& mail) override;
void InsertNewUgcModel(
std::stringstream& sd0Data,
const uint64_t blueprintId,
const uint32_t blueprintId,
const uint32_t accountId,
const LWOOBJID characterId) override;
std::vector<MailInfo> GetMailForPlayer(const LWOOBJID characterId, const uint32_t numberOfMail) override;
@@ -75,9 +75,9 @@ class TestSQLDatabase : public GameDatabase {
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint64_t> GetCurrentPersistentId() override;
IObjectIdTracker::Range GetPersistentIdRange() override;
std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;
std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override;
std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override;
std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override;
@@ -105,9 +105,7 @@ class TestSQLDatabase : public GameDatabase {
uint32_t GetAccountCount() override { return 0; };
bool IsNameInUse(const std::string_view name) override { return false; };
std::optional<IPropertyContents::Model> GetModel(const LWOOBJID modelID) override { return {}; }
std::optional<IProperty::Info> GetPropertyInfo(const LWOOBJID id) override { return {}; }
std::optional<IUgc::Model> GetUgcModel(const LWOOBJID ugcId) override { return {}; }
IPropertyContents::Model GetModel(const LWOOBJID modelID) override { return {}; }
};
#endif //!TESTSQLDATABASE_H

View File

@@ -10,7 +10,7 @@ void ModelNormalizeMigration::Run() {
for (auto& [lxfmlData, id, modelID] : Database::Get()->GetAllUgcModels()) {
const auto model = Database::Get()->GetModel(modelID);
// only BBB models (lot 14) and models with a position of NiPoint3::ZERO need to have their position fixed.
if (!model || model->position != NiPoint3Constant::ZERO || model->lot != 14) continue;
if (model.position != NiPoint3Constant::ZERO || model.lot != 14) continue;
Sd0 sd0(lxfmlData);
const auto asStr = sd0.GetAsStringUncompressed();
@@ -23,7 +23,7 @@ void ModelNormalizeMigration::Run() {
LOG("Updated model %llu to have a center of %f %f %f", modelID, newCenter.x, newCenter.y, newCenter.z);
sd0.FromData(reinterpret_cast<const uint8_t*>(newLxfml.data()), newLxfml.size());
auto asStream = sd0.GetAsStream();
Database::Get()->UpdateModel(model->id, newCenter, model->rotation, model->behaviors);
Database::Get()->UpdateModel(model.id, newCenter, model.rotation, model.behaviors);
Database::Get()->UpdateUgcModelData(id, asStream);
}
Database::Get()->SetAutoCommit(oldCommit);
@@ -35,15 +35,15 @@ void ModelNormalizeMigration::RunAfterFirstPart() {
for (auto& [lxfmlData, id, modelID] : Database::Get()->GetAllUgcModels()) {
const auto model = Database::Get()->GetModel(modelID);
// only BBB models (lot 14) need to have their position fixed from the above blunder
if (!model || model->lot != 14) continue;
if (model.lot != 14) continue;
Sd0 sd0(lxfmlData);
const auto asStr = sd0.GetAsStringUncompressed();
const auto [newLxfml, newCenter] = Lxfml::NormalizePositionAfterFirstPart(asStr, model->position);
const auto [newLxfml, newCenter] = Lxfml::NormalizePositionAfterFirstPart(asStr, model.position);
sd0.FromData(reinterpret_cast<const uint8_t*>(newLxfml.data()), newLxfml.size());
auto asStream = sd0.GetAsStream();
Database::Get()->UpdateModel(model->id, newCenter, model->rotation, model->behaviors);
Database::Get()->UpdateModel(model.id, newCenter, model.rotation, model.behaviors);
Database::Get()->UpdateUgcModelData(id, asStream);
}
Database::Get()->SetAutoCommit(oldCommit);
@@ -55,16 +55,16 @@ void ModelNormalizeMigration::RunBrickBuildGrid() {
for (auto& [lxfmlData, id, modelID] : Database::Get()->GetAllUgcModels()) {
const auto model = Database::Get()->GetModel(modelID);
// only BBB models (lot 14) need to have their position fixed from the above blunder
if (!model || model->lot != 14) continue;
if (model.lot != 14) continue;
Sd0 sd0(lxfmlData);
const auto asStr = sd0.GetAsStringUncompressed();
const auto [newLxfml, newCenter] = Lxfml::NormalizePosition(asStr, model->position);
const auto [newLxfml, newCenter] = Lxfml::NormalizePosition(asStr, model.position);
sd0.FromData(reinterpret_cast<const uint8_t*>(newLxfml.data()), newLxfml.size());
LOG("Updated model %llu to have a center of %f %f %f", modelID, newCenter.x, newCenter.y, newCenter.z);
auto asStream = sd0.GetAsStream();
Database::Get()->UpdateModel(model->id, newCenter, model->rotation, model->behaviors);
Database::Get()->UpdateModel(model.id, newCenter, model.rotation, model.behaviors);
Database::Get()->UpdateUgcModelData(id, asStream);
}
Database::Get()->SetAutoCommit(oldCommit);

View File

@@ -336,11 +336,8 @@ void Character::WriteToDatabase() {
tinyxml2::XMLPrinter printer(0, true, 0);
m_Doc.Print(&printer);
// Update the xml on the character for future use if needed
m_XMLData = printer.CStr();
//Finally, save to db:
Database::Get()->UpdateCharacterXml(m_ID, m_XMLData);
Database::Get()->UpdateCharacterXml(m_ID, printer.CStr());
}
void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {

View File

@@ -682,9 +682,6 @@ private:
* NOTE: quick as there's no DB lookups
*/
void DoQuickXMLDataParse();
public:
const decltype(m_PlayerFlags)& GetPlayerFlags() const { return m_PlayerFlags; }
const decltype(m_SessionFlags)& GetSessionFlags() const { return m_SessionFlags; }
};
#endif // CHARACTER_H

View File

@@ -84,8 +84,6 @@
#include "GhostComponent.h"
#include "AchievementVendorComponent.h"
#include "VanityUtilities.h"
#include "ObjectIDManager.h"
#include "ePlayerFlag.h"
// Table includes
#include "CDComponentsRegistryTable.h"
@@ -194,17 +192,14 @@ Entity::~Entity() {
}
void Entity::Initialize() {
RegisterMsg<GameMessages::RequestServerObjectInfo>(this, &Entity::MsgRequestServerObjectInfo);
RegisterMsg<GameMessages::DropClientLoot>(this, &Entity::MsgDropClientLoot);
RegisterMsg<GameMessages::GetFactionTokenType>(this, &Entity::MsgGetFactionTokenType);
RegisterMsg<GameMessages::PickupItem>(this, &Entity::MsgPickupItem);
RegisterMsg(MessageType::Game::REQUEST_SERVER_OBJECT_INFO, this, &Entity::MsgRequestServerObjectInfo);
/**
* Setup trigger
*/
const auto triggerInfo = GetVarAsString(u"trigger_id");
if (!triggerInfo.empty()) AddComponent<TriggerComponent>(-1, triggerInfo);
if (!triggerInfo.empty()) AddComponent<TriggerComponent>(triggerInfo);
/**
* Setup groups
@@ -239,11 +234,11 @@ void Entity::Initialize() {
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
AddComponent<ModelComponent>(-1)->LoadBehaviors();
AddComponent<ModelComponent>()->LoadBehaviors();
AddComponent<RenderComponent>(-1);
AddComponent<RenderComponent>();
auto* destroyableComponent = AddComponent<DestroyableComponent>(-1);
auto* destroyableComponent = AddComponent<DestroyableComponent>();
destroyableComponent->SetHealth(1);
destroyableComponent->SetMaxHealth(1.0f);
destroyableComponent->SetFaction(-1, true);
@@ -259,42 +254,37 @@ void Entity::Initialize() {
*/
if (m_Character && m_Character->GetParentUser()) {
AddComponent<MissionComponent>(-1)->LoadFromXml(m_Character->GetXMLDoc());
AddComponent<MissionComponent>()->LoadFromXml(m_Character->GetXMLDoc());
}
const auto petComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PET);
if (petComponentID > 0) {
AddComponent<PetComponent>(petComponentID);
const uint32_t petComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PET);
if (petComponentId > 0) {
AddComponent<PetComponent>(petComponentId);
}
const auto minigameControlID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MINI_GAME_CONTROL);
if (minigameControlID > 0) {
AddComponent<MiniGameControlComponent>(minigameControlID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MINI_GAME_CONTROL) > 0) {
AddComponent<MiniGameControlComponent>();
}
const auto possessableComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE);
if (possessableComponentID > 0) {
AddComponent<PossessableComponent>(possessableComponentID);
const uint32_t possessableComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE);
if (possessableComponentId > 0) {
AddComponent<PossessableComponent>(possessableComponentId);
}
const auto moduleAssemblyID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODULE_ASSEMBLY);
if (moduleAssemblyID > 0) {
AddComponent<ModuleAssemblyComponent>(moduleAssemblyID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODULE_ASSEMBLY) > 0) {
AddComponent<ModuleAssemblyComponent>();
}
const auto racingStatsID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_STATS);
if (racingStatsID > 0) {
AddComponent<RacingStatsComponent>(racingStatsID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_STATS) > 0) {
AddComponent<RacingStatsComponent>();
}
const auto lupExhibitID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::LUP_EXHIBIT, -1);
if (lupExhibitID >= 0) {
AddComponent<LUPExhibitComponent>(lupExhibitID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::LUP_EXHIBIT, -1) >= 0) {
AddComponent<LUPExhibitComponent>();
}
const auto racingControlID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_CONTROL);
if (racingControlID > 0) {
AddComponent<RacingControlComponent>(racingControlID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_CONTROL) > 0) {
AddComponent<RacingControlComponent>();
}
const auto propertyEntranceComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_ENTRANCE);
@@ -302,7 +292,7 @@ void Entity::Initialize() {
AddComponent<PropertyEntranceComponent>(propertyEntranceComponentID);
}
const auto controllablePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS);
const int32_t controllablePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS);
if (controllablePhysicsComponentID > 0) {
auto* controllablePhysics = AddComponent<ControllablePhysicsComponent>(controllablePhysicsComponentID);
@@ -347,48 +337,46 @@ void Entity::Initialize() {
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
}
const auto rigidBodyPhantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS);
const int32_t rigidBodyPhantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS);
if (rigidBodyPhantomPhysicsComponentID > 0) {
AddComponent<RigidbodyPhantomPhysicsComponent>(rigidBodyPhantomPhysicsComponentID);
}
const auto phantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS);
const int32_t phantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS);
if (markedAsPhantom || phantomPhysicsComponentID > 0) {
AddComponent<PhantomPhysicsComponent>(phantomPhysicsComponentID)->SetPhysicsEffectActive(false);
}
const auto havokVehiclePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS);
const int32_t havokVehiclePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS);
if (havokVehiclePhysicsComponentID > 0) {
auto* havokVehiclePhysicsComponent = AddComponent<HavokVehiclePhysicsComponent>(havokVehiclePhysicsComponentID);
havokVehiclePhysicsComponent->SetPosition(m_DefaultPosition);
havokVehiclePhysicsComponent->SetRotation(m_DefaultRotation);
}
const auto soundTriggerID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1);
const auto racingSoundTriggerID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_SOUND_TRIGGER, -1);
if (soundTriggerID > -1) {
AddComponent<SoundTriggerComponent>(soundTriggerID);
} else if (racingSoundTriggerID > -1) {
AddComponent<RacingSoundTriggerComponent>(racingSoundTriggerID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1) != -1) {
AddComponent<SoundTriggerComponent>();
} else if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_SOUND_TRIGGER, -1) != -1) {
AddComponent<RacingSoundTriggerComponent>();
}
const auto buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF);
if (buffComponentID > 0) {
AddComponent<BuffComponent>(buffComponentID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF) > 0) {
AddComponent<BuffComponent>();
}
const auto collectibleComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::COLLECTIBLE);
const int collectibleComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::COLLECTIBLE);
if (collectibleComponentID > 0) {
AddComponent<CollectibleComponent>(collectibleComponentID, GetVarAs<int32_t>(u"collectible_id"));
AddComponent<CollectibleComponent>(GetVarAs<int32_t>(u"collectible_id"));
}
/**
* Multiple components require the destructible component.
*/
const auto quickBuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
const int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF);
const int quickBuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
int32_t componentID = -1;
int componentID = -1;
if (collectibleComponentID > 0) componentID = collectibleComponentID;
if (quickBuildComponentID > 0) componentID = quickBuildComponentID;
if (buffComponentID > 0) componentID = buffComponentID;
@@ -396,7 +384,7 @@ void Entity::Initialize() {
bool isSmashable = GetVarAs<int32_t>(u"is_smashable") != 0;
if (buffComponentID > 0 || collectibleComponentID > 0 || isSmashable) {
DestroyableComponent* comp = AddComponent<DestroyableComponent>(componentID);
DestroyableComponent* comp = AddComponent<DestroyableComponent>();
auto* const destCompTable = CDClientManager::GetTable<CDDestructibleComponentTable>();
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([componentID](const CDDestructibleComponent& entry) { return (entry.id == componentID); });
@@ -424,7 +412,6 @@ void Entity::Initialize() {
comp->SetIsSmashable(destCompData[0].isSmashable);
comp->SetLootMatrixID(destCompData[0].LootMatrixIndex);
comp->SetCurrencyIndex(destCompData[0].CurrencyIndex);
Loot::CacheMatrix(destCompData[0].LootMatrixIndex);
// Now get currency information
@@ -486,30 +473,27 @@ void Entity::Initialize() {
}
}
const auto characterID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER);
if (characterID > 0 || m_Character) {
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER) > 0 || m_Character) {
// Character Component always has a possessor, level, and forced movement components
AddComponent<PossessorComponent>(characterID);
AddComponent<PossessorComponent>();
// load in the xml for the level
AddComponent<LevelProgressionComponent>(characterID)->LoadFromXml(m_Character->GetXMLDoc());
AddComponent<LevelProgressionComponent>()->LoadFromXml(m_Character->GetXMLDoc());
AddComponent<PlayerForcedMovementComponent>(characterID);
AddComponent<PlayerForcedMovementComponent>();
auto& systemAddress = m_Character->GetParentUser() ? m_Character->GetParentUser()->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS;
AddComponent<CharacterComponent>(characterID, m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc());
AddComponent<CharacterComponent>(m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc());
AddComponent<GhostComponent>(characterID);
AddComponent<GhostComponent>();
}
const auto inventoryID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY);
if (inventoryID > 0 || m_Character) {
AddComponent<InventoryComponent>(inventoryID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) {
AddComponent<InventoryComponent>();
}
// if this component exists, then we initialize it. it's value is always 0
const auto multiZoneEntranceID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1);
if (multiZoneEntranceID > -1) {
AddComponent<MultiZoneEntranceComponent>(multiZoneEntranceID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) {
AddComponent<MultiZoneEntranceComponent>();
}
/**
@@ -561,7 +545,7 @@ void Entity::Initialize() {
}
if (!scriptName.empty() || client || m_Character || scriptComponentID >= 0) {
AddComponent<ScriptComponent>(scriptComponentID, scriptName, true, client && scriptName.empty());
AddComponent<ScriptComponent>(scriptName, true, client && scriptName.empty());
}
// ZoneControl script
@@ -570,27 +554,26 @@ void Entity::Initialize() {
const CDZoneTable* const zoneData = CDZoneTableTable::Query(zoneID.GetMapID());
if (zoneData != nullptr) {
const int32_t zoneScriptID = zoneData->scriptID;
int zoneScriptID = zoneData->scriptID;
CDScriptComponent zoneScriptData = scriptCompTable->GetByID(zoneScriptID);
AddComponent<ScriptComponent>(zoneScriptID, zoneScriptData.script_name, true, false);
AddComponent<ScriptComponent>(zoneScriptData.script_name, true);
}
}
const auto skillID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SKILL, -1);
if (skillID > -1 || m_Character) {
AddComponent<SkillComponent>(skillID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SKILL, -1) != -1 || m_Character) {
AddComponent<SkillComponent>();
}
const auto combatAiID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BASE_COMBAT_AI);
if (combatAiID > 0) {
AddComponent<BaseCombatAIComponent>(combatAiID);
const auto combatAiId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BASE_COMBAT_AI);
if (combatAiId > 0) {
AddComponent<BaseCombatAIComponent>(combatAiId);
}
if (quickBuildComponentID > 0) {
auto* const quickBuildComponent = AddComponent<QuickBuildComponent>(quickBuildComponentID);
if (const int componentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD) > 0) {
auto* const quickBuildComponent = AddComponent<QuickBuildComponent>();
CDRebuildComponentTable* const rebCompTable = CDClientManager::GetTable<CDRebuildComponentTable>();
const std::vector<CDRebuildComponent> rebCompData = rebCompTable->Query([quickBuildComponentID](CDRebuildComponent entry) { return (entry.id == quickBuildComponentID); });
const std::vector<CDRebuildComponent> rebCompData = rebCompTable->Query([=](CDRebuildComponent entry) { return (entry.id == quickBuildComponentID); });
if (!rebCompData.empty()) {
quickBuildComponent->SetResetTime(rebCompData[0].reset_time);
@@ -635,63 +618,53 @@ void Entity::Initialize() {
}
}
const auto switchID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SWITCH, -1);
if (switchID > -1) {
AddComponent<SwitchComponent>(switchID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SWITCH, -1) != -1) {
AddComponent<SwitchComponent>();
}
const auto vendorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VENDOR);
const auto donationVendorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DONATION_VENDOR, -1);
const auto achievementVendorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ACHIEVEMENT_VENDOR, -1);
if (vendorID > 0) {
AddComponent<VendorComponent>(vendorID);
} else if (donationVendorID > -1) {
AddComponent<DonationVendorComponent>(donationVendorID);
} else if (achievementVendorID > -1) {
AddComponent<AchievementVendorComponent>(achievementVendorID);
if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VENDOR) > 0)) {
AddComponent<VendorComponent>();
} else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DONATION_VENDOR, -1) != -1)) {
AddComponent<DonationVendorComponent>();
} else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ACHIEVEMENT_VENDOR, -1) != -1)) {
AddComponent<AchievementVendorComponent>();
}
const auto propertyVendorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1);
if (propertyVendorID > -1) {
AddComponent<PropertyVendorComponent>(propertyVendorID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1) != -1) {
AddComponent<PropertyVendorComponent>();
}
const auto propertyManagementID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_MANAGEMENT, -1);
if (propertyManagementID > -1) {
AddComponent<PropertyManagementComponent>(propertyManagementID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_MANAGEMENT, -1) != -1) {
AddComponent<PropertyManagementComponent>();
}
const auto bouncerID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BOUNCER, -1);
if (bouncerID > -1) { // you have to determine it like this because all bouncers have a componentID of 0
AddComponent<BouncerComponent>(bouncerID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BOUNCER, -1) != -1) { // you have to determine it like this because all bouncers have a componentID of 0
AddComponent<BouncerComponent>();
}
const auto renderComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RENDER);
if ((renderComponentID > 0 && m_TemplateID != 2365) || m_Character) {
AddComponent<RenderComponent>(renderComponentID);
const int32_t renderComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RENDER);
if ((renderComponentId > 0 && m_TemplateID != 2365) || m_Character) {
AddComponent<RenderComponent>(renderComponentId);
}
const auto missionOfferComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MISSION_OFFER, -1);
if (missionOfferComponentID > -1 || m_Character) {
AddComponent<MissionOfferComponent>(missionOfferComponentID);
if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MISSION_OFFER) > 0) || m_Character) {
AddComponent<MissionOfferComponent>(m_TemplateID);
}
const auto buildBorderID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUILD_BORDER, -1);
if (buildBorderID > -1) {
AddComponent<BuildBorderComponent>(buildBorderID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUILD_BORDER, -1) != -1) {
AddComponent<BuildBorderComponent>();
}
// Scripted activity component
const auto scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY, -1);
if (scriptedActivityID > -1) {
const int32_t scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY, -1);
if (scriptedActivityID != -1) {
AddComponent<ScriptedActivityComponent>(scriptedActivityID);
}
const auto modelID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1);
if (modelID > -1 && !GetComponent<PetComponent>()) {
AddComponent<ModelComponent>(modelID)->LoadBehaviors();
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
AddComponent<ModelComponent>()->LoadBehaviors();
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
auto* const destroyableComponent = AddComponent<DestroyableComponent>(-1);
auto* const destroyableComponent = AddComponent<DestroyableComponent>();
destroyableComponent->SetHealth(1);
destroyableComponent->SetMaxHealth(1.0f);
destroyableComponent->SetFaction(-1, true);
@@ -699,10 +672,9 @@ void Entity::Initialize() {
}
}
PetComponent* petComponent{};
const auto itemID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM);
if (itemID > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent) && !HasComponent(eReplicaComponentType::MODEL)) {
AddComponent<ItemComponent>(itemID);
PetComponent* petComponent;
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM) > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent) && !HasComponent(eReplicaComponentType::MODEL)) {
AddComponent<ItemComponent>();
}
// Shooting gallery component
@@ -711,17 +683,16 @@ void Entity::Initialize() {
AddComponent<ShootingGalleryComponent>(shootingGalleryComponentID);
}
const auto propertyID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY, -1);
if (propertyID > -1) {
AddComponent<PropertyComponent>(propertyID);
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY, -1) != -1) {
AddComponent<PropertyComponent>();
}
const auto rocketID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH);
if ((rocketID > 0)) {
AddComponent<RocketLaunchpadControlComponent>(rocketID);
const int rocketId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH);
if ((rocketId > 0)) {
AddComponent<RocketLaunchpadControlComponent>(rocketId);
}
const auto railComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RAIL_ACTIVATOR);
const int32_t railComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RAIL_ACTIVATOR);
if (railComponentID > 0) {
AddComponent<RailActivatorComponent>(railComponentID);
}
@@ -751,9 +722,9 @@ void Entity::Initialize() {
}
}
AddComponent<MovementAIComponent>(movementAIID, moveInfo);
AddComponent<MovementAIComponent>(moveInfo);
}
} else if (petComponentID > 0 || combatAiID > 0 && GetComponent<BaseCombatAIComponent>()->GetTetherSpeed() > 0) {
} else if (petComponentId > 0 || combatAiId > 0 && GetComponent<BaseCombatAIComponent>()->GetTetherSpeed() > 0) {
MovementAIInfo moveInfo{
.movementType = "",
.wanderRadius = 16,
@@ -763,7 +734,7 @@ void Entity::Initialize() {
.wanderDelayMax = 5,
};
AddComponent<MovementAIComponent>(-1, moveInfo);
AddComponent<MovementAIComponent>(moveInfo);
}
const std::string pathName = GetVarAsString(u"attached_path");
@@ -773,10 +744,10 @@ void Entity::Initialize() {
if (path) {
// if we have a moving platform path, then we need a moving platform component
if (path->pathType == PathType::MovingPlatform) {
AddComponent<MovingPlatformComponent>(-1, pathName);
AddComponent<MovingPlatformComponent>(pathName);
} else if (path->pathType == PathType::Movement) {
auto* const movementAIcomponent = GetComponent<MovementAIComponent>();
if (movementAIcomponent && combatAiID == 0) {
if (movementAIcomponent && combatAiId == 0) {
movementAIcomponent->SetPath(pathName);
} else {
MovementAIInfo moveInfo{
@@ -788,24 +759,24 @@ void Entity::Initialize() {
.wanderDelayMax = 5,
};
AddComponent<MovementAIComponent>(-1, moveInfo);
AddComponent<MovementAIComponent>(moveInfo);
}
}
} else {
// else we still need to setup moving platform if it has a moving platform comp but no path
const auto movingPlatformComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1);
if (movingPlatformComponentID >= 0) {
AddComponent<MovingPlatformComponent>(movingPlatformComponentID, pathName);
const int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1);
if (movingPlatformComponentId >= 0) {
AddComponent<MovingPlatformComponent>(pathName);
}
}
const auto proximityMonitorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROXIMITY_MONITOR);
const int proximityMonitorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROXIMITY_MONITOR);
if (proximityMonitorID > 0) {
auto* const proxCompTable = CDClientManager::GetTable<CDProximityMonitorComponentTable>();
const auto proxCompData = proxCompTable->Query([proximityMonitorID](const CDProximityMonitorComponent& entry) { return (entry.id == proximityMonitorID); });
if (proxCompData.size() > 0) {
std::vector<std::string> proximityStr = GeneralUtils::SplitString(proxCompData[0].Proximities, ',');
AddComponent<ProximityMonitorComponent>(proximityMonitorID, std::stoi(proximityStr[0]), std::stoi(proximityStr[1]));
AddComponent<ProximityMonitorComponent>(std::stoi(proximityStr[0]), std::stoi(proximityStr[1]));
}
}
@@ -911,13 +882,12 @@ void Entity::Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationNa
void Entity::SetProximityRadius(float proxRadius, std::string name) {
auto* proxMon = GetComponent<ProximityMonitorComponent>();
if (!proxMon) proxMon = AddComponent<ProximityMonitorComponent>(-1);
if (!proxMon) proxMon = AddComponent<ProximityMonitorComponent>();
proxMon->SetProximityRadius(proxRadius, name);
}
void Entity::SetProximityRadius(dpEntity* entity, std::string name) {
auto* proxMon = GetComponent<ProximityMonitorComponent>();
if (!proxMon) proxMon = AddComponent<ProximityMonitorComponent>(-1);
ProximityMonitorComponent* proxMon = AddComponent<ProximityMonitorComponent>();
proxMon->SetProximityRadius(entity, name);
}
@@ -1669,7 +1639,7 @@ void Entity::AddLootItem(const Loot::Info& info) const {
auto* const characterComponent = GetComponent<CharacterComponent>();
if (!characterComponent) return;
LOG("Player %llu has been allowed to pickup %i with id %llu", m_ObjectID, info.lot, info.id);
auto& droppedLoot = characterComponent->GetDroppedLoot();
droppedLoot[info.id] = info;
}
@@ -2253,8 +2223,6 @@ bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
response.Insert("objectID", std::to_string(m_ObjectID));
response.Insert("serverInfo", true);
GameMessages::GetObjectReportInfo info{};
info.clientID = requestInfo.clientId;
info.bVerbose = requestInfo.bVerbose;
info.info = response.InsertArray("data");
auto& objectInfo = info.info->PushDebug("Object Details");
auto* table = CDClientManager::GetTable<CDObjectsTable>();
@@ -2268,87 +2236,17 @@ bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
auto& componentDetails = objectInfo.PushDebug("Component Information");
for (const auto [id, component] : m_Components) {
componentDetails.PushDebug(StringifiedEnum::ToString(id));
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* client = Game::entityManager->GetEntity(requestInfo.clientId);
if (client) GameMessages::SendUIMessageServerToSingleClient("ToggleObjectDebugger", response, client->GetSystemAddress());
return true;
}
bool Entity::MsgDropClientLoot(GameMessages::GameMsg& msg) {
auto& dropLootMsg = static_cast<GameMessages::DropClientLoot&>(msg);
if (dropLootMsg.item != LOT_NULL && dropLootMsg.item != 0) {
Loot::Info info{
.id = dropLootMsg.lootID,
.lot = dropLootMsg.item,
.count = dropLootMsg.count,
};
AddLootItem(info);
}
if (dropLootMsg.item == LOT_NULL && dropLootMsg.currency != 0) {
RegisterCoinDrop(dropLootMsg.currency);
}
return true;
}
bool Entity::MsgGetFlag(GameMessages::GameMsg& msg) {
auto& flagMsg = static_cast<GameMessages::GetFlag&>(msg);
if (m_Character) flagMsg.flag = m_Character->GetPlayerFlag(flagMsg.flagID);
return true;
}
bool Entity::MsgGetFactionTokenType(GameMessages::GameMsg& msg) {
auto& tokenMsg = static_cast<GameMessages::GetFactionTokenType&>(msg);
GameMessages::GetFlag getFlagMsg{};
getFlagMsg.flagID = ePlayerFlag::ASSEMBLY_FACTION;
MsgGetFlag(getFlagMsg);
if (getFlagMsg.flag) tokenMsg.tokenType = 8318;
getFlagMsg.flagID = ePlayerFlag::SENTINEL_FACTION;
MsgGetFlag(getFlagMsg);
if (getFlagMsg.flag) tokenMsg.tokenType = 8319;
getFlagMsg.flagID = ePlayerFlag::PARADOX_FACTION;
MsgGetFlag(getFlagMsg);
if (getFlagMsg.flag) tokenMsg.tokenType = 8320;
getFlagMsg.flagID = ePlayerFlag::VENTURE_FACTION;
MsgGetFlag(getFlagMsg);
if (getFlagMsg.flag) tokenMsg.tokenType = 8321;
LOG("Returning token type %i", tokenMsg.tokenType);
return tokenMsg.tokenType != LOT_NULL;
}
bool Entity::MsgPickupItem(GameMessages::GameMsg& msg) {
auto& pickupItemMsg = static_cast<GameMessages::PickupItem&>(msg);
if (GetObjectID() == pickupItemMsg.lootOwnerID) {
PickupItem(pickupItemMsg.lootID);
} else {
auto* const characterComponent = GetComponent<CharacterComponent>();
if (!characterComponent) return false;
auto& droppedLoot = characterComponent->GetDroppedLoot();
const auto it = droppedLoot.find(pickupItemMsg.lootID);
if (it != droppedLoot.end()) {
CDObjectsTable* objectsTable = CDClientManager::GetTable<CDObjectsTable>();
const CDObjects& object = objectsTable->GetByID(it->second.lot);
if (object.id != 0 && object.type == "Powerup") {
return false; // Let powerups be duplicated
}
}
droppedLoot.erase(pickupItemMsg.lootID);
}
return true;
}

View File

@@ -176,10 +176,6 @@ public:
void AddComponent(eReplicaComponentType componentId, Component* component);
bool MsgRequestServerObjectInfo(GameMessages::GameMsg& msg);
bool MsgDropClientLoot(GameMessages::GameMsg& msg);
bool MsgGetFlag(GameMessages::GameMsg& msg);
bool MsgGetFactionTokenType(GameMessages::GameMsg& msg);
bool MsgPickupItem(GameMessages::GameMsg& msg);
// This is expceted to never return nullptr, an assert checks this.
CppScripts::Script* const GetScript() const;
@@ -346,12 +342,6 @@ public:
RegisterMsg(msgId, std::bind(handler, self, std::placeholders::_1));
}
template<typename T>
inline void RegisterMsg(auto* self, const auto handler) {
T msg;
RegisterMsg(msg.msgId, self, handler);
}
/**
* @brief The observable for player entity position updates.
*/
@@ -610,5 +600,5 @@ auto Entity::GetComponents() const {
template<typename... T>
auto Entity::GetComponentsMut() const {
return std::tuple{ GetComponent<T>()... };
return std::tuple{GetComponent<T>()...};
}

View File

@@ -52,45 +52,6 @@ std::vector<LOT> EntityManager::m_GhostingExcludedLOTs = {
4967
};
template<typename T>
void ParseDelimSetting(std::set<T>& setting, const std::string_view settingName, const char delim = ',') {
const auto str = Game::config->GetValue(settingName.data());
setting.clear();
for (const auto& strVal : GeneralUtils::SplitString(str, delim)) {
const auto val = GeneralUtils::TryParse<T>(strVal);
if (val) {
setting.insert(val.value());
}
}
}
void EntityManager::ReloadConfig() {
auto hcmode = Game::config->GetValue("hardcore_mode");
m_HardcoreMode = hcmode.empty() ? false : (hcmode == "1");
auto hcUscorePercent = Game::config->GetValue("hardcore_lose_uscore_on_death_percent");
m_HardcoreLoseUscoreOnDeathPercent = hcUscorePercent.empty() ? 10 : GeneralUtils::TryParse<uint32_t>(hcUscorePercent).value_or(10);
auto hcUscoreMult = Game::config->GetValue("hardcore_uscore_enemies_multiplier");
m_HardcoreUscoreEnemiesMultiplier = hcUscoreMult.empty() ? 2 : GeneralUtils::TryParse<uint32_t>(hcUscoreMult).value_or(2);
auto hcDropInv = Game::config->GetValue("hardcore_dropinventory_on_death");
m_HardcoreDropinventoryOnDeath = hcDropInv.empty() ? false : (hcDropInv == "1");
ParseDelimSetting<LOT>(m_HardcoreExcludedItemDrops, "hardcore_excluded_item_drops");
// We don't need to save the worlds, just need to check if this one is in the list
std::set<LWOMAPID> worlds;
ParseDelimSetting<LWOMAPID>(worlds, "hardcore_uscore_reduced_worlds");
m_HardcoreUscoreReduced = worlds.contains(Game::zoneManager->GetZoneID().GetMapID());
ParseDelimSetting<LOT>(m_HardcoreUscoreReducedLots, "hardcore_uscore_reduced_lots");
ParseDelimSetting<LOT>(m_HardcoreUscoreExcludedEnemies, "hardcore_uscore_excluded_enemies");
ParseDelimSetting<LWOMAPID>(m_HardcoreDisabledWorlds, "hardcore_disabled_worlds");
auto hcXpReduction = Game::config->GetValue("hardcore_uscore_reduction");
m_HardcoreUscoreReduction = hcXpReduction.empty() ? 1.0f : GeneralUtils::TryParse<float>(hcXpReduction).value_or(1.0f);
m_HardcoreMode = GetHardcoreDisabledWorlds().contains(Game::zoneManager->GetZoneID().GetMapID()) ? false : m_HardcoreMode;
auto hcCoinKeep = Game::config->GetValue("hardcore_coin_keep");
m_HardcoreCoinKeep = hcCoinKeep.empty() ? false : GeneralUtils::TryParse<float>(hcCoinKeep).value_or(0.0f);
}
void EntityManager::Initialize() {
// Check if this zone has ghosting enabled
m_GhostingEnabled = std::find(
@@ -100,8 +61,15 @@ void EntityManager::Initialize() {
) == m_GhostingExcludedZones.end();
// grab hardcore mode settings and load them with sane defaults
Game::config->AddConfigHandler([]() {Game::entityManager->ReloadConfig();});
Game::entityManager->ReloadConfig();
auto hcmode = Game::config->GetValue("hardcore_mode");
m_HardcoreMode = hcmode.empty() ? false : (hcmode == "1");
auto hcUscorePercent = Game::config->GetValue("hardcore_lose_uscore_on_death_percent");
m_HardcoreLoseUscoreOnDeathPercent = hcUscorePercent.empty() ? 10 : std::stoi(hcUscorePercent);
auto hcUscoreMult = Game::config->GetValue("hardcore_uscore_enemies_multiplier");
m_HardcoreUscoreEnemiesMultiplier = hcUscoreMult.empty() ? 2 : std::stoi(hcUscoreMult);
auto hcDropInv = Game::config->GetValue("hardcore_dropinventory_on_death");
m_HardcoreDropinventoryOnDeath = hcDropInv.empty() ? false : (hcDropInv == "1");
// If cloneID is not zero, then hardcore mode is disabled
// aka minigames and props
if (Game::zoneManager->GetZoneID().GetCloneID() != 0) m_HardcoreMode = false;

View File

@@ -75,19 +75,11 @@ public:
const uint32_t GetHardcoreLoseUscoreOnDeathPercent() { return m_HardcoreLoseUscoreOnDeathPercent; };
const bool GetHardcoreDropinventoryOnDeath() { return m_HardcoreDropinventoryOnDeath; };
const uint32_t GetHardcoreUscoreEnemiesMultiplier() { return m_HardcoreUscoreEnemiesMultiplier; };
const std::set<LOT>& GetHardcoreExcludedItemDrops() { return m_HardcoreExcludedItemDrops; };
const float& GetHardcoreUscoreReduction() const { return m_HardcoreUscoreReduction; };
bool GetHardcoreUscoreReduced() const { return m_HardcoreUscoreReduced; };
const std::set<LOT>& GetHardcoreUscoreReducedLots() const { return m_HardcoreUscoreReducedLots; };
const std::set<LOT>& GetHardcoreUscoreExcludedEnemies() const { return m_HardcoreUscoreExcludedEnemies; };
const std::set<LWOMAPID>& GetHardcoreDisabledWorlds() const { return m_HardcoreDisabledWorlds; };
float GetHardcoreCoinKeep() const { return m_HardcoreCoinKeep; }
// Messaging
bool SendMessage(GameMessages::GameMsg& msg) const;
private:
void ReloadConfig();
void SerializeEntities();
void KillEntities();
void DeleteEntities();
@@ -120,13 +112,6 @@ private:
uint32_t m_HardcoreLoseUscoreOnDeathPercent;
bool m_HardcoreDropinventoryOnDeath;
uint32_t m_HardcoreUscoreEnemiesMultiplier;
std::set<LOT> m_HardcoreExcludedItemDrops;
float m_HardcoreUscoreReduction{};
bool m_HardcoreUscoreReduced{};
std::set<LOT> m_HardcoreUscoreReducedLots{};
std::set<LOT> m_HardcoreUscoreExcludedEnemies{};
std::set<LWOMAPID> m_HardcoreDisabledWorlds{};
float m_HardcoreCoinKeep{};
};
#endif // ENTITYMANAGER_H

View File

@@ -9,16 +9,6 @@ Team::Team() {
lootOption = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
}
LWOOBJID Team::GetNextLootOwner() {
lootRound++;
if (lootRound >= members.size()) {
lootRound = 0;
}
return members[lootRound];
}
TeamManager::TeamManager() {
}

View File

@@ -4,8 +4,6 @@
struct Team {
Team();
LWOOBJID GetNextLootOwner();
LWOOBJID teamID = LWOOBJID_EMPTY;
char lootOption = 0;
std::vector<LWOOBJID> members{};

View File

@@ -30,8 +30,6 @@
#include "BitStreamUtils.h"
#include "CheatDetection.h"
#include "CharacterComponent.h"
#include "dConfig.h"
#include "eCharacterVersion.h"
UserManager* UserManager::m_Address = nullptr;
@@ -93,23 +91,6 @@ void UserManager::Initialize() {
StripCR(line);
m_PreapprovedNames.push_back(line);
}
// Initialize cached config values and register a handler to update them on config reload
// This avoids repeated lookups into dConfig at runtime.
if (Game::config) {
m_MuteAutoRejectNames = (Game::config->GetValue("mute_auto_reject_names") == "1");
m_MuteRestrictTrade = (Game::config->GetValue("mute_restrict_trade") == "1");
m_MuteRestrictMail = (Game::config->GetValue("mute_restrict_mail") == "1");
Game::config->AddConfigHandler([this]() {
this->m_MuteAutoRejectNames = (Game::config->GetValue("mute_auto_reject_names") == "1");
this->m_MuteRestrictTrade = (Game::config->GetValue("mute_restrict_trade") == "1");
this->m_MuteRestrictMail = (Game::config->GetValue("mute_restrict_mail") == "1");
});
}
else {
LOG("Warning: dConfig not initialized before UserManager. Cached config values will not be available.");
}
}
UserManager::~UserManager() {
@@ -319,9 +300,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
inStream.Read(eyes);
inStream.Read(mouth);
const bool autoRejectNames = this->GetMuteAutoRejectNames() && u->GetIsMuted();
const auto name = autoRejectNames ? "" : LUWStringName.GetAsString();
const auto name = LUWStringName.GetAsString();
std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex);
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
@@ -339,88 +318,87 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
return;
}
if (autoRejectNames) {
LOG("AccountID: %i is muted, forcing use of predefined name", u->GetAccountID());
}
if (name.empty()) {
LOG("AccountID: %i is creating a character with predefined name: %s", u->GetAccountID(), predefinedName.c_str());
} else {
LOG("AccountID: %i is creating a character with name: %s (temporary: %s)", u->GetAccountID(), name.c_str(), predefinedName.c_str());
}
//Now that the name is ok, we can get a persistent ObjectID:
LWOOBJID objectID = ObjectIDManager::GetPersistentID();
const uint32_t maxRetries = 100;
uint32_t tries = 0;
while (Database::Get()->GetCharacterInfo(objectID) && tries < maxRetries) {
tries++;
LOG("Found a duplicate character %llu, getting a new objectID", objectID);
objectID = ObjectIDManager::GetPersistentID();
}
//Now that the name is ok, we can get an objectID from Master:
ObjectIDManager::RequestPersistentID([=, this](uint32_t persistentID) {
LWOOBJID objectID = persistentID;
GeneralUtils::SetBit(objectID, eObjectBits::CHARACTER);
if (Database::Get()->GetCharacterInfo(objectID)) {
LOG("Character object id unavailable, check object_id_tracker!");
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
return;
}
if (tries >= maxRetries) {
LOG("Failed to get a unique objectID for new character after %i tries, aborting char creation for account %i", maxRetries, u->GetAccountID());
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
return;
}
std::stringstream xml;
xml << "<obj v=\"1\">";
std::stringstream xml;
xml << "<obj v=\"1\">";
xml << "<mf hc=\"" << hairColor << "\" hs=\"" << hairStyle << "\" hd=\"0\" t=\"" << shirtColor << "\" l=\"" << pantsColor;
xml << "\" hdc=\"0\" cd=\"" << shirtStyle << "\" lh=\"" << lh << "\" rh=\"" << rh << "\" es=\"" << eyebrows << "\" ";
xml << "ess=\"" << eyes << "\" ms=\"" << mouth << "\"/>";
xml << "<mf hc=\"" << hairColor << "\" hs=\"" << hairStyle << "\" hd=\"0\" t=\"" << shirtColor << "\" l=\"" << pantsColor;
xml << "\" hdc=\"0\" cd=\"" << shirtStyle << "\" lh=\"" << lh << "\" rh=\"" << rh << "\" es=\"" << eyebrows << "\" ";
xml << "ess=\"" << eyes << "\" ms=\"" << mouth << "\"/>";
xml << "<char acct=\"" << u->GetAccountID() << "\" cc=\"0\" gm=\"0\" ft=\"0\" llog=\"" << time(NULL) << "\" ";
xml << "ls=\"0\" lzx=\"-626.5847\" lzy=\"613.3515\" lzz=\"-28.6374\" lzrx=\"0.0\" lzry=\"0.7015\" lzrz=\"0.0\" lzrw=\"0.7126\" ";
xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\">";
xml << "<vl><l id=\"1000\" cid=\"0\"/></vl>";
xml << "<char acct=\"" << u->GetAccountID() << "\" cc=\"0\" gm=\"0\" ft=\"0\" llog=\"" << time(NULL) << "\" ";
xml << "ls=\"0\" lzx=\"-626.5847\" lzy=\"613.3515\" lzz=\"-28.6374\" lzrx=\"0.0\" lzry=\"0.7015\" lzrz=\"0.0\" lzrw=\"0.7126\" ";
xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\">";
xml << "<vl><l id=\"1000\" cid=\"0\"/></vl>";
xml << "</char>";
xml << "</char>";
xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
xml << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
xml << "<inv><bag><b t=\"0\" m=\"20\"/><b t=\"1\" m=\"40\"/><b t=\"2\" m=\"240\"/><b t=\"3\" m=\"240\"/><b t=\"14\" m=\"40\"/></bag><items><in t=\"0\">";
xml << "<inv><bag><b t=\"0\" m=\"20\"/><b t=\"1\" m=\"40\"/><b t=\"2\" m=\"240\"/><b t=\"3\" m=\"240\"/><b t=\"14\" m=\"40\"/></bag><items><in t=\"0\">";
LWOOBJID lwoidforshirt = ObjectIDManager::GenerateRandomObjectID();
LWOOBJID lwoidforpants;
LWOOBJID lwoidforshirt = ObjectIDManager::GetPersistentID();
LWOOBJID lwoidforpants = ObjectIDManager::GetPersistentID();
do {
lwoidforpants = ObjectIDManager::GenerateRandomObjectID();
} while (lwoidforpants == lwoidforshirt); //Make sure we don't have the same ID for both shirt and pants
xml << "<i l=\"" << shirtLOT << "\" id=\"" << lwoidforshirt << "\" s=\"0\" c=\"1\" eq=\"1\" b=\"1\"/>";
xml << "<i l=\"" << pantsLOT << "\" id=\"" << lwoidforpants << "\" s=\"1\" c=\"1\" eq=\"1\" b=\"1\"/>";
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER);
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER);
GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT);
xml << "</in></items></inv><lvl l=\"1\" cv=\"" << GeneralUtils::ToUnderlying(eCharacterVersion::UP_TO_DATE) << "\" sb=\"500\"/><flag></flag></obj>";
xml << "<i l=\"" << shirtLOT << "\" id=\"" << lwoidforshirt << "\" s=\"0\" c=\"1\" eq=\"1\" b=\"1\"/>";
xml << "<i l=\"" << pantsLOT << "\" id=\"" << lwoidforpants << "\" s=\"1\" c=\"1\" eq=\"1\" b=\"1\"/>";
//Check to see if our name was pre-approved:
bool nameOk = IsNamePreapproved(name);
xml << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
//Check to see if our name was pre-approved:
bool nameOk = IsNamePreapproved(name);
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
// If predefined name is invalid, change it to be their object id
// that way more than one player can create characters if the predefined name files are not provided
auto assignedPredefinedName = predefinedName;
if (assignedPredefinedName == "INVALID") {
std::stringstream nameObjID;
nameObjID << "minifig" << objectID;
assignedPredefinedName = nameObjID.str();
}
// If predefined name is invalid, change it to be their object id
// that way more than one player can create characters if the predefined name files are not provided
auto assignedPredefinedName = predefinedName;
if (assignedPredefinedName == "INVALID") {
std::stringstream nameObjID;
nameObjID << "minifig" << objectID;
assignedPredefinedName = nameObjID.str();
}
std::string_view nameToAssign = !name.empty() && nameOk ? name : assignedPredefinedName;
std::string pendingName = !name.empty() && !nameOk ? name : "";
std::string_view nameToAssign = !name.empty() && nameOk ? name : assignedPredefinedName;
std::string pendingName = !name.empty() && !nameOk ? name : "";
ICharInfo::Info info;
info.name = nameToAssign;
info.pendingName = pendingName;
info.id = objectID;
info.accountId = u->GetAccountID();
ICharInfo::Info info;
info.name = nameToAssign;
info.pendingName = pendingName;
info.id = objectID;
info.accountId = u->GetAccountID();
Database::Get()->InsertNewCharacter(info);
Database::Get()->InsertNewCharacter(info);
//Now finally insert our character xml:
Database::Get()->InsertCharacterXml(objectID, xml.str());
//Now finally insert our character xml:
Database::Get()->InsertCharacterXml(objectID, xml.str());
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
UserManager::RequestCharacterList(sysAddr);
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
UserManager::RequestCharacterList(sysAddr);
});
}
void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) {
@@ -473,10 +451,9 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
LUWString LUWStringName;
inStream.Read(LUWStringName);
auto newName = LUWStringName.GetAsString();
const auto newName = LUWStringName.GetAsString();
Character* character = nullptr;
const bool autoRejectNames = this->GetMuteAutoRejectNames() && u->GetIsMuted();
//Check if this user has this character:
bool ownsCharacter = CheatDetection::VerifyLwoobjidIsSender(
@@ -497,30 +474,13 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
if (!ownsCharacter || !character) {
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR);
} else if (ownsCharacter && character) {
if (autoRejectNames) {
// Create a random preapproved name (fallback to default if none available)
if (!m_FirstNames.empty() && !m_MiddleNames.empty() && !m_LastNames.empty()) {
std::string firstName = GeneralUtils::GetRandomElement(m_FirstNames);
std::string middleName = GeneralUtils::GetRandomElement(m_MiddleNames);
std::string lastName = GeneralUtils::GetRandomElement(m_LastNames);
newName = firstName + middleName + lastName;
} else {
newName = "character" + std::to_string(objectID);
}
}
if (newName == character->GetName()) {
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_UNAVAILABLE);
return;
}
if (!Database::Get()->GetCharacterInfo(newName)) {
if (autoRejectNames) {
Database::Get()->SetCharacterName(objectID, newName);
LOG("Character %s auto-renamed to preapproved name %s due to mute", character->GetName().c_str(), newName.c_str());
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
UserManager::RequestCharacterList(sysAddr);
} else if (IsNamePreapproved(newName)) {
if (IsNamePreapproved(newName)) {
Database::Get()->SetCharacterName(objectID, newName);
LOG("Character %s now known as %s", character->GetName().c_str(), newName.c_str());
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);

View File

@@ -41,11 +41,6 @@ public:
size_t GetUserCount() const { return m_Users.size(); }
// Access cached config values
bool GetMuteAutoRejectNames() const { return m_MuteAutoRejectNames; }
bool GetMuteRestrictTrade() const { return m_MuteRestrictTrade; }
bool GetMuteRestrictMail() const { return m_MuteRestrictMail; }
private:
static UserManager* m_Address; //Singleton
std::map<SystemAddress, User*> m_Users;
@@ -55,11 +50,6 @@ private:
std::vector<std::string> m_MiddleNames;
std::vector<std::string> m_LastNames;
std::vector<std::string> m_PreapprovedNames;
// Cached config values that can change on config reload
bool m_MuteAutoRejectNames = false;
bool m_MuteRestrictTrade = false;
bool m_MuteRestrictMail = false;
};
#endif // USERMANAGER_H

View File

@@ -9,7 +9,7 @@
#include "UserManager.h"
#include "CDMissionsTable.h"
AchievementVendorComponent::AchievementVendorComponent(Entity* parent, const int32_t componentID) : VendorComponent(parent, componentID) {
AchievementVendorComponent::AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {
RefreshInventory(true);
};

View File

@@ -11,7 +11,7 @@ class Entity;
class AchievementVendorComponent final : public VendorComponent {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR;
AchievementVendorComponent(Entity* parent, const int32_t componentID);
AchievementVendorComponent(Entity* parent);
void RefreshInventory(bool isCreation = false) override;
bool SellsItem(Entity* buyer, const LOT lot);

View File

@@ -30,7 +30,7 @@
#include "CharacterComponent.h"
#include "Amf3.h"
ActivityComponent::ActivityComponent(Entity* parent, int32_t componentID) : Component(parent, componentID) {
ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Component(parent) {
using namespace GameMessages;
RegisterMsg<GetObjectReportInfo>(this, &ActivityComponent::OnGetObjectReportInfo);
/*
@@ -39,12 +39,39 @@ ActivityComponent::ActivityComponent(Entity* parent, int32_t componentID) : Comp
* if activityID is specified and if that column exists in the activities table, update the activity info with that data.
*/
m_ActivityID = componentID;
LoadActivityData(componentID);
m_ActivityID = activityID;
LoadActivityData(activityID);
if (m_Parent->HasVar(u"activityID")) {
m_ActivityID = parent->GetVar<int32_t>(u"activityID");
LoadActivityData(m_ActivityID);
}
auto* destroyableComponent = m_Parent->GetComponent<DestroyableComponent>();
if (destroyableComponent) {
// First lookup the loot matrix id for this component id.
CDActivityRewardsTable* activityRewardsTable = CDClientManager::GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); });
uint32_t startingLMI = 0;
// If we have one, set the starting loot matrix id to that.
if (activityRewards.size() > 0) {
startingLMI = activityRewards[0].LootMatrixIndex;
}
if (startingLMI > 0) {
// We may have more than 1 loot matrix index to use depending ont the size of the team that is looting the activity.
// So this logic will get the rest of the loot matrix indices for this activity.
std::vector<CDActivityRewards> objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); });
for (const auto& item : objectTemplateActivities) {
if (item.activityRating > 0 && item.activityRating < 5) {
m_ActivityLootMatrices.insert({ item.activityRating, item.LootMatrixIndex });
}
}
}
}
}
void ActivityComponent::LoadActivityData(const int32_t activityId) {
CDActivitiesTable* activitiesTable = CDClientManager::GetTable<CDActivitiesTable>();
@@ -671,6 +698,10 @@ bool ActivityComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
}
}
auto& lootMatrices = activityInfo.PushDebug("Loot Matrices");
for (const auto& [activityRating, lootMatrixID] : m_ActivityLootMatrices) {
lootMatrices.PushDebug<AMFIntValue>("Loot Matrix " + std::to_string(activityRating)) = lootMatrixID;
}
activityInfo.PushDebug<AMFIntValue>("ActivityID") = m_ActivityID;
return true;
}

View File

@@ -341,13 +341,19 @@ public:
*/
void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; };
/**
* Returns the LMI that this activity points to for a team size
* @param teamSize the team size to get the LMI for
* @return the LMI that this activity points to for a team size
*/
uint32_t GetLootMatrixForTeamSize(uint32_t teamSize) { return m_ActivityLootMatrices[teamSize]; }
private:
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
/**
* The database information for this activity
*/
CDActivities m_ActivityInfo{};
CDActivities m_ActivityInfo;
/**
* All the active instances of this activity
@@ -364,6 +370,11 @@ private:
*/
std::vector<ActivityPlayer*> m_ActivityPlayers;
/**
* LMIs for team sizes
*/
std::unordered_map<uint32_t, uint32_t> m_ActivityLootMatrices;
/**
* The activity id
*/

View File

@@ -28,7 +28,7 @@
#include "CDPhysicsComponentTable.h"
#include "dNavMesh.h"
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id) : Component(parent) {
m_Target = LWOOBJID_EMPTY;
m_DirtyStateOrTarget = true;
m_State = AiState::spawn;
@@ -43,7 +43,7 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t compo
//Grab the aggro information from BaseCombatAI:
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
componentQuery.bind(1, static_cast<int>(componentID));
componentQuery.bind(1, static_cast<int>(id));
auto componentResult = componentQuery.execQuery();
@@ -111,12 +111,12 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t compo
int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY);
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
const auto controllablePhysicsID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable<CDPhysicsComponentTable>();
if (physicsComponentTable != nullptr) {
auto* info = physicsComponentTable->GetByID(controllablePhysicsID);
auto* info = physicsComponentTable->GetByID(componentID);
if (info != nullptr) {
collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
}

View File

@@ -49,7 +49,7 @@ class BaseCombatAIComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
BaseCombatAIComponent(Entity* parentEntity, int32_t componentID);
BaseCombatAIComponent(Entity* parentEntity, uint32_t id);
~BaseCombatAIComponent() override;
void Update(float deltaTime) override;

View File

@@ -9,7 +9,7 @@
#include "BitStream.h"
#include "eTriggerEventType.h"
BouncerComponent::BouncerComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) {
m_PetEnabled = false;
m_PetBouncerEnabled = false;
m_PetSwitchLoaded = false;

View File

@@ -14,7 +14,7 @@ class BouncerComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
BouncerComponent(Entity* parentEntity, const int32_t componentID);
BouncerComponent(Entity* parentEntity);
~BouncerComponent() override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -26,7 +26,7 @@ namespace {
};
}
BuffComponent::BuffComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
BuffComponent::BuffComponent(Entity* parent) : Component(parent) {
}
BuffComponent::~BuffComponent() {

View File

@@ -51,7 +51,7 @@ class BuffComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
explicit BuffComponent(Entity* parent, const int32_t componentID);
explicit BuffComponent(Entity* parent);
~BuffComponent();

View File

@@ -9,7 +9,7 @@
#include "Item.h"
#include "PropertyManagementComponent.h"
BuildBorderComponent::BuildBorderComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
BuildBorderComponent::BuildBorderComponent(Entity* parent) : Component(parent) {
}
BuildBorderComponent::~BuildBorderComponent() {

View File

@@ -18,7 +18,7 @@ class BuildBorderComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
BuildBorderComponent(Entity* parent, const int32_t componentID);
BuildBorderComponent(Entity* parent);
~BuildBorderComponent() override;
/**

View File

@@ -25,7 +25,7 @@
#include "MessageType/Game.h"
#include <ctime>
CharacterComponent::CharacterComponent(Entity* parent, const int32_t componentID, Character* character, const SystemAddress& systemAddress) : Component(parent, componentID) {
CharacterComponent::CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress) : Component(parent) {
m_Character = character;
m_IsRacing = false;
@@ -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(sstream.str());
vl.PushDebug<AMFStringValue>(sstream.str()) = "";
}
// visited locations
@@ -84,30 +84,6 @@ bool CharacterComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
cmptType.PushDebug<AMFIntValue>("Current Activity Type") = GeneralUtils::ToUnderlying(m_CurrentActivity);
cmptType.PushDebug<AMFDoubleValue>("Property Clone ID") = m_Character->GetPropertyCloneID();
auto& flagCmptType = reportInfo.info->PushDebug("Player Flag");
auto& allFlags = flagCmptType.PushDebug("All flags");
for (const auto& [id, flagChunk] : m_Character->GetPlayerFlags()) {
const auto base = id * 64;
auto flagChunkCopy = flagChunk;
for (int i = 0; i < 64; i++) {
if (static_cast<bool>(flagChunkCopy & 1)) {
const int32_t flagId = base + i;
std::stringstream stream;
stream << "Flag: " << flagId;
allFlags.PushDebug(stream.str());
}
flagChunkCopy >>= 1;
}
}
auto& sessionFlags = flagCmptType.PushDebug("Session Only Flags");
for (const auto flagId : m_Character->GetSessionFlags()) {
std::stringstream stream;
stream << "Flag: " << flagId;
sessionFlags.PushDebug(stream.str());
}
return true;
}
@@ -883,7 +859,7 @@ void CharacterComponent::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) const {
character->SetZoneID(zoneID);
character->SetZoneInstance(zoneInstance);
character->SetZoneClone(zoneClone);
characterComponent->SetLastRocketConfig(u"");
characterComponent->AddVisitedLevel(LWOZONEID(zoneID, LWOINSTANCEID_INVALID, zoneClone));

View File

@@ -70,7 +70,7 @@ class CharacterComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
CharacterComponent(Entity* parent, const int32_t componentID, Character* character, const SystemAddress& systemAddress);
CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress);
~CharacterComponent() override;
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;

View File

@@ -7,7 +7,7 @@
class CollectibleComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE;
CollectibleComponent(Entity* parentEntity, const int32_t componentID, const int32_t collectibleId) : Component(parentEntity, componentID), m_CollectibleId(collectibleId) {}
CollectibleComponent(Entity* parentEntity, int32_t collectibleId) : Component(parentEntity), m_CollectibleId(collectibleId) {}
int16_t GetCollectibleId() const { return m_CollectibleId; }
void Serialize(RakNet::BitStream& outBitStream, bool isConstruction) override;

View File

@@ -19,7 +19,7 @@ class Entity;
*/
class Component {
public:
Component(Entity* parent, const int32_t componentID) : m_Parent{ parent }, m_ComponentID{componentID} {}
Component(Entity* parent) : m_Parent{ parent } {}
virtual ~Component() = default;
/**
@@ -28,8 +28,6 @@ public:
*/
Entity* GetParent() const { return m_Parent; }
[[nodiscard]] int32_t GetComponentID() const noexcept { return m_ComponentID; }
/**
* Updates the component in the game loop
* @param deltaTime time passed since last update
@@ -72,11 +70,4 @@ protected:
* The entity that owns this component
*/
Entity* m_Parent;
// The component ID, this should never be changed after initialization
// This is used in various different ways
// 1. To identify which entry this component is in its corresponding table
// 2. To mark that an Entity should have the component with no database entry (it will be 0 in this case)
// 3. The component exists implicitly due to design (CollectibleComponent always has a DestructibleComponent accompanying it). In this case the ID will be -1.
const int32_t m_ComponentID;
};

View File

@@ -17,7 +17,7 @@
#include "StringifiedEnum.h"
#include "Amf3.h"
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity, const int32_t componentID) : PhysicsComponent(entity, componentID) {
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity, int32_t componentId) : PhysicsComponent(entity, componentId) {
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &ControllablePhysicsComponent::OnGetObjectReportInfo);
m_Velocity = {};

View File

@@ -25,7 +25,7 @@ class ControllablePhysicsComponent : public PhysicsComponent {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
ControllablePhysicsComponent(Entity* entity, const int32_t componentID);
ControllablePhysicsComponent(Entity* entity, int32_t componentId);
~ControllablePhysicsComponent() override;
void Update(float deltaTime) override;

View File

@@ -3,9 +3,6 @@
#include "Logger.h"
#include "Game.h"
#include "dConfig.h"
#include "CDLootMatrixTable.h"
#include "CDLootTableTable.h"
#include "CDRarityTableTable.h"
#include "Amf3.h"
#include "AmfSerialize.h"
@@ -40,14 +37,13 @@
#include "eMissionTaskType.h"
#include "eStateChangeType.h"
#include "eGameActivity.h"
#include <ranges>
#include "CDComponentsRegistryTable.h"
Implementation<bool, const Entity*> DestroyableComponent::IsEnemyImplentation;
Implementation<bool, const Entity*> DestroyableComponent::IsFriendImplentation;
DestroyableComponent::DestroyableComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
using namespace GameMessages;
m_iArmor = 0;
m_fMaxArmor = 0.0f;
@@ -670,6 +666,11 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
return;
}
//check if hardcore mode is enabled
if (Game::entityManager->GetHardcoreMode()) {
DoHardcoreModeDrops(source);
}
Smash(source, eKillType::VIOLENT, u"", skillID);
}
@@ -697,13 +698,6 @@ void DestroyableComponent::NotifySubscribers(Entity* attacker, uint32_t damage)
}
void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType, uint32_t skillID) {
if (m_IsDead) return;
//check if hardcore mode is enabled
if (Game::entityManager->GetHardcoreMode()) {
DoHardcoreModeDrops(source);
}
if (m_iHealth > 0) {
SetArmor(0);
SetHealth(0);
@@ -711,7 +705,6 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
Game::entityManager->SerializeEntity(m_Parent);
}
m_IsDead = true;
m_KillerID = source;
auto* owner = Game::entityManager->GetEntity(source);
@@ -759,11 +752,40 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
//NANI?!
if (!isPlayer) {
if (owner != nullptr) {
Loot::DropLoot(owner, m_Parent->GetObjectID(), GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
auto* team = TeamManager::Instance()->GetTeam(owner->GetObjectID());
if (team != nullptr && m_Parent->GetComponent<BaseCombatAIComponent>() != nullptr) {
LWOOBJID specificOwner = LWOOBJID_EMPTY;
auto* scriptedActivityComponent = m_Parent->GetComponent<ScriptedActivityComponent>();
uint32_t teamSize = team->members.size();
uint32_t lootMatrixId = GetLootMatrixID();
if (scriptedActivityComponent) {
lootMatrixId = scriptedActivityComponent->GetLootMatrixForTeamSize(teamSize);
}
if (team->lootOption == 0) { // Round robin
specificOwner = TeamManager::Instance()->GetNextLootOwner(team);
auto* member = Game::entityManager->GetEntity(specificOwner);
if (member) Loot::DropLoot(member, m_Parent->GetObjectID(), lootMatrixId, GetMinCoins(), GetMaxCoins());
} else {
for (const auto memberId : team->members) { // Free for all
auto* member = Game::entityManager->GetEntity(memberId);
if (member == nullptr) continue;
Loot::DropLoot(member, m_Parent->GetObjectID(), lootMatrixId, GetMinCoins(), GetMaxCoins());
}
}
} else { // drop loot for non team user
Loot::DropLoot(owner, m_Parent->GetObjectID(), GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
}
}
} else {
//Check if this zone allows coin drops
if (Game::zoneManager->GetPlayerLoseCoinOnDeath() && !Game::entityManager->GetHardcoreMode()) {
if (Game::zoneManager->GetPlayerLoseCoinOnDeath()) {
auto* character = m_Parent->GetCharacter();
uint64_t coinsTotal = character->GetCoins();
const uint64_t minCoinsToLose = Game::zoneManager->GetWorldConfig().coinsLostOnDeathMin;
@@ -776,15 +798,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
coinsTotal -= coinsToLose;
GameMessages::DropClientLoot lootMsg{};
lootMsg.target = m_Parent->GetObjectID();
lootMsg.ownerID = m_Parent->GetObjectID();
lootMsg.currency = coinsToLose;
lootMsg.spawnPos = m_Parent->GetPosition();
lootMsg.sourceID = source;
lootMsg.item = LOT_NULL;
lootMsg.Send();
lootMsg.Send(m_Parent->GetSystemAddress());
Loot::DropLoot(m_Parent, m_Parent->GetObjectID(), -1, coinsToLose, coinsToLose);
character->SetCoins(coinsTotal, eLootSourceType::PICKUP);
}
}
@@ -967,8 +981,7 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
auto* character = m_Parent->GetComponent<CharacterComponent>();
auto uscore = character->GetUScore();
auto uscoreToLose = static_cast<uint64_t>(uscore * (Game::entityManager->GetHardcoreLoseUscoreOnDeathPercent() / 100.0f));
LOG("Player %llu has lost %llu uscore!", m_Parent->GetObjectID(), uscoreToLose);
auto uscoreToLose = uscore * (Game::entityManager->GetHardcoreLoseUscoreOnDeathPercent() / 100);
character->SetUScore(uscore - uscoreToLose);
GameMessages::SendModifyLEGOScore(m_Parent, m_Parent->GetSystemAddress(), -uscoreToLose, eLootSourceType::MISSION);
@@ -982,18 +995,13 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
if (items) {
auto itemMap = items->GetItems();
if (!itemMap.empty()) {
for (const auto item : itemMap | std::views::values) {
// Don't drop excluded items or null ones
if (!item || Game::entityManager->GetHardcoreExcludedItemDrops().contains(item->GetLot())) continue;
GameMessages::DropClientLoot lootMsg{};
lootMsg.target = m_Parent->GetObjectID();
lootMsg.ownerID = m_Parent->GetObjectID();
lootMsg.sourceID = m_Parent->GetObjectID();
lootMsg.item = item->GetLot();
lootMsg.count = 1;
lootMsg.spawnPos = m_Parent->GetPosition();
for (int i = 0; i < item->GetCount(); i++) Loot::DropItem(*m_Parent, lootMsg);
item->SetCount(0, false, false);
for (const auto& item : itemMap) {
//drop the item:
if (!item.second) continue;
// don't drop the thinkng cap
if (item.second->GetLot() == 6086) continue;
GameMessages::SendDropClientLoot(m_Parent, source, item.second->GetLot(), 0, m_Parent->GetPosition(), item.second->GetCount());
item.second->SetCount(0, false, false);
}
Game::entityManager->SerializeEntity(m_Parent);
}
@@ -1004,55 +1012,32 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
//get character:
auto* chars = m_Parent->GetCharacter();
if (chars) {
auto oldCoins = chars->GetCoins();
// Floor this so there arent coins generated from rounding
auto coins = static_cast<uint64_t>(oldCoins * Game::entityManager->GetHardcoreCoinKeep());
auto coinsToDrop = oldCoins - coins;
LOG("Player had %llu coins, will lose %i coins to have %i", oldCoins, coinsToDrop, coins);
auto coins = chars->GetCoins();
//lose all coins:
chars->SetCoins(coins, eLootSourceType::NONE);
chars->SetCoins(0, eLootSourceType::NONE);
//drop all coins:
constexpr auto MAX_TO_DROP_PER_GM = 100'000;
GameMessages::DropClientLoot lootMsg{};
lootMsg.target = m_Parent->GetObjectID();
lootMsg.ownerID = m_Parent->GetObjectID();
lootMsg.spawnPos = m_Parent->GetPosition();
lootMsg.sourceID = source;
lootMsg.item = LOT_NULL;
lootMsg.Send();
lootMsg.Send(m_Parent->GetSystemAddress());
while (coinsToDrop > MAX_TO_DROP_PER_GM) {
LOG("Dropping 100,000, %llu left", coinsToDrop);
lootMsg.currency = 100'000;
lootMsg.Send();
lootMsg.Send(m_Parent->GetSystemAddress());
coinsToDrop -= 100'000;
}
lootMsg.currency = coinsToDrop;
lootMsg.Send();
lootMsg.Send(m_Parent->GetSystemAddress());
GameMessages::SendDropClientLoot(m_Parent, source, LOT_NULL, coins, m_Parent->GetPosition());
}
// Reload the player since we can't normally reduce uscore from the server and we want the UI to update
// do this last so we don't get killed.... again
Game::entityManager->DestructEntity(m_Parent);
Game::entityManager->ConstructEntity(m_Parent);
return;
}
//award the player some u-score:
auto* player = Game::entityManager->GetEntity(source);
if (player && player->IsPlayer()) {
const auto lot = m_Parent->GetLOT();
auto* playerStats = player->GetComponent<CharacterComponent>();
if (playerStats && GetMaxHealth() > 0 && !Game::entityManager->GetHardcoreUscoreExcludedEnemies().contains(lot)) {
if (playerStats) {
//get the maximum health from this enemy:
auto maxHealth = GetMaxHealth();
const auto uscoreMultiplier = Game::entityManager->GetHardcoreUscoreEnemiesMultiplier();
const bool isUscoreReducedLot =
Game::entityManager->GetHardcoreUscoreReducedLots().contains(lot) ||
Game::entityManager->GetHardcoreUscoreReduced();
const auto uscoreReduction = isUscoreReducedLot ? Game::entityManager->GetHardcoreUscoreReduction() : 1.0f;
int uscore = maxHealth * Game::entityManager->GetHardcoreUscoreEnemiesMultiplier() * uscoreReduction;
LOG("Rewarding player %llu with %i uscore for killing enemy %i", player->GetObjectID(), uscore, lot);
int uscore = maxHealth * Game::entityManager->GetHardcoreUscoreEnemiesMultiplier();
playerStats->SetUScore(playerStats->GetUScore() + uscore);
GameMessages::SendModifyLEGOScore(player, player->GetSystemAddress(), uscore, eLootSourceType::MISSION);
@@ -1063,89 +1048,38 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
auto& reportInfo = static_cast<GameMessages::GetObjectReportInfo&>(msg);
auto& destroyableInfo = reportInfo.info->PushDebug("Destroyable");
destroyableInfo.PushDebug<AMFIntValue>("DestructibleComponent DB Table Template ID") = m_ComponentID;
if (m_CurrencyIndex == -1) {
destroyableInfo.PushDebug<AMFBoolValue>("Has Loot Currency") = false;
} else {
destroyableInfo.PushDebug<AMFIntValue>("Loot Currency ID") = m_CurrencyIndex;
auto& detailedCoinInfo = destroyableInfo.PushDebug("Coin Info");
detailedCoinInfo.PushDebug<AMFIntValue>("Min Coins") = m_MinCoins;
detailedCoinInfo.PushDebug<AMFIntValue>("Max Coins") = m_MaxCoins;
}
if (m_LootMatrixID == -1 || m_LootMatrixID == 0) {
destroyableInfo.PushDebug<AMFBoolValue>("Has Loot Matrix") = false;
} else {
auto& lootInfo = destroyableInfo.PushDebug("Loot Info");
lootInfo.PushDebug<AMFIntValue>("Loot Matrix ID") = m_LootMatrixID;
auto* const componentsRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
auto* const itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
auto* const lootMatrixTable = CDClientManager::GetTable<CDLootMatrixTable>();
auto* const lootTableTable = CDClientManager::GetTable<CDLootTableTable>();
auto* const rarityTableTable = CDClientManager::GetTable<CDRarityTableTable>();
const auto& matrix = lootMatrixTable->GetMatrix(m_LootMatrixID);
for (const auto& entry : matrix) {
auto& thisEntry = lootInfo.PushDebug("Loot table Index - " + std::to_string(entry.LootTableIndex));
thisEntry.PushDebug<AMFDoubleValue>("Percent chance to drop") = entry.percent * 100.0f;
thisEntry.PushDebug<AMFDoubleValue>("Minimum amount to drop") = entry.minToDrop;
thisEntry.PushDebug<AMFDoubleValue>("Maximum amount to drop") = entry.maxToDrop;
const auto& lootTable = lootTableTable->GetTable(entry.LootTableIndex);
const auto& rarityTable = rarityTableTable->GetRarityTable(entry.RarityTableIndex);
auto& thisRarity = thisEntry.PushDebug("Rarity");
for (const auto& rarity : rarityTable) {
thisRarity.PushDebug<AMFDoubleValue>("Rarity " + std::to_string(rarity.rarity)) = rarity.randmax;
}
auto& thisItems = thisEntry.PushDebug("Drop(s) Info");
for (const auto& loot : lootTable) {
uint32_t itemComponentId = componentsRegistryTable->GetByIDAndType(loot.itemid, eReplicaComponentType::ITEM);
uint32_t rarity = itemComponentTable->GetItemComponentByID(itemComponentId).rarity;
auto title = "%[Objects_" + std::to_string(loot.itemid) + "_name] " + std::to_string(loot.itemid);
if (loot.MissionDrop) title += " - Mission Drop";
thisItems.PushDebug(title);
}
}
}
auto* const entity = Game::entityManager->GetEntity(reportInfo.clientID);
destroyableInfo.PushDebug<AMFBoolValue>("Is on your team") = entity ? IsFriend(entity) : false;
auto& stats = destroyableInfo.PushDebug("Statistics");
stats.PushDebug<AMFIntValue>("Health") = m_iHealth;
stats.PushDebug<AMFDoubleValue>("Maximum Health") = m_fMaxHealth;
stats.PushDebug<AMFIntValue>("Armor") = m_iArmor;
stats.PushDebug<AMFDoubleValue>("Maximum Armor") = m_fMaxArmor;
stats.PushDebug<AMFIntValue>("Imagination") = m_iImagination;
stats.PushDebug<AMFDoubleValue>("Maximum Imagination") = m_fMaxImagination;
stats.PushDebug<AMFIntValue>("Damage Absorption Points") = m_DamageToAbsorb;
destroyableInfo.PushDebug<AMFIntValue>("Health") = m_iHealth;
destroyableInfo.PushDebug<AMFDoubleValue>("Max Health") = m_fMaxHealth;
destroyableInfo.PushDebug<AMFIntValue>("Armor") = m_iArmor;
destroyableInfo.PushDebug<AMFDoubleValue>("Max Armor") = m_fMaxArmor;
destroyableInfo.PushDebug<AMFIntValue>("Imagination") = m_iImagination;
destroyableInfo.PushDebug<AMFDoubleValue>("Max Imagination") = m_fMaxImagination;
destroyableInfo.PushDebug<AMFIntValue>("Damage To Absorb") = m_DamageToAbsorb;
destroyableInfo.PushDebug<AMFBoolValue>("Is GM Immune") = m_IsGMImmune;
destroyableInfo.PushDebug<AMFBoolValue>("Is Shielded") = m_IsShielded;
destroyableInfo.PushDebug<AMFIntValue>("Attacks To Block") = m_AttacksToBlock;
destroyableInfo.PushDebug<AMFIntValue>("Damage Reduction") = m_DamageReduction;
std::stringstream factionsStream;
auto& factions = destroyableInfo.PushDebug("Factions");
size_t i = 0;
for (const auto factionID : m_FactionIDs) {
factionsStream << factionID << " ";
factions.PushDebug<AMFStringValue>(std::to_string(i++) + " " + std::to_string(factionID)) = "";
}
destroyableInfo.PushDebug<AMFStringValue>("Factions") = factionsStream.str();
factionsStream.str("");
auto& enemyFactions = destroyableInfo.PushDebug("Enemy Factions");
i = 0;
for (const auto enemyFactionID : m_EnemyFactionIDs) {
factionsStream << enemyFactionID << " ";
enemyFactions.PushDebug<AMFStringValue>(std::to_string(i++) + " " + std::to_string(enemyFactionID)) = "";
}
destroyableInfo.PushDebug<AMFStringValue>("Enemy Factions") = factionsStream.str();
destroyableInfo.PushDebug<AMFBoolValue>("Is Smashable") = m_IsSmashable;
destroyableInfo.PushDebug<AMFBoolValue>("Is Dead") = m_IsDead;
destroyableInfo.PushDebug<AMFBoolValue>("Is Smashed") = m_IsSmashed;
destroyableInfo.PushDebug<AMFBoolValue>("Is Module Assembly") = m_IsModuleAssembly;
destroyableInfo.PushDebug<AMFDoubleValue>("Explode Factor") = m_ExplodeFactor;
destroyableInfo.PushDebug<AMFBoolValue>("Has Threats") = m_HasThreats;
destroyableInfo.PushDebug<AMFIntValue>("Loot Matrix ID") = m_LootMatrixID;
destroyableInfo.PushDebug<AMFIntValue>("Min Coins") = m_MinCoins;
destroyableInfo.PushDebug<AMFIntValue>("Max Coins") = m_MaxCoins;
destroyableInfo.PushDebug<AMFStringValue>("Killer ID") = std::to_string(m_KillerID);
// "Scripts"; idk what to do about scripts yet
@@ -1160,25 +1094,7 @@ bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
immuneCounts.PushDebug<AMFIntValue>("Quickbuild Interrupt") = m_ImmuneToQuickbuildInterruptCount;
immuneCounts.PushDebug<AMFIntValue>("Pull To Point") = m_ImmuneToPullToPointCount;
auto& deathInfo = destroyableInfo.PushDebug("Death Info");
deathInfo.PushDebug<AMFBoolValue>("Is Dead") = m_IsDead;
switch (m_DeathBehavior) {
case 0:
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Fade";
break;
case 1:
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Stay";
break;
case 2:
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Immediate";
break;
case -1:
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Invulnerable";
break;
default:
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Other";
break;
}
destroyableInfo.PushDebug<AMFIntValue>("Death Behavior") = m_DeathBehavior;
destroyableInfo.PushDebug<AMFDoubleValue>("Damage Cooldown Timer") = m_DamageCooldownTimer;
return true;

View File

@@ -26,7 +26,7 @@ class DestroyableComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE;
DestroyableComponent(Entity* parentEntity, const int32_t componentID);
DestroyableComponent(Entity* parentEntity);
~DestroyableComponent() override;
void Update(float deltaTime) override;
@@ -370,8 +370,6 @@ public:
*/
uint32_t GetLootMatrixID() const { return m_LootMatrixID; }
void SetCurrencyIndex(int32_t currencyIndex) { m_CurrencyIndex = currencyIndex; }
/**
* Returns the ID of the entity that killed this entity, if any
* @return the ID of the entity that killed this entity, if any
@@ -473,8 +471,6 @@ public:
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
bool OnSetFaction(GameMessages::GameMsg& msg);
void SetIsDead(const bool value) { m_IsDead = value; }
static Implementation<bool, const Entity*> IsEnemyImplentation;
static Implementation<bool, const Entity*> IsFriendImplentation;
@@ -589,9 +585,6 @@ private:
*/
uint32_t m_LootMatrixID;
// The currency index to determine how much loot to drop
int32_t m_CurrencyIndex{ -1 };
/**
* The min amount of coins that will drop when this entity is smashed
*/

View File

@@ -1,7 +1,7 @@
#include "DonationVendorComponent.h"
#include "Database.h"
DonationVendorComponent::DonationVendorComponent(Entity* parent, const int32_t componentID) : VendorComponent(parent, componentID) {
DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorComponent(parent) {
//LoadConfigData
m_PercentComplete = 0.0;
m_TotalDonated = 0;

View File

@@ -9,7 +9,7 @@ class Entity;
class DonationVendorComponent final : public VendorComponent {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR;
DonationVendorComponent(Entity* parent, const int32_t componentID);
DonationVendorComponent(Entity* parent);
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
uint32_t GetActivityID() {return m_ActivityId;};
void SubmitDonation(uint32_t count);

View File

@@ -1,6 +1,6 @@
#include "GhostComponent.h"
GhostComponent::GhostComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
GhostComponent::GhostComponent(Entity* parent) : Component(parent) {
m_GhostReferencePoint = NiPoint3Constant::ZERO;
m_GhostOverridePoint = NiPoint3Constant::ZERO;
m_GhostOverride = false;

View File

@@ -10,7 +10,7 @@ class NiPoint3;
class GhostComponent final : public Component {
public:
static inline const eReplicaComponentType ComponentType = eReplicaComponentType::GHOST;
GhostComponent(Entity* parent, const int32_t componentID);
GhostComponent(Entity* parent);
~GhostComponent() override;
void SetGhostOverride(bool value) { m_GhostOverride = value; };

View File

@@ -2,7 +2,7 @@
#include "EntityManager.h"
#include "Amf3.h"
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent, const int32_t componentID) : PhysicsComponent(parent, componentID) {
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) {
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &HavokVehiclePhysicsComponent::OnGetObjectReportInfo);
m_Velocity = NiPoint3Constant::ZERO;

View File

@@ -13,7 +13,7 @@ class HavokVehiclePhysicsComponent : public PhysicsComponent {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS;
HavokVehiclePhysicsComponent(Entity* parentEntity, const int32_t componentID);
HavokVehiclePhysicsComponent(Entity* parentEntity, int32_t componentId);
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -39,13 +39,10 @@
#include "CDObjectSkillsTable.h"
#include "CDSkillBehaviorTable.h"
#include "StringifiedEnum.h"
#include "Amf3.h"
#include <ranges>
InventoryComponent::InventoryComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
using namespace GameMessages;
RegisterMsg<GetObjectReportInfo>(this, &InventoryComponent::OnGetObjectReportInfo);
InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
this->m_Dirty = true;
this->m_Equipped = {};
this->m_Pushed = {};
@@ -282,14 +279,7 @@ void InventoryComponent::AddItem(
case 1:
for (size_t i = 0; i < size; i++) {
GameMessages::DropClientLoot lootMsg{};
lootMsg.target = m_Parent->GetObjectID();
lootMsg.ownerID = m_Parent->GetObjectID();
lootMsg.sourceID = m_Parent->GetObjectID();
lootMsg.item = lot;
lootMsg.count = 1;
lootMsg.spawnPos = m_Parent->GetPosition();
Loot::DropItem(*m_Parent, lootMsg);
GameMessages::SendDropClientLoot(this->m_Parent, this->m_Parent->GetObjectID(), lot, 0, this->m_Parent->GetPosition(), 1);
}
break;
@@ -450,7 +440,7 @@ Item* InventoryComponent::FindItemBySubKey(LWOOBJID id, eInventoryType inventory
}
}
bool InventoryComponent::HasSpaceForLoot(const Loot::Return& loot) {
bool InventoryComponent::HasSpaceForLoot(const std::unordered_map<LOT, int32_t>& loot) {
std::unordered_map<eInventoryType, int32_t> spaceOffset{};
uint32_t slotsNeeded = 0;
@@ -636,8 +626,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
for (const auto& pair : this->m_Inventories) {
auto* inventory = pair.second;
static const auto EXCLUDED_INVENTORIES = { VENDOR_BUYBACK, MODELS_IN_BBB, ITEM_SETS };
if (std::ranges::find(EXCLUDED_INVENTORIES, inventory->GetType()) != EXCLUDED_INVENTORIES.end()) {
if (inventory->GetType() == VENDOR_BUYBACK || inventory->GetType() == eInventoryType::MODELS_IN_BBB) {
continue;
}
@@ -1797,105 +1786,3 @@ void InventoryComponent::LoadGroupXml(const tinyxml2::XMLElement& groups) {
groupElement = groupElement->NextSiblingElement("grp");
}
}
void InventoryComponent::RegenerateItemIDs() {
for (auto* const inventory : m_Inventories | std::views::values) {
inventory->RegenerateItemIDs();
}
}
std::string DebugInvToString(const eInventoryType inv, bool verbose) {
switch (inv) {
case ITEMS:
return "Backpack";
case VAULT_ITEMS:
return "Bank";
case BRICKS:
return verbose ? "Bricks" : "Bricks (contents only shown in high-detail report)";
case MODELS_IN_BBB:
return "Models in BBB";
case TEMP_ITEMS:
return "Temp Equip";
case MODELS:
return verbose ? "Model" : "Model (contents only shown in high-detail report)";
case TEMP_MODELS:
return "Module";
case BEHAVIORS:
return "B3 Behavior";
case PROPERTY_DEEDS:
return "Property";
case BRICKS_IN_BBB:
return "Brick In BBB";
case VENDOR:
return "Vendor";
case VENDOR_BUYBACK:
return "BuyBack";
case QUEST:
return "Quest";
case DONATION:
return "Donation";
case VAULT_MODELS:
return "Bank Model";
case ITEM_SETS:
return "Bank Behavior";
case INVALID:
return "Invalid";
case ALL:
return "All";
}
return "";
}
bool InventoryComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
auto& report = static_cast<GameMessages::GetObjectReportInfo&>(msg);
auto& cmpt = report.info->PushDebug("Inventory");
cmpt.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
uint32_t numItems = 0;
for (auto* inventory : m_Inventories | std::views::values) numItems += inventory->GetItems().size();
cmpt.PushDebug<AMFIntValue>("Inventory Item Count") = numItems;
auto& itemsInBags = cmpt.PushDebug("Items in bags");
for (const auto& [id, inventoryMut] : m_Inventories) {
if (!inventoryMut) continue;
const auto* const inventory = inventoryMut;
auto& curInv = itemsInBags.PushDebug(DebugInvToString(id, report.bVerbose) + " - " + std::to_string(id));
for (uint32_t i = 0; i < inventory->GetSize(); i++) {
const auto* const item = inventory->FindItemBySlot(i);
if (!item) continue;
std::stringstream ss;
ss << "%[Objects_" << item->GetLot() << "_name] Slot " << item->GetSlot();
auto& slot = curInv.PushDebug(ss.str());
slot.PushDebug<AMFStringValue>("Object ID") = std::to_string(item->GetId());
slot.PushDebug<AMFIntValue>("LOT") = item->GetLot();
if (item->GetSubKey() != LWOOBJID_EMPTY) slot.PushDebug<AMFStringValue>("Subkey") = std::to_string(item->GetSubKey());
slot.PushDebug<AMFIntValue>("Count") = item->GetCount();
slot.PushDebug<AMFIntValue>("Slot") = item->GetSlot();
slot.PushDebug<AMFBoolValue>("Bind on pickup") = item->GetInfo().isBOP;
slot.PushDebug<AMFBoolValue>("Bind on equip") = item->GetInfo().isBOE;
slot.PushDebug<AMFBoolValue>("Is currently bound") = item->GetBound();
auto& extra = slot.PushDebug("Extra Info");
for (const auto* const setting : item->GetConfig()) {
if (setting) extra.PushDebug<AMFStringValue>(GeneralUtils::UTF16ToWTF8(setting->GetKey())) = setting->GetValueAsString();
}
}
}
auto& equipped = cmpt.PushDebug("Equipped Items");
for (const auto& [location, info] : GetEquippedItems()) {
std::stringstream ss;
ss << "%[Objects_" << info.lot << "_name]";
auto& equipSlot = equipped.PushDebug(ss.str());
equipSlot.PushDebug<AMFStringValue>("Location") = location;
equipSlot.PushDebug<AMFStringValue>("Object ID") = std::to_string(info.id);
equipSlot.PushDebug<AMFIntValue>("Slot") = info.slot;
equipSlot.PushDebug<AMFIntValue>("Count") = info.count;
auto& extra = equipSlot.PushDebug("Extra Info");
for (const auto* const setting : info.config) {
if (setting) extra.PushDebug<AMFStringValue>(GeneralUtils::UTF16ToWTF8(setting->GetKey())) = setting->GetValueAsString();
}
}
return true;
}

View File

@@ -22,7 +22,6 @@
#include "eInventoryType.h"
#include "eReplicaComponentType.h"
#include "eLootSourceType.h"
#include "Loot.h"
class Entity;
class ItemSet;
@@ -68,7 +67,7 @@ public:
static constexpr uint32_t MaximumGroupCount = 50;
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
InventoryComponent(Entity* parent, const int32_t componentID);
InventoryComponent(Entity* parent);
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
@@ -201,7 +200,7 @@ public:
* @param loot a map of items to add and how many to add
* @return whether the entity has enough space for all the items
*/
bool HasSpaceForLoot(const Loot::Return& loot);
bool HasSpaceForLoot(const std::unordered_map<LOT, int32_t>& loot);
/**
* Equips an item in the specified slot
@@ -403,16 +402,10 @@ public:
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
void UpdateGroup(const GroupUpdate& groupUpdate);
std::unordered_map<LWOOBJID, DatabasePet>& GetPetsMut() { return m_Pets; };
void RemoveGroup(const std::string& groupId);
void FixInvisibleItems();
// Used to migrate a character version, no need to call outside of that context
void RegenerateItemIDs();
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
~InventoryComponent() override;
private:

View File

@@ -8,7 +8,7 @@ class ItemComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ITEM;
ItemComponent(Entity* entity, const int32_t componentID) : Component(entity, componentID) {}
ItemComponent(Entity* entity) : Component(entity) {}
void Serialize(RakNet::BitStream& bitStream, bool isConstruction) override;
};

View File

@@ -16,7 +16,7 @@ class LUPExhibitComponent final : public Component
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::LUP_EXHIBIT;
LUPExhibitComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {};
LUPExhibitComponent(Entity* parent) : Component(parent) {};
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void NextLUPExhibit();

View File

@@ -6,7 +6,7 @@
#include "CDRewardsTable.h"
LevelProgressionComponent::LevelProgressionComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component(parent) {
m_Parent = parent;
m_Level = 1;
m_SpeedBase = 500.0f;

View File

@@ -19,7 +19,7 @@ public:
* Constructor for this component
* @param parent parent that contains this component
*/
LevelProgressionComponent(Entity* parent, const int32_t componentID);
LevelProgressionComponent(Entity* parent);
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -8,7 +8,7 @@ class MiniGameControlComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MINI_GAME_CONTROL;
MiniGameControlComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {}
MiniGameControlComponent(Entity* parent) : Component(parent) {}
void Serialize(RakNet::BitStream& outBitStream, bool isConstruction);
};

View File

@@ -0,0 +1,5 @@
#include "MinigameComponent.h"
void MinigameComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
outBitStream.Write<uint32_t>(0x40000000);
}

View File

@@ -26,13 +26,8 @@
std::unordered_map<AchievementCacheKey, std::vector<uint32_t>> MissionComponent::m_AchievementCache = {};
//! Initializer
MissionComponent::MissionComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
using namespace GameMessages;
MissionComponent::MissionComponent(Entity* parent) : Component(parent) {
m_LastUsedMissionOrderUID = Game::zoneManager->GetUniqueMissionIdStartingValue();
RegisterMsg<GetObjectReportInfo>(this, &MissionComponent::OnGetObjectReportInfo);
RegisterMsg<GameMessages::GetMissionState>(this, &MissionComponent::OnGetMissionState);
RegisterMsg<GameMessages::MissionNeedsLot>(this, &MissionComponent::OnMissionNeedsLot);
}
//! Destructor
@@ -627,123 +622,3 @@ 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;
}
bool MissionComponent::OnGetMissionState(GameMessages::GameMsg& msg) {
auto misState = static_cast<GameMessages::GetMissionState&>(msg);
misState.missionState = GetMissionState(misState.missionID);
return true;
}
bool MissionComponent::OnMissionNeedsLot(GameMessages::GameMsg& msg) {
const auto& needMsg = static_cast<GameMessages::MissionNeedsLot&>(msg);
return RequiresItem(needMsg.item);
}

View File

@@ -28,7 +28,7 @@ class MissionComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MISSION;
explicit MissionComponent(Entity* parent, const int32_t componentID);
explicit MissionComponent(Entity* parent);
~MissionComponent() override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
@@ -171,9 +171,6 @@ public:
void ResetMission(const int32_t missionId);
private:
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
bool OnGetMissionState(GameMessages::GameMsg& msg);
bool OnMissionNeedsLot(GameMessages::GameMsg& msg);
/**
* All the missions owned by this entity, mapped by mission ID
*/

View File

@@ -39,13 +39,19 @@ bool OfferedMission::GetAcceptsMission() const {
//------------------------ MissionOfferComponent below ------------------------
MissionOfferComponent::MissionOfferComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
if (componentID != -1) {
MissionOfferComponent::MissionOfferComponent(Entity* parent, const LOT parentLot) : Component(parent) {
auto* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
auto value = compRegistryTable->GetByIDAndType(parentLot, eReplicaComponentType::MISSION_OFFER, -1);
if (value != -1) {
const uint32_t componentId = value;
// Now lookup the missions in the MissionNPCComponent table
auto* missionNpcComponentTable = CDClientManager::GetTable<CDMissionNPCComponentTable>();
auto missions = missionNpcComponentTable->Query([componentID](const CDMissionNPCComponent& entry) {
return entry.id == static_cast<unsigned>(componentID);
auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) {
return entry.id == static_cast<unsigned>(componentId);
});
for (auto& mission : missions) {

View File

@@ -63,7 +63,7 @@ class MissionOfferComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER;
MissionOfferComponent(Entity* parent, const int32_t componentID);
MissionOfferComponent(Entity* parent, LOT parentLot);
/**
* Handles the OnUse event triggered by some entity, determines which missions to show based on what they may

View File

@@ -14,7 +14,7 @@
#include "Database.h"
#include "DluAssert.h"
ModelComponent::ModelComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
using namespace GameMessages;
m_OriginalPosition = m_Parent->GetDefaultPosition();
m_OriginalRotation = m_Parent->GetDefaultRotation();
@@ -24,7 +24,6 @@ ModelComponent::ModelComponent(Entity* parent, const int32_t componentID) : Comp
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
RegisterMsg<RequestUse>(this, &ModelComponent::OnRequestUse);
RegisterMsg<ResetModelToDefaults>(this, &ModelComponent::OnResetModelToDefaults);
RegisterMsg<GetObjectReportInfo>(this, &ModelComponent::OnGetObjectReportInfo);
}
bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) {
@@ -339,19 +338,3 @@ void ModelComponent::RemoveAttack() {
set.Send();
}
}
bool ModelComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
if (!reportMsg.info) return false;
auto& cmptInfo = reportMsg.info->PushDebug("Model Behaviors (Mutable)");
cmptInfo.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
cmptInfo.PushDebug<AMFStringValue>("Name") = "Objects_" + std::to_string(m_Parent->GetLOT()) + "_name";
cmptInfo.PushDebug<AMFBoolValue>("Has Unique Name") = false;
cmptInfo.PushDebug<AMFStringValue>("UGID (from item)") = std::to_string(m_userModelID);
cmptInfo.PushDebug<AMFStringValue>("UGID") = std::to_string(m_userModelID);
cmptInfo.PushDebug<AMFStringValue>("Description") = "";
cmptInfo.PushDebug<AMFIntValue>("Behavior Count") = m_Behaviors.size();
return true;
}

View File

@@ -27,14 +27,13 @@ class ModelComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MODEL;
ModelComponent(Entity* parent, const int32_t componentID);
ModelComponent(Entity* parent);
void LoadBehaviors();
void Update(float deltaTime) override;
bool OnRequestUse(GameMessages::GameMsg& msg);
bool OnResetModelToDefaults(GameMessages::GameMsg& msg);
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -1,6 +1,6 @@
#include "ModuleAssemblyComponent.h"
ModuleAssemblyComponent::ModuleAssemblyComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
ModuleAssemblyComponent::ModuleAssemblyComponent(Entity* parent) : Component(parent) {
m_SubKey = LWOOBJID_EMPTY;
m_UseOptionalParts = false;
m_AssemblyPartsLOTs = u"";

View File

@@ -14,7 +14,7 @@ class ModuleAssemblyComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY;
ModuleAssemblyComponent(Entity* parent, const int32_t componentID);
ModuleAssemblyComponent(Entity* parent);
~ModuleAssemblyComponent() override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -26,7 +26,7 @@ namespace {
std::map<LOT, float> m_PhysicsSpeedCache;
}
MovementAIComponent::MovementAIComponent(Entity* parent, const int32_t componentID, MovementAIInfo info) : Component(parent, componentID) {
MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : Component(parent) {
m_Info = info;
m_AtFinalWaypoint = true;

View File

@@ -62,7 +62,7 @@ class MovementAIComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI;
MovementAIComponent(Entity* parentEntity, const int32_t componentID, MovementAIInfo info);
MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
void SetPath(const std::string pathName);

View File

@@ -55,7 +55,7 @@ void MoverSubComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsIniti
//------------- MovingPlatformComponent below --------------
MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const int32_t componentID, const std::string& pathName) : Component(parent, componentID) {
MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const std::string& pathName) : Component(parent) {
m_MoverSubComponentType = eMoverSubComponentType::mover;
m_MoverSubComponent = new MoverSubComponent(m_Parent->GetDefaultPosition());
m_PathName = GeneralUtils::ASCIIToUTF16(pathName);

View File

@@ -108,7 +108,7 @@ class MovingPlatformComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM;
MovingPlatformComponent(Entity* parent, const int32_t componentID, const std::string& pathName);
MovingPlatformComponent(Entity* parent, const std::string& pathName);
~MovingPlatformComponent() override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -3,7 +3,7 @@
#include "InventoryComponent.h"
#include "CharacterComponent.h"
MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent) : Component(parent) {
m_Parent = parent;
std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar<std::u16string>(u"MultiZoneIDs"));
std::stringstream ss(zoneString);

View File

@@ -16,7 +16,7 @@ public:
* Constructor for this component, builds the m_LUPWorlds vector
* @param parent parent that contains this component
*/
MultiZoneEntranceComponent(Entity* parent, const int32_t componentID);
MultiZoneEntranceComponent(Entity* parent);
~MultiZoneEntranceComponent() override;
/**

View File

@@ -10,11 +10,9 @@
#include "InventoryComponent.h"
#include "Item.h"
#include "MissionComponent.h"
#include "User.h"
#include "SwitchComponent.h"
#include "DestroyableComponent.h"
#include "dpWorld.h"
#include "UserManager.h"
#include "PetDigServer.h"
#include "ObjectIDManager.h"
#include "eUnequippableActiveType.h"
@@ -23,7 +21,6 @@
#include "eUseItemResponse.h"
#include "ePlayerFlag.h"
#include "GeneralUtils.h"
#include "Game.h"
#include "dConfig.h"
#include "dChatFilter.h"
@@ -46,8 +43,9 @@ std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
* while the faction ones could be checked using their respective missions.
*/
PetComponent::PetComponent(Entity* parentEntity, const int32_t componentID) : Component{ parentEntity, componentID } {
m_PetInfo = CDClientManager::GetTable<CDPetComponentTable>()->GetByID(componentID); // TODO: Make reference when safe
PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } {
m_PetInfo = CDClientManager::GetTable<CDPetComponentTable>()->GetByID(componentId); // TODO: Make reference when safe
m_ComponentId = componentId;
m_Interaction = LWOOBJID_EMPTY;
m_Owner = LWOOBJID_EMPTY;
@@ -481,19 +479,10 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
return;
}
LWOOBJID petSubKey = ObjectIDManager::GetPersistentID();
const uint32_t maxTries = 100;
uint32_t tries = 0;
while (Database::Get()->GetPetNameInfo(petSubKey) && tries < maxTries) {
tries++;
LOG("Found a duplicate pet %llu, getting a new subKey", petSubKey);
petSubKey = ObjectIDManager::GetPersistentID();
}
LWOOBJID petSubKey = ObjectIDManager::GenerateRandomObjectID();
if (tries >= maxTries) {
LOG("Failed to get a unique pet subKey after %i tries, aborting pet creation for player %i", maxTries, tamer->GetCharacter() ? tamer->GetCharacter()->GetID() : -1);
return;
}
GeneralUtils::SetBit(petSubKey, eObjectBits::CHARACTER);
GeneralUtils::SetBit(petSubKey, eObjectBits::PERSISTENT);
m_DatabaseId = petSubKey;
@@ -539,7 +528,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
// Triggers the catch a pet missions
constexpr auto PET_FLAG_BASE = 800;
tamer->GetCharacter()->SetPlayerFlag(PET_FLAG_BASE + m_ComponentID, true);
tamer->GetCharacter()->SetPlayerFlag(PET_FLAG_BASE + m_ComponentId, true);
auto* missionComponent = tamer->GetComponent<MissionComponent>();
@@ -556,29 +545,18 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
}
void PetComponent::RequestSetPetName(std::u16string name) {
const bool autoRejectNames = UserManager::Instance()->GetMuteAutoRejectNames();
if (m_Tamer == LWOOBJID_EMPTY) {
if (m_Owner != LWOOBJID_EMPTY) {
auto* owner = GetOwner();
// If auto reject names is on, and the user is muted, force use of predefined name
if (autoRejectNames && owner && owner->GetCharacter() && owner->GetCharacter()->GetParentUser()->GetIsMuted()) {
m_ModerationStatus = 2; // Approved
std::string forcedName = "Pet";
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ forcedName, static_cast<int32_t>(m_ModerationStatus) });
GameMessages::SendSetPetName(m_Owner, GeneralUtils::UTF8ToUTF16(m_Name), m_DatabaseId, owner->GetSystemAddress());
GameMessages::SendSetPetNameModerated(m_Owner, m_DatabaseId, m_ModerationStatus, owner->GetSystemAddress());
} else {
m_ModerationStatus = 1; // Pending
m_Name = "";
m_ModerationStatus = 1; // Pending
m_Name = "";
//Save our pet's new name to the db:
SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(name));
//Save our pet's new name to the db:
SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(name));
GameMessages::SendSetPetName(m_Owner, GeneralUtils::UTF8ToUTF16(m_Name), m_DatabaseId, owner->GetSystemAddress());
GameMessages::SendSetPetNameModerated(m_Owner, m_DatabaseId, m_ModerationStatus, owner->GetSystemAddress());
}
GameMessages::SendSetPetName(m_Owner, GeneralUtils::UTF8ToUTF16(m_Name), m_DatabaseId, owner->GetSystemAddress());
GameMessages::SendSetPetNameModerated(m_Owner, m_DatabaseId, m_ModerationStatus, owner->GetSystemAddress());
}
return;
@@ -600,21 +578,11 @@ void PetComponent::RequestSetPetName(std::u16string name) {
return;
}
// If auto reject names is on, and the user is muted, force use of predefined name ELSE proceed with normal name check
if (autoRejectNames && tamer->GetCharacter() && tamer->GetCharacter()->GetParentUser()->GetIsMuted()) {
m_ModerationStatus = 2; // Approved
m_Name = "";
std::string forcedName = "Pet";
m_ModerationStatus = 1; // Pending
m_Name = "";
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ forcedName, static_cast<int32_t>(m_ModerationStatus) });
LOG("AccountID: %i is muted, forcing use of predefined pet name", tamer->GetCharacter()->GetParentUser()->GetAccountID());
} else {
m_ModerationStatus = 1; // Pending
m_Name = "";
//Save our pet's new name to the db:
SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(name));
}
//Save our pet's new name to the db:
SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(name));
Game::entityManager->SerializeEntity(m_Parent);

View File

@@ -18,7 +18,7 @@ class PetComponent final : public Component
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PET;
explicit PetComponent(Entity* parentEntity, const int32_t componentID);
explicit PetComponent(Entity* parentEntity, uint32_t componentId);
~PetComponent() override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
@@ -250,6 +250,11 @@ private:
*/
static std::unordered_map<LWOOBJID, LWOOBJID> currentActivities;
/**
* The ID of the component in the pet component table
*/
uint32_t m_ComponentId;
/**
* The ID of the model that was built to complete the taming minigame for this pet
*/

View File

@@ -28,7 +28,7 @@
#include "dpShapeBox.h"
#include "dpShapeSphere.h"
PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent, const int32_t componentID) : PhysicsComponent(parent, componentID) {
PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) {
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &PhantomPhysicsComponent::OnGetObjectReportInfo);
m_Position = m_Parent->GetDefaultPosition();

View File

@@ -28,7 +28,7 @@ class PhantomPhysicsComponent final : public PhysicsComponent {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS;
PhantomPhysicsComponent(Entity* parent, const int32_t componentID);
PhantomPhysicsComponent(Entity* parent, int32_t componentId);
~PhantomPhysicsComponent() override;
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -15,7 +15,7 @@
#include "EntityInfo.h"
#include "Amf3.h"
PhysicsComponent::PhysicsComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Component(parent) {
m_Position = NiPoint3Constant::ZERO;
m_Rotation = QuatUtils::IDENTITY;
m_DirtyPosition = false;
@@ -23,7 +23,7 @@ PhysicsComponent::PhysicsComponent(Entity* parent, const int32_t componentID) :
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable<CDPhysicsComponentTable>();
if (physicsComponentTable) {
auto* info = physicsComponentTable->GetByID(componentID);
auto* info = physicsComponentTable->GetByID(componentId);
if (info) {
m_CollisionGroup = info->collisionGroup;
}

View File

@@ -19,7 +19,7 @@ class dpEntity;
class PhysicsComponent : public Component {
public:
PhysicsComponent(Entity* parent, const int32_t componentID);
PhysicsComponent(Entity* parent, int32_t componentId);
virtual ~PhysicsComponent() = default;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -1,6 +1,6 @@
#include "PlayerForcedMovementComponent.h"
PlayerForcedMovementComponent::PlayerForcedMovementComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
PlayerForcedMovementComponent::PlayerForcedMovementComponent(Entity* parent) : Component(parent) {
m_Parent = parent;
}

View File

@@ -16,7 +16,7 @@ public:
* Constructor for this component
* @param parent parent that contains this component
*/
PlayerForcedMovementComponent(Entity* parent, const int32_t componentID);
PlayerForcedMovementComponent(Entity* parent);
~PlayerForcedMovementComponent() override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -4,7 +4,7 @@
#include "Inventory.h"
#include "Item.h"
PossessableComponent::PossessableComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) : Component(parent) {
m_Possessor = LWOOBJID_EMPTY;
CDItemComponent item = Inventory::FindItemComponent(m_Parent->GetLOT());
m_AnimationFlag = static_cast<eAnimationFlags>(item.animationFlag);
@@ -12,7 +12,7 @@ PossessableComponent::PossessableComponent(Entity* parent, const int32_t compone
// Get the possession Type from the CDClient
auto query = CDClientDatabase::CreatePreppedStmt("SELECT possessionType, depossessOnHit FROM PossessableComponent WHERE id = ?;");
query.bind(1, static_cast<int>(componentID));
query.bind(1, static_cast<int>(componentId));
auto result = query.execQuery();

View File

@@ -16,10 +16,15 @@ class PossessableComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE;
PossessableComponent(Entity* parentEntity, const int32_t componentID);
PossessableComponent(Entity* parentEntity, uint32_t componentId);
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
/**
* @brief mounts the Entity
*/
void Mount();
/**
* @brief dismounts the Entity
*/

View File

@@ -7,7 +7,7 @@
#include "eControlScheme.h"
#include "eStateChangeType.h"
PossessorComponent::PossessorComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
PossessorComponent::PossessorComponent(Entity* parent) : Component(parent) {
m_Possessable = LWOOBJID_EMPTY;
}

Some files were not shown because too many files have changed in this diff Show More