com.azerion.bluestack 6.0.0-beta.0 → 6.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/CHANGELOG.md +33 -16
  2. package/Editor/Android/AndroidDependencyParser.cs +5 -2
  3. package/Editor/BlueStackAssetPostProcessor.cs +5 -2
  4. package/Editor/BlueStackDependenciesSource.xml +1 -1
  5. package/Editor/BlueStackSettings.cs +8 -0
  6. package/Editor/BlueStackSettingsEditor.cs +70 -19
  7. package/Editor/BlueStackVersionExporter.cs +214 -0
  8. package/Editor/BlueStackVersionExporter.cs.meta +2 -0
  9. package/Editor/Prefabs/AdPrefabs/Banners/Banner.prefab +53 -17
  10. package/Editor/Prefabs/AdPrefabs/Banners/DynamicBanner.prefab +49 -12
  11. package/Editor/Prefabs/AdPrefabs/Banners/{BannerMask.prefab → DynamicLeaderboard.prefab} +178 -75
  12. package/Editor/Prefabs/AdPrefabs/Banners/{BannerMask.prefab.meta → DynamicLeaderboard.prefab.meta} +1 -1
  13. package/Editor/Prefabs/AdPrefabs/Banners/FullBanner.prefab +53 -17
  14. package/Editor/Prefabs/AdPrefabs/Banners/LargeBanner.prefab +53 -17
  15. package/Editor/Prefabs/AdPrefabs/Banners/Leaderboard.prefab +53 -17
  16. package/Editor/Prefabs/AdPrefabs/Banners/MediumRectangle.prefab +53 -17
  17. package/Editor/iOS/{BlueStackBuildPostProcess.cs → IOSBuildPostProcess.cs} +1 -1
  18. package/Example/Scripts/NativeAdManager.cs +11 -19
  19. package/Example/Scripts/SmallNativeAdManager.cs +11 -19
  20. package/Plugins/Android/bluestack-unity-android-bridge-600.aar +0 -0
  21. package/Plugins/Android/bluestack-unity-android-bridge-600.aar.meta +2 -0
  22. package/Plugins/iOS/BSUBanner.h +6 -4
  23. package/Plugins/iOS/BSUBanner.swift +23 -20
  24. package/Plugins/iOS/BSUInitializer.h +1 -1
  25. package/Plugins/iOS/BSUInitializer.swift +10 -3
  26. package/Plugins/iOS/BSUInterface.h +12 -13
  27. package/Plugins/iOS/BSUInterface.m +34 -64
  28. package/Plugins/iOS/BSUInterstitialAd.swift +12 -3
  29. package/Plugins/iOS/BSULogger.h +22 -0
  30. package/Plugins/iOS/{BSUSettings.h.meta → BSULogger.h.meta} +5 -5
  31. package/Plugins/iOS/BSULogger.swift +60 -0
  32. package/Plugins/iOS/{BSUSettings.swift.meta → BSULogger.swift.meta} +5 -5
  33. package/Plugins/iOS/BSUNativeAd.swift +5 -4
  34. package/Plugins/iOS/BSUPluginUtil.swift +3 -9
  35. package/Plugins/iOS/BSURequestOptions.h +13 -5
  36. package/Plugins/iOS/BSURequestOptions.swift +46 -42
  37. package/Plugins/iOS/BSURewardedAd.swift +11 -2
  38. package/Plugins/iOS/BSUTypes.h +0 -1
  39. package/Plugins/iOS/BSUTypes.swift +0 -1
  40. package/README.md +7 -6
  41. package/Runtime/API/Banner/AdMaskHandler.cs +3 -2
  42. package/Runtime/API/Banner/AdPlacementHandler.cs +4 -5
  43. package/Runtime/API/Banner/BannerAd.cs +64 -47
  44. package/Runtime/API/BlueStackAds.cs +14 -3
  45. package/Runtime/API/InterstitialAd.cs +5 -6
  46. package/Runtime/API/NativeAd/NativeAd.cs +9 -13
  47. package/Runtime/API/NativeAd/NativeAdInteractionHandler.cs +2 -5
  48. package/Runtime/API/NativeAd/NativeAdLoader.cs +2 -2
  49. package/Runtime/API/NativeAd/NativeAdObject.cs +7 -23
  50. package/Runtime/API/{Preference → RequestOptions}/Gender.cs +1 -1
  51. package/Runtime/API/{Preference → RequestOptions}/Location.cs +23 -19
  52. package/Runtime/API/RequestOptions/RequestOptions.cs +66 -0
  53. package/Runtime/API/RequestOptions.meta +8 -0
  54. package/Runtime/API/Rewarded/RewardedAd.cs +4 -5
  55. package/Runtime/Common/AdsMainThreadDispatcher.cs +1 -1
  56. package/Runtime/Common/BlueStackLogger.cs +126 -0
  57. package/Runtime/Common/BlueStackLogger.cs.meta +2 -0
  58. package/Runtime/Common/BlueStackSdkInfo.cs +10 -0
  59. package/Runtime/Common/BlueStackSdkInfo.cs.meta +2 -0
  60. package/Runtime/Common/NativeAd/NativeUtils.cs +2 -2
  61. package/Runtime/Internal/IBannerAdClient.cs +4 -4
  62. package/Runtime/Internal/IBlueStackClient.cs +3 -1
  63. package/Runtime/Internal/IClientsFactory.cs +0 -2
  64. package/Runtime/Internal/IInterstitialAdClient.cs +1 -1
  65. package/Runtime/Internal/IRewardedAdClient.cs +1 -1
  66. package/Runtime/Internal/NativeAd/INativeAdClient.cs +1 -1
  67. package/Runtime/Platforms/Android/BannerAdClient.cs +28 -15
  68. package/Runtime/Platforms/Android/BlueStackClient.cs +8 -0
  69. package/Runtime/Platforms/Android/ClientsFactory.cs +0 -11
  70. package/Runtime/Platforms/Android/InitializationStatusClient.cs +2 -1
  71. package/Runtime/Platforms/Android/InterstitialAdClient.cs +12 -4
  72. package/Runtime/Platforms/Android/NativeAdClient.cs +14 -12
  73. package/Runtime/Platforms/Android/RequestOptionsClient.cs +76 -0
  74. package/Runtime/Platforms/Android/RequestOptionsClient.cs.meta +2 -0
  75. package/Runtime/Platforms/Android/RewardedAdClient.cs +12 -5
  76. package/Runtime/Platforms/Unity/AdBehaviour.cs +0 -3
  77. package/Runtime/Platforms/Unity/AdvertisementManager.cs +4 -4
  78. package/Runtime/Platforms/Unity/BannerAdClient.cs +256 -282
  79. package/Runtime/Platforms/Unity/BaseAdClient.cs +3 -2
  80. package/Runtime/Platforms/Unity/BlueStackClient.cs +5 -0
  81. package/Runtime/Platforms/Unity/ClientsFactory.cs +0 -5
  82. package/Runtime/Platforms/Unity/InterstitialAdClient.cs +4 -3
  83. package/Runtime/Platforms/Unity/NativeAdClient.cs +3 -5
  84. package/Runtime/Platforms/Unity/RewardedAdClient.cs +4 -3
  85. package/Runtime/Platforms/Unity/RewardedTimer.cs +2 -1
  86. package/Runtime/Platforms/iOS/BannerAdClient.cs +34 -14
  87. package/Runtime/Platforms/iOS/BlueStackClient.cs +5 -0
  88. package/Runtime/Platforms/iOS/ClientsFactory.cs +0 -11
  89. package/Runtime/Platforms/iOS/Externs.cs +15 -31
  90. package/Runtime/Platforms/iOS/InterstitialAdClient.cs +21 -11
  91. package/Runtime/Platforms/iOS/NativeAdClient.cs +16 -3
  92. package/Runtime/Platforms/iOS/RequestOptionsClient.cs +40 -0
  93. package/Runtime/Platforms/iOS/RequestOptionsClient.cs.meta +2 -0
  94. package/Runtime/Platforms/iOS/RewardedAdClient.cs +21 -10
  95. package/Runtime/Platforms/iOS/Utils.cs +1 -9
  96. package/Runtime/Utilities/AndroidEntryChecker.cs +5 -4
  97. package/Runtime/Utilities/CoordinateConverter.cs +81 -61
  98. package/package.json +2 -2
  99. package/CLAUDE.md +0 -157
  100. package/Example/Scripts/AnchoredBannerExample.cs +0 -179
  101. package/Example/Scripts/AnchoredBannerExample.cs.meta +0 -2
  102. package/Example/Scripts/BannerAnchor.cs +0 -75
  103. package/Example/Scripts/BannerAnchor.cs.meta +0 -2
  104. package/Plugins/Android/bluestack-unity-android-bridge-6.0.1.aar +0 -0
  105. package/Plugins/Android/bluestack-unity-android-bridge-6.0.1.aar.meta +0 -2
  106. package/Plugins/iOS/BSUSettings.h +0 -18
  107. package/Plugins/iOS/BSUSettings.swift +0 -13
  108. package/Runtime/API/Preference/Preference.cs +0 -80
  109. package/Runtime/API/Preference.meta +0 -3
  110. package/Runtime/API/Settings.cs +0 -21
  111. package/Runtime/API/Settings.cs.meta +0 -3
  112. package/Runtime/Internal/IPreferenceClient.cs +0 -15
  113. package/Runtime/Internal/IPreferenceClient.cs.meta +0 -3
  114. package/Runtime/Platforms/Android/PreferenceClient.cs +0 -85
  115. package/Runtime/Platforms/Android/PreferenceClient.cs.meta +0 -3
  116. package/Runtime/Platforms/Unity/PreferenceClient.cs +0 -65
  117. package/Runtime/Platforms/Unity/PreferenceClient.cs.meta +0 -3
  118. package/Runtime/Platforms/iOS/PreferenceClient.cs +0 -69
  119. package/Runtime/Platforms/iOS/PreferenceClient.cs.meta +0 -3
  120. /package/Editor/iOS/{BlueStackBuildPostProcess.cs.meta → IOSBuildPostProcess.cs.meta} +0 -0
  121. /package/Runtime/API/{Preference → RequestOptions}/Gender.cs.meta +0 -0
  122. /package/Runtime/API/{Preference → RequestOptions}/Location.cs.meta +0 -0
  123. /package/Runtime/API/{Preference/Preference.cs.meta → RequestOptions/RequestOptions.cs.meta} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## [6.0.0] - 2026-05-21
