Merge branch 'main' into property_behavior_saving

This commit is contained in:
David Markowitz 2024-06-12 19:17:39 -07:00
commit b984cd6a0b
42 changed files with 936 additions and 251 deletions

View File

@ -31,7 +31,7 @@ COPY --from=build /app/build/*Server /app/
# Necessary suplimentary files # Necessary suplimentary files
COPY --from=build /app/build/*.ini /app/configs/ COPY --from=build /app/build/*.ini /app/configs/
COPY --from=build /app/build/vanity/*.* /app/vanity/* COPY --from=build /app/build/vanity/*.* /app/vanity/
COPY --from=build /app/build/navmeshes /app/navmeshes COPY --from=build /app/build/navmeshes /app/navmeshes
COPY --from=build /app/build/migrations /app/migrations COPY --from=build /app/build/migrations /app/migrations
COPY --from=build /app/build/*.dcf /app/ COPY --from=build /app/build/*.dcf /app/
@ -39,7 +39,7 @@ COPY --from=build /app/build/*.dcf /app/
# backup of config and vanity files to copy to the host incase # backup of config and vanity files to copy to the host incase
# of a mount clobbering the copy from above # of a mount clobbering the copy from above
COPY --from=build /app/build/*.ini /app/default-configs/ COPY --from=build /app/build/*.ini /app/default-configs/
COPY --from=build /app/build/vanity/*.* /app/default-vanity/* COPY --from=build /app/build/vanity/*.* /app/default-vanity/
# needed as the container runs with the root user # needed as the container runs with the root user
# and therefore sudo doesn't exist # and therefore sudo doesn't exist

View File

@ -179,6 +179,7 @@ int main(int argc, char** argv) {
} }
void HandlePacket(Packet* packet) { void HandlePacket(Packet* packet) {
if (packet->length < 1) return;
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
LOG("A server has disconnected, erasing their connected players from the list."); LOG("A server has disconnected, erasing their connected players from the list.");
} else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) { } else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {

View File

@ -201,7 +201,7 @@ void OnTerminate() {
} }
void MakeBacktrace() { void MakeBacktrace() {
struct sigaction sigact; struct sigaction sigact{};
sigact.sa_sigaction = CritErrHdlr; sigact.sa_sigaction = CritErrHdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO; sigact.sa_flags = SA_RESTART | SA_SIGINFO;

View File

@ -31,22 +31,22 @@ public:
virtual ~LDFBaseData() {} virtual ~LDFBaseData() {}
virtual void WriteToPacket(RakNet::BitStream& packet) = 0; virtual void WriteToPacket(RakNet::BitStream& packet) const = 0;
virtual const std::u16string& GetKey() = 0; virtual const std::u16string& GetKey() const = 0;
virtual eLDFType GetValueType() = 0; virtual eLDFType GetValueType() const = 0;
/** Gets a string from the key/value pair /** Gets a string from the key/value pair
* @param includeKey Whether or not to include the key in the data * @param includeKey Whether or not to include the key in the data
* @param includeTypeId Whether or not to include the type id in the data * @param includeTypeId Whether or not to include the type id in the data
* @return The string representation of the data * @return The string representation of the data
*/ */
virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) = 0; virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) const = 0;
virtual std::string GetValueAsString() = 0; virtual std::string GetValueAsString() const = 0;
virtual LDFBaseData* Copy() = 0; virtual LDFBaseData* Copy() const = 0;
/** /**
* Given an input string, return the data as a LDF key. * Given an input string, return the data as a LDF key.
@ -62,7 +62,7 @@ private:
T value; T value;
//! Writes the key to the packet //! Writes the key to the packet
void WriteKey(RakNet::BitStream& packet) { void WriteKey(RakNet::BitStream& packet) const {
packet.Write<uint8_t>(this->key.length() * sizeof(uint16_t)); packet.Write<uint8_t>(this->key.length() * sizeof(uint16_t));
for (uint32_t i = 0; i < this->key.length(); ++i) { for (uint32_t i = 0; i < this->key.length(); ++i) {
packet.Write<uint16_t>(this->key[i]); packet.Write<uint16_t>(this->key[i]);
@ -70,7 +70,7 @@ private:
} }
//! Writes the value to the packet //! Writes the value to the packet
void WriteValue(RakNet::BitStream& packet) { void WriteValue(RakNet::BitStream& packet) const {
packet.Write<uint8_t>(this->GetValueType()); packet.Write<uint8_t>(this->GetValueType());
packet.Write(this->value); packet.Write(this->value);
} }
@ -90,7 +90,7 @@ public:
/*! /*!
\return The value \return The value
*/ */
const T& GetValue(void) { return this->value; } const T& GetValue(void) const { return this->value; }
//! Sets the value //! Sets the value
/*! /*!
@ -102,13 +102,13 @@ public:
/*! /*!
\return The value string \return The value string
*/ */
std::string GetValueString(void) { return ""; } std::string GetValueString(void) const { return ""; }
//! Writes the data to a packet //! Writes the data to a packet
/*! /*!
\param packet The packet \param packet The packet
*/ */
void WriteToPacket(RakNet::BitStream& packet) override { void WriteToPacket(RakNet::BitStream& packet) const override {
this->WriteKey(packet); this->WriteKey(packet);
this->WriteValue(packet); this->WriteValue(packet);
} }
@ -117,13 +117,13 @@ public:
/*! /*!
\return The key \return The key
*/ */
const std::u16string& GetKey(void) override { return this->key; } const std::u16string& GetKey(void) const override { return this->key; }
//! Gets the LDF Type //! Gets the LDF Type
/*! /*!
\return The LDF value type \return The LDF value type
*/ */
eLDFType GetValueType(void) override { return LDF_TYPE_UNKNOWN; } eLDFType GetValueType(void) const override { return LDF_TYPE_UNKNOWN; }
//! Gets the string data //! Gets the string data
/*! /*!
@ -131,7 +131,7 @@ public:
\param includeTypeId Whether or not to include the type id in the data \param includeTypeId Whether or not to include the type id in the data
\return The string representation of the data \return The string representation of the data
*/ */
std::string GetString(const bool includeKey = true, const bool includeTypeId = true) override { std::string GetString(const bool includeKey = true, const bool includeTypeId = true) const override {
if (GetValueType() == -1) { if (GetValueType() == -1) {
return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>"; return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>";
} }
@ -154,11 +154,11 @@ public:
return stream.str(); return stream.str();
} }
std::string GetValueAsString() override { std::string GetValueAsString() const override {
return this->GetValueString(); return this->GetValueString();
} }
LDFBaseData* Copy() override { LDFBaseData* Copy() const override {
return new LDFData<T>(key, value); return new LDFData<T>(key, value);
} }
@ -166,19 +166,19 @@ public:
}; };
// LDF Types // LDF Types
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) { return LDF_TYPE_UTF_16; }; template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) const { return LDF_TYPE_UTF_16; };
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) { return LDF_TYPE_S32; }; template<> inline eLDFType LDFData<int32_t>::GetValueType(void) const { return LDF_TYPE_S32; };
template<> inline eLDFType LDFData<float>::GetValueType(void) { return LDF_TYPE_FLOAT; }; template<> inline eLDFType LDFData<float>::GetValueType(void) const { return LDF_TYPE_FLOAT; };
template<> inline eLDFType LDFData<double>::GetValueType(void) { return LDF_TYPE_DOUBLE; }; template<> inline eLDFType LDFData<double>::GetValueType(void) const { return LDF_TYPE_DOUBLE; };
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) { return LDF_TYPE_U32; }; template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) const { return LDF_TYPE_U32; };
template<> inline eLDFType LDFData<bool>::GetValueType(void) { return LDF_TYPE_BOOLEAN; }; template<> inline eLDFType LDFData<bool>::GetValueType(void) const { return LDF_TYPE_BOOLEAN; };
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) { return LDF_TYPE_U64; }; template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) const { return LDF_TYPE_U64; };
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) { return LDF_TYPE_OBJID; }; template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) const { return LDF_TYPE_OBJID; };
template<> inline eLDFType LDFData<std::string>::GetValueType(void) { return LDF_TYPE_UTF_8; }; template<> inline eLDFType LDFData<std::string>::GetValueType(void) const { return LDF_TYPE_UTF_8; };
// The specialized version for std::u16string (UTF-16) // The specialized version for std::u16string (UTF-16)
template<> template<>
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) { inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) const {
packet.Write<uint8_t>(this->GetValueType()); packet.Write<uint8_t>(this->GetValueType());
packet.Write<uint32_t>(this->value.length()); packet.Write<uint32_t>(this->value.length());
@ -189,7 +189,7 @@ inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) {
// The specialized version for bool // The specialized version for bool
template<> template<>
inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) { inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) const {
packet.Write<uint8_t>(this->GetValueType()); packet.Write<uint8_t>(this->GetValueType());
packet.Write<uint8_t>(this->value); packet.Write<uint8_t>(this->value);
@ -197,7 +197,7 @@ inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) {
// The specialized version for std::string (UTF-8) // The specialized version for std::string (UTF-8)
template<> template<>
inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) { inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) const {
packet.Write<uint8_t>(this->GetValueType()); packet.Write<uint8_t>(this->GetValueType());
packet.Write<uint32_t>(this->value.length()); packet.Write<uint32_t>(this->value.length());
@ -206,18 +206,18 @@ inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) {
} }
} }
template<> inline std::string LDFData<std::u16string>::GetValueString() { template<> inline std::string LDFData<std::u16string>::GetValueString() const {
return GeneralUtils::UTF16ToWTF8(this->value, this->value.size()); return GeneralUtils::UTF16ToWTF8(this->value, this->value.size());
} }
template<> inline std::string LDFData<int32_t>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<int32_t>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<float>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<float>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<double>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<double>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<uint32_t>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<uint32_t>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<bool>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<bool>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<uint64_t>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<uint64_t>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<LWOOBJID>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<LWOOBJID>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<std::string>::GetValueString() { return this->value; } template<> inline std::string LDFData<std::string>::GetValueString() const { return this->value; }
#endif //!__LDFFORMAT__H__ #endif //!__LDFFORMAT__H__

