diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a2b948a..e59a7514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Add support for script context and variables on Apple platforms ([#306](https://github.com/getsentry/sentry-godot/pull/306)) +### Improvements + +- Improve initialization flow ([#322](https://github.com/getsentry/sentry-godot/pull/322)) + ### Dependencies - Bump Cocoa SDK from v8.54.0 to v8.55.0 ([#318](https://github.com/getsentry/sentry-godot/pull/318)) diff --git a/src/register_types.cpp b/src/register_types.cpp index bbd70ffb..6e312cd1 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -39,49 +39,59 @@ using namespace godot; using namespace sentry; -void initialize_module(ModuleInitializationLevel p_level) { - if (p_level == MODULE_INITIALIZATION_LEVEL_CORE) { - } else if (p_level == godot::MODULE_INITIALIZATION_LEVEL_SERVERS) { - } else if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { - GDREGISTER_CLASS(SentryLoggerLimits); - GDREGISTER_CLASS(SentryOptions); - GDREGISTER_INTERNAL_CLASS(RuntimeConfig); - GDREGISTER_CLASS(SentryConfiguration); - GDREGISTER_CLASS(SentryUser); - GDREGISTER_CLASS(SentryTimestamp); - GDREGISTER_CLASS(SentrySDK); - GDREGISTER_ABSTRACT_CLASS(SentryAttachment); - GDREGISTER_ABSTRACT_CLASS(SentryEvent); - GDREGISTER_INTERNAL_CLASS(DisabledEvent); - GDREGISTER_INTERNAL_CLASS(SentryEventProcessor); - GDREGISTER_INTERNAL_CLASS(ScreenshotProcessor); - GDREGISTER_INTERNAL_CLASS(ViewHierarchyProcessor); - GDREGISTER_INTERNAL_CLASS(SentryLogger); +void register_runtime_classes() { + GDREGISTER_CLASS(SentryLoggerLimits); + GDREGISTER_CLASS(SentryOptions); + GDREGISTER_INTERNAL_CLASS(RuntimeConfig); + GDREGISTER_CLASS(SentryConfiguration); + GDREGISTER_CLASS(SentryUser); + GDREGISTER_CLASS(SentryTimestamp); + GDREGISTER_CLASS(SentrySDK); + GDREGISTER_ABSTRACT_CLASS(SentryAttachment); + GDREGISTER_ABSTRACT_CLASS(SentryEvent); + GDREGISTER_INTERNAL_CLASS(DisabledEvent); + GDREGISTER_INTERNAL_CLASS(SentryEventProcessor); + GDREGISTER_INTERNAL_CLASS(ScreenshotProcessor); + GDREGISTER_INTERNAL_CLASS(ViewHierarchyProcessor); + GDREGISTER_INTERNAL_CLASS(SentryLogger); #ifdef SDK_NATIVE - GDREGISTER_INTERNAL_CLASS(NativeEvent); + GDREGISTER_INTERNAL_CLASS(NativeEvent); #endif #ifdef SDK_ANDROID - GDREGISTER_INTERNAL_CLASS(AndroidEvent); - GDREGISTER_INTERNAL_CLASS(SentryAndroidBeforeSendHandler); + GDREGISTER_INTERNAL_CLASS(AndroidEvent); + GDREGISTER_INTERNAL_CLASS(SentryAndroidBeforeSendHandler); #endif #ifdef SDK_COCOA - GDREGISTER_INTERNAL_CLASS(cocoa::CocoaEvent); + GDREGISTER_INTERNAL_CLASS(cocoa::CocoaEvent); #endif +} - SentryOptions::create_singleton(); - - SentrySDK *sentry_singleton = memnew(SentrySDK); - Engine::get_singleton()->register_singleton("SentrySDK", SentrySDK::get_singleton()); - } else if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { +void register_editor_classes() { #ifdef TOOLS_ENABLED + GDREGISTER_INTERNAL_CLASS(SentryEditorExportPluginAndroid); + GDREGISTER_INTERNAL_CLASS(SentryEditorPlugin); + #ifndef WINDOWS_ENABLED - GDREGISTER_INTERNAL_CLASS(SentryEditorExportPluginUnix); + GDREGISTER_INTERNAL_CLASS(SentryEditorExportPluginUnix); #endif // !WINDOWS_ENABLED - GDREGISTER_INTERNAL_CLASS(SentryEditorExportPluginAndroid); - GDREGISTER_INTERNAL_CLASS(SentryEditorPlugin); + +#endif // TOOLS_ENABLED +} + +void initialize_module(ModuleInitializationLevel p_level) { + if (p_level == MODULE_INITIALIZATION_LEVEL_CORE) { + } else if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { + } else if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { + register_runtime_classes(); + SentryOptions::create_singleton(); + SentrySDK::create_singleton(); + SentrySDK::get_singleton()->prepare_and_auto_initialize(); + } else if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { +#ifdef TOOLS_ENABLED + register_editor_classes(); EditorPlugins::add_by_type(); #endif // TOOLS_ENABLED } @@ -89,9 +99,7 @@ void initialize_module(ModuleInitializationLevel p_level) { void uninitialize_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { - if (SentrySDK::get_singleton()) { - memdelete(SentrySDK::get_singleton()); - } + SentrySDK::destroy_singleton(); SentryOptions::destroy_singleton(); } } diff --git a/src/sentry/android/android_sdk.cpp b/src/sentry/android/android_sdk.cpp index c15d7538..914ece18 100644 --- a/src/sentry/android/android_sdk.cpp +++ b/src/sentry/android/android_sdk.cpp @@ -125,7 +125,7 @@ void AndroidSDK::add_attachment(const Ref &p_attachment) { } } -void AndroidSDK::initialize(const PackedStringArray &p_global_attachments) { +void AndroidSDK::init(const PackedStringArray &p_global_attachments) { ERR_FAIL_NULL(android_plugin); sentry::util::print_debug("Initializing Sentry Android SDK"); diff --git a/src/sentry/android/android_sdk.h b/src/sentry/android/android_sdk.h index ff71bc69..f5650844 100644 --- a/src/sentry/android/android_sdk.h +++ b/src/sentry/android/android_sdk.h @@ -49,7 +49,7 @@ class AndroidSDK : public InternalSDK { virtual void add_attachment(const Ref &p_attachment) override; - virtual void initialize(const PackedStringArray &p_global_attachments) override; + virtual void init(const PackedStringArray &p_global_attachments) override; bool has_android_plugin() const { return android_plugin != nullptr; } diff --git a/src/sentry/cocoa/cocoa_sdk.h b/src/sentry/cocoa/cocoa_sdk.h index d72dde02..217eb14c 100644 --- a/src/sentry/cocoa/cocoa_sdk.h +++ b/src/sentry/cocoa/cocoa_sdk.h @@ -36,8 +36,7 @@ class CocoaSDK : public InternalSDK { virtual void add_attachment(const Ref &p_attachment) override; - virtual void initialize(const PackedStringArray &p_global_attachments) override; - + virtual void init(const PackedStringArray &p_global_attachments) override; bool is_enabled() const; CocoaSDK(); diff --git a/src/sentry/cocoa/cocoa_sdk.mm b/src/sentry/cocoa/cocoa_sdk.mm index 0cd98973..4484b083 100644 --- a/src/sentry/cocoa/cocoa_sdk.mm +++ b/src/sentry/cocoa/cocoa_sdk.mm @@ -144,7 +144,7 @@ }]; } -void CocoaSDK::initialize(const PackedStringArray &p_global_attachments) { +void CocoaSDK::init(const PackedStringArray &p_global_attachments) { [PrivateSentrySDKOnly setSdkName:@"sentry.cocoa.godot"]; [objc::SentrySDK startWithConfigureOptions:^(objc::SentryOptions *options) { diff --git a/src/sentry/disabled/disabled_sdk.h b/src/sentry/disabled/disabled_sdk.h index ed9e7298..ace1d8a2 100644 --- a/src/sentry/disabled/disabled_sdk.h +++ b/src/sentry/disabled/disabled_sdk.h @@ -28,7 +28,7 @@ class DisabledSDK : public InternalSDK { virtual void add_attachment(const Ref &p_attachment) override {} - virtual void initialize(const PackedStringArray &p_global_attachments) override {} + virtual void init(const PackedStringArray &p_global_attachments) override {} }; } // namespace sentry diff --git a/src/sentry/internal_sdk.h b/src/sentry/internal_sdk.h index 4ca3edb7..c643bed8 100644 --- a/src/sentry/internal_sdk.h +++ b/src/sentry/internal_sdk.h @@ -36,7 +36,7 @@ class InternalSDK { virtual void add_attachment(const Ref &p_attachment) = 0; - virtual void initialize(const PackedStringArray &p_global_attachments) = 0; + virtual void init(const PackedStringArray &p_global_attachments) = 0; virtual ~InternalSDK() = default; }; diff --git a/src/sentry/native/native_sdk.cpp b/src/sentry/native/native_sdk.cpp index ddd0ed1c..9208427d 100644 --- a/src/sentry/native/native_sdk.cpp +++ b/src/sentry/native/native_sdk.cpp @@ -261,7 +261,7 @@ void NativeSDK::add_attachment(const Ref &p_attachment) { } } -void NativeSDK::initialize(const PackedStringArray &p_global_attachments) { +void NativeSDK::init(const PackedStringArray &p_global_attachments) { ERR_FAIL_NULL(OS::get_singleton()); ERR_FAIL_NULL(ProjectSettings::get_singleton()); diff --git a/src/sentry/native/native_sdk.h b/src/sentry/native/native_sdk.h index 7c7bc958..5bc70fbc 100644 --- a/src/sentry/native/native_sdk.h +++ b/src/sentry/native/native_sdk.h @@ -36,7 +36,7 @@ class NativeSDK : public InternalSDK { virtual void add_attachment(const Ref &p_attachment) override; - virtual void initialize(const PackedStringArray &p_global_attachments) override; + virtual void init(const PackedStringArray &p_global_attachments) override; NativeSDK(); virtual ~NativeSDK() override; diff --git a/src/sentry/sentry_sdk.cpp b/src/sentry/sentry_sdk.cpp index 52f2df77..89e4ccad 100644 --- a/src/sentry/sentry_sdk.cpp +++ b/src/sentry/sentry_sdk.cpp @@ -102,6 +102,22 @@ namespace sentry { SentrySDK *SentrySDK::singleton = nullptr; +void SentrySDK::create_singleton() { + ERR_FAIL_NULL(Engine::get_singleton()); + singleton = memnew(SentrySDK); + Engine::get_singleton()->register_singleton("SentrySDK", SentrySDK::get_singleton()); +} + +void SentrySDK::destroy_singleton() { + ERR_FAIL_NULL(Engine::get_singleton()); + if (!singleton) { + return; + } + Engine::get_singleton()->unregister_singleton("SentrySDK"); + memdelete(singleton); + singleton = nullptr; +} + String SentrySDK::capture_message(const String &p_message, Level p_level) { return internal_sdk->capture_message(p_message, p_level); } @@ -219,7 +235,7 @@ PackedStringArray SentrySDK::_get_global_attachments() { return attachments; } -void SentrySDK::_initialize() { +void SentrySDK::_auto_initialize() { sentry::util::print_debug("starting Sentry SDK version " + String(SENTRY_GODOT_SDK_VERSION)); // Initialize user if it wasn't set explicitly in the configuration script. @@ -249,30 +265,13 @@ void SentrySDK::_initialize() { sentry::util::print_debug("Sentry SDK is disabled when project is played from the editor. Tip: This can be changed in the project settings."); } +#if SDK_ANDROID if (should_enable) { -#ifdef SDK_NATIVE - internal_sdk = std::make_shared(); -#elif SDK_ANDROID - if (unlikely(OS::get_singleton()->has_feature("editor"))) { - sentry::util::print_debug("Sentry SDK is disabled in Android editor mode (only supported in exported Android projects)"); + if (OS::get_singleton()->has_feature("editor")) { should_enable = false; - } else { - auto sdk = std::make_shared(); - if (sdk->has_android_plugin()) { - internal_sdk = sdk; - } else { - sentry::util::print_error("Failed to initialize on Android. Disabling Sentry SDK..."); - should_enable = false; - } } -#elif SDK_COCOA - internal_sdk = std::make_shared(); -#else - // Unsupported platform - sentry::util::print_debug("This is an unsupported platform. Disabling Sentry SDK..."); - should_enable = false; -#endif } +#endif enabled = should_enable; @@ -281,15 +280,7 @@ void SentrySDK::_initialize() { return; } - // Add event processors - if (SentryOptions::get_singleton()->is_attach_screenshot_enabled()) { - SentryOptions::get_singleton()->add_event_processor(memnew(ScreenshotProcessor)); - } - if (SentryOptions::get_singleton()->is_attach_scene_tree_enabled()) { - SentryOptions::get_singleton()->add_event_processor(memnew(ViewHierarchyProcessor)); - } - - internal_sdk->initialize(_get_global_attachments()); + internal_sdk->init(_get_global_attachments()); if (SentryOptions::get_singleton()->is_logger_enabled()) { logger.instantiate(); @@ -302,7 +293,7 @@ void SentrySDK::_check_if_configuration_succeeded() { // Push error and initialize anyway. ERR_PRINT("Sentry: Configuration via user script failed. Will try to initialize SDK anyway."); sentry::util::print_error("initializing late because configuration via user script failed"); - _initialize(); + _auto_initialize(); } } @@ -314,10 +305,62 @@ void SentrySDK::_demo_helper_crash_app() { void SentrySDK::notify_options_configured() { sentry::util::print_debug("finished configuring options via user script"); configuration_succeeded = true; - _initialize(); + _auto_initialize(); _init_contexts(); } +void SentrySDK::prepare_and_auto_initialize() { + // Load the runtime configuration from the user's data directory. + runtime_config.instantiate(); + runtime_config->load_file(OS::get_singleton()->get_user_data_dir() + "/sentry.dat"); + + // Verify project settings and notify user via errors if there are any issues (deferred). + callable_mp_static(_verify_project_settings).call_deferred(); + +#if defined(LINUX_ENABLED) || defined(MACOS_ENABLED) + // Fix crashpad handler executable bit permissions on Unix platforms if the + // user extracts the distribution archive without preserving such permissions. + if (OS::get_singleton()->is_debug_build()) { + _fix_unix_executable_permissions("res://addons/sentry/bin/macos/crashpad_handler"); + _fix_unix_executable_permissions("res://addons/sentry/bin/linux/x86_64/crashpad_handler"); + _fix_unix_executable_permissions("res://addons/sentry/bin/linux/x86_32/crashpad_handler"); + _fix_unix_executable_permissions("res://addons/sentry/bin/linux/arm64/crashpad_handler"); + _fix_unix_executable_permissions("res://addons/sentry/bin/linux/arm32/crashpad_handler"); + } +#endif + + // Add event processors. + if (SentryOptions::get_singleton()->is_attach_screenshot_enabled()) { + SentryOptions::get_singleton()->add_event_processor(memnew(ScreenshotProcessor)); + } + if (SentryOptions::get_singleton()->is_attach_scene_tree_enabled()) { + SentryOptions::get_singleton()->add_event_processor(memnew(ViewHierarchyProcessor)); + } + + // Auto-initialize SDK. + if (SentryOptions::get_singleton()->get_configuration_script().is_empty() || Engine::get_singleton()->is_editor_hint()) { + // Early initialization path. + _auto_initialize(); + // Delay contexts initialization until the engine singletons are ready. + callable_mp(this, &SentrySDK::_init_contexts).call_deferred(); + } else { + // Register an autoload singleton, which is a user script extending the + // `SentryConfiguration` class. It will be instantiated and added to the + // scene tree by the engine shortly after ScriptServer is initialized. + // When this happens, the `SentryConfiguration` instance receives + // `NOTIFICATION_READY`, triggering our notification processing code in + // C++, which calls `_configure()` on the user script and then invokes + // `notify_options_configured()` in `SentrySDK`. This, in turn, + // auto-initializes the SDK. + sentry::util::print_debug("waiting for user configuration autoload"); + ERR_FAIL_NULL(ProjectSettings::get_singleton()); + ProjectSettings::get_singleton()->set_setting("autoload/SentryConfigurationScript", + SentryOptions::get_singleton()->get_configuration_script()); + // Ensure issues with the configuration script are detected. + callable_mp(this, &SentrySDK::_check_if_configuration_succeeded).call_deferred(); + } +} + void SentrySDK::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PREDELETE: { @@ -363,51 +406,28 @@ SentrySDK::SentrySDK() { user_mutex.instantiate(); - // Prevent potential crashes if initialization is skipped (unsupported platform, script failure, etc.) - internal_sdk = std::make_shared(); - - singleton = this; - - // Load the runtime configuration from the user's data directory. - runtime_config.instantiate(); - runtime_config->load_file(OS::get_singleton()->get_user_data_dir() + "/sentry.dat"); - - // Verify project settings and notify user via errors if there are any issues (deferred). - callable_mp_static(_verify_project_settings).call_deferred(); - -#if defined(LINUX_ENABLED) || defined(MACOS_ENABLED) - // Fix crashpad handler executable bit permissions on Unix platforms if the - // user extracts the distribution archive without preserving such permissions. - if (OS::get_singleton()->is_debug_build()) { - _fix_unix_executable_permissions("res://addons/sentry/bin/macos/crashpad_handler"); - _fix_unix_executable_permissions("res://addons/sentry/bin/linux/x86_64/crashpad_handler"); - _fix_unix_executable_permissions("res://addons/sentry/bin/linux/x86_32/crashpad_handler"); - _fix_unix_executable_permissions("res://addons/sentry/bin/linux/arm64/crashpad_handler"); - _fix_unix_executable_permissions("res://addons/sentry/bin/linux/arm32/crashpad_handler"); - } -#endif - - if (SentryOptions::get_singleton()->get_configuration_script().is_empty() || Engine::get_singleton()->is_editor_hint()) { - // Early initialization path. - _initialize(); - // Delay contexts initialization until the engine singletons are ready. - callable_mp(this, &SentrySDK::_init_contexts).call_deferred(); +#ifdef SDK_NATIVE + internal_sdk = std::make_shared(); +#elif SDK_ANDROID + if (unlikely(OS::get_singleton()->has_feature("editor"))) { + sentry::util::print_debug("Sentry SDK is disabled in Android editor mode (only supported in exported Android projects)"); + internal_sdk = std::make_shared(); } else { - // Register an autoload singleton, which is a user script extending the - // `SentryConfiguration` class. It will be instantiated and added to the - // scene tree by the engine shortly after ScriptServer is initialized. - // When this happens, the `SentryConfiguration` instance receives - // `NOTIFICATION_READY`, triggering our notification processing code in - // C++, which calls `_configure()` on the user script and then invokes - // `notify_options_configured()` in `SentrySDK`. This, in turn, initializes - // the internal SDK. - sentry::util::print_debug("waiting for user configuration autoload"); - ERR_FAIL_NULL(ProjectSettings::get_singleton()); - ProjectSettings::get_singleton()->set_setting("autoload/SentryConfigurationScript", - SentryOptions::get_singleton()->get_configuration_script()); - // Ensure issues with the configuration script are detected. - callable_mp(this, &SentrySDK::_check_if_configuration_succeeded).call_deferred(); + auto sdk = std::make_shared(); + if (sdk->has_android_plugin()) { + internal_sdk = sdk; + } else { + sentry::util::print_error("Failed to initialize on Android. Disabling Sentry SDK..."); + internal_sdk = std::make_shared(); + } } +#elif SDK_COCOA + internal_sdk = std::make_shared(); +#else + // Unsupported platform + sentry::util::print_debug("This is an unsupported platform. Disabling Sentry SDK..."); + internal_sdk = std::make_shared(); +#endif } SentrySDK::~SentrySDK() { diff --git a/src/sentry/sentry_sdk.h b/src/sentry/sentry_sdk.h index 05ed1bb5..a53a098b 100644 --- a/src/sentry/sentry_sdk.h +++ b/src/sentry/sentry_sdk.h @@ -41,7 +41,7 @@ class SentrySDK : public Object { void _init_contexts(); PackedStringArray _get_global_attachments(); - void _initialize(); + void _auto_initialize(); void _check_if_configuration_succeeded(); void _demo_helper_crash_app(); @@ -51,6 +51,8 @@ class SentrySDK : public Object { void _notification(int p_what); public: + static void create_singleton(); + static void destroy_singleton(); static SentrySDK *get_singleton() { return singleton; } _FORCE_INLINE_ std::shared_ptr get_internal_sdk() const { return internal_sdk; } @@ -86,6 +88,8 @@ class SentrySDK : public Object { void unset_before_send() { SentryOptions::get_singleton()->set_before_send(Callable()); } Callable get_before_send() { return SentryOptions::get_singleton()->get_before_send(); } + void prepare_and_auto_initialize(); + SentrySDK(); ~SentrySDK(); };