3
+ ## [6.0.0-beta.2] - 2026-06-10
4
4
 
5
5
  ### Native SDK upgrades
6
6
 
@@ -20,11 +20,15 @@
20
20
  - `BlueStackAds.IsInitialized` property to check native-side readiness.
21
21
  - `InterstitialAd.IsReady` and `RewardedAd.IsReady` properties.
22
22
  - `BannerAd` anchor-based positioning — new constructor
23
- `BannerAd(string placementId, Transform anchor, Camera camera = null)`. The banner follows the
24
- anchor's screen position automatically via the new `AdPlacementHandler` component.
25
- - `BannerAd` custom screen-space position constructor `BannerAd(string placementId, Vector2 position)`.
26
- - Optional `useSafeArea` parameter on `BannerAd(string, AdPosition, bool useSafeArea = true)` to opt out
27
- of safe-area insets for sticky Top/Bottom banners. Custom-positioned banners always ignore the safe area.
23
+ `BannerAd(string placementId, AdSize adSize, Transform anchor, Camera camera = null)`. The banner
24
+ follows the anchor's screen position automatically via the new `AdPlacementHandler` component.
25
+ The anchor's screen-space position is treated as the banner's **top-left corner** (matching
26
+ native iOS / Android frame placement).
27
+ - `BannerAd` custom screen-space position constructor `BannerAd(string placementId, AdSize adSize, Vector2 position)`.
28
+ The `position` is the banner's **top-left corner** in Unity pixels (bottom-left origin).
29
+ - Optional `useSafeArea` parameter on `BannerAd(string, AdSize, AdPosition, bool useSafeArea = true)`
30
+ to opt out of safe-area insets for sticky Top/Bottom banners. Custom-positioned banners always
31
+ ignore the safe area.
28
32
  - Banner masking — `BannerAd.SetMask(RectTransform, Camera)` / `BannerAd.RemoveMask()` clip the banner