View File

@ -1,6 +1,7 @@
#include "dConfig.h" #include "dConfig.h"
#include <sstream> #include <sstream>
#include <algorithm>
#include "BinaryPathFinder.h" #include "BinaryPathFinder.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"

View File

@ -0,0 +1,21 @@
#ifndef __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
#define __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
#include <cstdint>
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t {
SUCCESS,
FAIL_GENERIC,
FAIL_INV_FULL,
FAIL_ITEM_NOT_FOUND,
FAIL_CANT_MOVE_TO_THAT_INV_TYPE,
FAIL_NOT_NEAR_BANK,
FAIL_CANT_SWAP_ITEMS,
FAIL_SOURCE_TYPE,
FAIL_WRONG_DEST_TYPE,
FAIL_SWAP_DEST_TYPE,
FAIL_CANT_MOVE_THINKING_HAT,
FAIL_DISMOUNT_BEFORE_MOVING
};
#endif //!__EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__

View File

@ -27,6 +27,8 @@ Character::Character(uint32_t id, User* parentUser) {
m_ID = id; m_ID = id;
m_ParentUser = parentUser; m_ParentUser = parentUser;
m_OurEntity = nullptr; m_OurEntity = nullptr;
m_GMLevel = eGameMasterLevel::CIVILIAN;
m_PermissionMap = static_cast<ePermissionMap>(0);
} }
Character::~Character() { Character::~Character() {

View File

@ -464,22 +464,22 @@ private:
/** /**
* The ID of this character. First 32 bits of the ObjectID. * The ID of this character. First 32 bits of the ObjectID.
*/ */
uint32_t m_ID; uint32_t m_ID{};
/** /**
* The 64-bit unique ID used in the game. * The 64-bit unique ID used in the game.
*/ */
LWOOBJID m_ObjectID; LWOOBJID m_ObjectID{ LWOOBJID_EMPTY };
/** /**
* The user that owns this character. * The user that owns this character.
*/ */
User* m_ParentUser; User* m_ParentUser{};
/** /**
* If the character is in game, this is the entity that it represents, else nullptr. * If the character is in game, this is the entity that it represents, else nullptr.
*/ */
Entity* m_OurEntity; Entity* m_OurEntity{};
/** /**
* 0-9, the Game Master level of this character. * 0-9, the Game Master level of this character.
@ -506,17 +506,17 @@ private:
/** /**
* Whether the custom name of this character is rejected * Whether the custom name of this character is rejected
*/ */
bool m_NameRejected; bool m_NameRejected{};
/** /**
* The current amount of coins of this character * The current amount of coins of this character
*/ */
int64_t m_Coins; int64_t m_Coins{};
/** /**
* Whether the character is building * Whether the character is building
*/ */
bool m_BuildMode; bool m_BuildMode{};
/** /**
* The items equipped by the character on world load * The items equipped by the character on world load
@ -583,7 +583,7 @@ private:
/** /**
* The ID of the properties of this character * The ID of the properties of this character
*/ */
uint32_t m_PropertyCloneID; uint32_t m_PropertyCloneID{};
/** /**
* The XML data for this character, stored as string * The XML data for this character, stored as string
@ -613,7 +613,7 @@ private:
/** /**
* The last time this character logged in * The last time this character logged in
*/ */
uint64_t m_LastLogin; uint64_t m_LastLogin{};
/** /**
* The gameplay flags this character has (not just true values) * The gameplay flags this character has (not just true values)

View File

@ -12,6 +12,7 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
m_AccountID = 0; m_AccountID = 0;
m_Username = ""; m_Username = "";
m_SessionKey = ""; m_SessionKey = "";
m_MuteExpire = 0;
m_MaxGMLevel = eGameMasterLevel::CIVILIAN; //The max GM level this account can assign to it's characters m_MaxGMLevel = eGameMasterLevel::CIVILIAN; //The max GM level this account can assign to it's characters
m_LastCharID = 0; m_LastCharID = 0;

View File

@ -105,7 +105,7 @@ void BehaviorContext::ExecuteUpdates() {
this->scheduledUpdates.clear(); this->scheduledUpdates.clear();
} }
void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) { bool BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
BehaviorSyncEntry entry; BehaviorSyncEntry entry;
auto found = false; auto found = false;
@ -128,7 +128,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
if (!found) { if (!found) {
LOG("Failed to find behavior sync entry with sync id (%i)!", syncId); LOG("Failed to find behavior sync entry with sync id (%i)!", syncId);
return; return false;
} }
auto* behavior = entry.behavior; auto* behavior = entry.behavior;
@ -137,10 +137,11 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
if (behavior == nullptr) { if (behavior == nullptr) {
LOG("Invalid behavior for sync id (%i)!", syncId); LOG("Invalid behavior for sync id (%i)!", syncId);
return; return false;
} }
behavior->Sync(this, bitStream, branch); behavior->Sync(this, bitStream, branch);
return true;
} }

View File

@ -93,7 +93,7 @@ struct BehaviorContext
void ExecuteUpdates(); void ExecuteUpdates();
void SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream); bool SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
void Update(float deltaTime); void Update(float deltaTime);

View File

@ -29,7 +29,8 @@
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) { BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
m_Target = LWOOBJID_EMPTY; m_Target = LWOOBJID_EMPTY;
SetAiState(AiState::spawn); m_DirtyStateOrTarget = true;
m_State = AiState::spawn;
m_Timer = 1.0f; m_Timer = 1.0f;
m_StartPosition = parent->GetPosition(); m_StartPosition = parent->GetPosition();
m_MovementAI = nullptr; m_MovementAI = nullptr;

View File

@ -558,19 +558,9 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
itemElement->QueryAttribute("parent", &parent); itemElement->QueryAttribute("parent", &parent);
// End custom xml // End custom xml
std::vector<LDFBaseData*> config; auto* item = new Item(id, lot, inventory, slot, count, bound, {}, parent, subKey);
auto* extraInfo = itemElement->FirstChildElement("x"); item->LoadConfigXml(*itemElement);
if (extraInfo) {
std::string modInfo = extraInfo->Attribute("ma");
LDFBaseData* moduleAssembly = new LDFData<std::u16string>(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(modInfo.substr(2, modInfo.size() - 1)));
config.push_back(moduleAssembly);
}
const auto* item = new Item(id, lot, inventory, slot, count, bound, config, parent, subKey);
if (equipped) { if (equipped) {
const auto info = Inventory::FindItemComponent(lot); const auto info = Inventory::FindItemComponent(lot);
@ -676,17 +666,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
itemElement->SetAttribute("parent", item->GetParent()); itemElement->SetAttribute("parent", item->GetParent());
// End custom xml // End custom xml
for (auto* data : item->GetConfig()) { item->SaveConfigXml(*itemElement);
if (data->GetKey() != u"assemblyPartLOTs") {
continue;
}
auto* extraInfo = document.NewElement("x");
extraInfo->SetAttribute("ma", data->GetString(false).c_str());
itemElement->LinkEndChild(extraInfo);
}
bagElement->LinkEndChild(itemElement); bagElement->LinkEndChild(itemElement);
} }
@ -895,8 +875,6 @@ void InventoryComponent::UnEquipItem(Item* item) {
RemoveSlot(item->GetInfo().equipLocation); RemoveSlot(item->GetInfo().equipLocation);
PurgeProxies(item);
UnequipScripts(item); UnequipScripts(item);
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
@ -906,6 +884,8 @@ void InventoryComponent::UnEquipItem(Item* item) {
PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent); PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent); Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
} }
PurgeProxies(item);
} }
@ -1525,10 +1505,10 @@ void InventoryComponent::PurgeProxies(Item* item) {
const auto root = item->GetParent(); const auto root = item->GetParent();
if (root != LWOOBJID_EMPTY) { if (root != LWOOBJID_EMPTY) {
item = FindItemById(root); Item* itemRoot = FindItemById(root);
if (item != nullptr) { if (itemRoot != nullptr) {
UnEquipItem(item); UnEquipItem(itemRoot);
} }
return; return;
@ -1600,18 +1580,18 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument& document) {
} }
bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId){ bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId) {
BehaviorSlot behaviorSlot = BehaviorSlot::Invalid; BehaviorSlot behaviorSlot = BehaviorSlot::Invalid;
if (slot == 1 ) behaviorSlot = BehaviorSlot::Primary; if (slot == 1) behaviorSlot = BehaviorSlot::Primary;
else if (slot == 2 ) behaviorSlot = BehaviorSlot::Offhand; else if (slot == 2) behaviorSlot = BehaviorSlot::Offhand;
else if (slot == 3 ) behaviorSlot = BehaviorSlot::Neck; else if (slot == 3) behaviorSlot = BehaviorSlot::Neck;
else if (slot == 4 ) behaviorSlot = BehaviorSlot::Head; else if (slot == 4) behaviorSlot = BehaviorSlot::Head;
else if (slot == 5 ) behaviorSlot = BehaviorSlot::Consumable; else if (slot == 5) behaviorSlot = BehaviorSlot::Consumable;
else return false; else return false;
return SetSkill(behaviorSlot, skillId); return SetSkill(behaviorSlot, skillId);
} }
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) {
if (skillId == 0) return false; if (skillId == 0) return false;
const auto index = m_Skills.find(slot); const auto index = m_Skills.find(slot);
if (index != m_Skills.end()) { if (index != m_Skills.end()) {
@ -1623,4 +1603,3 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){
m_Skills.insert_or_assign(slot, skillId); m_Skills.insert_or_assign(slot, skillId);
return true; return true;
} }

View File

@ -32,6 +32,8 @@
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
#include "eMissionState.h" #include "eMissionState.h"
#include "dNavMesh.h" #include "dNavMesh.h"
#include "eGameActivity.h"
#include "eStateChangeType.h"
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{}; std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{}; std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
@ -210,24 +212,23 @@ void PetComponent::OnUse(Entity* originator) {
if (dpWorld::IsLoaded()) { if (dpWorld::IsLoaded()) {
NiPoint3 attempt = petPosition + forward * interactionDistance; NiPoint3 attempt = petPosition + forward * interactionDistance;
float y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt); NiPoint3 nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt);
while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) { while (std::abs(nearestPoint.y - petPosition.y) > 4 && interactionDistance > 10) {
const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector(); const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector();
attempt = originatorPosition + forward * interactionDistance; attempt = originatorPosition + forward * interactionDistance;
y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt); nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt);
interactionDistance -= 0.5f; interactionDistance -= 0.5f;
} }
position = attempt; position = nearestPoint;
} else { } else {
position = petPosition + forward * interactionDistance; position = petPosition + forward * interactionDistance;
} }
auto rotation = NiQuaternion::LookAt(position, petPosition); auto rotation = NiQuaternion::LookAt(position, petPosition);
GameMessages::SendNotifyPetTamingMinigame( GameMessages::SendNotifyPetTamingMinigame(
@ -246,11 +247,11 @@ void PetComponent::OnUse(Entity* originator) {
m_Parent->GetObjectID(), m_Parent->GetObjectID(),
LWOOBJID_EMPTY, LWOOBJID_EMPTY,
originator->GetObjectID(), originator->GetObjectID(),
true, false,
ePetTamingNotifyType::BEGIN, ePetTamingNotifyType::BEGIN,
petPosition, NiPoint3Constant::ZERO,
position, NiPoint3Constant::ZERO,
rotation, NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f),
UNASSIGNED_SYSTEM_ADDRESS UNASSIGNED_SYSTEM_ADDRESS
); );
@ -258,11 +259,18 @@ void PetComponent::OnUse(Entity* originator) {
m_Tamer = originator->GetObjectID(); m_Tamer = originator->GetObjectID();
SetStatus(5); SetStatus(5);
Game::entityManager->SerializeEntity(m_Parent);
currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID());
// Notify the start of a pet taming minigame // Notify the start of a pet taming minigame
m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN); m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN);
auto* characterComponent = originator->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetCurrentActivity(eGameActivity::PET_TAMING);
Game::entityManager->SerializeEntity(originator);
}
} }
void PetComponent::Update(float deltaTime) { void PetComponent::Update(float deltaTime) {
@ -627,6 +635,11 @@ void PetComponent::RequestSetPetName(std::u16string name) {
UNASSIGNED_SYSTEM_ADDRESS UNASSIGNED_SYSTEM_ADDRESS
); );
auto* characterComponent = tamer->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetCurrentActivity(eGameActivity::NONE);
Game::entityManager->SerializeEntity(tamer);
}
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
auto* modelEntity = Game::entityManager->GetEntity(m_ModelId); auto* modelEntity = Game::entityManager->GetEntity(m_ModelId);
@ -666,6 +679,11 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
UNASSIGNED_SYSTEM_ADDRESS UNASSIGNED_SYSTEM_ADDRESS
); );
auto* characterComponent = tamer->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetCurrentActivity(eGameActivity::NONE);
Game::entityManager->SerializeEntity(tamer);
}
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
@ -712,6 +730,11 @@ void PetComponent::ClientFailTamingMinigame() {
UNASSIGNED_SYSTEM_ADDRESS UNASSIGNED_SYSTEM_ADDRESS
); );
auto* characterComponent = tamer->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetCurrentActivity(eGameActivity::NONE);
Game::entityManager->SerializeEntity(tamer);
}
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());

View File

@ -25,6 +25,7 @@
#include "LeaderboardManager.h" #include "LeaderboardManager.h"
#include "dZoneManager.h" #include "dZoneManager.h"
#include "CDActivitiesTable.h" #include "CDActivitiesTable.h"
#include "eStateChangeType.h"
#include <ctime> #include <ctime>
#ifndef M_PI #ifndef M_PI
@ -77,6 +78,9 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) {
m_LoadedPlayers++; m_LoadedPlayers++;
// not live accurate to stun the player but prevents them from using skills during the race that are not meant to be used.
GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, true, true, true, true, true, true, true, true, true);
LOG("Loading player %i", LOG("Loading player %i",
m_LoadedPlayers); m_LoadedPlayers);
m_LobbyPlayers.push_back(player->GetObjectID()); m_LobbyPlayers.push_back(player->GetObjectID());

View File

@ -38,7 +38,7 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
context->skillID = skillID; context->skillID = skillID;
this->m_managedBehaviors.insert_or_assign(skillUid, context); this->m_managedBehaviors.insert({ skillUid, context });
auto* behavior = Behavior::CreateBehavior(behaviorId); auto* behavior = Behavior::CreateBehavior(behaviorId);
@ -52,17 +52,24 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
} }
void SkillComponent::SyncPlayerSkill(const uint32_t skillUid, const uint32_t syncId, RakNet::BitStream& bitStream) { void SkillComponent::SyncPlayerSkill(const uint32_t skillUid, const uint32_t syncId, RakNet::BitStream& bitStream) {
const auto index = this->m_managedBehaviors.find(skillUid); const auto index = this->m_managedBehaviors.equal_range(skillUid);
if (index == this->m_managedBehaviors.end()) { if (index.first == this->m_managedBehaviors.end()) {
LOG("Failed to find skill with uid (%i)!", skillUid, syncId); LOG("Failed to find skill with uid (%i)!", skillUid, syncId);
return; return;
} }
auto* context = index->second; bool foundSyncId = false;
for (auto it = index.first; it != index.second && !foundSyncId; ++it) {
const auto& context = it->second;
context->SyncBehavior(syncId, bitStream); foundSyncId = context->SyncBehavior(syncId, bitStream);
}
if (!foundSyncId) {
LOG("Failed to find sync id (%i) for skill with uid (%i)!", syncId, skillUid);
}
} }
@ -138,7 +145,7 @@ void SkillComponent::Update(const float deltaTime) {
for (const auto& pair : this->m_managedBehaviors) pair.second->UpdatePlayerSyncs(deltaTime); for (const auto& pair : this->m_managedBehaviors) pair.second->UpdatePlayerSyncs(deltaTime);
} }
std::map<uint32_t, BehaviorContext*> keep{}; std::multimap<uint32_t, BehaviorContext*> keep{};
for (const auto& pair : this->m_managedBehaviors) { for (const auto& pair : this->m_managedBehaviors) {
auto* context = pair.second; auto* context = pair.second;
@ -176,7 +183,7 @@ void SkillComponent::Update(const float deltaTime) {
} }
} }
keep.insert_or_assign(pair.first, context); keep.insert({ pair.first, context });
} }
this->m_managedBehaviors = keep; this->m_managedBehaviors = keep;
@ -285,7 +292,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(
return { false, 0 }; return { false, 0 };
} }
this->m_managedBehaviors.insert_or_assign(context->skillUId, context); this->m_managedBehaviors.insert({ context->skillUId, context });
if (!clientInitalized) { if (!clientInitalized) {
// Echo start skill // Echo start skill

View File

@ -188,7 +188,7 @@ private:
/** /**
* All of the active skills mapped by their unique ID. * All of the active skills mapped by their unique ID.
*/ */
std::map<uint32_t, BehaviorContext*> m_managedBehaviors; std::multimap<uint32_t, BehaviorContext*> m_managedBehaviors;
/** /**
* All active projectiles. * All active projectiles.

View File

@ -76,8 +76,8 @@ void VendorComponent::RefreshInventory(bool isCreation) {
if (vendorItems.empty()) break; if (vendorItems.empty()) break;
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1); auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
const auto& randomItem = vendorItems.at(randomItemIndex); const auto& randomItem = vendorItems.at(randomItemIndex);
vendorItems.erase(vendorItems.begin() + randomItemIndex);
if (SetupItem(randomItem.itemid)) m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority)); if (SetupItem(randomItem.itemid)) m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority));
vendorItems.erase(vendorItems.begin() + randomItemIndex);
} }
} }
} }

View File

@ -99,9 +99,11 @@
#include "ActivityManager.h" #include "ActivityManager.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#include "eVendorTransactionResult.h" #include "eVendorTransactionResult.h"
#include "eReponseMoveItemBetweenInventoryTypeCode.h"
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
#include "CDObjectsTable.h" #include "CDObjectsTable.h"
#include "eItemType.h"
void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) { void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) {
CBITSTREAM; CBITSTREAM;
@ -412,7 +414,8 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd
bitStream.Write(qUnexpectedRotation.w); bitStream.Write(qUnexpectedRotation.w);
} }
SEND_PACKET_BROADCAST; if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
SEND_PACKET;
} }
void GameMessages::SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr) { void GameMessages::SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr) {
@ -4563,16 +4566,31 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream&
if (inStream.ReadBit()) inStream.Read(itemLOT); if (inStream.ReadBit()) inStream.Read(itemLOT);
if (invTypeDst == invTypeSrc) { if (invTypeDst == invTypeSrc) {
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
return; return;
} }
auto* inventoryComponent = entity->GetComponent<InventoryComponent>(); auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
if (inventoryComponent != nullptr) { if (inventoryComponent) {
if (itemID != LWOOBJID_EMPTY) { if (itemID != LWOOBJID_EMPTY) {
auto* item = inventoryComponent->FindItemById(itemID); auto* item = inventoryComponent->FindItemById(itemID);
if (!item) return; if (!item) {
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_ITEM_NOT_FOUND);
return;
}
if (item->GetLot() == 6086) { // Thinking hat
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_CANT_MOVE_THINKING_HAT);
return;
}
auto* destInv = inventoryComponent->GetInventory(invTypeDst);
if (destInv && destInv->GetEmptySlots() == 0) {
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_INV_FULL);
return;
}
// Despawn the pet if we are moving that pet to the vault. // Despawn the pet if we are moving that pet to the vault.
auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID());
@ -4581,10 +4599,32 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream&
} }
inventoryComponent->MoveItemToInventory(item, invTypeDst, iStackCount, showFlyingLoot, false, false, destSlot); inventoryComponent->MoveItemToInventory(item, invTypeDst, iStackCount, showFlyingLoot, false, false, destSlot);
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::SUCCESS);
} }
} else {
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
} }
} }
void GameMessages::SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response) {
CBITSTREAM;
CMSGHEADER;
bitStream.Write(objectId);
bitStream.Write(eGameMessageType::RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES);
bitStream.Write(inventoryTypeDestination != eInventoryType::ITEMS);
if (inventoryTypeDestination != eInventoryType::ITEMS) bitStream.Write(inventoryTypeDestination);
bitStream.Write(inventoryTypeSource != eInventoryType::ITEMS);
if (inventoryTypeSource != eInventoryType::ITEMS) bitStream.Write(inventoryTypeSource);
bitStream.Write(response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
if (response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC) bitStream.Write(response);
SEND_PACKET;
}
void GameMessages::SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr) { void GameMessages::SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr) {
CBITSTREAM; CBITSTREAM;
@ -5351,7 +5391,8 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream& inStream, En
iStackCount = std::min<uint32_t>(item->GetCount(), iStackCount); iStackCount = std::min<uint32_t>(item->GetCount(), iStackCount);
if (bConfirmed) { if (bConfirmed) {
if (eInvType == eInventoryType::MODELS) { const auto itemType = static_cast<eItemType>(item->GetInfo().itemType);
if (itemType == eItemType::MODEL || itemType == eItemType::LOOT_MODEL) {
item->DisassembleModel(iStackCount); item->DisassembleModel(iStackCount);
} }
auto lot = item->GetLot(); auto lot = item->GetLot();
@ -6209,3 +6250,18 @@ void GameMessages::SendSlashCommandFeedbackText(Entity* entity, std::u16string t
auto sysAddr = entity->GetSystemAddress(); auto sysAddr = entity->GetSystemAddress();
SEND_PACKET; SEND_PACKET;
} }
void GameMessages::SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID) {
CBITSTREAM;
CMSGHEADER;
bitStream.Write(entity->GetObjectID());
bitStream.Write(eGameMessageType::FORCE_CAMERA_TARGET_CYCLE);
bitStream.Write(bForceCycling);
bitStream.Write(cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES);
if (cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES) bitStream.Write(cyclingMode);
bitStream.Write(optionalTargetID);
auto sysAddr = entity->GetSystemAddress();
SEND_PACKET;
}

View File

@ -39,6 +39,12 @@ enum class eQuickBuildFailReason : uint32_t;
enum class eQuickBuildState : uint32_t; enum class eQuickBuildState : uint32_t;
enum class BehaviorSlot : int32_t; enum class BehaviorSlot : int32_t;
enum class eVendorTransactionResult : uint32_t; enum class eVendorTransactionResult : uint32_t;
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t;
enum class eCameraTargetCyclingMode : int32_t {
ALLOW_CYCLE_TEAMMATES,
DISALLOW_CYCLING
};
namespace GameMessages { namespace GameMessages {
class PropertyDataMessage; class PropertyDataMessage;
@ -589,6 +595,7 @@ namespace GameMessages {
//NT: //NT:
void HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); void HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
void SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response);
void SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr); void SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr);
@ -666,6 +673,7 @@ namespace GameMessages {
void HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity); void HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity);
void SendSlashCommandFeedbackText(Entity* entity, std::u16string text); void SendSlashCommandFeedbackText(Entity* entity, std::u16string text);
void SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID);
}; };
#endif // GAMEMESSAGES_H #endif // GAMEMESSAGES_H

View File

@ -27,6 +27,23 @@
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
#include "CDPackageComponentTable.h" #include "CDPackageComponentTable.h"
namespace {
const std::map<std::string, std::string> ExtraSettingAbbreviations = {
{ "assemblyPartLOTs", "ma" },
{ "blueprintID", "b" },
{ "userModelID", "ui" },
{ "userModelName", "un" },
{ "userModelDesc", "ud" },
{ "userModelHasBhvr", "ub" },
{ "userModelBehaviors", "ubh" },
{ "userModelBehaviorSourceID", "ubs" },
{ "userModelPhysicsType", "up" },
{ "userModelMod", "um" },
{ "userModelOpt", "uo" },
{ "reforgedLOT", "rl" },
};
}
Item::Item(const LWOOBJID id, const LOT lot, Inventory* inventory, const uint32_t slot, const uint32_t count, const bool bound, const std::vector<LDFBaseData*>& config, const LWOOBJID parent, LWOOBJID subKey, eLootSourceType lootSourceType) { Item::Item(const LWOOBJID id, const LOT lot, Inventory* inventory, const uint32_t slot, const uint32_t count, const bool bound, const std::vector<LDFBaseData*>& config, const LWOOBJID parent, LWOOBJID subKey, eLootSourceType lootSourceType) {
if (!Inventory::IsValidItem(lot)) { if (!Inventory::IsValidItem(lot)) {
return; return;
@ -122,6 +139,10 @@ uint32_t Item::GetSlot() const {
return slot; return slot;
} }
std::vector<LDFBaseData*> Item::GetConfig() const {
return config;
}
std::vector<LDFBaseData*>& Item::GetConfig() { std::vector<LDFBaseData*>& Item::GetConfig() {
return config; return config;
} }
@ -251,7 +272,7 @@ bool Item::Consume() {
auto skills = skillsTable->Query([this](const CDObjectSkills entry) { auto skills = skillsTable->Query([this](const CDObjectSkills entry) {
return entry.objectTemplate == static_cast<uint32_t>(lot); return entry.objectTemplate == static_cast<uint32_t>(lot);
}); });
auto success = false; auto success = false;
@ -515,3 +536,35 @@ Item::~Item() {
config.clear(); config.clear();
} }
void Item::SaveConfigXml(tinyxml2::XMLElement& i) const {
tinyxml2::XMLElement* x = nullptr;
for (const auto* config : this->config) {
const auto& key = GeneralUtils::UTF16ToWTF8(config->GetKey());
const auto saveKey = ExtraSettingAbbreviations.find(key);
if (saveKey == ExtraSettingAbbreviations.end()) {
continue;
}
if (!x) {
x = i.InsertNewChildElement("x");
}
const auto dataToSave = config->GetString(false);
x->SetAttribute(saveKey->second.c_str(), dataToSave.c_str());
}
}
void Item::LoadConfigXml(const tinyxml2::XMLElement& i) {
const auto* x = i.FirstChildElement("x");
if (!x) return;
for (const auto& pair : ExtraSettingAbbreviations) {
const auto* data = x->Attribute(pair.second.c_str());
if (!data) continue;
const auto value = pair.first + "=" + data;
config.push_back(LDFBaseData::DataFromString(value));
}
}

View File

@ -9,6 +9,10 @@
#include "eInventoryType.h" #include "eInventoryType.h"
#include "eLootSourceType.h" #include "eLootSourceType.h"
namespace tinyxml2 {
class XMLElement;
};
/** /**
* An item that can be stored in an inventory and optionally consumed or equipped * An item that can be stored in an inventory and optionally consumed or equipped
* TODO: ideally this should be a component * TODO: ideally this should be a component
@ -116,6 +120,12 @@ public:
*/ */
std::vector<LDFBaseData*>& GetConfig(); std::vector<LDFBaseData*>& GetConfig();
/**
* Returns current config info for this item, e.g. for rockets
* @return current config info for this item
*/
std::vector<LDFBaseData*> GetConfig() const;
/** /**
* Returns the database info for this item * Returns the database info for this item
* @return the database info for this item * @return the database info for this item
@ -214,6 +224,10 @@ public:
*/ */
void RemoveFromInventory(); void RemoveFromInventory();
void SaveConfigXml(tinyxml2::XMLElement& i) const;
void LoadConfigXml(const tinyxml2::XMLElement& i);
private: private:
/** /**
* The object ID of this item * The object ID of this item

View File

@ -454,6 +454,16 @@ void Mission::YieldRewards() {
} }
} }
// Even with no repeatable column, reputation is repeatable
if (info.reward_reputation > 0) {
missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, LWOOBJID_EMPTY, "", info.reward_reputation);
auto* const character = entity->GetComponent<CharacterComponent>();
if (character) {
character->SetReputation(character->GetReputation() + info.reward_reputation);
GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress());
}
}
if (m_Completions > 0) { if (m_Completions > 0) {
std::vector<std::pair<LOT, uint32_t>> items; std::vector<std::pair<LOT, uint32_t>> items;
@ -532,15 +542,6 @@ void Mission::YieldRewards() {
modelInventory->SetSize(modelInventory->GetSize() + info.reward_bankinventory); modelInventory->SetSize(modelInventory->GetSize() + info.reward_bankinventory);
} }
if (info.reward_reputation > 0) {
missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, 0L, "", info.reward_reputation);
auto character = entity->GetComponent<CharacterComponent>();
if (character) {
character->SetReputation(character->GetReputation() + info.reward_reputation);
GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress());
}
}
if (info.reward_maxhealth > 0) { if (info.reward_maxhealth > 0) {
destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast<float>(info.reward_maxhealth), true); destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast<float>(info.reward_maxhealth), true);
} }

View File

@ -8,6 +8,7 @@
#include "SlashCommandHandler.h" #include "SlashCommandHandler.h"
#include <iomanip> #include <iomanip>
#include <ranges>
#include "DEVGMCommands.h" #include "DEVGMCommands.h"
#include "GMGreaterThanZeroCommands.h" #include "GMGreaterThanZeroCommands.h"
@ -30,7 +31,6 @@ void SlashCommandHandler::RegisterCommand(Command command) {
} }
for (const auto& alias : command.aliases) { for (const auto& alias : command.aliases) {
LOG_DEBUG("Registering command %s", alias.c_str());
auto [_, success] = RegisteredCommands.try_emplace(alias, command); auto [_, success] = RegisteredCommands.try_emplace(alias, command);
// Don't allow duplicate commands // Don't allow duplicate commands
if (!success) { if (!success) {
@ -60,11 +60,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity*
if (commandHandle.requiredLevel > eGameMasterLevel::CIVILIAN) Database::Get()->InsertSlashCommandUsage(entity->GetObjectID(), input); if (commandHandle.requiredLevel > eGameMasterLevel::CIVILIAN) Database::Get()->InsertSlashCommandUsage(entity->GetObjectID(), input);
commandHandle.handle(entity, sysAddr, args); commandHandle.handle(entity, sysAddr, args);
} else if (entity->GetGMLevel() != eGameMasterLevel::CIVILIAN) { } else if (entity->GetGMLevel() != eGameMasterLevel::CIVILIAN) {
// We don't need to tell civilians they aren't high enough level
error = "You are not high enough GM level to use \"" + command + "\""; error = "You are not high enough GM level to use \"" + command + "\"";
} }
} else if (entity->GetGMLevel() == eGameMasterLevel::CIVILIAN) { } else if (entity->GetGMLevel() == eGameMasterLevel::CIVILIAN) {
// We don't need to tell civilians commands don't exist
error = "Command " + command + " does not exist!"; error = "Command " + command + " does not exist!";
} }
@ -75,32 +73,74 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity*
void GMZeroCommands::Help(Entity* entity, const SystemAddress& sysAddr, const std::string args) { void GMZeroCommands::Help(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
std::ostringstream feedback; std::ostringstream feedback;
if (args.empty()) { constexpr size_t pageSize = 10;
feedback << "----- Commands -----";
for (const auto& [alias, command] : CommandInfos) { std::string trimmedArgs = args;
// TODO: Limit displaying commands based on GM level they require trimmedArgs.erase(trimmedArgs.begin(), std::find_if_not(trimmedArgs.begin(), trimmedArgs.end(), [](unsigned char ch) {
if (command.requiredLevel > entity->GetGMLevel()) continue; return std::isspace(ch);
LOG("Help command: %s", alias.c_str()); }));
feedback << "\n/" << alias << ": " << command.help; trimmedArgs.erase(std::find_if_not(trimmedArgs.rbegin(), trimmedArgs.rend(), [](unsigned char ch) {
return std::isspace(ch);
}).base(), trimmedArgs.end());
std::optional<uint32_t> parsedPage = GeneralUtils::TryParse<uint32_t>(trimmedArgs);
if (trimmedArgs.empty() || parsedPage.has_value()) {
size_t page = parsedPage.value_or(1);
std::map<std::string, Command> accessibleCommands;
for (const auto& [commandName, command] : CommandInfos) {
if (command.requiredLevel <= entity->GetGMLevel()) {
accessibleCommands.emplace(commandName, command);
}
}
size_t totalPages = (accessibleCommands.size() + pageSize - 1) / pageSize;
if (page < 1 || page > totalPages) {
feedback << "Invalid page number. Total pages: " << totalPages;
GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedback.str()));
return;
}
auto it = accessibleCommands.begin();
std::advance(it, (page - 1) * pageSize);
size_t endIdx = std::min(page * pageSize, accessibleCommands.size());
feedback << "----- Commands (Page " << page << " of " << totalPages << ") -----";
for (size_t i = (page - 1) * pageSize; i < endIdx; ++i, ++it) {
feedback << "\n/" << it->first << ": " << it->second.help;
}
const auto feedbackStr = feedback.str();
if (!feedbackStr.empty()) {
GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr));
}
return;
}
auto it = std::ranges::find_if(CommandInfos, [&trimmedArgs](const auto& pair) {
return std::ranges::find(pair.second.aliases, trimmedArgs) != pair.second.aliases.end();
});
if (it != CommandInfos.end() && entity->GetGMLevel() >= it->second.requiredLevel) {
const auto& command = it->second;
feedback << "----- " << it->first << " Info -----\n";
feedback << command.info << "\n";
if (command.aliases.size() > 1) {
feedback << "Aliases: ";
for (size_t i = 0; i < command.aliases.size(); ++i) {
if (i > 0) feedback << ", ";
feedback << command.aliases[i];
}
} }
} else { } else {
auto it = CommandInfos.find(args); feedback << "Command not found.";
if (it != CommandInfos.end() && entity->GetGMLevel() >= it->second.requiredLevel) {
feedback << "----- " << args << " -----\n";
feedback << it->second.info;
if (it->second.aliases.size() > 1) {
feedback << "\nAliases: ";
for (size_t i = 0; i < it->second.aliases.size(); i++) {
if (i > 0) feedback << ", ";
feedback << it->second.aliases[i];
}
}
} else if (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) {
feedback << "Command " << std::quoted(args) << " does not exist!";
}
} }
const auto feedbackStr = feedback.str(); const auto feedbackStr = feedback.str();
if (!feedbackStr.empty()) GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); if (!feedbackStr.empty()) {
GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr));
}
} }
void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) { void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) {
@ -888,6 +928,15 @@ void SlashCommandHandler::Startup() {
}; };
RegisterCommand(FindPlayerCommand); RegisterCommand(FindPlayerCommand);
Command SpectateCommand{
.help = "Spectate a player",
.info = "Specify a player name to spectate. They must be in the same world as you. Leave blank to stop spectating",
.aliases = { "spectate", "follow" },
.handle = GMGreaterThanZeroCommands::Spectate,
.requiredLevel = eGameMasterLevel::JUNIOR_MODERATOR
};
RegisterCommand(SpectateCommand);
// Register GM Zero Commands // Register GM Zero Commands
Command HelpCommand{ Command HelpCommand{
@ -1007,4 +1056,383 @@ void SlashCommandHandler::Startup() {
}; };
RegisterCommand(InstanceInfoCommand); RegisterCommand(InstanceInfoCommand);
//Commands that are handled by the client
Command faqCommand{
.help = "Show the LU FAQ Page",
.info = "Show the LU FAQ Page",
.aliases = {"faq","faqs"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(faqCommand);
Command teamChatCommand{
.help = "Send a message to your teammates.",
.info = "Send a message to your teammates.",
.aliases = {"team","t"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(teamChatCommand);
Command showStoreCommand{
.help = "Show the LEGO shop page.",
.info = "Show the LEGO shop page.",
.aliases = {"shop","store"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(showStoreCommand);
Command minigamesCommand{
.help = "Show the LEGO minigames page!",
.info = "Show the LEGO minigames page!",
.aliases = {"minigames"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(minigamesCommand);
Command forumsCommand{
.help = "Show the LU Forums!",
.info = "Show the LU Forums!",
.aliases = {"forums"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(forumsCommand);
Command exitGameCommand{
.help = "Exit to desktop",
.info = "Exit to desktop",
.aliases = {"exit","quit"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(exitGameCommand);
Command thumbsUpCommand{
.help = "Oh, yeah!",
.info = "Oh, yeah!",
.aliases = {"thumb","thumbs","thumbsup"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(thumbsUpCommand);
Command victoryCommand{
.help = "Victory!",
.info = "Victory!",
.aliases = {"victory!"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(victoryCommand);
Command backflipCommand{
.help = "Do a flip!",
.info = "Do a flip!",
.aliases = {"backflip"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(backflipCommand);
Command clapCommand{
.help = "A round of applause!",
.info = "A round of applause!",
.aliases = {"clap"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(clapCommand);
Command logoutCharacterCommand{
.help = "Returns you to the character select screen.",
.info = "Returns you to the character select screen.",
.aliases = {"camp","logoutcharacter"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(logoutCharacterCommand);
Command sayCommand{
.help = "Say something outloud so that everyone can hear you",
.info = "Say something outloud so that everyone can hear you",
.aliases = {"s","say"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(sayCommand);
Command whisperCommand{
.help = "Send a private message to another player.",
.info = "Send a private message to another player.",
.aliases = {"tell","w","whisper"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(whisperCommand);
Command locationCommand{
.help = "Output your current location on the map to the chat box.",
.info = "Output your current location on the map to the chat box.",
.aliases = {"loc","locate","location"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(locationCommand);
Command logoutCommand{
.help = "Returns you to the login screen.",
.info = "Returns you to the login screen.",
.aliases = {"logout","logoutaccount"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(logoutCommand);
Command shrugCommand{
.help = "I dunno...",
.info = "I dunno...",
.aliases = {"shrug"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(shrugCommand);
Command leaveTeamCommand{
.help = "Leave your current team.",
.info = "Leave your current team.",
.aliases = {"leave","leaveteam","teamleave","tleave"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(leaveTeamCommand);
Command teamLootTypeCommand{
.help = "[rr|ffa] Set the loot for your current team (round-robin/free for all).",
.info = "[rr|ffa] Set the loot for your current team (round-robin/free for all).",
.aliases = {"setloot","teamsetloot","tloot","tsetloot"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(teamLootTypeCommand);
Command removeFriendCommand{
.help = "[name] Removes a player from your friends list.",
.info = "[name] Removes a player from your friends list.",
.aliases = {"removefriend"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(removeFriendCommand);
Command yesCommand{
.help = "Aye aye, captain!",
.info = "Aye aye, captain!",
.aliases = {"yes"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(yesCommand);
Command teamInviteCommand{
.help = "[name] Invite a player to your team.",
.info = "[name] Invite a player to your team.",
.aliases = {"invite","inviteteam","teaminvite","tinvite"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(teamInviteCommand);
Command danceCommand{
.help = "Dance 'til you can't dance no more.",
.info = "Dance 'til you can't dance no more.",
.aliases = {"dance"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(danceCommand);
Command sighCommand{
.help = "Another day, another brick.",
.info = "Another day, another brick.",
.aliases = {"sigh"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(sighCommand);
Command recommendedOptionsCommand{
.help = "Sets the recommended performance options in the cfg file",
.info = "Sets the recommended performance options in the cfg file",
.aliases = {"recommendedperfoptions"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(recommendedOptionsCommand);
Command setTeamLeaderCommand{
.help = "[name] Set the leader for your current team.",
.info = "[name] Set the leader for your current team.",
.aliases = {"leader","setleader","teamsetleader","tleader","tsetleader"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(setTeamLeaderCommand);
Command cringeCommand{
.help = "I don't even want to talk about it...",
.info = "I don't even want to talk about it...",
.aliases = {"cringe"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(cringeCommand);
Command talkCommand{
.help = "Jibber Jabber",
.info = "Jibber Jabber",
.aliases = {"talk"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(talkCommand);
Command cancelQueueCommand{
.help = "Cancel Your position in the queue if you are in one.",
.info = "Cancel Your position in the queue if you are in one.",
.aliases = {"cancelqueue"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(cancelQueueCommand);
Command lowPerformanceCommand{
.help = "Sets the default low-spec performance options in the cfg file",
.info = "Sets the default low-spec performance options in the cfg file",
.aliases = {"perfoptionslow"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(lowPerformanceCommand);
Command kickFromTeamCommand{
.help = "[name] Kick a player from your current team.",
.info = "[name] Kick a player from your current team.",
.aliases = {"kick","kickplayer","teamkickplayer","tkick","tkickplayer"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(kickFromTeamCommand);
Command thanksCommand{
.help = "Express your gratitude for another.",
.info = "Express your gratitude for another.",
.aliases = {"thanks"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(thanksCommand);
Command waveCommand{
.help = "Wave to other players.",
.info = "Wave to other players.",
.aliases = {"wave"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(waveCommand);
Command whyCommand{
.help = "Why|!?!!",
.info = "Why|!?!!",
.aliases = {"why"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(whyCommand);
Command midPerformanceCommand{
.help = "Sets the default medium-spec performance options in the cfg file",
.info = "Sets the default medium-spec performance options in the cfg file",
.aliases = {"perfoptionsmid"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(midPerformanceCommand);
Command highPerformanceCommand{
.help = "Sets the default high-spec performance options in the cfg file",
.info = "Sets the default high-spec performance options in the cfg file",
.aliases = {"perfoptionshigh"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(highPerformanceCommand);
Command gaspCommand{
.help = "Oh my goodness!",
.info = "Oh my goodness!",
.aliases = {"gasp"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(gaspCommand);
Command ignoreCommand{
.help = "[name] Add a player to your ignore list.",
.info = "[name] Add a player to your ignore list.",
.aliases = {"addignore"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(ignoreCommand);
Command addFriendCommand{
.help = "[name] Add a player to your friends list.",
.info = "[name] Add a player to your friends list.",
.aliases = {"addfriend"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(addFriendCommand);
Command cryCommand{
.help = "Show everyone your 'Aw' face.",
.info = "Show everyone your 'Aw' face.",
.aliases = {"cry"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(cryCommand);
Command giggleCommand{
.help = "A good little chuckle",
.info = "A good little chuckle",
.aliases = {"giggle"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(giggleCommand);
Command saluteCommand{
.help = "For those about to build...",
.info = "For those about to build...",
.aliases = {"salute"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(saluteCommand);
Command removeIgnoreCommand{
.help = "[name] Removes a player from your ignore list.",
.info = "[name] Removes a player from your ignore list.",
.aliases = {"removeIgnore"},
.handle = GMZeroCommands::ClientHandled,
.requiredLevel = eGameMasterLevel::CIVILIAN
};
RegisterCommand(removeIgnoreCommand);
} }

View File

@ -322,4 +322,19 @@ namespace GMGreaterThanZeroCommands {
request.Serialize(bitStream); request.Serialize(bitStream);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
} }
void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
if (args.empty()) {
GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, entity->GetObjectID());
return;
}
auto player = PlayerManager::GetPlayer(args);
if (!player) {
GameMessages::SendSlashCommandFeedbackText(entity, u"Player not found");
return;
}
GameMessages::SendSlashCommandFeedbackText(entity, u"Spectating Player");
GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, player->GetObjectID());
}
} }

View File

@ -15,6 +15,7 @@ namespace GMGreaterThanZeroCommands {
void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args); void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args); void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args); void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args);
} }
#endif //!GMGREATERTHANZEROCOMMANDS_H #endif //!GMGREATERTHANZEROCOMMANDS_H

View File

@ -224,5 +224,9 @@ namespace GMZeroCommands {
ChatPackets::SendSystemMessage(sysAddr, u"Map: " + (GeneralUtils::to_u16string(zoneId.GetMapID())) + u"\nClone: " + (GeneralUtils::to_u16string(zoneId.GetCloneID())) + u"\nInstance: " + (GeneralUtils::to_u16string(zoneId.GetInstanceID()))); ChatPackets::SendSystemMessage(sysAddr, u"Map: " + (GeneralUtils::to_u16string(zoneId.GetMapID())) + u"\nClone: " + (GeneralUtils::to_u16string(zoneId.GetCloneID())) + u"\nInstance: " + (GeneralUtils::to_u16string(zoneId.GetInstanceID())));
} }
//For client side commands
void ClientHandled(Entity* entity, const SystemAddress& sysAddr, const std::string args) {}
}; };

View File

@ -15,6 +15,7 @@ namespace GMZeroCommands {
void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args); void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args); void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args); void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ClientHandled(Entity* entity, const SystemAddress& sysAddr, const std::string args);
} }
#endif //!GMZEROCOMMANDS_H #endif //!GMZEROCOMMANDS_H

View File

@ -285,7 +285,6 @@ void ParseXml(const std::string& file) {
} }
if (zoneID.value() != currentZoneID) { if (zoneID.value() != currentZoneID) {
LOG_DEBUG("Skipping (%s) %i location because it is in %i and not the current zone (%i)", name, lot, zoneID.value(), currentZoneID);
continue; continue;
} }

View File

@ -40,6 +40,7 @@
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "Start.h" #include "Start.h"
#include "Server.h" #include "Server.h"
#include "CDZoneTableTable.h"
namespace Game { namespace Game {
Logger* logger = nullptr; Logger* logger = nullptr;
@ -277,6 +278,17 @@ int main(int argc, char** argv) {
PersistentIDManager::Initialize(); PersistentIDManager::Initialize();
Game::im = new InstanceManager(Game::logger, Game::server->GetIP()); Game::im = new InstanceManager(Game::logger, Game::server->GetIP());
//Get CDClient initial information
try {
CDClientManager::LoadValuesFromDatabase();
} catch (CppSQLite3Exception& e) {
LOG("Failed to initialize CDServer SQLite Database");
LOG("May be caused by corrupted file: %s", (Game::assetManager->GetResPath() / "CDServer.sqlite").string().c_str());
LOG("Error: %s", e.errorMessage());
LOG("Error Code: %i", e.errorCode());
return EXIT_FAILURE;
}
//Depending on the config, start up servers: //Depending on the config, start up servers:
if (Game::config->GetValue("prestart_servers") != "0") { if (Game::config->GetValue("prestart_servers") != "0") {
StartChatServer(); StartChatServer();
@ -382,6 +394,7 @@ int main(int argc, char** argv) {
} }
void HandlePacket(Packet* packet) { void HandlePacket(Packet* packet) {
if (packet->length < 1) return;
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION) {
LOG("A server has disconnected"); LOG("A server has disconnected");

View File

@ -112,6 +112,31 @@ void dNavMesh::LoadNavmesh() {
m_NavMesh = mesh; m_NavMesh = mesh;
} }
NiPoint3 dNavMesh::NearestPoint(const NiPoint3& location, const float halfExtent) const {
NiPoint3 toReturn = location;
if (m_NavMesh != nullptr) {
float pos[3];
pos[0] = location.x;
pos[1] = location.y;
pos[2] = location.z;
dtPolyRef nearestRef = 0;
float polyPickExt[3] = { halfExtent, halfExtent, halfExtent };
float nearestPoint[3] = { 0.0f, 0.0f, 0.0f };
dtQueryFilter filter{};
auto hasPoly = m_NavQuery->findNearestPoly(pos, polyPickExt, &filter, &nearestRef, nearestPoint);
if (hasPoly != DT_SUCCESS) {
toReturn = location;
} else {
toReturn.x = nearestPoint[0];
toReturn.y = nearestPoint[1];
toReturn.z = nearestPoint[2];
}
}
return toReturn;
}
float dNavMesh::GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight) const { float dNavMesh::GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight) const {
if (m_NavMesh == nullptr) { if (m_NavMesh == nullptr) {
return location.y; return location.y;

View File

@ -29,7 +29,7 @@ public:
*/ */
float GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight = 32.0f) const; float GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight = 32.0f) const;
std::vector<NiPoint3> GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed = 10.0f); std::vector<NiPoint3> GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed = 10.0f);
NiPoint3 NearestPoint(const NiPoint3& location, const float halfExtent = 32.0f) const;
bool IsNavmeshLoaded() { return m_NavMesh != nullptr; } bool IsNavmeshLoaded() { return m_NavMesh != nullptr; }
private: private:

