com.amanotes.gdk 0.2.80 → 0.2.81-a2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.81-a2] - 2024-11-21
4
+ - [Dev] Add GDKPostBuild for iOS
5
+ - [Dev] Improve GDK editor experience before config asset imported
6
+ - [Dev] Analytics save file in async
7
+ - [Dev] Move GDKFileUtils to a separated file
8
+ - [Dev] Update game ops model
9
+ - [Dev] Add ABLab
10
+ - [Dev] Move imported GDK Adapter from RawData to Modules
11
+
3
12
  ## [0.2.80] - 2024-11-13
4
13
  - [Dev] Don't use obsolete functions
5
14
  - [Dev] Add CrashlyticHook
@@ -57,20 +57,21 @@ namespace Amanotes.Editor
57
57
 
58
58
  public void OnEnable()
59
59
  {
60
+ if (sdk == target) return;
61
+
60
62
  sdk = (AmaGDK)target;
61
63
  if (sdk != null) sdk.transform.hideFlags = HideFlags.HideInInspector;
62
-
63
64
  SDKStatus.ClearCache();
64
65
  RefreshConfigStatus();
65
- RefreshListAdapters();
66
+ RefreshListAdapters();
66
67
  }
67
68
 
68
69
  internal enum ConfigAssetStatus
69
70
  {
70
71
  None,
71
72
  ScriptNotFound,
73
+ ImportConfigPackage,
72
74
  AssetNotFound,
73
- AssetIsInvalid,
74
75
  AssetOk
75
76
  }
76
77
 
@@ -78,6 +79,8 @@ namespace Amanotes.Editor
78
79
 
79
80
  void RefreshConfigStatus()
80
81
  {
82
+ AmaGDK._hasLoadConfig = false;
83
+
81
84
  string[] scriptGUIDs = AssetDatabase.FindAssets("t:MonoScript AmaGDKConfigAsset");
82
85
  if (scriptGUIDs.Length == 0)
83
86
  {
@@ -85,14 +88,13 @@ namespace Amanotes.Editor
85
88
  return;
86
89
  }
87
90
 
88
- string[] assetGUIDs = AssetDatabase.FindAssets("t:ConfigAsset");
91
+ string[] assetGUIDs = AssetDatabase.FindAssets("t:AmaGDKConfigAsset");
89
92
  if (assetGUIDs.Length == 0)
90
93
  {
91
94
  cfgStatus = ConfigAssetStatus.AssetNotFound;
92
95
  return;
93
96
  }
94
97
 
95
- cfgStatus = configAsset != null ? ConfigAssetStatus.AssetOk : ConfigAssetStatus.AssetIsInvalid;
96
98
  if (cfgStatus == ConfigAssetStatus.AssetOk) adapterScanned = false;
97
99
  }
98
100
 
@@ -261,22 +263,30 @@ namespace Amanotes.Editor
261
263
  void DrawGUI_GDKConfig()
