Skip to content

Commit

Permalink
feat: major improvements to equip sync
Browse files Browse the repository at this point in the history
  • Loading branch information
RobbeBryssinck committed Mar 28, 2022
1 parent e993474 commit 04673a2
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 72 deletions.
6 changes: 4 additions & 2 deletions Code/client/Events/EquipmentChangeEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

struct EquipmentChangeEvent
{
uint32_t ActorId{ 0 };
uint32_t ActorId = 0;
uint32_t ItemId = 0;
uint32_t EquipSlotId = 0;
uint32_t Count = 0;
bool Unequip = false;
bool IsLeft = false;
bool IsSpell = false;
bool IsShout = false;
};
4 changes: 1 addition & 3 deletions Code/client/Games/Skyrim/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,20 +319,18 @@ void Actor::SetActorInventory(Inventory& aInventory) noexcept
SetEquipment(aInventory.CurrentEquipment);
}

// TODO: remove all the unequip stuff, not needed anymore
void Actor::SetEquipment(const Equipment& acEquipment) noexcept
{
auto* pEquipManager = EquipManager::Get();
auto& modSystem = World::Get().GetModSystem();

// TODO: now, you gotta set armor and trinkets too

const Equipment cCurrentEquipment = GetEquipment();

if (acEquipment.LeftHandWeapon != cCurrentEquipment.LeftHandWeapon)
{
if (acEquipment.LeftHandWeapon)
{
// TODO: isn't the right hand the main weapon?
uint32_t mainHandWeaponId = modSystem.GetGameId(acEquipment.LeftHandWeapon);
pEquipManager->Equip(this, TESForm::GetById(mainHandWeaponId), nullptr, 1, DefaultObjectManager::Get().leftEquipSlot, false, true, false, false);
}
Expand Down
95 changes: 48 additions & 47 deletions Code/client/Games/Skyrim/EquipManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,99 +79,102 @@ EquipManager* EquipManager::Get() noexcept
return *s_singleton.Get();
}

void* EquipManager::EquipSpell(Actor* apActor, TESForm* apSpell, uint32_t aSlotId)
// TODO: this equip func doesn't seem to work too well for armor
void* EquipManager::Equip(Actor* apActor, TESForm* apItem, ExtraDataList* apExtraDataList, int aCount, void* aSlot, bool aUnk1, bool aPreventEquip, bool aUnk2, bool aUnk3)
{
TP_THIS_FUNCTION(TEquipSpellInternal, void*, EquipManager, Actor*, TESForm*, uint32_t);
POINTER_SKYRIMSE(TEquipSpellInternal, s_equipFunc, 38896);
TP_THIS_FUNCTION(TEquipInternal, void*, EquipManager, Actor * apActor, TESForm * apItem, ExtraDataList * apExtraDataList, int aCount, void* aSlot, bool aUnk1, bool aPreventEquip, bool aUnk2, bool aUnk3);
POINTER_SKYRIMSE(TEquipInternal, s_equipFunc, 38894);

ScopedEquipOverride equipOverride;

const auto result = ThisCall(s_equipFunc, this, apActor, apSpell, aSlotId);
const auto result = ThisCall(s_equipFunc, this, apActor, apItem, apExtraDataList, aCount, aSlot, aUnk1, aPreventEquip, aUnk2, aUnk3);

return result;
}

void* EquipManager::UnEquipSpell(Actor* apActor, TESForm* apSpell, uint32_t aSlotId)
// TODO: crash in TESObjectREFR::UnequipItem() virtual func (offset 0xA1) when remote players shoots last arrow in quiver (and switching)
// This might be caused by the server deleting it and the client detecting the last arrow being shot
// Maybe hook UnequipItem() and cancel it remotely?
void* EquipManager::UnEquip(Actor* apActor, TESForm* apItem, ExtraDataList* apExtraDataList, int aCount, void* aSlot, int aUnk1, bool aPreventEquip, bool aUnk2, bool aUnk3, void* aUnk4)
{
TP_THIS_FUNCTION(TUnEquipSpellInternal, void*, EquipManager, Actor*, TESForm*, uint32_t);
POINTER_SKYRIMSE(TUnEquipSpellInternal, s_unequipFunc, 38903);
TP_THIS_FUNCTION(TUnEquipInternal, void*, EquipManager, Actor * apActor, TESForm * apItem, ExtraDataList * apExtraDataList, int aCount, void* aSlot, int aUnk1, bool aPreventEquip, bool aUnk2, bool aUnk3, void* aUnk4);
POINTER_SKYRIMSE(TUnEquipInternal, s_unequipFunc, 38901);

ScopedEquipOverride equipOverride;

const auto result = ThisCall(s_unequipFunc, this, apActor, apSpell, aSlotId);
const auto result = ThisCall(s_unequipFunc, this, apActor, apItem, apExtraDataList, aCount, aSlot, aUnk1, aPreventEquip, aUnk2, aUnk3, aUnk4);

return result;
}

void* EquipManager::EquipShout(Actor* apActor, TESForm* apShout)
void* EquipManager::EquipSpell(Actor* apActor, TESForm* apSpell, uint32_t aSlotId)
{
TP_THIS_FUNCTION(TEquipShoutInternal, void*, EquipManager, Actor*, TESForm*);
POINTER_SKYRIMSE(TEquipShoutInternal, s_equipFunc, 38897);
TP_THIS_FUNCTION(TEquipSpellInternal, void*, EquipManager, Actor*, TESForm*, uint32_t);
POINTER_SKYRIMSE(TEquipSpellInternal, s_equipFunc, 38896);

ScopedEquipOverride equipOverride;

const auto result = ThisCall(s_equipFunc, this, apActor, apShout);
const auto result = ThisCall(s_equipFunc, this, apActor, apSpell, aSlotId);

return result;
}

void* EquipManager::UnEquipShout(Actor* apActor, TESForm* apShout)
void* EquipManager::UnEquipSpell(Actor* apActor, TESForm* apSpell, uint32_t aSlotId)
{
TP_THIS_FUNCTION(TUnEquipShoutInternal, void*, EquipManager, Actor*, TESForm*);
POINTER_SKYRIMSE(TUnEquipShoutInternal, s_unequipFunc, 38903);
TP_THIS_FUNCTION(TUnEquipSpellInternal, void*, EquipManager, Actor*, TESForm*, uint32_t);
POINTER_SKYRIMSE(TUnEquipSpellInternal, s_unequipFunc, 38903);

ScopedEquipOverride equipOverride;

const auto result = ThisCall(s_unequipFunc, this, apActor, apShout);
const auto result = ThisCall(s_unequipFunc, this, apActor, apSpell, aSlotId);

return result;
}


void* EquipManager::Equip(Actor* apActor, TESForm* apItem, ExtraDataList* apExtraDataList, int aCount, void* aSlot, bool aUnk1, bool aPreventEquip, bool aUnk2, bool aUnk3)
void* EquipManager::EquipShout(Actor* apActor, TESForm* apShout)
{
TP_THIS_FUNCTION(TEquipInternal, void*, EquipManager, Actor * apActor, TESForm * apItem, ExtraDataList * apExtraDataList, int aCount, void* aSlot, bool aUnk1, bool aPreventEquip, bool aUnk2, bool aUnk3);
POINTER_SKYRIMSE(TEquipInternal, s_equipFunc, 38894);
TP_THIS_FUNCTION(TEquipShoutInternal, void*, EquipManager, Actor*, TESForm*);
POINTER_SKYRIMSE(TEquipShoutInternal, s_equipFunc, 38897);

ScopedEquipOverride equipOverride;

const auto result = ThisCall(s_equipFunc, this, apActor, apItem, apExtraDataList, aCount, aSlot, aUnk1, aPreventEquip, aUnk2, aUnk3);
const auto result = ThisCall(s_equipFunc, this, apActor, apShout);

return result;
}

void* EquipManager::UnEquip(Actor* apActor, TESForm* apItem, ExtraDataList* apExtraDataList, int aCount, void* aSlot, int aUnk1, bool aPreventEquip, bool aUnk2, bool aUnk3, void* aUnk4)
void* EquipManager::UnEquipShout(Actor* apActor, TESForm* apShout)
{
TP_THIS_FUNCTION(TUnEquipInternal, void*, EquipManager, Actor * apActor, TESForm * apItem, ExtraDataList * apExtraDataList, int aCount, void* aSlot, int aUnk1, bool aPreventEquip, bool aUnk2, bool aUnk3, void* aUnk4);
POINTER_SKYRIMSE(TUnEquipInternal, s_unequipFunc, 38901);
TP_THIS_FUNCTION(TUnEquipShoutInternal, void*, EquipManager, Actor*, TESForm*);
POINTER_SKYRIMSE(TUnEquipShoutInternal, s_unequipFunc, 38903);

ScopedEquipOverride equipOverride;

const auto result = ThisCall(s_unequipFunc, this, apActor, apItem, apExtraDataList, aCount, aSlot, aUnk1, aPreventEquip, aUnk2, aUnk3, aUnk4);
const auto result = ThisCall(s_unequipFunc, this, apActor, apShout);

return result;
}

void* TP_MAKE_THISCALL(EquipHook, EquipManager, Actor* apActor, TESForm* apItem, EquipData* apData)
{
spdlog::info("Equip, slot: {:X}", apData->slot->formID);
if (!apActor)
return nullptr;

const auto pExtension = apActor->GetExtension();
if (pExtension->IsRemote() && !ScopedEquipOverride::IsOverriden())
if (pExtension->IsRemote())
{
spdlog::info("Actor[{:X}]::Equip(), item form id: {:X}", apActor->formID, apItem->formID);
return nullptr;
if (!ScopedEquipOverride::IsOverriden())
return nullptr;
}

if (pExtension->IsLocal())
{
EquipmentChangeEvent evt;
evt.ActorId = apActor->formID;
evt.IsLeft = apData->slot == DefaultObjectManager::Get().leftEquipSlot;
evt.IsSpell = false;
evt.IsShout = false;
evt.Count = apData->count;
evt.ItemId = apItem->formID;
evt.EquipSlotId = apData->slot ? apData->slot->formID : 0;

World::Get().GetRunner().Trigger(evt);
}
Expand All @@ -183,7 +186,6 @@ void* TP_MAKE_THISCALL(EquipHook, EquipManager, Actor* apActor, TESForm* apItem,

void* TP_MAKE_THISCALL(UnEquipHook, EquipManager, Actor* apActor, TESForm* apItem, UnEquipData* apData)
{
spdlog::info("Unequip, slot: {:X}", apData->slot->formID);
if (!apActor)
return nullptr;

Expand All @@ -196,19 +198,21 @@ void* TP_MAKE_THISCALL(UnEquipHook, EquipManager, Actor* apActor, TESForm* apIte
spdlog::warn("Sending Unequip event");
EquipmentChangeEvent evt;
evt.ActorId = apActor->formID;
evt.IsLeft = apData->slot == DefaultObjectManager::Get().leftEquipSlot;
evt.IsSpell = false;
evt.IsShout = false;
evt.Count = apData->count;
evt.ItemId = apItem->formID;
evt.EquipSlotId = apData->slot ? apData->slot->formID : 0;
evt.Unequip = true;

World::Get().GetRunner().Trigger(evt);
}

spdlog::info("Actor[{:X}]::Unequip(), item form id: {:X}", apActor->formID, apItem->formID);

return ThisCall(RealUnEquip, apThis, apActor, apItem, apData);
}

void* TP_MAKE_THISCALL(EquipSpellHook, EquipManager, Actor* apActor, TESForm* apSpell, MagicEquipParams* apParams)
{
spdlog::info("EquipSpell, slot: {:X}", apParams->pEquipSlot->formID);
if (!apActor)
return nullptr;

Expand All @@ -220,9 +224,9 @@ void* TP_MAKE_THISCALL(EquipSpellHook, EquipManager, Actor* apActor, TESForm* ap
{
EquipmentChangeEvent evt;
evt.ActorId = apActor->formID;
evt.IsLeft = apParams->pEquipSlot == DefaultObjectManager::Get().leftEquipSlot;
evt.ItemId = apSpell->formID;
evt.EquipSlotId = apParams->pEquipSlot->formID;
evt.IsSpell = true;
evt.IsShout = false;

World::Get().GetRunner().Trigger(evt);
}
Expand All @@ -234,7 +238,6 @@ void* TP_MAKE_THISCALL(EquipSpellHook, EquipManager, Actor* apActor, TESForm* ap

void* TP_MAKE_THISCALL(UnEquipSpellHook, EquipManager, Actor* apActor, TESForm* apSpell, MagicEquipParams* apParams)
{
spdlog::info("UnequipSpell, slot: {:X}", apParams->pEquipSlot->formID);
if (!apActor)
return nullptr;

Expand All @@ -247,9 +250,10 @@ void* TP_MAKE_THISCALL(UnEquipSpellHook, EquipManager, Actor* apActor, TESForm*
spdlog::warn("Sending UnequipSpell event");
EquipmentChangeEvent evt;
evt.ActorId = apActor->formID;
evt.IsLeft = apParams->pEquipSlot == DefaultObjectManager::Get().leftEquipSlot;
evt.ItemId = apSpell->formID;
evt.EquipSlotId = apParams->pEquipSlot->formID;
evt.Unequip = true;
evt.IsSpell = true;
evt.IsShout = false;

World::Get().GetRunner().Trigger(evt);
}
Expand All @@ -259,7 +263,6 @@ void* TP_MAKE_THISCALL(UnEquipSpellHook, EquipManager, Actor* apActor, TESForm*

void* TP_MAKE_THISCALL(EquipShoutHook, EquipManager, Actor* apActor, TESForm* apShout, ShoutEquipParams* apParams)
{
spdlog::info("EquipShout");
if (!apActor)
return nullptr;

Expand All @@ -271,8 +274,7 @@ void* TP_MAKE_THISCALL(EquipShoutHook, EquipManager, Actor* apActor, TESForm* ap
{
EquipmentChangeEvent evt;
evt.ActorId = apActor->formID;
evt.IsLeft = false;
evt.IsSpell = false;
evt.ItemId = apShout->formID;
evt.IsShout = true;

World::Get().GetRunner().Trigger(evt);
Expand All @@ -285,7 +287,6 @@ void* TP_MAKE_THISCALL(EquipShoutHook, EquipManager, Actor* apActor, TESForm* ap

void* TP_MAKE_THISCALL(UnEquipShoutHook, EquipManager, Actor* apActor, TESForm* apShout, ShoutEquipParams* apParams)
{
spdlog::info("UnequipShout");
if (!apActor)
return nullptr;

Expand All @@ -298,8 +299,8 @@ void* TP_MAKE_THISCALL(UnEquipShoutHook, EquipManager, Actor* apActor, TESForm*
spdlog::warn("Sending UnequipShout event");
EquipmentChangeEvent evt;
evt.ActorId = apActor->formID;
evt.IsLeft = false;
evt.IsSpell = false;
evt.ItemId = apShout->formID;
evt.Unequip = true;
evt.IsShout = true;

World::Get().GetRunner().Trigger(evt);
Expand Down
55 changes: 53 additions & 2 deletions Code/client/Services/Generic/InventoryService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include <Forms/TESWorldSpace.h>
#include <Games/TES.h>
#include <Games/Overrides.h>
#include <EquipManager.h>
#include <DefaultObjectManager.h>

InventoryService::InventoryService(World& aWorld, entt::dispatcher& aDispatcher, TransportService& aTransport) noexcept
: m_world(aWorld)
Expand Down Expand Up @@ -94,9 +96,20 @@ void InventoryService::OnEquipmentChangeEvent(const EquipmentChangeEvent& acEven
if (!pActor)
return;

auto& modSystem = World::Get().GetModSystem();

RequestEquipmentChanges request;
request.ServerId = serverIdRes.value();
request.CurrentEquipment = pActor->GetEquipment();

if (!modSystem.GetServerModId(acEvent.EquipSlotId, request.EquipSlotId))
return;
if (!modSystem.GetServerModId(acEvent.ItemId, request.ItemId))
return;

request.Count = acEvent.Count;
request.Unequip = acEvent.Unequip;
request.IsSpell = acEvent.IsSpell;
request.IsShout = acEvent.IsShout;

m_transport.Send(request);

Expand Down Expand Up @@ -146,7 +159,45 @@ void InventoryService::OnNotifyEquipmentChanges(const NotifyEquipmentChanges& ac
if (!pActor)
return;

//pActor->SetEquipment(acMessage.CurrentEquipment);
auto& modSystem = World::Get().GetModSystem();

uint32_t itemId = modSystem.GetGameId(acMessage.ItemId);
TESForm* pItem = TESForm::GetById(itemId);

uint32_t equipSlotId = modSystem.GetGameId(acMessage.EquipSlotId);
TESForm* pEquipSlot = TESForm::GetById(equipSlotId);

spdlog::critical("pEquipSlot: {:X}, pItem: {:X}", (uint64_t)pEquipSlot, (uint64_t)pItem);

auto* pEquipManager = EquipManager::Get();

if (acMessage.IsSpell)
{
uint32_t spellSlotId = 0;
if (pEquipSlot == DefaultObjectManager::Get().rightEquipSlot)
spellSlotId = 1;

if (acMessage.Unequip)
pEquipManager->UnEquipSpell(pActor, pItem, spellSlotId);
else
pEquipManager->EquipSpell(pActor, pItem, spellSlotId);
}
else if (acMessage.IsShout)
{
if (acMessage.Unequip)
pEquipManager->UnEquipShout(pActor, pItem);
else
pEquipManager->EquipShout(pActor, pItem);
}
else
{
// TODO: ExtraData necessary? probably
// TODO: verify the bool params
if (acMessage.Unequip)
pEquipManager->UnEquip(pActor, pItem, nullptr, acMessage.Count, pEquipSlot, true, false, true, false, nullptr);
else
pEquipManager->Equip(pActor, pItem, nullptr, acMessage.Count, pEquipSlot, false, true, false, false);
}
}

void InventoryService::RunWeaponStateUpdates() noexcept
Expand Down
16 changes: 10 additions & 6 deletions Code/encoding/Messages/NotifyEquipmentChanges.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
void NotifyEquipmentChanges::SerializeRaw(TiltedPhoques::Buffer::Writer& aWriter) const noexcept
{
Serialization::WriteVarInt(aWriter, ServerId);
EquippedItem.Serialize(aWriter);
Serialization::WriteBool(aWriter, IsLeft);
ItemId.Serialize(aWriter);
EquipSlotId.Serialize(aWriter);
Serialization::WriteVarInt(aWriter, Count);
Serialization::WriteBool(aWriter, Unequip);
Serialization::WriteBool(aWriter, IsSpell);
Serialization::WriteBool(aWriter, IsShout);
}
Expand All @@ -15,8 +17,10 @@ void NotifyEquipmentChanges::DeserializeRaw(TiltedPhoques::Buffer::Reader& aRead
ServerMessage::DeserializeRaw(aReader);

ServerId = Serialization::ReadVarInt(aReader) & 0xFFFFFFFF;
EquippedItem.Deserialize(aReader);
IsLeft == Serialization::ReadBool(aReader);
IsSpell == Serialization::ReadBool(aReader);
IsShout == Serialization::ReadBool(aReader);
ItemId.Deserialize(aReader);
EquipSlotId.Deserialize(aReader);
Count = Serialization::ReadVarInt(aReader) & 0xFFFFFFFF;
Unequip = Serialization::ReadBool(aReader);
IsSpell = Serialization::ReadBool(aReader);
IsShout = Serialization::ReadBool(aReader);
}
Loading

0 comments on commit 04673a2

Please sign in to comment.