mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-20 11:59:40 -06:00
Compare commits
19 Commits
hardcore-m
...
web-dashbo
| Author | SHA1 | Date | |
|---|---|---|---|
| d532a9b063 | |||
| 5453d163a3 | |||
|
|
62ac65c520 | ||
|
|
5d5bce53d0 | ||
|
|
5791c55a9e | ||
|
|
17d0c45382 | ||
|
|
7dbbef81ac | ||
|
|
06958cb9cd | ||
|
|
69b1a694a6 | ||
|
|
b2609ff6cb | ||
|
|
e8c0b3e6da | ||
|
|
25418fd8b2 | ||
|
|
502c965d97 | ||
|
|
205c190c61 | ||
|
|
670cb124c0 | ||
|
|
76c2f380bf | ||
|
|
b5a3cc9187 | ||
|
|
74e1d36bb1 | ||
|
|
64faac714c |
@@ -89,6 +89,7 @@ elseif(MSVC)
|
||||
add_compile_options("/wd4267" "/utf-8" "/volatile:iso" "/Zc:inline")
|
||||
elseif(WIN32)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
add_compile_definitions(NOMINMAX)
|
||||
endif()
|
||||
|
||||
# Our output dir
|
||||
@@ -253,6 +254,7 @@ include_directories(
|
||||
"thirdparty/MD5"
|
||||
"thirdparty/nlohmann"
|
||||
"thirdparty/mongoose"
|
||||
"thirdparty/inja"
|
||||
)
|
||||
|
||||
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
|
||||
@@ -322,6 +324,7 @@ endif()
|
||||
add_subdirectory(dWorldServer)
|
||||
add_subdirectory(dAuthServer)
|
||||
add_subdirectory(dChatServer)
|
||||
add_subdirectory(dDashboardServer)
|
||||
add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries
|
||||
|
||||
target_precompile_headers(
|
||||
|
||||
@@ -26,12 +26,14 @@ void HandleHTTPPlayersRequest(HTTPReply& reply, std::string body) {
|
||||
const json data = Game::playerContainer;
|
||||
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
|
||||
reply.message = data.empty() ? "{\"error\":\"No Players Online\"}" : data.dump();
|
||||
reply.contentType = ContentType::JSON;
|
||||
}
|
||||
|
||||
void HandleHTTPTeamsRequest(HTTPReply& reply, std::string body) {
|
||||
const json data = TeamContainer::GetTeamContainer();
|
||||
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
|
||||
reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
|
||||
reply.contentType = ContentType::JSON;
|
||||
}
|
||||
|
||||
void HandleHTTPAnnounceRequest(HTTPReply& reply, std::string body) {
|
||||
@@ -39,6 +41,7 @@ void HandleHTTPAnnounceRequest(HTTPReply& reply, std::string body) {
|
||||
if (!data) {
|
||||
reply.status = eHTTPStatusCode::BAD_REQUEST;
|
||||
reply.message = "{\"error\":\"Invalid JSON\"}";
|
||||
reply.contentType = ContentType::JSON;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -47,6 +50,7 @@ void HandleHTTPAnnounceRequest(HTTPReply& reply, std::string body) {
|
||||
if (!check.empty()) {
|
||||
reply.status = eHTTPStatusCode::BAD_REQUEST;
|
||||
reply.message = check;
|
||||
reply.contentType = ContentType::JSON;
|
||||
} else {
|
||||
|
||||
ChatPackets::Announcement announcement;
|
||||
@@ -56,6 +60,7 @@ void HandleHTTPAnnounceRequest(HTTPReply& reply, std::string body) {
|
||||
|
||||
reply.status = eHTTPStatusCode::OK;
|
||||
reply.message = "{\"status\":\"Announcement Sent\"}";
|
||||
reply.contentType = ContentType::JSON;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// C++
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "dPlatforms.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "DluAssert.h"
|
||||
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
|
||||
@@ -305,7 +306,7 @@ namespace GeneralUtils {
|
||||
template<typename Container>
|
||||
inline Container::value_type GetRandomElement(const Container& container) {
|
||||
DluAssert(!container.empty());
|
||||
return container[GenerateRandomNumber<typename Container::value_type>(0, container.size() - 1)];
|
||||
return container[GenerateRandomNumber<typename Container::size_type>(0, container.size() - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
|
||||
namespace MessageType {
|
||||
enum class Master : uint32_t {
|
||||
REQUEST_PERSISTENT_ID = 1,
|
||||
REQUEST_PERSISTENT_ID_RESPONSE,
|
||||
REQUEST_ZONE_TRANSFER,
|
||||
REQUEST_ZONE_TRANSFER = 1,
|
||||
REQUEST_ZONE_TRANSFER_RESPONSE,
|
||||
SERVER_INFO,
|
||||
REQUEST_SESSION_KEY,
|
||||
|
||||
@@ -18,7 +18,9 @@ enum class eCharacterVersion : uint32_t {
|
||||
SPEED_BASE,
|
||||
// Fixes nexus force explorer missions
|
||||
NJ_JAYMISSIONS,
|
||||
UP_TO_DATE, // will become NEXUS_FORCE_EXPLORER
|
||||
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
|
||||
};
|
||||
|
||||
#endif //!__ECHARACTERVERSION__H__
|
||||
|
||||
@@ -50,7 +50,10 @@ enum class eMissionState : int {
|
||||
/**
|
||||
* The mission has been completed before and has now been completed again. Used for daily missions.
|
||||
*/
|
||||
COMPLETE_READY_TO_COMPLETE = 12
|
||||
COMPLETE_READY_TO_COMPLETE = 12,
|
||||
|
||||
// The mission is failed (don't know where this is used)
|
||||
FAILED = 16,
|
||||
};
|
||||
|
||||
#endif //!__MISSIONSTATE__H__
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
#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
|
||||
|
||||
8
dDashboardServer/CMakeLists.txt
Normal file
8
dDashboardServer/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
set(DDASHBOARDSERVER_SOURCES
|
||||
"DashboardWeb.cpp"
|
||||
)
|
||||
|
||||
add_executable(DashboardServer "DashboardServer.cpp" "DashboardWeb.cpp")
|
||||
target_link_libraries(DashboardServer ${COMMON_LIBRARIES} dServer dWeb)
|
||||
target_include_directories(DashboardServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer ${PROJECT_SOURCE_DIR}/dWeb)
|
||||
add_compile_definitions(DashboardServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
|
||||
182
dDashboardServer/DashboardServer.cpp
Normal file
182
dDashboardServer/DashboardServer.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
//DLU Includes:
|
||||
#include "dCommonVars.h"
|
||||
#include "dServer.h"
|
||||
#include "Logger.h"
|
||||
#include "Database.h"
|
||||
#include "dConfig.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "AssetManager.h"
|
||||
#include "BinaryPathFinder.h"
|
||||
#include "ServiceType.h"
|
||||
#include "StringifiedEnum.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Server.h"
|
||||
|
||||
//RakNet includes:
|
||||
#include "RakNetDefines.h"
|
||||
#include "MessageIdentifiers.h"
|
||||
|
||||
#include "DashboardWeb.h"
|
||||
|
||||
namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
dServer* server = nullptr;
|
||||
dConfig* config = nullptr;
|
||||
AssetManager* assetManager = nullptr;
|
||||
Game::signal_t lastSignal = 0;
|
||||
std::mt19937 randomEngine;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
constexpr uint32_t dashboardFramerate = mediumFramerate;
|
||||
constexpr uint32_t dashboardFrameDelta = mediumFrameDelta;
|
||||
Diagnostics::SetProcessName("Dashboard");
|
||||
Diagnostics::SetProcessFileName(argv[0]);
|
||||
Diagnostics::Initialize();
|
||||
|
||||
std::signal(SIGINT, Game::OnSignal);
|
||||
std::signal(SIGTERM, Game::OnSignal);
|
||||
|
||||
Game::config = new dConfig("dashboardconfig.ini");
|
||||
|
||||
//Create all the objects we need to run our service:
|
||||
Server::SetupLogger("DashboardServer");
|
||||
if (!Game::logger) return EXIT_FAILURE;
|
||||
Game::config->LogSettings();
|
||||
|
||||
//Read our config:
|
||||
|
||||
LOG("Starting Dashboard server...");
|
||||
LOG("Version: %s", PROJECT_VERSION);
|
||||
LOG("Compiled on: %s", __TIMESTAMP__);
|
||||
|
||||
try {
|
||||
std::string clientPathStr = Game::config->GetValue("client_location");
|
||||
if (clientPathStr.empty()) clientPathStr = "./res";
|
||||
std::filesystem::path clientPath = std::filesystem::path(clientPathStr);
|
||||
if (clientPath.is_relative()) {
|
||||
clientPath = BinaryPathFinder::GetBinaryDir() / clientPath;
|
||||
}
|
||||
|
||||
Game::assetManager = new AssetManager(clientPath);
|
||||
} catch (std::runtime_error& ex) {
|
||||
LOG("Got an error while setting up assets: %s", ex.what());
|
||||
delete Game::logger;
|
||||
delete Game::config;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
//Connect to the Database
|
||||
try {
|
||||
Database::Connect();
|
||||
} catch (std::exception& ex) {
|
||||
LOG("Got an error while connecting to the database: %s", ex.what());
|
||||
Database::Destroy("DashboardServer");
|
||||
delete Game::logger;
|
||||
delete Game::config;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// setup the chat api web server
|
||||
const uint32_t web_server_port = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("web_server_port")).value_or(80);
|
||||
if (!Game::web.Startup("localhost", web_server_port)) {
|
||||
// if we want the web server and it fails to start, exit
|
||||
LOG("Failed to start web server, shutting down.");
|
||||
Database::Destroy("DashboardServer");
|
||||
delete Game::logger;
|
||||
delete Game::config;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
DashboardWeb::RegisterRoutes();
|
||||
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1000;
|
||||
std::string masterPassword;
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
masterPassword = masterInfo->password;
|
||||
}
|
||||
|
||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||
std::string ourIP = "localhost";
|
||||
const uint32_t maxClients = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_clients")).value_or(999);
|
||||
const uint32_t ourPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("dashboard_server_port")).value_or(2006);
|
||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
||||
|
||||
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServiceType::COMMON, Game::config, &Game::lastSignal, masterPassword);
|
||||
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
|
||||
//Run it until server gets a kill message from Master:
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
Packet* packet = nullptr;
|
||||
constexpr uint32_t logFlushTime = 30 * dashboardFramerate; // 30 seconds in frames
|
||||
constexpr uint32_t sqlPingTime = 10 * 60 * dashboardFramerate; // 10 minutes in frames
|
||||
uint32_t framesSinceLastFlush = 0;
|
||||
uint32_t framesSinceMasterDisconnect = 0;
|
||||
uint32_t framesSinceLastSQLPing = 0;
|
||||
|
||||
auto lastTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
Game::logger->Flush(); // once immediately before main loop
|
||||
while (!Game::ShouldShutdown()) {
|
||||
// Check if we're still connected to master:
|
||||
if (!Game::server->GetIsConnectedToMaster()) {
|
||||
framesSinceMasterDisconnect++;
|
||||
|
||||
if (framesSinceMasterDisconnect >= dashboardFramerate)
|
||||
break; //Exit our loop, shut down.
|
||||
} else framesSinceMasterDisconnect = 0;
|
||||
|
||||
const auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
const float deltaTime = std::chrono::duration<float>(currentTime - lastTime).count();
|
||||
lastTime = currentTime;
|
||||
|
||||
// Check and handle web requests:
|
||||
Game::web.ReceiveRequests();
|
||||
|
||||
//Push our log every 30s:
|
||||
if (framesSinceLastFlush >= logFlushTime) {
|
||||
Game::logger->Flush();
|
||||
framesSinceLastFlush = 0;
|
||||
} else framesSinceLastFlush++;
|
||||
|
||||
//Every 10 min we ping our sql server to keep it alive hopefully:
|
||||
if (framesSinceLastSQLPing >= sqlPingTime) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
}
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else framesSinceLastSQLPing++;
|
||||
|
||||
//Sleep our thread since auth can afford to.
|
||||
t += std::chrono::milliseconds(dashboardFrameDelta); //Chat can run at a lower "fps"
|
||||
std::this_thread::sleep_until(t);
|
||||
}
|
||||
|
||||
//Delete our objects here:
|
||||
Database::Destroy("DashboardServer");
|
||||
delete Game::server;
|
||||
delete Game::logger;
|
||||
delete Game::config;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
59
dDashboardServer/DashboardWeb.cpp
Normal file
59
dDashboardServer/DashboardWeb.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "DashboardWeb.h"
|
||||
|
||||
// thanks bill gates
|
||||
#ifdef _WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
#include "inja.hpp"
|
||||
|
||||
#include "eHTTPMethod.h"
|
||||
|
||||
|
||||
// simple home page with inja
|
||||
void HandleHTTPHomeRequest(HTTPReply& reply, std::string body) {
|
||||
try {
|
||||
inja::Environment env;
|
||||
env.set_trim_blocks(true);
|
||||
env.set_lstrip_blocks(true);
|
||||
|
||||
nlohmann::json data;
|
||||
data["title"] = "Darkflame Universe Dashboard";
|
||||
data["header"] = "Welcome to the Darkflame Universe Dashboard";
|
||||
data["message"] = "This is a simple dashboard page served using Inja templating engine.";
|
||||
|
||||
const std::string template_str = R"(
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ title }}</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ header }}</h1>
|
||||
<p>{{ message }}</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
std::string rendered = env.render(template_str, data);
|
||||
reply.message = rendered;
|
||||
reply.status = eHTTPStatusCode::OK;
|
||||
reply.contentType = ContentType::HTML;
|
||||
} catch (const std::exception& e) {
|
||||
reply.status = eHTTPStatusCode::INTERNAL_SERVER_ERROR;
|
||||
reply.message = "Internal Server Error";
|
||||
reply.contentType = ContentType::PLAIN;
|
||||
}
|
||||
}
|
||||
|
||||
namespace DashboardWeb {
|
||||
void RegisterRoutes() {
|
||||
Game::web.RegisterHTTPRoute({
|
||||
.path = "/",
|
||||
.method = eHTTPMethod::GET,
|
||||
.handle = HandleHTTPHomeRequest
|
||||
});
|
||||
}
|
||||
}
|
||||
11
dDashboardServer/DashboardWeb.h
Normal file
11
dDashboardServer/DashboardWeb.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef __DASHBOARDWEB_H__
|
||||
#define __DASHBOARDWEB_H__
|
||||
|
||||
#include "Web.h"
|
||||
|
||||
namespace DashboardWeb {
|
||||
void RegisterRoutes();
|
||||
};
|
||||
|
||||
|
||||
#endif // __DASHBOARDWEB_H__
|
||||
@@ -6,14 +6,19 @@
|
||||
|
||||
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<uint32_t> GetCurrentPersistentId() = 0;
|
||||
virtual std::optional<uint64_t> GetCurrentPersistentId() = 0;
|
||||
|
||||
// Insert the default persistent id.
|
||||
virtual void InsertDefaultPersistentId() = 0;
|
||||
|
||||
// Update the persistent id.
|
||||
virtual void UpdatePersistentId(const uint32_t newId) = 0;
|
||||
virtual Range GetPersistentIdRange() = 0;
|
||||
};
|
||||
|
||||
#endif //!__IOBJECTIDTRACKER__H__
|
||||
|
||||
@@ -39,6 +39,9 @@ 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;
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@ public:
|
||||
NiQuaternion rotation = QuatUtils::IDENTITY;
|
||||
LWOOBJID id{};
|
||||
LOT lot{};
|
||||
uint32_t ugcId{};
|
||||
LWOOBJID ugcId{};
|
||||
std::array<LWOOBJID, 5> behaviors{};
|
||||
};
|
||||
|
||||
// Inserts a new UGC model into the database.
|
||||
virtual void InsertNewUgcModel(
|
||||
std::stringstream& sd0Data,
|
||||
const uint32_t blueprintId,
|
||||
const uint64_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 Model GetModel(const LWOOBJID modelID) = 0;
|
||||
virtual std::optional<Model> GetModel(const LWOOBJID modelID) = 0;
|
||||
};
|
||||
#endif //!__IPROPERTIESCONTENTS__H__
|
||||
|
||||
@@ -29,5 +29,7 @@ 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__
|
||||
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
void InsertNewMail(const MailInfo& mail) override;
|
||||
void InsertNewUgcModel(
|
||||
std::stringstream& sd0Data,
|
||||
const uint32_t blueprintId,
|
||||
const uint64_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<uint32_t> GetCurrentPersistentId() override;
|
||||
std::optional<uint64_t> GetCurrentPersistentId() override;
|
||||
IObjectIdTracker::Range GetPersistentIdRange() 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,7 +127,9 @@ public:
|
||||
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
||||
uint32_t GetAccountCount() override;
|
||||
bool IsNameInUse(const std::string_view name) override;
|
||||
IPropertyContents::Model GetModel(const LWOOBJID modelID) 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;
|
||||
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
||||
private:
|
||||
|
||||
@@ -168,91 +170,91 @@ private:
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string_view param) {
|
||||
// LOG("%s", param.data());
|
||||
LOG_DEBUG("%s", param.data());
|
||||
stmt->setString(index, param.data());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const char* param) {
|
||||
// LOG("%s", param);
|
||||
LOG_DEBUG("%s", param);
|
||||
stmt->setString(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string param) {
|
||||
// LOG("%s", param.c_str());
|
||||
LOG_DEBUG("%s", param.c_str());
|
||||
stmt->setString(index, param.c_str());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int8_t param) {
|
||||
// LOG("%u", param);
|
||||
LOG_DEBUG("%u", param);
|
||||
stmt->setByte(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint8_t param) {
|
||||
// LOG("%d", param);
|
||||
LOG_DEBUG("%d", param);
|
||||
stmt->setByte(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int16_t param) {
|
||||
// LOG("%u", param);
|
||||
LOG_DEBUG("%u", param);
|
||||
stmt->setShort(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint16_t param) {
|
||||
// LOG("%d", param);
|
||||
LOG_DEBUG("%d", param);
|
||||
stmt->setShort(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint32_t param) {
|
||||
// LOG("%u", param);
|
||||
LOG_DEBUG("%u", param);
|
||||
stmt->setUInt(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int32_t param) {
|
||||
// LOG("%d", param);
|
||||
LOG_DEBUG("%d", param);
|
||||
stmt->setInt(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int64_t param) {
|
||||
// LOG("%llu", param);
|
||||
LOG_DEBUG("%llu", param);
|
||||
stmt->setInt64(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint64_t param) {
|
||||
// LOG("%llu", param);
|
||||
LOG_DEBUG("%llu", param);
|
||||
stmt->setUInt64(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const float param) {
|
||||
// LOG("%f", param);
|
||||
LOG_DEBUG("%f", param);
|
||||
stmt->setFloat(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const double param) {
|
||||
// LOG("%f", param);
|
||||
LOG_DEBUG("%f", param);
|
||||
stmt->setDouble(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const bool param) {
|
||||
// LOG("%d", param);
|
||||
LOG_DEBUG("%s", param ? "true" : "false");
|
||||
stmt->setBoolean(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::istream* param) {
|
||||
// LOG("Blob");
|
||||
LOG_DEBUG("Blob");
|
||||
// This is the one time you will ever see me use const_cast.
|
||||
stmt->setBlob(index, const_cast<std::istream*>(param));
|
||||
}
|
||||
@@ -260,10 +262,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("%d", param.value());
|
||||
LOG_DEBUG("%d", param.value());
|
||||
stmt->setInt(index, param.value());
|
||||
} else {
|
||||
// LOG("Null");
|
||||
LOG_DEBUG("Null");
|
||||
stmt->setNull(index, sql::DataType::SQLNULL);
|
||||
}
|
||||
}
|
||||
@@ -271,10 +273,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("%d", param.value());
|
||||
LOG_DEBUG("%d", param.value());
|
||||
stmt->setInt64(index, param.value());
|
||||
} else {
|
||||
// LOG("Null");
|
||||
LOG_DEBUG("Null");
|
||||
stmt->setNull(index, sql::DataType::SQLNULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,42 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::optional<uint32_t> MySQLDatabase::GetCurrentPersistentId() {
|
||||
std::optional<uint64_t> MySQLDatabase::GetCurrentPersistentId() {
|
||||
auto result = ExecuteSelect("SELECT last_object_id FROM object_id_tracker");
|
||||
if (!result->next()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return result->getUInt("last_object_id");
|
||||
return result->getUInt64("last_object_id");
|
||||
}
|
||||
|
||||
void MySQLDatabase::InsertDefaultPersistentId() {
|
||||
ExecuteInsert("INSERT INTO object_id_tracker VALUES (1);");
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdatePersistentId(const uint32_t newId) {
|
||||
ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = ?;", newId);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
#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;
|
||||
@@ -117,19 +134,7 @@ std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(co
|
||||
|
||||
while (properties->next()) {
|
||||
if (!result) result = IProperty::PropertyEntranceResult();
|
||||
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");
|
||||
result->entries.push_back(ReadPropertyInfo(properties));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -144,21 +149,7 @@ std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID map
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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;
|
||||
return ReadPropertyInfo(propertyEntry);
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) {
|
||||
@@ -195,3 +186,15 @@ 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);
|
||||
}
|
||||
|
||||
@@ -64,26 +64,27 @@ void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId);
|
||||
}
|
||||
|
||||
IPropertyContents::Model MySQLDatabase::GetModel(const LWOOBJID modelID) {
|
||||
std::optional<IPropertyContents::Model> MySQLDatabase::GetModel(const LWOOBJID modelID) {
|
||||
auto result = ExecuteSelect("SELECT * FROM properties_contents WHERE id = ?", modelID);
|
||||
|
||||
IPropertyContents::Model model{};
|
||||
std::optional<IPropertyContents::Model> model = std::nullopt;
|
||||
while (result->next()) {
|
||||
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 = 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");
|
||||
}
|
||||
|
||||
return model;
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
#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;",
|
||||
@@ -8,14 +20,7 @@ std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId)
|
||||
std::vector<IUgc::Model> toReturn;
|
||||
|
||||
while (result->next()) {
|
||||
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));
|
||||
toReturn.push_back(ReadModel(result));
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
@@ -27,14 +32,7 @@ std::vector<IUgc::Model> MySQLDatabase::GetAllUgcModels() {
|
||||
std::vector<IUgc::Model> models;
|
||||
models.reserve(result->rowsCount());
|
||||
while (result->next()) {
|
||||
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));
|
||||
models.push_back(ReadModel(result));
|
||||
}
|
||||
|
||||
return models;
|
||||
@@ -45,8 +43,8 @@ void MySQLDatabase::RemoveUnreferencedUgcModels() {
|
||||
}
|
||||
|
||||
void MySQLDatabase::InsertNewUgcModel(
|
||||
std:: stringstream& sd0Data, // cant be const sad
|
||||
const uint32_t blueprintId,
|
||||
std::stringstream& sd0Data, // cant be const sad
|
||||
const uint64_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const LWOOBJID characterId) {
|
||||
const std::istream stream(sd0Data.rdbuf());
|
||||
@@ -71,3 +69,14 @@ 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;
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ bool SQLiteDatabase::GetAutoCommit() {
|
||||
|
||||
void SQLiteDatabase::SetAutoCommit(bool value) {
|
||||
if (value) {
|
||||
if (GetAutoCommit()) con->compileStatement("BEGIN;").execDML();
|
||||
} else {
|
||||
if (!GetAutoCommit()) con->compileStatement("COMMIT;").execDML();
|
||||
} else {
|
||||
if (GetAutoCommit()) con->compileStatement("BEGIN;").execDML();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
void InsertNewMail(const MailInfo& mail) override;
|
||||
void InsertNewUgcModel(
|
||||
std::stringstream& sd0Data,
|
||||
const uint32_t blueprintId,
|
||||
const uint64_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<uint32_t> GetCurrentPersistentId() override;
|
||||
std::optional<uint64_t> GetCurrentPersistentId() override;
|
||||
IObjectIdTracker::Range GetPersistentIdRange() 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,7 +125,9 @@ public:
|
||||
void DeleteUgcBuild(const LWOOBJID bigId) override;
|
||||
uint32_t GetAccountCount() override;
|
||||
bool IsNameInUse(const std::string_view name) override;
|
||||
IPropertyContents::Model GetModel(const LWOOBJID modelID) 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;
|
||||
private:
|
||||
CppSQLite3Statement CreatePreppedStmt(const std::string& query);
|
||||
|
||||
|
||||
@@ -1,17 +1,40 @@
|
||||
#include "SQLiteDatabase.h"
|
||||
|
||||
std::optional<uint32_t> SQLiteDatabase::GetCurrentPersistentId() {
|
||||
std::optional<uint64_t> SQLiteDatabase::GetCurrentPersistentId() {
|
||||
auto [_, result] = ExecuteSelect("SELECT last_object_id FROM object_id_tracker");
|
||||
if (result.eof()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return result.getIntField("last_object_id");
|
||||
return result.getInt64Field("last_object_id");
|
||||
}
|
||||
|
||||
void SQLiteDatabase::InsertDefaultPersistentId() {
|
||||
ExecuteInsert("INSERT INTO object_id_tracker VALUES (1);");
|
||||
}
|
||||
|
||||
void SQLiteDatabase::UpdatePersistentId(const uint32_t newId) {
|
||||
ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = ?;", newId);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
#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;
|
||||
@@ -118,19 +135,7 @@ std::optional<IProperty::PropertyEntranceResult> SQLiteDatabase::GetProperties(c
|
||||
auto& [_, properties] = propertiesRes;
|
||||
if (!properties.eof() && !result.has_value()) result = IProperty::PropertyEntranceResult();
|
||||
while (!properties.eof()) {
|
||||
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");
|
||||
result->entries.push_back(ReadPropertyInfo(properties));
|
||||
properties.nextRow();
|
||||
}
|
||||
|
||||
@@ -146,21 +151,7 @@ std::optional<IProperty::Info> SQLiteDatabase::GetPropertyInfo(const LWOMAPID ma
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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;
|
||||
return ReadPropertyInfo(propertyEntry);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) {
|
||||
@@ -197,3 +188,15 @@ 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);
|
||||
}
|
||||
|
||||
@@ -64,27 +64,28 @@ void SQLiteDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId);
|
||||
}
|
||||
|
||||
IPropertyContents::Model SQLiteDatabase::GetModel(const LWOOBJID modelID) {
|
||||
std::optional<IPropertyContents::Model> SQLiteDatabase::GetModel(const LWOOBJID modelID) {
|
||||
auto [_, result] = ExecuteSelect("SELECT * FROM properties_contents WHERE id = ?", modelID);
|
||||
|
||||
IPropertyContents::Model model{};
|
||||
std::optional<IPropertyContents::Model> model = std::nullopt;
|
||||
if (!result.eof()) {
|
||||
do {
|
||||
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 = 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");
|
||||
} while (result.nextRow());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
#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;",
|
||||
@@ -8,14 +20,7 @@ std::vector<IUgc::Model> SQLiteDatabase::GetUgcModels(const LWOOBJID& propertyId
|
||||
std::vector<IUgc::Model> toReturn;
|
||||
|
||||
while (!result.eof()) {
|
||||
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));
|
||||
toReturn.push_back(ReadModel(result));
|
||||
result.nextRow();
|
||||
}
|
||||
|
||||
@@ -27,14 +32,7 @@ std::vector<IUgc::Model> SQLiteDatabase::GetAllUgcModels() {
|
||||
|
||||
std::vector<IUgc::Model> models;
|
||||
while (!result.eof()) {
|
||||
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));
|
||||
models.push_back(ReadModel(result));
|
||||
result.nextRow();
|
||||
}
|
||||
|
||||
@@ -47,7 +45,7 @@ void SQLiteDatabase::RemoveUnreferencedUgcModels() {
|
||||
|
||||
void SQLiteDatabase::InsertNewUgcModel(
|
||||
std::stringstream& sd0Data, // cant be const sad
|
||||
const uint32_t blueprintId,
|
||||
const uint64_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const LWOOBJID characterId) {
|
||||
const std::istream stream(sd0Data.rdbuf());
|
||||
@@ -72,3 +70,14 @@ 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;
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ void TestSQLDatabase::InsertNewMail(const MailInfo& mail) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewUgcModel(std::stringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const LWOOBJID characterId) {
|
||||
void TestSQLDatabase::InsertNewUgcModel(std::stringstream& sd0Data, const uint64_t blueprintId, const uint32_t accountId, const LWOOBJID characterId) {
|
||||
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ void TestSQLDatabase::SetMasterInfo(const IServers::MasterInfo& info) {
|
||||
|
||||
}
|
||||
|
||||
std::optional<uint32_t> TestSQLDatabase::GetCurrentPersistentId() {
|
||||
std::optional<uint64_t> TestSQLDatabase::GetCurrentPersistentId() {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -252,10 +252,6 @@ void TestSQLDatabase::InsertDefaultPersistentId() {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdatePersistentId(const uint32_t id) {
|
||||
|
||||
}
|
||||
|
||||
std::optional<uint32_t> TestSQLDatabase::GetDonationTotal(const uint32_t activityId) {
|
||||
return {};
|
||||
}
|
||||
@@ -304,3 +300,6 @@ void TestSQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGame
|
||||
|
||||
}
|
||||
|
||||
IObjectIdTracker::Range TestSQLDatabase::GetPersistentIdRange() {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class TestSQLDatabase : public GameDatabase {
|
||||
void InsertNewMail(const MailInfo& mail) override;
|
||||
void InsertNewUgcModel(
|
||||
std::stringstream& sd0Data,
|
||||
const uint32_t blueprintId,
|
||||
const uint64_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<uint32_t> GetCurrentPersistentId() override;
|
||||
std::optional<uint64_t> GetCurrentPersistentId() override;
|
||||
IObjectIdTracker::Range GetPersistentIdRange() 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,7 +105,9 @@ class TestSQLDatabase : public GameDatabase {
|
||||
uint32_t GetAccountCount() override { return 0; };
|
||||
|
||||
bool IsNameInUse(const std::string_view name) override { return false; };
|
||||
IPropertyContents::Model GetModel(const LWOOBJID modelID) override { return {}; }
|
||||
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 {}; }
|
||||
};
|
||||
|
||||
#endif //!TESTSQLDATABASE_H
|
||||
|
||||
@@ -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.position != NiPoint3Constant::ZERO || model.lot != 14) continue;
|
||||
if (!model || 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.lot != 14) continue;
|
||||
if (!model || 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.lot != 14) continue;
|
||||
if (!model || 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);
|
||||
|
||||
@@ -336,8 +336,11 @@ 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, printer.CStr());
|
||||
Database::Get()->UpdateCharacterXml(m_ID, m_XMLData);
|
||||
}
|
||||
|
||||
void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
||||
|
||||
@@ -682,6 +682,9 @@ 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
|
||||
|
||||
241
dGame/Entity.cpp
241
dGame/Entity.cpp
@@ -199,7 +199,7 @@ void Entity::Initialize() {
|
||||
|
||||
const auto triggerInfo = GetVarAsString(u"trigger_id");
|
||||
|
||||
if (!triggerInfo.empty()) AddComponent<TriggerComponent>(triggerInfo);
|
||||
if (!triggerInfo.empty()) AddComponent<TriggerComponent>(-1, triggerInfo);
|
||||
|
||||
/**
|
||||
* Setup groups
|
||||
@@ -234,11 +234,11 @@ void Entity::Initialize() {
|
||||
|
||||
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
||||
|
||||
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||
AddComponent<ModelComponent>(-1)->LoadBehaviors();
|
||||
|
||||
AddComponent<RenderComponent>();
|
||||
AddComponent<RenderComponent>(-1);
|
||||
|
||||
auto* destroyableComponent = AddComponent<DestroyableComponent>();
|
||||
auto* destroyableComponent = AddComponent<DestroyableComponent>(-1);
|
||||
destroyableComponent->SetHealth(1);
|
||||
destroyableComponent->SetMaxHealth(1.0f);
|
||||
destroyableComponent->SetFaction(-1, true);
|
||||
@@ -254,37 +254,42 @@ void Entity::Initialize() {
|
||||
*/
|
||||
|
||||
if (m_Character && m_Character->GetParentUser()) {
|
||||
AddComponent<MissionComponent>()->LoadFromXml(m_Character->GetXMLDoc());
|
||||
AddComponent<MissionComponent>(-1)->LoadFromXml(m_Character->GetXMLDoc());
|
||||
}
|
||||
|
||||
const uint32_t petComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PET);
|
||||
if (petComponentId > 0) {
|
||||
AddComponent<PetComponent>(petComponentId);
|
||||
const auto petComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PET);
|
||||
if (petComponentID > 0) {
|
||||
AddComponent<PetComponent>(petComponentID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MINI_GAME_CONTROL) > 0) {
|
||||
AddComponent<MiniGameControlComponent>();
|
||||
const auto minigameControlID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MINI_GAME_CONTROL);
|
||||
if (minigameControlID > 0) {
|
||||
AddComponent<MiniGameControlComponent>(minigameControlID);
|
||||
}
|
||||
|
||||
const uint32_t possessableComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE);
|
||||
if (possessableComponentId > 0) {
|
||||
AddComponent<PossessableComponent>(possessableComponentId);
|
||||
const auto possessableComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE);
|
||||
if (possessableComponentID > 0) {
|
||||
AddComponent<PossessableComponent>(possessableComponentID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODULE_ASSEMBLY) > 0) {
|
||||
AddComponent<ModuleAssemblyComponent>();
|
||||
const auto moduleAssemblyID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODULE_ASSEMBLY);
|
||||
if (moduleAssemblyID > 0) {
|
||||
AddComponent<ModuleAssemblyComponent>(moduleAssemblyID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_STATS) > 0) {
|
||||
AddComponent<RacingStatsComponent>();
|
||||
const auto racingStatsID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_STATS);
|
||||
if (racingStatsID > 0) {
|
||||
AddComponent<RacingStatsComponent>(racingStatsID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::LUP_EXHIBIT, -1) >= 0) {
|
||||
AddComponent<LUPExhibitComponent>();
|
||||
const auto lupExhibitID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::LUP_EXHIBIT, -1);
|
||||
if (lupExhibitID >= 0) {
|
||||
AddComponent<LUPExhibitComponent>(lupExhibitID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_CONTROL) > 0) {
|
||||
AddComponent<RacingControlComponent>();
|
||||
const auto racingControlID =compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_CONTROL);
|
||||
if (racingControlID > 0) {
|
||||
AddComponent<RacingControlComponent>(racingControlID);
|
||||
}
|
||||
|
||||
const auto propertyEntranceComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_ENTRANCE);
|
||||
@@ -292,7 +297,7 @@ void Entity::Initialize() {
|
||||
AddComponent<PropertyEntranceComponent>(propertyEntranceComponentID);
|
||||
}
|
||||
|
||||
const int32_t controllablePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
const auto controllablePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
if (controllablePhysicsComponentID > 0) {
|
||||
auto* controllablePhysics = AddComponent<ControllablePhysicsComponent>(controllablePhysicsComponentID);
|
||||
|
||||
@@ -337,46 +342,48 @@ void Entity::Initialize() {
|
||||
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
||||
}
|
||||
|
||||
const int32_t rigidBodyPhantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS);
|
||||
const auto rigidBodyPhantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS);
|
||||
if (rigidBodyPhantomPhysicsComponentID > 0) {
|
||||
AddComponent<RigidbodyPhantomPhysicsComponent>(rigidBodyPhantomPhysicsComponentID);
|
||||
}
|
||||
|
||||
const int32_t phantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS);
|
||||
const auto phantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS);
|
||||
if (markedAsPhantom || phantomPhysicsComponentID > 0) {
|
||||
AddComponent<PhantomPhysicsComponent>(phantomPhysicsComponentID)->SetPhysicsEffectActive(false);
|
||||
}
|
||||
|
||||
const int32_t havokVehiclePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS);
|
||||
const auto 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);
|
||||
}
|
||||
|
||||
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 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::BUFF) > 0) {
|
||||
AddComponent<BuffComponent>();
|
||||
const auto buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF);
|
||||
if (buffComponentID > 0) {
|
||||
AddComponent<BuffComponent>(buffComponentID);
|
||||
}
|
||||
|
||||
const int collectibleComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::COLLECTIBLE);
|
||||
const auto collectibleComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::COLLECTIBLE);
|
||||
|
||||
if (collectibleComponentID > 0) {
|
||||
AddComponent<CollectibleComponent>(GetVarAs<int32_t>(u"collectible_id"));
|
||||
AddComponent<CollectibleComponent>(collectibleComponentID, GetVarAs<int32_t>(u"collectible_id"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple components require the destructible component.
|
||||
*/
|
||||
const int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF);
|
||||
const int quickBuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
|
||||
const auto quickBuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
|
||||
|
||||
int componentID = -1;
|
||||
int32_t componentID = -1;
|
||||
if (collectibleComponentID > 0) componentID = collectibleComponentID;
|
||||
if (quickBuildComponentID > 0) componentID = quickBuildComponentID;
|
||||
if (buffComponentID > 0) componentID = buffComponentID;
|
||||
@@ -384,7 +391,7 @@ void Entity::Initialize() {
|
||||
|
||||
bool isSmashable = GetVarAs<int32_t>(u"is_smashable") != 0;
|
||||
if (buffComponentID > 0 || collectibleComponentID > 0 || isSmashable) {
|
||||
DestroyableComponent* comp = AddComponent<DestroyableComponent>();
|
||||
DestroyableComponent* comp = AddComponent<DestroyableComponent>(componentID);
|
||||
auto* const destCompTable = CDClientManager::GetTable<CDDestructibleComponentTable>();
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([componentID](const CDDestructibleComponent& entry) { return (entry.id == componentID); });
|
||||
|
||||
@@ -473,27 +480,30 @@ void Entity::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER) > 0 || m_Character) {
|
||||
const auto characterID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER);
|
||||
if (characterID > 0 || m_Character) {
|
||||
// Character Component always has a possessor, level, and forced movement components
|
||||
AddComponent<PossessorComponent>();
|
||||
AddComponent<PossessorComponent>(characterID);
|
||||
|
||||
// load in the xml for the level
|
||||
AddComponent<LevelProgressionComponent>()->LoadFromXml(m_Character->GetXMLDoc());
|
||||
AddComponent<LevelProgressionComponent>(characterID)->LoadFromXml(m_Character->GetXMLDoc());
|
||||
|
||||
AddComponent<PlayerForcedMovementComponent>();
|
||||
AddComponent<PlayerForcedMovementComponent>(characterID);
|
||||
|
||||
auto& systemAddress = m_Character->GetParentUser() ? m_Character->GetParentUser()->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS;
|
||||
AddComponent<CharacterComponent>(m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc());
|
||||
AddComponent<CharacterComponent>(characterID, m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc());
|
||||
|
||||
AddComponent<GhostComponent>();
|
||||
AddComponent<GhostComponent>(characterID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) {
|
||||
AddComponent<InventoryComponent>();
|
||||
const auto inventoryID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY);
|
||||
if (inventoryID > 0 || m_Character) {
|
||||
AddComponent<InventoryComponent>(inventoryID);
|
||||
}
|
||||
// if this component exists, then we initialize it. it's value is always 0
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) {
|
||||
AddComponent<MultiZoneEntranceComponent>();
|
||||
const auto multiZoneEntranceID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1);
|
||||
if (multiZoneEntranceID > -1) {
|
||||
AddComponent<MultiZoneEntranceComponent>(multiZoneEntranceID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -545,7 +555,7 @@ void Entity::Initialize() {
|
||||
}
|
||||
|
||||
if (!scriptName.empty() || client || m_Character || scriptComponentID >= 0) {
|
||||
AddComponent<ScriptComponent>(scriptName, true, client && scriptName.empty());
|
||||
AddComponent<ScriptComponent>(scriptComponentID, scriptName, true, client && scriptName.empty());
|
||||
}
|
||||
|
||||
// ZoneControl script
|
||||
@@ -554,26 +564,27 @@ void Entity::Initialize() {
|
||||
const CDZoneTable* const zoneData = CDZoneTableTable::Query(zoneID.GetMapID());
|
||||
|
||||
if (zoneData != nullptr) {
|
||||
int zoneScriptID = zoneData->scriptID;
|
||||
const int32_t zoneScriptID = zoneData->scriptID;
|
||||
CDScriptComponent zoneScriptData = scriptCompTable->GetByID(zoneScriptID);
|
||||
AddComponent<ScriptComponent>(zoneScriptData.script_name, true);
|
||||
AddComponent<ScriptComponent>(zoneScriptID, zoneScriptData.script_name, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SKILL, -1) != -1 || m_Character) {
|
||||
AddComponent<SkillComponent>();
|
||||
const auto skillID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SKILL, -1);
|
||||
if (skillID > -1 || m_Character) {
|
||||
AddComponent<SkillComponent>(skillID);
|
||||
}
|
||||
|
||||
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 (const int componentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD) > 0) {
|
||||
auto* const quickBuildComponent = AddComponent<QuickBuildComponent>();
|
||||
if (quickBuildComponentID > 0) {
|
||||
auto* const quickBuildComponent = AddComponent<QuickBuildComponent>(quickBuildComponentID);
|
||||
|
||||
CDRebuildComponentTable* const rebCompTable = CDClientManager::GetTable<CDRebuildComponentTable>();
|
||||
const std::vector<CDRebuildComponent> rebCompData = rebCompTable->Query([=](CDRebuildComponent entry) { return (entry.id == quickBuildComponentID); });
|
||||
const std::vector<CDRebuildComponent> rebCompData = rebCompTable->Query([quickBuildComponentID](CDRebuildComponent entry) { return (entry.id == quickBuildComponentID); });
|
||||
|
||||
if (!rebCompData.empty()) {
|
||||
quickBuildComponent->SetResetTime(rebCompData[0].reset_time);
|
||||
@@ -618,53 +629,63 @@ void Entity::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SWITCH, -1) != -1) {
|
||||
AddComponent<SwitchComponent>();
|
||||
const auto switchID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SWITCH, -1);
|
||||
if (switchID > -1) {
|
||||
AddComponent<SwitchComponent>(switchID);
|
||||
}
|
||||
|
||||
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 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::PROPERTY_VENDOR, -1) != -1) {
|
||||
AddComponent<PropertyVendorComponent>();
|
||||
const auto propertyVendorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1);
|
||||
if (propertyVendorID > -1) {
|
||||
AddComponent<PropertyVendorComponent>(propertyVendorID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_MANAGEMENT, -1) != -1) {
|
||||
AddComponent<PropertyManagementComponent>();
|
||||
const auto propertyManagementID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_MANAGEMENT, -1);
|
||||
if (propertyManagementID > -1) {
|
||||
AddComponent<PropertyManagementComponent>(propertyManagementID);
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
const int32_t renderComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RENDER);
|
||||
if ((renderComponentId > 0 && m_TemplateID != 2365) || m_Character) {
|
||||
AddComponent<RenderComponent>(renderComponentId);
|
||||
const auto renderComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RENDER);
|
||||
if ((renderComponentID > 0 && m_TemplateID != 2365) || m_Character) {
|
||||
AddComponent<RenderComponent>(renderComponentID);
|
||||
}
|
||||
|
||||
if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MISSION_OFFER) > 0) || m_Character) {
|
||||
AddComponent<MissionOfferComponent>(m_TemplateID);
|
||||
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::BUILD_BORDER, -1) != -1) {
|
||||
AddComponent<BuildBorderComponent>();
|
||||
const auto buildBorderID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUILD_BORDER, -1);
|
||||
if (buildBorderID > -1) {
|
||||
AddComponent<BuildBorderComponent>(buildBorderID);
|
||||
}
|
||||
|
||||
// Scripted activity component
|
||||
const int32_t scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY, -1);
|
||||
if (scriptedActivityID != -1) {
|
||||
const auto scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY, -1);
|
||||
if (scriptedActivityID > -1) {
|
||||
AddComponent<ScriptedActivityComponent>(scriptedActivityID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
|
||||
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||
const auto modelID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1);
|
||||
if (modelID > -1 && !GetComponent<PetComponent>()) {
|
||||
AddComponent<ModelComponent>(modelID)->LoadBehaviors();
|
||||
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
|
||||
auto* const destroyableComponent = AddComponent<DestroyableComponent>();
|
||||
auto* const destroyableComponent = AddComponent<DestroyableComponent>(-1);
|
||||
destroyableComponent->SetHealth(1);
|
||||
destroyableComponent->SetMaxHealth(1.0f);
|
||||
destroyableComponent->SetFaction(-1, true);
|
||||
@@ -672,9 +693,10 @@ void Entity::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
PetComponent* petComponent;
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM) > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent) && !HasComponent(eReplicaComponentType::MODEL)) {
|
||||
AddComponent<ItemComponent>();
|
||||
PetComponent* petComponent{};
|
||||
const auto itemID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM);
|
||||
if (itemID > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent) && !HasComponent(eReplicaComponentType::MODEL)) {
|
||||
AddComponent<ItemComponent>(itemID);
|
||||
}
|
||||
|
||||
// Shooting gallery component
|
||||
@@ -683,16 +705,17 @@ void Entity::Initialize() {
|
||||
AddComponent<ShootingGalleryComponent>(shootingGalleryComponentID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY, -1) != -1) {
|
||||
AddComponent<PropertyComponent>();
|
||||
const auto propertyID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY, -1);
|
||||
if (propertyID > -1) {
|
||||
AddComponent<PropertyComponent>(propertyID);
|
||||
}
|
||||
|
||||
const int rocketId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH);
|
||||
if ((rocketId > 0)) {
|
||||
AddComponent<RocketLaunchpadControlComponent>(rocketId);
|
||||
const auto rocketID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH);
|
||||
if ((rocketID > 0)) {
|
||||
AddComponent<RocketLaunchpadControlComponent>(rocketID);
|
||||
}
|
||||
|
||||
const int32_t railComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RAIL_ACTIVATOR);
|
||||
const auto railComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RAIL_ACTIVATOR);
|
||||
if (railComponentID > 0) {
|
||||
AddComponent<RailActivatorComponent>(railComponentID);
|
||||
}
|
||||
@@ -722,9 +745,9 @@ void Entity::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
AddComponent<MovementAIComponent>(moveInfo);
|
||||
AddComponent<MovementAIComponent>(movementAIID, 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,
|
||||
@@ -734,7 +757,7 @@ void Entity::Initialize() {
|
||||
.wanderDelayMax = 5,
|
||||
};
|
||||
|
||||
AddComponent<MovementAIComponent>(moveInfo);
|
||||
AddComponent<MovementAIComponent>(-1, moveInfo);
|
||||
}
|
||||
|
||||
const std::string pathName = GetVarAsString(u"attached_path");
|
||||
@@ -744,10 +767,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>(pathName);
|
||||
AddComponent<MovingPlatformComponent>(-1, 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{
|
||||
@@ -759,24 +782,24 @@ void Entity::Initialize() {
|
||||
.wanderDelayMax = 5,
|
||||
};
|
||||
|
||||
AddComponent<MovementAIComponent>(moveInfo);
|
||||
AddComponent<MovementAIComponent>(-1, moveInfo);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// else we still need to setup moving platform if it has a moving platform comp but no path
|
||||
const int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1);
|
||||
if (movingPlatformComponentId >= 0) {
|
||||
AddComponent<MovingPlatformComponent>(pathName);
|
||||
const auto movingPlatformComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1);
|
||||
if (movingPlatformComponentID >= 0) {
|
||||
AddComponent<MovingPlatformComponent>(movingPlatformComponentID, pathName);
|
||||
}
|
||||
}
|
||||
|
||||
const int proximityMonitorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROXIMITY_MONITOR);
|
||||
const auto 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>(std::stoi(proximityStr[0]), std::stoi(proximityStr[1]));
|
||||
AddComponent<ProximityMonitorComponent>(proximityMonitorID, std::stoi(proximityStr[0]), std::stoi(proximityStr[1]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,12 +905,13 @@ 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>();
|
||||
if (!proxMon) proxMon = AddComponent<ProximityMonitorComponent>(-1);
|
||||
proxMon->SetProximityRadius(proxRadius, name);
|
||||
}
|
||||
|
||||
void Entity::SetProximityRadius(dpEntity* entity, std::string name) {
|
||||
ProximityMonitorComponent* proxMon = AddComponent<ProximityMonitorComponent>();
|
||||
auto* proxMon = GetComponent<ProximityMonitorComponent>();
|
||||
if (!proxMon) proxMon = AddComponent<ProximityMonitorComponent>(-1);
|
||||
proxMon->SetProximityRadius(entity, name);
|
||||
}
|
||||
|
||||
@@ -2223,6 +2247,7 @@ bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||
response.Insert("objectID", std::to_string(m_ObjectID));
|
||||
response.Insert("serverInfo", true);
|
||||
GameMessages::GetObjectReportInfo info{};
|
||||
info.bVerbose = requestInfo.bVerbose;
|
||||
info.info = response.InsertArray("data");
|
||||
auto& objectInfo = info.info->PushDebug("Object Details");
|
||||
auto* table = CDClientManager::GetTable<CDObjectsTable>();
|
||||
@@ -2236,14 +2261,14 @@ bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||
|
||||
auto& componentDetails = objectInfo.PushDebug("Component Information");
|
||||
for (const auto [id, component] : m_Components) {
|
||||
componentDetails.PushDebug<AMFStringValue>(StringifiedEnum::ToString(id)) = "";
|
||||
componentDetails.PushDebug(StringifiedEnum::ToString(id));
|
||||
}
|
||||
|
||||
auto& configData = objectInfo.PushDebug("Config Data");
|
||||
for (const auto config : m_Settings) {
|
||||
configData.PushDebug<AMFStringValue>(GeneralUtils::UTF16ToWTF8(config->GetKey())) = config->GetValueAsString();
|
||||
|
||||
}
|
||||
|
||||
HandleMsg(info);
|
||||
|
||||
auto* client = Game::entityManager->GetEntity(requestInfo.clientId);
|
||||
|
||||
@@ -87,6 +87,8 @@ void EntityManager::ReloadConfig() {
|
||||
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() {
|
||||
|
||||
@@ -81,6 +81,7 @@ public:
|
||||
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;
|
||||
@@ -125,6 +126,7 @@ private:
|
||||
std::set<LOT> m_HardcoreUscoreReducedLots{};
|
||||
std::set<LOT> m_HardcoreUscoreExcludedEnemies{};
|
||||
std::set<LWOMAPID> m_HardcoreDisabledWorlds{};
|
||||
float m_HardcoreCoinKeep{};
|
||||
};
|
||||
|
||||
#endif // ENTITYMANAGER_H
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "BitStreamUtils.h"
|
||||
#include "CheatDetection.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "dConfig.h"
|
||||
#include "eCharacterVersion.h"
|
||||
|
||||
UserManager* UserManager::m_Address = nullptr;
|
||||
|
||||
@@ -91,6 +93,23 @@ 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() {
|
||||
@@ -300,7 +319,9 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
inStream.Read(eyes);
|
||||
inStream.Read(mouth);
|
||||
|
||||
const auto name = LUWStringName.GetAsString();
|
||||
const bool autoRejectNames = this->GetMuteAutoRejectNames() && u->GetIsMuted();
|
||||
|
||||
const auto name = autoRejectNames ? "" : LUWStringName.GetAsString();
|
||||
std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex);
|
||||
|
||||
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
|
||||
@@ -318,87 +339,88 @@ 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 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;
|
||||
}
|
||||
//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();
|
||||
}
|
||||
|
||||
std::stringstream xml;
|
||||
xml << "<obj v=\"1\">";
|
||||
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;
|
||||
}
|
||||
|
||||
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 << "\"/>";
|
||||
std::stringstream xml;
|
||||
xml << "<obj v=\"1\">";
|
||||
|
||||
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 << "<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>";
|
||||
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 << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
|
||||
xml << "</char>";
|
||||
|
||||
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 << "<dest hm=\"4\" hc=\"4\" im=\"0\" ic=\"0\" am=\"0\" ac=\"0\" d=\"0\"/>";
|
||||
|
||||
LWOOBJID lwoidforshirt = ObjectIDManager::GenerateRandomObjectID();
|
||||
LWOOBJID lwoidforpants;
|
||||
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\">";
|
||||
|
||||
do {
|
||||
lwoidforpants = ObjectIDManager::GenerateRandomObjectID();
|
||||
} while (lwoidforpants == lwoidforshirt); //Make sure we don't have the same ID for both shirt and pants
|
||||
LWOOBJID lwoidforshirt = ObjectIDManager::GetPersistentID();
|
||||
LWOOBJID lwoidforpants = ObjectIDManager::GetPersistentID();
|
||||
|
||||
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT);
|
||||
GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT);
|
||||
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\"/>";
|
||||
|
||||
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\"/>";
|
||||
xml << "</in></items></inv><lvl l=\"1\" cv=\"" << GeneralUtils::ToUnderlying(eCharacterVersion::UP_TO_DATE) << "\" sb=\"500\"/><flag></flag></obj>";
|
||||
|
||||
xml << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
|
||||
//Check to see if our name was pre-approved:
|
||||
bool nameOk = IsNamePreapproved(name);
|
||||
|
||||
//Check to see if our name was pre-approved:
|
||||
bool nameOk = IsNamePreapproved(name);
|
||||
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
|
||||
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) {
|
||||
@@ -451,9 +473,10 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
|
||||
LUWString LUWStringName;
|
||||
inStream.Read(LUWStringName);
|
||||
const auto newName = LUWStringName.GetAsString();
|
||||
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(
|
||||
@@ -474,13 +497,30 @@ 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 (IsNamePreapproved(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)) {
|
||||
Database::Get()->SetCharacterName(objectID, newName);
|
||||
LOG("Character %s now known as %s", character->GetName().c_str(), newName.c_str());
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
|
||||
|
||||
@@ -41,6 +41,11 @@ 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;
|
||||
@@ -50,6 +55,11 @@ 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
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "UserManager.h"
|
||||
#include "CDMissionsTable.h"
|
||||
|
||||
AchievementVendorComponent::AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {
|
||||
AchievementVendorComponent::AchievementVendorComponent(Entity* parent, const int32_t componentID) : VendorComponent(parent, componentID) {
|
||||
RefreshInventory(true);
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class Entity;
|
||||
class AchievementVendorComponent final : public VendorComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR;
|
||||
AchievementVendorComponent(Entity* parent);
|
||||
AchievementVendorComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
void RefreshInventory(bool isCreation = false) override;
|
||||
bool SellsItem(Entity* buyer, const LOT lot);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "CharacterComponent.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Component(parent) {
|
||||
ActivityComponent::ActivityComponent(Entity* parent, int32_t componentID) : Component(parent, componentID) {
|
||||
using namespace GameMessages;
|
||||
RegisterMsg<GetObjectReportInfo>(this, &ActivityComponent::OnGetObjectReportInfo);
|
||||
/*
|
||||
@@ -39,8 +39,8 @@ ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Compo
|
||||
* if activityID is specified and if that column exists in the activities table, update the activity info with that data.
|
||||
*/
|
||||
|
||||
m_ActivityID = activityID;
|
||||
LoadActivityData(activityID);
|
||||
m_ActivityID = componentID;
|
||||
LoadActivityData(componentID);
|
||||
if (m_Parent->HasVar(u"activityID")) {
|
||||
m_ActivityID = parent->GetVar<int32_t>(u"activityID");
|
||||
LoadActivityData(m_ActivityID);
|
||||
|
||||
@@ -353,7 +353,7 @@ private:
|
||||
/**
|
||||
* The database information for this activity
|
||||
*/
|
||||
CDActivities m_ActivityInfo;
|
||||
CDActivities m_ActivityInfo{};
|
||||
|
||||
/**
|
||||
* All the active instances of this activity
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "CDPhysicsComponentTable.h"
|
||||
#include "dNavMesh.h"
|
||||
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id) : Component(parent) {
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
m_DirtyStateOrTarget = true;
|
||||
m_State = AiState::spawn;
|
||||
@@ -43,7 +43,7 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id)
|
||||
//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>(id));
|
||||
componentQuery.bind(1, static_cast<int>(componentID));
|
||||
|
||||
auto componentResult = componentQuery.execQuery();
|
||||
|
||||
@@ -111,12 +111,12 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id)
|
||||
int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY);
|
||||
|
||||
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||
auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
const auto controllablePhysicsID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
|
||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable<CDPhysicsComponentTable>();
|
||||
|
||||
if (physicsComponentTable != nullptr) {
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
auto* info = physicsComponentTable->GetByID(controllablePhysicsID);
|
||||
if (info != nullptr) {
|
||||
collisionGroup = info->bStatic ? COLLISION_GROUP_NEUTRAL : info->collisionGroup;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class BaseCombatAIComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI;
|
||||
|
||||
BaseCombatAIComponent(Entity* parentEntity, uint32_t id);
|
||||
BaseCombatAIComponent(Entity* parentEntity, int32_t componentID);
|
||||
~BaseCombatAIComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "BitStream.h"
|
||||
#include "eTriggerEventType.h"
|
||||
|
||||
BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) {
|
||||
BouncerComponent::BouncerComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_PetEnabled = false;
|
||||
m_PetBouncerEnabled = false;
|
||||
m_PetSwitchLoaded = false;
|
||||
|
||||
@@ -14,7 +14,7 @@ class BouncerComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER;
|
||||
|
||||
BouncerComponent(Entity* parentEntity);
|
||||
BouncerComponent(Entity* parentEntity, const int32_t componentID);
|
||||
~BouncerComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace {
|
||||
};
|
||||
}
|
||||
|
||||
BuffComponent::BuffComponent(Entity* parent) : Component(parent) {
|
||||
BuffComponent::BuffComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
}
|
||||
|
||||
BuffComponent::~BuffComponent() {
|
||||
|
||||
@@ -51,7 +51,7 @@ class BuffComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUFF;
|
||||
|
||||
explicit BuffComponent(Entity* parent);
|
||||
explicit BuffComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
~BuffComponent();
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "Item.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
|
||||
BuildBorderComponent::BuildBorderComponent(Entity* parent) : Component(parent) {
|
||||
BuildBorderComponent::BuildBorderComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
}
|
||||
|
||||
BuildBorderComponent::~BuildBorderComponent() {
|
||||
|
||||
@@ -18,7 +18,7 @@ class BuildBorderComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER;
|
||||
|
||||
BuildBorderComponent(Entity* parent);
|
||||
BuildBorderComponent(Entity* parent, const int32_t componentID);
|
||||
~BuildBorderComponent() override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "MessageType/Game.h"
|
||||
#include <ctime>
|
||||
|
||||
CharacterComponent::CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress) : Component(parent) {
|
||||
CharacterComponent::CharacterComponent(Entity* parent, const int32_t componentID, Character* character, const SystemAddress& systemAddress) : Component(parent, componentID) {
|
||||
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<AMFStringValue>(sstream.str()) = "";
|
||||
vl.PushDebug(sstream.str());
|
||||
}
|
||||
|
||||
// visited locations
|
||||
@@ -84,6 +84,30 @@ 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;
|
||||
}
|
||||
|
||||
@@ -859,7 +883,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));
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class CharacterComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
|
||||
|
||||
CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress);
|
||||
CharacterComponent(Entity* parent, const int32_t componentID, Character* character, const SystemAddress& systemAddress);
|
||||
~CharacterComponent() override;
|
||||
|
||||
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
class CollectibleComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE;
|
||||
CollectibleComponent(Entity* parentEntity, int32_t collectibleId) : Component(parentEntity), m_CollectibleId(collectibleId) {}
|
||||
CollectibleComponent(Entity* parentEntity, const int32_t componentID, const int32_t collectibleId) : Component(parentEntity, componentID), m_CollectibleId(collectibleId) {}
|
||||
|
||||
int16_t GetCollectibleId() const { return m_CollectibleId; }
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool isConstruction) override;
|
||||
|
||||
@@ -19,7 +19,7 @@ class Entity;
|
||||
*/
|
||||
class Component {
|
||||
public:
|
||||
Component(Entity* parent) : m_Parent{ parent } {}
|
||||
Component(Entity* parent, const int32_t componentID) : m_Parent{ parent }, m_ComponentID{componentID} {}
|
||||
virtual ~Component() = default;
|
||||
|
||||
/**
|
||||
@@ -28,6 +28,8 @@ 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
|
||||
@@ -70,4 +72,11 @@ 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;
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "StringifiedEnum.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity, int32_t componentId) : PhysicsComponent(entity, componentId) {
|
||||
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity, const int32_t componentID) : PhysicsComponent(entity, componentID) {
|
||||
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &ControllablePhysicsComponent::OnGetObjectReportInfo);
|
||||
|
||||
m_Velocity = {};
|
||||
|
||||
@@ -25,7 +25,7 @@ class ControllablePhysicsComponent : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
|
||||
ControllablePhysicsComponent(Entity* entity, int32_t componentId);
|
||||
ControllablePhysicsComponent(Entity* entity, const int32_t componentID);
|
||||
~ControllablePhysicsComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
Implementation<bool, const Entity*> DestroyableComponent::IsEnemyImplentation;
|
||||
Implementation<bool, const Entity*> DestroyableComponent::IsFriendImplentation;
|
||||
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
using namespace GameMessages;
|
||||
m_iArmor = 0;
|
||||
m_fMaxArmor = 0.0f;
|
||||
@@ -786,7 +786,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
}
|
||||
} else {
|
||||
//Check if this zone allows coin drops
|
||||
if (Game::zoneManager->GetPlayerLoseCoinOnDeath()) {
|
||||
if (Game::zoneManager->GetPlayerLoseCoinOnDeath() && !Game::entityManager->GetHardcoreMode()) {
|
||||
auto* character = m_Parent->GetCharacter();
|
||||
uint64_t coinsTotal = character->GetCoins();
|
||||
const uint64_t minCoinsToLose = Game::zoneManager->GetWorldConfig().coinsLostOnDeathMin;
|
||||
@@ -1012,13 +1012,23 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
//get character:
|
||||
auto* chars = m_Parent->GetCharacter();
|
||||
if (chars) {
|
||||
auto coins = chars->GetCoins();
|
||||
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);
|
||||
|
||||
//lose all coins:
|
||||
chars->SetCoins(0, eLootSourceType::NONE);
|
||||
chars->SetCoins(coins, eLootSourceType::NONE);
|
||||
|
||||
//drop all coins:
|
||||
GameMessages::SendDropClientLoot(m_Parent, source, LOT_NULL, coins, m_Parent->GetPosition());
|
||||
constexpr auto MAX_TO_DROP_PER_GM = 100'000;
|
||||
while (coinsToDrop > MAX_TO_DROP_PER_GM) {
|
||||
LOG("Dropping 100,000, %llu left", coinsToDrop);
|
||||
GameMessages::SendDropClientLoot(m_Parent, source, LOT_NULL, MAX_TO_DROP_PER_GM, m_Parent->GetPosition());
|
||||
coinsToDrop -= MAX_TO_DROP_PER_GM;
|
||||
}
|
||||
GameMessages::SendDropClientLoot(m_Parent, source, LOT_NULL, coinsToDrop, m_Parent->GetPosition());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class DestroyableComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE;
|
||||
|
||||
DestroyableComponent(Entity* parentEntity);
|
||||
DestroyableComponent(Entity* parentEntity, const int32_t componentID);
|
||||
~DestroyableComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "DonationVendorComponent.h"
|
||||
#include "Database.h"
|
||||
|
||||
DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorComponent(parent) {
|
||||
DonationVendorComponent::DonationVendorComponent(Entity* parent, const int32_t componentID) : VendorComponent(parent, componentID) {
|
||||
//LoadConfigData
|
||||
m_PercentComplete = 0.0;
|
||||
m_TotalDonated = 0;
|
||||
|
||||
@@ -9,7 +9,7 @@ class Entity;
|
||||
class DonationVendorComponent final : public VendorComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR;
|
||||
DonationVendorComponent(Entity* parent);
|
||||
DonationVendorComponent(Entity* parent, const int32_t componentID);
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
uint32_t GetActivityID() {return m_ActivityId;};
|
||||
void SubmitDonation(uint32_t count);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "GhostComponent.h"
|
||||
|
||||
GhostComponent::GhostComponent(Entity* parent) : Component(parent) {
|
||||
GhostComponent::GhostComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_GhostReferencePoint = NiPoint3Constant::ZERO;
|
||||
m_GhostOverridePoint = NiPoint3Constant::ZERO;
|
||||
m_GhostOverride = false;
|
||||
|
||||
@@ -10,7 +10,7 @@ class NiPoint3;
|
||||
class GhostComponent final : public Component {
|
||||
public:
|
||||
static inline const eReplicaComponentType ComponentType = eReplicaComponentType::GHOST;
|
||||
GhostComponent(Entity* parent);
|
||||
GhostComponent(Entity* parent, const int32_t componentID);
|
||||
~GhostComponent() override;
|
||||
|
||||
void SetGhostOverride(bool value) { m_GhostOverride = value; };
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "EntityManager.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) {
|
||||
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent, const int32_t componentID) : PhysicsComponent(parent, componentID) {
|
||||
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &HavokVehiclePhysicsComponent::OnGetObjectReportInfo);
|
||||
|
||||
m_Velocity = NiPoint3Constant::ZERO;
|
||||
|
||||
@@ -13,7 +13,7 @@ class HavokVehiclePhysicsComponent : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS;
|
||||
|
||||
HavokVehiclePhysicsComponent(Entity* parentEntity, int32_t componentId);
|
||||
HavokVehiclePhysicsComponent(Entity* parentEntity, const int32_t componentID);
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
#include <ranges>
|
||||
|
||||
InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
|
||||
InventoryComponent::InventoryComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
this->m_Dirty = true;
|
||||
this->m_Equipped = {};
|
||||
this->m_Pushed = {};
|
||||
@@ -626,7 +626,8 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
|
||||
for (const auto& pair : this->m_Inventories) {
|
||||
auto* inventory = pair.second;
|
||||
|
||||
if (inventory->GetType() == VENDOR_BUYBACK || inventory->GetType() == eInventoryType::MODELS_IN_BBB) {
|
||||
static const auto EXCLUDED_INVENTORIES = {VENDOR_BUYBACK, MODELS_IN_BBB, ITEM_SETS};
|
||||
if (std::ranges::find(EXCLUDED_INVENTORIES, inventory->GetType()) != EXCLUDED_INVENTORIES.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1786,3 +1787,9 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
static constexpr uint32_t MaximumGroupCount = 50;
|
||||
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
|
||||
InventoryComponent(Entity* parent);
|
||||
InventoryComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
@@ -402,10 +402,14 @@ public:
|
||||
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
|
||||
|
||||
void UpdateGroup(const GroupUpdate& groupUpdate);
|
||||
void RemoveGroup(const std::string& groupId);
|
||||
|
||||
std::unordered_map<LWOOBJID, DatabasePet>& GetPetsMut() { return m_Pets; };
|
||||
|
||||
void FixInvisibleItems();
|
||||
|
||||
// Used to migrate a character version, no need to call outside of that context
|
||||
void RegenerateItemIDs();
|
||||
|
||||
~InventoryComponent() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -8,7 +8,7 @@ class ItemComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ITEM;
|
||||
|
||||
ItemComponent(Entity* entity) : Component(entity) {}
|
||||
ItemComponent(Entity* entity, const int32_t componentID) : Component(entity, componentID) {}
|
||||
|
||||
void Serialize(RakNet::BitStream& bitStream, bool isConstruction) override;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ class LUPExhibitComponent final : public Component
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::LUP_EXHIBIT;
|
||||
|
||||
LUPExhibitComponent(Entity* parent) : Component(parent) {};
|
||||
LUPExhibitComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {};
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
void NextLUPExhibit();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "CDRewardsTable.h"
|
||||
|
||||
LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component(parent) {
|
||||
LevelProgressionComponent::LevelProgressionComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Parent = parent;
|
||||
m_Level = 1;
|
||||
m_SpeedBase = 500.0f;
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
* Constructor for this component
|
||||
* @param parent parent that contains this component
|
||||
*/
|
||||
LevelProgressionComponent(Entity* parent);
|
||||
LevelProgressionComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ class MiniGameControlComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MINI_GAME_CONTROL;
|
||||
|
||||
MiniGameControlComponent(Entity* parent) : Component(parent) {}
|
||||
MiniGameControlComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {}
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool isConstruction);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#include "MinigameComponent.h"
|
||||
|
||||
void MinigameComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
|
||||
outBitStream.Write<uint32_t>(0x40000000);
|
||||
}
|
||||
@@ -26,8 +26,11 @@
|
||||
std::unordered_map<AchievementCacheKey, std::vector<uint32_t>> MissionComponent::m_AchievementCache = {};
|
||||
|
||||
//! Initializer
|
||||
MissionComponent::MissionComponent(Entity* parent) : Component(parent) {
|
||||
MissionComponent::MissionComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
using namespace GameMessages;
|
||||
m_LastUsedMissionOrderUID = Game::zoneManager->GetUniqueMissionIdStartingValue();
|
||||
|
||||
RegisterMsg<GetObjectReportInfo>(this, &MissionComponent::OnGetObjectReportInfo);
|
||||
}
|
||||
|
||||
//! Destructor
|
||||
@@ -622,3 +625,111 @@ void MissionComponent::ResetMission(const int32_t missionId) {
|
||||
m_Missions.erase(missionId);
|
||||
GameMessages::SendResetMissions(m_Parent, m_Parent->GetSystemAddress(), missionId);
|
||||
}
|
||||
|
||||
void PushMissions(const std::map<uint32_t, Mission*>& missions, AMFArrayValue& V, bool verbose) {
|
||||
for (const auto& [id, mission] : missions) {
|
||||
std::stringstream ss;
|
||||
if (!mission) {
|
||||
ss << "Mission ID: " << id;
|
||||
V.PushDebug(ss.str());
|
||||
} else if (!verbose) {
|
||||
ss << "%[Missions_" << id << "_name]" << ", Mission ID";
|
||||
V.PushDebug<AMFIntValue>(ss.str()) = id;
|
||||
} else {
|
||||
ss << "%[Missions_" << id << "_name]" << ", Mission ID: " << id;
|
||||
auto& missionV = V.PushDebug(ss.str());
|
||||
auto& missionInformation = missionV.PushDebug("Mission Information");
|
||||
|
||||
if (mission->IsComplete()) {
|
||||
missionInformation.PushDebug<AMFStringValue>("Time mission last completed") = std::to_string(mission->GetTimestamp());
|
||||
missionInformation.PushDebug<AMFIntValue>("Number of times completed") = mission->GetCompletions();
|
||||
}
|
||||
// Expensive to network this especially when its read from the client anyways
|
||||
// missionInformation.PushDebug("Description").PushDebug("None");
|
||||
// missionInformation.PushDebug("Text").PushDebug("None");
|
||||
|
||||
auto& statusInfo = missionInformation.PushDebug("Mission statuses for local player");
|
||||
if (mission->IsAvalible()) statusInfo.PushDebug("Available");
|
||||
if (mission->IsActive()) statusInfo.PushDebug("Active");
|
||||
if (mission->IsReadyToComplete()) statusInfo.PushDebug("Ready To Complete");
|
||||
if (mission->IsComplete()) statusInfo.PushDebug("Completed");
|
||||
if (mission->IsFailed()) statusInfo.PushDebug("Failed");
|
||||
const auto& clientInfo = mission->GetClientInfo();
|
||||
|
||||
statusInfo.PushDebug<AMFBoolValue>("Is an achievement mission") = mission->IsAchievement();
|
||||
statusInfo.PushDebug<AMFBoolValue>("Is an timed mission") = clientInfo.time_limit > 0;
|
||||
auto& taskInfo = statusInfo.PushDebug("Task Info");
|
||||
taskInfo.PushDebug<AMFIntValue>("Number of tasks in this mission") = mission->GetTasks().size();
|
||||
int32_t i = 0;
|
||||
for (const auto* task : mission->GetTasks()) {
|
||||
auto& thisTask = taskInfo.PushDebug("Task " + std::to_string(i));
|
||||
// Expensive to network this especially when its read from the client anyways
|
||||
// thisTask.PushDebug("Description").PushDebug("%[MissionTasks_" + taskUidStr + "_description]");
|
||||
thisTask.PushDebug<AMFIntValue>("Number done") = std::min(task->GetProgress(), static_cast<uint32_t>(task->GetClientInfo().targetValue));
|
||||
thisTask.PushDebug<AMFIntValue>("Number total needed") = task->GetClientInfo().targetValue;
|
||||
thisTask.PushDebug<AMFIntValue>("Task Type") = task->GetClientInfo().taskType;
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
// auto& chatText = missionInformation.PushDebug("Chat Text for Mission States");
|
||||
// Expensive to network this especially when its read from the client anyways
|
||||
// chatText.PushDebug("Available Text").PushDebug("%[MissionText_" + idStr + "_chat_state_1]");
|
||||
// chatText.PushDebug("Active Text").PushDebug("%[MissionText_" + idStr + "_chat_state_2]");
|
||||
// chatText.PushDebug("Ready-to-Complete Text").PushDebug("%[MissionText_" + idStr + "_chat_state_3]");
|
||||
// chatText.PushDebug("Complete Text").PushDebug("%[MissionText_" + idStr + "_chat_state_4]");
|
||||
|
||||
if (clientInfo.time_limit > 0) {
|
||||
missionInformation.PushDebug<AMFIntValue>("Time Limit") = clientInfo.time_limit;
|
||||
missionInformation.PushDebug<AMFDoubleValue>("Time Remaining") = 0;
|
||||
}
|
||||
|
||||
if (clientInfo.offer_objectID != -1) {
|
||||
missionInformation.PushDebug<AMFIntValue>("Offer Object LOT") = clientInfo.offer_objectID;
|
||||
}
|
||||
|
||||
if (clientInfo.target_objectID != -1) {
|
||||
missionInformation.PushDebug<AMFIntValue>("Complete Object LOT") = clientInfo.target_objectID;
|
||||
}
|
||||
|
||||
if (!clientInfo.prereqMissionID.empty()) {
|
||||
missionInformation.PushDebug<AMFStringValue>("Requirement Mission IDs") = clientInfo.prereqMissionID;
|
||||
}
|
||||
|
||||
missionInformation.PushDebug<AMFBoolValue>("Is Repeatable") = clientInfo.repeatable;
|
||||
const bool hasNoOfferer = clientInfo.offer_objectID == -1 || clientInfo.offer_objectID == 0;
|
||||
const bool hasNoCompleter = clientInfo.target_objectID == -1 || clientInfo.target_objectID == 0;
|
||||
missionInformation.PushDebug<AMFBoolValue>("Is Achievement") = hasNoOfferer && hasNoCompleter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MissionComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
auto& missionInfo = reportMsg.info->PushDebug("Mission (Laggy)");
|
||||
missionInfo.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||
// Sort the missions so they are easier to parse and present to the end user
|
||||
std::map<uint32_t, Mission*> achievements;
|
||||
std::map<uint32_t, Mission*> missions;
|
||||
std::map<uint32_t, Mission*> doneMissions;
|
||||
for (const auto [id, mission] : m_Missions) {
|
||||
if (!mission) continue;
|
||||
else if (mission->IsComplete()) doneMissions[id] = mission;
|
||||
else if (mission->IsAchievement()) achievements[id] = mission;
|
||||
else if (mission->IsMission()) missions[id] = mission;
|
||||
}
|
||||
|
||||
// None of these should be empty, but if they are dont print the field
|
||||
if (!achievements.empty() || !missions.empty()) {
|
||||
auto& incompleteMissions = missionInfo.PushDebug("Incomplete Missions");
|
||||
PushMissions(achievements, incompleteMissions, reportMsg.bVerbose);
|
||||
PushMissions(missions, incompleteMissions, reportMsg.bVerbose);
|
||||
}
|
||||
|
||||
if (!doneMissions.empty()) {
|
||||
auto& completeMissions = missionInfo.PushDebug("Completed Missions");
|
||||
PushMissions(doneMissions, completeMissions, reportMsg.bVerbose);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class MissionComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MISSION;
|
||||
|
||||
explicit MissionComponent(Entity* parent);
|
||||
explicit MissionComponent(Entity* parent, const int32_t componentID);
|
||||
~MissionComponent() override;
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
|
||||
@@ -171,6 +171,7 @@ public:
|
||||
|
||||
void ResetMission(const int32_t missionId);
|
||||
private:
|
||||
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
/**
|
||||
* All the missions owned by this entity, mapped by mission ID
|
||||
*/
|
||||
|
||||
@@ -39,19 +39,13 @@ bool OfferedMission::GetAcceptsMission() const {
|
||||
|
||||
//------------------------ MissionOfferComponent below ------------------------
|
||||
|
||||
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;
|
||||
|
||||
MissionOfferComponent::MissionOfferComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
if (componentID != -1) {
|
||||
// Now lookup the missions in the MissionNPCComponent table
|
||||
auto* missionNpcComponentTable = CDClientManager::GetTable<CDMissionNPCComponentTable>();
|
||||
|
||||
auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) {
|
||||
return entry.id == static_cast<unsigned>(componentId);
|
||||
auto missions = missionNpcComponentTable->Query([componentID](const CDMissionNPCComponent& entry) {
|
||||
return entry.id == static_cast<unsigned>(componentID);
|
||||
});
|
||||
|
||||
for (auto& mission : missions) {
|
||||
|
||||
@@ -63,7 +63,7 @@ class MissionOfferComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER;
|
||||
|
||||
MissionOfferComponent(Entity* parent, LOT parentLot);
|
||||
MissionOfferComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
/**
|
||||
* Handles the OnUse event triggered by some entity, determines which missions to show based on what they may
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "Database.h"
|
||||
#include "DluAssert.h"
|
||||
|
||||
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
|
||||
ModelComponent::ModelComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
using namespace GameMessages;
|
||||
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
||||
m_OriginalRotation = m_Parent->GetDefaultRotation();
|
||||
|
||||
@@ -27,7 +27,7 @@ class ModelComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MODEL;
|
||||
|
||||
ModelComponent(Entity* parent);
|
||||
ModelComponent(Entity* parent, const int32_t componentID);
|
||||
|
||||
void LoadBehaviors();
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ModuleAssemblyComponent.h"
|
||||
|
||||
ModuleAssemblyComponent::ModuleAssemblyComponent(Entity* parent) : Component(parent) {
|
||||
ModuleAssemblyComponent::ModuleAssemblyComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_SubKey = LWOOBJID_EMPTY;
|
||||
m_UseOptionalParts = false;
|
||||
m_AssemblyPartsLOTs = u"";
|
||||
|
||||
@@ -14,7 +14,7 @@ class ModuleAssemblyComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY;
|
||||
|
||||
ModuleAssemblyComponent(Entity* parent);
|
||||
ModuleAssemblyComponent(Entity* parent, const int32_t componentID);
|
||||
~ModuleAssemblyComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace {
|
||||
std::map<LOT, float> m_PhysicsSpeedCache;
|
||||
}
|
||||
|
||||
MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : Component(parent) {
|
||||
MovementAIComponent::MovementAIComponent(Entity* parent, const int32_t componentID, MovementAIInfo info) : Component(parent, componentID) {
|
||||
m_Info = info;
|
||||
m_AtFinalWaypoint = true;
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class MovementAIComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI;
|
||||
|
||||
MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
|
||||
MovementAIComponent(Entity* parentEntity, const int32_t componentID, MovementAIInfo info);
|
||||
|
||||
void SetPath(const std::string pathName);
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ void MoverSubComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsIniti
|
||||
|
||||
//------------- MovingPlatformComponent below --------------
|
||||
|
||||
MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const std::string& pathName) : Component(parent) {
|
||||
MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const int32_t componentID, const std::string& pathName) : Component(parent, componentID) {
|
||||
m_MoverSubComponentType = eMoverSubComponentType::mover;
|
||||
m_MoverSubComponent = new MoverSubComponent(m_Parent->GetDefaultPosition());
|
||||
m_PathName = GeneralUtils::ASCIIToUTF16(pathName);
|
||||
|
||||
@@ -108,7 +108,7 @@ class MovingPlatformComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM;
|
||||
|
||||
MovingPlatformComponent(Entity* parent, const std::string& pathName);
|
||||
MovingPlatformComponent(Entity* parent, const int32_t componentID, const std::string& pathName);
|
||||
~MovingPlatformComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "InventoryComponent.h"
|
||||
#include "CharacterComponent.h"
|
||||
|
||||
MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent) : Component(parent) {
|
||||
MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Parent = parent;
|
||||
std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar<std::u16string>(u"MultiZoneIDs"));
|
||||
std::stringstream ss(zoneString);
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
* Constructor for this component, builds the m_LUPWorlds vector
|
||||
* @param parent parent that contains this component
|
||||
*/
|
||||
MultiZoneEntranceComponent(Entity* parent);
|
||||
MultiZoneEntranceComponent(Entity* parent, const int32_t componentID);
|
||||
~MultiZoneEntranceComponent() override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
#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"
|
||||
@@ -21,6 +23,7 @@
|
||||
#include "eUseItemResponse.h"
|
||||
#include "ePlayerFlag.h"
|
||||
|
||||
#include "GeneralUtils.h"
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
#include "dChatFilter.h"
|
||||
@@ -43,9 +46,8 @@ std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
|
||||
* while the faction ones could be checked using their respective missions.
|
||||
*/
|
||||
|
||||
PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } {
|
||||
m_PetInfo = CDClientManager::GetTable<CDPetComponentTable>()->GetByID(componentId); // TODO: Make reference when safe
|
||||
m_ComponentId = componentId;
|
||||
PetComponent::PetComponent(Entity* parentEntity, const int32_t componentID) : Component{ parentEntity, componentID } {
|
||||
m_PetInfo = CDClientManager::GetTable<CDPetComponentTable>()->GetByID(componentID); // TODO: Make reference when safe
|
||||
|
||||
m_Interaction = LWOOBJID_EMPTY;
|
||||
m_Owner = LWOOBJID_EMPTY;
|
||||
@@ -479,10 +481,19 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
|
||||
return;
|
||||
}
|
||||
|
||||
LWOOBJID petSubKey = ObjectIDManager::GenerateRandomObjectID();
|
||||
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();
|
||||
}
|
||||
|
||||
GeneralUtils::SetBit(petSubKey, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(petSubKey, eObjectBits::PERSISTENT);
|
||||
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;
|
||||
}
|
||||
|
||||
m_DatabaseId = petSubKey;
|
||||
|
||||
@@ -528,7 +539,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>();
|
||||
|
||||
@@ -545,18 +556,29 @@ 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();
|
||||
|
||||
m_ModerationStatus = 1; // Pending
|
||||
m_Name = "";
|
||||
// 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 = "";
|
||||
|
||||
//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;
|
||||
@@ -578,11 +600,21 @@ void PetComponent::RequestSetPetName(std::u16string name) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_ModerationStatus = 1; // Pending
|
||||
m_Name = "";
|
||||
// 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";
|
||||
|
||||
//Save our pet's new name to the db:
|
||||
SetPetNameForModeration(GeneralUtils::UTF16ToWTF8(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));
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class PetComponent final : public Component
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PET;
|
||||
|
||||
explicit PetComponent(Entity* parentEntity, uint32_t componentId);
|
||||
explicit PetComponent(Entity* parentEntity, const int32_t componentID);
|
||||
~PetComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
@@ -250,11 +250,6 @@ 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
|
||||
*/
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "dpShapeBox.h"
|
||||
#include "dpShapeSphere.h"
|
||||
|
||||
PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) {
|
||||
PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent, const int32_t componentID) : PhysicsComponent(parent, componentID) {
|
||||
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &PhantomPhysicsComponent::OnGetObjectReportInfo);
|
||||
|
||||
m_Position = m_Parent->GetDefaultPosition();
|
||||
|
||||
@@ -28,7 +28,7 @@ class PhantomPhysicsComponent final : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS;
|
||||
|
||||
PhantomPhysicsComponent(Entity* parent, int32_t componentId);
|
||||
PhantomPhysicsComponent(Entity* parent, const int32_t componentID);
|
||||
~PhantomPhysicsComponent() override;
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "EntityInfo.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Component(parent) {
|
||||
PhysicsComponent::PhysicsComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Position = NiPoint3Constant::ZERO;
|
||||
m_Rotation = QuatUtils::IDENTITY;
|
||||
m_DirtyPosition = false;
|
||||
@@ -23,7 +23,7 @@ PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Compon
|
||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable<CDPhysicsComponentTable>();
|
||||
|
||||
if (physicsComponentTable) {
|
||||
auto* info = physicsComponentTable->GetByID(componentId);
|
||||
auto* info = physicsComponentTable->GetByID(componentID);
|
||||
if (info) {
|
||||
m_CollisionGroup = info->collisionGroup;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class dpEntity;
|
||||
|
||||
class PhysicsComponent : public Component {
|
||||
public:
|
||||
PhysicsComponent(Entity* parent, int32_t componentId);
|
||||
PhysicsComponent(Entity* parent, const int32_t componentID);
|
||||
virtual ~PhysicsComponent() = default;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "PlayerForcedMovementComponent.h"
|
||||
|
||||
PlayerForcedMovementComponent::PlayerForcedMovementComponent(Entity* parent) : Component(parent) {
|
||||
PlayerForcedMovementComponent::PlayerForcedMovementComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Parent = parent;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
* Constructor for this component
|
||||
* @param parent parent that contains this component
|
||||
*/
|
||||
PlayerForcedMovementComponent(Entity* parent);
|
||||
PlayerForcedMovementComponent(Entity* parent, const int32_t componentID);
|
||||
~PlayerForcedMovementComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Inventory.h"
|
||||
#include "Item.h"
|
||||
|
||||
PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) : Component(parent) {
|
||||
PossessableComponent::PossessableComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
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, uint32_t componentId)
|
||||
// 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();
|
||||
|
||||
|
||||
@@ -16,15 +16,10 @@ class PossessableComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE;
|
||||
|
||||
PossessableComponent(Entity* parentEntity, uint32_t componentId);
|
||||
PossessableComponent(Entity* parentEntity, const int32_t componentID);
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
/**
|
||||
* @brief mounts the Entity
|
||||
*/
|
||||
void Mount();
|
||||
|
||||
/**
|
||||
* @brief dismounts the Entity
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "eControlScheme.h"
|
||||
#include "eStateChangeType.h"
|
||||
|
||||
PossessorComponent::PossessorComponent(Entity* parent) : Component(parent) {
|
||||
PossessorComponent::PossessorComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
m_Possessable = LWOOBJID_EMPTY;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class PossessorComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSOR;
|
||||
|
||||
PossessorComponent(Entity* parent);
|
||||
PossessorComponent(Entity* parent, const int32_t componentID);
|
||||
~PossessorComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
class PropertyComponent final : public Component {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY;
|
||||
explicit PropertyComponent(Entity* const parentEntity) noexcept : Component{ parentEntity } {}
|
||||
explicit PropertyComponent(Entity* const parentEntity, const int32_t componentID) noexcept : Component{ parentEntity, componentID } {}
|
||||
};
|
||||
|
||||
#endif // !PROPERTYCOMPONENT_H
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "ePropertySortType.h"
|
||||
#include "User.h"
|
||||
|
||||
PropertyEntranceComponent::PropertyEntranceComponent(Entity* parent, uint32_t componentID) : Component(parent) {
|
||||
PropertyEntranceComponent::PropertyEntranceComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
this->propertyQueries = {};
|
||||
|
||||
auto table = CDClientManager::GetTable<CDPropertyEntranceComponentTable>();
|
||||
@@ -135,7 +135,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
||||
const auto owner = propertyEntry.ownerId;
|
||||
const auto otherCharacter = Database::Get()->GetCharacterInfo(owner);
|
||||
if (!otherCharacter.has_value()) {
|
||||
LOG("Failed to find property owner name for %u!", owner);
|
||||
LOG("Failed to find property owner name for %llu!", owner);
|
||||
continue;
|
||||
}
|
||||
auto& entry = entries.emplace_back();
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
class PropertyEntranceComponent final : public Component {
|
||||
public:
|
||||
explicit PropertyEntranceComponent(Entity* parent, uint32_t componentID);
|
||||
explicit PropertyEntranceComponent(Entity* parent, const int32_t componentID);
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_ENTRANCE;
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
PropertyManagementComponent* PropertyManagementComponent::instance = nullptr;
|
||||
|
||||
PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Component(parent) {
|
||||
PropertyManagementComponent::PropertyManagementComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||
this->owner = LWOOBJID_EMPTY;
|
||||
this->templateId = 0;
|
||||
this->propertyId = LWOOBJID_EMPTY;
|
||||
@@ -170,7 +170,7 @@ void PropertyManagementComponent::UpdatePropertyDetails(std::string name, std::s
|
||||
info.name = propertyName;
|
||||
info.description = propertyDescription;
|
||||
info.lastUpdatedTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
|
||||
Database::Get()->UpdateLastSave(info);
|
||||
Database::Get()->UpdatePropertyDetails(info);
|
||||
|
||||
@@ -203,14 +203,22 @@ bool PropertyManagementComponent::Claim(const LWOOBJID playerId) {
|
||||
|
||||
auto prop_path = zone->GetPath(m_Parent->GetVarAsString(u"propertyName"));
|
||||
|
||||
if (prop_path){
|
||||
if (prop_path) {
|
||||
if (!prop_path->property.displayName.empty()) name = prop_path->property.displayName;
|
||||
description = prop_path->property.displayDesc;
|
||||
}
|
||||
|
||||
SetOwnerId(playerId);
|
||||
|
||||
propertyId = ObjectIDManager::GenerateRandomObjectID();
|
||||
// Due to legacy IDs being random
|
||||
propertyId = ObjectIDManager::GetPersistentID();
|
||||
const uint32_t maxTries = 100;
|
||||
uint32_t tries = 0;
|
||||
while (Database::Get()->GetPropertyInfo(propertyId) && tries < maxTries) {
|
||||
tries++;
|
||||
LOG("Found a duplicate property %llu, getting a new propertyId", propertyId);
|
||||
propertyId = ObjectIDManager::GetPersistentID();
|
||||
}
|
||||
|
||||
IProperty::Info info;
|
||||
info.id = propertyId;
|
||||
@@ -374,46 +382,45 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
|
||||
node->position = position;
|
||||
node->rotation = rotation;
|
||||
|
||||
ObjectIDManager::RequestPersistentID([this, node, modelLOT, entity, position, rotation, originalRotation](uint32_t persistentId) {
|
||||
SpawnerInfo info{};
|
||||
SpawnerInfo info{};
|
||||
|
||||
info.templateID = modelLOT;
|
||||
info.nodes = { node };
|
||||
info.templateScale = 1.0f;
|
||||
info.activeOnLoad = true;
|
||||
info.amountMaintained = 1;
|
||||
info.respawnTime = 10;
|
||||
info.templateID = modelLOT;
|
||||
info.nodes = { node };
|
||||
info.templateScale = 1.0f;
|
||||
info.activeOnLoad = true;
|
||||
info.amountMaintained = 1;
|
||||
info.respawnTime = 10;
|
||||
|
||||
info.emulated = true;
|
||||
info.emulator = Game::entityManager->GetZoneControlEntity()->GetObjectID();
|
||||
info.emulated = true;
|
||||
info.emulator = Game::entityManager->GetZoneControlEntity()->GetObjectID();
|
||||
|
||||
info.spawnerID = persistentId;
|
||||
GeneralUtils::SetBit(info.spawnerID, eObjectBits::CLIENT);
|
||||
info.spawnerID = ObjectIDManager::GetPersistentID();
|
||||
GeneralUtils::SetBit(info.spawnerID, eObjectBits::CLIENT);
|
||||
|
||||
const auto spawnerId = Game::zoneManager->MakeSpawner(info);
|
||||
const auto spawnerId = Game::zoneManager->MakeSpawner(info);
|
||||
|
||||
auto* spawner = Game::zoneManager->GetSpawner(spawnerId);
|
||||
auto* spawner = Game::zoneManager->GetSpawner(spawnerId);
|
||||
|
||||
info.nodes[0]->config.push_back(new LDFData<LWOOBJID>(u"modelBehaviors", 0));
|
||||
info.nodes[0]->config.push_back(new LDFData<LWOOBJID>(u"userModelID", info.spawnerID));
|
||||
info.nodes[0]->config.push_back(new LDFData<int>(u"modelType", 2));
|
||||
info.nodes[0]->config.push_back(new LDFData<bool>(u"propertyObjectID", true));
|
||||
info.nodes[0]->config.push_back(new LDFData<int>(u"componentWhitelist", 1));
|
||||
info.nodes[0]->config.push_back(new LDFData<LWOOBJID>(u"modelBehaviors", 0));
|
||||
info.nodes[0]->config.push_back(new LDFData<LWOOBJID>(u"userModelID", info.spawnerID));
|
||||
info.nodes[0]->config.push_back(new LDFData<int>(u"modelType", 2));
|
||||
info.nodes[0]->config.push_back(new LDFData<bool>(u"propertyObjectID", true));
|
||||
info.nodes[0]->config.push_back(new LDFData<int>(u"componentWhitelist", 1));
|
||||
|
||||
auto* model = spawner->Spawn();
|
||||
auto* modelComponent = model->GetComponent<ModelComponent>();
|
||||
if (modelComponent) modelComponent->Pause();
|
||||
auto* model = spawner->Spawn();
|
||||
auto* modelComponent = model->GetComponent<ModelComponent>();
|
||||
if (modelComponent) modelComponent->Pause();
|
||||
|
||||
models.insert_or_assign(model->GetObjectID(), spawnerId);
|
||||
models.insert_or_assign(model->GetObjectID(), spawnerId);
|
||||
|
||||
GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), position, m_Parent->GetObjectID(), 14, originalRotation);
|
||||
GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), position, m_Parent->GetObjectID(), 14, originalRotation);
|
||||
|
||||
GameMessages::SendUGCEquipPreCreateBasedOnEditMode(entity->GetObjectID(), entity->GetSystemAddress(), 0, spawnerId);
|
||||
GameMessages::SendUGCEquipPreCreateBasedOnEditMode(entity->GetObjectID(), entity->GetSystemAddress(), 0, spawnerId);
|
||||
|
||||
GameMessages::SendGetModelsOnProperty(entity->GetObjectID(), GetModels(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
GameMessages::SendGetModelsOnProperty(entity->GetObjectID(), GetModels(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
Game::entityManager->GetZoneControlEntity()->OnZonePropertyModelPlaced(entity);
|
||||
|
||||
Game::entityManager->GetZoneControlEntity()->OnZonePropertyModelPlaced(entity);
|
||||
});
|
||||
// Progress place model missions
|
||||
auto missionComponent = entity->GetComponent<MissionComponent>();
|
||||
if (missionComponent != nullptr) missionComponent->Progress(eMissionTaskType::PLACE_MODEL, 0);
|
||||
@@ -621,8 +628,6 @@ void PropertyManagementComponent::Load() {
|
||||
//BBB property models need to have extra stuff set for them:
|
||||
if (databaseModel.lot == 14) {
|
||||
LWOOBJID blueprintID = databaseModel.ugcId;
|
||||
GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT);
|
||||
|
||||
settings.push_back(new LDFData<LWOOBJID>(u"blueprintid", blueprintID));
|
||||
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
|
||||
@@ -695,7 +700,7 @@ void PropertyManagementComponent::Save() {
|
||||
// save the behaviors of the model
|
||||
for (const auto& [behaviorId, behaviorStr] : modelBehaviors) {
|
||||
if (behaviorStr.empty() || behaviorId == -1 || behaviorId == 0) continue;
|
||||
IBehaviors::Info info {
|
||||
IBehaviors::Info info{
|
||||
.behaviorId = behaviorId,
|
||||
.characterId = character->GetID(),
|
||||
.behaviorInfo = behaviorStr
|
||||
@@ -823,7 +828,7 @@ void PropertyManagementComponent::OnChatMessageReceived(const std::string& sMess
|
||||
if (!model) continue;
|
||||
auto* const modelComponent = model->GetComponent<ModelComponent>();
|
||||
if (!modelComponent) continue;
|
||||
|
||||
|
||||
modelComponent->OnChatMessageReceived(sMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user