mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-19 03:36:56 -06:00
Compare commits
14 Commits
reset-enti
...
despawn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7145536061 | ||
|
|
6b52cf67a0 | ||
|
|
71f708f1b5 | ||
|
|
49aa632d42 | ||
|
|
5ec4142ca1 | ||
|
|
5e9fe40bec | ||
|
|
9524198044 | ||
|
|
a5d0788488 | ||
|
|
a1ba5b8f12 | ||
|
|
48510b7315 | ||
|
|
c697f8ad97 | ||
|
|
55d181ea4b | ||
|
|
ecbb465020 | ||
|
|
ec9927acbb |
@@ -3,6 +3,7 @@
|
||||
// C++
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
@@ -145,7 +146,7 @@ namespace GeneralUtils {
|
||||
template <typename... Bases>
|
||||
struct overload : Bases... {
|
||||
using is_transparent = void;
|
||||
using Bases::operator() ... ;
|
||||
using Bases::operator() ...;
|
||||
};
|
||||
|
||||
struct char_pointer_hash {
|
||||
@@ -202,7 +203,7 @@ namespace GeneralUtils {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires(!Numeric<T>)
|
||||
requires(!Numeric<T>)
|
||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
|
||||
|
||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
||||
@@ -221,7 +222,7 @@ namespace GeneralUtils {
|
||||
*/
|
||||
template <std::floating_point T>
|
||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
|
||||
try {
|
||||
try {
|
||||
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
||||
|
||||
size_t parseNum;
|
||||
@@ -299,6 +300,12 @@ namespace GeneralUtils {
|
||||
return T();
|
||||
}
|
||||
|
||||
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)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts the value of an enum entry to its underlying type
|
||||
* @param entry Enum entry to cast
|
||||
@@ -323,4 +330,28 @@ namespace GeneralUtils {
|
||||
|
||||
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
|
||||
}
|
||||
|
||||
// https://www.quora.com/How-do-you-round-to-specific-increments-like-0-5-in-C
|
||||
// Rounds to the nearest floating point value specified.
|
||||
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||
T RountToNearestEven(const T value, const T modulus) {
|
||||
const auto modulo = std::fmod(value, modulus);
|
||||
const auto abs_modulo_2 = std::abs(modulo * 2);
|
||||
const auto abs_modulus = std::abs(modulus);
|
||||
|
||||
bool round_away_from_zero = false;
|
||||
if (abs_modulo_2 > abs_modulus) {
|
||||
round_away_from_zero = true;
|
||||
} else if (abs_modulo_2 == abs_modulus) {
|
||||
const auto trunc_quot = std::floor(std::abs(value / modulus));
|
||||
const auto odd = std::fmod(trunc_quot, T{ 2 }) != 0;
|
||||
round_away_from_zero = odd;
|
||||
}
|
||||
|
||||
if (round_away_from_zero) {
|
||||
return value + (std::copysign(modulus, value) - modulo);
|
||||
} else {
|
||||
return value - modulo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <ranges>
|
||||
|
||||
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
|
||||
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoint3& curPosition) {
|
||||
Result toReturn;
|
||||
tinyxml2::XMLDocument doc;
|
||||
const auto err = doc.Parse(data.data());
|
||||
@@ -44,29 +44,42 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
|
||||
NiPoint3 lowest{ 10'000.0f, 10'000.0f, 10'000.0f };
|
||||
NiPoint3 highest{ -10'000.0f, -10'000.0f, -10'000.0f };
|
||||
|
||||
// Calculate the lowest and highest points on the entire model
|
||||
for (const auto& transformation : transformations | std::views::values) {
|
||||
auto split = GeneralUtils::SplitString(transformation, ',');
|
||||
if (split.size() < 12) {
|
||||
LOG("Not enough in the split?");
|
||||
continue;
|
||||
NiPoint3 delta = NiPoint3Constant::ZERO;
|
||||
if (curPosition == NiPoint3Constant::ZERO) {
|
||||
// Calculate the lowest and highest points on the entire model
|
||||
for (const auto& transformation : transformations | std::views::values) {
|
||||
auto split = GeneralUtils::SplitString(transformation, ',');
|
||||
if (split.size() < 12) {
|
||||
LOG("Not enough in the split?");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto x = GeneralUtils::TryParse<float>(split[9]).value();
|
||||
auto y = GeneralUtils::TryParse<float>(split[10]).value();
|
||||
auto z = GeneralUtils::TryParse<float>(split[11]).value();
|
||||
if (x < lowest.x) lowest.x = x;
|
||||
if (y < lowest.y) lowest.y = y;
|
||||
if (z < lowest.z) lowest.z = z;
|
||||
|
||||
if (highest.x < x) highest.x = x;
|
||||
if (highest.y < y) highest.y = y;
|
||||
if (highest.z < z) highest.z = z;
|
||||
}
|
||||
|
||||
auto x = GeneralUtils::TryParse<float>(split[9]).value();
|
||||
auto y = GeneralUtils::TryParse<float>(split[10]).value();
|
||||
auto z = GeneralUtils::TryParse<float>(split[11]).value();
|
||||
if (x < lowest.x) lowest.x = x;
|
||||
if (y < lowest.y) lowest.y = y;
|
||||
if (z < lowest.z) lowest.z = z;
|
||||
|
||||
if (highest.x < x) highest.x = x;
|
||||
if (highest.y < y) highest.y = y;
|
||||
if (highest.z < z) highest.z = z;
|
||||
delta = (highest - lowest) / 2.0f;
|
||||
} else {
|
||||
lowest = curPosition;
|
||||
highest = curPosition;
|
||||
delta = NiPoint3Constant::ZERO;
|
||||
}
|
||||
|
||||
auto delta = (highest - lowest) / 2.0f;
|
||||
auto newRootPos = lowest + delta;
|
||||
|
||||
// Need to snap this chosen position to the nearest valid spot
|
||||
// on the LEGO grid
|
||||
newRootPos.x = GeneralUtils::RountToNearestEven(newRootPos.x, 0.8f);
|
||||
newRootPos.z = GeneralUtils::RountToNearestEven(newRootPos.z, 0.8f);
|
||||
|
||||
// Clamp the Y to the lowest point on the model
|
||||
newRootPos.y = lowest.y;
|
||||
|
||||
@@ -78,9 +91,9 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto x = GeneralUtils::TryParse<float>(split[9]).value() - newRootPos.x;
|
||||
auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y;
|
||||
auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z;
|
||||
auto x = GeneralUtils::TryParse<float>(split[9]).value() - newRootPos.x + curPosition.x;
|
||||
auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y + curPosition.y;
|
||||
auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z + curPosition.z;
|
||||
std::stringstream stream;
|
||||
for (int i = 0; i < 9; i++) {
|
||||
stream << split[i];
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Lxfml {
|
||||
|
||||
// Normalizes a LXFML model to be positioned relative to its local 0, 0, 0 rather than a game worlds 0, 0, 0.
|
||||
// Returns a struct of its new center and the updated LXFML containing these edits.
|
||||
[[nodiscard]] Result NormalizePosition(const std::string_view data);
|
||||
[[nodiscard]] Result NormalizePosition(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO);
|
||||
|
||||
// these are only for the migrations due to a bug in one of the implementations.
|
||||
[[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data);
|
||||
|
||||
@@ -48,6 +48,7 @@ void MigrationRunner::RunMigrations() {
|
||||
bool runSd0Migrations = false;
|
||||
bool runNormalizeMigrations = false;
|
||||
bool runNormalizeAfterFirstPartMigrations = false;
|
||||
bool runBrickBuildsNotOnGrid = false;
|
||||
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) {
|
||||
auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry);
|
||||
|
||||
@@ -64,6 +65,8 @@ void MigrationRunner::RunMigrations() {
|
||||
runNormalizeMigrations = true;
|
||||
} else if (migration.name.ends_with("_normalize_model_positions_after_first_part.sql")) {
|
||||
runNormalizeAfterFirstPartMigrations = true;
|
||||
} else if (migration.name.ends_with("_brickbuilds_not_on_grid.sql")) {
|
||||
runBrickBuildsNotOnGrid = true;
|
||||
} else {
|
||||
finalSQL.append(migration.data.c_str());
|
||||
}
|
||||
@@ -71,7 +74,7 @@ void MigrationRunner::RunMigrations() {
|
||||
Database::Get()->InsertMigration(migration.name);
|
||||
}
|
||||
|
||||
if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations && !runNormalizeAfterFirstPartMigrations) {
|
||||
if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations && !runNormalizeAfterFirstPartMigrations && !runBrickBuildsNotOnGrid) {
|
||||
LOG("Server database is up to date.");
|
||||
return;
|
||||
}
|
||||
@@ -103,6 +106,10 @@ void MigrationRunner::RunMigrations() {
|
||||
if (runNormalizeAfterFirstPartMigrations) {
|
||||
ModelNormalizeMigration::RunAfterFirstPart();
|
||||
}
|
||||
|
||||
if (runBrickBuildsNotOnGrid) {
|
||||
ModelNormalizeMigration::RunBrickBuildGrid();
|
||||
}
|
||||
}
|
||||
|
||||
void MigrationRunner::RunSQLiteMigrations() {
|
||||
|
||||
@@ -48,3 +48,24 @@ void ModelNormalizeMigration::RunAfterFirstPart() {
|
||||
}
|
||||
Database::Get()->SetAutoCommit(oldCommit);
|
||||
}
|
||||
|
||||
void ModelNormalizeMigration::RunBrickBuildGrid() {
|
||||
const auto oldCommit = Database::Get()->GetAutoCommit();
|
||||
Database::Get()->SetAutoCommit(false);
|
||||
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;
|
||||
|
||||
Sd0 sd0(lxfmlData);
|
||||
const auto asStr = sd0.GetAsStringUncompressed();
|
||||
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()->UpdateUgcModelData(id, asStream);
|
||||
}
|
||||
Database::Get()->SetAutoCommit(oldCommit);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
namespace ModelNormalizeMigration {
|
||||
void Run();
|
||||
void RunAfterFirstPart();
|
||||
void RunBrickBuildGrid();
|
||||
};
|
||||
|
||||
#endif //!MODELNORMALIZEMIGRATION_H
|
||||
|
||||
@@ -97,6 +97,8 @@
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "CDZoneTableTable.h"
|
||||
|
||||
#include "StringifiedEnum.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
Observable<Entity*, const PositionUpdate&> Entity::OnPlayerPositionUpdate;
|
||||
@@ -187,6 +189,7 @@ Entity::~Entity() {
|
||||
}
|
||||
|
||||
void Entity::Initialize() {
|
||||
RegisterMsg(MessageType::Game::REQUEST_SERVER_OBJECT_INFO, this, &Entity::MsgRequestServerObjectInfo);
|
||||
/**
|
||||
* Setup trigger
|
||||
*/
|
||||
@@ -920,7 +923,7 @@ void Entity::WriteLDFData(const std::vector<LDFBaseData*>& ldf, RakNet::BitStrea
|
||||
numberOfValidKeys--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now write it to the main bitstream
|
||||
outBitStream.Write<uint32_t>(settingStream.GetNumberOfBytesUsed() + 1 + sizeof(uint32_t));
|
||||
outBitStream.Write<uint8_t>(0); //no compression used
|
||||
@@ -1662,11 +1665,9 @@ void Entity::PickupItem(const LWOOBJID& objectID) const {
|
||||
auto* const skillsTable = CDClientManager::GetTable<CDObjectSkillsTable>();
|
||||
const auto skills = skillsTable->Query([&info](CDObjectSkills entry) {return (entry.objectTemplate == info.lot); });
|
||||
for (const auto& skill : skills) {
|
||||
auto* skillComponent = GetComponent<SkillComponent>();
|
||||
const auto [skillComponent, missionComponent] = GetComponentsMut<SkillComponent, MissionComponent>();
|
||||
if (skillComponent) skillComponent->CastSkill(skill.skillID, GetObjectID(), GetObjectID(), skill.castOnType, NiQuaternion(0, 0, 0, 0));
|
||||
|
||||
auto* missionComponent = GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent != nullptr) {
|
||||
missionComponent->Progress(eMissionTaskType::POWERUP, skill.skillID);
|
||||
}
|
||||
@@ -2211,3 +2212,38 @@ bool Entity::HandleMsg(GameMessages::GameMsg& msg) const {
|
||||
void Entity::RegisterMsg(const MessageType::Game msgId, std::function<bool(GameMessages::GameMsg&)> handler) {
|
||||
m_MsgHandlers.emplace(msgId, handler);
|
||||
}
|
||||
|
||||
bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||
auto& requestInfo = static_cast<GameMessages::RequestServerObjectInfo&>(msg);
|
||||
AMFArrayValue response;
|
||||
response.Insert("visible", true);
|
||||
response.Insert("objectID", std::to_string(m_ObjectID));
|
||||
response.Insert("serverInfo", true);
|
||||
GameMessages::GetObjectReportInfo info{};
|
||||
info.info = response.InsertArray("data");
|
||||
auto& objectInfo = info.info->PushDebug("Object Details");
|
||||
auto* table = CDClientManager::GetTable<CDObjectsTable>();
|
||||
|
||||
const auto& objTableInfo = table->GetByID(GetLOT());
|
||||
|
||||
objectInfo.PushDebug<AMFStringValue>("Name") = objTableInfo.name;
|
||||
objectInfo.PushDebug<AMFIntValue>("Template ID(LOT)") = GetLOT();
|
||||
objectInfo.PushDebug<AMFStringValue>("Object ID") = std::to_string(GetObjectID());
|
||||
objectInfo.PushDebug<AMFStringValue>("Spawner's Object ID") = std::to_string(GetSpawnerID());
|
||||
|
||||
auto& componentDetails = objectInfo.PushDebug("Component Information");
|
||||
for (const auto [id, component] : m_Components) {
|
||||
componentDetails.PushDebug<AMFStringValue>(StringifiedEnum::ToString(id)) = "";
|
||||
}
|
||||
|
||||
auto& configData = objectInfo.PushDebug("Config Data");
|
||||
for (const auto config : m_Settings) {
|
||||
configData.PushDebug<AMFStringValue>(GeneralUtils::UTF16ToWTF8(config->GetKey())) = config->GetValueAsString();
|
||||
|
||||
}
|
||||
HandleMsg(info);
|
||||
|
||||
auto* targetForReport = Game::entityManager->GetEntity(requestInfo.targetForReport);
|
||||
if (targetForReport) GameMessages::SendUIMessageServerToSingleClient("ToggleObjectDebugger", response, targetForReport->GetSystemAddress());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
@@ -161,6 +162,12 @@ public:
|
||||
template<typename T>
|
||||
T* GetComponent() const;
|
||||
|
||||
template<typename... T>
|
||||
auto GetComponents() const;
|
||||
|
||||
template<typename... T>
|
||||
auto GetComponentsMut() const;
|
||||
|
||||
template<typename T>
|
||||
bool TryGetComponent(eReplicaComponentType componentId, T*& component) const;
|
||||
|
||||
@@ -168,6 +175,8 @@ public:
|
||||
|
||||
void AddComponent(eReplicaComponentType componentId, Component* component);
|
||||
|
||||
bool MsgRequestServerObjectInfo(GameMessages::GameMsg& msg);
|
||||
|
||||
// This is expceted to never return nullptr, an assert checks this.
|
||||
CppScripts::Script* const GetScript() const;
|
||||
|
||||
@@ -329,6 +338,10 @@ public:
|
||||
|
||||
bool HandleMsg(GameMessages::GameMsg& msg) const;
|
||||
|
||||
void RegisterMsg(const MessageType::Game msgId, auto* self, const auto handler) {
|
||||
RegisterMsg(msgId, std::bind(handler, self, std::placeholders::_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The observable for player entity position updates.
|
||||
*/
|
||||
@@ -579,3 +592,13 @@ inline ComponentType* Entity::AddComponent(VaArgs... args) {
|
||||
// To allow a static cast here instead of a dynamic one.
|
||||
return dynamic_cast<ComponentType*>(componentToReturn);
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
auto Entity::GetComponents() const {
|
||||
return GetComponentsMut<const T...>();
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
auto Entity::GetComponentsMut() const {
|
||||
return std::tuple{GetComponent<T>()...};
|
||||
}
|
||||
|
||||
@@ -129,6 +129,8 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE
|
||||
// Set the zone control entity if the entity is a zone control object, this should only happen once
|
||||
if (controller) {
|
||||
m_ZoneControlEntity = entity;
|
||||
// Proooooobably shouldn't ghost zoneControl
|
||||
m_ZoneControlEntity->SetIsGhostingCandidate(false);
|
||||
}
|
||||
|
||||
// Check if this entity is a respawn point, if so add it to the registry
|
||||
@@ -394,7 +396,7 @@ void EntityManager::ConstructAllEntities(const SystemAddress& sysAddr) {
|
||||
}
|
||||
}
|
||||
|
||||
UpdateGhosting(PlayerManager::GetPlayer(sysAddr), true);
|
||||
UpdateGhosting(PlayerManager::GetPlayer(sysAddr));
|
||||
}
|
||||
|
||||
void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr) {
|
||||
@@ -463,7 +465,7 @@ void EntityManager::UpdateGhosting() {
|
||||
m_PlayersToUpdateGhosting.clear();
|
||||
}
|
||||
|
||||
void EntityManager::UpdateGhosting(Entity* player, const bool constructAll) {
|
||||
void EntityManager::UpdateGhosting(Entity* player) {
|
||||
if (!player) return;
|
||||
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
@@ -513,9 +515,6 @@ void EntityManager::UpdateGhosting(Entity* player, const bool constructAll) {
|
||||
|
||||
entity->SetObservers(entity->GetObservers() + 1);
|
||||
|
||||
// TODO: figure out if zone control should be ghosted at all
|
||||
if (constructAll && entity->GetObjectID() == GetZoneControlEntity()->GetObjectID()) continue;
|
||||
|
||||
ConstructEntity(entity, player->GetSystemAddress());
|
||||
}
|
||||
}
|
||||
@@ -605,3 +604,14 @@ void EntityManager::FireEventServerSide(Entity* origin, std::string args) {
|
||||
bool EntityManager::IsExcludedFromGhosting(LOT lot) {
|
||||
return std::find(m_GhostingExcludedLOTs.begin(), m_GhostingExcludedLOTs.end(), lot) != m_GhostingExcludedLOTs.end();
|
||||
}
|
||||
|
||||
bool EntityManager::SendMessage(GameMessages::GameMsg& msg) const {
|
||||
bool handled = false;
|
||||
const auto entityItr = m_Entities.find(msg.target);
|
||||
if (entityItr != m_Entities.end()) {
|
||||
auto* const entity = entityItr->second;
|
||||
if (entity) handled = entity->HandleMsg(msg);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ class Player;
|
||||
class User;
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
|
||||
namespace GameMessages {
|
||||
struct GameMsg;
|
||||
}
|
||||
|
||||
struct SystemAddress;
|
||||
|
||||
class EntityManager {
|
||||
@@ -54,7 +58,7 @@ public:
|
||||
void SetGhostDistanceMin(float value);
|
||||
void QueueGhostUpdate(LWOOBJID playerID);
|
||||
void UpdateGhosting();
|
||||
void UpdateGhosting(Entity* player, const bool constructAll = false);
|
||||
void UpdateGhosting(Entity* player);
|
||||
void CheckGhosting(Entity* entity);
|
||||
Entity* GetGhostCandidate(LWOOBJID id) const;
|
||||
bool GetGhostingEnabled() const;
|
||||
@@ -72,6 +76,9 @@ public:
|
||||
const bool GetHardcoreDropinventoryOnDeath() { return m_HardcoreDropinventoryOnDeath; };
|
||||
const uint32_t GetHardcoreUscoreEnemiesMultiplier() { return m_HardcoreUscoreEnemiesMultiplier; };
|
||||
|
||||
// Messaging
|
||||
bool SendMessage(GameMessages::GameMsg& msg) const;
|
||||
|
||||
private:
|
||||
void SerializeEntities();
|
||||
void KillEntities();
|
||||
|
||||
@@ -575,7 +575,7 @@ void ActivityInstance::RewardParticipant(Entity* participant) {
|
||||
maxCoins = currencyTable[0].maxvalue;
|
||||
}
|
||||
|
||||
Loot::DropLoot(participant, m_Parent, activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
|
||||
Loot::DropLoot(participant, m_Parent->GetObjectID(), activityRewards[0].LootMatrixIndex, minCoins, maxCoins);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,19 +49,13 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character, con
|
||||
m_LastUpdateTimestamp = std::time(nullptr);
|
||||
m_SystemAddress = systemAddress;
|
||||
|
||||
RegisterMsg(MessageType::Game::REQUEST_SERVER_OBJECT_INFO, this, &CharacterComponent::OnRequestServerObjectInfo);
|
||||
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &CharacterComponent::OnGetObjectReportInfo);
|
||||
}
|
||||
|
||||
bool CharacterComponent::OnRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||
auto& request = static_cast<GameMessages::RequestServerObjectInfo&>(msg);
|
||||
AMFArrayValue response;
|
||||
bool CharacterComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& reportInfo = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
|
||||
response.Insert("visible", true);
|
||||
response.Insert("objectID", std::to_string(request.targetForReport));
|
||||
response.Insert("serverInfo", true);
|
||||
|
||||
auto& data = *response.InsertArray("data");
|
||||
auto& cmptType = data.PushDebug("Character");
|
||||
auto& cmptType = reportInfo.info->PushDebug("Character");
|
||||
|
||||
cmptType.PushDebug<AMFIntValue>("Component ID") = GeneralUtils::ToUnderlying(ComponentType);
|
||||
cmptType.PushDebug<AMFIntValue>("Character's account ID") = m_Character->GetParentUser()->GetAccountID();
|
||||
@@ -72,6 +66,13 @@ bool CharacterComponent::OnRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||
cmptType.PushDebug<AMFStringValue>("Total currency") = std::to_string(m_Character->GetCoins());
|
||||
cmptType.PushDebug<AMFStringValue>("Currency able to be picked up") = std::to_string(m_DroppedCoins);
|
||||
cmptType.PushDebug<AMFStringValue>("Tooltip flags value") = "0";
|
||||
auto& vl = cmptType.PushDebug("Visited Levels");
|
||||
for (const auto zoneID : m_VisitedLevels) {
|
||||
std::stringstream sstream;
|
||||
sstream << "MapID: " << zoneID.GetMapID() << " CloneID: " << zoneID.GetCloneID();
|
||||
vl.PushDebug<AMFStringValue>(sstream.str()) = "";
|
||||
}
|
||||
|
||||
// visited locations
|
||||
cmptType.PushDebug<AMFBoolValue>("is a GM") = m_GMLevel > eGameMasterLevel::CIVILIAN;
|
||||
cmptType.PushDebug<AMFBoolValue>("Has PVP flag turned on") = m_PvpEnabled;
|
||||
@@ -83,9 +84,6 @@ bool CharacterComponent::OnRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
||||
cmptType.PushDebug<AMFIntValue>("Current Activity Type") = GeneralUtils::ToUnderlying(m_CurrentActivity);
|
||||
cmptType.PushDebug<AMFDoubleValue>("Property Clone ID") = m_Character->GetPropertyCloneID();
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient("ToggleObjectDebugger", response, m_Parent->GetSystemAddress());
|
||||
|
||||
LOG("Handled!");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ public:
|
||||
void LoadVisitedLevelsXml(const tinyxml2::XMLElement& doc);
|
||||
private:
|
||||
|
||||
bool OnRequestServerObjectInfo(GameMessages::GameMsg& msg);
|
||||
bool OnGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
|
||||
/**
|
||||
* The map of active venture vision effects
|
||||
|
||||
@@ -755,18 +755,18 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
auto* member = Game::entityManager->GetEntity(specificOwner);
|
||||
|
||||
if (member) Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
if (member) Loot::DropLoot(member, m_Parent->GetObjectID(), lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
} else {
|
||||
for (const auto memberId : team->members) { // Free for all
|
||||
auto* member = Game::entityManager->GetEntity(memberId);
|
||||
|
||||
if (member == nullptr) continue;
|
||||
|
||||
Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
Loot::DropLoot(member, m_Parent->GetObjectID(), lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
} else { // drop loot for non team user
|
||||
Loot::DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
|
||||
Loot::DropLoot(owner, m_Parent->GetObjectID(), GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -784,7 +784,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
coinsTotal -= coinsToLose;
|
||||
|
||||
Loot::DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose);
|
||||
Loot::DropLoot(m_Parent, m_Parent->GetObjectID(), -1, coinsToLose, coinsToLose);
|
||||
character->SetCoins(coinsTotal, eLootSourceType::PICKUP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,12 @@ void ModelComponent::Update(float deltaTime) {
|
||||
for (auto& behavior : m_Behaviors) {
|
||||
behavior.Update(deltaTime, *this);
|
||||
}
|
||||
|
||||
if (!m_RestartAtEndOfFrame) return;
|
||||
|
||||
GameMessages::ResetModelToDefaults reset{};
|
||||
OnResetModelToDefaults(reset);
|
||||
m_RestartAtEndOfFrame = false;
|
||||
}
|
||||
|
||||
void ModelComponent::LoadBehaviors() {
|
||||
@@ -265,7 +271,7 @@ bool ModelComponent::TrySetVelocity(const NiPoint3& velocity) const {
|
||||
|
||||
// If we're currently moving on an axis, prevent the move so only 1 behavior can have control over an axis
|
||||
if (velocity != NiPoint3Constant::ZERO) {
|
||||
const auto [x, y, z] = velocity;
|
||||
const auto [x, y, z] = velocity * m_Speed;
|
||||
if (x != 0.0f) {
|
||||
if (currentVelocity.x != 0.0f) return false;
|
||||
currentVelocity.x = x;
|
||||
@@ -280,7 +286,6 @@ bool ModelComponent::TrySetVelocity(const NiPoint3& velocity) const {
|
||||
currentVelocity = velocity;
|
||||
}
|
||||
|
||||
currentVelocity *= m_Speed;
|
||||
m_Parent->SetVelocity(currentVelocity);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,11 @@ public:
|
||||
|
||||
void OnChatMessageReceived(const std::string& sMessage);
|
||||
|
||||
// Sets the speed of the model
|
||||
void SetSpeed(const float newSpeed) { m_Speed = newSpeed; }
|
||||
|
||||
// Whether or not to restart at the end of the frame
|
||||
void RestartAtEndOfFrame() { m_RestartAtEndOfFrame = true; }
|
||||
private:
|
||||
|
||||
// Loads a behavior from the database.
|
||||
@@ -190,4 +194,7 @@ private:
|
||||
|
||||
// The speed at which this model moves
|
||||
float m_Speed{ 3.0f };
|
||||
|
||||
// Whether or not to restart at the end of the frame.
|
||||
bool m_RestartAtEndOfFrame{ false };
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "dZoneManager.h"
|
||||
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "CDPhysicsComponentTable.h"
|
||||
|
||||
#include "dNavMesh.h"
|
||||
@@ -89,6 +90,9 @@ void MovementAIComponent::Resume() {
|
||||
void MovementAIComponent::Update(const float deltaTime) {
|
||||
if (m_Paused) return;
|
||||
|
||||
auto* const quickBuildComponent = m_Parent->GetComponent<QuickBuildComponent>();
|
||||
if (quickBuildComponent && quickBuildComponent->GetState() != eQuickBuildState::COMPLETED) return;
|
||||
|
||||
if (m_PullingToPoint) {
|
||||
const auto source = GetCurrentWaypoint();
|
||||
|
||||
@@ -154,6 +158,7 @@ void MovementAIComponent::Update(const float deltaTime) {
|
||||
}
|
||||
} else {
|
||||
// Check if there are more waypoints in the queue, if so set our next destination to the next waypoint
|
||||
const auto waypointNum = m_IsBounced ? m_CurrentPath.size() : m_CurrentPathWaypointCount - m_CurrentPath.size() - 1;
|
||||
if (m_CurrentPath.empty()) {
|
||||
if (m_Path) {
|
||||
if (m_Path->pathBehavior == PathBehavior::Loop) {
|
||||
@@ -161,20 +166,24 @@ void MovementAIComponent::Update(const float deltaTime) {
|
||||
} else if (m_Path->pathBehavior == PathBehavior::Bounce) {
|
||||
m_IsBounced = !m_IsBounced;
|
||||
std::vector<PathWaypoint> waypoints = m_Path->pathWaypoints;
|
||||
if (m_IsBounced) std::reverse(waypoints.begin(), waypoints.end());
|
||||
if (m_IsBounced) std::ranges::reverse(waypoints);
|
||||
SetPath(waypoints);
|
||||
} else if (m_Path->pathBehavior == PathBehavior::Once) {
|
||||
m_Parent->GetScript()->OnWaypointReached(m_Parent, waypointNum);
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
m_Parent->GetScript()->OnWaypointReached(m_Parent, waypointNum);
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
SetDestination(m_CurrentPath.top().position);
|
||||
} else {
|
||||
m_Parent->GetScript()->OnWaypointReached(m_Parent, waypointNum);
|
||||
SetDestination(m_CurrentPath.top().position);
|
||||
|
||||
m_CurrentPath.pop();
|
||||
m_CurrentPath.pop();
|
||||
}
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
@@ -250,6 +259,7 @@ void MovementAIComponent::Stop() {
|
||||
|
||||
m_InterpolatedWaypoints.clear();
|
||||
while (!m_CurrentPath.empty()) m_CurrentPath.pop();
|
||||
m_CurrentPathWaypointCount = 0;
|
||||
|
||||
m_PathIndex = 0;
|
||||
|
||||
@@ -272,6 +282,7 @@ void MovementAIComponent::SetPath(std::vector<PathWaypoint> path) {
|
||||
this->m_CurrentPath.push(point);
|
||||
});
|
||||
|
||||
m_CurrentPathWaypointCount = path.size();
|
||||
SetDestination(path.front().position);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,6 +209,8 @@ public:
|
||||
*/
|
||||
static float GetBaseSpeed(LOT lot);
|
||||
|
||||
bool IsPaused() const { return m_Paused; }
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@@ -323,6 +325,9 @@ private:
|
||||
NiPoint3 m_SavedVelocity;
|
||||
|
||||
bool m_IsBounced{};
|
||||
|
||||
// The number of waypoints that were on the path in the call to SetPath
|
||||
uint32_t m_CurrentPathWaypointCount{ 0 };
|
||||
};
|
||||
|
||||
#endif // MOVEMENTAICOMPONENT_H
|
||||
|
||||
@@ -459,7 +459,7 @@ void QuickBuildComponent::CompleteQuickBuild(Entity* const user) {
|
||||
auto* missionComponent = builder->GetComponent<MissionComponent>();
|
||||
if (missionComponent) missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityId);
|
||||
}
|
||||
Loot::DropActivityLoot(builder, m_Parent, m_ActivityId, 1);
|
||||
Loot::DropActivityLoot(builder, m_Parent->GetObjectID(), m_ActivityId, 1);
|
||||
}
|
||||
|
||||
// Notify scripts
|
||||
|
||||
@@ -393,7 +393,7 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
|
||||
}
|
||||
|
||||
const auto score = playersRating * 10 + data->finished;
|
||||
Loot::GiveActivityLoot(player, m_Parent, m_ActivityID, score);
|
||||
Loot::GiveActivityLoot(player, m_Parent->GetObjectID(), m_ActivityID, score);
|
||||
|
||||
// Giving rewards
|
||||
GameMessages::SendNotifyRacingClient(
|
||||
|
||||
@@ -6165,12 +6165,9 @@ void GameMessages::HandleRemoveDonationItem(RakNet::BitStream& inStream, Entity*
|
||||
}
|
||||
|
||||
void GameMessages::HandleConfirmDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity) {
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
if (!inventoryComponent) return;
|
||||
auto* missionComponent = entity->GetComponent<MissionComponent>();
|
||||
if (!missionComponent) return;
|
||||
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
||||
if (!characterComponent || !characterComponent->GetCurrentInteracting()) return;
|
||||
const auto [inventoryComponent, missionComponent, characterComponent] = entity->GetComponentsMut<InventoryComponent, MissionComponent, CharacterComponent>();
|
||||
if (!inventoryComponent || !missionComponent || !characterComponent || !characterComponent->GetCurrentInteracting()) return;
|
||||
|
||||
auto* donationEntity = Game::entityManager->GetEntity(characterComponent->GetCurrentInteracting());
|
||||
if (!donationEntity) return;
|
||||
auto* donationVendorComponent = donationEntity->GetComponent<DonationVendorComponent>();
|
||||
@@ -6312,6 +6309,10 @@ void GameMessages::SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress&
|
||||
}
|
||||
|
||||
namespace GameMessages {
|
||||
bool GameMsg::Send() {
|
||||
return Game::entityManager->SendMessage(*this);
|
||||
}
|
||||
|
||||
void GameMsg::Send(const SystemAddress& sysAddr) const {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "eGameMasterLevel.h"
|
||||
|
||||
class AMFBaseValue;
|
||||
class AMFArrayValue;
|
||||
class Entity;
|
||||
class Item;
|
||||
class NiQuaternion;
|
||||
@@ -54,6 +55,12 @@ namespace GameMessages {
|
||||
GameMsg(MessageType::Game gmId, eGameMasterLevel lvl) : msgId{ gmId }, requiredGmLevel{ lvl } {}
|
||||
GameMsg(MessageType::Game gmId) : GameMsg(gmId, eGameMasterLevel::CIVILIAN) {}
|
||||
virtual ~GameMsg() = default;
|
||||
|
||||
// Sends a message to the entity manager to route to the target
|
||||
bool Send();
|
||||
|
||||
// Sends the message to the specified client or
|
||||
// all clients if UNASSIGNED_SYSTEM_ADDRESS is specified
|
||||
void Send(const SystemAddress& sysAddr) const;
|
||||
virtual void Serialize(RakNet::BitStream& bitStream) const {}
|
||||
virtual bool Deserialize(RakNet::BitStream& bitStream) { return true; }
|
||||
@@ -782,6 +789,13 @@ namespace GameMessages {
|
||||
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
|
||||
};
|
||||
|
||||
struct GetObjectReportInfo : public GameMsg {
|
||||
AMFArrayValue* info{};
|
||||
bool bVerbose{};
|
||||
|
||||
GetObjectReportInfo() : GameMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, eGameMasterLevel::DEVELOPER) {}
|
||||
};
|
||||
|
||||
struct RequestUse : public GameMsg {
|
||||
RequestUse() : GameMsg(MessageType::Game::REQUEST_USE) {}
|
||||
|
||||
|
||||
@@ -96,20 +96,18 @@ void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
|
||||
if (index >= m_Tasks.size()) {
|
||||
break;
|
||||
}
|
||||
auto* const curTask = m_Tasks[index];
|
||||
|
||||
const auto type = m_Tasks[index]->GetType();
|
||||
const auto type = curTask->GetType();
|
||||
|
||||
if (type == eMissionTaskType::COLLECTION ||
|
||||
type == eMissionTaskType::VISIT_PROPERTY) {
|
||||
auto value = std::stoul(task->Attribute("v"));
|
||||
curTask->SetProgress(value, false);
|
||||
task = task->NextSiblingElement();
|
||||
|
||||
// Collection tasks and visit property tasks store each of the collected/visited targets after the progress value
|
||||
if (type == eMissionTaskType::COLLECTION || type == eMissionTaskType::VISIT_PROPERTY) {
|
||||
std::vector<uint32_t> uniques;
|
||||
|
||||
const auto value = std::stoul(task->Attribute("v"));
|
||||
|
||||
m_Tasks[index]->SetProgress(value, false);
|
||||
|
||||
task = task->NextSiblingElement();
|
||||
|
||||
while (task != nullptr) {
|
||||
while (task != nullptr && value > 0) {
|
||||
const auto unique = std::stoul(task->Attribute("v"));
|
||||
|
||||
uniques.push_back(unique);
|
||||
@@ -119,19 +117,10 @@ void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
|
||||
}
|
||||
|
||||
task = task->NextSiblingElement();
|
||||
value--;
|
||||
}
|
||||
|
||||
m_Tasks[index]->SetUnique(uniques);
|
||||
|
||||
m_Tasks[index]->SetProgress(uniques.size(), false);
|
||||
|
||||
break;
|
||||
} else {
|
||||
const auto value = std::stoul(task->Attribute("v"));
|
||||
|
||||
m_Tasks[index]->SetProgress(value, false);
|
||||
|
||||
task = task->NextSiblingElement();
|
||||
curTask->SetUnique(uniques);
|
||||
}
|
||||
|
||||
index++;
|
||||
@@ -163,31 +152,19 @@ void Mission::UpdateXmlCur(tinyxml2::XMLElement& element) {
|
||||
|
||||
if (IsComplete()) return;
|
||||
|
||||
for (auto* task : m_Tasks) {
|
||||
if (task->GetType() == eMissionTaskType::COLLECTION ||
|
||||
task->GetType() == eMissionTaskType::VISIT_PROPERTY) {
|
||||
|
||||
auto* child = element.GetDocument()->NewElement("sv");
|
||||
|
||||
child->SetAttribute("v", static_cast<unsigned int>(task->GetProgress()));
|
||||
|
||||
element.LinkEndChild(child);
|
||||
|
||||
for (auto unique : task->GetUnique()) {
|
||||
auto* uniqueElement = element.GetDocument()->NewElement("sv");
|
||||
|
||||
uniqueElement->SetAttribute("v", static_cast<unsigned int>(unique));
|
||||
|
||||
element.LinkEndChild(uniqueElement);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
auto* child = element.GetDocument()->NewElement("sv");
|
||||
|
||||
for (const auto* const task : m_Tasks) {
|
||||
auto* const child = element.InsertNewChildElement("sv");
|
||||
child->SetAttribute("v", static_cast<unsigned int>(task->GetProgress()));
|
||||
|
||||
element.LinkEndChild(child);
|
||||
// Collection and visit property tasks then need to store the collected/visited items after the progress
|
||||
const auto taskType = task->GetType();
|
||||
if (taskType == eMissionTaskType::COLLECTION || taskType == eMissionTaskType::VISIT_PROPERTY) {
|
||||
for (const auto unique : task->GetUnique()) {
|
||||
auto* uniqueElement = element.InsertNewChildElement("sv");
|
||||
|
||||
uniqueElement->SetAttribute("v", static_cast<unsigned int>(unique));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,11 +484,6 @@ void Mission::YieldRewards() {
|
||||
// If a mission rewards zero of an item, make it reward 1.
|
||||
auto count = pair.second > 0 ? pair.second : 1;
|
||||
|
||||
// Sanity check, 6 is the max any mission yields
|
||||
if (count > 6) {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT);
|
||||
}
|
||||
|
||||
@@ -540,11 +512,6 @@ void Mission::YieldRewards() {
|
||||
// If a mission rewards zero of an item, make it reward 1.
|
||||
auto count = pair.second > 0 ? pair.second : 1;
|
||||
|
||||
// Sanity check, 6 is the max any mission yields
|
||||
if (count > 6) {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT);
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +221,8 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent) {
|
||||
sound.target = modelComponent.GetParent()->GetObjectID();
|
||||
sound.soundID = numberAsInt;
|
||||
sound.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
} else if (nextActionType == "Restart") {
|
||||
modelComponent.RestartAtEndOfFrame();
|
||||
}
|
||||
/* END Action */
|
||||
/* BEGIN Gameplay */
|
||||
|
||||
@@ -65,6 +65,8 @@ private:
|
||||
|
||||
// The position of the parent model on the previous frame
|
||||
NiPoint3 m_PreviousFramePosition{};
|
||||
|
||||
NiPoint3 m_SavedVelocity{};
|
||||
};
|
||||
|
||||
#endif //!__STRIP__H__
|
||||
|
||||
@@ -214,7 +214,7 @@ void Loot::GiveLoot(Entity* player, std::unordered_map<LOT, int32_t>& result, eL
|
||||
}
|
||||
}
|
||||
|
||||
void Loot::GiveActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating) {
|
||||
void Loot::GiveActivityLoot(Entity* player, const LWOOBJID source, uint32_t activityID, int32_t rating) {
|
||||
CDActivityRewardsTable* activityRewardsTable = CDClientManager::GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([activityID](CDActivityRewards entry) { return (entry.objectTemplate == activityID); });
|
||||
|
||||
@@ -248,7 +248,7 @@ void Loot::GiveActivityLoot(Entity* player, Entity* source, uint32_t activityID,
|
||||
character->SetCoins(character->GetCoins() + coins, eLootSourceType::ACTIVITY);
|
||||
}
|
||||
|
||||
void Loot::DropLoot(Entity* player, Entity* killedObject, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins) {
|
||||
void Loot::DropLoot(Entity* player, const LWOOBJID source, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins) {
|
||||
player = player->GetOwner(); // if the owner is overwritten, we collect that here
|
||||
|
||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
@@ -258,10 +258,10 @@ void Loot::DropLoot(Entity* player, Entity* killedObject, uint32_t matrixIndex,
|
||||
|
||||
std::unordered_map<LOT, int32_t> result = RollLootMatrix(player, matrixIndex);
|
||||
|
||||
DropLoot(player, killedObject, result, minCoins, maxCoins);
|
||||
DropLoot(player, source, result, minCoins, maxCoins);
|
||||
}
|
||||
|
||||
void Loot::DropLoot(Entity* player, Entity* killedObject, std::unordered_map<LOT, int32_t>& result, uint32_t minCoins, uint32_t maxCoins) {
|
||||
void Loot::DropLoot(Entity* player, const LWOOBJID source, std::unordered_map<LOT, int32_t>& result, uint32_t minCoins, uint32_t maxCoins) {
|
||||
player = player->GetOwner(); // if the owner is overwritten, we collect that here
|
||||
|
||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
@@ -269,9 +269,11 @@ void Loot::DropLoot(Entity* player, Entity* killedObject, std::unordered_map<LOT
|
||||
if (!inventoryComponent)
|
||||
return;
|
||||
|
||||
const auto spawnPosition = killedObject->GetPosition();
|
||||
GameMessages::GetPosition posMsg;
|
||||
posMsg.target = source;
|
||||
posMsg.Send();
|
||||
|
||||
const auto source = killedObject->GetObjectID();
|
||||
const auto spawnPosition = posMsg.pos;
|
||||
|
||||
for (const auto& pair : result) {
|
||||
for (int i = 0; i < pair.second; ++i) {
|
||||
@@ -284,7 +286,7 @@ void Loot::DropLoot(Entity* player, Entity* killedObject, std::unordered_map<LOT
|
||||
GameMessages::SendDropClientLoot(player, source, LOT_NULL, coins, spawnPosition);
|
||||
}
|
||||
|
||||
void Loot::DropActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating) {
|
||||
void Loot::DropActivityLoot(Entity* player, const LWOOBJID source, uint32_t activityID, int32_t rating) {
|
||||
CDActivityRewardsTable* activityRewardsTable = CDClientManager::GetTable<CDActivityRewardsTable>();
|
||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([activityID](CDActivityRewards entry) { return (entry.objectTemplate == activityID); });
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ namespace Loot {
|
||||
void CacheMatrix(const uint32_t matrixIndex);
|
||||
void GiveLoot(Entity* player, uint32_t matrixIndex, eLootSourceType lootSourceType = eLootSourceType::NONE);
|
||||
void GiveLoot(Entity* player, std::unordered_map<LOT, int32_t>& result, eLootSourceType lootSourceType = eLootSourceType::NONE);
|
||||
void GiveActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating = 0);
|
||||
void DropLoot(Entity* player, Entity* killedObject, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins);
|
||||
void DropLoot(Entity* player, Entity* killedObject, std::unordered_map<LOT, int32_t>& result, uint32_t minCoins, uint32_t maxCoins);
|
||||
void DropActivityLoot(Entity* player, Entity* source, uint32_t activityID, int32_t rating = 0);
|
||||
void GiveActivityLoot(Entity* player, const LWOOBJID source, uint32_t activityID, int32_t rating = 0);
|
||||
void DropLoot(Entity* player, const LWOOBJID source, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins);
|
||||
void DropLoot(Entity* player, const LWOOBJID source, std::unordered_map<LOT, int32_t>& result, uint32_t minCoins, uint32_t maxCoins);
|
||||
void DropActivityLoot(Entity* player, const LWOOBJID source, uint32_t activityID, int32_t rating = 0);
|
||||
};
|
||||
|
||||
@@ -1445,12 +1445,28 @@ void SlashCommandHandler::Startup() {
|
||||
};
|
||||
RegisterCommand(removeIgnoreCommand);
|
||||
|
||||
Command shutdownCommand{
|
||||
Command command{
|
||||
.help = "Shuts this world down",
|
||||
.info = "Shuts this world down",
|
||||
.aliases = {"shutdown"},
|
||||
.handle = DEVGMCommands::Shutdown,
|
||||
.requiredLevel = eGameMasterLevel::DEVELOPER
|
||||
};
|
||||
RegisterCommand(shutdownCommand);
|
||||
RegisterCommand(command);
|
||||
|
||||
RegisterCommand({
|
||||
.help = "Turns all players' pvp mode on",
|
||||
.info = "Turns all players' pvp mode on",
|
||||
.aliases = {"barfight"},
|
||||
.handle = DEVGMCommands::Barfight,
|
||||
.requiredLevel = eGameMasterLevel::DEVELOPER
|
||||
});
|
||||
|
||||
RegisterCommand({
|
||||
.help = "Despawns an object by id",
|
||||
.info = "Despawns an object by id",
|
||||
.aliases = {"despawn"},
|
||||
.handle = DEVGMCommands::Despawn,
|
||||
.requiredLevel = eGameMasterLevel::DEVELOPER
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "DEVGMCommands.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
// Classes
|
||||
#include "AssetManager.h"
|
||||
#include "Character.h"
|
||||
@@ -48,6 +50,7 @@
|
||||
#include "MessageType/Master.h"
|
||||
#include "eInventoryType.h"
|
||||
#include "ePlayerFlag.h"
|
||||
#include "StringifiedEnum.h"
|
||||
|
||||
|
||||
namespace DEVGMCommands {
|
||||
@@ -759,6 +762,7 @@ namespace DEVGMCommands {
|
||||
info.spawner = nullptr;
|
||||
info.spawnerID = entity->GetObjectID();
|
||||
info.spawnerNodeID = 0;
|
||||
info.settings = { new LDFData<bool>(u"SpawnedFromSlashCommand", true) };
|
||||
|
||||
Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr);
|
||||
|
||||
@@ -800,6 +804,7 @@ namespace DEVGMCommands {
|
||||
info.spawner = nullptr;
|
||||
info.spawnerID = entity->GetObjectID();
|
||||
info.spawnerNodeID = 0;
|
||||
info.settings = { new LDFData<bool>(u"SpawnedFromSlashCommand", true) };
|
||||
|
||||
auto playerPosition = entity->GetPosition();
|
||||
while (numberToSpawn > 0) {
|
||||
@@ -1514,7 +1519,12 @@ namespace DEVGMCommands {
|
||||
}
|
||||
|
||||
if (!closest) return;
|
||||
LOG("%llu", closest->GetObjectID());
|
||||
|
||||
GameMessages::RequestServerObjectInfo objectInfo;
|
||||
objectInfo.bVerbose = true;
|
||||
objectInfo.target = closest->GetObjectID();
|
||||
objectInfo.targetForReport = entity->GetObjectID();
|
||||
closest->HandleMsg(objectInfo);
|
||||
|
||||
Game::entityManager->SerializeEntity(closest);
|
||||
|
||||
@@ -1528,16 +1538,6 @@ namespace DEVGMCommands {
|
||||
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(header.str()));
|
||||
|
||||
for (const auto& pair : closest->GetComponents()) {
|
||||
auto id = pair.first;
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
stream << "Component [" << std::to_string(static_cast<uint32_t>(id)) << "]";
|
||||
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(stream.str()));
|
||||
}
|
||||
|
||||
if (splitArgs.size() >= 2) {
|
||||
if (splitArgs[1] == "-m" && splitArgs.size() >= 3) {
|
||||
auto* const movingPlatformComponent = closest->GetComponent<MovingPlatformComponent>();
|
||||
@@ -1557,13 +1557,6 @@ namespace DEVGMCommands {
|
||||
Game::entityManager->SerializeEntity(closest);
|
||||
} else if (splitArgs[1] == "-a" && splitArgs.size() >= 3) {
|
||||
RenderComponent::PlayAnimation(closest, splitArgs.at(2));
|
||||
} else if (splitArgs[1] == "-s") {
|
||||
for (auto* entry : closest->GetSettings()) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::UTF8ToUTF16(entry->GetString()));
|
||||
}
|
||||
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"------");
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Spawner ID: " + GeneralUtils::to_u16string(closest->GetSpawnerID()));
|
||||
} else if (splitArgs[1] == "-p") {
|
||||
const auto postion = closest->GetPosition();
|
||||
|
||||
@@ -1633,4 +1626,40 @@ namespace DEVGMCommands {
|
||||
if (character) LOG("Mythran (%s) has shutdown the world", character->GetName().c_str());
|
||||
Game::OnSignal(-1);
|
||||
}
|
||||
|
||||
void Barfight(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
auto* const characterComponent = entity->GetComponent<CharacterComponent>();
|
||||
if (!characterComponent) return;
|
||||
|
||||
for (auto* const player : PlayerManager::GetAllPlayers()) {
|
||||
auto* const pCharacterComponent = player->GetComponent<CharacterComponent>();
|
||||
if (pCharacterComponent) pCharacterComponent->SetPvpEnabled(true);
|
||||
Game::entityManager->SerializeEntity(player);
|
||||
}
|
||||
const auto msg = u"Pvp has been turned on for all players by " + GeneralUtils::ASCIIToUTF16(characterComponent->GetName());
|
||||
ChatPackets::SendSystemMessage(UNASSIGNED_SYSTEM_ADDRESS, msg, true);
|
||||
}
|
||||
|
||||
void Despawn(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
if (args.empty()) return;
|
||||
|
||||
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
|
||||
const auto objId = GeneralUtils::TryParse<LWOOBJID>(splitArgs[0]);
|
||||
if (!objId) return;
|
||||
|
||||
auto* const target = Game::entityManager->GetEntity(*objId);
|
||||
if (!target) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Entity not found: " + GeneralUtils::UTF8ToUTF16(splitArgs[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!target->GetVar<bool>(u"SpawnedFromSlashCommand")) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"You cannot despawn this entity as it was not despawned from a slash command.");
|
||||
return;
|
||||
}
|
||||
|
||||
target->Smash(LWOOBJID_EMPTY, eKillType::SILENT);
|
||||
LOG("Despawned entity (%llu)", target->GetObjectID());
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Despawned entity: " + GeneralUtils::to_u16string(target->GetObjectID()));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -74,6 +74,8 @@ namespace DEVGMCommands {
|
||||
void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void Shutdown(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void Barfight(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void Despawn(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
}
|
||||
|
||||
#endif //!DEVGMCOMMANDS_H
|
||||
|
||||
@@ -14,24 +14,21 @@
|
||||
|
||||
#include "Start.h"
|
||||
|
||||
InstanceManager::InstanceManager(Logger* logger, const std::string& externalIP) {
|
||||
mLogger = logger;
|
||||
mExternalIP = externalIP;
|
||||
using std::make_unique;
|
||||
|
||||
namespace {
|
||||
const InstancePtr g_Empty{ nullptr };
|
||||
}
|
||||
|
||||
InstanceManager::InstanceManager(const std::string& externalIP) : mExternalIP{ externalIP } {
|
||||
m_LastPort =
|
||||
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("world_port_start")).value_or(m_LastPort);
|
||||
m_LastInstanceID = LWOINSTANCEID_INVALID;
|
||||
}
|
||||
|
||||
InstanceManager::~InstanceManager() {
|
||||
for (Instance* i : m_Instances) {
|
||||
delete i;
|
||||
i = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Instance* InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneID) {
|
||||
const InstancePtr& InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneID) {
|
||||
LOG("Searching for an instance for mapID %i/%i", mapID, cloneID);
|
||||
Instance* instance = FindInstance(mapID, isFriendTransfer, cloneID);
|
||||
auto& instance = FindInstance(mapID, isFriendTransfer, cloneID);
|
||||
if (instance) return instance;
|
||||
|
||||
// If we are shutting down, return a nullptr so a new instance is not created.
|
||||
@@ -40,7 +37,7 @@ Instance* InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, LW
|
||||
mapID,
|
||||
m_LastInstanceID + 1,
|
||||
cloneID);
|
||||
return nullptr;
|
||||
return g_Empty;
|
||||
}
|
||||
//TODO: Update this so that the IP is read from a configuration file instead
|
||||
|
||||
@@ -56,23 +53,23 @@ Instance* InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, LW
|
||||
}
|
||||
|
||||
uint32_t port = GetFreePort();
|
||||
instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, softCap, maxPlayers);
|
||||
auto newInstance = make_unique<Instance>(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, softCap, maxPlayers);
|
||||
|
||||
//Start the actual process:
|
||||
StartWorldServer(mapID, port, m_LastInstanceID, maxPlayers, cloneID);
|
||||
|
||||
m_Instances.push_back(instance);
|
||||
m_Instances.push_back(std::move(newInstance));
|
||||
|
||||
if (instance) {
|
||||
if (m_Instances.back()) {
|
||||
LOG("Created new instance: %i/%i/%i with min/max %i/%i", mapID, m_LastInstanceID, cloneID, softCap, maxPlayers);
|
||||
return instance;
|
||||
return m_Instances.back();
|
||||
} else LOG("Failed to create a new instance!");
|
||||
|
||||
return nullptr;
|
||||
return g_Empty;
|
||||
}
|
||||
|
||||
bool InstanceManager::IsPortInUse(uint32_t port) {
|
||||
for (Instance* i : m_Instances) {
|
||||
for (const auto& i : m_Instances) {
|
||||
if (i && i->GetPort() == port) {
|
||||
return true;
|
||||
}
|
||||
@@ -84,7 +81,7 @@ bool InstanceManager::IsPortInUse(uint32_t port) {
|
||||
uint32_t InstanceManager::GetFreePort() {
|
||||
uint32_t port = m_LastPort;
|
||||
std::vector<uint32_t> usedPorts;
|
||||
for (Instance* i : m_Instances) {
|
||||
for (const auto& i : m_Instances) {
|
||||
usedPorts.push_back(i->GetPort());
|
||||
}
|
||||
|
||||
@@ -101,7 +98,7 @@ uint32_t InstanceManager::GetFreePort() {
|
||||
}
|
||||
|
||||
void InstanceManager::AddPlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID) {
|
||||
Instance* inst = FindInstance(mapID, instanceID);
|
||||
const auto& inst = FindInstance(mapID, instanceID);
|
||||
if (inst) {
|
||||
Player player;
|
||||
player.addr = systemAddr;
|
||||
@@ -111,7 +108,7 @@ void InstanceManager::AddPlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINS
|
||||
}
|
||||
|
||||
void InstanceManager::RemovePlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID) {
|
||||
Instance* inst = FindInstance(mapID, instanceID);
|
||||
const auto& inst = FindInstance(mapID, instanceID);
|
||||
if (inst) {
|
||||
Player player;
|
||||
player.addr = systemAddr;
|
||||
@@ -120,25 +117,23 @@ void InstanceManager::RemovePlayer(SystemAddress systemAddr, LWOMAPID mapID, LWO
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Instance*> InstanceManager::GetInstances() const {
|
||||
const std::vector<InstancePtr>& InstanceManager::GetInstances() const {
|
||||
return m_Instances;
|
||||
}
|
||||
|
||||
void InstanceManager::AddInstance(Instance* instance) {
|
||||
void InstanceManager::AddInstance(InstancePtr& instance) {
|
||||
if (instance == nullptr) return;
|
||||
|
||||
m_Instances.push_back(instance);
|
||||
m_Instances.push_back(std::move(instance));
|
||||
}
|
||||
|
||||
void InstanceManager::RemoveInstance(Instance* instance) {
|
||||
void InstanceManager::RemoveInstance(const InstancePtr& instance) {
|
||||
for (uint32_t i = 0; i < m_Instances.size(); ++i) {
|
||||
if (m_Instances[i] == instance) {
|
||||
instance->SetShutdownComplete(true);
|
||||
|
||||
if (!Game::ShouldShutdown()) RedirectPendingRequests(instance);
|
||||
|
||||
delete m_Instances[i];
|
||||
|
||||
m_Instances.erase(m_Instances.begin() + i);
|
||||
|
||||
break;
|
||||
@@ -146,7 +141,7 @@ void InstanceManager::RemoveInstance(Instance* instance) {
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceManager::ReadyInstance(Instance* instance) {
|
||||
void InstanceManager::ReadyInstance(const InstancePtr& instance) {
|
||||
instance->SetIsReady(true);
|
||||
|
||||
auto& pending = instance->GetPendingRequests();
|
||||
@@ -172,7 +167,7 @@ void InstanceManager::ReadyInstance(Instance* instance) {
|
||||
pending.clear();
|
||||
}
|
||||
|
||||
void InstanceManager::RequestAffirmation(Instance* instance, const PendingInstanceRequest& request) {
|
||||
void InstanceManager::RequestAffirmation(const InstancePtr& instance, const PendingInstanceRequest& request) {
|
||||
instance->GetPendingAffirmations().push_back(request);
|
||||
|
||||
CBITSTREAM;
|
||||
@@ -189,7 +184,7 @@ void InstanceManager::RequestAffirmation(Instance* instance, const PendingInstan
|
||||
);
|
||||
}
|
||||
|
||||
void InstanceManager::AffirmTransfer(Instance* instance, const uint64_t transferID) {
|
||||
void InstanceManager::AffirmTransfer(const InstancePtr& instance, const uint64_t transferID) {
|
||||
auto& pending = instance->GetPendingAffirmations();
|
||||
|
||||
for (auto i = 0u; i < pending.size(); ++i) {
|
||||
@@ -217,11 +212,11 @@ void InstanceManager::AffirmTransfer(Instance* instance, const uint64_t transfer
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceManager::RedirectPendingRequests(Instance* instance) {
|
||||
void InstanceManager::RedirectPendingRequests(const InstancePtr& instance) {
|
||||
const auto& zoneId = instance->GetZoneID();
|
||||
|
||||
for (const auto& request : instance->GetPendingAffirmations()) {
|
||||
auto* in = Game::im->GetInstance(zoneId.GetMapID(), false, zoneId.GetCloneID());
|
||||
const auto& in = Game::im->GetInstance(zoneId.GetMapID(), false, zoneId.GetCloneID());
|
||||
|
||||
if (in && !in->GetIsReady()) // Instance not ready, make a pending request
|
||||
{
|
||||
@@ -234,57 +229,48 @@ void InstanceManager::RedirectPendingRequests(Instance* instance) {
|
||||
}
|
||||
}
|
||||
|
||||
Instance* InstanceManager::GetInstanceBySysAddr(SystemAddress& sysAddr) {
|
||||
for (uint32_t i = 0; i < m_Instances.size(); ++i) {
|
||||
if (m_Instances[i] && m_Instances[i]->GetSysAddr() == sysAddr) {
|
||||
return m_Instances[i];
|
||||
const InstancePtr& InstanceManager::GetInstanceBySysAddr(SystemAddress& sysAddr) {
|
||||
for (const auto& instance : m_Instances) {
|
||||
if (instance && instance->GetSysAddr() == sysAddr) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return g_Empty;
|
||||
}
|
||||
|
||||
bool InstanceManager::IsInstanceFull(Instance* instance, bool isFriendTransfer) {
|
||||
if (!isFriendTransfer && instance->GetSoftCap() > instance->GetCurrentClientCount())
|
||||
return false;
|
||||
else if (isFriendTransfer && instance->GetHardCap() > instance->GetCurrentClientCount())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Instance* InstanceManager::FindInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneId) {
|
||||
for (Instance* i : m_Instances) {
|
||||
if (i && i->GetMapID() == mapID && i->GetCloneID() == cloneId && !IsInstanceFull(i, isFriendTransfer) && !i->GetIsPrivate() && !i->GetShutdownComplete() && !i->GetIsShuttingDown()) {
|
||||
const InstancePtr& InstanceManager::FindInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneId) {
|
||||
for (const auto& i : m_Instances) {
|
||||
if (i && i->GetMapID() == mapID && i->GetCloneID() == cloneId && !i->IsFull(isFriendTransfer) && !i->GetIsPrivate() && !i->GetShutdownComplete() && !i->GetIsShuttingDown()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return g_Empty;
|
||||
}
|
||||
|
||||
Instance* InstanceManager::FindInstance(LWOMAPID mapID, LWOINSTANCEID instanceID) {
|
||||
for (Instance* i : m_Instances) {
|
||||
const InstancePtr& InstanceManager::FindInstance(LWOMAPID mapID, LWOINSTANCEID instanceID) {
|
||||
for (const auto& i : m_Instances) {
|
||||
if (i && i->GetMapID() == mapID && i->GetInstanceID() == instanceID && !i->GetIsPrivate() && !i->GetShutdownComplete() && !i->GetIsShuttingDown()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return g_Empty;
|
||||
}
|
||||
|
||||
Instance* InstanceManager::FindInstanceWithPrivate(LWOMAPID mapID, LWOINSTANCEID instanceID) {
|
||||
for (Instance* i : m_Instances) {
|
||||
const InstancePtr& InstanceManager::FindInstanceWithPrivate(LWOMAPID mapID, LWOINSTANCEID instanceID) {
|
||||
for (const auto& i : m_Instances) {
|
||||
if (i && i->GetMapID() == mapID && i->GetInstanceID() == instanceID && !i->GetShutdownComplete() && !i->GetIsShuttingDown()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return g_Empty;
|
||||
}
|
||||
|
||||
Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password) {
|
||||
auto* instance = FindPrivateInstance(password);
|
||||
const InstancePtr& InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password) {
|
||||
const auto& instance = FindPrivateInstance(password);
|
||||
|
||||
if (instance != nullptr) {
|
||||
return instance;
|
||||
@@ -295,27 +281,27 @@ Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID clon
|
||||
mapID,
|
||||
m_LastInstanceID + 1,
|
||||
cloneID);
|
||||
return nullptr;
|
||||
return g_Empty;
|
||||
}
|
||||
|
||||
int maxPlayers = 999;
|
||||
|
||||
uint32_t port = GetFreePort();
|
||||
instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, maxPlayers, maxPlayers, true, password);
|
||||
auto newInstance = make_unique<Instance>(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, maxPlayers, maxPlayers, true, password);
|
||||
|
||||
//Start the actual process:
|
||||
StartWorldServer(mapID, port, m_LastInstanceID, maxPlayers, cloneID);
|
||||
|
||||
m_Instances.push_back(instance);
|
||||
m_Instances.push_back(std::move(newInstance));
|
||||
|
||||
if (instance) return instance;
|
||||
if (m_Instances.back()) return m_Instances.back();
|
||||
else LOG("Failed to create a new instance!");
|
||||
|
||||
return instance;
|
||||
return g_Empty;
|
||||
}
|
||||
|
||||
Instance* InstanceManager::FindPrivateInstance(const std::string& password) {
|
||||
for (auto* instance : m_Instances) {
|
||||
const InstancePtr& InstanceManager::FindPrivateInstance(const std::string& password) {
|
||||
for (const auto& instance : m_Instances) {
|
||||
if (!instance) continue;
|
||||
|
||||
if (!instance->GetIsPrivate()) {
|
||||
@@ -329,7 +315,7 @@ Instance* InstanceManager::FindPrivateInstance(const std::string& password) {
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return g_Empty;
|
||||
}
|
||||
|
||||
int InstanceManager::GetSoftCap(LWOMAPID mapID) {
|
||||
@@ -363,3 +349,14 @@ void Instance::Shutdown() {
|
||||
|
||||
LOG("Triggered world shutdown for zone/clone/instance %i/%i/%i", GetMapID(), GetCloneID(), GetInstanceID());
|
||||
}
|
||||
|
||||
|
||||
bool Instance::IsFull(bool isFriendTransfer) const {
|
||||
if (!isFriendTransfer && GetSoftCap() > GetCurrentClientCount())
|
||||
return false;
|
||||
else if (isFriendTransfer && GetHardCap() > GetCurrentClientCount())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,8 @@ public:
|
||||
|
||||
void Shutdown();
|
||||
|
||||
bool IsFull(bool isFriendTransfer) const;
|
||||
|
||||
private:
|
||||
std::string m_IP{};
|
||||
uint32_t m_Port{};
|
||||
@@ -99,42 +101,42 @@ private:
|
||||
//Private functions:
|
||||
};
|
||||
|
||||
using InstancePtr = std::unique_ptr<Instance>;
|
||||
|
||||
class InstanceManager {
|
||||
public:
|
||||
InstanceManager(Logger* logger, const std::string& externalIP);
|
||||
~InstanceManager();
|
||||
InstanceManager(const std::string& externalIP);
|
||||
|
||||
Instance* GetInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneID); //Creates an instance if none found
|
||||
const InstancePtr& GetInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneID); //Creates an instance if none found
|
||||
bool IsPortInUse(uint32_t port);
|
||||
uint32_t GetFreePort();
|
||||
|
||||
void AddPlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID);
|
||||
void RemovePlayer(SystemAddress systemAddr, LWOMAPID mapID, LWOINSTANCEID instanceID);
|
||||
|
||||
std::vector<Instance*> GetInstances() const;
|
||||
void AddInstance(Instance* instance);
|
||||
void RemoveInstance(Instance* instance);
|
||||
const std::vector<InstancePtr>& GetInstances() const;
|
||||
void AddInstance(InstancePtr& instance);
|
||||
void RemoveInstance(const InstancePtr& instance);
|
||||
|
||||
void ReadyInstance(Instance* instance);
|
||||
void RequestAffirmation(Instance* instance, const PendingInstanceRequest& request);
|
||||
void AffirmTransfer(Instance* instance, uint64_t transferID);
|
||||
void ReadyInstance(const InstancePtr& instance);
|
||||
void RequestAffirmation(const InstancePtr& instance, const PendingInstanceRequest& request);
|
||||
void AffirmTransfer(const InstancePtr& instance, uint64_t transferID);
|
||||
|
||||
void RedirectPendingRequests(Instance* instance);
|
||||
void RedirectPendingRequests(const InstancePtr& instance);
|
||||
|
||||
Instance* GetInstanceBySysAddr(SystemAddress& sysAddr);
|
||||
const InstancePtr& GetInstanceBySysAddr(SystemAddress& sysAddr);
|
||||
|
||||
Instance* FindInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneId = 0);
|
||||
Instance* FindInstance(LWOMAPID mapID, LWOINSTANCEID instanceID);
|
||||
Instance* FindInstanceWithPrivate(LWOMAPID mapID, LWOINSTANCEID instanceID);
|
||||
const InstancePtr& FindInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneId = 0);
|
||||
const InstancePtr& FindInstance(LWOMAPID mapID, LWOINSTANCEID instanceID);
|
||||
const InstancePtr& FindInstanceWithPrivate(LWOMAPID mapID, LWOINSTANCEID instanceID);
|
||||
|
||||
Instance* CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password);
|
||||
Instance* FindPrivateInstance(const std::string& password);
|
||||
const InstancePtr& CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password);
|
||||
const InstancePtr& FindPrivateInstance(const std::string& password);
|
||||
void SetIsShuttingDown(bool value) { this->m_IsShuttingDown = value; };
|
||||
|
||||
private:
|
||||
Logger* mLogger;
|
||||
std::string mExternalIP;
|
||||
std::vector<Instance*> m_Instances;
|
||||
std::vector<std::unique_ptr<Instance>> m_Instances;
|
||||
uint16_t m_LastPort = 3000;
|
||||
LWOINSTANCEID m_LastInstanceID;
|
||||
|
||||
@@ -144,7 +146,6 @@ private:
|
||||
bool m_IsShuttingDown = false;
|
||||
|
||||
//Private functions:
|
||||
bool IsInstanceFull(Instance* instance, bool isFriendTransfer);
|
||||
int GetSoftCap(LWOMAPID mapID);
|
||||
int GetHardCap(LWOMAPID mapID);
|
||||
};
|
||||
|
||||
@@ -359,7 +359,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
//Create additional objects here:
|
||||
PersistentIDManager::Initialize();
|
||||
Game::im = new InstanceManager(Game::logger, Game::server->GetIP());
|
||||
Game::im = new InstanceManager(Game::server->GetIP());
|
||||
|
||||
//Get CDClient initial information
|
||||
try {
|
||||
@@ -434,9 +434,9 @@ int main(int argc, char** argv) {
|
||||
framesSinceKillUniverseCommand++;
|
||||
}
|
||||
|
||||
const auto instances = Game::im->GetInstances();
|
||||
const auto& instances = Game::im->GetInstances();
|
||||
|
||||
for (auto* instance : instances) {
|
||||
for (const auto& instance : instances) {
|
||||
if (instance == nullptr) {
|
||||
break;
|
||||
}
|
||||
@@ -460,7 +460,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
//Remove dead instances
|
||||
for (auto* instance : instances) {
|
||||
for (const auto& instance : instances) {
|
||||
if (instance == nullptr) {
|
||||
break;
|
||||
}
|
||||
@@ -489,7 +489,7 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
//Since this disconnection is intentional, we'll just delete it as
|
||||
//we'll start a new one anyway if needed:
|
||||
Instance* instance =
|
||||
const auto& instance =
|
||||
Game::im->GetInstanceBySysAddr(packet->systemAddress);
|
||||
if (instance) {
|
||||
LOG("Actually disconnected from zone %i clone %i instance %i port %i", instance->GetMapID(), instance->GetCloneID(), instance->GetInstanceID(), instance->GetPort());
|
||||
@@ -510,7 +510,7 @@ void HandlePacket(Packet* packet) {
|
||||
if (packet->data[0] == ID_CONNECTION_LOST) {
|
||||
LOG("A server has lost the connection");
|
||||
|
||||
Instance* instance =
|
||||
const auto& instance =
|
||||
Game::im->GetInstanceBySysAddr(packet->systemAddress);
|
||||
if (instance) {
|
||||
LWOZONEID zoneID = instance->GetZoneID(); //Get the zoneID so we can recreate a server
|
||||
@@ -561,9 +561,9 @@ void HandlePacket(Packet* packet) {
|
||||
LOG("Shutdown sequence has been started. Not creating a new zone.");
|
||||
break;
|
||||
}
|
||||
Instance* in = Game::im->GetInstance(zoneID, false, zoneClone);
|
||||
const auto& in = Game::im->GetInstance(zoneID, false, zoneClone);
|
||||
|
||||
for (auto* instance : Game::im->GetInstances()) {
|
||||
for (const auto& instance : Game::im->GetInstances()) {
|
||||
LOG("Instance: %i/%i/%i -> %i %s", instance->GetMapID(), instance->GetCloneID(), instance->GetInstanceID(), instance == in, instance->GetSysAddr().ToString());
|
||||
}
|
||||
|
||||
@@ -603,12 +603,12 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
if (theirServerType == ServerType::World) {
|
||||
if (!Game::im->IsPortInUse(theirPort)) {
|
||||
Instance* in = new Instance(theirIP.string, theirPort, theirZoneID, theirInstanceID, 0, 12, 12);
|
||||
auto in = std::make_unique<Instance>(theirIP.string, theirPort, theirZoneID, theirInstanceID, 0, 12, 12);
|
||||
|
||||
in->SetSysAddr(packet->systemAddress);
|
||||
Game::im->AddInstance(in);
|
||||
} else {
|
||||
auto* instance = Game::im->FindInstanceWithPrivate(theirZoneID, static_cast<LWOINSTANCEID>(theirInstanceID));
|
||||
const auto& instance = Game::im->FindInstanceWithPrivate(theirZoneID, static_cast<LWOINSTANCEID>(theirInstanceID));
|
||||
if (instance) {
|
||||
instance->SetSysAddr(packet->systemAddress);
|
||||
}
|
||||
@@ -682,7 +682,7 @@ void HandlePacket(Packet* packet) {
|
||||
inStream.Read(theirZoneID);
|
||||
inStream.Read(theirInstanceID);
|
||||
|
||||
auto instance =
|
||||
const auto& instance =
|
||||
Game::im->FindInstance(theirZoneID, theirInstanceID);
|
||||
if (instance) {
|
||||
instance->AddPlayer(Player());
|
||||
@@ -702,7 +702,7 @@ void HandlePacket(Packet* packet) {
|
||||
inStream.Read(theirZoneID);
|
||||
inStream.Read(theirInstanceID);
|
||||
|
||||
auto instance =
|
||||
const auto& instance =
|
||||
Game::im->FindInstance(theirZoneID, theirInstanceID);
|
||||
if (instance) {
|
||||
instance->RemovePlayer(Player());
|
||||
@@ -728,7 +728,7 @@ void HandlePacket(Packet* packet) {
|
||||
inStream.Read<char>(character);
|
||||
password += character;
|
||||
}
|
||||
auto* newInst = Game::im->CreatePrivateInstance(mapId, cloneId, password.c_str());
|
||||
const auto& newInst = Game::im->CreatePrivateInstance(mapId, cloneId, password.c_str());
|
||||
LOG("Creating private zone %i/%i/%i with password %s", newInst->GetMapID(), newInst->GetCloneID(), newInst->GetInstanceID(), password.c_str());
|
||||
|
||||
break;
|
||||
@@ -754,9 +754,9 @@ void HandlePacket(Packet* packet) {
|
||||
password += character;
|
||||
}
|
||||
|
||||
auto* instance = Game::im->FindPrivateInstance(password.c_str());
|
||||
const auto& instance = Game::im->FindPrivateInstance(password.c_str());
|
||||
|
||||
LOG("Join private zone: %llu %d %s %p", requestID, mythranShift, password.c_str(), instance);
|
||||
LOG("Join private zone: %llu %d %s %p", requestID, mythranShift, password.c_str(), instance.get());
|
||||
|
||||
if (instance == nullptr) {
|
||||
return;
|
||||
@@ -781,7 +781,7 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
LOG("Got world ready %i %i", zoneID, instanceID);
|
||||
|
||||
auto* instance = Game::im->FindInstance(zoneID, instanceID);
|
||||
const auto& instance = Game::im->FindInstance(zoneID, instanceID);
|
||||
|
||||
if (instance == nullptr) {
|
||||
LOG("Failed to find zone to ready");
|
||||
@@ -819,7 +819,7 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
LOG("Got affirmation of transfer %llu", requestID);
|
||||
|
||||
auto* instance = Game::im->GetInstanceBySysAddr(packet->systemAddress);
|
||||
const auto& instance = Game::im->GetInstanceBySysAddr(packet->systemAddress);
|
||||
|
||||
if (instance == nullptr)
|
||||
return;
|
||||
@@ -832,7 +832,7 @@ void HandlePacket(Packet* packet) {
|
||||
case MessageType::Master::SHUTDOWN_RESPONSE: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
auto* instance = Game::im->GetInstanceBySysAddr(packet->systemAddress);
|
||||
const auto& instance = Game::im->GetInstanceBySysAddr(packet->systemAddress);
|
||||
LOG("Got shutdown response from %s", packet->systemAddress.ToString());
|
||||
if (instance == nullptr) {
|
||||
return;
|
||||
@@ -882,13 +882,13 @@ int ShutdownSequence(int32_t signal) {
|
||||
LOG("Saved ObjectIDTracker to DB");
|
||||
|
||||
// A server might not be finished spinning up yet, remove all of those here.
|
||||
for (auto* instance : Game::im->GetInstances()) {
|
||||
for (const auto& instance : Game::im->GetInstances()) {
|
||||
if (!instance) continue;
|
||||
|
||||
if (!instance->GetIsReady()) {
|
||||
Game::im->RemoveInstance(instance);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* instance : Game::im->GetInstances()) {
|
||||
|
||||
instance->SetIsShuttingDown(true);
|
||||
}
|
||||
|
||||
@@ -909,7 +909,7 @@ int ShutdownSequence(int32_t signal) {
|
||||
|
||||
allInstancesShutdown = true;
|
||||
|
||||
for (auto* instance : Game::im->GetInstances()) {
|
||||
for (const auto& instance : Game::im->GetInstances()) {
|
||||
if (instance == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void HTTPMonitorInfo::Serialize(RakNet::BitStream &bitStream) const {
|
||||
void HTTPMonitorInfo::Serialize(RakNet::BitStream& bitStream) const {
|
||||
bitStream.Write(port);
|
||||
bitStream.Write<uint8_t>(openWeb);
|
||||
bitStream.Write<uint8_t>(supportsSum);
|
||||
@@ -81,32 +81,29 @@ void WorldPackets::SendServerState(const SystemAddress& sysAddr) {
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm) {
|
||||
void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm, const LWOCLONEID cloneID) {
|
||||
using namespace std;
|
||||
RakNet::BitStream bitStream;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CREATE_CHARACTER);
|
||||
|
||||
RakNet::BitStream data;
|
||||
data.Write<uint32_t>(7); //LDF key count
|
||||
|
||||
std::unique_ptr<LDFData<LWOOBJID>> objid(new LDFData<LWOOBJID>(u"objid", player));
|
||||
std::unique_ptr<LDFData<LOT>> lot(new LDFData<LOT>(u"template", 1));
|
||||
std::unique_ptr<LDFData<std::string>> xmlConfigData(new LDFData<std::string>(u"xmlData", xmlData));
|
||||
std::unique_ptr<LDFData<std::u16string>> name(new LDFData<std::u16string>(u"name", username));
|
||||
std::unique_ptr<LDFData<int32_t>> gmlevel(new LDFData<int32_t>(u"gmlevel", static_cast<int32_t>(gm)));
|
||||
std::unique_ptr<LDFData<int32_t>> chatmode(new LDFData<int32_t>(u"chatmode", static_cast<int32_t>(gm)));
|
||||
std::unique_ptr<LDFData<int64_t>> reputationLdf(new LDFData<int64_t>(u"reputation", reputation));
|
||||
|
||||
objid->WriteToPacket(data);
|
||||
lot->WriteToPacket(data);
|
||||
name->WriteToPacket(data);
|
||||
gmlevel->WriteToPacket(data);
|
||||
chatmode->WriteToPacket(data);
|
||||
xmlConfigData->WriteToPacket(data);
|
||||
reputationLdf->WriteToPacket(data);
|
||||
std::vector<std::unique_ptr<LDFBaseData>> ldfData;
|
||||
ldfData.push_back(move(make_unique<LDFData<LWOOBJID>>(u"objid", player)));
|
||||
ldfData.push_back(move(make_unique<LDFData<LOT>>(u"template", 1)));
|
||||
ldfData.push_back(move(make_unique<LDFData<string>>(u"xmlData", xmlData)));
|
||||
ldfData.push_back(move(make_unique<LDFData<u16string>>(u"name", username)));
|
||||
ldfData.push_back(move(make_unique<LDFData<int32_t>>(u"gmlevel", static_cast<int32_t>(gm))));
|
||||
ldfData.push_back(move(make_unique<LDFData<int32_t>>(u"chatmode", static_cast<int32_t>(gm))));
|
||||
ldfData.push_back(move(make_unique<LDFData<int64_t>>(u"reputation", reputation)));
|
||||
ldfData.push_back(move(make_unique<LDFData<int32_t>>(u"propertycloneid", cloneID)));
|
||||
|
||||
data.Write<uint32_t>(ldfData.size());
|
||||
for (const auto& toSerialize : ldfData) toSerialize->WriteToPacket(data);
|
||||
|
||||
//Compress the data before sending:
|
||||
const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed());
|
||||
uint8_t* compressedData = new uint8_t[reservedSize];
|
||||
const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed());
|
||||
uint8_t* compressedData = new uint8_t[reservedSize];
|
||||
|
||||
// TODO There should be better handling here for not enough memory...
|
||||
if (!compressedData) return;
|
||||
@@ -177,7 +174,7 @@ void WorldPackets::SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPM
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void WorldPackets::SendDebugOuput(const SystemAddress& sysAddr, const std::string& data){
|
||||
void WorldPackets::SendDebugOuput(const SystemAddress& sysAddr, const std::string& data) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::DEBUG_OUTPUT);
|
||||
bitStream.Write<uint32_t>(data.size());
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace WorldPackets {
|
||||
void SendCharacterDeleteResponse(const SystemAddress& sysAddr, bool response);
|
||||
void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift);
|
||||
void SendServerState(const SystemAddress& sysAddr);
|
||||
void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm);
|
||||
void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm, const LWOCLONEID cloneID);
|
||||
void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::set<std::pair<uint8_t, uint8_t>> unacceptedItems);
|
||||
void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel);
|
||||
void SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info);
|
||||
|
||||
@@ -33,10 +33,10 @@ void TreasureChestDragonServer::OnUse(Entity* self, Entity* user) {
|
||||
|
||||
if (memberObject == nullptr) continue;
|
||||
|
||||
Loot::DropActivityLoot(memberObject, self, scriptedActivityComponent->GetActivityID(), rating);
|
||||
Loot::DropActivityLoot(memberObject, self->GetObjectID(), scriptedActivityComponent->GetActivityID(), rating);
|
||||
}
|
||||
} else {
|
||||
Loot::DropActivityLoot(user, self, scriptedActivityComponent->GetActivityID(), rating);
|
||||
Loot::DropActivityLoot(user, self->GetObjectID(), scriptedActivityComponent->GetActivityID(), rating);
|
||||
}
|
||||
|
||||
self->Smash(self->GetObjectID());
|
||||
|
||||
@@ -45,7 +45,7 @@ BootyDigServer::OnFireEventServerSide(Entity* self, Entity* sender, std::string
|
||||
if (renderComponent != nullptr)
|
||||
renderComponent->PlayEffect(7730, u"cast", "bootyshine");
|
||||
|
||||
Loot::DropLoot(player, self, 231, 75, 75);
|
||||
Loot::DropLoot(player, self->GetObjectID(), 231, 75, 75);
|
||||
}
|
||||
}
|
||||
} else if (args == "ChestDead") {
|
||||
|
||||
@@ -22,7 +22,7 @@ void BaseInteractDropLootServer::BaseUse(Entity* self, Entity* user) {
|
||||
|
||||
self->SetNetworkVar(u"bInUse", true);
|
||||
|
||||
Loot::DropLoot(user, self, lootMatrix, 0, 0);
|
||||
Loot::DropLoot(user, self->GetObjectID(), lootMatrix, 0, 0);
|
||||
|
||||
self->AddCallbackTimer(cooldownTime, [this, self]() {
|
||||
self->SetNetworkVar(u"bInUse", false);
|
||||
|
||||
@@ -13,7 +13,7 @@ void GrowingFlower::OnSkillEventFired(Entity* self, Entity* target, const std::s
|
||||
const auto mission1 = self->GetVar<int32_t>(u"missionID");
|
||||
const auto mission2 = self->GetVar<int32_t>(u"missionID2");
|
||||
|
||||
Loot::DropActivityLoot(target, self, self->GetLOT(), 0);
|
||||
Loot::DropActivityLoot(target, self->GetObjectID(), self->GetLOT(), 0);
|
||||
|
||||
auto* missionComponent = target->GetComponent<MissionComponent>();
|
||||
if (missionComponent != nullptr) {
|
||||
|
||||
@@ -23,7 +23,7 @@ void WishingWellServer::OnUse(Entity* self, Entity* user) {
|
||||
|
||||
Loot::DropActivityLoot(
|
||||
user,
|
||||
self,
|
||||
self->GetObjectID(),
|
||||
static_cast<uint32_t>(scriptedActivity->GetActivityID()),
|
||||
GeneralUtils::GenerateRandomNumber<int32_t>(1, 1000)
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "eTerminateType.h"
|
||||
|
||||
void VeMissionConsole::OnUse(Entity* self, Entity* user) {
|
||||
Loot::DropActivityLoot(user, self, 12551);
|
||||
Loot::DropActivityLoot(user, self->GetObjectID(), 12551);
|
||||
|
||||
auto* inventoryComponent = user->GetComponent<InventoryComponent>();
|
||||
if (inventoryComponent != nullptr) {
|
||||
|
||||
@@ -14,6 +14,6 @@ void NjDragonEmblemChestServer::OnUse(Entity* self, Entity* user) {
|
||||
|
||||
auto* destroyable = self->GetComponent<DestroyableComponent>();
|
||||
if (destroyable != nullptr) {
|
||||
Loot::DropLoot(user, self, destroyable->GetLootMatrixID(), 0, 0);
|
||||
Loot::DropLoot(user, self->GetObjectID(), destroyable->GetLootMatrixID(), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ void MinigameTreasureChestServer::OnUse(Entity* self, Entity* user) {
|
||||
|
||||
if (self->GetLOT() == frakjawChestId) activityRating = team->members.size();
|
||||
|
||||
Loot::DropActivityLoot(teamMember, self, sac->GetActivityID(), activityRating);
|
||||
Loot::DropActivityLoot(teamMember, self->GetObjectID(), sac->GetActivityID(), activityRating);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -35,7 +35,7 @@ void MinigameTreasureChestServer::OnUse(Entity* self, Entity* user) {
|
||||
|
||||
if (self->GetLOT() == frakjawChestId) activityRating = 1;
|
||||
|
||||
Loot::DropActivityLoot(user, self, sac->GetActivityID(), activityRating);
|
||||
Loot::DropActivityLoot(user, self->GetObjectID(), sac->GetActivityID(), activityRating);
|
||||
}
|
||||
|
||||
sac->PlayerRemove(user->GetObjectID());
|
||||
|
||||
@@ -72,7 +72,7 @@ void ActivityManager::StopActivity(Entity* self, const LWOOBJID playerID, const
|
||||
SetActivityValue(self, playerID, 1, value1);
|
||||
SetActivityValue(self, playerID, 2, value2);
|
||||
|
||||
Loot::GiveActivityLoot(player, self, gameID, CalculateActivityRating(self, playerID));
|
||||
Loot::GiveActivityLoot(player, self->GetObjectID(), gameID, CalculateActivityRating(self, playerID));
|
||||
|
||||
if (sac != nullptr) {
|
||||
sac->PlayerRemove(player->GetObjectID());
|
||||
|
||||
@@ -29,7 +29,7 @@ void ScriptedPowerupSpawner::OnTimerDone(Entity* self, std::string message) {
|
||||
renderComponent->PlayEffect(0, u"cast", "N_cast");
|
||||
}
|
||||
|
||||
Loot::DropLoot(owner, self, drops, 0, 0);
|
||||
Loot::DropLoot(owner, self->GetObjectID(), drops, 0, 0);
|
||||
}
|
||||
|
||||
// Increment the current cycle
|
||||
|
||||
@@ -11,7 +11,7 @@ void AgPicnicBlanket::OnUse(Entity* self, Entity* user) {
|
||||
self->SetVar<bool>(u"active", true);
|
||||
|
||||
auto lootTable = std::unordered_map<LOT, int32_t>{ {935, 3} };
|
||||
Loot::DropLoot(user, self, lootTable, 0, 0);
|
||||
Loot::DropLoot(user, self->GetObjectID(), lootTable, 0, 0);
|
||||
|
||||
self->AddCallbackTimer(5.0f, [self]() {
|
||||
self->SetVar<bool>(u"active", false);
|
||||
|
||||
@@ -572,7 +572,7 @@ void SGCannon::StopGame(Entity* self, bool cancel) {
|
||||
missionComponent->Progress(eMissionTaskType::ACTIVITY, m_CannonLot, 0, "", self->GetVar<int32_t>(TotalScoreVariable));
|
||||
}
|
||||
|
||||
Loot::GiveActivityLoot(player, self, GetGameID(self), self->GetVar<int32_t>(TotalScoreVariable));
|
||||
Loot::GiveActivityLoot(player, self->GetObjectID(), GetGameID(self), self->GetVar<int32_t>(TotalScoreVariable));
|
||||
|
||||
SaveScore(self, player->GetObjectID(),
|
||||
static_cast<float>(self->GetVar<int32_t>(TotalScoreVariable)), static_cast<float>(self->GetVar<uint32_t>(MaxStreakVariable)), percentage);
|
||||
|
||||
@@ -1040,7 +1040,7 @@ void HandlePacket(Packet* packet) {
|
||||
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
||||
if (!characterComponent) return;
|
||||
|
||||
WorldPackets::SendCreateCharacter(packet->systemAddress, player->GetComponent<CharacterComponent>()->GetReputation(), player->GetObjectID(), c->GetXMLData(), username, c->GetGMLevel());
|
||||
WorldPackets::SendCreateCharacter(packet->systemAddress, player->GetComponent<CharacterComponent>()->GetReputation(), player->GetObjectID(), c->GetXMLData(), username, c->GetGMLevel(), c->GetPropertyCloneID());
|
||||
WorldPackets::SendServerState(packet->systemAddress);
|
||||
|
||||
const auto respawnPoint = player->GetCharacter()->GetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID());
|
||||
|
||||
@@ -114,21 +114,23 @@ These commands are primarily for development and testing. The usage of many of t
|
||||
|addfaction|`/addfaction <faction id>`|Add the faction to the users list of factions|8|
|
||||
|getfactions|`/getfactions`|Shows the player's factions|8|
|
||||
|setrewardcode|`/setrewardcode <code>`|Sets the rewardcode for the account you are logged into if it's a valid rewardcode, See cdclient table `RewardCodes`|8|
|
||||
|barfight|`/barfight start`|Starts a barfight (turns everyones pvp on)|8|
|
||||
|despawn|`/despawn <objectID>`|Despawns the entity objectID IF it was spawned in through a slash command.|8|
|
||||
|crash|`/crash`|Crashes the server.|9|
|
||||
|rollloot|`/rollloot <loot matrix index> <item id> <amount>`|Given a `loot matrix index`, look for `item id` in that matrix `amount` times and print to the chat box statistics of rolling that loot matrix.|9|
|
||||
|castskill|`/castskill <skill id>`|Casts the skill as the player|9|
|
||||
|
||||
## Detailed `/inspect` Usage
|
||||
|
||||
`/inspect <component> (-m <waypoint> | -a <animation> | -s | -p | -f (faction) | -t)`
|
||||
`/inspect <component> (-m <waypoint> | -a <animation> | -p | -f (faction) | -t)`
|
||||
|
||||
Finds the closest entity with the given component or LDF variable (ignoring players and racing cars), printing its ID, distance from the player, and whether it is sleeping, as well as the the IDs of all components the entity has.
|
||||
This info is then shown in a window on the client which the user can navigate
|
||||
|
||||
### `/inspect` Options
|
||||
|
||||
* `-m`: If the entity has a moving platform component, sends it to the given waypoint, or stops the platform if `waypoint` is `-1`.
|
||||
* `-a`: Plays the given animation on the entity.
|
||||
* `-s`: Prints the entity's settings and spawner ID.
|
||||
* `-p`: Prints the entity's position
|
||||
* `-f`: If the entity has a destroyable component, prints whether the entity is smashable and its friendly and enemy faction IDs; if `faction` is specified, adds that faction to the entity.
|
||||
* `-cf`: check if the entity is enemy or friend
|
||||
|
||||
1
migrations/dlu/mysql/22_brickbuilds_not_on_grid.sql
Normal file
1
migrations/dlu/mysql/22_brickbuilds_not_on_grid.sql
Normal file
@@ -0,0 +1 @@
|
||||
/* See ModelNormalizeMigration.cpp for details */
|
||||
1
migrations/dlu/sqlite/5_brickbuilds_not_on_grid.sql
Normal file
1
migrations/dlu/sqlite/5_brickbuilds_not_on_grid.sql
Normal file
@@ -0,0 +1 @@
|
||||
/* See ModelNormalizeMigration.cpp for details */
|
||||
Reference in New Issue
Block a user