View File

@ -5,6 +5,7 @@
#include "MessageIdentifiers.h" #include "MessageIdentifiers.h"
#include "BitStream.h" #include "BitStream.h"
#include <string> #include <string>
#include <algorithm>
enum class eConnectionType : uint16_t; enum class eConnectionType : uint16_t;

View File

@ -15,6 +15,7 @@
#include "SkillComponent.h" #include "SkillComponent.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "RenderComponent.h" #include "RenderComponent.h"
#include "PlayerManager.h"
#include <vector> #include <vector>
@ -53,11 +54,13 @@ void BossSpiderQueenEnemyServer::OnStartup(Entity* self) {
void BossSpiderQueenEnemyServer::OnDie(Entity* self, Entity* killer) { void BossSpiderQueenEnemyServer::OnDie(Entity* self, Entity* killer) {
if (Game::zoneManager->GetZoneID().GetMapID() == instanceZoneID && killer) { if (Game::zoneManager->GetZoneID().GetMapID() == instanceZoneID && killer) {
auto* missionComponent = killer->GetComponent<MissionComponent>(); for (const auto& player : PlayerManager::GetAllPlayers()) {
if (missionComponent == nullptr) auto* missionComponent = player->GetComponent<MissionComponent>();
return; if (missionComponent == nullptr)
return;
missionComponent->CompleteMission(instanceMissionID); missionComponent->CompleteMission(instanceMissionID);
}
} }
// There is suppose to be a 0.1 second delay here but that may be admitted? // There is suppose to be a 0.1 second delay here but that may be admitted?

View File

@ -14,6 +14,7 @@
#include "AgShipPlayerDeathTrigger.h" #include "AgShipPlayerDeathTrigger.h"
#include "AgShipPlayerShockServer.h" #include "AgShipPlayerShockServer.h"
#include "AgSpaceStuff.h" #include "AgSpaceStuff.h"
#include "AgShipShake.h"
#include "AgImagSmashable.h" #include "AgImagSmashable.h"
#include "NpcNpSpacemanBob.h" #include "NpcNpSpacemanBob.h"
#include "StoryBoxInteractServer.h" #include "StoryBoxInteractServer.h"
@ -341,6 +342,7 @@ namespace {
{ "scripts\\ai\\AG\\L_AG_SHIP_PLAYER_DEATH_TRIGGER.lua", []() { return new AgShipPlayerDeathTrigger(); } }, { "scripts\\ai\\AG\\L_AG_SHIP_PLAYER_DEATH_TRIGGER.lua", []() { return new AgShipPlayerDeathTrigger(); } },
{"scripts\\ai\\NP\\L_NPC_NP_SPACEMAN_BOB.lua", []() { return new NpcNpSpacemanBob(); } }, {"scripts\\ai\\NP\\L_NPC_NP_SPACEMAN_BOB.lua", []() { return new NpcNpSpacemanBob(); } },
{"scripts\\ai\\AG\\L_AG_SPACE_STUFF.lua", []() { return new AgSpaceStuff();} }, {"scripts\\ai\\AG\\L_AG_SPACE_STUFF.lua", []() { return new AgSpaceStuff();} },
{"scripts\\ai\\AG\\L_AG_SHIP_SHAKE.lua", []() { return new AgShipShake();}},
{"scripts\\ai\\AG\\L_AG_SHIP_PLAYER_SHOCK_SERVER.lua", []() { return new AgShipPlayerShockServer();} }, {"scripts\\ai\\AG\\L_AG_SHIP_PLAYER_SHOCK_SERVER.lua", []() { return new AgShipPlayerShockServer();} },
{"scripts\\ai\\AG\\L_AG_IMAG_SMASHABLE.lua", []() { return new AgImagSmashable();} }, {"scripts\\ai\\AG\\L_AG_IMAG_SMASHABLE.lua", []() { return new AgImagSmashable();} },
{"scripts\\02_server\\Map\\General\\L_STORY_BOX_INTERACT_SERVER.lua", []() { return new StoryBoxInteractServer();} }, {"scripts\\02_server\\Map\\General\\L_STORY_BOX_INTERACT_SERVER.lua", []() { return new StoryBoxInteractServer();} },
@ -580,6 +582,7 @@ namespace {
{"scripts\\02_server\\Map\\AM\\L_SKULLKIN_DRILL_STAND.lua", []() {return new AmSkullkinDrillStand();}}, {"scripts\\02_server\\Map\\AM\\L_SKULLKIN_DRILL_STAND.lua", []() {return new AmSkullkinDrillStand();}},
{"scripts\\02_server\\Map\\AM\\L_SKULLKIN_TOWER.lua", []() {return new AmSkullkinTower();}}, {"scripts\\02_server\\Map\\AM\\L_SKULLKIN_TOWER.lua", []() {return new AmSkullkinTower();}},
{"scripts\\02_server\\Enemy\\AM\\L_AM_NAMED_DARKLING_DRAGON.lua", []() {return new AmDarklingDragon();}}, {"scripts\\02_server\\Enemy\\AM\\L_AM_NAMED_DARKLING_DRAGON.lua", []() {return new AmDarklingDragon();}},
{"scripts\\02_server\\Enemy\\AM\\L_AM_DARKLING_DRAGON.lua", []() {return new AmDarklingDragon();}},
{"scripts\\02_server\\Enemy\\AM\\L_AM_DARKLING_APE.lua", []() {return new BaseEnemyApe();}}, {"scripts\\02_server\\Enemy\\AM\\L_AM_DARKLING_APE.lua", []() {return new BaseEnemyApe();}},
{"scripts\\02_server\\Map\\AM\\L_BLUE_X.lua", []() {return new AmBlueX();}}, {"scripts\\02_server\\Map\\AM\\L_BLUE_X.lua", []() {return new AmBlueX();}},
{"scripts\\02_server\\Map\\AM\\L_TEAPOT_SERVER.lua", []() {return new AmTeapotServer();}}, {"scripts\\02_server\\Map\\AM\\L_TEAPOT_SERVER.lua", []() {return new AmTeapotServer();}},
@ -654,6 +657,7 @@ namespace {
//Pickups //Pickups
{"scripts\\ai\\SPEC\\L_SPECIAL_1_BRONZE-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(1);}}, {"scripts\\ai\\SPEC\\L_SPECIAL_1_BRONZE-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(1);}},
{"scripts\\ai\\SPEC\\L_SPECIAL_1_GOLD-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(10000);}},
{"scripts\\ai\\SPEC\\L_SPECIAL_1_SILVER-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(100);}}, {"scripts\\ai\\SPEC\\L_SPECIAL_1_SILVER-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(100);}},
{"scripts\\ai\\SPEC\\L_SPECIAL_10_BRONZE-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(10);}}, {"scripts\\ai\\SPEC\\L_SPECIAL_10_BRONZE-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(10);}},
{"scripts\\ai\\SPEC\\L_SPECIAL_10_GOLD-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(100000);}}, {"scripts\\ai\\SPEC\\L_SPECIAL_10_GOLD-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(100000);}},
@ -700,7 +704,8 @@ CppScripts::Script* const CppScripts::GetScript(Entity* parent, const std::strin
(scriptName == "scripts\\02_server\\Enemy\\General\\L_BASE_ENEMY_SPIDERLING.lua") || (scriptName == "scripts\\02_server\\Enemy\\General\\L_BASE_ENEMY_SPIDERLING.lua") ||
(scriptName == "scripts\\ai\\FV\\L_ACT_NINJA_STUDENT.lua") || (scriptName == "scripts\\ai\\FV\\L_ACT_NINJA_STUDENT.lua") ||
(scriptName == "scripts\\ai\\WILD\\L_WILD_GF_FROG.lua") || (scriptName == "scripts\\ai\\WILD\\L_WILD_GF_FROG.lua") ||
(scriptName == "scripts\\empty.lua") (scriptName == "scripts\\empty.lua") ||
(scriptName == "scripts\\ai\\AG\\L_AG_SENTINEL_GUARD.lua")
)) LOG_DEBUG("LOT %i attempted to load CppScript for '%s', but returned InvalidScript.", parent->GetLOT(), scriptName.c_str()); )) LOG_DEBUG("LOT %i attempted to load CppScript for '%s', but returned InvalidScript.", parent->GetLOT(), scriptName.c_str());
} }

