Skip to content

Commit

Permalink
Magic effect removal code
Browse files Browse the repository at this point in the history
This allows the function that explicitly removes magic effects to sync when applied to a player
Cherry-pick to newest /dev branch needed to move Actor::RemoveSpell to Actor.cpp
  • Loading branch information
MostExcellent authored and rfortier committed Nov 12, 2024
1 parent cff5144 commit 28aa711
Show file tree
Hide file tree
Showing 15 changed files with 236 additions and 5 deletions.
9 changes: 9 additions & 0 deletions Code/client/Events/RemoveSpellEvent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

struct RemoveSpellEvent
{
RemoveSpellEvent() = default;

uint32_t TargetId{};
uint32_t SpellId{};
};
13 changes: 13 additions & 0 deletions Code/client/Games/Skyrim/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,19 @@ void Actor::StopCombat() noexcept
s_pStopCombat(this);
}

void Actor::RemoveSpell(MagicItem* apSpell) noexcept
{
TP_THIS_FUNCTION(TRemoveSpell, void, Actor, MagicItem*);
POINTER_SKYRIMSE(TRemoveSpell, removeSpell, 38717);
if (!apSpell)
{
spdlog::error("Actor::RemoveSpell: apSpell is null");
return;
}
// spdlog::info("Removing spell: {} from actor: {}", apSpell->formID, formID);
TiltedPhoques::ThisCall(removeSpell, this, apSpell);
}

