Skip to content

Commit 7bf195c

Browse files
rfortierotsffsmiredirex
authored
Experimental Misc. quest sync w/ server option, default off (#799)
* Quest Sync update: Include miscellaneous tasks and more side tasks * Add missing header so IDE can find definitions used in the file. Didn't affect compilation. * Misc quest sync usability / debuggability Builds on PR #788. Adds a server flag, default false, to enable miscellaneous quest syncing. Adds logging to misc. quest sync & communications so we can figure out what went wrong if it doesn't work. Update fixes formatting of GameIds in logs so they actually work. * Fix GCC (linux) compilation complaint. GCC doesn't like the string-splice with __FUNCTION__, but it works everywhere else. I think clang-format wrapped the line on me and this is a quick patch to test that. * Remove string splice that doesn't work with GCC * Review comments * Separate commit for this longstanding bugfix so it can be cherry-picked into its own PR * Resolve some PR comments refining which quests are non-syncable. Restore original location of "inparty" check, there were side effects. Tweakfix IsNonSyncableQuest Symplified logic. * constexpr way to get std::array<T, size> size. Co-authored-by: Daniil <[email protected]> --------- Co-authored-by: otsffs <[email protected]> Co-authored-by: Daniil <[email protected]>
1 parent 7acd594 commit 7bf195c

File tree

11 files changed

+95
-14
lines changed

11 files changed

+95
-14
lines changed

Code/client/Services/Generic/QuestService.cpp

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,24 @@ BSTEventResult QuestService::OnEvent(const TESQuestStartStopEvent* apEvent, cons
6363
{
6464
if (IsNonSyncableQuest(pQuest))
6565
return BSTEventResult::kOk;
66-
66+
67+
if (pQuest->type == TESQuest::Type::None || pQuest->type == TESQuest::Type::Miscellaneous)
68+
{
69+
// Perhaps redundant, but necessary. We need the logging and
70+
// the lambda coming up is queued and runs later
71+
GameId Id;
72+
auto& modSys = m_world.GetModSystem();
73+
if (modSys.GetServerModId(pQuest->formID, Id))
74+
{
75+
spdlog::info(__FUNCTION__ ": queuing type none/misc quest gameId {:X} questStage {} questStatus {} questType {} formId {:X} name {}",
76+
Id.LogFormat(), pQuest->currentStage, pQuest->IsStopped() ? RequestQuestUpdate::Stopped : RequestQuestUpdate::Started,
77+
static_cast<std::underlying_type_t<TESQuest::Type>>(pQuest->type),
78+
pQuest->formID, pQuest->fullName.value.AsAscii());
79+
}
80+
}
81+
6782
m_world.GetRunner().Queue(
68-
[&, formId = pQuest->formID, stageId = pQuest->currentStage, stopped = pQuest->IsStopped()]()
83+
[&, formId = pQuest->formID, stageId = pQuest->currentStage, stopped = pQuest->IsStopped(), type = pQuest->type]()
6984
{
7085
GameId Id;
7186
auto& modSys = m_world.GetModSystem();
@@ -75,6 +90,7 @@ BSTEventResult QuestService::OnEvent(const TESQuestStartStopEvent* apEvent, cons
7590
update.Id = Id;
7691
update.Stage = stageId;
7792
update.Status = stopped ? RequestQuestUpdate::Stopped : RequestQuestUpdate::Started;
93+
update.ClientQuestType = static_cast<std::underlying_type_t<TESQuest::Type>>(type);
7894

7995
m_world.GetTransport().Send(update);
8096
}
@@ -97,8 +113,24 @@ BSTEventResult QuestService::OnEvent(const TESQuestStageEvent* apEvent, const Ev
97113
if (IsNonSyncableQuest(pQuest))
98114
return BSTEventResult::kOk;
99115

116+
if (pQuest->type == TESQuest::Type::None || pQuest->type == TESQuest::Type::Miscellaneous)
117+
{
118+
// Perhaps redundant, but necessary. We need the logging and
119+
// the lambda coming up is queued and runs later
120+
GameId Id;
121+
auto& modSys = m_world.GetModSystem();
122+
if (modSys.GetServerModId(pQuest->formID, Id))
123+
{
124+
spdlog::info(__FUNCTION__ ": queuing type none/misc quest gameId {:X} questStage {} questStatus {} questType {} formId {:X} name {}",
125+
Id.LogFormat(), pQuest->currentStage,
126+
RequestQuestUpdate::StageUpdate,
127+
static_cast<std::underlying_type_t<TESQuest::Type>>(pQuest->type),
128+
pQuest->formID, pQuest->fullName.value.AsAscii());
129+
}
130+
}
131+
100132
m_world.GetRunner().Queue(
101-
[&, formId = apEvent->formId, stageId = apEvent->stageId]()
133+
[&, formId = apEvent->formId, stageId = apEvent->stageId, type = pQuest->type]()
102134
{
103135
GameId Id;
104136
auto& modSys = m_world.GetModSystem();
@@ -108,6 +140,7 @@ BSTEventResult QuestService::OnEvent(const TESQuestStageEvent* apEvent, const Ev
108140
update.Id = Id;
109141
update.Stage = stageId;
110142
update.Status = RequestQuestUpdate::StageUpdate;
143+
update.ClientQuestType = static_cast<std::underlying_type_t<TESQuest::Type>>(type);
111144

112145
m_world.GetTransport().Send(update);
113146
}
@@ -128,6 +161,13 @@ void QuestService::OnQuestUpdate(const NotifyQuestUpdate& aUpdate) noexcept
128161
return;
129162
}
130163

164+
if (pQuest->type == TESQuest::Type::None || pQuest->type == TESQuest::Type::Miscellaneous)
165+
{
166+
spdlog::info(__FUNCTION__ ": receiving type none/misc quest update gameId {:X} questStage {} questStatus {} questType {} formId {:X} name {}",
167+
aUpdate.Id.LogFormat(), aUpdate.Stage, aUpdate.Status,
168+
aUpdate.ClientQuestType, formId, pQuest->fullName.value.AsAscii());
169+
}
170+
131171
bool bResult = false;
132172
switch (aUpdate.Status)
133173
{
@@ -168,12 +208,21 @@ bool QuestService::StopQuest(uint32_t aformId)
168208
return false;
169209
}
170210

211+
static constexpr std::array kNonSyncableQuestIds = std::to_array<uint32_t>({
212+
0x2BA16, // Werewolf transformation quest
213+
0x20071D0, // Vampire transformation quest
214+
0x3AC44, // MS13BleakFallsBarrowLeverScene
215+
// 0xFE014801, // Unknown dynamic ID, kept as note, maybe lookup correct ID this game?
216+
0xF2593 // Skill experience quest
217+
});
218+
171219
bool QuestService::IsNonSyncableQuest(TESQuest* apQuest)
172220
{
173-
// non story quests are "blocked" and not synced
174-
auto& stages = apQuest->stages;
175-
return apQuest->type == TESQuest::Type::None // internal event
176-
|| apQuest->type == TESQuest::Type::Miscellaneous || stages.Empty();
221+
// Quests with no quest stages are never synced. Most TESQues::Type:: quests should
222+
// be synced, including Type::None and Type::Miscellaneous, but there are a few
223+
// known exceptions that should be excluded that are in the table.
224+
return apQuest->stages.Empty()
225+
|| std::find(kNonSyncableQuestIds.begin(), kNonSyncableQuestIds.end(), apQuest->formID) != kNonSyncableQuestIds.end();
177226
}
178227

179228
void QuestService::DebugDumpQuests()

Code/encoding/Messages/NotifyQuestUpdate.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ void NotifyQuestUpdate::SerializeRaw(TiltedPhoques::Buffer::Writer& aWriter) con
77
Id.Serialize(aWriter);
88
aWriter.WriteBits(Stage, 16);
99
aWriter.WriteBits(Status, 8);
10+
aWriter.WriteBits(ClientQuestType, 8);
1011
}
1112

1213
void NotifyQuestUpdate::DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader) noexcept
@@ -20,4 +21,7 @@ void NotifyQuestUpdate::DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader) n
2021

2122
aReader.ReadBits(tmp, 8);
2223
Status = tmp & 0xFF;
24+
25+
aReader.ReadBits(tmp, 8);
26+
ClientQuestType = tmp & 0xFF;
2327
}

Code/encoding/Messages/NotifyQuestUpdate.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ struct NotifyQuestUpdate final : ServerMessage
1515
void SerializeRaw(TiltedPhoques::Buffer::Writer& aWriter) const noexcept override;
1616
void DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader) noexcept override;
1717

18-
bool operator==(const NotifyQuestUpdate& acRhs) const noexcept { return GetOpcode() == acRhs.GetOpcode() && Id == acRhs.Id && Stage == acRhs.Stage && Status == acRhs.Status; }
18+
bool operator==(const NotifyQuestUpdate& acRhs) const noexcept { return GetOpcode() == acRhs.GetOpcode() && Id == acRhs.Id && Stage == acRhs.Stage && Status == acRhs.Status && ClientQuestType == acRhs.ClientQuestType; }
1919

2020
enum StatusCode : uint8_t
2121
{
@@ -27,4 +27,5 @@ struct NotifyQuestUpdate final : ServerMessage
2727
GameId Id;
2828
uint16_t Stage;
2929
uint8_t Status;
30+
uint8_t ClientQuestType;
3031
};

Code/encoding/Messages/RequestQuestUpdate.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ void RequestQuestUpdate::SerializeRaw(TiltedPhoques::Buffer::Writer& aWriter) co
77
Id.Serialize(aWriter);
88
aWriter.WriteBits(Stage, 16);
99
aWriter.WriteBits(Status, 8);
10+
aWriter.WriteBits(ClientQuestType, 8);
1011
}
1112

1213
void RequestQuestUpdate::DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader) noexcept
@@ -20,4 +21,7 @@ void RequestQuestUpdate::DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader)
2021