262
264
  {
263
265
  if (cfgStatus == ConfigAssetStatus.None) RefreshConfigStatus();
264
- if (cfgStatus == ConfigAssetStatus.ScriptNotFound
265
- || cfgStatus == ConfigAssetStatus.AssetIsInvalid)
266
+ if (cfgStatus == ConfigAssetStatus.ScriptNotFound)
266
267
  {
267
- // Automatically import GDKConfigAsset
268
+ cfgStatus = ConfigAssetStatus.ImportConfigPackage;
269
+ AmaGDK._hasLoadConfig = false;
268
270
  AssetDatabase.ImportPackage(Res.AMAGDK_CONFIG_PACKAGE, false);
269
271
  return;
270
272
  }
271
273
 
274
+ if (cfgStatus == ConfigAssetStatus.ImportConfigPackage)
275
+ {
276
+ EditorGUILayout.HelpBox("Importing AmaGDKConfig package...", MessageType.Info);
277
+ return;
278
+ }
279
+
272
280
  if (cfgStatus == ConfigAssetStatus.AssetNotFound)
273
281
  {
274
- EditorGUILayout.HelpBox("[Warning] <Resources/AmaGDKConfig.asset> NOT FOUND!\nYou need to create one to control all the adapter's configurations!", MessageType.Warning);
282
+ EditorGUILayout.HelpBox("[Warning] <Resources/AmaGDKConfig.asset> NOT FOUND or INVALID!\nYou need to create one to control all the adapter's configurations!", MessageType.Warning);
275
283
  GUILayout.BeginHorizontal();
276
284
  {
277
285
  if (AmaGUI.BigButton("CREATE", 40f, BLUE))
278
286
  {
279
287
  CreateGDKConfig();
288
+
289
+ cfgStatus = ConfigAssetStatus.None;
280
290
  RefreshConfigStatus();
281
291
  }
282
292
 
@@ -291,6 +301,9 @@ namespace Amanotes.Editor
291
301
 
292
302
  if (configAsset == null)
293
303
  {
304
+ AmaGDK._hasLoadConfig = false;
305
+ Resources.UnloadUnusedAssets();
306
+
294
307
  Debug.LogWarning("Have you just deleted AmaGDKConfig.asset?" + cfgStatus);
295
308
  cfgStatus = ConfigAssetStatus.None;
296
309
  Repaint();
@@ -24,32 +24,58 @@ namespace Amanotes.Core
24
24
  private const string VERSION_NUMBER_VAR = "VERSION_NUMBER_VAR";
25
25
  private const string VERSION_iOS = "VERSION_BUILD_VAR";
26
26
 
27
- public static void InstallPod(EventHandler processExit)
27
+ public static void InstallPod(EventHandler processExit, string buildPath = null)
28
28
  {
29
29
  try
30
30
  {
31
- if (TryGetEnv("CI_SCRIPT_DIR", out string scriptDir))
31
+ var arguments = $"-c \"/usr/local/bin/pod install --project-directory={buildPath}\"";
32
+ if (Application.isBatchMode && TryGetEnv("CI_SCRIPT_DIR", out string scriptDir))
32
33
  {
33
- var startInfo = new ProcessStartInfo()
34
- {
35
- FileName = "/bin/bash",
36
- Arguments = $"{scriptDir}/utils.sh pod-install",
37
- CreateNoWindow = true
38
- };
39
- var proc = new Process
40
- {
41
- StartInfo = startInfo,
42
- EnableRaisingEvents = true
43
- };
44
- proc.Exited += processExit;
45
- proc.Start();
46
- proc.WaitForExit();
34
+ arguments = $"{scriptDir}/utils.sh pod-install";
47
35
  }
48
- else
36
+
37
+ // Configure process start info
38
+ var startInfo = new ProcessStartInfo
49
39
  {
50
- Debug.Log("Failed to run pod install: $CI_SCRIPT_DIR not found");
51
- }
52
- } catch (Exception e)
40
+ FileName = "/bin/zsh",
41
+ Arguments = arguments,
42
+ CreateNoWindow = false,
43
+ RedirectStandardOutput = true,
44
+ RedirectStandardError = true,
45
+ UseShellExecute = false
46
+ };
47
+
48
+ var proc = new Process
49
+ {
50
+ StartInfo = startInfo,
51
+ EnableRaisingEvents = true
52
+ };
53
+
54
+ // Attach event handlers
55
+ proc.OutputDataReceived += (sender, args) =>
56
+ {
57
+ if (args.Data == null) return; // End of the stream
58
+ Debug.Log(args.Data);
59
+ };
60
+
61
+ // proc.ErrorDataReceived += (sender, args) =>
62
+ // {
63
+ // if (args.Data == null) return; // End of the stream
64
+ // Debug.LogError(args.Data);
65
+ // };
66
+
67
+ proc.Exited += processExit;
68
+
69
+ // Start the process
70
+ proc.Start();
71
+ proc.BeginOutputReadLine();
72
+ proc.BeginErrorReadLine();
73
+ proc.WaitForExit();
74
+
75
+ // Log exit code
76
+ // Debug.LogWarning("Process exited with code: " + proc.ExitCode);
77
+ }
78
+ catch (Exception e)
53
79
  {
54
80
  Debug.LogError(e);
55
81
  }
@@ -0,0 +1,376 @@
1
+ using UnityEngine;
2
+ using UnityEditor;
3
+
4
+ using System.IO;
5
+ using System.Collections.Generic;
6
+ using UnityEditor.Callbacks;
7
+
8
+ #if UNITY_IOS
9
+ using Amanotes.Core;
10
+ using UnityEditor.iOS.Xcode;
11
+ using UnityEditor.iOS.Xcode.Extensions;
12
+ #endif
13
+
14
+ namespace Amanotes.Editor
15
+ {
16
+ [CreateAssetMenu(fileName = "GDKPostBuildAsset", menuName = "Ama GDK/GDKPostBuildAsset", order = 1)]
17
+ public class GDKPostBuildAsset : ScriptableObject
18
+ {
19
+ [System.Serializable]
20
+ public class Framework
21
+ {
22
+ public string name;
23
+ public bool enable;
24
+ public bool isWeak;
25
+ }
26
+
27
+ [System.Serializable]
28
+ public class PodFramework
29
+ {
30
+ public string relativePath; // Only the framework name, e.g., "AppLovinSDK"
31
+ public bool enable;
32
+ }
33
+
34
+ [System.Serializable]
35
+ public class PlistKey
36
+ {
37
+ public string key;
38
+ public string value;
39
+ public bool isString;
40
+ public bool enable;
41
+ }
42
+
43
+ [System.Serializable]
44
+ public class BuildProperty
45
+ {
46
+ public string key;
47
+ public string value;
48
+ public bool enable;
49
+ }
50
+
51
+ [System.Serializable]
52
+ public class Capability
53
+ {
54
+ public string type;
55
+ public string entitlementFile;
56
+ public bool enable;
57
+ }
58
+
59
+ [Header("Frameworks to Add")]
60
+ public List<Framework> frameworks = new List<Framework>
61
+ {
62
+ new Framework { name = "iAd.framework", enable = true, isWeak = false },
63
+ new Framework { name = "AdSupport.framework", enable = true, isWeak = false },
64
+ new Framework { name = "CoreData.framework", enable = true, isWeak = false },
65
+ new Framework { name = "Security.framework", enable = true, isWeak = false },
66
+ new Framework { name = "UserNotifications.framework", enable = true, isWeak = false },
67
+ new Framework { name = "UIKit.framework", enable = true, isWeak = false },
68
+ new Framework { name = "WebKit.framework", enable = true, isWeak = false },
69
+ new Framework { name = "CoreGraphics.framework", enable = true, isWeak = false },
70
+ new Framework { name = "SystemConfiguration.framework", enable = true, isWeak = false },
71
+ new Framework { name = "MobileCoreServices.framework", enable = true, isWeak = false },
72
+ new Framework { name = "AppTrackingTransparency.framework", enable = true, isWeak = false }
73
+ };
74
+
75
+ [Header("Pod Frameworks to Add")]
76
+ public List<PodFramework> podFrameworks = new List<PodFramework>
77
+ {
78
+ new PodFramework { relativePath = "AppLovinSDK", enable = true },
79
+ new PodFramework { relativePath = "InMobiSDK", enable = true }
80
+ };
81
+
82
+ [Header("Info.plist Keys to Add")]
83
+ public List<PlistKey> plistKeys = new List<PlistKey>
84
+ {
85
+ new PlistKey { key = "NSCalendarsUsageDescription", value = "App requires calendar access.", isString = true, enable = true },
86
+ new PlistKey { key = "NSCameraUsageDescription", value = "App requires camera access.", isString = true, enable = true },
87
+ new PlistKey { key = "NSPhotoLibraryUsageDescription", value = "App requires photo library access.", isString = true, enable = true },
88
+ new PlistKey { key = "NSMotionUsageDescription", value = "App requires motion control.", isString = true, enable = true },
89
+ new PlistKey { key = "NSLocationWhenInUseUsageDescription", value = "App requires location access.", isString = true, enable = true },
90
+ new PlistKey { key = "NSUserTrackingUsageDescription", value = "App requires user tracking permission.", isString = true, enable = true }
91
+ };
92
+
93
+ [Header("Build Properties to Set")]
94
+ public List<BuildProperty> buildProperties = new List<BuildProperty>
95
+ {
96
+ new BuildProperty { key = "ENABLE_BITCODE", value = "NO", enable = true },
97
+ new BuildProperty { key = "VERSIONING_SYSTEM", value = "apple-generic", enable = true },
98
+ new BuildProperty { key = "CURRENT_PROJECT_VERSION", value = "1", enable = true },
99
+ new BuildProperty { key = "CODE_SIGN_STYLE", value = "Manual", enable = true }
100
+ };
101
+
102
+ [Header("Capabilities to Enable")]
103
+ public List<Capability> capabilities = new List<Capability>
104
+ {
105
+ new Capability { type = "com.apple.Push", entitlementFile = "amanotes.entitlements", enable = true },
106
+ new Capability { type = "com.apple.InAppPurchase", entitlementFile = "", enable = true },
107
+ new Capability { type = "com.apple.BackgroundModes", entitlementFile = "", enable = true }
108
+ };
109
+
110
+ public string lastBuildPath;
111
+
112
+ #if UNITY_IOS
113
+ private const string PLIST_FILE = "Info.plist";
114
+ private const string EXIST_ON_SUSPEND_KEY = "UIApplicationExitsOnSuspend";
115
+
116
+ public void ExecutePostBuild(string pathToBuildProject)
117
+ {
118
+ lastBuildPath = pathToBuildProject;
119
+
120
+ if (string.IsNullOrEmpty(pathToBuildProject))
121
+ {
122
+ Debug.LogError("Path to build project is null or empty.");
123
+ return;
124
+ }
125
+
126
+ string projectPath = PBXProject.GetPBXProjectPath(pathToBuildProject);
127
+ if (!File.Exists(projectPath))
128
+ {
129
+ Debug.LogError($"Xcode project file not found at {projectPath}");
130
+ return;
131
+ }
132
+
133
+ var proj = new PBXProject();
134
+ proj.ReadFromFile(projectPath);
135
+
136
+ #if UNITY_2020_1_OR_NEWER
137
+ string frameworkTargetGuid = proj.GetUnityFrameworkTargetGuid();
138
+ string mainTargetGuid = proj.GetUnityMainTargetGuid();
139
+ #else
140
+ string frameworkTargetGuid = proj.TargetGuidByName("UnityFramework");
141
+ string mainTargetGuid = proj.TargetGuidByName("Unity-iPhone");
142
+ #endif
143
+
144
+ // Remove deprecated Info.plist keys
145
+ RemoveDeprecatedInfoPListKeys(pathToBuildProject);
146
+ if (string.IsNullOrEmpty(mainTargetGuid))
147
+ {
148
+ Debug.LogError("Main target GUID is null or empty.");
149
+ return;
150
+ }
151
+
152
+ SetBuildProperties(proj, mainTargetGuid);
153
+ AddCapabilities(proj, pathToBuildProject, mainTargetGuid);
154
+ AddFrameworks(proj, mainTargetGuid);
155
+
156
+ GDKCIBuildCommand.InstallPod((sender, e) =>
157
+ {
158
+ AddPodFrameworks(proj, pathToBuildProject, mainTargetGuid);
159
+ ModifyInfoPlist(pathToBuildProject);
160
+ proj.WriteToFile(projectPath);
161
+ }, pathToBuildProject);
162
+ }
163
+
164
+ private void RemoveDeprecatedInfoPListKeys(string pathToBuildProject)
165
+ {
166
+ string plistPath = Path.Combine(pathToBuildProject, PLIST_FILE);
167
+ PlistDocument plist = new PlistDocument();
168
+ plist.ReadFromString(File.ReadAllText(plistPath));
169
+
170
+ PlistElementDict rootDict = plist.root;
171
+
172
+ if (rootDict.values.ContainsKey(EXIST_ON_SUSPEND_KEY))
173
+ {
174
+ Debug.Log($"Removing deprecated key \"{EXIST_ON_SUSPEND_KEY}\" from \"{PLIST_FILE}\".");
175
+ rootDict.values.Remove(EXIST_ON_SUSPEND_KEY);
176
+ }
177
+
178
+ File.WriteAllText(plistPath, plist.WriteToString());
179
+ }
180
+
181
+ private void SetBuildProperties(PBXProject proj, string targetGuid)
182
+ {
183
+ foreach (var property in buildProperties)
184
+ {
185
+ if (!property.enable) continue;
186
+ proj.SetBuildProperty(targetGuid, property.key, property.value);
187
+ Debug.Log($"Set build property: {property.key} = {property.value}");
188
+ }
189
+ }
190
+
191
+ // Enable Capabilities
192
+ private void AddCapabilities(PBXProject proj, string pathToBuildProject, string targetGuid)
193
+ {
194
+ foreach (var capability in capabilities)
195
+ {
196
+ if (!capability.enable) continue;
197
+
198
+ var pbxCapabilityType = PBXCapabilityType.StringToPBXCapabilityType(capability.type);
199
+ if (pbxCapabilityType == null)
200
+ {
201
+ Debug.LogError($"Unknown capability type: {capability.type}");
202
+ continue;
203
+ }
204
+
205
+ if (!string.IsNullOrEmpty(capability.entitlementFile))
206
+ {
207
+ string entitlementsPath = Path.Combine(pathToBuildProject, capability.entitlementFile);
208
+ if (!File.Exists(entitlementsPath))
209
+ {
210
+ Debug.LogError($"Entitlements file not found: {entitlementsPath}");
211
+ continue;
212
+ }
213
+ proj.AddCapability(targetGuid, pbxCapabilityType, entitlementsPath);
214
+ Debug.Log($"Added capability: {pbxCapabilityType.id} with entitlements: {entitlementsPath}");
215
+ }
216
+ else
217
+ {
218
+ proj.AddCapability(targetGuid, pbxCapabilityType);
219
+ Debug.Log($"Added capability: {pbxCapabilityType.id}");
220
+ }
221
+ }
222
+ }
223
+
224
+ private void AddFrameworks(PBXProject proj, string targetGuid)
225
+ {
226
+ foreach (var framework in frameworks)
227
+ {
228
+ if (!framework.enable) continue;
229
+ proj.AddFrameworkToProject(targetGuid, framework.name, framework.isWeak);
230
+ Debug.Log($"Added framework: {framework.name} (Weak: {framework.isWeak})");
231
+ }
232
+ }
233
+
234
+ private void AddPodFrameworks(PBXProject proj, string pathToBuildProject, string targetGuid)
235
+ {
236
+ string podsDirectory = Path.Combine(pathToBuildProject, "Pods");
237
+ if (!Directory.Exists(podsDirectory))
238
+ {
239
+ Debug.LogError($"Pods directory not found. Ensure pod install has completed successfully.");
240
+ return;
241
+ }
242
+
243
+ foreach (var podFramework in podFrameworks)
244
+ {
245
+ if (!podFramework.enable) continue;
246
+
247
+ string frameworkPath = SearchForPodFramework(pathToBuildProject, podFramework.relativePath);
248
+ if (string.IsNullOrEmpty(frameworkPath))
249
+ {
250
+ Debug.LogError($"Failed to find Pod framework for: {podFramework.relativePath}");
251
+ continue;
252
+ }
253
+
254
+ string fileGuid = proj.AddFile(frameworkPath, frameworkPath, PBXSourceTree.Source);
255
+ if (string.IsNullOrEmpty(fileGuid))
256
+ {
257
+ Debug.LogError($"Failed to add Pod framework: {frameworkPath}");
258
+ continue;
259
+ }
260
+
261
+ proj.AddFileToEmbedFrameworks(targetGuid, fileGuid);
262
+
263
+ // Set CODE_SIGN_ON_COPY for Debug and Release configurations
264
+ // string debugConfig = proj.BuildConfigByName(targetGuid, "Debug");
265
+ // string releaseConfig = proj.BuildConfigByName(targetGuid, "Release");
266
+ //
267
+ // if (!string.IsNullOrEmpty(debugConfig))
268
+ // {
269
+ // proj.SetBuildProperty(debugConfig, "CODE_SIGN_ON_COPY", "YES");
270
+ // }
271
+ //
272
+ // if (!string.IsNullOrEmpty(releaseConfig))
273
+ // {
274
+ // proj.SetBuildProperty(releaseConfig, "CODE_SIGN_ON_COPY", "YES");
275
+ // }
276
+
277
+ Debug.Log($"Added Pod framework to Embed Frameworks: {frameworkPath}");
278
+ }
279
+ }
280
+
281
+ private void ModifyInfoPlist(string pathToBuildProject)
282
+ {
283
+ string plistPath = Path.Combine(pathToBuildProject, PLIST_FILE);
284
+ if (!File.Exists(plistPath))
285
+ {
286
+ Debug.LogError($"Info.plist file not found at {plistPath}");
287
+ return;
288
+ }
289
+
290
+ var plist = new PlistDocument();
291
+ plist.ReadFromFile(plistPath);
292
+ var rootDict = plist.root;
293
+
294
+ foreach (var plistKey in plistKeys)
295
+ {
296
+ if (!plistKey.enable) continue;
297
+
298
+ if (plistKey.isString)
299
+ {
300
+ rootDict.SetString(plistKey.key, plistKey.value);
301
+ }
302
+ else if (bool.TryParse(plistKey.value, out bool boolValue))
303
+ {
304
+ rootDict.SetBoolean(plistKey.key, boolValue);
305
+ }
306
+
307
+ Debug.Log($"Added/Updated Info.plist key: {plistKey.key}");
308
+ }
309
+
310
+ plist.WriteToFile(plistPath);
311
+ }
312
+
313
+ private string SearchForPodFramework(string projectPath, string podFrameworkName)
314
+ {
315
+ string podsDirectory = Path.Combine(projectPath, "Pods", podFrameworkName);
316
+
317
+ if (!Directory.Exists(podsDirectory))
318
+ {
319
+ Debug.LogError($"Pods directory not found for: {podFrameworkName}");
320
+ return null;
321
+ }
322
+
323
+ // Recursively search for directories named {POD_FRAMEWORK_NAME}.xcframework
324
+ string[] frameworkPaths = Directory.GetDirectories(podsDirectory, $"{podFrameworkName}.xcframework", SearchOption.AllDirectories);
325
+
326
+ if (frameworkPaths.Length > 0)
327
+ {
328
+ Debug.Log($"Found framework for {podFrameworkName}: {frameworkPaths[0]}");
329
+ return frameworkPaths[0];
330
+ }
331
+
332
+ Debug.LogError($"No .xcframework folder found for {podFrameworkName} in {podsDirectory}");
333
+ return null;
334
+ }
335
+
336
+ [PostProcessBuild]
337
+ public static void OnPostprocessBuild(BuildTarget target, string pathToBuildProject)
338
+ {
339
+ if (target != BuildTarget.iOS) return;
340
+
341
+ GDKPostBuildAsset postBuildAsset = FindGDKPostBuildAsset();
342
+ if (postBuildAsset == null) return;
343
+
344
+ Debug.Log($"Post processing: {pathToBuildProject}");
345
+ postBuildAsset.ExecutePostBuild(pathToBuildProject);
346
+ }
347
+
348
+ [MenuItem("Window/AmaGDK/Postprocess Build iOS")]
349
+ public static void RunPostBuildIOS()
350
+ {
351
+ var buildAsset = FindGDKPostBuildAsset();
352
+ if (buildAsset == null) return;
353
+
354
+ if (string.IsNullOrEmpty(buildAsset.lastBuildPath))
355
+ {
356
+ buildAsset.lastBuildPath = Path.GetFullPath("../../_Build/XCode_PHB3_v2.13.3_b200611");
357
+ }
358
+
359
+ OnPostprocessBuild(BuildTarget.iOS, buildAsset.lastBuildPath);
360
+ }
361
+
362
+ public static GDKPostBuildAsset FindGDKPostBuildAsset()
363
+ {
364
+ string[] guids = AssetDatabase.FindAssets("t:GDKPostBuildAsset");
365
+ if (guids.Length == 0)
366
+ {
367
+ Debug.LogError("No GDKPostBuildAsset found in the project.");
368
+ return null;
369
+ }
370
+
371
+ string path = AssetDatabase.GUIDToAssetPath(guids[0]);
372
+ return AssetDatabase.LoadAssetAtPath<GDKPostBuildAsset>(path);
373
+ }
374
+ #endif
375
+ }
376
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: e1f86b36b12bb4965b3d96d737729f17
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -270,7 +270,6 @@ namespace Amanotes.Editor
270
270
  public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
271
271
  {
272
272
  var rawData = _dicThirdPartyVersion
273
- .Concat(_dicImportedGDKAdapter)
274
273
  .Concat(_dicExtraVersion)
275
274
  .Concat(_dicIronSourceAdapterVersion.ToDictionary(kvp => kvp.Key, kvp => (object)("IS_" + kvp.Value)))
276
275
  .ToDictionary(x => x.Key, x => x.Value);
@@ -293,8 +292,10 @@ namespace Amanotes.Editor
293
292
  {"BuildTime", GetBuildTime()},
294
293
  {"AppId", PlayerSettings.applicationIdentifier },
295
294
  {"Platform", platform},
295
+ {"Modules", _dicImportedGDKAdapter },
296
296
  {"RawData", rawData }
297
297
  };
298
+
298
299
  string json = JsonUtils.DictionaryToJson(buildInfo, false, false);
299
300
  Task.Run(()=>SendDataToAirTable(json));
300
301
  }
@@ -93,7 +93,7 @@ namespace Amanotes.Core
93
93
 
94
94
  internal void Save()
95
95
  {
96
- this.SaveJsonToFile();
96
+ this.SaveJsonToFile(false);
97
97
  }
98
98
  }
99
99
 
@@ -135,7 +135,7 @@ namespace Amanotes.Core
135
135
 
136
136
  if (!localData._dirty) return;
137
137
  localData._dirty = false;
138
- localData.SaveJsonToFile();
138
+ localData.SaveJsonToFile(true); // immediately = true
139
139
  }
140
140
 
141
141
  public static IEventParamsBuilder LogFunnelEvent(string eventName, string signature = "")
@@ -314,7 +314,7 @@ namespace Amanotes.Core
314
314
  internal static readonly GDKPool.Pool<EventParams> epPool = new GDKPool.Pool<EventParams>(1);
315
315
 
316
316
  private static readonly Lazy<AnalyticsData> _localData = new Lazy<AnalyticsData>(
317
- () => new AnalyticsData().LoadJsonFromFile().BuildCache());
317
+ () => new AnalyticsData().ReloadDataFromLocal());
318
318
  internal static AnalyticsData localData => _localData.Value;