bool Actor::HasPerk(uint32_t aPerkFormId) const noexcept
{
return GetPerkRank(aPerkFormId) != 0;
Expand Down
1 change: 1 addition & 0 deletions Code/client/Games/Skyrim/Actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ struct Actor : TESObjectREFR
void StopCombat() noexcept;
bool PlayIdle(TESIdleForm* apIdle) noexcept;
void FixVampireLordModel() noexcept;
void RemoveSpell(MagicItem* apSpell) noexcept;

enum ActorFlags
{
Expand Down
27 changes: 27 additions & 0 deletions Code/client/Games/Skyrim/Magic/MagicTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

#include <Effects/ActiveEffect.h>

#include <Events/RemoveSpellEvent.h>

TP_THIS_FUNCTION(TAddTarget, bool, MagicTarget, MagicTarget::AddTargetData& arData);
TP_THIS_FUNCTION(TRemoveSpell, bool, Actor, MagicItem* apSpell);
TP_THIS_FUNCTION(TCheckAddEffectTargetData, bool, MagicTarget::AddTargetData, void* arArgs, float afResistance);
TP_THIS_FUNCTION(TFindTargets, bool, MagicCaster, float afEffectivenessMult, int32_t* aruiTargetCount,
TESBoundObject* apSource, char abLoadCast, char abAdjust);
Expand All @@ -26,6 +29,7 @@ TP_THIS_FUNCTION(THasPerk, bool, Actor, TESForm* apPerk, void* apUnk1, double* a
TP_THIS_FUNCTION(TGetPerkRank, uint8_t, Actor, TESForm* apPerk);

static TAddTarget* RealAddTarget = nullptr;
static TRemoveSpell* RealRemoveSpell = nullptr;
static TCheckAddEffectTargetData* RealCheckAddEffectTargetData = nullptr;
static TFindTargets* RealFindTargets = nullptr;
static TAdjustForPerks* RealAdjustForPerks = nullptr;
Expand Down Expand Up @@ -181,6 +185,26 @@ bool TP_MAKE_THISCALL(HookAddTarget, MagicTarget, MagicTarget::AddTargetData& ar
}
}

// Designed to run when we hook removespell (AddressLib ID is 38717)
// Sends a message to the server to remove the spell from this player on other clients
bool TP_MAKE_THISCALL(HookRemoveSpell, Actor, MagicItem* apSpell)
{
bool result = TiltedPhoques::ThisCall(RealRemoveSpell, apThis, apSpell);
if (apThis->GetExtension()->IsLocalPlayer() && result)
{
// Log spell info
//spdlog::info("Removing spell {}, ID: {} from local player", apSpell->GetName() , apSpell->formID);
RemoveSpellEvent removalEvent;

removalEvent.TargetId = apThis->formID;
removalEvent.SpellId = apSpell->formID;
World::Get().GetRunner().Trigger(removalEvent);
}

return result;
}


bool TP_MAKE_THISCALL(HookCheckAddEffectTargetData, MagicTarget::AddTargetData, void* arArgs, float afResistance)
{
if (s_autoSucceedEffectCheck)
Expand Down Expand Up @@ -227,20 +251,23 @@ uint8_t TP_MAKE_THISCALL(HookGetPerkRank, Actor, TESForm* apPerk)

static TiltedPhoques::Initializer s_magicTargetHooks([]() {
POINTER_SKYRIMSE(TAddTarget, addTarget, 34526);
POINTER_SKYRIMSE(TRemoveSpell, removeSpell, 38717);
POINTER_SKYRIMSE(TCheckAddEffectTargetData, checkAddEffectTargetData, 34525);
POINTER_SKYRIMSE(TFindTargets, findTargets, 34410);
POINTER_SKYRIMSE(TAdjustForPerks, adjustForPerks, 34053);
POINTER_SKYRIMSE(THasPerk, hasPerk, 21622);
POINTER_SKYRIMSE(TGetPerkRank, getPerkRank, 37698);

RealAddTarget = addTarget.Get();
RealRemoveSpell = removeSpell.Get();
RealCheckAddEffectTargetData = checkAddEffectTargetData.Get();
RealFindTargets = findTargets.Get();
RealAdjustForPerks = adjustForPerks.Get();
RealHasPerk = hasPerk.Get();
RealGetPerkRank = getPerkRank.Get();

TP_HOOK(&RealAddTarget, HookAddTarget);
TP_HOOK(&RealRemoveSpell, HookRemoveSpell);
TP_HOOK(&RealCheckAddEffectTargetData, HookCheckAddEffectTargetData);
TP_HOOK(&RealFindTargets, HookFindTargets);
TP_HOOK(&RealAdjustForPerks, HookAdjustForPerks);
Expand Down
74 changes: 74 additions & 0 deletions Code/client/Services/Generic/MagicService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#include <Events/SpellCastEvent.h>
#include <Events/InterruptCastEvent.h>
#include <Events/AddTargetEvent.h>
#include <Events/RemoveSpellEvent.h>

#include <Messages/RemoveSpellRequest.h>

#include <Messages/SpellCastRequest.h>
#include <Messages/InterruptCastRequest.h>
Expand Down Expand Up @@ -40,6 +43,8 @@ MagicService::MagicService(World& aWorld, entt::dispatcher& aDispatcher, Transpo
m_notifyInterruptCastConnection = m_dispatcher.sink<NotifyInterruptCast>().connect<&MagicService::OnNotifyInterruptCast>(this);
m_addTargetEventConnection = m_dispatcher.sink<AddTargetEvent>().connect<&MagicService::OnAddTargetEvent>(this);
m_notifyAddTargetConnection = m_dispatcher.sink<NotifyAddTarget>().connect<&MagicService::OnNotifyAddTarget>(this);
m_removeSpellEventConnection = m_dispatcher.sink<RemoveSpellEvent>().connect<&MagicService::OnRemoveSpellEvent>(this);
m_notifyRemoveSpell = m_dispatcher.sink<NotifyRemoveSpell>().connect<&MagicService::OnNotifyRemoveSpell>(this);
}

void MagicService::OnUpdate(const UpdateEvent& acEvent) noexcept
Expand Down Expand Up @@ -409,6 +414,75 @@ void MagicService::OnNotifyAddTarget(const NotifyAddTarget& acMessage) noexcept
spdlog::debug("Applied remote magic effect");
}

void MagicService::OnRemoveSpellEvent(const RemoveSpellEvent& acEvent) noexcept
{
if (!m_transport.IsConnected())
return;

RemoveSpellRequest request{};

if (!m_world.GetModSystem().GetServerModId(acEvent.SpellId, request.SpellId.ModId, request.SpellId.BaseId))
{
spdlog::error("{}: Could not find spell with form {:X}", __FUNCTION__, acEvent.SpellId);
return;
}

auto view = m_world.view<FormIdComponent>();
const auto it = std::find_if(std::begin(view), std::end(view), [id = acEvent.TargetId, view](auto entity) {
return view.get<FormIdComponent>(entity).Id == id;
});

if (it == std::end(view))
{
spdlog::warn("Form id not found for magic remove target, form id: {:X}", acEvent.TargetId);
return;
}

std::optional<uint32_t> serverIdRes = Utils::GetServerId(*it);
if (!serverIdRes.has_value())
{
spdlog::warn("Server id not found for magic remove target, form id: {:X}", acEvent.TargetId);
return;
}

request.TargetId = serverIdRes.value();

//spdlog::info("Requesting remove spell with base id {:X} from actor with server id {:X}", request.SpellId.BaseId, request.TargetId);

m_transport.Send(request);
}

void MagicService::OnNotifyRemoveSpell(const NotifyRemoveSpell& acMessage) noexcept
{
uint32_t targetFormId = acMessage.TargetId;

Actor* pActor = Utils::GetByServerId<Actor>(acMessage.TargetId);
if (!pActor)
{
spdlog::warn(__FUNCTION__ ": could not find actor server id {:X}", acMessage.TargetId);
return;
}

const uint32_t cSpellId = World::Get().GetModSystem().GetGameId(acMessage.SpellId);
if (cSpellId == 0)
{
spdlog::error("{}: failed to retrieve spell id, GameId base: {:X}, mod: {:X}", __FUNCTION__,
acMessage.SpellId.BaseId, acMessage.SpellId.ModId);
return;
}

MagicItem* pSpell = Cast<MagicItem>(TESForm::GetById(cSpellId));
if (!pSpell)
{
spdlog::error("{}: Failed to retrieve spell by id {:X}", __FUNCTION__, cSpellId);
return;
}

// Remove the spell from the actor
//spdlog::info("Removing spell with form id {:X} from actor with form id {:X}", cSpellId, targetFormId);
pActor->RemoveSpell(pSpell);
}

void MagicService::ApplyQueuedEffects() noexcept
{
static std::chrono::steady_clock::time_point lastSendTimePoint;
Expand Down
12 changes: 12 additions & 0 deletions Code/client/Services/MagicService.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <Events/EventDispatcher.h>
#include <Messages/AddTargetRequest.h>
#include <Messages/NotifyAddTarget.h>
#include <Messages/NotifyRemoveSpell.h>

struct World;
struct TransportService;
Expand All @@ -12,6 +13,7 @@ struct UpdateEvent;
struct SpellCastEvent;
struct InterruptCastEvent;
struct AddTargetEvent;
struct RemoveSpellEvent;

struct NotifySpellCast;
struct NotifyInterruptCast;
Expand Down Expand Up @@ -62,6 +64,14 @@ struct MagicService
* @brief Applies a magic effect based on a server message.
*/
void OnNotifyAddTarget(const NotifyAddTarget& acMessage) noexcept;
/**
* @brief Sends a message to remove a spell from a player.
*/
void OnRemoveSpellEvent(const RemoveSpellEvent& acEvent) noexcept;
/*
* @brief Handles removal of a spell
*/
void OnNotifyRemoveSpell(const NotifyRemoveSpell& acMessage) noexcept;

private:
/**
Expand Down Expand Up @@ -97,4 +107,6 @@ struct MagicService
entt::scoped_connection m_notifyInterruptCastConnection;
entt::scoped_connection m_addTargetEventConnection;
entt::scoped_connection m_notifyAddTargetConnection;
entt::scoped_connection m_removeSpellEventConnection;
entt::scoped_connection m_notifyRemoveSpell;
};
9 changes: 5 additions & 4 deletions Code/encoding/Messages/ClientMessageFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <Messages/SpellCastRequest.h>
#include <Messages/InterruptCastRequest.h>
#include <Messages/AddTargetRequest.h>
#include <Messages/RemoveSpellRequest.h>
#include <Messages/ProjectileLaunchRequest.h>
#include <Messages/ScriptAnimationRequest.h>
#include <Messages/DrawWeaponRequest.h>
Expand Down Expand Up @@ -66,10 +67,10 @@ struct ClientMessageFactory
{
auto s_visitor = CreateMessageVisitor<
AuthenticationRequest, AssignCharacterRequest, CancelAssignmentRequest, ClientReferencesMoveRequest, EnterInteriorCellRequest, RequestInventoryChanges, RequestFactionsChanges, RequestQuestUpdate, PartyInviteRequest, PartyAcceptInviteRequest, PartyLeaveRequest, PartyCreateRequest,
PartyChangeLeaderRequest, PartyKickRequest, RequestActorValueChanges, RequestActorMaxValueChanges, EnterExteriorCellRequest, RequestHealthChangeBroadcast, ActivateRequest, LockChangeRequest, AssignObjectsRequest, RequestDeathStateChange, ShiftGridCellRequest, RequestOwnershipTransfer,
RequestOwnershipClaim, RequestObjectInventoryChanges, SpellCastRequest, ProjectileLaunchRequest, InterruptCastRequest, AddTargetRequest, ScriptAnimationRequest, DrawWeaponRequest, MountRequest, NewPackageRequest, RequestRespawn, SyncExperienceRequest, RequestEquipmentChanges,
SendChatMessageRequest, TeleportCommandRequest, PlayerRespawnRequest, DialogueRequest, SubtitleRequest, PlayerDialogueRequest, PlayerLevelRequest, TeleportRequest, RequestPlayerHealthUpdate, RequestWeatherChange, RequestCurrentWeather, RequestSetWaypoint, RequestRemoveWaypoint,
SetTimeCommandRequest>;
PartyChangeLeaderRequest, PartyKickRequest, RequestActorValueChanges, RequestActorMaxValueChanges, EnterExteriorCellRequest, RequestHealthChangeBroadcast, ActivateRequest, LockChangeRequest, AssignObjectsRequest, RequestDeathStateChange, ShiftGridCellRequest,
RequestOwnershipTransfer, RequestOwnershipClaim, RequestObjectInventoryChanges, SpellCastRequest, ProjectileLaunchRequest, InterruptCastRequest, AddTargetRequest, ScriptAnimationRequest, DrawWeaponRequest, MountRequest, NewPackageRequest, RequestRespawn, SyncExperienceRequest,
RequestEquipmentChanges, SendChatMessageRequest, TeleportCommandRequest, PlayerRespawnRequest, DialogueRequest, SubtitleRequest, PlayerDialogueRequest, PlayerLevelRequest, TeleportRequest, RequestPlayerHealthUpdate, RequestWeatherChange, RequestCurrentWeather, RequestSetWaypoint,
RequestRemoveWaypoint, RemoveSpellRequest, SetTimeCommandRequest>;

return s_visitor(std::forward<T>(func));
}
Expand Down
13 changes: 13 additions & 0 deletions Code/encoding/Messages/NotifyRemoveSpell.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "NotifyRemoveSpell.h"

void NotifyRemoveSpell::SerializeRaw(TiltedPhoques::Buffer::Writer& aWriter) const noexcept
{
Serialization::WriteVarInt(aWriter, TargetId);
SpellId.Serialize(aWriter);
}

void NotifyRemoveSpell::DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader) noexcept
{
TargetId = Serialization::ReadVarInt(aReader);
SpellId.Deserialize(aReader);
}
24 changes: 24 additions & 0 deletions Code/encoding/Messages/NotifyRemoveSpell.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once
#include "Message.h"
#include <Structs/GameId.h>

struct NotifyRemoveSpell final : ServerMessage
{
static constexpr ServerOpcode Opcode = kNotifyRemoveSpell;

NotifyRemoveSpell() : ServerMessage(Opcode)
{
}

void SerializeRaw(TiltedPhoques::Buffer::Writer& aWriter) const noexcept override;

void DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader) noexcept override;

bool operator==(const NotifyRemoveSpell& acRhs) const noexcept
{
return GetOpcode() == acRhs.GetOpcode() && TargetId == acRhs.TargetId && SpellId == acRhs.SpellId;
}

uint32_t TargetId{};
GameId SpellId{};
};
14 changes: 14 additions & 0 deletions Code/encoding/Messages/RemoveSpellRequest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "EncodingPch.h"
#include "RemoveSpellRequest.h"

void RemoveSpellRequest::SerializeRaw(TiltedPhoques::Buffer::Writer& aWriter) const noexcept
{
Serialization::WriteVarInt(aWriter, TargetId);
SpellId.Serialize(aWriter);
}

void RemoveSpellRequest::DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader) noexcept
{
TargetId = Serialization::ReadVarInt(aReader);
SpellId.Deserialize(aReader);
}
16 changes: 16 additions & 0 deletions Code/encoding/Messages/RemoveSpellRequest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once
#include "Message.h"
#include <Structs/GameId.h>

struct RemoveSpellRequest final: ClientMessage
{
static constexpr ClientOpcode Opcode = kRequestRemoveSpell;
RemoveSpellRequest() : ClientMessage(Opcode) {}
virtual ~RemoveSpellRequest() = default;
void SerializeRaw(TiltedPhoques::Buffer::Writer& aWriter) const noexcept override;
void DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader) noexcept override;
bool operator==(const RemoveSpellRequest& achRhs) const noexcept { return TargetId == achRhs.TargetId && SpellId == achRhs.SpellId && Opcode == achRhs.Opcode; }

uint32_t TargetId{};
GameId SpellId{};
};
3 changes: 2 additions & 1 deletion Code/encoding/Messages/ServerMessageFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <Messages/NotifySpellCast.h>
#include <Messages/NotifyInterruptCast.h>
#include <Messages/NotifyAddTarget.h>
#include <Messages/NotifyRemoveSpell.h>
#include <Messages/NotifyProjectileLaunch.h>
#include <Messages/NotifyScriptAnimation.h>
#include <Messages/NotifyDrawWeapon.h>
Expand Down Expand Up @@ -73,7 +74,7 @@ struct ServerMessageFactory
NotifyActorValueChanges, NotifyPartyJoined, NotifyPartyLeft, NotifyActorMaxValueChanges, NotifyHealthChangeBroadcast, NotifySpawnData, NotifyActivate, NotifyLockChange, AssignObjectsResponse, NotifyDeathStateChange, NotifyOwnershipTransfer, NotifyObjectInventoryChanges, NotifySpellCast,
NotifyProjectileLaunch, NotifyInterruptCast, NotifyAddTarget, NotifyScriptAnimation, NotifyDrawWeapon, NotifyMount, NotifyNewPackage, NotifyRespawn, NotifySyncExperience, NotifyEquipmentChanges, NotifyChatMessageBroadcast, TeleportCommandResponse, NotifyPlayerRespawn, NotifyDialogue,
NotifySubtitle, NotifyPlayerDialogue, NotifyActorTeleport, NotifyRelinquishControl, NotifyPlayerLeft, NotifyPlayerJoined, NotifyDialogue, NotifySubtitle, NotifyPlayerDialogue, NotifyPlayerLevel, NotifyPlayerCellChanged, NotifyTeleport, NotifyPlayerHealthUpdate, NotifySettingsChange,
NotifyWeatherChange, NotifySetWaypoint, NotifyRemoveWaypoint, NotifySetTimeResult>;
NotifyWeatherChange, NotifySetWaypoint, NotifyRemoveWaypoint, NotifySetTimeResult, NotifyRemoveSpell>;

return s_visitor(std::forward<T>(func));
}
Expand Down
2 changes: 2 additions & 0 deletions Code/encoding/Opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ enum ClientOpcode : unsigned char
kSpellCastRequest,
kInterruptCastRequest,
kAddTargetRequest,
kRequestRemoveSpell,
kProjectileLaunchRequest,
kScriptAnimationRequest,
kDrawWeaponRequest,
Expand Down Expand Up @@ -85,6 +86,7 @@ enum ServerOpcode : unsigned char
kNotifySpellCast,
kNotifyInterruptCast,
kNotifyAddTarget,
kNotifyRemoveSpell,
kNotifyProjectileLaunch,
kNotifyScriptAnimation,
kNotifyDrawWeapon,
Expand Down
Loading

0 comments on commit 28aa711

Please sign in to comment.