Skip to content

Commit

Permalink
auto de-occupy on disable
Browse files Browse the repository at this point in the history
  • Loading branch information
Pospelove committed Feb 14, 2025
1 parent e0958af commit 9676234
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 11 deletions.
25 changes: 22 additions & 3 deletions skymp5-server/cpp/server_guest_lib/MpActor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@

struct MpActor::Impl
{
std::map<uint32_t, Viet::Promise<VarValue>> snippetPromises;
// TODO: consider optimizing data structure and/or general refactoring
std::set<std::shared_ptr<DestroyEventSink>> destroyEventSinks;
std::set<std::shared_ptr<DisableEventSink>> disableEventSinks;

std::map<uint32_t, Viet::Promise<VarValue>> snippetPromises;
uint32_t snippetIndex = 0;
uint32_t respawnTimerIndex = 0;
bool isRespawning = false;
Expand Down Expand Up @@ -374,6 +377,10 @@ void MpActor::Disable()
return;
}

for (auto& sink : pImpl->disableEventSinks) {
sink->BeforeDisable(*this);
}

MpObjectReference::Disable();

for (auto [snippetIdx, promise] : pImpl->snippetPromises) {
Expand Down Expand Up @@ -503,11 +510,22 @@ void MpActor::AddEventSink(std::shared_ptr<DestroyEventSink> sink)
pImpl->destroyEventSinks.insert(sink);
}

void MpActor::RemoveEventSink(std::shared_ptr<DestroyEventSink> sink)
void MpActor::RemoveAllDestroyEventSinks(
std::shared_ptr<DestroyEventSink> sink)
{
pImpl->destroyEventSinks.erase(sink);
}

void MpActor::AddEventSink(std::shared_ptr<DestroyEventSink> sink)
{
pImpl->disableEventSinks.insert(sink);
}

void MpActor::RemoveEventSink(std::shared_ptr<DestroyEventSink> sink)
{
pImpl->disableEventSinks.erase(sink);
}