29
33
  to a UI region. The mask follows the `RectTransform` as it animates or resizes.
30
34
  - `BannerAd.OnAdResized` event delivers a `PreferredBannerSize` (`Width`, `Height`) whenever the SDK
@@ -32,8 +36,7 @@
32
36
  - `BannerAd.OnAdLoaded` payload changed to `PreferredBannerSize` carrying the SDK-preferred dimensions
33
37
  (was `EventArgs`).
34
38
  - `RewardedAd.OnAdRewardEarned` (renamed from `OnRewardEarned`) delivers a `RewardedItem`.
35
- - `UpdateInterval` API on `AdMaskHandler` and `AdPlacementHandler`, clamped to `[0, 5]` seconds
36
- (0 = every frame).
39
+ - `UpdateInterval` and `ForceUpdate` API added to `AdMaskHandler` and `AdPlacementHandler`
37
40
 
38
41
  ### Changed
39
42
 
@@ -66,17 +69,31 @@
66
69
  - `OnRewardedVideoAdClicked` → `OnAdClicked`
67
70
  - `OnRewardedVideoAdClosed` → `OnAdDismissed`
68
71
  - `OnUserRewardEarned` → `OnAdRewardEarned`
69
- - Banner ads now show automatically on load (auto-refresh handling moved to the BlueStack console).
70
- - Banner container view now wraps the loaded ad's actual rendered size (previously used the requested
71
- `AdSize`), reducing layout-padding artifacts for dynamic-width banners.
72
- - iOS bridge routes init and SDK-config calls through a `BSUInitializer` Swift façade that caches the
73
- debug-mode flag for bridge-side diagnostics.
74
- - Android bridge routes init and SDK-config calls through a `UnityMobileAds` Kotlin singleton mirroring
75
- the iOS façade.
72
+ - To display banner ads, `BannerAd.Show()` must be called after the ad has been loaded.
73
+ - **Breaking** `BannerAd` constructors now take `AdSize` as a mandatory second parameter, and
74
+ `BannerAd.Load` no longer takes one:
75
+ - Before: `new BannerAd(placementId, adPosition); bannerAd.Load(adSize);`
76
+ - After: `new BannerAd(placementId, adSize, adPosition); bannerAd.Load();`
77
+ - Applies to all three constructor shapes (`AdPosition`, `Vector2`, `Transform` anchor).
78
+ - `BannerAd.Load(AdSize)` and `BannerAd.Load(AdSize, Preference)` are replaced by parameterless
79
+ `Load()` and `Load(Preference)` — the ad size is now fixed for the lifetime of the `BannerAd`.
80
+ - **Breaking** — `Preference` class renamed to `RequestOptions` and made **immutable**.
81
+ - Mutable `Set*` builder methods are replaced by a single optional-named-args constructor:
82
+ `new RequestOptions(age: 25, gender: Gender.Male, location: ..., consentFlag: 1,
83
+ language: "en", keyword: "...", contentUrl: "...")`.
84
+ - A single `RequestOptions` instance is safe to reuse across multiple `Load(...)` calls.
85
+ - **Breaking** — `Location` made immutable:
86
+ - Constructor now `new Location(double latitude, double longitude, string provider = NONE_PROVIDER)`
87
+ instead of `new Location(provider)` + mutable `Latitude` / `Longitude` setters.
88
+ - `getProvider()` method replaced by a `Provider` get-only property.
76
89
 