2122
aReader.ReadBits(tmp, 8);
2223
Status = tmp & 0xFF;
24+
25+
aReader.ReadBits(tmp, 8);
26+
ClientQuestType = tmp & 0xFF;
2327
}

Code/encoding/Messages/RequestQuestUpdate.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ struct RequestQuestUpdate final : ClientMessage
1515
void SerializeRaw(TiltedPhoques::Buffer::Writer& aWriter) const noexcept override;
1616
void DeserializeRaw(TiltedPhoques::Buffer::Reader& aReader) noexcept override;
1717

18-
bool operator==(const RequestQuestUpdate& acRhs) const noexcept { return GetOpcode() == acRhs.GetOpcode() && Id == acRhs.Id && Stage == acRhs.Stage && Status == acRhs.Status; }
18+
bool operator==(const RequestQuestUpdate& acRhs) const noexcept { return GetOpcode() == acRhs.GetOpcode() && Id == acRhs.Id && Stage == acRhs.Stage && Status == acRhs.Status && ClientQuestType == acRhs.ClientQuestType; }
1919

2020
enum StatusCode : uint8_t
2121
{
@@ -27,4 +27,5 @@ struct RequestQuestUpdate final : ClientMessage
2727
GameId Id;
2828
uint16_t Stage;
2929
uint8_t Status;
30+
uint8_t ClientQuestType;
3031
};