View File

@ -0,0 +1,81 @@
#include "AgShipShake.h"
#include "EntityInfo.h"
#include "GeneralUtils.h"
#include "GameMessages.h"
#include "EntityManager.h"
#include "RenderComponent.h"
#include "Entity.h"
void AgShipShake::OnStartup(Entity* self) {
EntityInfo info{};
info.pos = { -418, 585, -30 };
info.lot = 33;
info.spawnerID = self->GetObjectID();
auto* ref = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(ref);
self->SetVar(u"ShakeObject", ref->GetObjectID());
self->AddTimer("ShipShakeIdle", 2.0f);
self->SetVar(u"RandomTime", 10);
}
void AgShipShake::OnTimerDone(Entity* self, std::string timerName) {
auto* shipFxObject = GetEntityInGroup(ShipFX);
auto* shipFxObject2 = GetEntityInGroup(ShipFX2);
auto* debrisObject = GetEntityInGroup(DebrisFX);
if (timerName == "ShipShakeIdle") {
auto* ref = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"ShakeObject"));
const auto randomTime = self->GetVar<int>(u"RandomTime");
auto time = GeneralUtils::GenerateRandomNumber<int>(0, randomTime + 1);
if (time < randomTime / 2) {
time += randomTime / 2;
}
self->AddTimer("ShipShakeIdle", static_cast<float>(time));
if (ref)
GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(ref, FXName, ref->GetObjectID(), 500.0f);
if (debrisObject)
GameMessages::SendPlayFXEffect(debrisObject, -1, u"DebrisFall", "Debris", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
const auto randomFx = GeneralUtils::GenerateRandomNumber<int>(0, 3);
if (shipFxObject) {
std::string effectType = "shipboom" + std::to_string(randomFx);
GameMessages::SendPlayFXEffect(shipFxObject, 559, GeneralUtils::ASCIIToUTF16(effectType), "FX", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
}
self->AddTimer("ShipShakeExplode", 5.0f);
if (shipFxObject2)
RenderComponent::PlayAnimation(shipFxObject2, u"explosion");
} else if (timerName == "ShipShakeExplode") {
if (shipFxObject)
RenderComponent::PlayAnimation(shipFxObject, u"idle");
if (shipFxObject2)
RenderComponent::PlayAnimation(shipFxObject2, u"idle");
}
}
Entity* AgShipShake::GetEntityInGroup(const std::string& group) {
auto entities = Game::entityManager->GetEntitiesInGroup(group);
Entity* en = nullptr;
for (auto entity : entities) {
if (entity) {
en = entity;
break;
}
}
return en;
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "CppScripts.h"
class AgShipShake : public CppScripts::Script {
public:
void OnStartup(Entity* self) override;
void OnTimerDone(Entity* self, std::string timerName) override;
std::string DebrisFX = "DebrisFX";
std::string ShipFX = "ShipFX";
std::string ShipFX2 = "ShipFX2";
std::u16string FXName = u"camshake-bridge";
private:
Entity* GetEntityInGroup(const std::string& group);
};

View File

@ -8,21 +8,6 @@
void AgSpaceStuff::OnStartup(Entity* self) { void AgSpaceStuff::OnStartup(Entity* self) {
self->AddTimer("FloaterScale", 5.0f); self->AddTimer("FloaterScale", 5.0f);
EntityInfo info{};
info.pos = { -418, 585, -30 };
info.lot = 33;
info.spawnerID = self->GetObjectID();
auto* ref = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(ref);
self->SetVar(u"ShakeObject", ref->GetObjectID());
self->AddTimer("ShipShakeIdle", 2.0f);
self->SetVar(u"RandomTime", 10);
} }
void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) { void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) {
@ -37,70 +22,5 @@ void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) {
RenderComponent::PlayAnimation(self, u"path_0" + (GeneralUtils::to_u16string(pathType))); RenderComponent::PlayAnimation(self, u"path_0" + (GeneralUtils::to_u16string(pathType)));
self->AddTimer("FloaterScale", randTime); self->AddTimer("FloaterScale", randTime);
} else if (timerName == "ShipShakeExplode") {
DoShake(self, true);
} else if (timerName == "ShipShakeIdle") {
DoShake(self, false);
} }
} }
void AgSpaceStuff::DoShake(Entity* self, bool explodeIdle) {
if (!explodeIdle) {
auto* ref = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"ShakeObject"));
const auto randomTime = self->GetVar<int>(u"RandomTime");
auto time = GeneralUtils::GenerateRandomNumber<int>(0, randomTime + 1);
if (time < randomTime / 2) {
time += randomTime / 2;
}
self->AddTimer("ShipShakeIdle", static_cast<float>(time));
if (ref)
GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(ref, FXName, ref->GetObjectID(), 500.0f);
auto* debrisObject = GetEntityInGroup(DebrisFX);
if (debrisObject)
GameMessages::SendPlayFXEffect(debrisObject, -1, u"DebrisFall", "Debris", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
const auto randomFx = GeneralUtils::GenerateRandomNumber<int>(0, 3);
auto* shipFxObject = GetEntityInGroup(ShipFX);
if (shipFxObject) {
std::string effectType = "shipboom" + std::to_string(randomFx);
GameMessages::SendPlayFXEffect(shipFxObject, 559, GeneralUtils::ASCIIToUTF16(effectType), "FX", LWOOBJID_EMPTY, 1.0f, 1.0f, true);
}
self->AddTimer("ShipShakeExplode", 5.0f);
auto* shipFxObject2 = GetEntityInGroup(ShipFX2);
if (shipFxObject2)
RenderComponent::PlayAnimation(shipFxObject2, u"explosion");
} else {
auto* shipFxObject = GetEntityInGroup(ShipFX);
auto* shipFxObject2 = GetEntityInGroup(ShipFX2);
if (shipFxObject)
RenderComponent::PlayAnimation(shipFxObject, u"idle");
if (shipFxObject2)
RenderComponent::PlayAnimation(shipFxObject2, u"idle");
}
}
Entity* AgSpaceStuff::GetEntityInGroup(const std::string& group) {
auto entities = Game::entityManager->GetEntitiesInGroup(group);
Entity* en = nullptr;
for (auto entity : entities) {
if (entity) {
en = entity;
break;
}
}
return en;
}

