|
|
@@ -10,6 +10,7 @@
|
|
|
#include "../../core/runtime/core_runtime.h"
|
|
|
#include "../../core/runtime/host_environment.h"
|
|
|
#include "callbacks/callback_registry.h"
|
|
|
+#include "core/threading/data_mutex.h"
|
|
|
#include "internal.h"
|
|
|
#include "runtime.h"
|
|
|
#include "steam_context_state.h"
|
|
|
@@ -20,12 +21,15 @@ namespace {
|
|
|
/** The only delay-loaded module allowed to start the platform Client group. */
|
|
|
constexpr wchar_t kNetworkingModuleName[] = L"steamnetworkingsockets.dll";
|
|
|
|
|
|
-SRWLOCK g_lifecycleLock{SRWLOCK_INIT};
|
|
|
+struct Lifecycle {
|
|
|
+ bool mainActivationDone{};
|
|
|
+ bool mainActivationResult{};
|
|
|
+ bool graphicsActivationAttempted{};
|
|
|
+ bool platformActivationAttempted{};
|
|
|
+};
|
|
|
+
|
|
|
+core::threading::SharedDataMutex<Lifecycle> g_lifecycle;
|
|
|
std::atomic_bool g_initialized{false};
|
|
|
-bool g_mainActivationDone{};
|
|
|
-bool g_mainActivationResult{};
|
|
|
-bool g_graphicsActivationAttempted{};
|
|
|
-bool g_platformActivationAttempted{};
|
|
|
|
|
|
/**
|
|
|
* Finds the loaded image that owns a caught return address. It takes no module reference.
|
|
|
@@ -55,60 +59,58 @@ bool initialize(void* module) noexcept {
|
|
|
if (!client::hooks::egress::install()) {
|
|
|
return false;
|
|
|
}
|
|
|
- AcquireSRWLockExclusive(&g_lifecycleLock);
|
|
|
- if (g_initialized.load(std::memory_order_acquire)) {
|
|
|
- ReleaseSRWLockExclusive(&g_lifecycleLock);
|
|
|
+
|
|
|
+ return g_lifecycle.lock_write([module](Lifecycle& lifecycle) {
|
|
|
+ if (g_initialized.load(std::memory_order_acquire)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (!core::initialize(module)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // Base generation (_0) packages register during bootload, before the first callback pump,
|
|
|
+ // so package trust must attach at Steam init rather than in the main-image hook sweep.
|
|
|
+ if (!client::hooks::package_trust::install()) {
|
|
|
+ core::log::write(core::log::Channel::client,
|
|
|
+ core::log::Level::error,
|
|
|
+ "ev=steam_init stage=package_trust result=fail");
|
|
|
+ (void)core::shutdown();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ advance_context_generation();
|
|
|
+ g_initialized.store(true, std::memory_order_release);
|
|
|
+ core::log::write(
|
|
|
+ core::log::Channel::client, core::log::Level::info, "ev=steam_init result=ok");
|
|
|
+ // The guard attaches above, before Core logging exists, so its outcome is reported here.
|
|
|
+ client::hooks::egress::report_installation();
|
|
|
return true;
|
|
|
- }
|
|
|
- if (!core::initialize(module)) {
|
|
|
- ReleaseSRWLockExclusive(&g_lifecycleLock);
|
|
|
- return false;
|
|
|
- }
|
|
|
- // Base generation (_0) packages register during bootload, before the first callback pump, so
|
|
|
- // package trust must attach at Steam init rather than in the main-image hook sweep.
|
|
|
- if (!client::hooks::package_trust::install()) {
|
|
|
- core::log::write(core::log::Channel::client,
|
|
|
- core::log::Level::error,
|
|
|
- "ev=steam_init stage=package_trust result=fail");
|
|
|
- (void)core::shutdown();
|
|
|
- ReleaseSRWLockExclusive(&g_lifecycleLock);
|
|
|
- return false;
|
|
|
- }
|
|
|
- advance_context_generation();
|
|
|
- g_initialized.store(true, std::memory_order_release);
|
|
|
- core::log::write(core::log::Channel::client, core::log::Level::info, "ev=steam_init result=ok");
|
|
|
- // The guard attaches above, before Core logging exists, so its outcome is reported here.
|
|
|
- client::hooks::egress::report_installation();
|
|
|
- ReleaseSRWLockExclusive(&g_lifecycleLock);
|
|
|
- return true;
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
/** Stops callback delivery and clears Steam state. */
|
|
|
bool shutdown() noexcept {
|
|
|
- AcquireSRWLockExclusive(&g_lifecycleLock);
|
|
|
- const bool hadRuntime = g_initialized.load(std::memory_order_acquire) || core::is_initialized();
|
|
|
- if (!hadRuntime) {
|
|
|
- ReleaseSRWLockExclusive(&g_lifecycleLock);
|
|
|
+ return g_lifecycle.lock_write([](Lifecycle& lifecycle) {
|
|
|
+ const bool hadRuntime =
|
|
|
+ g_initialized.load(std::memory_order_acquire) || core::is_initialized();
|
|
|
+ if (!hadRuntime) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (!core::shutdown()) {
|
|
|
+ core::log::write(core::log::Channel::client,
|
|
|
+ core::log::Level::error,
|
|
|
+ "ev=steam_shutdown stage=core result=fail");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Callback pointers are released only after Client hooks stop producing events.
|
|
|
+ runtime::callbacks::clear();
|
|
|
+ g_initialized.store(false, std::memory_order_release);
|
|
|
+ lifecycle.mainActivationDone = false;
|
|
|
+ lifecycle.mainActivationResult = false;
|
|
|
+ lifecycle.graphicsActivationAttempted = false;
|
|
|
+ lifecycle.platformActivationAttempted = false;
|
|
|
+ advance_context_generation();
|
|
|
return true;
|
|
|
- }
|
|
|
- if (!core::shutdown()) {
|
|
|
- core::log::write(core::log::Channel::client,
|
|
|
- core::log::Level::error,
|
|
|
- "ev=steam_shutdown stage=core result=fail");
|
|
|
- ReleaseSRWLockExclusive(&g_lifecycleLock);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // Callback pointers are released only after Client hooks stop producing events.
|
|
|
- runtime::callbacks::clear();
|
|
|
- g_initialized.store(false, std::memory_order_release);
|
|
|
- g_mainActivationDone = false;
|
|
|
- g_mainActivationResult = false;
|
|
|
- g_graphicsActivationAttempted = false;
|
|
|
- g_platformActivationAttempted = false;
|
|
|
- advance_context_generation();
|
|
|
- ReleaseSRWLockExclusive(&g_lifecycleLock);
|
|
|
- return true;
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
/** @return True. The in-process Steam provider stays up for the whole DLL lifetime. */
|
|
|
@@ -122,33 +124,33 @@ namespace sunrise::steam::runtime {
|
|
|
|
|
|
/** Runs main-image activation once, from a caller that proves the game is loaded. */
|
|
|
bool activate_main_once() noexcept {
|
|
|
- AcquireSRWLockExclusive(&g_lifecycleLock);
|
|
|
- if (!g_mainActivationDone && core::is_initialized()) {
|
|
|
- g_mainActivationDone = true;
|
|
|
- g_mainActivationResult = client::activate_main_once();
|
|
|
- }
|
|
|
- const bool result = g_mainActivationResult;
|
|
|
- ReleaseSRWLockExclusive(&g_lifecycleLock);
|
|
|
- return result;
|
|
|
+ return g_lifecycle.lock_write([](Lifecycle& lifecycle) {
|
|
|
+ if (!lifecycle.mainActivationDone && core::is_initialized()) {
|
|
|
+ lifecycle.mainActivationDone = true;
|
|
|
+ lifecycle.mainActivationResult = client::activate_main_once();
|
|
|
+ }
|
|
|
+
|
|
|
+ const bool result = lifecycle.mainActivationResult;
|
|
|
+ return result;
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
/** @return True while the main-image sweep has not run yet. */
|
|
|
bool main_activation_pending() noexcept {
|
|
|
- AcquireSRWLockShared(&g_lifecycleLock);
|
|
|
- const bool pending = !g_mainActivationDone;
|
|
|
- ReleaseSRWLockShared(&g_lifecycleLock);
|
|
|
+ const bool pending = g_lifecycle.lock_read(
|
|
|
+ [](const Lifecycle& lifecycle) { return !lifecycle.mainActivationDone; });
|
|
|
// Matches the activation's Core test. A failed Core must not raise an endless overlay.
|
|
|
return pending && core::is_initialized();
|
|
|
}
|
|
|
|
|
|
/** Installs the presentation hooks once, from the callback pump, before the game sweep. */
|
|
|
void activate_graphics_once() noexcept {
|
|
|
- AcquireSRWLockExclusive(&g_lifecycleLock);
|
|
|
- if (!g_graphicsActivationAttempted && core::is_initialized()) {
|
|
|
- g_graphicsActivationAttempted = true;
|
|
|
- (void)client::activate_graphics_once();
|
|
|
- }
|
|
|
- ReleaseSRWLockExclusive(&g_lifecycleLock);
|
|
|
+ g_lifecycle.lock_write([](Lifecycle& lifecycle) {
|
|
|
+ if (!lifecycle.graphicsActivationAttempted && core::is_initialized()) {
|
|
|
+ lifecycle.graphicsActivationAttempted = true;
|
|
|
+ (void)client::activate_graphics_once();
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
/** Activates the platform Client group at its exact interface request boundary. */
|
|
|
@@ -158,12 +160,14 @@ void activate_platform_once(const void* callerAddress) noexcept {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- AcquireSRWLockExclusive(&g_lifecycleLock);
|
|
|
- if (!g_platformActivationAttempted && g_initialized.load(std::memory_order_acquire)) {
|
|
|
- g_platformActivationAttempted = true;
|
|
|
- (void)client::activate_platform_once(callerModule);
|
|
|
- }
|
|
|
- ReleaseSRWLockExclusive(&g_lifecycleLock);
|
|
|
+ g_lifecycle.lock_write([callerModule](Lifecycle& lifecycle) {
|
|
|
+ if (!lifecycle.platformActivationAttempted
|
|
|
+ && g_initialized.load(std::memory_order_acquire)) {
|
|
|
+
|
|
|
+ lifecycle.platformActivationAttempted = true;
|
|
|
+ (void)client::activate_platform_once(callerModule);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
} // namespace sunrise::steam::runtime
|