319
319
 
320
320
  internal static readonly Dictionary<string, string> reservedPrefixes = new Dictionary<string, string>()
@@ -800,6 +800,12 @@ namespace Amanotes.Core
800
800
  return this;
801
801
  }
802
802
 
803
+ public AnalyticsData ReloadDataFromLocal()
804
+ {
805
+ this.LoadJsonFromFile().BuildCache();
806
+ return this;
807
+ }
808
+
803
809
  internal EventDetail GetEventDetail(string eventName, bool autoNew = false)
804
810
  {
805
811
  if (string.IsNullOrEmpty(eventName)) return null;
@@ -843,7 +849,7 @@ namespace Amanotes.Core
843
849
  if (!Config.analytics.autoFlushEventDetail) return;
844
850
  if (!_dirty) return;
845
851
  _dirty = false;
846
- this.SaveJsonToFile();
852
+ this.SaveJsonToFile(false);
847
853
  }
848
854
 
849
855
  internal void UpdateMigrationLog()
@@ -66,7 +66,7 @@ namespace Amanotes.Core
66
66
  ConsentHook.ATTRequest(isAllowed =>
67
67
  {
68
68
  data.att = isAllowed ? Status.Allowed : Status.NotAllowed;
69
- data.SaveJsonToFile();
69
+ data.SaveJsonToFile(false);
70
70
  onComplete?.Invoke(allowedATT);
71
71
  });