77
90
  ### Removed
78
91
 
79
- - **Breaking** — `Settings` parameter removed from `BlueStackAds.Initialize`. The `Settings` class is retained for source compatibility only and is no longer used by the SDK.
92
+ - **Breaking** — `Settings` parameter removed from `BlueStackAds.Initialize`. Use `BlueStackAds.SetDebugMode(bool)` instead of
93
+ `new Settings(isDebugModeEnabled: ...)`.
94
+ - **Breaking** — `Preference.Destroy()` removed — the new immutable `RequestOptions` holds no
95
+ native resources on the Unity side; the native intermediate is constructed and released inside
96
+ each `Load(...)` call.
80
97
 
81
98
  ## [3.4.1] - 2026-02-27
82
99
 
@@ -48,12 +48,15 @@ namespace Azerion.BlueStack.Editor.Android
48
48
  {
49
49
  var aliasAttribute = androidPackageNode.Attributes?["alias"];
50
50
  var specAttribute = androidPackageNode.Attributes?["spec"];
51
- var versionAttribute = androidPackageNode.Attributes?["version"];
52
51
  var alias = aliasAttribute != null ? aliasAttribute.Value : specAttribute?.Value;
53
52
  var name = specAttribute?.Value;
54
- var version = versionAttribute?.Value;
55
53
  if (name == null)
56
54
  continue;
55
+ // Extract "version" from the spec "group:artifact:version"
56
+ var lastColon = name.LastIndexOf(':');
57
+ var version = lastColon >= 0 && lastColon < name.Length - 1
58
+ ? name.Substring(lastColon + 1)
59
+ : null;
57
60
  bool active = activeNames != null && activeNames.Contains(name);
58
61
  dependencies.Add(
59
62
  new BlueStackDependency(alias, name, version, androidPackageNode, active)
@@ -7,8 +7,11 @@ namespace Azerion.BlueStack.Editor
7
7
  static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets,
8
8
  string[] movedFromAssetPaths, bool didDomainReload)
9
9
  {
10
- // This is Required when updating the SDK.
11
- // Initialize and Sync dependencies when project loaded in Unity Editor.
10
+ // Required when updating the SDK: sync dependencies when the project loads in the Unity Editor.
11
+ // Only run on a domain reload (project load / script recompile / SDK update).
12
+ if (!didDomainReload)
13
+ return;
14
+
12
15
  BlueStackSettingsEditor.Initialize();
13
16
  }
14
17
  }
@@ -1,4 +1,4 @@
1
- <dependencies version="6.0.0">
1
+ <dependencies version="6.0.0-beta.2">
2
2
  <androidPackages>
3
3
  <androidPackage alias="BlueStack" spec="com.azerion:bluestack-sdk-core:6.0.1">
4
4
  <repositories>
@@ -8,6 +8,14 @@ namespace Azerion.BlueStack.Editor
8
8
  private static BlueStackSettings instance;
9
9
  private const string BlueStackSettingsFile = "BlueStackSettings";
10
10
 
11
+ [SerializeField]
12
+ private string version;
13
+ public string Version
14
+ {
15
+ get => Instance.version;
16
+ set => Instance.version = value;
17
+ }
18
+
11
19
  [SerializeField]
12
20
  private string admobAppIdAndroid;
13
21
  public string AdmobAppIdAndroid
@@ -53,8 +53,6 @@ namespace Azerion.BlueStack.Editor
53
53
  BlueStackSettingsFileExtension
54
54
  );
55
55
  AssetDatabase.CreateAsset(BlueStackSettings.Instance, assetPathWithExtension);
56
- AssetDatabase.SaveAssets();
57
-
58
56
  return true;
59
57
  }
60
58
  catch (Exception e)
