Merge branch 'DarkflameUniverse:main' into MSVCCompilerFlags

This commit is contained in:
jadebenn 2024-02-08 21:41:50 -06:00 committed by GitHub
commit 62fb8ad071
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 273 additions and 335 deletions

View File

@ -2,7 +2,6 @@ set(DGAME_SOURCES "Character.cpp"
"Entity.cpp"
"EntityManager.cpp"
"LeaderboardManager.cpp"
"Player.cpp"
"PlayerManager.cpp"
"TeamManager.cpp"
"TradingManager.cpp"

View File

@ -457,6 +457,8 @@ public:
void SetBillboardVisible(bool visible);
User* GetParentUser() const { return m_ParentUser; }
private:
void UpdateInfoFromDatabase();
/**

View File

@ -15,7 +15,6 @@
#include "Spawner.h"
#include "UserManager.h"
#include "dpWorld.h"
#include "Player.h"
#include "LUTriggers.h"
#include "User.h"
#include "EntityTimer.h"
@ -26,6 +25,7 @@
#include "eObjectBits.h"
#include "PositionUpdate.h"
#include "eChatMessageType.h"
#include "PlayerManager.h"
//Component includes:
#include "Component.h"
@ -95,7 +95,7 @@
#include "CDSkillBehaviorTable.h"
#include "CDZoneTableTable.h"
Entity::Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity) {
Entity::Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser, Entity* parentEntity) {
m_ObjectID = objectID;
m_TemplateID = info.lot;
m_ParentEntity = parentEntity;
@ -124,9 +124,42 @@ Entity::Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity)
m_SpawnerNodeID = info.spawnerNodeID;
if (info.lot != 1) m_PlayerIsReadyForUpdates = true;
if (parentUser) {
m_Character = parentUser->GetLastUsedChar();
parentUser->SetLoggedInChar(objectID);
m_GMLevel = m_Character->GetGMLevel();
m_Character->SetEntity(this);
PlayerManager::AddPlayer(this);
}
}
Entity::~Entity() {
if (IsPlayer()) {
LOG("Deleted player");
// Make sure the player exists first. Remove afterwards to prevent the OnPlayerExist functions from not being able to find the player.
if (!PlayerManager::RemovePlayer(this)) {
LOG("Unable to find player to remove from manager.");
return;
}
Entity* zoneControl = Game::entityManager->GetZoneControlEntity();
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
script->OnPlayerExit(zoneControl, this);
}
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
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
script->OnPlayerExit(scriptEntity, this);
}
}
}
}
if (m_Character) {
m_Character->SaveXMLToDatabase();
}
@ -212,7 +245,7 @@ void Entity::Initialize() {
* Not all components are implemented. Some are represented by a nullptr, as they hold no data.
*/
if (GetParentUser()) {
if (m_Character && m_Character->GetParentUser()) {
AddComponent<MissionComponent>()->LoadFromXml(m_Character->GetXMLDoc());
}
@ -437,7 +470,8 @@ void Entity::Initialize() {
AddComponent<PlayerForcedMovementComponent>();
AddComponent<CharacterComponent>(m_Character)->LoadFromXml(m_Character->GetXMLDoc());
auto& systemAddress = m_Character->GetParentUser() ? m_Character->GetParentUser()->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS;
AddComponent<CharacterComponent>(m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc());
AddComponent<GhostComponent>();
}
@ -788,14 +822,6 @@ bool Entity::operator!=(const Entity& other) const {
return other.m_ObjectID != m_ObjectID;
}
User* Entity::GetParentUser() const {
if (!IsPlayer()) {
return nullptr;
}
return static_cast<const Player*>(this)->GetParentUser();
}
Component* Entity::GetComponent(eReplicaComponentType componentID) const {
const auto& index = m_Components.find(componentID);
@ -850,17 +876,12 @@ void Entity::SetProximityRadius(dpEntity* entity, std::string name) {
void Entity::SetGMLevel(eGameMasterLevel value) {
m_GMLevel = value;
if (GetParentUser()) {
Character* character = GetParentUser()->GetLastUsedChar();
if (m_Character) m_Character->SetGMLevel(value);
if (character) {
character->SetGMLevel(value);
}
}
auto* characterComponent = GetComponent<CharacterComponent>();
if (!characterComponent) return;
CharacterComponent* character = GetComponent<CharacterComponent>();
if (!character) return;
character->SetGMLevel(value);
characterComponent->SetGMLevel(value);
GameMessages::SendGMLevelBroadcast(m_ObjectID, value);
@ -1630,18 +1651,23 @@ bool Entity::GetIsDead() const {
void Entity::AddLootItem(const Loot::Info& info) {
if (!IsPlayer()) return;
auto& droppedLoot = static_cast<Player*>(this)->GetDroppedLoot();
auto* characterComponent = GetComponent<CharacterComponent>();
if (!characterComponent) return;
auto& droppedLoot = characterComponent->GetDroppedLoot();
droppedLoot.insert(std::make_pair(info.id, info));
}
void Entity::PickupItem(const LWOOBJID& objectID) {
if (!IsPlayer()) return;
InventoryComponent* inv = GetComponent<InventoryComponent>();
if (!inv) return;
auto* characterComponent = GetComponent<CharacterComponent>();
if (!inv || !characterComponent) return;
CDObjectsTable* objectsTable = CDClientManager::Instance().GetTable<CDObjectsTable>();
auto& droppedLoot = static_cast<Player*>(this)->GetDroppedLoot();
auto& droppedLoot = characterComponent->GetDroppedLoot();
for (const auto& p : droppedLoot) {
if (p.first == objectID) {
@ -1677,22 +1703,28 @@ void Entity::PickupItem(const LWOOBJID& objectID) {
bool Entity::CanPickupCoins(uint64_t count) {
if (!IsPlayer()) return false;
auto* player = static_cast<Player*>(this);
auto droppedCoins = player->GetDroppedCoins();
auto* characterComponent = GetComponent<CharacterComponent>();
if (!characterComponent) return false;
auto droppedCoins = characterComponent->GetDroppedCoins();
if (count > droppedCoins) {
return false;
} else {
player->SetDroppedCoins(droppedCoins - count);
characterComponent->SetDroppedCoins(droppedCoins - count);
return true;
}
}
void Entity::RegisterCoinDrop(uint64_t count) {
if (!IsPlayer()) return;
auto* player = static_cast<Player*>(this);
auto droppedCoins = player->GetDroppedCoins();
auto* characterComponent = GetComponent<CharacterComponent>();
if (!characterComponent) return;
auto droppedCoins = characterComponent->GetDroppedCoins();
droppedCoins += count;
player->SetDroppedCoins(droppedCoins);
characterComponent->SetDroppedCoins(droppedCoins);
}
void Entity::AddChild(Entity* child) {
@ -1990,7 +2022,7 @@ std::vector<LWOOBJID> Entity::GetTargetsInPhantom() {
// Clean up invalid targets, like disconnected players
m_TargetsInPhantom.erase(std::remove_if(m_TargetsInPhantom.begin(), m_TargetsInPhantom.end(), [](const LWOOBJID id) {
return !Game::entityManager->GetEntity(id);
}), m_TargetsInPhantom.end());
}), m_TargetsInPhantom.end());
std::vector<LWOOBJID> enemies;
for (const auto id : m_TargetsInPhantom) {
@ -2133,3 +2165,27 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) {
if (updateChar) Game::entityManager->SerializeEntity(this);
}
const SystemAddress& Entity::GetSystemAddress() const {
auto* characterComponent = GetComponent<CharacterComponent>();
return characterComponent ? characterComponent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS;
}
const NiPoint3& Entity::GetRespawnPosition() const {
auto* characterComponent = GetComponent<CharacterComponent>();
return characterComponent ? characterComponent->GetRespawnPosition() : NiPoint3Constant::ZERO;
}
const NiQuaternion& Entity::GetRespawnRotation() const {
auto* characterComponent = GetComponent<CharacterComponent>();
return characterComponent ? characterComponent->GetRespawnRotation() : NiQuaternionConstant::IDENTITY;
}
void Entity::SetRespawnPos(const NiPoint3& position) {
auto* characterComponent = GetComponent<CharacterComponent>();
if (characterComponent) characterComponent->SetRespawnPos(position);
}
void Entity::SetRespawnRot(const NiQuaternion& rotation) {
auto* characterComponent = GetComponent<CharacterComponent>();
if (characterComponent) characterComponent->SetRespawnRot(rotation);
}

View File

@ -47,10 +47,10 @@ namespace CppScripts {
*/
class Entity {
public:
explicit Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity = nullptr);
virtual ~Entity();
explicit Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser = nullptr, Entity* parentEntity = nullptr);
~Entity();
virtual void Initialize();
void Initialize();
bool operator==(const Entity& other) const;
bool operator!=(const Entity& other) const;
@ -104,9 +104,7 @@ public:
const NiQuaternion& GetRotation() const;
virtual User* GetParentUser() const;
virtual const SystemAddress& GetSystemAddress() const { return UNASSIGNED_SYSTEM_ADDRESS; };
const SystemAddress& GetSystemAddress() const;
/**
* Setters
@ -128,11 +126,9 @@ public:
void SetRotation(const NiQuaternion& rotation);
virtual void SetRespawnPos(const NiPoint3& position) {}
void SetRespawnPos(const NiPoint3& position);
virtual void SetRespawnRot(const NiQuaternion& rotation) {}
virtual void SetSystemAddress(const SystemAddress& value) {};
void SetRespawnRot(const NiQuaternion& rotation);
/**
* Component management
@ -229,8 +225,8 @@ public:
void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr);
void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; }
virtual const NiPoint3& GetRespawnPosition() const { return NiPoint3Constant::ZERO; }
virtual const NiQuaternion& GetRespawnRotation() const { return NiQuaternionConstant::IDENTITY; }
const NiPoint3& GetRespawnPosition() const;
const NiQuaternion& GetRespawnRotation() const;
void Sleep();
void Wake();

View File

@ -7,7 +7,6 @@
#include "GeneralUtils.h"
#include "dServer.h"
#include "Spawner.h"
#include "Player.h"
#include "SkillComponent.h"
#include "SwitchComponent.h"
#include "UserManager.h"
@ -118,14 +117,7 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE
info.id = id;
Entity* entity;
// Check if the entitty if a player, in case use the extended player entity class
if (user != nullptr) {
entity = new Player(id, info, user, parentEntity);
} else {
entity = new Entity(id, info, parentEntity);
}
Entity* entity = new Entity(id, info, user, parentEntity);
// Initialize the entity
entity->Initialize();
@ -482,7 +474,7 @@ void EntityManager::UpdateGhosting() {
m_PlayersToUpdateGhosting.clear();
}
void EntityManager::UpdateGhosting(Player* player) {
void EntityManager::UpdateGhosting(Entity* player) {
if (player == nullptr) {
return;
}

View File

@ -55,7 +55,7 @@ public:
float GetGhostDistanceMin() const;
void QueueGhostUpdate(LWOOBJID playerID);
void UpdateGhosting();
void UpdateGhosting(Player* player);
void UpdateGhosting(Entity* player);
void CheckGhosting(Entity* entity);
Entity* GetGhostCandidate(int32_t id);
bool GetGhostingEnabled() const;

View File

@ -1,71 +0,0 @@
#include "Player.h"
#include <ctime>
#include "Character.h"
#include "UserManager.h"
#include "EntityManager.h"
#include "Game.h"
#include "Logger.h"
#include "dZoneManager.h"
#include "User.h"
#include "CppScripts.h"
#include "Loot.h"
#include "eReplicaComponentType.h"
#include "PlayerManager.h"
void Player::SetRespawnPos(const NiPoint3& position) {
if (!m_Character) return;
m_respawnPos = position;
m_Character->SetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID(), position);
}
void Player::SetRespawnRot(const NiQuaternion& rotation) {
m_respawnRot = rotation;
}
void Player::SetSystemAddress(const SystemAddress& value) {
m_SystemAddress = value;
}
Player::Player(const LWOOBJID& objectID, const EntityInfo info, User* user, Entity* parentEntity) : Entity(objectID, info, parentEntity) {
m_ParentUser = user;
m_Character = m_ParentUser->GetLastUsedChar();
m_ParentUser->SetLoggedInChar(objectID);
m_GMLevel = m_Character->GetGMLevel();
m_SystemAddress = m_ParentUser->GetSystemAddress();
m_DroppedCoins = 0;
m_Character->SetEntity(this);
PlayerManager::AddPlayer(this);
}
Player::~Player() {
LOG("Deleted player");
// Make sure the player exists first. Remove afterwards to prevent the OnPlayerExist functions from not being able to find the player.
if (!PlayerManager::RemovePlayer(this)) {
LOG("Unable to find player to remove from manager.");
return;
}
if (IsPlayer()) {
Entity* zoneControl = Game::entityManager->GetZoneControlEntity();
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
script->OnPlayerExit(zoneControl, this);
}
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
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
script->OnPlayerExit(scriptEntity, this);
}
}
}
}
}

View File

@ -1,62 +0,0 @@
#pragma once
#include "Entity.h"
/**
* Extended Entity for player data and behavior.
*
* Contains properties only a player entity would require, like associated SystemAddress and User.
*
* Keeps track of which entities are observed by this user for ghosting.
*/
class Player final : public Entity
{
public:
explicit Player(const LWOOBJID& objectID, EntityInfo info, User* user, Entity* parentEntity = nullptr);
/**
* Getters
*/
User* GetParentUser() const override { return m_ParentUser; };
const SystemAddress& GetSystemAddress() const override { return m_SystemAddress; };
const NiPoint3& GetRespawnPosition() const override { return m_respawnPos; };
const NiQuaternion& GetRespawnRotation() const override { return m_respawnRot; };
std::map<LWOOBJID, Loot::Info>& GetDroppedLoot() { return m_DroppedLoot; };
uint64_t GetDroppedCoins() const { return m_DroppedCoins; };
/**
* Setters
*/
void SetDroppedCoins(const uint64_t value) { m_DroppedCoins = value; };
void SetSystemAddress(const SystemAddress& value) override;
void SetRespawnPos(const NiPoint3& position) override;
void SetRespawnRot(const NiQuaternion& rotation) override;
/**
* Ghosting
*/
~Player() override;
private:
SystemAddress m_SystemAddress;
NiPoint3 m_respawnPos;
NiQuaternion m_respawnRot;
User* m_ParentUser;
std::map<LWOOBJID, Loot::Info> m_DroppedLoot;
uint64_t m_DroppedCoins;
};

View File

@ -1,20 +1,19 @@
#include "PlayerManager.h"
#include "Character.h"
#include "Player.h"
#include "User.h"
#include "UserManager.h"
#include "eReplicaComponentType.h"
namespace {
std::vector<Player*> m_Players;
std::vector<Entity*> m_Players;
};
const std::vector<Player*>& PlayerManager::GetAllPlayers() {
const std::vector<Entity*>& PlayerManager::GetAllPlayers() {
return m_Players;
}
void PlayerManager::AddPlayer(Player* player) {
void PlayerManager::AddPlayer(Entity* player) {
const auto& iter = std::find(m_Players.begin(), m_Players.end(), player);
if (iter == m_Players.end()) {
@ -22,7 +21,7 @@ void PlayerManager::AddPlayer(Player* player) {
}
}
bool PlayerManager::RemovePlayer(Player* player) {
bool PlayerManager::RemovePlayer(Entity* player) {
const auto iter = std::find(m_Players.begin(), m_Players.end(), player);
const bool toReturn = iter != m_Players.end();
@ -33,21 +32,21 @@ bool PlayerManager::RemovePlayer(Player* player) {
return toReturn;
}
Player* PlayerManager::GetPlayer(const SystemAddress& sysAddr) {
Entity* PlayerManager::GetPlayer(const SystemAddress& sysAddr) {
auto* entity = UserManager::Instance()->GetUser(sysAddr)->GetLastUsedChar()->GetEntity();
return static_cast<Player*>(entity);
return entity;
}
Player* PlayerManager::GetPlayer(const std::string& name) {
Entity* PlayerManager::GetPlayer(const std::string& name) {
const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER);
Player* player = nullptr;
Entity* player = nullptr;
for (auto* character : characters) {
if (!character->IsPlayer()) continue;
if (GeneralUtils::CaseInsensitiveStringCompare(name, character->GetCharacter()->GetName())) {
player = dynamic_cast<Player*>(character);
player = character;
break;
}
}
@ -55,8 +54,8 @@ Player* PlayerManager::GetPlayer(const std::string& name) {
return player;
}
Player* PlayerManager::GetPlayer(LWOOBJID playerID) {
Player* playerToReturn = nullptr;
Entity* PlayerManager::GetPlayer(LWOOBJID playerID) {
Entity* playerToReturn = nullptr;
for (auto* player : m_Players) {
if (player->GetObjectID() == playerID) {
playerToReturn = player;

View File

@ -5,21 +5,21 @@
#include <string>
class Player;
class Entity;
struct SystemAddress;
namespace PlayerManager {
void AddPlayer(Player* player);
void AddPlayer(Entity* player);
bool RemovePlayer(Player* player);
bool RemovePlayer(Entity* player);
Player* GetPlayer(const SystemAddress& sysAddr);
Entity* GetPlayer(const SystemAddress& sysAddr);
Player* GetPlayer(const std::string& name);
Entity* GetPlayer(const std::string& name);
Player* GetPlayer(LWOOBJID playerID);
Entity* GetPlayer(LWOOBJID playerID);
const std::vector<Player*>& GetAllPlayers();
const std::vector<Entity*>& GetAllPlayers();
};
#endif //!__PLAYERMANAGER__H__

View File

@ -10,7 +10,6 @@
#include "WorldPackets.h"
#include "EntityManager.h"
#include "ChatPackets.h"
#include "Player.h"
#include "BitStreamUtils.h"
#include "dServer.h"
#include "GeneralUtils.h"

View File

@ -24,7 +24,7 @@
#include "WorldPackets.h"
#include <ctime>
CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) {
CharacterComponent::CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress) : Component(parent) {
m_Character = character;
m_IsRacing = false;
@ -46,6 +46,7 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character) : C
m_CurrentActivity = eGameActivity::NONE;
m_CountryCode = 0;
m_LastUpdateTimestamp = std::time(nullptr);
m_SystemAddress = systemAddress;
}
bool CharacterComponent::LandingAnimDisabled(int zoneID) {
@ -762,8 +763,8 @@ void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventu
}
void CharacterComponent::AwardClaimCodes() {
if (!m_Parent) return;
auto* user = m_Parent->GetParentUser();
if (!m_Parent || !m_Parent->GetCharacter()) return;
auto* user = m_Parent->GetCharacter()->GetParentUser();
if (!user) return;
auto rewardCodes = Database::Get()->GetRewardCodesByAccountID(user->GetAccountID());
@ -817,3 +818,20 @@ void CharacterComponent::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) const {
Game::entityManager->DestructEntity(entity);
});
}
const SystemAddress& CharacterComponent::GetSystemAddress() const {
return m_SystemAddress;
}
void CharacterComponent::SetRespawnPos(const NiPoint3& position) {
if (!m_Character) return;
m_respawnPos = position;
m_Character->SetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID(), position);
}
void CharacterComponent::SetRespawnRot(const NiQuaternion& rotation) {
m_respawnRot = rotation;
}

View File

@ -11,6 +11,7 @@
#include "tinyxml2.h"
#include "eReplicaComponentType.h"
#include <array>
#include "Loot.h"
enum class eGameActivity : uint32_t;
@ -65,7 +66,7 @@ class CharacterComponent final : public Component {
public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER;
CharacterComponent(Entity* parent, Character* character);
CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress);
~CharacterComponent() override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
@ -289,6 +290,22 @@ public:
*/
void SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId = 0) const;
const SystemAddress& GetSystemAddress() const;
const NiPoint3& GetRespawnPosition() const { return m_respawnPos; };
void SetRespawnPos(const NiPoint3& position);
const NiQuaternion& GetRespawnRotation() const { return m_respawnRot; };
void SetRespawnRot(const NiQuaternion& rotation);
std::map<LWOOBJID, Loot::Info>& GetDroppedLoot() { return m_DroppedLoot; };
uint64_t GetDroppedCoins() const { return m_DroppedCoins; };
void SetDroppedCoins(const uint64_t value) { m_DroppedCoins = value; };
/**
* Character info regarding this character, including clothing styles, etc.
*/
@ -579,6 +596,16 @@ private:
std::array<uint64_t, 4> m_ClaimCodes{};
void AwardClaimCodes();
SystemAddress m_SystemAddress;
NiPoint3 m_respawnPos;
NiQuaternion m_respawnRot;
std::map<LWOOBJID, Loot::Info> m_DroppedLoot;
uint64_t m_DroppedCoins = 0;
};
#endif // CHARACTERCOMPONENT_H

View File

@ -251,13 +251,14 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
if (playAnim) {
// Now update the player bar
if (!m_Parent->GetParentUser()) return;
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
if (!characterComponent) return;
AMFArrayValue args;
args.Insert("amount", std::to_string(difference));
args.Insert("type", "health");
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
GameMessages::SendUIMessageServerToSingleClient(m_Parent, characterComponent->GetSystemAddress(), "MaxPlayerBarUpdate", args);
}
Game::entityManager->SerializeEntity(m_Parent);
@ -292,13 +293,14 @@ void DestroyableComponent::SetMaxArmor(float value, bool playAnim) {
if (playAnim) {
// Now update the player bar
if (!m_Parent->GetParentUser()) return;
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
if (!characterComponent) return;
AMFArrayValue args;
args.Insert("amount", std::to_string(value));
args.Insert("type", "armor");
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
GameMessages::SendUIMessageServerToSingleClient(m_Parent, characterComponent->GetSystemAddress(), "MaxPlayerBarUpdate", args);
}
Game::entityManager->SerializeEntity(m_Parent);
@ -332,13 +334,14 @@ void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
if (playAnim) {
// Now update the player bar
if (!m_Parent->GetParentUser()) return;
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
if (!characterComponent) return;
AMFArrayValue args;
args.Insert("amount", std::to_string(difference));
args.Insert("type", "imagination");
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args);
GameMessages::SendUIMessageServerToSingleClient(m_Parent, characterComponent->GetSystemAddress(), "MaxPlayerBarUpdate", args);
}
Game::entityManager->SerializeEntity(m_Parent);
}

View File

@ -14,7 +14,6 @@
#include "Character.h"
#include "EntityManager.h"
#include "ItemSet.h"
#include "Player.h"
#include "PetComponent.h"
#include "PossessorComponent.h"
#include "PossessableComponent.h"

View File

@ -14,7 +14,6 @@
#include "Item.h"
#include "Database.h"
#include "ObjectIDManager.h"
#include "Player.h"
#include "RocketLaunchpadControlComponent.h"
#include "PropertyEntranceComponent.h"
#include "InventoryComponent.h"
@ -177,8 +176,6 @@ bool PropertyManagementComponent::Claim(const LWOOBJID playerId) {
auto* entity = Game::entityManager->GetEntity(playerId);
auto* user = entity->GetParentUser();
auto character = entity->GetCharacter();
if (!character) return false;

View File

@ -12,7 +12,6 @@
#include "Item.h"
#include "MissionComponent.h"
#include "ModuleAssemblyComponent.h"
#include "Player.h"
#include "PossessableComponent.h"
#include "PossessorComponent.h"
#include "eRacingTaskParam.h"

View File

@ -9,7 +9,6 @@
#include "ControllablePhysicsComponent.h"
#include "MissionComponent.h"
#include "PhantomPhysicsComponent.h"
#include "Player.h"
#include "QuickBuildComponent.h"
#include "SkillComponent.h"
#include "eEndBehavior.h"

View File

@ -18,7 +18,6 @@
#include "Character.h"
#include "ControllablePhysicsComponent.h"
#include "dZoneManager.h"
#include "Player.h"
#include "CppScripts.h"
#include "CDClientDatabase.h"

View File

@ -20,7 +20,6 @@
#include "WorldPackets.h"
#include "Item.h"
#include "ZCompression.h"
#include "Player.h"
#include "dConfig.h"
#include "TeamManager.h"
#include "ChatPackets.h"
@ -2584,6 +2583,7 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent
//We need to get a new ID for our model first:
ObjectIDManager::RequestPersistentID([=](uint32_t newID) {
if (!entity || !entity->GetCharacter() || !entity->GetCharacter()->GetParentUser()) return;
LWOOBJID newIDL = newID;
GeneralUtils::SetBit(newIDL, eObjectBits::CHARACTER);
GeneralUtils::SetBit(newIDL, eObjectBits::PERSISTENT);
@ -2606,7 +2606,7 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent
//Insert into ugc:
std::string str(sd0Data.get(), sd0Size);
std::istringstream sd0DataStream(str);
Database::Get()->InsertNewUgcModel(sd0DataStream, blueprintIDSmall, entity->GetParentUser()->GetAccountID(), entity->GetCharacter()->GetID());
Database::Get()->InsertNewUgcModel(sd0DataStream, blueprintIDSmall, entity->GetCharacter()->GetParentUser()->GetAccountID(), entity->GetCharacter()->GetID());
//Insert into the db as a BBB model:
IPropertyContents::Model model;
@ -5110,13 +5110,8 @@ void GameMessages::HandleSetFlag(RakNet::BitStream* inStream, Entity* entity) {
inStream->Read(bFlag);
inStream->Read(iFlagID);
auto user = entity->GetParentUser();
if (user) {
auto character = user->GetLastUsedChar();
if (!character) return;
character->SetPlayerFlag(iFlagID, bFlag);
}
auto character = entity->GetCharacter();
if (character) character->SetPlayerFlag(iFlagID, bFlag);
}
void GameMessages::HandleRespondToMission(RakNet::BitStream* inStream, Entity* entity) {

View File

@ -24,6 +24,7 @@
#include "eMissionTaskType.h"
#include "eMissionLockState.h"
#include "eReplicaComponentType.h"
#include "Character.h"
#include "CDMissionEmailTable.h"
@ -208,8 +209,8 @@ Entity* Mission::GetAssociate() const {
return m_MissionComponent->GetParent();
}
User* Mission::GetUser() const {
return GetAssociate()->GetParentUser();
Character* Mission::GetCharacter() const {
return GetAssociate()->GetCharacter();
}
uint32_t Mission::GetMissionId() const {
@ -390,7 +391,7 @@ void Mission::Catchup() {
if (type == eMissionTaskType::PLAYER_FLAG) {
for (int32_t target : task->GetAllTargets()) {
const auto flag = GetUser()->GetLastUsedChar()->GetPlayerFlag(target);
const auto flag = GetCharacter()->GetPlayerFlag(target);
if (!flag) {
continue;
@ -413,7 +414,7 @@ void Mission::YieldRewards() {
return;
}
auto* character = GetUser()->GetLastUsedChar();
auto* character = GetCharacter();
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
auto* levelComponent = entity->GetComponent<LevelProgressionComponent>();
@ -599,8 +600,10 @@ void Mission::SetMissionState(const eMissionState state, const bool sendingRewar
if (entity == nullptr) {
return;
}
auto* characterComponent = entity->GetComponent<CharacterComponent>();
if (!characterComponent) return;
GameMessages::SendNotifyMission(entity, entity->GetParentUser()->GetSystemAddress(), info.id, static_cast<int>(state), sendingRewards);
GameMessages::SendNotifyMission(entity, characterComponent->GetSystemAddress(), info.id, static_cast<int>(state), sendingRewards);
}
void Mission::SetMissionTypeState(eMissionLockState state, const std::string& type, const std::string& subType) {

View File

@ -17,6 +17,7 @@ namespace tinyxml2 {
enum class eMissionState : int;
enum class eMissionLockState : int;
class MissionComponent;
class Character;
/**
* A mission (or achievement) that a player may unlock, progress and complete.
@ -46,7 +47,7 @@ public:
* Returns the account owns the entity that is currently progressing this mission
* @return the account owns the entity that is currently progressing this mission
*/
User* GetUser() const;
Character* GetCharacter() const;
/**
* Returns the current state of this mission

View File

@ -13,6 +13,7 @@
#include "User.h"
#include "tinyxml2.h"
#include "CDClientDatabase.h"
#include "CharacterComponent.h"
// Message includes
#include "Action.h"
@ -145,10 +146,12 @@ void ControlBehaviors::ProcessCommand(Entity* modelEntity, const SystemAddress&
} else if (command == "moveToInventory") {
MoveToInventoryMessage msg(arguments);
context.modelComponent->MoveToInventory(msg);
auto* characterComponent = modelOwner->GetComponent<CharacterComponent>();
if (!characterComponent) return;
AMFArrayValue args;
args.Insert("BehaviorID", std::to_string(msg.GetBehaviorId()));
GameMessages::SendUIMessageServerToSingleClient(modelOwner, modelOwner->GetParentUser()->GetSystemAddress(), "BehaviorRemoved", args);
GameMessages::SendUIMessageServerToSingleClient(modelOwner, characterComponent->GetSystemAddress(), "BehaviorRemoved", args);
SendBehaviorListToClient(context);
} else if (command == "updateAction") {

View File

@ -2,7 +2,6 @@
#include "Database.h"
#include "Entity.h"
#include "PossessableComponent.h"
#include "Player.h"
#include "Game.h"
#include "EntityManager.h"
#include "Character.h"
@ -59,13 +58,13 @@ void LogAndSaveFailedAntiCheatCheck(const LWOOBJID& id, const SystemAddress& sys
player->GetCharacter()->GetName().c_str(), player->GetObjectID(),
sysAddr.ToString(),
entity->GetCharacter()->GetName().c_str(), entity->GetObjectID());
toReport = player->GetParentUser();
if (player->GetCharacter()) toReport = player->GetCharacter()->GetParentUser();
// In the case that the target entity id did not exist, just log the player info.
} else if (player) {
LOG("Player (%s) (%llu) at system address (%s) with sending player (%llu) does not match their own.",
player->GetCharacter()->GetName().c_str(), player->GetObjectID(),
sysAddr.ToString(), id);
toReport = player->GetParentUser();
if (player->GetCharacter()) toReport = player->GetCharacter()->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.",

View File

@ -53,7 +53,6 @@
#include "Loot.h"
#include "EntityInfo.h"
#include "LUTriggers.h"
#include "Player.h"
#include "PhantomPhysicsComponent.h"
#include "ProximityMonitorComponent.h"
#include "dpShapeSphere.h"
@ -1017,7 +1016,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
return;
}
} else {
accountId = player->GetParentUser()->GetAccountID();
auto* character = player->GetCharacter();
auto* user = character != nullptr ? character->GetParentUser() : nullptr;
if (user) accountId = user->GetAccountID();
characterId = player->GetObjectID();
}
@ -1045,7 +1046,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
expire += 60 * 60 * hours;
}
Database::Get()->UpdateAccountUnmuteTime(accountId, expire);
if (accountId != 0) Database::Get()->UpdateAccountUnmuteTime(accountId, expire);
char buffer[32] = "brought up for review.\0";
@ -1109,10 +1110,12 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
return;
}
} else {
accountId = player->GetParentUser()->GetAccountID();
auto* character = player->GetCharacter();
auto* user = character != nullptr ? character->GetParentUser() : nullptr;
if (user) accountId = user->GetAccountID();
}
Database::Get()->UpdateAccountBan(accountId, true);
if (accountId != 0) Database::Get()->UpdateAccountBan(accountId, true);
if (player != nullptr) {
Game::server->Disconnect(player->GetSystemAddress(), eServerDisconnectIdentifiers::FREE_TRIAL_EXPIRED);
@ -1423,7 +1426,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
}
// Allow for this on even while not a GM, as it sometimes toggles incorrrectly.
if (chatCommand == "gminvis" && entity->GetParentUser()->GetMaxGMLevel() >= eGameMasterLevel::DEVELOPER) {
if (chatCommand == "gminvis" && entity->GetCharacter()->GetParentUser()->GetMaxGMLevel() >= eGameMasterLevel::DEVELOPER) {
GameMessages::SendToggleGMInvis(entity->GetObjectID(), true, UNASSIGNED_SYSTEM_ADDRESS);
return;

View File

@ -11,6 +11,7 @@
#include "dZoneManager.h"
#include "DluAssert.h"
#include "DetourExtensions.h"
dNavMesh::dNavMesh(uint32_t zoneId) {
m_ZoneId = zoneId;
@ -30,16 +31,8 @@ dNavMesh::dNavMesh(uint32_t zoneId) {
dNavMesh::~dNavMesh() {
// Clean up Recast information
if(m_Solid) rcFreeHeightField(m_Solid);
if (m_CHF) rcFreeCompactHeightfield(m_CHF);
if (m_CSet) rcFreeContourSet(m_CSet);
if (m_PMesh) rcFreePolyMesh(m_PMesh);
if (m_PMDMesh) rcFreePolyMeshDetail(m_PMDMesh);
if (m_NavMesh) dtFreeNavMesh(m_NavMesh);
if (m_NavQuery) dtFreeNavMeshQuery(m_NavQuery);
if (m_Ctx) delete m_Ctx;
if (m_Triareas) delete[] m_Triareas;
}

View File

@ -2,13 +2,17 @@
#include <cstdint>
#include <vector>
#include <map>
#include <string>
#include <cstring>
#include "DetourExtensions.h"
class NiPoint3;
class rcHeightfield;
class rcCompactHeightfield;
class rcContourSet;
class rcPolyMesh;
class rcPolyMeshDetail;
class InputGeom;
class dtNavMesh;
class dtNavMeshQuery;
class rcContext;
class dNavMesh {
public:
@ -26,24 +30,14 @@ public:
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);
class dtNavMesh* GetdtNavMesh() { return m_NavMesh; }
bool IsNavmeshLoaded() { return m_NavMesh != nullptr; }
private:
void LoadNavmesh();
uint32_t m_ZoneId;
uint8_t* m_Triareas = nullptr;
rcHeightfield* m_Solid = nullptr;
rcCompactHeightfield* m_CHF = nullptr;
rcContourSet* m_CSet = nullptr;
rcPolyMesh* m_PMesh = nullptr;
rcConfig m_Config;
rcPolyMeshDetail* m_PMDMesh = nullptr;
class InputGeom* m_Geometry = nullptr;
class dtNavMesh* m_NavMesh = nullptr;
class dtNavMeshQuery* m_NavQuery = nullptr;
dtNavMesh* m_NavMesh = nullptr;
dtNavMeshQuery* m_NavQuery = nullptr;
uint8_t m_NavMeshDrawFlags;
rcContext* m_Ctx = nullptr;
};

View File

@ -76,7 +76,7 @@ void dpEntity::CheckCollision(dpEntity* other) {
return;
}
bool wasFound = (m_CurrentlyCollidingObjects.find(other->GetObjectID()) != m_CurrentlyCollidingObjects.end());
bool wasFound = m_CurrentlyCollidingObjects.contains(other->GetObjectID());
bool isColliding = m_CollisionShape->IsColliding(other->GetShape());

View File

@ -8,24 +8,14 @@ dpGrid::dpGrid(int numCells, int cellSize) {
CELL_SIZE = cellSize;
m_DeleteGrid = true;
//fill x
for (int i = 0; i < NUM_CELLS; i++) {
m_Cells.push_back(std::vector<std::forward_list<dpEntity*>>());
}
//fill z
for (int i = 0; i < NUM_CELLS; i++) {
for (int i = 0; i < NUM_CELLS; i++) {
m_Cells[i].push_back(std::forward_list<dpEntity*>());
}
}
m_Cells.resize(NUM_CELLS, std::vector<std::vector<dpEntity*>>(NUM_CELLS));
}
dpGrid::~dpGrid() {
if (!this->m_DeleteGrid) return;
for (auto& x : m_Cells) { //x
for (auto& y : x) { //y
for (auto en : y) {
for (auto& z : x) { //y
for (auto en : z) {
if (!en) continue;
delete en;
en = nullptr;
@ -39,13 +29,12 @@ void dpGrid::Add(dpEntity* entity) {
int cellX = (int)std::round(entity->m_Position.x) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
int cellZ = (int)std::round(entity->m_Position.z) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
if (cellX < 0) cellX = 0;
if (cellZ < 0) cellZ = 0;
if (cellX >= NUM_CELLS) cellX = NUM_CELLS - 1;
if (cellZ >= NUM_CELLS) cellZ = NUM_CELLS - 1;
// Clamp values to the range [0, NUM_CELLS - 1]
cellX = std::clamp(cellX, 0, NUM_CELLS - 1);
cellZ = std::clamp(cellZ, 0, NUM_CELLS - 1);
//Add to cell:
m_Cells[cellX][cellZ].push_front(entity);
m_Cells[cellX][cellZ].push_back(entity);
//To verify that the object isn't gargantuan:
if (entity->GetScale() >= CELL_SIZE * 2 || entity->GetIsGargantuan())
@ -59,23 +48,27 @@ void dpGrid::Move(dpEntity* entity, float x, float z) {
int cellX = (int)std::round(x) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
int cellZ = (int)std::round(z) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
if (cellX < 0) cellX = 0;
if (cellZ < 0) cellZ = 0;
if (cellX >= NUM_CELLS) cellX = NUM_CELLS - 1;
if (cellZ >= NUM_CELLS) cellZ = NUM_CELLS - 1;
// Clamp values to the range [0, NUM_CELLS - 1]
cellX = std::clamp(cellX, 0, NUM_CELLS - 1);
cellZ = std::clamp(cellZ, 0, NUM_CELLS - 1);
if (oldCellX < 0) oldCellX = 0;
if (oldCellZ < 0) oldCellZ = 0;
if (oldCellX >= NUM_CELLS) oldCellX = NUM_CELLS - 1;
if (oldCellZ >= NUM_CELLS) oldCellZ = NUM_CELLS - 1;
oldCellX = std::clamp(oldCellX, 0, NUM_CELLS - 1);
oldCellZ = std::clamp(oldCellZ, 0, NUM_CELLS - 1);
if (oldCellX == cellX && oldCellZ == cellZ) return;
//Remove from perv cell:
m_Cells[oldCellX][oldCellZ].remove(entity);
//Remove from prev cell:
auto& cell = m_Cells[oldCellX][oldCellZ];
// For speed, find the single match and swap it with the last element, then pop_back.
auto toRemove = std::find(cell.begin(), cell.end(), entity);
if (toRemove != cell.end()) {
*toRemove = cell.back();
cell.pop_back();
}
//Add to the new cell
m_Cells[cellX][cellZ].push_front(entity);
m_Cells[cellX][cellZ].push_back(entity);
}
void dpGrid::Delete(dpEntity* entity) {
@ -83,15 +76,18 @@ void dpGrid::Delete(dpEntity* entity) {
int oldCellX = (int)std::round(entity->m_Position.x) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
int oldCellZ = (int)std::round(entity->m_Position.z) / dpGrid::CELL_SIZE + NUM_CELLS / 2;
if (oldCellX < 0) oldCellX = 0;
if (oldCellZ < 0) oldCellZ = 0;
if (oldCellX >= NUM_CELLS) oldCellX = NUM_CELLS - 1;
if (oldCellZ >= NUM_CELLS) oldCellZ = NUM_CELLS - 1;
// Clamp values to the range [0, NUM_CELLS - 1]
oldCellX = std::clamp(oldCellX, 0, NUM_CELLS - 1);
oldCellZ = std::clamp(oldCellZ, 0, NUM_CELLS - 1);
m_Cells[oldCellX][oldCellZ].remove(entity);
auto& cell = m_Cells[oldCellX][oldCellZ];
auto toRemove = std::find(cell.begin(), cell.end(), entity);
if (toRemove != cell.end()) {
*toRemove = cell.back();
cell.pop_back();
}
if (m_GargantuanObjects.find(entity->m_ObjectID) != m_GargantuanObjects.end())
m_GargantuanObjects.erase(entity->m_ObjectID);
m_GargantuanObjects.erase(entity->m_ObjectID);
if (entity) delete entity;
entity = nullptr;
@ -100,8 +96,8 @@ void dpGrid::Delete(dpEntity* entity) {
void dpGrid::Update(float deltaTime) {
//Pre-update:
for (auto& x : m_Cells) { //x
for (auto& y : x) { //y
for (auto en : y) {
for (auto& z : x) { //y
for (auto en : z) {
if (!en) continue;
en->PreUpdate();
}
@ -110,8 +106,8 @@ void dpGrid::Update(float deltaTime) {
//Actual collision detection update:
for (int x = 0; x < NUM_CELLS; x++) {
for (int y = 0; y < NUM_CELLS; y++) {
HandleCell(x, y, deltaTime);
for (int z = 0; z < NUM_CELLS; z++) {
HandleCell(x, z, deltaTime);
}
}
}
@ -157,7 +153,7 @@ void dpGrid::HandleCell(int x, int z, float deltaTime) {
HandleEntity(en, other);
}
for (auto other : m_GargantuanObjects)
HandleEntity(en, other.second);
for (auto& [id, entity] : m_GargantuanObjects)
HandleEntity(en, entity);
}
}

View File

@ -1,8 +1,7 @@
#pragma once
#include <vector>
#include <list>
#include <forward_list>
#include <map>
#include <vector>
#include "dCommonVars.h"
class dpEntity;
@ -30,7 +29,8 @@ public:
*/
void SetDeleteGrid(bool value) { this->m_DeleteGrid = value; };
std::vector<std::vector<std::forward_list<dpEntity*>>> GetCells() { return this->m_Cells; };
// Intentional copy since this is only used when we delete this class to re-create it.
std::vector<std::vector<std::vector<dpEntity*>>> GetCells() { return this->m_Cells; };
private:
void HandleEntity(dpEntity* entity, dpEntity* other);
@ -38,7 +38,7 @@ private:
private:
//cells on X, cells on Y for that X, then another vector that contains the entities within that cell.
std::vector<std::vector<std::forward_list<dpEntity*>>> m_Cells;
std::vector<std::vector<std::vector<dpEntity*>>> m_Cells;
std::map<LWOOBJID, dpEntity*> m_GargantuanObjects;
bool m_DeleteGrid = true;
};

View File

@ -81,7 +81,7 @@ void dpWorld::Shutdown() {
}
bool dpWorld::IsLoaded() {
return m_NavMesh->GetdtNavMesh() != nullptr;
return m_NavMesh->IsNavmeshLoaded();
}
void dpWorld::StepWorld(float deltaTime) {

View File

@ -107,7 +107,7 @@ void PetDigServer::HandleXBuildDig(const Entity* self, Entity* owner, Entity* pe
return;
auto* playerEntity = Game::entityManager->GetEntity(playerID);
if (!playerEntity || !playerEntity->GetParentUser() || !playerEntity->GetParentUser()->GetLastUsedChar())
if (!playerEntity || !playerEntity->GetCharacter())
return;
auto* player = playerEntity->GetCharacter();

View File

@ -2,7 +2,6 @@
#include "EntityManager.h"
#include "GameMessages.h"
#include "dZoneManager.h"
#include "Player.h"
#include "Character.h"
#include "ShootingGalleryComponent.h"
#include "PossessorComponent.h"

View File

@ -56,7 +56,6 @@
#include "DestroyableComponent.h"
#include "Game.h"
#include "MasterPackets.h"
#include "Player.h"
#include "PropertyManagementComponent.h"
#include "AssetManager.h"
#include "LevelProgressionComponent.h"
@ -607,9 +606,10 @@ void HandlePacketChat(Packet* packet) {
inStream.Read(expire);
auto* entity = Game::entityManager->GetEntity(playerId);
if (entity != nullptr) {
entity->GetParentUser()->SetMuteExpire(expire);
auto* character = entity != nullptr ? entity->GetCharacter() : nullptr;
auto* user = character != nullptr ? character->GetParentUser() : nullptr;
if (user) {
user->SetMuteExpire(expire);
entity->GetCharacter()->SendMuteNotice();
}
@ -1127,9 +1127,10 @@ void HandlePacket(Packet* packet) {
//Mail::HandleNotificationRequest(packet->systemAddress, player->GetObjectID());
//Notify chat that a player has loaded:
{
const auto& playerName = player->GetCharacter()->GetName();
//RakNet::RakString playerName(player->GetCharacter()->GetName().c_str());
auto* character = player->GetCharacter();
auto* user = character != nullptr ? character->GetParentUser() : nullptr;
if (user) {
const auto& playerName = character->GetName();
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION);
@ -1143,7 +1144,7 @@ void HandlePacket(Packet* packet) {
bitStream.Write(zone.GetMapID());
bitStream.Write(zone.GetInstanceID());
bitStream.Write(zone.GetCloneID());
bitStream.Write(player->GetParentUser()->GetMuteExpire());
bitStream.Write(user->GetMuteExpire());
bitStream.Write(player->GetGMLevel());
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);