72
72
  }
@@ -107,7 +107,7 @@ namespace Amanotes.Core
107
107
  if (successful)
108
108
  {
109
109
  data.cmp = !IsRequired ? Status.NotRequired : Status.Allowed;
110
- data.SaveJsonToFile();
110
+ data.SaveJsonToFile(false);
111
111
  }
112
112
  onComplete?.Invoke(checkedCMP);
113
113
  });
@@ -306,7 +306,7 @@ namespace Amanotes.Core
306
306
  subscriptionExpiredDate = _subscriptionExpiredDate?.ToString(DATE_FORMAT);
307
307
  _unsubscribedDate = Adapter.GetUnsubscribedDate();
308
308
  unsubscribedDate = _unsubscribedDate?.ToString(DATE_FORMAT);
309
- this.SaveJsonToFile();
309
+ this.SaveJsonToFile(false);
310
310
  }
311
311
  }
312
312
  }
@@ -33,14 +33,14 @@ namespace Amanotes.Core
33
33
  private bool _dirty = false;
34
34
  private void Save()
35
35
  {
36
- this.SaveJsonToFile();
36
+ this.SaveJsonToFile(false);
37
37
  }
38
38
 
39
39
  private void SaveIfDirty()
40
40
  {
41
41
  if (!_dirty) return;
42
42
  _dirty = false;
43
- this.SaveJsonToFile();
43
+ this.SaveJsonToFile(false);
44
44
  }
