core_runtime.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. #include "core_runtime.h"
  2. #include <Windows.h>
  3. #include <array>
  4. #include <atomic>
  5. #include <cstdint>
  6. #include <cstdio>
  7. #include <string_view>
  8. #include "../../client/runtime/host/game_host_classification.h"
  9. #include "../../client/runtime/runtime.h"
  10. #include "../../middleware/runtime/middleware_runtime.h"
  11. #include "../../server/runtime/server_runtime.h"
  12. #include "../../state/content_manifest/content_manifest_state_runtime.h"
  13. #include "../../state/entitlements/entitlement_runtime.h"
  14. #include "../../state/runtime/runtime.h"
  15. #include "../../state/unlocks/unlocks_runtime.h"
  16. #include "../filesystem/path.h"
  17. #include "../logging/log.h"
  18. #include "../settings/settings.h"
  19. #include "../ui/modules/logs/logs.h"
  20. #include "../ui/modules/registry/ui_module_registry.h"
  21. #include "../ui/runtime/ui_visibility_runtime.h"
  22. namespace sunrise::core {
  23. namespace {
  24. std::atomic_bool g_initialized{false};
  25. SRWLOCK g_runtimeLock{SRWLOCK_INIT};
  26. /** The installed public package headers live beside the game executable. */
  27. constexpr std::wstring_view kInstalledPackagesDirectory = L"packages";
  28. /**
  29. * Builds the local content manifest only for the production game host.
  30. * @param module Loaded Sunrise module used for generated cache placement.
  31. * @return True when the host needs no manifest or one complete catalog is ready.
  32. */
  33. [[nodiscard]] bool initialize_content_manifest(void* module) noexcept {
  34. if (client::runtime::host::current_requirement()
  35. == client::runtime::host::NetworkRequirement::notApplicable) {
  36. return true;
  37. }
  38. const HMODULE process = GetModuleHandleW(nullptr);
  39. path::Buffer packages;
  40. if (process == nullptr || !path::module_directory(process, packages)
  41. || !path::append(packages, kInstalledPackagesDirectory)) {
  42. log::write(log::Channel::core,
  43. log::Level::error,
  44. "ev=initialize stage=content_manifest result=fail");
  45. return false;
  46. }
  47. const bool initialized = state::content_manifest::initialize(
  48. module, std::wstring_view(packages.chars.data(), packages.length));
  49. if (!initialized) {
  50. log::write(log::Channel::core,
  51. log::Level::error,
  52. "ev=initialize stage=content_manifest result=fail");
  53. }
  54. return initialized;
  55. }
  56. /**
  57. * Names the initialization stage that failed.
  58. * The logging sinks are the first stage, so their own failure has only the debugger.
  59. * @param stage Short key naming the stage.
  60. */
  61. void report_stage_failure(const char* stage) noexcept {
  62. std::array<char, 96> line{};
  63. const int written =
  64. std::snprintf(line.data(), line.size(), "ev=initialize stage=%s result=fail", stage);
  65. if (written <= 0) {
  66. return;
  67. }
  68. const std::string_view event{line.data(), static_cast<std::size_t>(written)};
  69. log::early(event);
  70. log::write(log::Channel::core, log::Level::error, event);
  71. }
  72. } // namespace
  73. /** Initializes every runtime layer in dependency order. */
  74. bool initialize(void* module) noexcept {
  75. AcquireSRWLockExclusive(&g_runtimeLock);
  76. if (g_initialized.load(std::memory_order_relaxed)) {
  77. ReleaseSRWLockExclusive(&g_runtimeLock);
  78. return true;
  79. }
  80. // Taken before the first stage, so the reported duration covers settings and the sinks too.
  81. const std::uint64_t startedTick = GetTickCount64();
  82. if (!settings::initialize(module)) {
  83. // Settings name their own failure; the sinks do not exist yet to carry a second line.
  84. ReleaseSRWLockExclusive(&g_runtimeLock);
  85. return false;
  86. }
  87. state::unlocks::publish(settings::get().initialUnlocks);
  88. // One stage per step, so a boot failure names the step instead of the whole expression.
  89. const char* stage = nullptr;
  90. if (!log::initialize(module, settings::get().logging)) {
  91. stage = "logging";
  92. } else {
  93. // The sinks exist only from here, so this is the earliest a begin marker can reach a
  94. // channel. The duration it pairs with still counts from function entry.
  95. log::write(log::Channel::core, log::Level::debug, "ev=initialize phase=begin");
  96. if (!ui::runtime::initialize(settings::get().client.userInterface)) {
  97. stage = "ui";
  98. } else if (!ui::modules::logs::initialize()) {
  99. stage = "ui_logs";
  100. } else if (!state::entitlements::publish(settings::get().server.entitlements)) {
  101. stage = "entitlements";
  102. } else if (!state::initialize(module,
  103. settings::get().initialAccount,
  104. settings::get().initialActivityDefaults)) {
  105. stage = "state";
  106. } else if (!initialize_content_manifest(module)) {
  107. stage = "content_manifest";
  108. } else if (!middleware::initialize()) {
  109. stage = "middleware";
  110. } else if (!server::initialize()) {
  111. stage = "server";
  112. } else if (!client::initialize(module)) {
  113. stage = "client";
  114. }
  115. }
  116. if (stage != nullptr) {
  117. report_stage_failure(stage);
  118. // Reported before the unwind, so this measures initialization alone and stays comparable
  119. // with the success line. The unwind's own quiesce waits would otherwise be counted here.
  120. // A logging-stage failure has no sinks left to carry it, and reports nothing.
  121. log::write_elapsed(log::Channel::core, "ev=initialize phase=complete", startedTick, "fail");
  122. // Reverse every stage because the failing expression may have completed earlier stages.
  123. (void)client::shutdown();
  124. server::shutdown();
  125. middleware::shutdown();
  126. state::content_manifest::shutdown();
  127. state::shutdown();
  128. state::entitlements::clear();
  129. ui::modules::logs::shutdown();
  130. ui::modules::registry::shutdown();
  131. ui::runtime::shutdown();
  132. state::unlocks::clear();
  133. log::shutdown();
  134. settings::shutdown();
  135. ReleaseSRWLockExclusive(&g_runtimeLock);
  136. return false;
  137. }
  138. g_initialized.store(true, std::memory_order_release);
  139. log::write(log::Channel::core, log::Level::info, "ev=initialize result=ok");
  140. log::write_elapsed(log::Channel::core, "ev=initialize phase=complete", startedTick, "ok");
  141. ReleaseSRWLockExclusive(&g_runtimeLock);
  142. return true;
  143. }
  144. /** Stops every runtime layer in reverse dependency order. */
  145. bool shutdown() noexcept {
  146. AcquireSRWLockExclusive(&g_runtimeLock);
  147. if (!g_initialized.load(std::memory_order_acquire)) {
  148. ReleaseSRWLockExclusive(&g_runtimeLock);
  149. return true;
  150. }
  151. if (!client::shutdown()) {
  152. // Server and State must remain valid while any Client hook is attached.
  153. ReleaseSRWLockExclusive(&g_runtimeLock);
  154. return false;
  155. }
  156. g_initialized.store(false, std::memory_order_release);
  157. server::shutdown();
  158. middleware::shutdown();
  159. state::content_manifest::shutdown();
  160. state::shutdown();
  161. state::entitlements::clear();
  162. ui::modules::logs::shutdown();
  163. ui::modules::registry::shutdown();
  164. ui::runtime::shutdown();
  165. log::write(log::Channel::core, log::Level::info, "ev=shutdown result=ok");
  166. state::unlocks::clear();
  167. log::shutdown();
  168. settings::shutdown();
  169. ReleaseSRWLockExclusive(&g_runtimeLock);
  170. return true;
  171. }
  172. /** @return True after every Core-owned runtime layer initializes. */
  173. bool is_initialized() noexcept {
  174. return g_initialized.load(std::memory_order_acquire);
  175. }
  176. } // namespace sunrise::core