@@ -72,35 +70,88 @@ namespace Azerion.BlueStack.Editor
72
70
  }
73
71
  }
74
72
 
75
- // This is Required when updating the SDK.
76
- // Initialize and Sync dependencies when project loaded in Unity Editor.
77
- // This logic is now moved to OnPostprocessAllAssets of BlueStackAssetPostProcessor
78
- // [InitializeOnLoadMethod]
79
- // static void OnProjectLoadedInEditor()
80
- // {
81
- // Initialize();
82
- // }
83
-
84
73
  void OnEnable()
85
74
  {
86
- Initialize();
75
+ // Only refresh the in-memory lists for display.
76
+ // PackageNode is a live XmlNode and BlueStackDependency uses auto-properties, so Unity
77
+ // serializes none of this into the asset, the lists must be rebuilt from XML each load.
78
+
79
+ LoadDependenciesIntoMemory();
80
+ }
81
+
82
+ private static void LoadDependenciesIntoMemory()
83
+ {
84
+ if (!DoesBlueStackSettingsExist())
85
+ return;
86
+
87
+ BlueStackSettings.Instance.AndroidDependencies =
88
+ DependencyProvider.GetBlueStackDependencies(new AndroidDependencyParser());
89
+ BlueStackSettings.Instance.IOSDependencies =
90
+ DependencyProvider.GetBlueStackDependencies(new IOSDependencyParser());
87
91
  }
88
92
 
93
+ private static bool _isInitializing;
94
+
95
+ // The XML sync + persist happens once on domain reload from BlueStackAssetPostProcessor.
89
96
  public static void Initialize()
90
97
  {
98
+ // re-entrancy guard
99
+ if (_isInitializing)
100
+ return;
101
+
91
102
  if (!DoesBlueStackSettingsExist())
92
103
  return;
93
104
 
94
- BlueStackSettings.Instance.IOSDependencies.Clear();
95
- BlueStackSettings.Instance.AndroidDependencies.Clear();
105
+ _isInitializing = true;
106
+ try
107
+ {
108
+ DependencyProvider.SyncDependenciesXMLs();
109
+
110
+ var androidDependencies = DependencyProvider.GetBlueStackDependencies(new AndroidDependencyParser());
111
+ var iOSDependencies = DependencyProvider.GetBlueStackDependencies(new IOSDependencyParser());
112
+
113
+ var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(
114
+ "Packages/com.azerion.bluestack");
115
+ var version = packageInfo != null ? packageInfo.version : BlueStackSettings.Instance.Version;
96
116
 
97
- DependencyProvider.SyncDependenciesXMLs();
117
+ // Only persist when something actually changed. Re-saving an unchanged asset
118
+ // re-triggers OnPostprocessAllAssets and causes an infinite import loop.
119
+ bool changed =
120
+ BlueStackSettings.Instance.Version != version
121
+ || !DependenciesEqual(BlueStackSettings.Instance.AndroidDependencies, androidDependencies)
122
+ || !DependenciesEqual(BlueStackSettings.Instance.IOSDependencies, iOSDependencies);
98
123
 
99
- var androidDependencies = DependencyProvider.GetBlueStackDependencies(new AndroidDependencyParser());
100
- var iOSDependencies = DependencyProvider.GetBlueStackDependencies(new IOSDependencyParser());
124
+ BlueStackSettings.Instance.AndroidDependencies = androidDependencies;
125
+ BlueStackSettings.Instance.IOSDependencies = iOSDependencies;
126
+ BlueStackSettings.Instance.Version = version;
127
+
128
+ if (changed)
129
+ {
130
+ EditorUtility.SetDirty(BlueStackSettings.Instance);
131
+ AssetDatabase.SaveAssets();
132
+ }
133
+ }
134
+ finally
135
+ {
136
+ _isInitializing = false;
137
+ }
138
+ }
101
139
 
102
- BlueStackSettings.Instance.AndroidDependencies = androidDependencies;
103
- BlueStackSettings.Instance.IOSDependencies = iOSDependencies;
140
+ // Compares dependency lists by the fields that get persisted in the asset.
141
+ private static bool DependenciesEqual(
142
+ List<BlueStackDependency> a,
143
+ List<BlueStackDependency> b)
144
+ {
145
+ if (a == null || b == null)
146
+ return ReferenceEquals(a, b);
147
+ if (a.Count != b.Count)
148
+ return false;
149
+ for (int i = 0; i < a.Count; i++)
150
+ {
151
+ if (a[i].Name != b[i].Name || a[i].Active != b[i].Active)
152
+ return false;
153
+ }
154
+ return true;
104
155
  }
105
156
 
106
157
  public override void OnInspectorGUI()
