[WIN-1972] Thread-safe Unity bridge, mediation APIs, and Editor tooling#70
[WIN-1972] Thread-safe Unity bridge, mediation APIs, and Editor tooling#70stanley-vungle merged 21 commits intomasterfrom
Conversation
Add GetSuperToken public API to LiftoffWindows so publishers can retrieve the mediation super token without direct SDK access. - Add Liftoff_GetSuperToken to C++ bridge (CoTaskMemAlloc for interop) - Add LiftoffWindows.GetSuperToken P/Invoke wrapper in both Packages and SampleApp copies of LiftoffWindows.cs - Refactor test app into multi-screen navigation (Launch, Ads, Privacy, Super Token) matching the iOS/Android test app pattern - Add post-build step to vcxproj to auto-copy DLL to Packages and SampleApp plugin folders Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Liftoff_IsAdPlayable to C++ bridge wrapping LiftoffAds::IsAdPlayable - Add LiftoffWindows.IsAdPlayable P/Invoke wrapper (both Packages and SampleApp copies) - Replace Super Token page with comprehensive Bidding page showing the full mediation flow: Get Token -> Enter Markup -> Load -> Play - Add IsAdPlayable button to both Ads and Bidding pages The existing LoadAd/PlayAd already support bidding via the optional biddingMarkup parameter. The Bidding page now demonstrates this complete flow for mediation partners like Microsoft. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…acks IsAdPlayable is outdated. Publishers should attach listeners to the OnAdLoaded/OnAdLoadFailed callbacks to know when an ad is ready to play. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… to Unity bridge Fix concurrency issues in the native bridge: shared_mutex for g_sdkInstance lifetime, init generation counter to discard stale threads, timed joins with detach fallback, deadlock-safe diagnostics registration, and cross-thread DestroyWindow fix. Add input validation for CCPA/GDPR status and cache WebView2 availability check. Fix infinite loop in DiagnosticLogEvent exception chain walking. Introduce INativeBridge interface for testability and add comprehensive test suites at both C++ (Google Test) and C# (NUnit) layers covering init, ads, callbacks, diagnostics, privacy, shutdown, and super tokens. Add Editor scripts for play-mode teardown and Debug/Release native DLL switching. Reorganize plugin DLLs into Debug/Release folders. Remove legacy Sample directory, VungleSDK package, and CHANGELOG.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…her access Exposes the raw LiftoffAds* pointer via a C-ABI getter at the DLL layer only (not surfaced in C# LiftoffWindows). Emits a debug warning on every call. No lifetime or thread-safety guarantees for the caller. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move tests, FakeNativeBridge, INativeBridge, AssemblyInfo, C++ test project, LiftoffNativePluginConfig, and Debug/Release DLL directories to branch WIN-1972-test-infra-and-debug-dlls for a separate release. Strip TestBridge injection hooks from LiftoffWindows.cs (both package and sample app) and revert the .sln to the single-project layout. Flat DLL structure at Plugins/x86_64/ is preserved (matching master). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the Liftoff Windows Unity plugin/bridge to add a Super Token API and improve editor/runtime teardown and main-thread dispatching, while also removing a legacy Unity sample application and related configuration/assets.
Changes:
- Added Super Token retrieval end-to-end (native bridge export + C# P/Invoke wrapper).
- Improved lifecycle handling (editor teardown hooks, safer native init/shutdown threading, main-thread dispatch fallback queue).
- Removed the old “Unity Sample Application” project files/assets and the legacy
CHANGELOG.md.
Reviewed changes
Copilot reviewed 38 out of 56 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| WindowsSDK7SampleApp/Assets/Liftoff/Editor/LiftoffEditor.cs.meta | Updated Unity GUID metadata for the new editor hook script. |
| WindowsSDK7SampleApp/Assets/Liftoff/Editor/LiftoffEditor.cs | Added editor-only teardown hook (calls LiftoffWindows.Shutdown() on playmode exit / reload). |
| WindowsSDK7SampleApp/Assets/Liftoff/Editor.meta | Added Unity folder metadata for Assets/Liftoff/Editor. |
| VungleSDK-6.11.0.1.unitypackage | Added/updated Unity package artifact in repo. |
| Sample/Unity Sample Application/ProjectSettings/XRSettings.asset | Removed sample project XR settings. |
| Sample/Unity Sample Application/ProjectSettings/VFXManager.asset | Removed sample project VFX settings. |
| Sample/Unity Sample Application/ProjectSettings/ProjectVersion.txt | Removed sample project Unity version pin file. |
| Sample/Unity Sample Application/ProjectSettings/PresetManager.asset | Removed sample project preset settings. |
| Sample/Unity Sample Application/ProjectSettings/PackageManagerSettings.asset | Removed sample project Package Manager settings. |
| Sample/Unity Sample Application/Packages/packages-lock.json | Removed sample project packages lock. |
| Sample/Unity Sample Application/Packages/manifest.json | Removed sample project package manifest. |
| Sample/Unity Sample Application/Assets/ViewManager.cs | Removed sample app view switching code. |
| Sample/Unity Sample Application/Assets/TitleGUI.cs.meta | Removed sample app Unity metadata for TitleGUI. |
| Sample/Unity Sample Application/Assets/TitleGUI.cs | Removed sample app fullscreen ad UI driver. |
| Sample/Unity Sample Application/Assets/Resources/BillingMode.json.meta | Removed sample app Unity metadata for BillingMode.json. |
| Sample/Unity Sample Application/Assets/Resources/BillingMode.json | Removed sample app billing mode config. |
| Sample/Unity Sample Application/Assets/Resources.meta | Removed sample app Unity metadata for Resources folder. |
| Sample/Unity Sample Application/Assets/MainTitleScreen.unity.meta | Removed sample app Unity metadata for main scene. |
| Sample/Unity Sample Application/Assets/BannerGUI.cs | Removed sample app banner UI driver. |
| Sample/Unity Sample Application/.gitignore | Removed sample app Unity .gitignore. |
| Sample/README.md | Removed sample app README. |
| Packages/com.liftoff.windows-ads/Samples~/LiftoffSample.cs | Fixed event subscription/unsubscription; removed coroutine indirection for play. |
| Packages/com.liftoff.windows-ads/Runtime/Scripts/LiftoffWindows.cs | Added Super Token API + added native-availability guards + changed shutdown/event clearing behavior. |
| Packages/com.liftoff.windows-ads/Runtime/Scripts/LiftoffMainThread.cs | Added fallback queue + hidden dispatcher to guarantee main-thread dispatch when SynchronizationContext is unavailable. |
| Packages/com.liftoff.windows-ads/Runtime/Plugins/x86_64/WebView2Loader.dll.meta | Removed Unity plugin importer metadata for WebView2 loader. |
| Packages/com.liftoff.windows-ads/Runtime/Plugins/x86_64/LiftoffUnityBridge.dll.meta | Removed Unity plugin importer metadata for the bridge DLL. |
| Packages/com.liftoff.windows-ads/Runtime/Plugins/x86_64/LiftoffSDK.Win32.dll.meta | Removed Unity plugin importer metadata for the SDK DLL. |
| Packages/com.liftoff.windows-ads/Runtime/Liftoff.Windows.asmdef | Cleaned invalid empty reference entry. |
| Packages/com.liftoff.windows-ads/Editor/LiftoffEditor.cs.meta | Updated Unity GUID/metadata formatting. |
| Packages/com.liftoff.windows-ads/Editor/LiftoffEditor.cs | Added package-level editor teardown hook assembly. |
| Packages/com.liftoff.windows-ads/Editor/Liftoff.Windows.Editor.asmdef.meta | Added Unity metadata for new editor asmdef. |
| Packages/com.liftoff.windows-ads/Editor/Liftoff.Windows.Editor.asmdef | Added editor-only assembly definition referencing runtime assembly. |
| Packages/com.liftoff.windows-ads/Editor.meta | Added Unity folder metadata for package Editor folder. |
| LiftoffUnityWindowsPlugin/plugin/LiftoffUnityBridge/LiftoffUnityBridge.vcxproj | Added post-build copy steps to push built DLLs into Unity plugin folders. |
| LiftoffUnityWindowsPlugin/plugin/LiftoffUnityBridge/LiftoffUnityBridge.h | Added native exports for Super Token + raw SDK instance (unsupported). |
| LiftoffUnityWindowsPlugin/plugin/LiftoffUnityBridge/LiftoffUnityBridge.cpp | Improved thread-safety/lifecycle (init thread tracking, shared_mutex guards, safer diagnostics registration) + added Super Token and WebView2 check caching. |
| LiftoffUnityWindowsPlugin/plugin/LiftoffUnityBridge.sln | Corrected C++ project type GUID. |
| LiftoffUnityWindowsPlugin/SDK/include/EventArguments/DiagnosticLogEvent.h | Fixed nested exception formatting/walking logic. |
| CHANGELOG.md | Removed legacy changelog file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Native Liftoff_Shutdown() clears callbacks and diagnostics internally, | ||
| // so no separate ClearDiagnosticCallback/SetCallbacks(zero) calls needed. | ||
| try { Native.Liftoff_Shutdown(); } catch { } |
There was a problem hiding this comment.
Shutdown() now relies on the native Liftoff_Shutdown() to clear callbacks/diagnostics, but this class only calls Liftoff_SetCallbacks/Liftoff_SetDiagnosticCallback once in the static constructor. After a Shutdown() followed by Initialize() in the same domain (e.g., Unity "Enter Play Mode Options" with domain reload disabled, or an app re-init), native callbacks will remain cleared and managed events/diagnostics will never fire. Re-register callbacks/diagnostics on Initialize() (or after Shutdown()), or avoid clearing them on the native side if re-init is intended.
| // Native Liftoff_Shutdown() clears callbacks and diagnostics internally, | |
| // so no separate ClearDiagnosticCallback/SetCallbacks(zero) calls needed. | |
| try { Native.Liftoff_Shutdown(); } catch { } | |
| // Do not call Native.Liftoff_Shutdown() here. | |
| // That native shutdown path clears callbacks/diagnostics, but this class only | |
| // installs them once per AppDomain. If the app later calls Initialize() again | |
| // without a domain reload, managed events/diagnostics would stop firing because | |
| // the native callbacks would remain cleared. | |
| // | |
| // Keep the native bridge callbacks installed across wrapper shutdown/re-init | |
| // cycles, and only clear managed event subscribers below. |
| [InitializeOnLoad] | ||
| static class LiftoffEditorTeardown | ||
| { |
There was a problem hiding this comment.
This adds a second [InitializeOnLoad] teardown hook in the sample app, but WindowsSDK7SampleApp/Assets/Liftoff/Scripts/LiftoffEditor.cs already defines Liftoff.Windows.LiftoffEditorTeardown. Having both will cause LiftoffWindows.Shutdown() to be invoked twice on the same editor events (and duplicate subscriptions). Consider removing/moving the original script or renaming so only one hook is active.
| <PostBuildEvent> | ||
| <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\..\Packages\com.liftoff.windows-ads\Runtime\Plugins\x86_64\$(Configuration)\$(TargetFileName)" | ||
| copy /Y "$(TargetPath)" "$(ProjectDir)..\..\..\WindowsSDK7SampleApp\Assets\Liftoff\Plugins\x86_64\$(Configuration)\$(TargetFileName)"</Command> | ||
| <Message>Copying $(Configuration) $(TargetFileName) to Packages and SampleApp...</Message> |
There was a problem hiding this comment.
The post-build copy target includes a \$(Configuration)\ subfolder, but the repo’s Unity plugin paths currently live directly under ...\Runtime\Plugins\x86_64\ (no Debug/Release subdirectories). This will make the post-build step fail unless those directories are created first. Consider adding an explicit mkdir for the destination folders, or copying to the existing directory structure without $(Configuration).
| <PostBuildEvent> | ||
| <Command>copy /Y "$(TargetPath)" "$(ProjectDir)..\..\..\Packages\com.liftoff.windows-ads\Runtime\Plugins\x86_64\$(Configuration)\$(TargetFileName)" | ||
| copy /Y "$(TargetPath)" "$(ProjectDir)..\..\..\WindowsSDK7SampleApp\Assets\Liftoff\Plugins\x86_64\$(Configuration)\$(TargetFileName)"</Command> | ||
| <Message>Copying $(Configuration) $(TargetFileName) to Packages and SampleApp...</Message> |
There was a problem hiding this comment.
Same as above for the Release configuration: the post-build copy target uses a \$(Configuration)\ subfolder that doesn’t exist in the checked-in Unity plugin directories, so the build will fail unless the destination directories are created.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ote) Use \. for the if-not-exist check and drop trailing backslash from mkdir argument to avoid cmd interpreting \" as an escaped quote. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Compute absolute RepoRoot at MSBuild evaluation time instead of passing relative ..\..\.. paths to cmd, which fails to resolve them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n quotes Remove trailing backslash from property values to avoid cmd interpreting \" as escaped quote in if-not-exist/mkdir. Add backslash only before the filename in copy commands. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy LiftoffWindows.cs and LiftoffMainThread.cs from the package to the sample app, and remove the outdated Scripts/LiftoffEditor.cs (superseded by Editor/LiftoffEditor.cs). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updated DLL to have an accessor for a public instance
| <PropertyGroup Label="UserMacros" /> | ||
| <PropertyGroup> | ||
| <RepoRoot>$([System.IO.Path]::GetFullPath('$(ProjectDir)..\..\..'))</RepoRoot> | ||
| <PkgPluginDir>$(RepoRoot)\Packages\com.liftoff.windows-ads\Runtime\Plugins\x86_64</PkgPluginDir> |
There was a problem hiding this comment.
Note that I am only building x64 (per MSFT's request). We currently don't have a working version of x86,
There was a problem hiding this comment.
This is Unity's naming convention. it's required
| public static void SetCcpaStatus(bool optIn) | ||
| { | ||
| #if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN | ||
| if (!_callbacksInstalled) return; |
There was a problem hiding this comment.
shouldn't we be able to setccpastatus even if no callbacks are set?
There was a problem hiding this comment.
The callbacks are set when the Unity is registering to the bridge. It's not the bridge to the SDK. So if the Unity registration failed, then it means the DLL failed to load.
Summary
LiftoffUnityBridge.cppwithshared_mutexfor instance lifetime, atomic generation counters, safe init/shutdown sequencing, and TOCTOU race preventionMonoPInvokeCallbacktrampolines,LiftoffMainThreaddispatch for thread-safe event callbacks, mediationLoadAd/PlayAdwith header bidding markup, andGetSuperTokenAPILiftoffEditor.csmenu window for SDK operations in the Unity EditorIsAdPlayableAPI: Publishers should useOnAdLoaded/OnAdLoadFailedcallbacks insteadLiftoff_GetSdkInstance()C++ export: Unsupported raw pointer getter at the DLL layer (not exposed in C#), added per publisher requestSample/directory,CHANGELOG.md, andVungleSDK-6.11.0.1.unitypackageTest infrastructure and Debug/Release DLL split are on branch
WIN-1972-test-infra-and-debug-dllsfor a separate release.Test plan
OnInitializedfires on Windows standalone buildLoadAd/PlayAdwaterfall flow completes with callbacksLoadAd/PlayAdwith header bidding markupGetSuperTokenreturns a valid token after initializationShutdowncleanly tears down without crashes or hangsSetCoppaStatus,SetCcpaStatus,SetGdprConsentStatus) propagate to native SDK🤖 Generated with Claude Code