View File

@ -5,14 +5,5 @@ class AgSpaceStuff : public CppScripts::Script {
public: public:
void OnStartup(Entity* self); void OnStartup(Entity* self);
void OnTimerDone(Entity* self, std::string timerName); void OnTimerDone(Entity* self, std::string timerName);
void DoShake(Entity* self, bool explodeIdle);
std::string DebrisFX = "DebrisFX";
std::string ShipFX = "ShipFX";
std::string ShipFX2 = "ShipFX2";
std::u16string FXName = u"camshake-bridge";
private:
Entity* GetEntityInGroup(const std::string& group);
}; };

View File

@ -1,6 +1,7 @@
set(DSCRIPTS_SOURCES_AI_AG set(DSCRIPTS_SOURCES_AI_AG
"AgShipPlayerDeathTrigger.cpp" "AgShipPlayerDeathTrigger.cpp"
"AgSpaceStuff.cpp" "AgSpaceStuff.cpp"
"AgShipShake.cpp"
"AgShipPlayerShockServer.cpp" "AgShipPlayerShockServer.cpp"
"AgImagSmashable.cpp" "AgImagSmashable.cpp"
"ActSharkPlayerDeathTrigger.cpp" "ActSharkPlayerDeathTrigger.cpp"

View File

@ -529,6 +529,7 @@ int main(int argc, char** argv) {
} }
void HandlePacketChat(Packet* packet) { void HandlePacketChat(Packet* packet) {
if (packet->length < 1) return;
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
LOG("Lost our connection to chat, zone(%i), instance(%i)", Game::server->GetZoneID(), Game::server->GetInstanceID()); LOG("Lost our connection to chat, zone(%i), instance(%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
@ -542,7 +543,7 @@ void HandlePacketChat(Packet* packet) {
chatConnected = true; chatConnected = true;
} }
if (packet->data[0] == ID_USER_PACKET_ENUM) { if (packet->data[0] == ID_USER_PACKET_ENUM && packet->length >= 4) {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) { if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
switch (static_cast<eChatMessageType>(packet->data[3])) { switch (static_cast<eChatMessageType>(packet->data[3])) {
case eChatMessageType::WORLD_ROUTE_PACKET: { case eChatMessageType::WORLD_ROUTE_PACKET: {
@ -557,8 +558,9 @@ void HandlePacketChat(Packet* packet) {
//Write our stream outwards: //Write our stream outwards:
CBITSTREAM; CBITSTREAM;
for (BitSize_t i = 0; i < inStream.GetNumberOfBytesUsed(); i++) { unsigned char data;
bitStream.Write(packet->data[i + 16]); //16 bytes == header + playerID to skip while (inStream.Read(data)) {
bitStream.Write(data);
} }
SEND_PACKET; //send routed packet to player SEND_PACKET; //send routed packet to player
@ -659,7 +661,7 @@ void HandlePacketChat(Packet* packet) {
} }
void HandleMasterPacket(Packet* packet) { void HandleMasterPacket(Packet* packet) {
if (packet->length < 2) return;
if (static_cast<eConnectionType>(packet->data[1]) != eConnectionType::MASTER || packet->length < 4) return; if (static_cast<eConnectionType>(packet->data[1]) != eConnectionType::MASTER || packet->length < 4) return;
switch (static_cast<eMasterMessageType>(packet->data[3])) { switch (static_cast<eMasterMessageType>(packet->data[3])) {
case eMasterMessageType::REQUEST_PERSISTENT_ID_RESPONSE: { case eMasterMessageType::REQUEST_PERSISTENT_ID_RESPONSE: {
@ -785,6 +787,7 @@ void HandleMasterPacket(Packet* packet) {
} }
void HandlePacket(Packet* packet) { void HandlePacket(Packet* packet) {
if (packet->length < 1) return;
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
auto user = UserManager::Instance()->GetUser(packet->systemAddress); auto user = UserManager::Instance()->GetUser(packet->systemAddress);
if (!user) return; if (!user) return;
@ -1207,8 +1210,8 @@ void HandlePacket(Packet* packet) {
//Now write the rest of the data: //Now write the rest of the data:
auto data = inStream.GetData(); auto data = inStream.GetData();
for (uint32_t i = 0; i < size; ++i) { for (uint32_t i = 23; i - 23 < size && i < packet->length; ++i) {
bitStream.Write(data[i + 23]); bitStream.Write(data[i]);
} }
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, Game::chatSysAddr, false); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, Game::chatSysAddr, false);

View File

@ -23,6 +23,11 @@ if(NOT WIN32)
target_include_directories(bcrypt PRIVATE "libbcrypt/include/bcrypt") target_include_directories(bcrypt PRIVATE "libbcrypt/include/bcrypt")
endif() endif()
# Need to define this on Clang and GNU for 'strdup' support
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
target_compile_definitions(bcrypt PRIVATE "_POSIX_C_SOURCE=200809L")
endif()
target_include_directories(bcrypt INTERFACE "libbcrypt/include") target_include_directories(bcrypt INTERFACE "libbcrypt/include")
target_include_directories(bcrypt PRIVATE "libbcrypt/src") target_include_directories(bcrypt PRIVATE "libbcrypt/src")