mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-11-20 00:52:33 +00:00
feat: proper gminvs with ghosting (#1920)
* feat: proper gminvis ghosting * address feedback --------- Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
This commit is contained in:
@@ -504,7 +504,7 @@ void Entity::Initialize() {
|
||||
auto& systemAddress = m_Character->GetParentUser() ? m_Character->GetParentUser()->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS;
|
||||
AddComponent<CharacterComponent>(characterID, m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc());
|
||||
|
||||
AddComponent<GhostComponent>(characterID);
|
||||
AddComponent<GhostComponent>(characterID)->LoadFromXml(m_Character->GetXMLDoc());
|
||||
}
|
||||
|
||||
const auto inventoryID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY);
|
||||
|
||||
@@ -361,16 +361,24 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
||||
LOG("Attempted to construct null entity");
|
||||
return;
|
||||
}
|
||||
// Don't construct GM invisible entities unless it's for the GM themselves
|
||||
// GMs can see other GMs if they are the same or lower level
|
||||
GameMessages::GetGMInvis getGMInvisMsg;
|
||||
getGMInvisMsg.Send(entity->GetObjectID());
|
||||
if (getGMInvisMsg.bGMInvis && sysAddr != entity->GetSystemAddress()) {
|
||||
auto* toUser = UserManager::Instance()->GetUser(sysAddr);
|
||||
if (!toUser) return;
|
||||
auto* constructedUser = UserManager::Instance()->GetUser(entity->GetSystemAddress());
|
||||
if (!constructedUser) return;
|
||||
if (toUser->GetMaxGMLevel() < constructedUser->GetMaxGMLevel()) return;
|
||||
}
|
||||
|
||||
if (entity->GetNetworkId() == 0) {
|
||||
uint16_t networkId;
|
||||
|
||||
if (!m_LostNetworkIds.empty()) {
|
||||
networkId = m_LostNetworkIds.top();
|
||||
m_LostNetworkIds.pop();
|
||||
} else {
|
||||
networkId = ++m_NetworkIdCounter;
|
||||
}
|
||||
} else networkId = ++m_NetworkIdCounter;
|
||||
|
||||
entity->SetNetworkId(networkId);
|
||||
}
|
||||
@@ -379,10 +387,8 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
||||
if (std::find(m_EntitiesToGhost.begin(), m_EntitiesToGhost.end(), entity) == m_EntitiesToGhost.end()) {
|
||||
m_EntitiesToGhost.push_back(entity);
|
||||
}
|
||||
|
||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
|
||||
CheckGhosting(entity);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -413,14 +419,9 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
||||
Game::server->Send(stream, sysAddr, false);
|
||||
}
|
||||
|
||||
if (entity->IsPlayer()) {
|
||||
if (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) {
|
||||
GameMessages::SendToggleGMInvis(entity->GetObjectID(), true, sysAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EntityManager::ConstructAllEntities(const SystemAddress& sysAddr) {
|
||||
void EntityManager::ConstructAllEntities(const SystemAddress& sysAddr) {
|
||||
//ZoneControl is special:
|
||||
ConstructEntity(m_ZoneControlEntity, sysAddr);
|
||||
|
||||
@@ -488,11 +489,7 @@ void EntityManager::QueueGhostUpdate(LWOOBJID playerID) {
|
||||
void EntityManager::UpdateGhosting() {
|
||||
for (const auto playerID : m_PlayersToUpdateGhosting) {
|
||||
auto* player = PlayerManager::GetPlayer(playerID);
|
||||
|
||||
if (player == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!player) continue;
|
||||
UpdateGhosting(player);
|
||||
}
|
||||
|
||||
@@ -519,6 +516,7 @@ void EntityManager::UpdateGhosting(Entity* player) {
|
||||
|
||||
const auto distance = NiPoint3::DistanceSquared(referencePoint, entityPoint);
|
||||
|
||||
|
||||
auto ghostingDistanceMax = m_GhostDistanceMaxSquared;
|
||||
auto ghostingDistanceMin = m_GhostDistanceMinSqaured;
|
||||
|
||||
@@ -555,35 +553,25 @@ void EntityManager::UpdateGhosting(Entity* player) {
|
||||
}
|
||||
|
||||
void EntityManager::CheckGhosting(Entity* entity) {
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!entity) return;
|
||||
|
||||
const auto& referencePoint = entity->GetPosition();
|
||||
|
||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||
if (!ghostComponent) continue;
|
||||
|
||||
const auto& entityPoint = ghostComponent->GetGhostReferencePoint();
|
||||
|
||||
const auto id = entity->GetObjectID();
|
||||
|
||||
const auto observed = ghostComponent->IsObserved(id);
|
||||
|
||||
const auto distance = NiPoint3::DistanceSquared(referencePoint, entityPoint);
|
||||
|
||||
if (observed && distance > m_GhostDistanceMaxSquared) {
|
||||
ghostComponent->GhostEntity(id);
|
||||
|
||||
DestructEntity(entity, player->GetSystemAddress());
|
||||
|
||||
entity->SetObservers(entity->GetObservers() - 1);
|
||||
} else if (!observed && m_GhostDistanceMinSqaured > distance) {
|
||||
ghostComponent->ObserveEntity(id);
|
||||
|
||||
ConstructEntity(entity, player->GetSystemAddress());
|
||||
|
||||
entity->SetObservers(entity->GetObservers() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
#include "GhostComponent.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "Character.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "UserManager.h"
|
||||
#include "User.h"
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "GameMessages.h"
|
||||
@@ -7,7 +12,9 @@ GhostComponent::GhostComponent(Entity* parent, const int32_t componentID) : Comp
|
||||
m_GhostReferencePoint = NiPoint3Constant::ZERO;
|
||||
m_GhostOverridePoint = NiPoint3Constant::ZERO;
|
||||
m_GhostOverride = false;
|
||||
|
||||
|
||||
RegisterMsg<GameMessages::ToggleGMInvis>(this, &GhostComponent::OnToggleGMInvis);
|
||||
RegisterMsg<GameMessages::GetGMInvis>(this, &GhostComponent::OnGetGMInvis);
|
||||
RegisterMsg<GameMessages::GetObjectReportInfo>(this, &GhostComponent::MsgGetObjectReportInfo);
|
||||
}
|
||||
|
||||
@@ -22,6 +29,25 @@ GhostComponent::~GhostComponent() {
|
||||
}
|
||||
}
|
||||
|
||||
void GhostComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
|
||||
auto* objElement = doc.FirstChildElement("obj");
|
||||
if (!objElement) return;
|
||||
auto* ghstElement = objElement->FirstChildElement("ghst");
|
||||
if (!ghstElement) return;
|
||||
m_IsGMInvisible = ghstElement->BoolAttribute("i");
|
||||
}
|
||||
|
||||
void GhostComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
|
||||
auto* objElement = doc.FirstChildElement("obj");
|
||||
if (!objElement) return;
|
||||
auto* ghstElement = objElement->FirstChildElement("ghst");
|
||||
if (ghstElement) objElement->DeleteChild(ghstElement);
|
||||
// Only save if GM invisible
|
||||
if (!m_IsGMInvisible) return;
|
||||
ghstElement = objElement->InsertNewChildElement("ghst");
|
||||
if (ghstElement) ghstElement->SetAttribute("i", m_IsGMInvisible);
|
||||
}
|
||||
|
||||
void GhostComponent::SetGhostReferencePoint(const NiPoint3& value) {
|
||||
m_GhostReferencePoint = value;
|
||||
}
|
||||
@@ -61,6 +87,40 @@ void GhostComponent::GhostEntity(LWOOBJID id) {
|
||||
m_ObservedEntities.erase(id);
|
||||
}
|
||||
|
||||
bool GhostComponent::OnToggleGMInvis(GameMessages::GameMsg& msg) {
|
||||
auto& gmInvisMsg = static_cast<GameMessages::ToggleGMInvis&>(msg);
|
||||
gmInvisMsg.bStateOut = !m_IsGMInvisible;
|
||||
m_IsGMInvisible = !m_IsGMInvisible;
|
||||
LOG_DEBUG("GM Invisibility toggled to: %s", m_IsGMInvisible ? "true" : "false");
|
||||
gmInvisMsg.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
auto* thisUser = UserManager::Instance()->GetUser(m_Parent->GetSystemAddress());
|
||||
for (const auto& player : PlayerManager::GetAllPlayers()) {
|
||||
if (!player || player->GetObjectID() == m_Parent->GetObjectID()) continue;
|
||||
auto* toUser = UserManager::Instance()->GetUser(player->GetSystemAddress());
|
||||
if (m_IsGMInvisible) {
|
||||
if (toUser->GetMaxGMLevel() < thisUser->GetMaxGMLevel()) {
|
||||
Game::entityManager->DestructEntity(m_Parent, player->GetSystemAddress());
|
||||
}
|
||||
} else {
|
||||
if (toUser->GetMaxGMLevel() >= thisUser->GetMaxGMLevel()) {
|
||||
Game::entityManager->ConstructEntity(m_Parent, player->GetSystemAddress());
|
||||
auto* controllableComp = m_Parent->GetComponent<ControllablePhysicsComponent>();
|
||||
controllableComp->SetDirtyPosition(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GhostComponent::OnGetGMInvis(GameMessages::GameMsg& msg) {
|
||||
LOG_DEBUG("GM Invisibility requested: %s", m_IsGMInvisible ? "true" : "false");
|
||||
auto& gmInvisMsg = static_cast<GameMessages::GetGMInvis&>(msg);
|
||||
gmInvisMsg.bGMInvis = m_IsGMInvisible;
|
||||
return gmInvisMsg.bGMInvis;
|
||||
}
|
||||
|
||||
bool GhostComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||
auto& cmptType = reportMsg.info->PushDebug("Ghost");
|
||||
|
||||
@@ -7,11 +7,17 @@
|
||||
|
||||
class NiPoint3;
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLDocument;
|
||||
}
|
||||
|
||||
class GhostComponent final : public Component {
|
||||
public:
|
||||
static inline const eReplicaComponentType ComponentType = eReplicaComponentType::GHOST;
|
||||
GhostComponent(Entity* parent, const int32_t componentID);
|
||||
~GhostComponent() override;
|
||||
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
|
||||
void UpdateXml(tinyxml2::XMLDocument& doc) override;
|
||||
|
||||
void SetGhostOverride(bool value) { m_GhostOverride = value; };
|
||||
|
||||
@@ -39,9 +45,14 @@ public:
|
||||
|
||||
void GhostEntity(const LWOOBJID id);
|
||||
|
||||
bool OnToggleGMInvis(GameMessages::GameMsg& msg);
|
||||
|
||||
bool OnGetGMInvis(GameMessages::GameMsg& msg);
|
||||
|
||||
bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||
|
||||
private:
|
||||
|
||||
NiPoint3 m_GhostReferencePoint;
|
||||
|
||||
NiPoint3 m_GhostOverridePoint;
|
||||
@@ -51,6 +62,9 @@ private:
|
||||
std::unordered_set<LWOOBJID> m_LimboConstructions;
|
||||
|
||||
bool m_GhostOverride;
|
||||
|
||||
bool m_IsGMInvisible;
|
||||
|
||||
};
|
||||
|
||||
#endif //!__GHOSTCOMPONENT__H__
|
||||
|
||||
@@ -1847,18 +1847,6 @@ void GameMessages::SendNotifyClientFailedPrecondition(LWOOBJID objectId, const S
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void GameMessages::SendToggleGMInvis(LWOOBJID objectId, bool enabled, const SystemAddress& sysAddr) {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(objectId);
|
||||
bitStream.Write(MessageType::Game::TOGGLE_GM_INVIS);
|
||||
bitStream.Write(enabled); // does not matter?
|
||||
|
||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void GameMessages::SendSetName(LWOOBJID objectID, std::u16string name, const SystemAddress& sysAddr) {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
@@ -6449,4 +6437,8 @@ namespace GameMessages {
|
||||
stream.Write(lootID);
|
||||
stream.Write(lootOwnerID);
|
||||
}
|
||||
|
||||
void ToggleGMInvis::Serialize(RakNet::BitStream& stream) const {
|
||||
stream.Write(bStateOut);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,8 +243,6 @@ namespace GameMessages {
|
||||
bool cancelOnLogout = false, bool cancelOnRemoveBuff = true, bool cancelOnUi = false,
|
||||
bool cancelOnUnequip = false, bool cancelOnZone = false, bool addedByTeammate = false, bool applyOnTeammates = false, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
void SendToggleGMInvis(LWOOBJID objectId, bool enabled, const SystemAddress& sysAddr);
|
||||
|
||||
void SendSetName(LWOOBJID objectID, std::u16string name, const SystemAddress& sysAddr);
|
||||
|
||||
// Property messages
|
||||
@@ -938,6 +936,20 @@ namespace GameMessages {
|
||||
LWOOBJID lootOwnerID{};
|
||||
};
|
||||
|
||||
struct ToggleGMInvis : public GameMsg {
|
||||
ToggleGMInvis() : GameMsg(MessageType::Game::TOGGLE_GM_INVIS) {}
|
||||
|
||||
void Serialize(RakNet::BitStream& stream) const override;
|
||||
bool bStateOut{ false };
|
||||
|
||||
};
|
||||
|
||||
struct GetGMInvis : public GameMsg {
|
||||
GetGMInvis() : GameMsg(MessageType::Game::GET_GM_INVIS) {}
|
||||
|
||||
bool bGMInvis{ false };
|
||||
};
|
||||
|
||||
struct ChildRemoved : public GameMsg {
|
||||
ChildRemoved() : GameMsg(MessageType::Game::CHILD_REMOVED) {}
|
||||
|
||||
|
||||
@@ -893,10 +893,10 @@ void SlashCommandHandler::Startup() {
|
||||
|
||||
Command GmInvisCommand{
|
||||
.help = "Toggles invisibility for the character",
|
||||
.info = "Toggles invisibility for the character, though it's currently a bit buggy. Requires nonzero GM Level for the character, but the account must have a GM level of 8",
|
||||
.info = "Toggles invisibility for the character, making them invisible to other players and lower GM levels",
|
||||
.aliases = { "gminvis" },
|
||||
.handle = GMGreaterThanZeroCommands::GmInvis,
|
||||
.requiredLevel = eGameMasterLevel::DEVELOPER
|
||||
.requiredLevel = eGameMasterLevel::FORUM_MODERATOR
|
||||
};
|
||||
RegisterCommand(GmInvisCommand);
|
||||
|
||||
|
||||
@@ -89,7 +89,8 @@ namespace DEVGMCommands {
|
||||
GameMessages::SendChatModeUpdate(entity->GetObjectID(), eGameMasterLevel::CIVILIAN);
|
||||
entity->SetGMLevel(eGameMasterLevel::CIVILIAN);
|
||||
|
||||
GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
GameMessages::ToggleGMInvis msg;
|
||||
msg.Send(entity->GetObjectID());
|
||||
|
||||
GameMessages::SendSlashCommandFeedbackText(entity, u"Your game master level has been changed, you may not be able to use all commands.");
|
||||
}
|
||||
@@ -183,7 +184,6 @@ namespace DEVGMCommands {
|
||||
Game::entityManager->ConstructEntity(entity);
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(lowerName) + u" set to " + (GeneralUtils::to_u16string(minifigItemId)));
|
||||
|
||||
GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); // need to retoggle because it gets reenabled on creation of new character
|
||||
}
|
||||
|
||||
void PlayAnimation(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
|
||||
@@ -275,7 +275,8 @@ namespace GMGreaterThanZeroCommands {
|
||||
}
|
||||
|
||||
void GmInvis(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
GameMessages::SendToggleGMInvis(entity->GetObjectID(), true, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
GameMessages::ToggleGMInvis msg;
|
||||
msg.Send(entity->GetObjectID());
|
||||
}
|
||||
|
||||
void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
|
||||
Reference in New Issue
Block a user