45
45
  }
46
46
 
package/Runtime/AmaGDK.cs CHANGED
@@ -27,10 +27,10 @@ namespace Amanotes.Core
27
27
  {
28
28
  public partial class AmaGDK : MonoBehaviour
29
29
  {
30
- public const string VERSION = "0.2.80";
30
+ public const string VERSION = "0.2.81-a2";
31
31
 
32
32
  internal static Status _status = Status.None;
33
- private static ConfigAsset _config = null;
33
+ internal static ConfigAsset _config = null;
34
34
 
35
35
  private static readonly Lazy<GDKGeoLocation> _geoLocation = new Lazy<GDKGeoLocation>(() => new GDKGeoLocation());
36
36
  private static readonly Lazy<GDKServerTime> _serverTime = new Lazy<GDKServerTime>(() => new GDKServerTime());
@@ -41,7 +41,8 @@ namespace Amanotes.Core
41
41
  internal static GDKServerTime ServerTime => _serverTime.Value;
42
42
  private static ForceQuitMonitor ForceQuit => _forceQuit.Value;
43
43
 
44
- internal static bool _allowInit = false;
44
+ [NonSerialized] internal static bool _allowInit = false;
45
+ [NonSerialized] internal static bool _hasLoadConfig = false;
45
46
 
46
47
  public bool autoInit = true;
47
48
 
@@ -65,8 +66,10 @@ namespace Amanotes.Core
65
66
  {
66
67
  get
67
68
  {
68
- if (_config != null) return _config;
69
+ if (_config != null || _hasLoadConfig) return _config;
70
+ _hasLoadConfig = true;
69
71
  var config = Resources.Load<ConfigAsset>("AmaGDKConfig");
72
+
70
73
  #if UNITY_EDITOR
71
74
  if (EditorApplication.isPlayingOrWillChangePlaymode)
72
75
  {
@@ -56,11 +56,19 @@ namespace Amanotes.Core.Internal
56
56
  return;
57
57
  }
58
58
 
59
- GameObject go = UnityObject.Instantiate(prefab);
60
- go.name = "AmaGDK";
61
- go.transform.SetAsLastSibling();
59
+ tempGO = UnityObject.Instantiate(prefab);
60
+ tempGO.name = "AmaGDK";
61
+ tempGO.transform.SetAsLastSibling();
62
62
  Selection.activeObject = null;
63
- Selection.activeObject = go;
63
+ EditorApplication.update -= DelaySelect;
64
+ EditorApplication.update += DelaySelect;
65
+ }
66
+
67
+ private static GameObject tempGO;
68
+ private static void DelaySelect()
69
+ {
70
+ EditorApplication.update -= DelaySelect;
71
+ Selection.activeObject = tempGO;
64
72
  }
65
73
  #endif
66
74
  }
@@ -1,7 +1,6 @@
1
1
  using System;
2
2
  using System.Collections;
3
3
  using System.Collections.Generic;
4
- using System.IO;
5
4
  using System.Linq;
6
5
  using System.Reflection;
7
6
  using System.Text;
@@ -128,230 +127,7 @@ namespace Amanotes.Core.Internal
128
127
  }
129
128
 
130
129
  }
