mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-08-06 23:31:25 +00:00
fix: security vulnerabilities (#1980)
* fix: security vulnerabilities Tested that all functions related to the touched files work will test sqlite on a CI build * fix failing test * ai feedback * add buffer size checking * use c_str * dont log session key * Try this for a mac definition * be quiet apple
This commit is contained in:
@@ -112,6 +112,7 @@ public:
|
||||
|
||||
uint16_t GetNetworkId() const;
|
||||
|
||||
// Cannot return nullptr.
|
||||
Entity* GetOwner() const;
|
||||
|
||||
const NiPoint3& GetDefaultPosition() const;
|
||||
|
||||
@@ -30,6 +30,21 @@ void AirMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream& bitS
|
||||
return;
|
||||
}
|
||||
|
||||
// So a player can't send an arbitrary behaviorID in a modified client and cast any behavior on any air behavior
|
||||
Behavior* toSync = nullptr;
|
||||
if (m_GroundAction->GetBehaviorID() == behaviorId) {
|
||||
toSync = m_GroundAction;
|
||||
} else if (m_HitAction->GetBehaviorID() == behaviorId) {
|
||||
toSync = m_HitAction;
|
||||
} else if (m_HitActionEnemy->GetBehaviorID() == behaviorId) {
|
||||
toSync = m_HitActionEnemy;
|
||||
} else if (m_TimeoutAction->GetBehaviorID() == behaviorId) {
|
||||
toSync = m_TimeoutAction;
|
||||
} else {
|
||||
LOG("Invalid Air Movement Behavior sync for behaviorID %i on behavior %i", behaviorId, m_behaviorId);
|
||||
return;
|
||||
}
|
||||
|
||||
LWOOBJID target{};
|
||||
|
||||
if (!bitStream.Read(target)) {
|
||||
@@ -37,15 +52,17 @@ void AirMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream& bitS
|
||||
return;
|
||||
}
|
||||
|
||||
auto* behavior = CreateBehavior(behaviorId);
|
||||
|
||||
if (Game::entityManager->GetEntity(target) != nullptr) {
|
||||
branch.target = target;
|
||||
}
|
||||
|
||||
behavior->Handle(context, bitStream, branch);
|
||||
toSync->Handle(context, bitStream, branch);
|
||||
}
|
||||
|
||||
void AirMovementBehavior::Load() {
|
||||
this->m_Timeout = (GetFloat("timeout_ms") / 1000.0f);
|
||||
m_Timeout = (GetFloat("timeout_ms") / 1000.0f);
|
||||
m_GroundAction = GetAction("ground_action");
|
||||
m_HitAction = GetAction("hit_action");
|
||||
m_HitActionEnemy = GetAction("hit_action_enemy");
|
||||
m_TimeoutAction = GetAction("timeout_action");
|
||||
}
|
||||
|
||||
@@ -15,4 +15,9 @@ public:
|
||||
void Load() override;
|
||||
private:
|
||||
float m_Timeout;
|
||||
|
||||
Behavior* m_GroundAction{};
|
||||
Behavior* m_HitAction{};
|
||||
Behavior* m_HitActionEnemy{};
|
||||
Behavior* m_TimeoutAction{};
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream& b
|
||||
LWOOBJID target{};
|
||||
if (!bitStream.Read(target)) {
|
||||
LOG("failed to read in target %i from bitStream, aborting target Handle!", i);
|
||||
continue;
|
||||
};
|
||||
targets.push_back(target);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
||||
}
|
||||
|
||||
if (isBlocked) {
|
||||
destroyableComponent->SetAttacksToBlock(std::min(destroyableComponent->GetAttacksToBlock() - 1, 0U));
|
||||
destroyableComponent->SetAttacksToBlock(std::max<int32_t>(static_cast<int32_t>(destroyableComponent->GetAttacksToBlock() - 1), 0));
|
||||
Game::entityManager->SerializeEntity(targetEntity);
|
||||
this->m_OnFailBlocked->Handle(context, bitStream, branch);
|
||||
return;
|
||||
@@ -103,9 +103,10 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t totalDamageDealt = armorDamageDealt + healthDamageDealt;
|
||||
uint64_t totalDamageDealt = armorDamageDealt + healthDamageDealt;
|
||||
|
||||
// A value that's too large may be a cheating attempt, so we set it to MIN
|
||||
// Can't overflow here either because should we somehow get to a 64 bit number it'll be clamped to a sane value.
|
||||
if (totalDamageDealt > this->m_MaxDamage) {
|
||||
totalDamageDealt = this->m_MinDamage;
|
||||
}
|
||||
|
||||
@@ -48,15 +48,13 @@ void BlockBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branc
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
auto* const destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
destroyableComponent->SetAttacksToBlock(this->m_numAttacksCanBlock);
|
||||
|
||||
if (destroyableComponent == nullptr) {
|
||||
return;
|
||||
if (destroyableComponent) {
|
||||
// ??? what is going on here?
|
||||
destroyableComponent->SetAttacksToBlock(this->m_numAttacksCanBlock);
|
||||
destroyableComponent->SetAttacksToBlock(0);
|
||||
}
|
||||
|
||||
destroyableComponent->SetAttacksToBlock(0);
|
||||
}
|
||||
|
||||
void BlockBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) {
|
||||
|
||||
@@ -11,6 +11,11 @@ void ChainBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStrea
|
||||
return;
|
||||
}
|
||||
|
||||
if (chainIndex == 0) {
|
||||
LOG("Received invalid chain index of 0 for behavior %i.", m_behaviorId);
|
||||
return;
|
||||
}
|
||||
|
||||
chainIndex--;
|
||||
|
||||
if (chainIndex < this->m_behaviors.size()) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
void JetPackBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bit_stream, const BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
if (!entity) return;
|
||||
|
||||
GameMessages::SendSetJetPackMode(entity, true, this->m_BypassChecks, this->m_EnableHover, this->m_effectId, this->m_Airspeed, this->m_MaxAirspeed, this->m_VerticalVelocity, this->m_WarningEffectID);
|
||||
|
||||
@@ -21,6 +22,7 @@ void JetPackBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bit_st
|
||||
|
||||
void JetPackBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||
auto* entity = Game::entityManager->GetEntity(branch.target);
|
||||
if (!entity) return;
|
||||
|
||||
GameMessages::SendSetJetPackMode(entity, false);
|
||||
|
||||
|
||||
@@ -17,6 +17,11 @@ void SwitchMultipleBehavior::Handle(BehaviorContext* context, RakNet::BitStream&
|
||||
return;
|
||||
};
|
||||
|
||||
if (m_behaviors.empty()) {
|
||||
LOG("No behaviors were loaded for %i, aborting call.", m_behaviorId);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t trigger = 0;
|
||||
|
||||
for (unsigned int i = 0; i < this->m_behaviors.size(); i++) {
|
||||
|
||||
@@ -478,6 +478,7 @@ std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const {
|
||||
|
||||
for (auto id : m_Parent->GetTargetsInPhantom()) {
|
||||
auto* other = Game::entityManager->GetEntity(id);
|
||||
if (!other) continue;
|
||||
|
||||
const auto distance = Vector3::DistanceSquared(m_Parent->GetPosition(), other->GetPosition());
|
||||
|
||||
|
||||
@@ -450,19 +450,10 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
|
||||
param.value = result.getFloatField("NumberValue");
|
||||
param.effectId = result.getIntField("EffectID");
|
||||
|
||||
if (!result.fieldIsNull("StringValue")) {
|
||||
std::istringstream stream(result.getStringField("StringValue"));
|
||||
std::string token;
|
||||
|
||||
while (std::getline(stream, token, ',')) {
|
||||
try {
|
||||
const auto value = std::stof(token);
|
||||
|
||||
param.values.push_back(value);
|
||||
} catch (std::invalid_argument& exception) {
|
||||
LOG("Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
|
||||
}
|
||||
}
|
||||
for (const auto& str : GeneralUtils::SplitString(result.getStringField("StringValue"), ',')) {
|
||||
if (str.empty()) continue;
|
||||
const auto value = GeneralUtils::TryParse<float>(str);
|
||||
if (value) param.values.push_back(value.value());
|
||||
}
|
||||
|
||||
parameters.push_back(param);
|
||||
|
||||
@@ -797,8 +797,14 @@ std::string CharacterComponent::StatisticsToString() const {
|
||||
return result.str();
|
||||
}
|
||||
|
||||
uint64_t CharacterComponent::GetStatisticFromSplit(std::vector<std::string> split, uint32_t index) {
|
||||
return split.size() > index ? std::stoull(split.at(index)) : 0;
|
||||
uint64_t CharacterComponent::GetStatisticFromSplit(const std::vector<std::string>& split, const uint32_t index) {
|
||||
uint64_t toReturn = 0;
|
||||
if (index < split.size()) {
|
||||
const auto parsed = GeneralUtils::TryParse<uint64_t>(split[index]);
|
||||
if (parsed) toReturn = *parsed;
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
ZoneStatistics& CharacterComponent::GetZoneStatisticsForMap(LWOMAPID mapID) {
|
||||
|
||||
@@ -450,7 +450,7 @@ private:
|
||||
* @param index the statistics ID in the string
|
||||
* @return the integer value of this statistic, parsed from the string
|
||||
*/
|
||||
static uint64_t GetStatisticFromSplit(std::vector<std::string> split, uint32_t index);
|
||||
static uint64_t GetStatisticFromSplit(const std::vector<std::string>& split, const uint32_t index);
|
||||
|
||||
/**
|
||||
* Gets all the statistics for a certain map, if it doesn't exist, it creates empty stats
|
||||
@@ -526,6 +526,7 @@ private:
|
||||
|
||||
/**
|
||||
* Total amount of meters traveled by this character
|
||||
* Should be a double and then truncated so decimals can be tracked
|
||||
*/
|
||||
uint64_t m_MetersTraveled;
|
||||
|
||||
|
||||
@@ -793,7 +793,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
||||
for (Entity* scriptEntity : scriptedActs) {
|
||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
if (!zoneControl || scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
scriptEntity->GetScript()->OnPlayerDied(scriptEntity, m_Parent);
|
||||
}
|
||||
}
|
||||
@@ -964,6 +964,8 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
if (m_Parent->IsPlayer()) {
|
||||
//remove hardcore_lose_uscore_on_death_percent from the player's uscore:
|
||||
auto* character = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (!character) return;
|
||||
|
||||
auto uscore = character->GetUScore();
|
||||
|
||||
auto uscoreToLose = static_cast<uint64_t>(uscore * (Game::entityManager->GetHardcoreLoseUscoreOnDeathPercent() / 100.0f));
|
||||
|
||||
@@ -966,8 +966,9 @@ void InventoryComponent::EquipScripts(Item* equippedItem) {
|
||||
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
|
||||
if (!itemScript) {
|
||||
LOG("null script?");
|
||||
} else {
|
||||
itemScript->OnFactionTriggerItemEquipped(m_Parent, equippedItem->GetId());
|
||||
}
|
||||
itemScript->OnFactionTriggerItemEquipped(m_Parent, equippedItem->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,8 +982,9 @@ void InventoryComponent::UnequipScripts(Item* unequippedItem) {
|
||||
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
|
||||
if (!itemScript) {
|
||||
LOG("null script?");
|
||||
} else {
|
||||
itemScript->OnFactionTriggerItemUnequipped(m_Parent, unequippedItem->GetId());
|
||||
}
|
||||
itemScript->OnFactionTriggerItemUnequipped(m_Parent, unequippedItem->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1633,7 +1635,7 @@ void InventoryComponent::LoadPetXml(const tinyxml2::XMLDocument& document) {
|
||||
DatabasePet databasePet;
|
||||
databasePet.lot = lot;
|
||||
databasePet.moderationState = moderationStatus;
|
||||
databasePet.name = std::string(name);
|
||||
databasePet.name = name ? name : "";
|
||||
|
||||
SetDatabasePet(id, databasePet);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
void NextLUPExhibit();
|
||||
private:
|
||||
float m_UpdateTimer = 0.0f;
|
||||
std::array<LOT, 4> m_LUPExhibits = { 11121, 11295, 11423, 11979 };
|
||||
const std::array<LOT, 4> m_LUPExhibits = { 11121, 11295, 11423, 11979 };
|
||||
uint8_t m_LUPExhibitIndex = 0;
|
||||
bool m_DirtyLUPExhibit = true;
|
||||
};
|
||||
|
||||
@@ -449,45 +449,48 @@ void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
|
||||
|
||||
if (mis == nullptr) return;
|
||||
|
||||
auto* cur = mis->FirstChildElement("cur");
|
||||
auto* done = mis->FirstChildElement("done");
|
||||
|
||||
auto* doneM = done->FirstChildElement();
|
||||
|
||||
while (doneM) {
|
||||
int missionId;
|
||||
|
||||
doneM->QueryAttribute("id", &missionId);
|
||||
|
||||
auto* mission = new Mission(this, missionId);
|
||||
|
||||
mission->LoadFromXmlDone(*doneM);
|
||||
|
||||
doneM = doneM->NextSiblingElement();
|
||||
|
||||
m_Missions.insert_or_assign(missionId, mission);
|
||||
}
|
||||
|
||||
auto* currentM = cur->FirstChildElement();
|
||||
|
||||
uint32_t missionOrder{};
|
||||
while (currentM) {
|
||||
int missionId;
|
||||
|
||||
currentM->QueryAttribute("id", &missionId);
|
||||
|
||||
auto* mission = m_Missions.contains(missionId) ? m_Missions[missionId] : new Mission(this, missionId);
|
||||
|
||||
mission->LoadFromXmlCur(*currentM);
|
||||
|
||||
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
|
||||
mission->SetUniqueMissionOrderID(missionOrder);
|
||||
if (missionOrder > m_LastUsedMissionOrderUID) m_LastUsedMissionOrderUID = missionOrder;
|
||||
if (done) {
|
||||
auto* doneM = done->FirstChildElement();
|
||||
|
||||
while (doneM) {
|
||||
int missionId;
|
||||
|
||||
doneM->QueryAttribute("id", &missionId);
|
||||
|
||||
auto* mission = new Mission(this, missionId);
|
||||
|
||||
mission->LoadFromXmlDone(*doneM);
|
||||
|
||||
doneM = doneM->NextSiblingElement();
|
||||
|
||||
m_Missions.insert_or_assign(missionId, mission);
|
||||
}
|
||||
}
|
||||
|
||||
auto* cur = mis->FirstChildElement("cur");
|
||||
if (cur) {
|
||||
auto* currentM = cur->FirstChildElement();
|
||||
|
||||
currentM = currentM->NextSiblingElement();
|
||||
uint32_t missionOrder{};
|
||||
while (currentM) {
|
||||
int missionId;
|
||||
|
||||
m_Missions.insert_or_assign(missionId, mission);
|
||||
currentM->QueryAttribute("id", &missionId);
|
||||
|
||||
auto* mission = m_Missions.contains(missionId) ? m_Missions[missionId] : new Mission(this, missionId);
|
||||
|
||||
mission->LoadFromXmlCur(*currentM);
|
||||
|
||||
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
|
||||
mission->SetUniqueMissionOrderID(missionOrder);
|
||||
if (missionOrder > m_LastUsedMissionOrderUID) m_LastUsedMissionOrderUID = missionOrder;
|
||||
}
|
||||
|
||||
currentM = currentM->NextSiblingElement();
|
||||
|
||||
m_Missions.insert_or_assign(missionId, mission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,10 @@ MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent, const int
|
||||
MultiZoneEntranceComponent::~MultiZoneEntranceComponent() {}
|
||||
|
||||
void MultiZoneEntranceComponent::OnUse(Entity* originator) {
|
||||
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
|
||||
auto* const characterComponent = originator->GetComponent<CharacterComponent>();
|
||||
if (!characterComponent) return;
|
||||
|
||||
auto* rocket = characterComponent->RocketEquip(originator);
|
||||
if (!rocket) return;
|
||||
|
||||
// the LUP world menu is just the property menu, the client knows how to handle it
|
||||
@@ -26,7 +29,7 @@ void MultiZoneEntranceComponent::OnUse(Entity* originator) {
|
||||
|
||||
void MultiZoneEntranceComponent::OnSelectWorld(Entity* originator, uint32_t index) {
|
||||
auto* rocketLaunchpadControlComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
|
||||
if (!rocketLaunchpadControlComponent) return;
|
||||
if (!rocketLaunchpadControlComponent || index >= m_LUPWorlds.size()) return;
|
||||
|
||||
rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0);
|
||||
}
|
||||
|
||||
@@ -922,7 +922,9 @@ void PetComponent::Deactivate() {
|
||||
}
|
||||
|
||||
void PetComponent::Release() {
|
||||
auto* inventoryComponent = GetOwner()->GetComponent<InventoryComponent>();
|
||||
auto* const owner = GetOwner();
|
||||
if (!owner) return;
|
||||
auto* const inventoryComponent = owner->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
return;
|
||||
@@ -932,9 +934,9 @@ void PetComponent::Release() {
|
||||
|
||||
inventoryComponent->RemoveDatabasePet(m_DatabaseId);
|
||||
|
||||
auto* item = inventoryComponent->FindItemBySubKey(m_DatabaseId);
|
||||
auto* const item = inventoryComponent->FindItemBySubKey(m_DatabaseId);
|
||||
|
||||
item->SetCount(0, false, false);
|
||||
if (item) item->SetCount(0, false, false);
|
||||
}
|
||||
|
||||
void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, const int32_t commandType, const int32_t typeId, const bool overrideObey) {
|
||||
|
||||
@@ -58,31 +58,16 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent, const int32_t c
|
||||
}
|
||||
|
||||
if (m_IsRespawnVolume) {
|
||||
{
|
||||
auto respawnString = std::stringstream(m_Parent->GetVarAsString(u"rspPos"));
|
||||
const auto respawnPos = GeneralUtils::SplitString(m_Parent->GetVarAsString(u"rspPos"), '\x1f');
|
||||
m_RespawnPos = GeneralUtils::TryParse(respawnPos, NiPoint3Constant::ZERO);
|
||||
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(respawnString, segment, '\x1f')) {
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
|
||||
m_RespawnPos = NiPoint3(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]));
|
||||
}
|
||||
|
||||
{
|
||||
auto respawnString = std::stringstream(m_Parent->GetVarAsString(u"rspRot"));
|
||||
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(respawnString, segment, '\x1f')) {
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
|
||||
m_RespawnRot = NiQuaternion(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]), std::stof(seglist[3]));
|
||||
}
|
||||
const auto respawnRot = GeneralUtils::SplitString(m_Parent->GetVarAsString(u"rspRot"), '\x1f');
|
||||
m_RespawnRot = respawnRot.size() >= 4 ? NiQuaternion(
|
||||
GeneralUtils::TryParse(respawnRot[0], 1.0f),
|
||||
GeneralUtils::TryParse(respawnRot[1], 0.0f),
|
||||
GeneralUtils::TryParse(respawnRot[2], 0.0f),
|
||||
GeneralUtils::TryParse(respawnRot[3], 0.0f))
|
||||
: QuatUtils::IDENTITY;
|
||||
}
|
||||
|
||||
// HF - RespawnPoints. Legacy respawn entity.
|
||||
|
||||
@@ -131,7 +131,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
||||
|
||||
const auto lookupResult = Database::Get()->GetProperties(propertyLookup);
|
||||
|
||||
for (const auto& propertyEntry : lookupResult->entries) {
|
||||
for (const auto& propertyEntry : lookupResult.entries) {
|
||||
const auto owner = propertyEntry.ownerId;
|
||||
const auto otherCharacter = Database::Get()->GetCharacterInfo(owner);
|
||||
if (!otherCharacter.has_value()) {
|
||||
@@ -174,5 +174,5 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
||||
}
|
||||
|
||||
// Query here is to figure out whether or not to display the button to go to the next page or not.
|
||||
GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, lookupResult->totalEntriesMatchingQuery - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr);
|
||||
GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, lookupResult.totalEntriesMatchingQuery - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr);
|
||||
}
|
||||
|
||||
@@ -107,20 +107,12 @@ std::vector<NiPoint3> PropertyManagementComponent::GetPaths() const {
|
||||
|
||||
std::vector<float> points;
|
||||
|
||||
std::istringstream stream(result.getStringField("path"));
|
||||
std::string token;
|
||||
|
||||
while (std::getline(stream, token, ' ')) {
|
||||
try {
|
||||
auto value = std::stof(token);
|
||||
|
||||
points.push_back(value);
|
||||
} catch (std::invalid_argument& exception) {
|
||||
LOG("Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
|
||||
}
|
||||
for (const auto& str : GeneralUtils::SplitString(result.getStringField("path"), ' ')) {
|
||||
const auto value = GeneralUtils::TryParse<float>(str);
|
||||
if (value) points.push_back(value.value());
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < points.size(); i += 3) {
|
||||
for (auto i = 0u; i + 2 < points.size(); i += 3) {
|
||||
paths.emplace_back(points[i], points[i + 1], points[i + 2]);
|
||||
}
|
||||
|
||||
@@ -780,15 +772,17 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const
|
||||
privacy = static_cast<char>(this->privacyOption);
|
||||
if (moderatorRequested) {
|
||||
auto moderationInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId);
|
||||
if (moderationInfo->rejectionReason != "") {
|
||||
moderatorRequested = false;
|
||||
rejectionReason = moderationInfo->rejectionReason;
|
||||
} else if (moderationInfo->rejectionReason == "" && moderationInfo->modApproved == 1) {
|
||||
moderatorRequested = false;
|
||||
rejectionReason = "";
|
||||
} else {
|
||||
moderatorRequested = true;
|
||||
rejectionReason = "";
|
||||
if (moderationInfo) {
|
||||
if (moderationInfo->rejectionReason != "") {
|
||||
moderatorRequested = false;
|
||||
rejectionReason = moderationInfo->rejectionReason;
|
||||
} else if (moderationInfo->rejectionReason == "" && moderationInfo->modApproved == 1) {
|
||||
moderatorRequested = false;
|
||||
rejectionReason = "";
|
||||
} else {
|
||||
moderatorRequested = true;
|
||||
rejectionReason = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +380,7 @@ void QuickBuildComponent::StartQuickBuild(Entity* const user) {
|
||||
m_Builder = user->GetObjectID();
|
||||
|
||||
auto* character = user->GetComponent<CharacterComponent>();
|
||||
character->SetCurrentActivity(eGameActivity::QUICKBUILDING);
|
||||
if (character) character->SetCurrentActivity(eGameActivity::QUICKBUILDING);
|
||||
|
||||
Game::entityManager->SerializeEntity(user);
|
||||
|
||||
|
||||
@@ -306,7 +306,7 @@ void RacingControlComponent::OnRequestDie(Entity* player, const std::u16string&
|
||||
eKillType::VIOLENT, deathType, 0, 0, 90.0f, false, true, 0);
|
||||
|
||||
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
||||
uint32_t respawnImagination = 0;
|
||||
int32_t respawnImagination = 0;
|
||||
// Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live.
|
||||
// Do not actually change the value yet. Do that on respawn.
|
||||
if (destroyableComponent) {
|
||||
|
||||
@@ -385,26 +385,30 @@ void TriggerComponent::HandleSetPhysicsVolumeEffect(Entity* targetEntity, std::v
|
||||
}
|
||||
phantomPhysicsComponent->SetPhysicsEffectActive(true);
|
||||
ePhysicsEffectType effectType = ePhysicsEffectType::PUSH;
|
||||
std::transform(argArray.at(0).begin(), argArray.at(0).end(), argArray.at(0).begin(), ::tolower); //Transform to lowercase
|
||||
if (argArray.at(0) == "push") effectType = ePhysicsEffectType::PUSH;
|
||||
else if (argArray.at(0) == "attract") effectType = ePhysicsEffectType::ATTRACT;
|
||||
else if (argArray.at(0) == "repulse") effectType = ePhysicsEffectType::REPULSE;
|
||||
else if (argArray.at(0) == "gravity") effectType = ePhysicsEffectType::GRAVITY_SCALE;
|
||||
else if (argArray.at(0) == "friction") effectType = ePhysicsEffectType::FRICTION;
|
||||
if (!argArray.empty()) {
|
||||
std::transform(argArray.at(0).begin(), argArray.at(0).end(), argArray.at(0).begin(), ::tolower); //Transform to lowercase
|
||||
if (argArray.at(0) == "push") effectType = ePhysicsEffectType::PUSH;
|
||||
else if (argArray.at(0) == "attract") effectType = ePhysicsEffectType::ATTRACT;
|
||||
else if (argArray.at(0) == "repulse") effectType = ePhysicsEffectType::REPULSE;
|
||||
else if (argArray.at(0) == "gravity") effectType = ePhysicsEffectType::GRAVITY_SCALE;
|
||||
else if (argArray.at(0) == "friction") effectType = ePhysicsEffectType::FRICTION;
|
||||
}
|
||||
|
||||
phantomPhysicsComponent->SetEffectType(effectType);
|
||||
phantomPhysicsComponent->SetDirectionalMultiplier(std::stof(argArray.at(1)));
|
||||
if (argArray.size() > 1) {
|
||||
phantomPhysicsComponent->SetDirectionalMultiplier(GeneralUtils::TryParse(argArray.at(1), 0.0f));
|
||||
}
|
||||
if (argArray.size() > 4) {
|
||||
const NiPoint3 direction =
|
||||
GeneralUtils::TryParse<NiPoint3>(argArray.at(2), argArray.at(3), argArray.at(4)).value_or(NiPoint3Constant::ZERO);
|
||||
GeneralUtils::TryParse(argArray.at(2), argArray.at(3), argArray.at(4), NiPoint3Constant::ZERO);
|
||||
|
||||
phantomPhysicsComponent->SetDirection(direction);
|
||||
}
|
||||
if (argArray.size() > 5) {
|
||||
const uint32_t min = GeneralUtils::TryParse<uint32_t>(argArray.at(6)).value_or(0);
|
||||
if (argArray.size() > 6) {
|
||||
const uint32_t min = GeneralUtils::TryParse(argArray.at(6), 0);
|
||||
phantomPhysicsComponent->SetMin(min);
|
||||
|
||||
const uint32_t max = GeneralUtils::TryParse<uint32_t>(argArray.at(7)).value_or(0);
|
||||
const uint32_t max = GeneralUtils::TryParse(argArray.at(7), 0);
|
||||
phantomPhysicsComponent->SetMax(max);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
||||
|
||||
User* usr = UserManager::Instance()->GetUser(sysAddr);
|
||||
|
||||
if (!usr) {
|
||||
LOG("Failed to find a logged in user for (%llu), aborting GM: %4i, %s!", objectID, messageID, StringifiedEnum::ToString(messageID).data());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity) {
|
||||
LOG("Failed to find associated entity (%llu), aborting GM: %4i, %s!", objectID, messageID, StringifiedEnum::ToString(messageID).data());
|
||||
return;
|
||||
@@ -76,7 +81,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
||||
if (msg->requiredGmLevel > eGameMasterLevel::CIVILIAN) {
|
||||
auto* usingEntity = Game::entityManager->GetEntity(usr->GetLoggedInChar());
|
||||
if (!usingEntity || usingEntity->GetGMLevel() < msg->requiredGmLevel) {
|
||||
LOG("User %s (%llu) does not have the required GM level to execute this command.", usingEntity->GetCharacter()->GetName().c_str(), usingEntity->GetObjectID());
|
||||
if (usingEntity) LOG("User %s (%llu) does not have the required GM level to execute this command.", usingEntity->GetCharacter()->GetName().c_str(), usingEntity->GetObjectID());
|
||||
else LOG("ObjectID %llu tried to use a gm required message.", usr->GetLoggedInChar());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -167,8 +173,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
||||
|
||||
GameMessages::SendRestoreToPostLoadStats(entity, sysAddr);
|
||||
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
destroyable->SetImagination(destroyable->GetImagination());
|
||||
auto* const destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
if (destroyable) destroyable->SetImagination(destroyable->GetImagination());
|
||||
Game::entityManager->SerializeEntity(entity);
|
||||
|
||||
std::vector<Entity*> racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
|
||||
@@ -186,7 +192,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
||||
|
||||
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT);
|
||||
for (Entity* scriptEntity : scriptedActs) {
|
||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
if (!zoneControl || scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
scriptEntity->GetScript()->OnPlayerLoaded(scriptEntity, entity);
|
||||
}
|
||||
}
|
||||
@@ -332,9 +338,9 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
||||
if (behaviorId > 0) {
|
||||
auto bs = RakNet::BitStream(reinterpret_cast<unsigned char*>(&startSkill.sBitStream[0]), startSkill.sBitStream.size(), false);
|
||||
|
||||
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
||||
auto* const skillComponent = entity->GetComponent<SkillComponent>();
|
||||
|
||||
success = skillComponent->CastPlayerSkill(behaviorId, startSkill.uiSkillHandle, bs, startSkill.optionalTargetID, startSkill.skillID);
|
||||
if (skillComponent) success = skillComponent->CastPlayerSkill(behaviorId, startSkill.uiSkillHandle, bs, startSkill.optionalTargetID, startSkill.skillID);
|
||||
|
||||
if (success && entity->GetCharacter()) {
|
||||
DestroyableComponent* destComp = entity->GetComponent<DestroyableComponent>();
|
||||
@@ -387,9 +393,9 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
|
||||
if (usr != nullptr) {
|
||||
auto bs = RakNet::BitStream(reinterpret_cast<unsigned char*>(&sync.sBitStream[0]), sync.sBitStream.size(), false);
|
||||
|
||||
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
||||
auto* const skillComponent = entity->GetComponent<SkillComponent>();
|
||||
|
||||
skillComponent->SyncPlayerSkill(sync.uiSkillHandle, sync.uiBehaviorHandle, bs);
|
||||
if (skillComponent) skillComponent->SyncPlayerSkill(sync.uiSkillHandle, sync.uiBehaviorHandle, bs);
|
||||
}
|
||||
|
||||
EchoSyncSkill echo = EchoSyncSkill();
|
||||
|
||||
@@ -2137,6 +2137,7 @@ void GameMessages::HandleUpdatePropertyOrModelForFilterCheck(RakNet::BitStream&
|
||||
inStream.Read(worldId);
|
||||
|
||||
inStream.Read(descriptionLength);
|
||||
if (descriptionLength > MAX_MESSAGE_LENGTH) return;
|
||||
for (uint32_t i = 0; i < descriptionLength; ++i) {
|
||||
uint16_t character;
|
||||
inStream.Read(character);
|
||||
@@ -2144,6 +2145,7 @@ void GameMessages::HandleUpdatePropertyOrModelForFilterCheck(RakNet::BitStream&
|
||||
}
|
||||
|
||||
inStream.Read(nameLength);
|
||||
if (nameLength > MAX_MESSAGE_LENGTH) return;
|
||||
for (uint32_t i = 0; i < nameLength; ++i) {
|
||||
uint16_t character;
|
||||
inStream.Read(character);
|
||||
@@ -2474,9 +2476,15 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
|
||||
|
||||
uint32_t sd0Size;
|
||||
inStream.Read(sd0Size);
|
||||
std::unique_ptr<char[]> sd0Data(new char[sd0Size]);
|
||||
|
||||
if (sd0Data == nullptr) return;
|
||||
// For the sake of letting players make models as big as they want, only reject if we cant allocate the required memory.
|
||||
std::unique_ptr<char[]> sd0Data;
|
||||
try {
|
||||
sd0Data.reset(new char[sd0Size]);
|
||||
} catch (std::exception& e) {
|
||||
LOG("Failed to allocate sd0 of size %u", sd0Size);
|
||||
return;
|
||||
}
|
||||
|
||||
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(sd0Data.get()), sd0Size);
|
||||
|
||||
@@ -2652,6 +2660,7 @@ void GameMessages::HandlePropertyEntranceSync(RakNet::BitStream& inStream, Entit
|
||||
inStream.Read(startIndex);
|
||||
inStream.Read(filterTextLength);
|
||||
|
||||
if (filterTextLength > MAX_MESSAGE_LENGTH) return;
|
||||
for (auto i = 0u; i < filterTextLength; i++) {
|
||||
char c;
|
||||
inStream.Read(c);
|
||||
@@ -3040,6 +3049,7 @@ void GameMessages::HandleVerifyAck(RakNet::BitStream& inStream, Entity* entity,
|
||||
|
||||
uint32_t sBitStreamLength = 0;
|
||||
inStream.Read(sBitStreamLength);
|
||||
if (sBitStreamLength > MAX_MESSAGE_LENGTH) return;
|
||||
for (uint64_t k = 0; k < sBitStreamLength; k++) {
|
||||
uint8_t character;
|
||||
inStream.Read(character);
|
||||
@@ -3261,6 +3271,7 @@ void GameMessages::HandleClientTradeUpdate(RakNet::BitStream& inStream, Entity*
|
||||
|
||||
inStream.Read(currency);
|
||||
inStream.Read(itemCount);
|
||||
if (itemCount > MAX_MESSAGE_LENGTH) return;
|
||||
|
||||
LOG("Trade update from (%llu) -> (%llu), (%i)", entity->GetObjectID(), currency, itemCount);
|
||||
|
||||
@@ -3673,6 +3684,7 @@ void GameMessages::HandleRequestSetPetName(RakNet::BitStream& inStream, Entity*
|
||||
|
||||
inStream.Read(nameLength);
|
||||
|
||||
if (nameLength > MAX_MESSAGE_LENGTH) return;
|
||||
for (size_t i = 0; i < nameLength; i++) {
|
||||
char16_t character;
|
||||
inStream.Read(character);
|
||||
@@ -3742,6 +3754,7 @@ void GameMessages::HandleMessageBoxResponse(RakNet::BitStream& inStream, Entity*
|
||||
inStream.Read(iButton);
|
||||
|
||||
inStream.Read(identifierLength);
|
||||
if (identifierLength > MAX_MESSAGE_LENGTH) return;
|
||||
for (size_t i = 0; i < identifierLength; i++) {
|
||||
char16_t character;
|
||||
inStream.Read(character);
|
||||
@@ -3749,6 +3762,7 @@ void GameMessages::HandleMessageBoxResponse(RakNet::BitStream& inStream, Entity*
|
||||
}
|
||||
|
||||
inStream.Read(userDataLength);
|
||||
if (userDataLength > MAX_MESSAGE_LENGTH) return;
|
||||
for (size_t i = 0; i < userDataLength; i++) {
|
||||
char16_t character;
|
||||
inStream.Read(character);
|
||||
@@ -3796,6 +3810,7 @@ void GameMessages::HandleChoiceBoxRespond(RakNet::BitStream& inStream, Entity* e
|
||||
std::u16string identifier;
|
||||
|
||||
inStream.Read(buttonIdentifierLength);
|
||||
if (buttonIdentifierLength > MAX_MESSAGE_LENGTH) return;
|
||||
for (size_t i = 0; i < buttonIdentifierLength; i++) {
|
||||
char16_t character;
|
||||
inStream.Read(character);
|
||||
@@ -3805,6 +3820,7 @@ void GameMessages::HandleChoiceBoxRespond(RakNet::BitStream& inStream, Entity* e
|
||||
inStream.Read(iButton);
|
||||
|
||||
inStream.Read(identifierLength);
|
||||
if (identifierLength > MAX_MESSAGE_LENGTH) return;
|
||||
for (size_t i = 0; i < identifierLength; i++) {
|
||||
char16_t character;
|
||||
inStream.Read(character);
|
||||
@@ -4158,7 +4174,13 @@ void GameMessages::HandleUpdatePropertyPerformanceCost(RakNet::BitStream& inStre
|
||||
return;
|
||||
}
|
||||
|
||||
Database::Get()->UpdatePerformanceCost(zone->GetZoneID(), performanceCost);
|
||||
const auto* const propertyManagementComponent = entity->GetComponent<PropertyManagementComponent>();
|
||||
const auto* const ownerEntity = propertyManagementComponent ? propertyManagementComponent->GetOwner() : nullptr;
|
||||
const auto* const character = ownerEntity ? ownerEntity->GetCharacter() : nullptr;
|
||||
const auto& zoneID = zone->GetZoneID();
|
||||
if (character && character->GetPropertyCloneID() == zoneID.GetCloneID()) {
|
||||
Database::Get()->UpdatePerformanceCost(zoneID, performanceCost);
|
||||
}
|
||||
}
|
||||
|
||||
void GameMessages::HandleVehicleNotifyHitImaginationServer(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
||||
@@ -4783,13 +4805,19 @@ void GameMessages::HandleParseChatMessage(RakNet::BitStream& inStream, Entity* e
|
||||
|
||||
uint32_t wsStringLength;
|
||||
inStream.Read(wsStringLength);
|
||||
|
||||
if (wsStringLength > MAX_MESSAGE_LENGTH) {
|
||||
LOG("Max message length reached, capping message.");
|
||||
wsStringLength = MAX_MESSAGE_LENGTH;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < wsStringLength; ++i) {
|
||||
uint16_t character;
|
||||
inStream.Read(character);
|
||||
wsString.push_back(character);
|
||||
}
|
||||
|
||||
if (wsString[0] == L'/') {
|
||||
if (!wsString.empty() && wsString[0] == L'/') {
|
||||
SlashCommandHandler::HandleChatCommand(wsString, entity, sysAddr);
|
||||
}
|
||||
}
|
||||
@@ -4806,6 +4834,7 @@ void GameMessages::HandleFireEventServerSide(RakNet::BitStream& inStream, Entity
|
||||
LWOOBJID senderID{};
|
||||
|
||||
inStream.Read(argsLength);
|
||||
if (argsLength > MAX_MESSAGE_LENGTH) return;
|
||||
for (uint32_t i = 0; i < argsLength; ++i) {
|
||||
uint16_t character;
|
||||
inStream.Read(character);
|
||||
@@ -5436,7 +5465,7 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream& inStream, Entity*
|
||||
std::vector<LOT> modList;
|
||||
auto& oldPartList = character->GetVar<std::string>(u"currentModifiedBuild");
|
||||
bool everyPieceSwapped = !oldPartList.empty(); // If the player didn't put a build in initially, then they should not get this achievement.
|
||||
if (count >= 3) {
|
||||
if (count >= 3 && count < 8) {
|
||||
std::u16string modules;
|
||||
|
||||
for (uint32_t k = 0; k < count; k++) {
|
||||
@@ -5733,6 +5762,7 @@ void GameMessages::HandleMatchRequest(RakNet::BitStream& inStream, Entity* entit
|
||||
|
||||
inStream.Read(activator);
|
||||
inStream.Read(playerChoicesLen);
|
||||
if (playerChoicesLen > MAX_MESSAGE_LENGTH) return;
|
||||
for (uint32_t i = 0; i < playerChoicesLen; ++i) {
|
||||
uint16_t character;
|
||||
inStream.Read(character);
|
||||
@@ -5890,7 +5920,7 @@ void GameMessages::HandlePlayerRailArrivedNotification(RakNet::BitStream& inStre
|
||||
const SystemAddress& sysAddr) {
|
||||
uint32_t pathNameLength;
|
||||
inStream.Read(pathNameLength);
|
||||
|
||||
if (pathNameLength > MAX_MESSAGE_LENGTH) return;
|
||||
std::u16string pathName;
|
||||
for (auto k = 0; k < pathNameLength; k++) {
|
||||
uint16_t c;
|
||||
|
||||
@@ -75,7 +75,8 @@ uint32_t Inventory::GetLotCount(const LOT lot) const {
|
||||
}
|
||||
|
||||
void Inventory::SetSize(const uint32_t value) {
|
||||
free += static_cast<int32_t>(value) - static_cast<int32_t>(size);
|
||||
const auto delta = static_cast<int32_t>(value) - static_cast<int32_t>(size);
|
||||
free = static_cast<uint32_t>(std::max(0, static_cast<int32_t>(free) + delta));
|
||||
|
||||
size = value;
|
||||
|
||||
|
||||
@@ -401,7 +401,8 @@ void Item::Disassemble(const eInventoryType inventoryType) {
|
||||
const auto deliminator = '+';
|
||||
|
||||
while (std::getline(ssData, token, deliminator)) {
|
||||
const auto modLot = std::stoi(token.substr(2, token.size() - 1));
|
||||
if (token.size() <= 2) continue; // invalid token, must have size of at least 3.
|
||||
const auto modLot = GeneralUtils::TryParse(token.substr(2, token.size() - 1), LOT_NULL);
|
||||
|
||||
modArray.push_back(modLot);
|
||||
}
|
||||
@@ -440,7 +441,10 @@ void Item::DisassembleModel(uint32_t numToDismantle) {
|
||||
std::vector<std::string> renderAssetSplit = GeneralUtils::SplitString(renderAsset, '/');
|
||||
if (renderAssetSplit.empty()) return;
|
||||
|
||||
std::string lxfmlPath = "BrickModels" + lxfmlFolderName + "/" + GeneralUtils::SplitString(renderAssetSplit.back(), '.').at(0) + ".lxfml";
|
||||
const auto renderAssetSplitSplit = GeneralUtils::SplitString(renderAssetSplit.back(), '.');
|
||||
if (renderAssetSplitSplit.empty()) return;
|
||||
|
||||
std::string lxfmlPath = "BrickModels" + lxfmlFolderName + "/" + renderAssetSplitSplit[0] + ".lxfml";
|
||||
auto file = Game::assetManager->GetFile(lxfmlPath.c_str());
|
||||
|
||||
if (!file) {
|
||||
|
||||
@@ -128,8 +128,8 @@ void ItemSet::OnEquip(const LOT lot) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* skillComponent = m_InventoryComponent->GetParent()->GetComponent<SkillComponent>();
|
||||
auto* missionComponent = m_InventoryComponent->GetParent()->GetComponent<MissionComponent>();
|
||||
auto [skillComponent, missionComponent] = m_InventoryComponent->GetParent()->GetComponentsMut<SkillComponent, MissionComponent>();
|
||||
if (!skillComponent || !missionComponent) return; // Nothing to do here if these are null
|
||||
|
||||
for (const auto skill : skillSet) {
|
||||
auto* skillTable = CDClientManager::GetTable<CDSkillBehaviorTable>();
|
||||
|
||||
@@ -74,23 +74,19 @@ Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) {
|
||||
|
||||
void Mission::LoadFromXmlDone(const tinyxml2::XMLElement& element) {
|
||||
// Start custom XML
|
||||
if (element.Attribute("state") != nullptr) {
|
||||
m_State = static_cast<eMissionState>(std::stoul(element.Attribute("state")));
|
||||
}
|
||||
m_State = static_cast<eMissionState>(element.UnsignedAttribute("state"));
|
||||
// End custom XML
|
||||
|
||||
if (element.Attribute("cct") != nullptr) {
|
||||
m_Completions = std::stoul(element.Attribute("cct"));
|
||||
m_Completions = element.UnsignedAttribute("cct");
|
||||
|
||||
m_Timestamp = std::stoul(element.Attribute("cts"));
|
||||
}
|
||||
m_Timestamp = element.UnsignedAttribute("cts");
|
||||
}
|
||||
|
||||
void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
|
||||
const auto* const character = GetCharacter();
|
||||
// Start custom XML
|
||||
if (element.Attribute("state") != nullptr) {
|
||||
m_State = static_cast<eMissionState>(std::stoul(element.Attribute("state")));
|
||||
m_State = static_cast<eMissionState>(element.IntAttribute("state", -1));
|
||||
}
|
||||
// End custom XML
|
||||
|
||||
@@ -106,7 +102,7 @@ void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
|
||||
|
||||
const auto type = curTask->GetType();
|
||||
|
||||
auto value = std::stoul(task->Attribute("v"));
|
||||
auto value = task->UnsignedAttribute("v");
|
||||
curTask->SetProgress(value, false);
|
||||
task = task->NextSiblingElement();
|
||||
|
||||
@@ -114,7 +110,7 @@ void Mission::LoadFromXmlCur(const tinyxml2::XMLElement& element) {
|
||||
if (type == eMissionTaskType::COLLECTION || type == eMissionTaskType::VISIT_PROPERTY) {
|
||||
std::vector<uint32_t> uniques;
|
||||
while (task != nullptr && value > 0) {
|
||||
const auto unique = std::stoul(task->Attribute("v"));
|
||||
const auto unique = task->UnsignedAttribute("v");
|
||||
|
||||
uniques.push_back(unique);
|
||||
|
||||
|
||||
@@ -17,50 +17,63 @@ PropertyBehavior::PropertyBehavior(bool _isTemplated) {
|
||||
isTemplated = _isTemplated;
|
||||
}
|
||||
|
||||
bool CheckStateRange(const BehaviorState state) {
|
||||
return state >= BehaviorState::HOME_STATE && state <= BehaviorState::STAR_STATE;
|
||||
}
|
||||
|
||||
template<>
|
||||
void PropertyBehavior::HandleMsg(AddStripMessage& msg) {
|
||||
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||
};
|
||||
|
||||
template<>
|
||||
void PropertyBehavior::HandleMsg(AddActionMessage& msg) {
|
||||
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||
};
|
||||
|
||||
template<>
|
||||
void PropertyBehavior::HandleMsg(RearrangeStripMessage& msg) {
|
||||
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||
};
|
||||
|
||||
template<>
|
||||
void PropertyBehavior::HandleMsg(UpdateActionMessage& msg) {
|
||||
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||
};
|
||||
|
||||
template<>
|
||||
void PropertyBehavior::HandleMsg(UpdateStripUiMessage& msg) {
|
||||
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||
};
|
||||
|
||||
template<>
|
||||
void PropertyBehavior::HandleMsg(RemoveStripMessage& msg) {
|
||||
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||
};
|
||||
|
||||
template<>
|
||||
void PropertyBehavior::HandleMsg(RemoveActionsMessage& msg) {
|
||||
if (!CheckStateRange(msg.GetActionContext().GetStateId())) return;
|
||||
m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_LastEditedState = msg.GetActionContext().GetStateId();
|
||||
};
|
||||
|
||||
template<>
|
||||
void PropertyBehavior::HandleMsg(SplitStripMessage& msg) {
|
||||
if (!CheckStateRange(msg.GetSourceActionContext().GetStateId())) return;
|
||||
if (!CheckStateRange(msg.GetDestinationActionContext().GetStateId())) return;
|
||||
m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_LastEditedState = msg.GetDestinationActionContext().GetStateId();
|
||||
@@ -68,6 +81,8 @@ void PropertyBehavior::HandleMsg(SplitStripMessage& msg) {
|
||||
|
||||
template<>
|
||||
void PropertyBehavior::HandleMsg(MigrateActionsMessage& msg) {
|
||||
if (!CheckStateRange(msg.GetSourceActionContext().GetStateId())) return;
|
||||
if (!CheckStateRange(msg.GetDestinationActionContext().GetStateId())) return;
|
||||
m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_LastEditedState = msg.GetDestinationActionContext().GetStateId();
|
||||
@@ -75,6 +90,8 @@ void PropertyBehavior::HandleMsg(MigrateActionsMessage& msg) {
|
||||
|
||||
template<>
|
||||
void PropertyBehavior::HandleMsg(MergeStripsMessage& msg) {
|
||||
if (!CheckStateRange(msg.GetSourceActionContext().GetStateId())) return;
|
||||
if (!CheckStateRange(msg.GetDestinationActionContext().GetStateId())) return;
|
||||
m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg);
|
||||
m_LastEditedState = msg.GetDestinationActionContext().GetStateId();
|
||||
|
||||
@@ -66,13 +66,9 @@ const BrickList& BrickDatabase::GetBricks(const LxfmlPath& lxfmlPath) {
|
||||
std::string materialString(materialList);
|
||||
const auto materials = GeneralUtils::SplitString(materialString, ',');
|
||||
|
||||
if (!materials.empty()) {
|
||||
brick.materialID = std::stoi(materials[0]);
|
||||
} else {
|
||||
brick.materialID = 0;
|
||||
}
|
||||
brick.materialID = GeneralUtils::TryParse(materials[0], 0);
|
||||
} else if (materialID != nullptr) {
|
||||
brick.materialID = std::stoi(materialID);
|
||||
brick.materialID = GeneralUtils::TryParse(materialID, 0);
|
||||
} else {
|
||||
brick.materialID = 0; // This is bad, makes it so the minigame can't be played
|
||||
}
|
||||
|
||||
@@ -54,17 +54,23 @@ void LogAndSaveFailedAntiCheatCheck(const LWOOBJID& id, const SystemAddress& sys
|
||||
|
||||
// If player exists and entity exists in world, use both for logging info.
|
||||
if (entity && player) {
|
||||
const auto* const playerChar = player->GetCharacter();
|
||||
const auto& playerName = playerChar ? playerChar->GetName() : "(null player character)";
|
||||
const auto* const entityChar = entity->GetCharacter();
|
||||
const auto& entityName = entityChar ? entityChar->GetName() : "(null entity character)";
|
||||
LOG("Player (%s) (%llu) at system address (%s) with sending player (%s) (%llu) does not match their own.",
|
||||
player->GetCharacter()->GetName().c_str(), player->GetObjectID(),
|
||||
playerName.c_str(), player->GetObjectID(),
|
||||
sysAddr.ToString(),
|
||||
entity->GetCharacter()->GetName().c_str(), entity->GetObjectID());
|
||||
if (player->GetCharacter()) toReport = player->GetCharacter()->GetParentUser();
|
||||
entityName.c_str(), entity->GetObjectID());
|
||||
if (playerChar) toReport = playerChar->GetParentUser();
|
||||
// In the case that the target entity id did not exist, just log the player info.
|
||||
} else if (player) {
|
||||
const auto* const playerChar = player->GetCharacter();
|
||||
const auto& playerName = playerChar ? playerChar->GetName() : "(null player character)";
|
||||
LOG("Player (%s) (%llu) at system address (%s) with sending player (%llu) does not match their own.",
|
||||
player->GetCharacter()->GetName().c_str(), player->GetObjectID(),
|
||||
playerName.c_str(), player->GetObjectID(),
|
||||
sysAddr.ToString(), id);
|
||||
if (player->GetCharacter()) toReport = player->GetCharacter()->GetParentUser();
|
||||
if (playerChar) toReport = playerChar->GetParentUser();
|
||||
// In the rare case that the player does not exist, just log the system address and who the target id was.
|
||||
} else {
|
||||
LOG("Player at system address (%s) with sending player (%llu) does not match their own.",
|
||||
@@ -76,8 +82,11 @@ void LogAndSaveFailedAntiCheatCheck(const LWOOBJID& id, const SystemAddress& sys
|
||||
auto* user = UserManager::Instance()->GetUser(sysAddr);
|
||||
|
||||
if (user) {
|
||||
const auto* const lastChar = user->GetLastUsedChar();
|
||||
const auto& lastName = lastChar ? lastChar->GetName() : "(null last char)";
|
||||
const auto lastObjID = lastChar ? lastChar->GetObjectID() : LWOOBJID_EMPTY;
|
||||
LOG("User at system address (%s) (%s) (%llu) sent a packet as (%llu) which is not an id they own.",
|
||||
sysAddr.ToString(), user->GetLastUsedChar()->GetName().c_str(), user->GetLastUsedChar()->GetObjectID(), id);
|
||||
sysAddr.ToString(), lastName.c_str(), lastObjID, id);
|
||||
// Can't know sending player. Just log system address for IP banning.
|
||||
} else {
|
||||
LOG("No user found for system address (%s).", sysAddr.ToString());
|
||||
|
||||
@@ -326,7 +326,7 @@ void DropRegularLoot(Team& team, GameMessages::DropClientLoot& lootMsg, const bo
|
||||
void DropLoot(Entity* player, const LWOOBJID source, const std::map<LOT, LootDropInfo>& rolledItems, uint32_t minCoins, uint32_t maxCoins, const bool noTeamLootOnDeath) {
|
||||
player = player->GetOwner(); // if the owner is overwritten, we collect that here
|
||||
const auto playerID = player->GetObjectID();
|
||||
if (!player || !player->IsPlayer()) {
|
||||
if (!player->IsPlayer()) {
|
||||
LOG("Trying to drop loot for non-player %llu:%i", playerID, player->GetLOT());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,69 +74,74 @@ namespace Mail {
|
||||
void SendRequest::Handle() {
|
||||
SendResponse response;
|
||||
auto* character = player->GetCharacter();
|
||||
const bool restrictMailOnMute = UserManager::Instance()->GetMuteRestrictMail() && character->GetParentUser()->GetIsMuted();
|
||||
const bool restrictedMailAccess = character->HasPermission(ePermissionMap::RestrictedMailAccess);
|
||||
|
||||
if (character && !(restrictedMailAccess || restrictMailOnMute)) {
|
||||
mailInfo.recipient = std::regex_replace(mailInfo.recipient, std::regex("[^0-9a-zA-Z]+"), "");
|
||||
auto receiverID = Database::Get()->GetCharacterInfo(mailInfo.recipient);
|
||||
|
||||
if (!receiverID) {
|
||||
response.status = eSendResponse::RecipientNotFound;
|
||||
} else if (GeneralUtils::CaseInsensitiveStringCompare(mailInfo.recipient, character->GetName()) || receiverID->id == character->GetID()) {
|
||||
response.status = eSendResponse::CannotMailSelf;
|
||||
} else {
|
||||
uint32_t mailCost = Game::zoneManager->GetWorldConfig().mailBaseFee;
|
||||
uint32_t stackSize = 0;
|
||||
|
||||
auto inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
Item* item = nullptr;
|
||||
|
||||
bool hasAttachment = mailInfo.itemID != 0 && mailInfo.itemCount > 0;
|
||||
|
||||
if (hasAttachment) {
|
||||
item = inventoryComponent->FindItemById(mailInfo.itemID);
|
||||
if (item) {
|
||||
mailCost += (item->GetInfo().baseValue * Game::zoneManager->GetWorldConfig().mailPercentAttachmentFee);
|
||||
mailInfo.itemLOT = item->GetLot();
|
||||
}
|
||||
}
|
||||
|
||||
if (hasAttachment && !item) {
|
||||
response.status = eSendResponse::AttachmentNotFound;
|
||||
} else if (player->GetCharacter()->GetCoins() - mailCost < 0) {
|
||||
response.status = eSendResponse::NotEnoughCoins;
|
||||
} else {
|
||||
bool removeSuccess = true;
|
||||
// Remove coins and items from the sender
|
||||
player->GetCharacter()->SetCoins(player->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL);
|
||||
if (inventoryComponent && hasAttachment && item) {
|
||||
removeSuccess = inventoryComponent->RemoveItem(mailInfo.itemLOT, mailInfo.itemCount, ALL, true);
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
if (missionComponent && removeSuccess) missionComponent->Progress(eMissionTaskType::GATHER, mailInfo.itemLOT, LWOOBJID_EMPTY, "", -mailInfo.itemCount);
|
||||
}
|
||||
|
||||
// we passed all the checks, now we can actully send the mail
|
||||
if (removeSuccess) {
|
||||
mailInfo.senderId = character->GetID();
|
||||
mailInfo.senderUsername = character->GetName();
|
||||
mailInfo.receiverId = receiverID->id;
|
||||
mailInfo.itemSubkey = LWOOBJID_EMPTY;
|
||||
|
||||
//clear out the attachementID
|
||||
mailInfo.itemID = 0;
|
||||
|
||||
Database::Get()->InsertNewMail(mailInfo);
|
||||
response.status = eSendResponse::Success;
|
||||
character->SaveXMLToDatabase();
|
||||
} else {
|
||||
response.status = eSendResponse::AttachmentNotFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!character) {
|
||||
response.status = eSendResponse::UnknownError;
|
||||
} else {
|
||||
response.status = eSendResponse::SenderAccountIsMuted;
|
||||
const bool restrictMailOnMute = UserManager::Instance()->GetMuteRestrictMail() && character->GetParentUser()->GetIsMuted();
|
||||
const bool restrictedMailAccess = character->HasPermission(ePermissionMap::RestrictedMailAccess);
|
||||
|
||||
if (character && !(restrictedMailAccess || restrictMailOnMute)) {
|
||||
mailInfo.recipient = std::regex_replace(mailInfo.recipient, std::regex("[^0-9a-zA-Z]+"), "");
|
||||
auto receiverID = Database::Get()->GetCharacterInfo(mailInfo.recipient);
|
||||
|
||||
if (!receiverID) {
|
||||
response.status = eSendResponse::RecipientNotFound;
|
||||
} else if (GeneralUtils::CaseInsensitiveStringCompare(mailInfo.recipient, character->GetName()) || receiverID->id == character->GetID()) {
|
||||
response.status = eSendResponse::CannotMailSelf;
|
||||
} else {
|
||||
uint32_t mailCost = Game::zoneManager->GetWorldConfig().mailBaseFee;
|
||||
uint32_t stackSize = 0;
|
||||
|
||||
auto inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
Item* item = nullptr;
|
||||
|
||||
bool hasAttachment = mailInfo.itemID != 0 && mailInfo.itemCount > 0;
|
||||
|
||||
if (hasAttachment) {
|
||||
item = inventoryComponent->FindItemById(mailInfo.itemID);
|
||||
if (item) {
|
||||
mailCost += (item->GetInfo().baseValue * Game::zoneManager->GetWorldConfig().mailPercentAttachmentFee);
|
||||
mailInfo.itemLOT = item->GetLot();
|
||||
}
|
||||
}
|
||||
|
||||
if (hasAttachment && !item) {
|
||||
response.status = eSendResponse::AttachmentNotFound;
|
||||
} else if (player->GetCharacter()->GetCoins() - mailCost < 0) {
|
||||
response.status = eSendResponse::NotEnoughCoins;
|
||||
} else {
|
||||
bool removeSuccess = true;
|
||||
// Remove coins and items from the sender
|
||||
player->GetCharacter()->SetCoins(player->GetCharacter()->GetCoins() - mailCost, eLootSourceType::MAIL);
|
||||
if (inventoryComponent && hasAttachment && item) {
|
||||
removeSuccess = inventoryComponent->RemoveItem(mailInfo.itemLOT, mailInfo.itemCount, ALL, true);
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
if (missionComponent && removeSuccess) missionComponent->Progress(eMissionTaskType::GATHER, mailInfo.itemLOT, LWOOBJID_EMPTY, "", -mailInfo.itemCount);
|
||||
}
|
||||
|
||||
// we passed all the checks, now we can actully send the mail
|
||||
if (removeSuccess) {
|
||||
mailInfo.senderId = character->GetID();
|
||||
mailInfo.senderUsername = character->GetName();
|
||||
mailInfo.receiverId = receiverID->id;
|
||||
mailInfo.itemSubkey = LWOOBJID_EMPTY;
|
||||
|
||||
//clear out the attachementID
|
||||
mailInfo.itemID = 0;
|
||||
|
||||
Database::Get()->InsertNewMail(mailInfo);
|
||||
response.status = eSendResponse::Success;
|
||||
character->SaveXMLToDatabase();
|
||||
} else {
|
||||
response.status = eSendResponse::AttachmentNotFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
response.status = eSendResponse::SenderAccountIsMuted;
|
||||
}
|
||||
}
|
||||
|
||||
LOG("Finished send with status %s", StringifiedEnum::ToString(response.status).data());
|
||||
response.Send(sysAddr);
|
||||
}
|
||||
@@ -193,7 +198,7 @@ namespace Mail {
|
||||
|
||||
if (mailID > 0 && playerID == player->GetObjectID() && inv) {
|
||||
auto playerMail = Database::Get()->GetMail(mailID);
|
||||
if (!playerMail) {
|
||||
if (!playerMail || playerMail->receiverId != player->GetObjectID()) {
|
||||
response.status = eAttachmentCollectResponse::MailNotFound;
|
||||
} else if (!inv->HasSpaceForLoot({ {playerMail->itemLOT, playerMail->itemCount} })) {
|
||||
response.status = eAttachmentCollectResponse::NoSpaceInInventory;
|
||||
@@ -225,15 +230,21 @@ namespace Mail {
|
||||
DeleteResponse response;
|
||||
response.mailID = mailID;
|
||||
|
||||
auto mailData = Database::Get()->GetMail(mailID);
|
||||
if (mailData && !(mailData->itemLOT > 0 && mailData->itemCount > 0)) {
|
||||
Database::Get()->DeleteMail(mailID);
|
||||
response.status = eDeleteResponse::Success;
|
||||
} else if (mailData && mailData->itemLOT > 0 && mailData->itemCount > 0) {
|
||||
response.status = eDeleteResponse::HasAttachments;
|
||||
} else {
|
||||
response.status = eDeleteResponse::NotFound;
|
||||
const auto mailData = Database::Get()->GetMail(mailID);
|
||||
response.status = eDeleteResponse::NotFound;
|
||||
if (mailData) {
|
||||
if (mailData->receiverId != playerID) {
|
||||
LOG("Player %llu attempted to delete mail owned by %llu. Possible spoof?", playerID, mailData->receiverId);
|
||||
} else {
|
||||
if (!(mailData->itemLOT > 0 && mailData->itemCount > 0)) {
|
||||
Database::Get()->DeleteMail(mailID);
|
||||
response.status = eDeleteResponse::Success;
|
||||
} else if (mailData->itemLOT > 0 && mailData->itemCount > 0) {
|
||||
response.status = eDeleteResponse::HasAttachments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG("DeleteRequest status %s", StringifiedEnum::ToString(response.status).data());
|
||||
response.Send(sysAddr);
|
||||
}
|
||||
@@ -253,11 +264,19 @@ namespace Mail {
|
||||
|
||||
void ReadRequest::Handle() {
|
||||
ReadResponse response;
|
||||
response.status = eReadResponse::UnknownError;
|
||||
response.mailID = mailID;
|
||||
|
||||
if (Database::Get()->GetMail(mailID)) {
|
||||
response.status = eReadResponse::Success;
|
||||
Database::Get()->MarkMailRead(mailID);
|
||||
const auto mail = Database::Get()->GetMail(mailID);
|
||||
if (mail) {
|
||||
if (mail->receiverId == player->GetObjectID()) {
|
||||
response.status = eReadResponse::Success;
|
||||
Database::Get()->MarkMailRead(mailID);
|
||||
} else {
|
||||
LOG("Player %llu tried to mark mail read for player %llu", mail->receiverId, player->GetObjectID());
|
||||
}
|
||||
} else {
|
||||
LOG("No mail by ID %llu found to mark as read.", mailID);
|
||||
}
|
||||
|
||||
LOG("ReadRequest %s", StringifiedEnum::ToString(response.status).data());
|
||||
|
||||
@@ -114,11 +114,13 @@ bool Precondition::Check(Entity* player, bool evaluateCosts) const {
|
||||
|
||||
|
||||
bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluateCosts) const {
|
||||
auto* missionComponent = player->GetComponent<MissionComponent>();
|
||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
auto* destroyableComponent = player->GetComponent<DestroyableComponent>();
|
||||
auto* levelComponent = player->GetComponent<LevelProgressionComponent>();
|
||||
auto* character = player->GetCharacter();
|
||||
auto [missionComponent, inventoryComponent, destroyableComponent, levelComponent] =
|
||||
player->GetComponentsMut<const MissionComponent, /* not const */ InventoryComponent, const DestroyableComponent, const LevelProgressionComponent>();
|
||||
|
||||
if (!missionComponent || !inventoryComponent || !destroyableComponent || !levelComponent || !character) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Mission* mission;
|
||||
|
||||
|
||||
@@ -825,7 +825,7 @@ namespace DEVGMCommands {
|
||||
}
|
||||
|
||||
const auto numberToSpawnOptional = GeneralUtils::TryParse<uint32_t>(splitArgs[1]);
|
||||
if (!numberToSpawnOptional && numberToSpawnOptional.value() > 0) {
|
||||
if (!numberToSpawnOptional) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid number of enemies to spawn.");
|
||||
return;
|
||||
}
|
||||
@@ -833,7 +833,7 @@ namespace DEVGMCommands {
|
||||
|
||||
// Must spawn within a radius of at least 0.0f
|
||||
const auto radiusToSpawnWithinOptional = GeneralUtils::TryParse<float>(splitArgs[2]);
|
||||
if (!radiusToSpawnWithinOptional && radiusToSpawnWithinOptional.value() < 0.0f) {
|
||||
if (!radiusToSpawnWithinOptional || radiusToSpawnWithinOptional.value() < 0.0f) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid radius to spawn within.");
|
||||
return;
|
||||
}
|
||||
@@ -1133,6 +1133,10 @@ namespace DEVGMCommands {
|
||||
}
|
||||
|
||||
const auto& password = splitArgs[2];
|
||||
if (password.length() >= 50) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Password is too long.");
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneInstanceManager::Instance()->CreatePrivateZone(Game::server, zone.value(), clone.value(), password);
|
||||
|
||||
|
||||
@@ -187,8 +187,13 @@ namespace GMZeroCommands {
|
||||
auto splitArgs = GeneralUtils::SplitString(args, ' ');
|
||||
if (splitArgs.empty()) return;
|
||||
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Requesting private map...");
|
||||
const auto& password = splitArgs[0];
|
||||
if (password.length() >= 50) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Password is too long.");
|
||||
return;
|
||||
}
|
||||
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Requesting private map...");
|
||||
|
||||
ZoneInstanceManager::Instance()->RequestPrivateZone(Game::server, false, password, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
|
||||
Reference in New Issue
Block a user