@@ -0,0 +1,214 @@
1
+ using System;
2
+ using System.IO;
3
+ using System.Text.RegularExpressions;
4
+ using UnityEditor;
5
+ using UnityEditor.Build;
6
+ using UnityEditor.Build.Reporting;
7
+ using UnityEditor.PackageManager;
8
+ using UnityEngine;
9
+ using PackageInfo = UnityEditor.PackageManager.PackageInfo;
10
+
11
+ namespace Azerion.BlueStack.Editor
12
+ {
13
+ /// <summary>
14
+ /// Keeps the package version declared in <c>package.json</c> mirrored into,
15
+ /// • <c>Runtime/Common/BlueStackSdkInfo.cs</c>
16
+ /// • <c>BlueStackDependenciesSource.xml</c>
17
+ /// </summary>
18
+ [InitializeOnLoad]
19
+ public static class BlueStackVersionExporter
20
+ {
21
+ internal const string DependenciesVersionPattern = "(<dependencies\\b[^>]*?\\bversion=\")([^\"]*)(\")";
22
+
23
+ private static string _packageJsonPath;
24
+ private static DateTime _lastStamp;
25
+
26
+ static BlueStackVersionExporter()
27
+ {
28
+ Export();
29
+ EditorApplication.update += PollForPackageJsonChange;
30
+ }
31
+
32
+ // Cheap mtime check; only does real work when package.json actually changes on disk.
33
+ private static void PollForPackageJsonChange()
34
+ {
35
+ if (string.IsNullOrEmpty(_packageJsonPath) || !File.Exists(_packageJsonPath))
36
+ {
37
+ return;
38
+ }
39
+
40
+ var stamp = File.GetLastWriteTimeUtc(_packageJsonPath);
41
+ if (stamp == _lastStamp)
42
+ {
43
+ return;
44
+ }
45
+
46
+ _lastStamp = stamp;
47
+ Debug.Log("[BSU sync] package.json changed -> regenerating version files");
48
+ Export();
49
+ }
50
+
51
+ private static void Export()
52
+ {
53
+ var packageInfo = PackageInfo.FindForAssembly(typeof(BlueStackVersionExporter).Assembly);
54
+ if (packageInfo == null)
55
+ {
56
+ return;
57
+ }
58
+
59
+ // Never write into a read-only install (registry / git / embedded-in-cache).
60
+ if (packageInfo.source != PackageSource.Local && packageInfo.source != PackageSource.Embedded)
61
+ {
62
+ return;
63
+ }
64
+
65
+ _packageJsonPath = Path.Combine(packageInfo.resolvedPath, "package.json");
66
+
67
+ // Read the version straight from disk — independent of when the Package Manager re-resolves.
68
+ var version = ReadPackageJsonVersion(_packageJsonPath);
69
+ if (string.IsNullOrEmpty(version))
70
+ {
71
+ return;
72
+ }
73
+
74
+ // Remember the current mtime so the poller doesn't re-fire on the next domain reload.
75
+ _lastStamp = File.GetLastWriteTimeUtc(_packageJsonPath);
76
+
77
+ var changed = SyncDependenciesVersion(packageInfo.resolvedPath, version);
78
+ changed |= SyncSdkInfo(packageInfo.resolvedPath, version);
79
+
80
+ if (changed)
81
+ {
82
+ AssetDatabase.Refresh();
83
+ Debug.Log($"[BSU sync] version files updated to {version}");
84
+ }
85
+ }
86
+
87
+ internal static string ReadPackageJsonVersion(string packageJsonPath)
88
+ {
89
+ if (!File.Exists(packageJsonPath))
90
+ {
91
+ return null;
92
+ }
93
+
94
+ try
95
+ {
96
+ var manifest = JsonUtility.FromJson<PackageManifest>(File.ReadAllText(packageJsonPath));
97
+ return manifest != null ? manifest.version : null;
98
+ }
99
+ catch (Exception e)
100
+ {
101
+ Debug.LogWarning($"[BSU sync] Could not parse {packageJsonPath}: {e.Message}");
102
+ return null;
103
+ }
104
+ }
105
+
106
+ // Returns true if the file was rewritten.
107
+ private static bool SyncSdkInfo(string resolvedPath, string version)
108
+ {
109
+ var outputFile = Path.Combine(resolvedPath, "Runtime", "Common", "BlueStackSdkInfo.cs");
110
+ var content = BuildSdkInfoContent(version);
111
+
112
+ if (File.Exists(outputFile) && File.ReadAllText(outputFile) == content)
113
+ {
114
+ return false;
115
+ }
116
+
117
+ File.WriteAllText(outputFile, content);
118
+ return true;
119
+ }
120
+
121
+ // Returns true if the file was rewritten. Touches ONLY the root <dependencies version="…">.
122
+ internal static bool SyncDependenciesVersion(string resolvedPath, string version)
123
+ {
124
+ var xmlFile = Path.Combine(resolvedPath, "Editor", "BlueStackDependenciesSource.xml");
125
+ if (!File.Exists(xmlFile))
126
+ {
127
+ return false;
128
+ }
129
+
130
+ var xml = File.ReadAllText(xmlFile);
131
+ var match = Regex.Match(xml, DependenciesVersionPattern);
132
+ if (!match.Success)
133
+ {
134
+ Debug.LogWarning(
135
+ $"[BSU sync] No <dependencies version=\"...\"> attribute found in {xmlFile}; skipping version sync.");
136
+ return false;
137
+ }
138
+
139
+ if (match.Groups[2].Value == version)
140
+ {
141
+ return false;
142
+ }
143
+
144
+ var updated = Regex.Replace(xml, DependenciesVersionPattern, "${1}" + version + "${3}");
145
+ File.WriteAllText(xmlFile, updated);
146
+ return true;
147
+ }
148
+
149
+ // Explicit "\n" keeps output byte-identical across platforms so the write-if-changed check holds.
150
+ private static string BuildSdkInfoContent(string version)
151
+ {
152
+ return
153
+ "// <auto-generated>\n" +
154
+ "// Generated by BlueStackVersionExporter from package.json. Do not edit by hand.\n" +
155
+ "// </auto-generated>\n" +
156
+ "namespace Azerion.BlueStack.Common\n" +
157
+ "{\n" +
158
+ " public static class BlueStackSdkInfo\n" +
159
+ " {\n" +
160
+ " public const string Version = \"" + version + "\";\n" +
161
+ " }\n" +
162
+ "}\n";
163
+ }
164
+
165
+ [Serializable]
166
+ private class PackageManifest
167
+ {
168
+ public string version;
169
+ }
170
+ }
171
+
172
+ /// <summary>
173
+ /// Build time validation, check that the version number in <c>BlueStackSdkInfo</c> and
174
+ /// <c>BlueStackDependenciesSource.xml</c> matches <c>package.json</c>
175
+ /// </summary>
176
+ public class BlueStackVersionBuildGate : IPreprocessBuildWithReport
177
+ {
178
+ public int callbackOrder => 0;
179
+
180
+ public void OnPreprocessBuild(BuildReport report)
181
+ {
182
+ var packageInfo = PackageInfo.FindForAssembly(typeof(BlueStackVersionBuildGate).Assembly);
183
+ if (packageInfo == null)
184
+ {
185
+ throw new BuildFailedException("[BlueStack] PackageInfo not found in package.json");
186
+ }
187
+
188
+ var version = BlueStackVersionExporter.ReadPackageJsonVersion(
189
+ Path.Combine(packageInfo.resolvedPath, "package.json"));
190
+ if (string.IsNullOrEmpty(version))
191
+ {
192
+ throw new BuildFailedException("[BlueStack] Unable to retrieve the version number from package.json");
193
+ }
194
+
195
+ // The XML is consumed at build/resolve time and isn't compiled — safe to rewrite in a mutable install.
196
+ if (packageInfo.source == PackageSource.Local || packageInfo.source == PackageSource.Embedded)
197
+ {
198
+ BlueStackVersionExporter.SyncDependenciesVersion(packageInfo.resolvedPath, version);
199
+ }
200
+
201
+ var sdkInfoPath = Path.Combine(packageInfo.resolvedPath, "Runtime", "Common", "BlueStackSdkInfo.cs");
202
+ var committed = File.Exists(sdkInfoPath)
203
+ ? Regex.Match(File.ReadAllText(sdkInfoPath), "Version\\s*=\\s*\"([^\"]+)\"").Groups[1].Value
204
+ : null;
205
+
206
+ if (committed != version)
207
+ {
208
+ throw new BuildFailedException(
209
+ $"[BlueStack] BlueStackSdkInfo.Version ('{committed}') is out of sync with package.json ('{version}'). " +
210
+ "Open the project in the Unity Editor so the version is regenerated, then rebuild.");
211
+ }
212
+ }
213
+ }
214
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: f5a0a4fcd47804487b8e298158c0ce02
@@ -31,12 +31,12 @@ RectTransform:
31
31
  m_LocalScale: {x: 1, y: 1, z: 1}
32
32
  m_ConstrainProportionsScale: 0
33
33
  m_Children: []
34
- m_Father: {fileID: 224975965904302652}
34
+ m_Father: {fileID: 9100000000000002}
35
35
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
36
- m_AnchorMin: {x: 0.5, y: 0}
37
- m_AnchorMax: {x: 0.5, y: 0}
38
- m_AnchoredPosition: {x: 0, y: 87.5}
39
- m_SizeDelta: {x: 1120, y: 175}
36
+ m_AnchorMin: {x: 0, y: 0}
37
+ m_AnchorMax: {x: 1, y: 1}
38
+ m_AnchoredPosition: {x: 0, y: 0}
39
+ m_SizeDelta: {x: 0, y: 0}
40
40
  m_Pivot: {x: 0.5, y: 0.5}
41
41
  --- !u!222 &222746751799034550
42
42
  CanvasRenderer:
@@ -56,8 +56,8 @@ MonoBehaviour:
56
56
  m_Enabled: 1
57
57
  m_EditorHideFlags: 0
58
58
  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
59
- m_Name:
60
- m_EditorClassIdentifier:
59
+ m_Name:
60
+ m_EditorClassIdentifier:
61
61
  m_Material: {fileID: 0}
62
62
  m_Color: {r: 1, g: 1, b: 1, a: 1}
63
63
  m_RaycastTarget: 1
@@ -86,8 +86,8 @@ MonoBehaviour:
86
86
  m_Enabled: 1
87
87
  m_EditorHideFlags: 0
88
88
  m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
89
- m_Name:
90
- m_EditorClassIdentifier:
89
+ m_Name:
90
+ m_EditorClassIdentifier:
91
91
  m_Navigation:
92
92
  m_Mode: 3
93
93
  m_WrapAround: 0
@@ -148,17 +148,17 @@ RectTransform:
148
148
  m_GameObject: {fileID: 1922916089534430}
149
149
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
150
150
  m_LocalPosition: {x: 0, y: 0, z: 0}
151
- m_LocalScale: {x: 0, y: 0, z: 0}
151
+ m_LocalScale: {x: 1, y: 1, z: 1}
152
152
  m_ConstrainProportionsScale: 0
153
153
  m_Children:
154
- - {fileID: 224102598220924924}
154
+ - {fileID: 9100000000000002}
155
155
  m_Father: {fileID: 0}
156
156
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
157
157
  m_AnchorMin: {x: 0, y: 0}
158
- m_AnchorMax: {x: 0, y: 0}
158
+ m_AnchorMax: {x: 1, y: 1}
159
159
  m_AnchoredPosition: {x: 0, y: 0}
160
160
  m_SizeDelta: {x: 0, y: 0}
161
- m_Pivot: {x: 0, y: 0}
161
+ m_Pivot: {x: 0.5, y: 0.5}
162
162
  --- !u!223 &223151453590684250
163
163
  Canvas:
164
164
  m_ObjectHideFlags: 0
@@ -192,8 +192,8 @@ MonoBehaviour:
192
192
  m_Enabled: 1
193
193
  m_EditorHideFlags: 0
194
194
  m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
195
- m_Name:
196
- m_EditorClassIdentifier:
195
+ m_Name:
196
+ m_EditorClassIdentifier:
197
197
  m_IgnoreReversedGraphics: 1
198
198
  m_BlockingObjects: 0
199
199
  m_BlockingMask:
@@ -209,8 +209,8 @@ MonoBehaviour:
209
209
  m_Enabled: 1
210
210
  m_EditorHideFlags: 0
211
211
  m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
212
- m_Name:
213
- m_EditorClassIdentifier:
212
+ m_Name:
213
+ m_EditorClassIdentifier:
214
214
  m_UiScaleMode: 1
215
215
  m_ReferencePixelsPerUnit: 100
216
216
  m_ScaleFactor: 1
@@ -222,3 +222,39 @@ MonoBehaviour:
222
222
  m_DefaultSpriteDPI: 96
223
223
  m_DynamicPixelsPerUnit: 1
224
224
  m_PresetInfoIsWorld: 0
225
+ --- !u!1 &9100000000000001
226
+ GameObject:
227
+ m_ObjectHideFlags: 0
228
+ m_CorrespondingSourceObject: {fileID: 0}
229
+ m_PrefabInstance: {fileID: 0}
230
+ m_PrefabAsset: {fileID: 0}
231
+ serializedVersion: 6
232
+ m_Component:
233
+ - component: {fileID: 9100000000000002}
234
+ m_Layer: 5
235
+ m_Name: BannerContainer
236
+ m_TagString: Untagged
237
+ m_Icon: {fileID: 0}
238
+ m_NavMeshLayer: 0
239
+ m_StaticEditorFlags: 0
240
+ m_IsActive: 1
241
+ --- !u!224 &9100000000000002
242
+ RectTransform:
243
+ m_ObjectHideFlags: 0
244
+ m_CorrespondingSourceObject: {fileID: 0}
245
+ m_PrefabInstance: {fileID: 0}
246
+ m_PrefabAsset: {fileID: 0}
247
+ m_GameObject: {fileID: 9100000000000001}
248
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
249
+ m_LocalPosition: {x: 0, y: 0, z: 0}
250
+ m_LocalScale: {x: 1, y: 1, z: 1}
251
+ m_ConstrainProportionsScale: 0
252
+ m_Children:
253
+ - {fileID: 224102598220924924}
254
+ m_Father: {fileID: 224975965904302652}
255
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
256
+ m_AnchorMin: {x: 0.5, y: 0}
257
+ m_AnchorMax: {x: 0.5, y: 0}
258
+ m_AnchoredPosition: {x: 0, y: 75}
259
+ m_SizeDelta: {x: 960, y: 150}
260
+ m_Pivot: {x: 0.5, y: 0.5}