Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions Sunrise/Sunrise.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@
<None Include="vendor\lua\UPSTREAM_README" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\state\build_data\cache\records\cache_reward_records.cpp" />
<ClCompile Include="src\client\content\items\packages\package_reward_build.cpp" />
<ClCompile Include="src\state\rewards\reward_resolver.cpp" />
<ClCompile Include="src\state\build_data\rewards\reward_catalog.cpp" />
<ClCompile Include="src\state\build_data\rewards\reward_build_data_runtime.cpp" />
<ClCompile Include="src\state\gameplay\external\entity_object_types.cpp" />
<ClCompile Include="src\state\investment\investment_account_store.cpp" />
<ClCompile Include="src\state\investment\investment_database.cpp" />
Expand Down Expand Up @@ -642,7 +647,7 @@
<ClCompile Include="src\state\build_data\cache\records\cache_vendor_records.cpp" />
<ClCompile Include="src\state\build_data\vendors\vendor_catalog.cpp" />
<ClCompile Include="src\state\build_data\vendors\artifact_sale_view.cpp" />
<ClCompile Include="src\state\build_data\vendors\vendor_expression.cpp" />
<ClCompile Include="src\state\unlocks\unlocks_expression.cpp" />
<ClCompile Include="src\client\content\vendors\package_vendor_build.cpp" />
<ClCompile Include="src\state\build_data\cache\records\cache_hash_name_records.cpp" />
<ClCompile Include="src\state\build_data\cache\records\cache_entity_metadata_records.cpp" />
Expand Down Expand Up @@ -1254,6 +1259,11 @@
<ClCompile Remove="vendor\lua\lzio.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\middleware\content\packages\tables\field_reader.h" />
<ClInclude Include="src\client\content\items\packages\package_reward_build.h" />
<ClInclude Include="src\state\rewards\reward_resolver.h" />
<ClInclude Include="src\state\build_data\rewards\reward_catalog.h" />
<ClInclude Include="src\state\build_data\rewards\definition.h" />
<ClInclude Include="resources\resource.h" />
<ClInclude Include="src\core\logging\log.h" />
<ClInclude Include="src\core\logging\snapshot\snapshot.h" />
Expand Down Expand Up @@ -1510,7 +1520,7 @@
<ClInclude Include="src\state\build_data\spawn_sets\definition.h" />
<ClInclude Include="src\state\build_data\vendors\definition.h" />
<ClInclude Include="src\state\build_data\vendors\vendor_catalog.h" />
<ClInclude Include="src\state\build_data\vendors\vendor_expression.h" />
<ClInclude Include="src\state\unlocks\unlocks_expression.h" />
<ClInclude Include="src\client\content\vendors\layout.h" />
<ClInclude Include="src\client\content\vendors\vendor_build.h" />
<ClInclude Include="src\state\build_data\scenarios\definition.h" />
Expand Down
19 changes: 11 additions & 8 deletions Sunrise/src/client/content/items/packages/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "../../../../state/build_data/runtime.h"
#include "../../../../state/build_data/season_pass/definition.h"
#include "../../../../state/build_data/sobjects/sobject_catalog.h"
#include "package_reward_build.h"