131
-
132
- public static class GDKFileUtils
133
- {
134
- private static string _basePath;
135
- public static string basePath
136
- {
137
- get
138
- {
139
- if (!string.IsNullOrEmpty(_basePath)) return _basePath;
140
- _basePath = Application.isEditor
141
- ? Path.Combine(Directory.GetCurrentDirectory(), "Library", "AmaGDK")
142
- : Path.Combine(Application.persistentDataPath, "AmaGDK");
143
-
144
- if (!Directory.Exists(_basePath)) Directory.CreateDirectory(_basePath);
145
- return _basePath;
146
- }
147
- }
148
-
149
- internal static string GetPath(string fileName)
150
- {
151
- return Path.Combine(basePath, fileName);
152
- }
153
-
154
- internal static bool Exist(string fileName)
155
- {
156
- return File.Exists(GetPath(fileName));
157
- }
158
-
159
- public static void Save(string fileName, string content)
160
- {
161
- if (string.IsNullOrWhiteSpace(fileName))
162
- {
163
- LogWarningOnce("File name must be not null or empty");
164
- return;
165
- }
166
-
167
- if (string.IsNullOrWhiteSpace(content))
168
- {
169
- LogWarningOnce($"Content for {fileName} is empty");
170
- }
171
-
172
- string path = GetPath(fileName);
173
-
174
- try
175
- {
176
- File.WriteAllText(path, content);
177
- }
178
- catch (Exception ex)
179
- {
180
- LogWarningOnce(path + "\n" + ex);
181
- }
182
- }
183
-
184
- internal static void SaveAppend(string fileName, IEnumerable<string> contents)
185
- {
186
- if (string.IsNullOrWhiteSpace(fileName))
187
- {
188
- LogWarningOnce("File name must be not null or empty");
189
- return;
190
- }
191
-
192
- if (contents == null || !contents.GetEnumerator().MoveNext())
193
- {
194
- LogWarningOnce($"Content for {fileName} is empty");
195
- }
196
-
197
- string path = GetPath(fileName);
198
- try
199
- {
200
- File.AppendAllLines(path, contents);
201
- }
202
- catch (Exception ex)
203
- {
204
- LogWarningOnce(path + "\n" + ex);
205
- }
206
- }
207
-
208
- internal static void SaveAppend(string fileName, string content)
209
- {
210
- if (string.IsNullOrWhiteSpace(fileName))
211
- {
212
- LogWarningOnce("File name must be not null or empty");
213
- return;
214
- }
215
-
216
- if (string.IsNullOrWhiteSpace(content))
217
- {
218
- LogWarningOnce($"Content for {fileName} is empty");
219
- }
220
-
221
- string path = GetPath(fileName);
222
- try
223
- {
224
- File.AppendAllText(path, content);
225
- }
226
- catch (Exception ex)
227
- {
228
- LogWarningOnce(path + "\n" + ex);
229
- }
230
- }
231
-
232
- public static string Load(string fileName)
233
- {
234
- var content = "";
235
-
236
- if (string.IsNullOrWhiteSpace(fileName))
237
- {
238
- LogWarningOnce("File name must be not null or empty");
239
- return content;
240
- }
241
-
242
- string path = GetPath(fileName);
243
- if (!File.Exists(path)) return content;
244
-
245
- try
246
- {
247
- content = File.ReadAllText(path);
248
- }
249
- catch (Exception ex)
250
- {
251
- LogWarningOnce(path + "\n" + ex);
252
- }
253
-
254
- return content;
255
- }
256
-
257
- public static async Task<string> LoadFromStreamingAssets(string fileName)
258
- {
259
- string filePath = Path.Combine(Application.streamingAssetsPath, fileName);
260
-
261
- // Android
262
- if (Application.platform == RuntimePlatform.Android)
263
- {
264
- using (UnityWebRequest request = UnityWebRequest.Get(filePath))
265
- {
266
- var operation = request.SendWebRequest();
267
- while (!operation.isDone)
268
- {
269
- await Task.Yield();
270
- }
271
- if (request.result != UnityWebRequest.Result.Success)
272
- {
273
- LogWarning($"Error reading file: {request.error}");
274
- return null;
275
- }
276
- return request.downloadHandler.text;
277
- }
278
- }
279
-
280
- // Other
281
- try
282
- {
283
- return File.ReadAllText(filePath);
284
- }
285
- catch (Exception e)
286
- {
287
- LogWarning($"Error reading file: {e.Message}");
288
- }
289
- return null;
290
- }
291
-
292
- internal static void Delete(string fileName)
293
- {
294
- string path = GetPath(fileName);
295
- if (!File.Exists(path)) return;
296
-
297
- try
298
- {
299
- File.Delete(path);
300
- }
301
- catch (Exception ex)
302
- {
303
- LogWarningOnce(path + "\n" + ex);
304
- }
305
- }
306
-
307
- public static void ClearCacheData()
308
- {
309
- var sb = GDKPool.GetStringBuilder();
310
- sb.AppendLine("[AmaGDK] All cached data & stats cleared");
311
-
312
- if (!Directory.Exists(basePath))
313
- {
314
- sb.AppendLine("Cache path not exists: " + basePath);
315
- return;
316
- }
317
-
318
- string[] filePaths = Directory.GetFiles(basePath);
319
- foreach (string path in filePaths)
320
- {
321
- Delete(path);
322
- sb.AppendLine($"[deleted] - {path}");
323
- }
324
- Debug.Log(sb);
325
- }
326
-
327
- public static T LoadJsonFromFile<T>(T instance) where T: new()
328
- {
329
- instance ??= new T();
330
-
331
- string fileName = instance.GetType().Name;
332
- string json = Load(fileName);
333
- if (!string.IsNullOrEmpty(json))
334
- {
335
- try
336
- {
337
- JsonUtility.FromJsonOverwrite(json, instance);
338
- } catch (Exception e)
339
- {
340
- LogWarning("LoadJsonFromFile error: " + fileName + "\n" + e);
341
- }
342
- }
343
-
344
- return instance;
345
- }
346
-
347
- public static void SaveJsonToFile<T>(T instance)
348
- {
349
- string fileName = instance.GetType().Name;
350
- string json = JsonUtility.ToJson(instance);
351
- Save(fileName, json);
352
- }
353
- }
354
-
130
+
355
131
  public class ActionQueue
