core_runtime.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. /**
  73. * Reports how long one boot boundary took, for the debug channel only.
  74. * Timing is diagnostic, so it never appears at the levels a normal run uses.
  75. * @param event Event and phase text the duration is appended to.
  76. * @param startedTick Tick count taken when the boundary began.
  77. * @param result Outcome text for the log line.
  78. */
  79. void report_elapsed(const char* event, std::uint64_t startedTick, const char* result) noexcept {
  80. const std::uint64_t elapsed = GetTickCount64() - startedTick;
  81. std::array<char, 96> line{};
  82. const int written = std::snprintf(line.data(),
  83. line.size(),
  84. "%s ms=%llu result=%s",
  85. event,
  86. static_cast<unsigned long long>(elapsed),
  87. result);
  88. if (written <= 0) {
  89. return;
  90. }
  91. const auto length = static_cast<std::size_t>(written) < line.size()
  92. ? static_cast<std::size_t>(written)
  93. : line.size() - 1;
  94. log::write(log::Channel::core, log::Level::debug, {line.data(), length});
  95. }
  96. } // namespace
  97. /** Initializes every runtime layer in dependency order. */
  98. bool initialize(void* module) noexcept {
  99. AcquireSRWLockExclusive(&g_runtimeLock);
  100. if (g_initialized.load(std::memory_order_relaxed)) {
  101. ReleaseSRWLockExclusive(&g_runtimeLock);
  102. return true;
  103. }
  104. // Taken before the first stage, so the reported duration covers settings and the sinks too.
  105. const std::uint64_t startedTick = GetTickCount64();
  106. if (!settings::initialize(module)) {
  107. // Settings name their own failure; the sinks do not exist yet to carry a second line.
  108. ReleaseSRWLockExclusive(&g_runtimeLock);
  109. return false;
  110. }
  111. state::unlocks::publish(settings::get().initialUnlocks);
  112. // One stage per step, so a boot failure names the step instead of the whole expression.
  113. const char* stage = nullptr;
  114. if (!log::initialize(module, settings::get().logging)) {
  115. stage = "logging";
  116. } else {
  117. // The sinks exist only from here, so this is the earliest a begin marker can reach a
  118. // channel. The duration it pairs with still counts from function entry.
  119. log::write(log::Channel::core, log::Level::debug, "ev=initialize phase=begin");
  120. if (!ui::runtime::initialize(settings::get().client.userInterface)) {
  121. stage = "ui";
  122. } else if (!ui::modules::logs::initialize()) {
  123. stage = "ui_logs";
  124. } else if (!state::entitlements::publish(settings::get().server.entitlements)) {
  125. stage = "entitlements";
  126. } else if (!state::initialize(module,
  127. settings::get().initialAccount,
  128. settings::get().initialActivityDefaults)) {
  129. stage = "state";
  130. } else if (!initialize_content_manifest(module)) {
  131. stage = "content_manifest";
  132. } else if (!middleware::initialize()) {
  133. stage = "middleware";
  134. } else if (!server::initialize()) {
  135. stage = "server";
  136. } else if (!client::initialize(module)) {
  137. stage = "client";
  138. }
  139. }
  140. if (stage != nullptr) {
  141. report_stage_failure(stage);
  142. // Reported before the unwind, so this measures initialization alone and stays comparable
  143. // with the success line. The unwind's own quiesce waits would otherwise be counted here.
  144. // A logging-stage failure has no sinks left to carry it, and reports nothing.
  145. report_elapsed("ev=initialize phase=complete", startedTick, "fail");
  146. // Reverse every stage because the failing expression may have completed earlier stages.
  147. (void)client::shutdown();
  148. server::shutdown();
  149. middleware::shutdown();
  150. state::content_manifest::shutdown();
  151. state::shutdown();
  152. state::entitlements::clear();
  153. ui::modules::logs::shutdown();
  154. ui::modules::registry::shutdown();
  155. ui::runtime::shutdown();
  156. state::unlocks::clear();
  157. log::shutdown();
  158. settings::shutdown();
  159. ReleaseSRWLockExclusive(&g_runtimeLock);
  160. return false;
  161. }
  162. g_initialized.store(true, std::memory_order_release);
  163. log::write(log::Channel::core, log::Level::info, "ev=initialize result=ok");
  164. report_elapsed("ev=initialize phase=complete", startedTick, "ok");
  165. ReleaseSRWLockExclusive(&g_runtimeLock);
  166. return true;
  167. }
  168. /** Stops every runtime layer in reverse dependency order. */
  169. bool shutdown() noexcept {
  170. AcquireSRWLockExclusive(&g_runtimeLock);
  171. if (!g_initialized.load(std::memory_order_acquire)) {
  172. ReleaseSRWLockExclusive(&g_runtimeLock);
  173. return true;
  174. }
  175. if (!client::shutdown()) {
  176. // Server and State must remain valid while any Client hook is attached.
  177. ReleaseSRWLockExclusive(&g_runtimeLock);
  178. return false;
  179. }
  180. g_initialized.store(false, std::memory_order_release);
  181. server::shutdown();
  182. middleware::shutdown();
  183. state::content_manifest::shutdown();
  184. state::shutdown();
  185. state::entitlements::clear();
  186. ui::modules::logs::shutdown();
  187. ui::modules::registry::shutdown();
  188. ui::runtime::shutdown();
  189. log::write(log::Channel::core, log::Level::info, "ev=shutdown result=ok");
  190. state::unlocks::clear();
  191. log::shutdown();
  192. settings::shutdown();
  193. ReleaseSRWLockExclusive(&g_runtimeLock);
  194. return true;
  195. }
  196. /** @return True after every Core-owned runtime layer initializes. */
  197. bool is_initialized() noexcept {
  198. return g_initialized.load(std::memory_order_acquire);
  199. }
  200. } // namespace sunrise::core