MpChangeForm MpActor::GetChangeForm() const
{
auto res = MpObjectReference::GetChangeForm();
Expand Down Expand Up @@ -1103,8 +1121,9 @@ void MpActor::ModifyActorValuePercentage(espm::ActorValue av,

void MpActor::BeforeDestroy()
{
for (auto& sink : pImpl->destroyEventSinks)
for (auto& sink : pImpl->destroyEventSinks) {
sink->BeforeDestroy(*this);
}

MpObjectReference::BeforeDestroy();

Expand Down
14 changes: 13 additions & 1 deletion skymp5-server/cpp/server_guest_lib/MpActor.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class MpActor : public MpObjectReference

[[nodiscard]] bool OnEquip(uint32_t baseId);

// TODO: consider removing the entire DestroyEventSink feature because it's
// only used in unit tests
class DestroyEventSink
{
public:
Expand All @@ -82,7 +84,17 @@ class MpActor : public MpObjectReference
};

void AddEventSink(std::shared_ptr<DestroyEventSink> sink);
void RemoveEventSink(std::shared_ptr<DestroyEventSink> sink);
void RemoveAllEventSink(std::shared_ptr<DestroyEventSink> sink);

class DisableEventSink
{
public:
virtual ~DisableEventSink() = default;
virtual void BeforeDisable(MpActor& actor) = 0;
};

void AddEventSink(std::shared_ptr<DisableEventSink> sink);
void RemoveEventSink(std::shared_ptr<DisableEventSink> sink);

MpChangeForm GetChangeForm() const override;
void ApplyChangeForm(const MpChangeForm& changeForm) override;
Expand Down
63 changes: 56 additions & 7 deletions skymp5-server/cpp/server_guest_lib/MpObjectReference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ UpdatePropertyMessage MpObjectReference::PreparePropertyMessage(
return res;
}

// TODO: de-duplicate code of
// OccupantDisableEventSink/OccupantDisableEventSink, the only difference is
// base classes

class OccupantDestroyEventSink : public MpActor::DestroyEventSink
{
public:
Expand All @@ -85,8 +89,44 @@ class OccupantDestroyEventSink : public MpActor::DestroyEventSink

void BeforeDestroy(MpActor& actor) override
{
if (!RefStillValid())
if (!RefStillValid()) {
return;
}

if (untrustedRefPtr->occupant == &actor) {
untrustedRefPtr->SetOpen(false);
untrustedRefPtr->occupant = nullptr;
}
}

private:
bool RefStillValid() const
{
return untrustedRefPtr == wst.LookupFormById(refId).get();
}

WorldState& wst;
MpObjectReference* const untrustedRefPtr;
const uint32_t refId;
};

class OccupantDisableEventSink : public MpActor::DisableEventSink
{
public:
OccupantDisableEventSink(WorldState& wst_,
MpObjectReference* untrustedRefPtr_)
: wst(wst_)
, untrustedRefPtr(untrustedRefPtr_)
, refId(untrustedRefPtr_->GetFormId())
{
}

void BeforeDisable(MpActor& actor) override
{
if (!RefStillValid()) {
return;
}

if (untrustedRefPtr->occupant == &actor) {
untrustedRefPtr->SetOpen(false);
untrustedRefPtr->occupant = nullptr;
Expand Down Expand Up @@ -1455,6 +1495,7 @@ void MpObjectReference::ProcessActivateNormal(
if (CheckIfObjectCanStartOccupyThis(activationSource, kOccupationReach)) {
if (this->occupant) {
this->occupant->RemoveEventSink(this->occupantDestroySink);
this->occupant->RemoveEventSink(this->occupantDisableSink);
}
SetOpen(true);
actorActivator->SendToUser(
Expand All @@ -1466,7 +1507,11 @@ void MpObjectReference::ProcessActivateNormal(

this->occupantDestroySink.reset(
new OccupantDestroyEventSink(*GetParent(), this));
this->occupant->AddEventSink(occupantDestroySink);
this->occupant->AddEventSink(this->occupantDestroySink);

this->occupantDisableSink.reset(
new OccupantDisableEventSink(*GetParent(), this));
this->occupant->AddEventSink(this->occupantDisableSink);
}
} else if (t == espm::ACTI::kType && actorActivator) {
// SendOpenContainer being used to activate the object
Expand All @@ -1479,6 +1524,7 @@ void MpObjectReference::ProcessActivateNormal(
if (CheckIfObjectCanStartOccupyThis(activationSource, kOccupationReach)) {
if (this->occupant) {
this->occupant->RemoveEventSink(this->occupantDestroySink);
this->occupant->RemoveEventSink(this->occupantDisableSink);
}

// SendOpenContainer being used to activate the object
Expand All @@ -1489,7 +1535,11 @@ void MpObjectReference::ProcessActivateNormal(

this->occupantDestroySink.reset(
new OccupantDestroyEventSink(*GetParent(), this));
this->occupant->AddEventSink(occupantDestroySink);
this->occupant->AddEventSink(this->occupantDestroySink);

this->occupantDisableSink.reset(
new OccupantDisableEventSink(*GetParent(), this));
this->occupant->AddEventSink(this->occupantDisableSink);
}
}
}
Expand Down Expand Up @@ -1785,10 +1835,9 @@ void MpObjectReference::SendInventoryUpdate()
if (actor) {
std::string msg;
msg += Networking::MinPacketId;
msg += nlohmann::json{
{ "inventory", actor->GetInventory().ToJson() },
{ "type", "setInventory" }
}.dump();
msg += nlohmann::json{ { "inventory", actor->GetInventory().ToJson() },
{ "type", "setInventory" } }
.dump();
actor->SendToUserDeferred(msg.data(), msg.size(), true,
kChannelSetInventory, true);
}
Expand Down
2 changes: 2 additions & 0 deletions skymp5-server/cpp/server_guest_lib/MpObjectReference.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class MpObjectReference
, protected ChangeFormGuard
{
friend class OccupantDestroyEventSink;
friend class OccupantDisableEventSink;

public:
static const char* Type() { return "ObjectReference"; }
Expand Down Expand Up @@ -252,6 +253,7 @@ class MpObjectReference
uint32_t baseId = 0;
MpActor* occupant = nullptr;
std::shared_ptr<OccupantDestroyEventSink> occupantDestroySink;
std::shared_ptr<OccupantDisableEventSink> occupantDisableSink;
std::optional<std::chrono::system_clock::duration> relootTimeOverride;
std::unique_ptr<uint8_t> chanceNoneOverride;
bool activationBlocked = false;
Expand Down

0 comments on commit 9676234

Please sign in to comment.