356
132
  {
357
133
  private bool isProcessing;
@@ -419,14 +195,14 @@ namespace Amanotes.Core.Internal
419
195
 
420
196
  internal static class JsonFileExtension
421
197
  {
422
- public static T LoadJsonFromFile<T>(this T instance) where T : IJsonFile, new()
198
+ internal static T LoadJsonFromFile<T>(this T instance) where T : IJsonFile, new()
423
199
  {
424
200
  return GDKFileUtils.LoadJsonFromFile(instance);
425
201
  }
426
202
 
427
- public static void SaveJsonToFile<T>(this T instance) where T: IJsonFile
203
+ internal static void SaveJsonToFile<T>(this T instance, bool immediately) where T: IJsonFile
428
204
  {
429
- GDKFileUtils.SaveJsonToFile(instance);
205
+ GDKFileUtils.SaveJsonToFile(instance, immediately);
430
206
  }
431
207
  }
432
208
 
@@ -34,7 +34,7 @@ namespace Amanotes.Core.Internal
34
34
  internal void SaveIfDirty(bool forceSave = false)
35
35
  {
36
36
  if (!isDirty && !forceSave) return;
37
- this.SaveJsonToFile();
37
+ this.SaveJsonToFile(true);
38
38
  isDirty = false;
39
39
  }
40
40
  }