Code/encoding/Structs/GameId.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct GameId
1515

1616
void Serialize(TiltedPhoques::Buffer::Writer& aWriter) const noexcept;
1717
void Deserialize(TiltedPhoques::Buffer::Reader& aReader) noexcept;
18+
inline uint64_t LogFormat() const noexcept { return static_cast<uint64_t>(ModId) << 32 | BaseId; }
1819

1920
uint32_t BaseId;
2021
uint32_t ModId;

Code/server/Services/CharacterService.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <Messages/NotifyActorTeleport.h>
4141
#include <Messages/NotifyRelinquishControl.h>
4242

43+
#include <Setting.h>
4344
namespace
4445
{
4546
Console::Setting bEnableXpSync{"Gameplay:bEnableXpSync", "Syncs combat XP within the party", true};

Code/server/Services/InventoryService.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <Messages/NotifyEquipmentChanges.h>
1212
#include <Messages/DrawWeaponRequest.h>
1313

14+
#include <Setting.h>
1415
namespace
1516
{
1617
Console::Setting bEnableItemDrops{"Gameplay:bEnableItemDrops", "(Experimental) Syncs dropped items by players", false};

Code/server/Services/PartyService.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <Messages/PartyKickRequest.h>
2020
#include <Messages/NotifyPlayerJoined.h>
2121

22+
#include <Setting.h>
2223
namespace
2324
{
2425
Console::Setting bAutoPartyJoin{"Gameplay:bAutoPartyJoin", "Join parties automatically, as long as there is only one party in the server", true};
@@ -149,7 +150,7 @@ void PartyService::OnPartyChangeLeader(const PacketEvent<PartyChangeLeaderReques
149150

150151
if (!pNewLeader)
151152
{
152-
spdlog::error("[PartyService]: Player {} does not exist¨. Cannot change paty leader", message.PartyMemberPlayerId);
153+
spdlog::error("[PartyService]: Player {} does not exist. Cannot change party leader", message.PartyMemberPlayerId);
153154
return;
154155
}
155156

Code/server/Services/PlayerService.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <Messages/NotifyPlayerLevel.h>
1919
#include <Messages/NotifyPlayerCellChanged.h>
2020

21+
#include <Setting.h>
2122
namespace
2223
{
2324
Console::Setting fGoldLossFactor{"Gameplay:fGoldLossFactor", "Factor of the amount of gold lost on death", 0.0f};

0 commit comments

Comments
 (0)