namespace sunrise::client::content::items::packages {

Expand Down Expand Up @@ -70,9 +71,10 @@ static_assert(kUnmappedSlot
/** Bank index per unlock slot, indexed by slot. The first mapping row of a slot wins. */
using SlotMap = std::array<std::uint16_t, kSlotSpace>;

/** The four maps one root's two unlock mapping tables carry. 256 KiB together. */
/** Saved-bank indices resolved from the root's flag and value mapping tables. */
struct SlotMaps {
SlotMap accountFlag{};
SlotMap profileFlag{};
SlotMap characterFlag{};
SlotMap accountValue{};
SlotMap characterValue{};
Expand All @@ -83,6 +85,8 @@ struct Storage {
reader::Scratch scratch{};
/** Read once per root. Every domain resolves its unlock slots through these. */
SlotMaps slotMaps{};
/** Reward pools, wrappers and condition tables read alongside the item walk. */
RewardBuild rewardBuild{};
/** Node rows held until the value slot and owned records are resolved. */
std::array<state::build_data::nodes::Definition, state::build_data::nodes::kDefinitionCapacity>
nodeRows{};
Expand Down Expand Up @@ -154,15 +158,11 @@ struct Storage {
state::build_data::progressions::kStepCapacity>
progressionSteps{};
std::size_t progressionStepCount{};
/** Season pass reward rows and the wrapper items they grant. */
/** Season pass reward rows in native claim order. */
std::array<state::build_data::season_pass::Reward,
state::build_data::season_pass::kRewardCapacity>
seasonPassRewards{};
std::array<state::build_data::season_pass::Package,
state::build_data::season_pass::kPackageCapacity>
seasonPassPackages{};
std::size_t seasonPassRewardCount{};
std::size_t seasonPassPackageCount{};
/** Repeatable bounty rows, keyed by the item-type pair their pool shares. */
std::array<state::build_data::bounties::Definition,
state::build_data::bounties::kDefinitionCapacity>
Expand Down Expand Up @@ -207,6 +207,9 @@ struct Storage {
/** @return True when the catalyst catalog is published or cannot exist on this executable. */
[[nodiscard]] bool exotic_catalysts_settled() noexcept;

/** @return True when the reward graph is published or can never be. */
[[nodiscard]] bool reward_definitions_settled() noexcept;

/** Adds one native definition index to the deduplicated request set. */
void request(std::uint16_t definitionIndex, DetailRequests& requested) noexcept;

Expand Down Expand Up @@ -324,7 +327,7 @@ read_investment_constants(const reader::Source& source,
/**
* Reads one root's two unlock mapping tables into the pass slot maps.
* @param source Package source.
* @param storage Pass storage receiving the four maps.
* @param storage Pass storage receiving the five maps.
* @param root Investment root bytes.
* @return True when both account maps read. A character map may stay unmapped.
*/
Expand Down Expand Up @@ -379,7 +382,7 @@ read_investment_constants(const reader::Source& source,
std::span<state::build_data::progressions::Step> steps,
std::size_t& stepCount) noexcept;

/** Reads the season pass reward list and the wrapper items it grants. */
/** Reads the season pass reward list, keeping an unreadable row as an unavailable one. */
[[nodiscard]] bool build_season_pass(const reader::Source& source,
Storage& storage,
std::span<const std::byte> root) noexcept;
Expand Down
23 changes: 16 additions & 7 deletions Sunrise/src/client/content/items/packages/package_item_build.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "../../vendors/vendor_build.h"
#include "build.h"
#include "internal.h"
#include "package_reward_build.h"
#include "package_socket_plug_build.h"

namespace sunrise::client::content::items::packages {
Expand All @@ -34,7 +35,7 @@ namespace {
&& state::build_data::ability_buckets_ready()
&& state::build_data::socket_entry_buckets_ready()
&& state::build_data::progression_definitions_ready()
&& state::build_data::season_pass_ready()
&& state::build_data::season_pass_ready() && reward_definitions_settled()
&& state::build_data::repeatable_bounties_ready()
&& state::build_data::record_definitions_ready()
&& state::build_data::node_definitions_ready()
Expand Down Expand Up @@ -126,7 +127,8 @@ bool build() noexcept {
if (!state::build_data::item_definitions_ready()
|| !state::build_data::record_definitions_ready()
|| !state::build_data::node_definitions_ready()
|| !state::build_data::season_pass_ready() || !exotic_catalysts_settled()) {
|| !state::build_data::season_pass_ready() || !reward_definitions_settled()
|| !exotic_catalysts_settled()) {
reason = "unlock_maps";
if (!read_unlock_slot_maps(
source, storage, std::span<const std::byte>{storage.root})) {
Expand Down Expand Up @@ -184,11 +186,18 @@ bool build() noexcept {
std::span(storage.progressionSteps).first(storage.progressionStepCount));
}
}
if (!state::build_data::season_pass_ready()
&& build_season_pass(source, storage, std::span<const std::byte>{storage.root})) {
(void)state::build_data::publish_season_pass(
std::span(storage.seasonPassRewards).first(storage.seasonPassRewardCount),
std::span(storage.seasonPassPackages).first(storage.seasonPassPackageCount));
// Pass rows bind through the reward condition tables, which load before the pools.
if (!reward_definitions_settled() || !state::build_data::season_pass_ready()) {
(void)storage.rewardBuild.load(
source, storage.scratch, storage.root, storage.slotMaps);
}
if (!state::build_data::season_pass_ready() && storage.rewardBuild.conditions.loaded()
&& build_season_pass(source, storage, std::span<const std::byte>{storage.root})
&& !state::build_data::publish_season_pass(
std::span(storage.seasonPassRewards).first(storage.seasonPassRewardCount))) {
core::log::write(core::log::Channel::client,
core::log::Level::warn,
"ev=pkg stage=season_pass result=fail reason=publish");
}
// Records are read before nodes: a node's lore-book flag and its parent bar come
// from the records it owns, so the record rows must already be in pass storage.
Expand Down
36 changes: 31 additions & 5 deletions Sunrise/src/client/content/items/packages/package_item_rows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,16 @@ bool exotic_catalysts_settled() noexcept {
* @param table Located item index array within storage.child.
* @param rowCount Starts at zero; receives the rows retained even if publication fails.
* @param reason Receives the last stage reached or its failure reason.
* @return True when this pass's required item domains are ready; failure may retain prior results.
* @return True when required non-reward item domains are ready. Failure may retain prior results.
*/
bool build_item_rows(const reader::Source& source,
Storage& storage,
const tables::Array& table,
std::size_t& rowCount,
const char*& reason) noexcept {
const bool needRewards = !reward_definitions_settled();
const bool rewardStorageReady =
needRewards && storage.rewardBuild.begin_items(static_cast<std::size_t>(table.count));
const bool needDefinitions = !state::build_data::item_definitions_ready();
const bool needDetails = !state::build_data::configured_item_details_ready();
const bool needSocketPlugs = !state::build_data::socket_plug_rules_ready();
Expand All @@ -71,7 +74,7 @@ bool build_item_rows(const reader::Source& source,
const bool needDetailRows = needDetails || needSocketRows;
// Bucket equipment slots are derived from this same complete item walk, so a partial retry
// must still revisit the table even when definitions and detail domains already published.
const bool needRows = needDefinitions || needDetailRows || needBuckets;
const bool needRows = needDefinitions || needDetailRows || needBuckets || rewardStorageReady;
bool published = !needRows;
if (retainDetails && storage.details.size() != kDetailCapacity) {
storage.details.assign(kDetailCapacity, build_details::Definition{});
Expand Down Expand Up @@ -107,11 +110,23 @@ bool build_item_rows(const reader::Source& source,
item.definitionHash = row.definitionHash;
item.definitionIndex = static_cast<std::uint16_t>(index);
std::uint32_t itemClass = 0;
if (!reader::read_tag(source, storage.scratch, row.targetTag, storage.definition, itemClass)
|| !tables::items::read_definition(std::span<const std::byte>{storage.definition},
item)) {
if (!reader::read_tag(
source, storage.scratch, row.targetTag, storage.definition, itemClass)) {
// The published catalog read this row before, so its reward waits for a retry; a row
// the catalog lacks stays pruned from both.
state::build_data::items::Definition cataloged{};
if (rewardStorageReady
&& state::build_data::find_item_definition_index(item.definitionIndex, cataloged)) {
storage.rewardBuild.item_unread(item.definitionIndex);
}
continue;
}
if (!tables::items::read_definition(std::span<const std::byte>{storage.definition}, item)) {
continue;
}
if (rewardStorageReady && itemClass == tables::kItemDefinitionClass) {
storage.rewardBuild.item(item.definitionIndex, item.definitionHash, storage.definition);
}
const std::uint32_t plugCategoryHash =
corrected_plug_category(item.definitionHash, item.plugCategoryHash);
build_items::QuestInitialization quest{};
Expand Down Expand Up @@ -175,6 +190,13 @@ bool build_item_rows(const reader::Source& source,
published =
state::build_data::publish_item_definitions(std::span(storage.rows).first(rowCount));
}
// A reward refusal is reported by RewardBuild and does not block the other item domains.
// Rewards publish only beside the item catalog and at its row count, so both omit the same
// unread rows.
const bool rewardsPublished =
!needRewards
|| (rewardStorageReady && state::build_data::item_definitions_ready()
&& storage.rewardBuild.publish(state::build_data::item_definition_count()));
if (!published) {
reason = !detailStorageReady ? "detail_storage"
: !requestsFit ? "detail_capacity"
Expand Down Expand Up @@ -301,6 +323,10 @@ bool build_item_rows(const reader::Source& source,
report_ability_count(abilityCount);
}
}
// Named last; later stages overwrite the reason as they start.
if (published && !rewardsPublished) {
reason = "rewards";
}
return published && state::build_data::item_definitions_ready()
&& state::build_data::configured_item_details_ready()
&& state::build_data::socket_plug_rules_ready() && exotic_catalysts_settled()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ void clear_slot_map(SlotMap& output) noexcept {
output.fill(kUnmappedSlot);
}

/** Clears all four so an unread table cannot leave a prior pass's indexes addressable. */
/** An unread table must not leave a prior pass's indices addressable. */
void clear_slot_maps(SlotMaps& maps) noexcept {
clear_slot_map(maps.accountFlag);
clear_slot_map(maps.profileFlag);
clear_slot_map(maps.characterFlag);
clear_slot_map(maps.accountValue);
clear_slot_map(maps.characterValue);
Expand Down Expand Up @@ -113,7 +114,7 @@ read_slot_map(std::span<const std::byte> blob, std::size_t descriptor, SlotMap&
/**
* A gate names a slot; its saved bank index is the mapping row that names that slot.
* @param source Borrowed package source.
* @param storage Receives four slot maps and retained value-map bytes; may be partial on failure.
* @param storage Receives slot maps and retained value-map bytes; may be partial on failure.
* @param root Investment root bytes naming the flag and value mapping tables.
* @return True when both account maps read; a character map may remain unmapped.
*/
Expand All @@ -131,6 +132,7 @@ bool read_unlock_slot_maps(const reader::Source& source,
const std::span<const std::byte> flagMap{storage.child};
const bool flagMapRead =
read_slot_map(flagMap, tables::kAccountFlagMapDescriptor, maps.accountFlag);
(void)read_slot_map(flagMap, tables::kProfileFlagMapDescriptor, maps.profileFlag);
(void)read_slot_map(flagMap, tables::kCharacterFlagMapDescriptor, maps.characterFlag);

std::uint32_t valueMapTag = 0;
Expand Down
Loading
Loading