bap_world_reward_queue.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. #include <memory>
  2. #include <mutex>
  3. #include <new>
  4. #include "../../state/build_data/runtime.h"
  5. #include "../../state/investment/store_internal.h"
  6. #include "core/logging/log.h"
  7. #include "internal.h"
  8. namespace sunrise::server::bap {
  9. namespace {
  10. /** Grants and retires one earned reward in the same database transaction. */
  11. bool commit_world_reward(const WorldRewardRequest& request) noexcept {
  12. state::investment::store::Transaction transaction;
  13. if (!transaction.ready()) {
  14. return false;
  15. }
  16. bool committed = false;
  17. if (request.kind == WorldRewardKind::item) {
  18. if (state::item_grant_route(request.itemDefinitionIndex) == state::ItemGrantRoute::quest) {
  19. state::PendingItemAcquisition acquisition;
  20. committed = request.quantity == 1
  21. && state::prepare_item_acquisition_for_item(request.itemDefinitionIndex,
  22. acquisition)
  23. && state::commit_item_acquisition(acquisition);
  24. } else {
  25. const std::unique_ptr<state::PendingRecordRewardGrant> grant(
  26. new (std::nothrow) state::PendingRecordRewardGrant);
  27. const char* reason = "reward_allocation";
  28. committed = grant && request.quantity > 0
  29. && state::prepare_item_reward(request.itemDefinitionIndex,
  30. static_cast<std::uint32_t>(request.quantity),
  31. *grant,
  32. &reason);
  33. if (committed) {
  34. reason = "inventory_commit";
  35. committed = state::commit_record_reward(*grant);
  36. }
  37. if (!committed) {
  38. report_reward_refusal("world_settle", request.itemDefinitionIndex, reason);
  39. }
  40. }
  41. } else {
  42. state::PendingProfileItemAcquisition acquisition;
  43. committed = state::prepare_profile_item_acquisition_for_item(
  44. request.itemDefinitionIndex, request.quantity, acquisition)
  45. && state::commit_profile_item_acquisition(acquisition);
  46. }
  47. return committed && complete_world_reward(request.id) && transaction.commit();
  48. }
  49. /** Saves the reward under its stable definition hash and earning character. */
  50. bool enqueue_world_reward(std::uint16_t definitionIndex,
  51. std::int32_t quantity,
  52. WorldRewardKind kind) noexcept {
  53. state::build_data::items::Definition definition;
  54. if (!state::build_data::find_item_definition_index(definitionIndex, definition)
  55. || !state::investment::store::enqueue_reward(
  56. definition.definitionHash, quantity, static_cast<std::uint8_t>(kind))) {
  57. return false;
  58. }
  59. if (!has_active_family4_peer()) {
  60. settle_world_reward();
  61. }
  62. return true;
  63. }
  64. } // namespace
  65. void report_reward_refusal(const char* stage, std::uint16_t index, const char* reason) noexcept {
  66. core::log::writef(core::log::Channel::server,
  67. core::log::Level::warn,
  68. "ev=reward stage=%s index=%u result=refused reason=%s",
  69. stage,
  70. static_cast<unsigned>(index),
  71. reason != nullptr ? reason : "unknown");
  72. }
  73. /** Saves one item reward before its pickup presentation is queued. */
  74. bool arm_world_item_acquisition(std::uint16_t itemDefinitionIndex) noexcept {
  75. return enqueue_world_reward(itemDefinitionIndex, 1, WorldRewardKind::item);
  76. }
  77. /** Saves a profile material reward before its pickup presentation is queued. */
  78. bool arm_world_profile_item_acquisition(std::uint16_t itemDefinitionIndex,
  79. std::int32_t quantity) noexcept {
  80. return quantity > 0
  81. && enqueue_world_reward(itemDefinitionIndex, quantity, WorldRewardKind::profileItem);
  82. }
  83. /** Restores the oldest reward belonging to the selected character. */
  84. bool current_world_reward(WorldRewardRequest& request) noexcept {
  85. request = {};
  86. state::investment::store::PendingReward saved;
  87. state::build_data::items::Definition definition;
  88. if (!state::investment::store::next_reward(saved)
  89. || !state::build_data::find_item_definition_hash(saved.definitionHash, definition)) {
  90. return false;
  91. }
  92. request.id = saved.id;
  93. request.quantity = saved.quantity;
  94. request.itemDefinitionIndex = definition.definitionIndex;
  95. request.kind = static_cast<WorldRewardKind>(saved.kind);
  96. return true;
  97. }
  98. /** Acknowledges exactly the reward whose inventory grant is being committed. */
  99. bool complete_world_reward(std::uint64_t id) noexcept {
  100. return state::investment::store::complete_reward(id);
  101. }
  102. /** A failed grant stays saved for a later attempt. */
  103. void settle_world_reward() noexcept {
  104. WorldRewardRequest request;
  105. if (current_world_reward(request) && commit_world_reward(request)) {
  106. arm_account_resync_everywhere();
  107. }
  108. }
  109. /** Shutdown may grant ready rewards; rewards for other characters stay in the database. */
  110. void drain_world_rewards() noexcept {
  111. WorldRewardRequest request;
  112. while (current_world_reward(request)) {
  113. if (!commit_world_reward(request)) {
  114. break;
  115. }
  116. }
  117. }
  118. } // namespace sunrise::server::bap