@@ -0,0 +1,246 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.IO;
4
+ using System.Threading.Tasks;
5
+ using UnityEngine;
6
+ using UnityEngine.Networking;
7
+ using static Amanotes.Core.GDKDebug;
8
+
9
+ namespace Amanotes.Core.Internal
10
+ {
11
+ public static class GDKFileUtils
12
+ {
13
+ private static string _basePath;
14
+ public static string basePath
15
+ {
16
+ get
17
+ {
18
+ if (!string.IsNullOrEmpty(_basePath)) return _basePath;
19
+ _basePath = Application.isEditor
20
+ ? Path.Combine(Directory.GetCurrentDirectory(), "Library", "AmaGDK")
21
+ : Path.Combine(Application.persistentDataPath, "AmaGDK");
22
+
23
+ if (!Directory.Exists(_basePath)) Directory.CreateDirectory(_basePath);
24
+ return _basePath;
25
+ }
26
+ }
27
+
28
+ internal static string GetPath(string fileName)
29
+ {
30
+ return Path.Combine(basePath, fileName);
31
+ }
32
+
33
+ internal static bool Exist(string fileName)
34
+ {
35
+ return File.Exists(GetPath(fileName));
36
+ }
37
+
38
+ public static void Save(string fileName, string content, bool immediately = true)
39
+ {
40
+ if (string.IsNullOrWhiteSpace(fileName))
41
+ {
42
+ LogWarningOnce("File name must be not null or empty");
43
+ return;
44
+ }
45
+
46
+ if (string.IsNullOrWhiteSpace(content))
47
+ {
48
+ LogWarningOnce($"Content for {fileName} is empty");
49
+ }
50
+
51
+ ThreadWrite(fileName, content, immediately);
52
+ }
53
+
54
+ private static void ThreadWrite(string fileName, string content, bool immediately)
55
+ {
56
+ // Only write on main thread if immediately is true
57
+ if (!immediately && GDKUtils.isOnMainThread)
58
+ {
59
+ Task.Run(()=>ThreadWrite(fileName, content, true))
60
+ .ConfigureAwait(false);
61
+ return;
62
+ }
63
+
64
+ string filePath = GetPath(fileName);
65
+ try
66
+ {
67
+ File.WriteAllText(filePath, content);
68
+ }
69
+ catch (Exception ex)
70
+ {
71
+ LogWarningOnce($"Exception in ThreadWrite(): {filePath}\n{ex}");
72
+ }
73
+ }
74
+
75
+ internal static void SaveAppend(string fileName, IEnumerable<string> contents)
76
+ {
77
+ if (string.IsNullOrWhiteSpace(fileName))
78
+ {
79
+ LogWarningOnce("File name must be not null or empty");
80
+ return;
81
+ }
82
+
83
+ if (contents == null || !contents.GetEnumerator().MoveNext())
84
+ {
85
+ LogWarningOnce($"Content for {fileName} is empty");
86
+ }
87
+
88
+ string path = GetPath(fileName);
89
+ try
90
+ {
91
+ File.AppendAllLines(path, contents);
92
+ }
93
+ catch (Exception ex)
94
+ {
95
+ LogWarningOnce(path + "\n" + ex);
96
+ }
97
+ }
98
+
99
+ internal static void SaveAppend(string fileName, string content)
100
+ {
101
+ if (string.IsNullOrWhiteSpace(fileName))
102
+ {
103
+ LogWarningOnce("File name must be not null or empty");
104
+ return;
105
+ }
106
+
107
+ if (string.IsNullOrWhiteSpace(content))
108
+ {
109
+ LogWarningOnce($"Content for {fileName} is empty");
110
+ }
111
+
112
+ string path = GetPath(fileName);
113
+ try
114
+ {
115
+ File.AppendAllText(path, content);
116
+ }
117
+ catch (Exception ex)
118
+ {
119
+ LogWarningOnce(path + "\n" + ex);
120
+ }
121
+ }
122
+
123
+ public static string Load(string fileName)
124
+ {
125
+ var content = "";
126
+
127
+ if (string.IsNullOrWhiteSpace(fileName))
128
+ {
129
+ LogWarningOnce("File name must be not null or empty");
130
+ return content;
131
+ }
132
+
133
+ string path = GetPath(fileName);
134
+ if (!File.Exists(path)) return content;
135
+
136
+ try
137
+ {
138
+ content = File.ReadAllText(path);
139
+ }
140
+ catch (Exception ex)
141
+ {
142
+ LogWarningOnce(path + "\n" + ex);
143
+ }
144
+
145
+ return content;
146
+ }
147
+
148
+ public static async Task<string> LoadFromStreamingAssets(string fileName)
149
+ {
150
+ string filePath = Path.Combine(Application.streamingAssetsPath, fileName);
151
+
152
+ // Android
153
+ if (Application.platform == RuntimePlatform.Android)
154
+ {
155
+ using (UnityWebRequest request = UnityWebRequest.Get(filePath))
156
+ {
157
+ var operation = request.SendWebRequest();
158
+ while (!operation.isDone)
159
+ {
160
+ await Task.Yield();
161
+ }
162
+ if (request.result != UnityWebRequest.Result.Success)
163
+ {
164
+ LogWarning($"Error reading file: {request.error}");
165
+ return null;
166
+ }
167
+ return request.downloadHandler.text;
168
+ }
169
+ }
170
+
171
+ // Other
172
+ try
173
+ {
174
+ return File.ReadAllText(filePath);
175
+ }
176
+ catch (Exception e)
177
+ {
178
+ LogWarning($"Error reading file: {e.Message}");
179
+ }
180
+ return null;
181
+ }
182
+
183
+
184
+ internal static void Delete(string fileName)
185
+ {
186
+ string path = GetPath(fileName);
187
+ if (!File.Exists(path)) return;
188
+
189
+ try
190
+ {
191
+ File.Delete(path);
192
+ }
193
+ catch (Exception ex)
194
+ {
195
+ LogWarningOnce(path + "\n" + ex);
196
+ }
197
+ }
198
+
199
+ public static void ClearCacheData()
200
+ {
201
+ var sb = GDKPool.GetStringBuilder();
202
+ sb.AppendLine("[AmaGDK] All cached data & stats cleared");
203
+
204
+ if (!Directory.Exists(basePath))
205
+ {
206
+ sb.AppendLine("Cache path not exists: " + basePath);
207
+ return;
208
+ }
209
+
210
+ string[] filePaths = Directory.GetFiles(basePath);
211
+ foreach (string path in filePaths)
212
+ {
213
+ Delete(path);
214
+ sb.AppendLine($"[deleted] - {path}");
215
+ }
216
+ Debug.Log(sb);
217
+ }
218
+
219
+ public static T LoadJsonFromFile<T>(T instance) where T: new()
220
+ {
221
+ instance ??= new T();
222
+
223
+ string fileName = instance.GetType().Name;
224
+ string json = Load(fileName);
225
+ if (!string.IsNullOrEmpty(json))
226
+ {
227
+ try
228
+ {
229
+ JsonUtility.FromJsonOverwrite(json, instance);
230
+ } catch (Exception e)
231
+ {
232
+ LogWarning("LoadJsonFromFile error: " + fileName + "\n" + e);
233
+ }
234
+ }
235
+
236
+ return instance;
237
+ }
238
+
239
+ public static void SaveJsonToFile<T>(T instance, bool immediately)
240
+ {
241
+ string fileName = instance.GetType().Name;
242
+ string json = JsonUtility.ToJson(instance);
243
+ ThreadWrite(fileName, json, immediately);
244
+ }
245
+ }
246
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: eea38fc318cc4eaea93827ed66cdd96f
3
+ timeCreated: 1729828422
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.amanotes.gdk",
3
- "version": "0.2.80",
3
+ "version": "0.2.81-a2",
4
4
  "displayName": "AmaGDK",
5
5
  "description": "Amanotes Game Development Kit",
6
6
  "unity": "2019.4",