com.amanotes.gdk 0.2.68 → 0.2.70-1

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 (55) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/Editor/AmaGDKEditor.cs +78 -23
  3. package/Editor/AmaGDKExtra.cs +14 -13
  4. package/Editor/Extra/GDKAutoUpdateAdapter.cs +14 -1
  5. package/Editor/Extra/GDKCIBuildCommand.cs +2 -16
  6. package/Editor/Extra/GDKSymbolDefine.cs +372 -0
  7. package/Editor/{AmaGDKManager.cs.meta → Extra/GDKSymbolDefine.cs.meta} +1 -1
  8. package/Editor/Utils/AmaGDKEditor.ExtractVersion.cs +41 -3
  9. package/Extra/AmaGDKInstaller.unitypackage +0 -0
  10. package/Extra/CheckDiskSpace.unitypackage +0 -0
  11. package/Extra/Consent.unitypackage +0 -0
  12. package/Extra/{GoogleCMP.unitypackage.meta → Consent.unitypackage.meta} +1 -1
  13. package/Extra/ForceUpdate.unitypackage +0 -0
  14. package/Extra/LegacyGDKUpdateHelper.unitypackage +0 -0
  15. package/Extra/PostProcessor.unitypackage +0 -0
  16. package/Packages/AmaGDKConfig.unitypackage +0 -0
  17. package/Packages/AmaGDKExample.unitypackage +0 -0
  18. package/Packages/AmaGDKTest.unitypackage +0 -0
  19. package/Packages/AppsFlyerAdapter.PurchaseConnector.unitypackage +0 -0
  20. package/Packages/AppsFlyerAdapter.unitypackage +0 -0
  21. package/Packages/FirebaseAnalyticsAdapter.unitypackage +0 -0
  22. package/Packages/FirebaseRemoteConfigAdapter.unitypackage +0 -0
  23. package/Packages/IronSourceAdapter.unitypackage +0 -0
  24. package/Packages/MaxAdNetworkAdapter.unitypackage +0 -0
  25. package/{Editor/Resources.meta → Packages/MaxAdNetworkAdapter.unitypackage.meta} +1 -2
  26. package/Packages/RevenueCatAdapter.unitypackage +0 -0
  27. package/Packages/SqliteAnalyticsAdapter.unitypackage +0 -0
  28. package/Runtime/Ad/AdExtension.cs +2 -0
  29. package/Runtime/Ad/AdLogic.cs +83 -70
  30. package/Runtime/Ad/AdShowContext.cs +1 -0
  31. package/Runtime/Ad/AmaGDK.Ads.cs +17 -3
  32. package/Runtime/AmaGDK.Adapters.cs +12 -5
  33. package/Runtime/AmaGDK.Analytics.cs +45 -38
  34. package/Runtime/AmaGDK.Consent.cs +259 -0
  35. package/Runtime/AmaGDK.Consent.cs.meta +11 -0
  36. package/Runtime/AmaGDK.Device.cs +4 -4
  37. package/Runtime/AmaGDK.RemoteConfig.cs +3 -1
  38. package/Runtime/AmaGDK.cs +28 -19
  39. package/Runtime/AnalyticQualityAsset.cs +16 -9
  40. package/Runtime/AudioToolkit/AmaGDK.Audio.Latency.cs +1 -3
  41. package/Runtime/AudioToolkit/AmaGDK.Audio.cs +41 -25
  42. package/Runtime/AudioToolkit/Plugins/AudioDeviceDetector.cs +13 -12
  43. package/Runtime/Core/GDKPool.cs +83 -17
  44. package/Runtime/Fps/AmaFPS.cs +0 -1
  45. package/Runtime/Fps/AmaFPSVisualizer.cs +7 -7
  46. package/Runtime/Internal/AmaGDK.Internal.AmaGUI.cs +1 -1
  47. package/Runtime/Internal/AmaGDK.Internal.cs +2 -5
  48. package/Runtime/Internal/AmaGDK.Utils.cs +32 -81
  49. package/Runtime/Internal/GDKGeoLocationcs.cs +3 -1
  50. package/Runtime/Internal/GDKServerTime.cs +13 -10
  51. package/package.json +1 -1
  52. package/Editor/AmaGDKManager.cs +0 -235
  53. package/Editor/Resources/amasdk-modules.json +0 -31
  54. package/Editor/Resources/amasdk-modules.json.meta +0 -7
  55. package/Extra/GoogleCMP.unitypackage +0 -0
@@ -7,6 +7,7 @@ namespace Amanotes.Core.Internal
7
7
  {
8
8
  public abstract class AdLogic
9
9
  {
10
+ private const int MAX_FRAMES_WAIT_FOR_CALLBACK = 60;
10
11
  protected AdModuleConfig adConfig => Config.ad;
11
12
  protected AdShowContext context => Ads.context;
12
13
 
@@ -130,9 +131,11 @@ namespace Amanotes.Core.Internal
130
131
  private Coroutine _showAdRoutine;
131
132
  protected bool _isRequesting;
132
133
  protected bool _isInterstitial => adType == AdType.Interstitial;
134
+ private bool isFocus;
133
135
 
134
136
  public void StartShowAd()
135
137
  {
138
+ isFocus = false;
136
139
  unityCallbacks.OnApplicationFocus -= OnApplicationFocus;
137
140
  unityCallbacks.OnApplicationFocus += OnApplicationFocus;
138
141
 
@@ -157,16 +160,12 @@ namespace Amanotes.Core.Internal
157
160
  _showAdRoutine = _instance.StartCoroutine(ShowAdRoutine());
158
161
  }
159
162
 
160
- void OnApplicationFocus(bool isFocus)
163
+ private void OnApplicationFocus(bool focus)
161
164
  {
162
- if (!isFocus) return;
165
+ if (!focus) return;
163
166
 
164
167
  unityCallbacks.OnApplicationFocus -= OnApplicationFocus;
165
- if (context == null) return;
166
-
167
- Log($"OnApplicationFocus Before: {context.hasClosed}");
168
- context.adCallback |= AdCallback.Close;
169
- Log($"OnApplicationFocus After: {context.hasClosed}");
168
+ isFocus = true;
170
169
  }
171
170
 
172
171
  public void StopShowAd()
@@ -209,55 +208,26 @@ namespace Amanotes.Core.Internal
209
208
 
210
209
  if (!HasLocalNetworkConnection)
211
210
  {
212
- _showState = ShowAdsState.ShowFail;
213
- OnAdShowReadyStatus(false, AdShowReadyStatus.Wifi3GDisabled);
214
- ShowAdRoutineCompleted(false);
211
+ EndShowAdRoutineWithFailure(AdShowReadyStatus.Wifi3GDisabled);
215
212
  yield break;
216
213
  }
217
214
 
218
215
  int timeout = Config.ad.adShowTimeoutInSecs;
219
216
  if (timeout == 0) // do not wait for Ad
220
217
  {
221
- _showState = ShowAdsState.ShowFail;
222
- OnAdShowReadyStatus(false, AdShowReadyStatus.TimeOut);
223
- ShowAdRoutineCompleted(false);
218
+ EndShowAdRoutineWithFailure(AdShowReadyStatus.TimeOut);
224
219
  yield break;
225
220
  }
226
221
 
227
222
  _showState = ShowAdsState.WaitForAd;
228
223
  OnAdShowReadyStatus(false, AdShowReadyStatus.WaitForAd);
229
224
 
230
- if (_loadState != LoadAdsState.Requesting) StartLoadAd();
231
- var counter = 0;
232
- bool? hasInternet = null;
233
- if (adConfig.checkInternet) _instance.StartCoroutine(WebUtils.CheckInternet(result => hasInternet = result));
234
-
235
- var wait1Sec = new WaitForSecondsRealtime(1f);
236
- while (counter < timeout || timeout == -1)
237
- {
238
- yield return wait1Sec;
239
- counter++;
240
-
241
- // finish checking internet & result is false
242
- if (adConfig.checkInternet && hasInternet == false)
243
- {
244
- Log("[Ad] Stop WaitForAd : No internet!");
245
- _showState = ShowAdsState.ShowFail;
246
- OnAdShowReadyStatus(false, AdShowReadyStatus.NoInternet);
247
- ShowAdRoutineCompleted(false);
248
- yield break;
249
- }
250
-
251
- if (_loadState != LoadAdsState.Ready) continue;
252
- Log("[Ad] WaitForAd success after " + counter + " | timeout = " + timeout + " secs");
253
- break;
254
- }
255
-
225
+ yield return WaitForAdReadyWithTimeout(timeout);
226
+
256
227
  if (!isAdReady)
257
228
  {
258
- Log("[Ad] WaitForAd failed (timeout) " + counter + " | timeout = " + timeout + " secs");
259
- OnAdShowReadyStatus(false, AdShowReadyStatus.TimeOut);
260
- ShowAdRoutineCompleted(false);
229
+ Log("[Ad] WaitForAd failed (timeout) " + " | timeout = " + timeout + " secs");
230
+ EndShowAdRoutineWithFailure(AdShowReadyStatus.TimeOut);
261
231
  yield break;
262
232
  }
263
233
  }
@@ -266,43 +236,78 @@ namespace Amanotes.Core.Internal
266
236
  OnAdShowReadyStatus(true, AdShowReadyStatus.Ready);
267
237
  TriggerBeforeAdShow();
268
238
  ShowAd();
239
+
240
+ yield return WaitForMediationCallback();
269
241
 
270
- while (!context.potentiallyCompleted)
271
- {
272
- yield return null; // callback as quickly as possible
273
- }
242
+ // wait 1 frame in case hasReward callback after onClose
243
+ if (!context.hasReward && adType == AdType.VideoReward && !context.hasFailedOrCancelled) yield return null;
244
+ ShowAdRoutineCompleted(context.isSuccess);
245
+ }
274
246
 
275
- if (adType == AdType.Interstitial)
276
- {
277
- ShowAdRoutineCompleted(!context.hasFailedOrCancelled);
278
- yield break;
279
- }
247
+ private void EndShowAdRoutineWithFailure(AdShowReadyStatus status)
248
+ {
249
+ _showState = ShowAdsState.ShowFail;
250
+ OnAdShowReadyStatus(false, status);
251
+ ShowAdRoutineCompleted(false);
252
+ }
280
253
 
281
- if (context.hasFailedOrCancelled)
254
+ private IEnumerator WaitForAdReadyWithTimeout(int timeout)
255
+ {
256
+ if (_loadState != LoadAdsState.Requesting) StartLoadAd();
257
+ var counter = 0;
258
+ bool? hasInternet = null;
259
+ if (adConfig.checkInternet) _instance.StartCoroutine(WebUtils.CheckInternet(result => hasInternet = result));
260
+
261
+ var wait1Sec = new WaitForSecondsRealtime(1f);
262
+ while (counter < timeout || timeout == -1)
282
263
  {
283
- Log($"[Ad] {adType} Waiting for [close]");
284
- yield return null;
285
- ShowAdRoutineCompleted(false);
286
- yield break;
264
+ yield return wait1Sec;
265
+ counter++;
266
+ Log($"[Ad] Waiting for ad {counter} secs");
267
+
268
+ // finish checking internet & result is false
269
+ if (adConfig.checkInternet && hasInternet == false)
270
+ {
271
+ Log("[Ad] Stop WaitForAd : No internet!");
272
+ EndShowAdRoutineWithFailure(AdShowReadyStatus.NoInternet);
273
+ yield break;
274
+ }
275
+
276
+ if (_loadState != LoadAdsState.Ready) continue;
277
+ Log("[Ad] WaitForAd success after " + counter + " | timeout = " + timeout + " secs");
278
+ break;
287
279
  }
280
+ }
288
281
 
289
- if (context.hasClosed)
282
+ private IEnumerator WaitForMediationCallback()
283
+ {
284
+ #if !UNITY_EDITOR
285
+ while (!isFocus) yield return null;
286
+ #endif
287
+
288
+ int wait4CallbackCounter = 0;
289
+ while (!context.potentiallyCompleted && wait4CallbackCounter <= MAX_FRAMES_WAIT_FOR_CALLBACK)
290
290
  {
291
- // wait 1 frame in case hasReward callback after onClose
292
- if (!context.hasReward) yield return null;
293
- ShowAdRoutineCompleted(context.hasReward);
294
- yield break;
291
+ wait4CallbackCounter++;
292
+ yield return null;
295
293
  }
294
+ Log($"[Ad] Waiting for potentially completed callback: {wait4CallbackCounter} frames");
295
+
296
+ if (!context.hasClosed) yield return WaitForAdClosedCallback();
297
+ }
296
298
 
297
- var wait4CloseCounter = 0;
298
- while (!context.hasClosed)
299
+ private IEnumerator WaitForAdClosedCallback()
300
+ {
301
+ int counter = 0;
302
+ while (!context.hasClosed && counter <= MAX_FRAMES_WAIT_FOR_CALLBACK)
299
303
  {
300
- wait4CloseCounter++;
301
- Log($"[Ad] {adType} Waiting for [close]: {wait4CloseCounter}");
304
+ counter++;
302
305
  yield return null;
303
306
  }
304
-
305
- ShowAdRoutineCompleted(true);
307
+ Log($"[Ad] Waiting for ad closed callback: {counter} frames");
308
+ if (context.hasClosed) yield break;
309
+ LogWarning("[Ad] Force close ad (mediation missing OnAdClose callback)");
310
+ OnAdClosed();
306
311
  }
307
312
 
308
313
  private void ShowAdRoutineCompleted(bool isSuccess)
@@ -315,9 +320,7 @@ namespace Amanotes.Core.Internal
315
320
 
316
321
  _showState = isSuccess ? ShowAdsState.ShowSuccess : ShowAdsState.ShowFail;
317
322
 
318
- // Extra callback
319
- Ads.context.showFinishAt = Time.realtimeSinceStartup;
320
- Ads.context.onAdResult?.Invoke(isSuccess);
323
+
321
324
 
322
325
  var localData = Ads.localData;
323
326
  var stat = _isInterstitial ? localData.interstitial : localData.reward;
@@ -336,8 +339,13 @@ namespace Amanotes.Core.Internal
336
339
  TriggerOnAfterAdShow();
337
340
 
338
341
  Log($"[Ad] {adType} ShowAdRoutineCompleted: {isSuccess}\n{JsonUtility.ToJson(Ads.context)}");
342
+
343
+ // Extra callback
344
+ context.showFinishAt = Time.realtimeSinceStartup;
345
+ var resultCallback = context.onAdResult;
339
346
  Ads.context = null;
340
347
  _showState = ShowAdsState.None;
348
+ resultCallback?.Invoke(isSuccess);
341
349
  }
342
350
 
343
351
  private void TriggerBeforeAdShow()
@@ -399,6 +407,11 @@ namespace Amanotes.Core.Internal
399
407
 
400
408
  if (Ads.context != null)
401
409
  {
410
+ if ((Ads.context.adCallback & AdCallback.Close) != 0)
411
+ {
412
+ LogWarning("[Ad] OnAdClosed() has been called multiple times for the same ad.");
413
+ return;
414
+ }
402
415
  Ads.context.adCallback |= AdCallback.Close;
403
416
  Ads.context.onClosed?.Invoke();
404
417
  } else
@@ -39,5 +39,6 @@ namespace Amanotes.Core.Internal
39
39
  public bool hasReward => (adCallback & AdCallback.Reward) != 0;
40
40
  public bool hasClosed => (adCallback & AdCallback.Close) != 0;
41
41
  public bool hasClicked => (adCallback & AdCallback.Click) != 0;
42
+ public bool isSuccess => (hasReward || adType == AdType.Interstitial) && !hasFailedOrCancelled;
42
43
  }
43
44
  }
@@ -150,8 +150,12 @@ namespace Amanotes.Core
150
150
  {
151
151
  internal static bool InitModule()
152
152
  {
153
+ if (Adapter2.GetAllAdapter<AdAdapter>().Count > 1)
154
+ {
155
+ Utils.ThrowExceptionIfEditor("Multiple AdAdapters is not supported!");
156
+ }
153
157
  _adapter = Adapter2.GetAdapter<AdAdapter>();
154
- return _adapter == null;
158
+ return _adapter != null;
155
159
  }
156
160
 
157
161
  internal static void StartModule()
@@ -181,24 +185,34 @@ namespace Amanotes.Core
181
185
  return false;
182
186
  }
183
187
 
184
- public static IAdCallback ShowInterstitial(string placementName = null, Dictionary<string, object> userData = null)
188
+ public static IAdCallback ShowInterstitial(Action<bool> onAdResult = null, string placementName = null, Dictionary<string, object> userData = null)
185
189
  {
186
190
  if (_adapter == null) return null;
187
191
  if (!ClearPrevContext()) return null;
188
192
 
189
193
  context = new AdShowContext(AdType.Interstitial, placementName, userData);
194
+ if (onAdResult != null)
195
+ {
196
+ context.onAdResult += onAdResult;
197
+ }
198
+
190
199
  _adapter.interstitial.StartShowAd();
191
200
  return context;
192
201
  }
193
202
 
194
203
  public static bool IsInterstitialShowing => (context != null) && (context.adType == AdType.Interstitial);
195
204
 
196
- public static IAdCallback ShowRewardedVideo(string placementName = null, Dictionary<string, object> userData = null)
205
+ public static IAdCallback ShowRewardedVideo(Action<bool> onAdResult = null, string placementName = null, Dictionary<string, object> userData = null)
197
206
  {
198
207
  if (_adapter == null) return null;
199
208
  if (!ClearPrevContext()) return null;
200
209
 
201
210
  context = new AdShowContext(AdType.VideoReward, placementName, userData);
211
+ if (onAdResult != null)
212
+ {
213
+ context.onAdResult += onAdResult;
214
+ }
215
+
202
216
  _adapter.rewardVideo.StartShowAd();
203
217
  return context;
204
218
  }
@@ -10,7 +10,7 @@ namespace Amanotes.Core.Internal
10
10
 
11
11
  public abstract partial class Adapter2
12
12
  {
13
- internal protected enum ConfigStatus
13
+ protected internal enum ConfigStatus
14
14
  {
15
15
  Ok,
16
16
  Invalid,
@@ -22,12 +22,15 @@ namespace Amanotes.Core.Internal
22
22
  [HideInNormalInspector] [SerializeField] internal bool enabled = true;
23
23
  [NonSerialized] internal string adapterId;
24
24
  [NonSerialized] internal string adapterVersion;
25
- [NonSerialized] internal protected Status status = Status.None;
25
+ [NonSerialized] protected internal Status status = Status.None;
26
26
 
27
27
  protected abstract void Init(Action<bool> onComplete);
28
- internal protected abstract string GetServiceKey();
29
- internal protected abstract ConfigStatus configStatus { get; }
30
-
28
+ protected internal abstract string GetServiceKey();
29
+ protected virtual float GetTimeoutInEditor() => 0.1f;
30
+ protected virtual float GetTimeoutInRuntime() => 0.1f;
31
+ protected internal abstract ConfigStatus configStatus { get; }
32
+ protected internal float Timeout => Application.isEditor ? GetTimeoutInEditor() : GetTimeoutInRuntime();
33
+
31
34
  public bool IsReady => status == Status.Ready;
32
35
 
33
36
  internal void InitSDK() // call by AmaGDK
@@ -63,6 +66,10 @@ namespace Amanotes.Core.Internal
63
66
  LogWarning($"Adapter <{id}> == null in ConfigAsset? (non-serialized?)");
64
67
  return false;
65
68
  }
69
+ if (!adapter.enabled)
70
+ {
71
+ return false;
72
+ }
66
73
 
67
74
  adapter.adapterId = id;
68
75
  adapter.adapterVersion = version;
@@ -68,8 +68,9 @@ namespace Amanotes.Core
68
68
  doNotIncreaseCounter = false;
69
69
  isLoggedImmediately = false;
70
70
  overrideConfig = false;
71
- ignoreAdapterIds = null;
72
- allowAdapterIds = null;
71
+
72
+ GDKPool.ReturnAndNullize(ref ignoreAdapterIds);
73
+ GDKPool.ReturnAndNullize(ref allowAdapterIds);
73
74
  _detail = null;
74
75
  }
75
76
 
@@ -307,9 +308,8 @@ namespace Amanotes.Core
307
308
  internal static readonly List<AnalyticsAdapter> listAdapters = new List<AnalyticsAdapter>();
308
309
  internal static readonly AnalyticsData localData = new AnalyticsData().LoadJsonFromFile().BuildCache();
309
310
  internal static SessionStat sessionStat;
310
- internal static readonly GDKPool<StringBuilder> sbPool = new GDKPool<StringBuilder>(3);
311
- internal static readonly GDKPool<EventParams> epPool = new GDKPool<EventParams>(1);
312
-
311
+ internal static readonly GDKPool.Pool<EventParams> epPool = new GDKPool.Pool<EventParams>(1);
312
+
313
313
  internal static readonly Dictionary<string, string> reservedPrefixes = new Dictionary<string, string>()
314
314
  {
315
315
  { "firebase_", "frb_" },
@@ -515,32 +515,31 @@ namespace Amanotes.Core
515
515
  {
516
516
  Profiler.BeginSample("AmaGDK.Analytics.NormalizeEventName()");
517
517
  {
518
- var sb = sbPool.Get();
519
- sb.Clear();
520
- NormalizeKeyString(ref eventName, sb, dryRun);
518
+ var warningLog = GDKPool.GetStringBuilder(8);
519
+ NormalizeKeyString(ref eventName, warningLog, dryRun);
521
520
  if (reservedEvents.Contains(eventName))
522
- sb.AppendLine($" - EventName <{eventName}> is reserved and should not be used");
523
- if (sb.Length > 0 && dryRun) LogWarningOnce($"NormalizeEventName <{eventName}> warnings:\n{sb}\n", $"NormalizeEventName<{eventName}>");
524
- sbPool.Return(sb);
521
+ {
522
+ warningLog.AppendLine($" - EventName <{eventName}> is reserved and should not be used");
523
+ }
524
+ if (warningLog.Length > 0 && dryRun) LogWarningOnce($"NormalizeEventName <{eventName}> warnings:\n{warningLog}\n", $"NormalizeEventName<{eventName}>");
525
+ GDKPool.Return(warningLog);
525
526
  }
526
527
  Profiler.EndSample();
527
528
  }
528
529
 
529
530
  internal static void NormalizeKeyString(ref string key, StringBuilder warningLog, bool dryRun)
530
531
  {
532
+ if (string.IsNullOrWhiteSpace(key))
533
+ {
534
+ LogWarning($" - Name should not be empty");
535
+ return;
536
+ }
537
+
531
538
  Profiler.BeginSample("AmaGDK.Analytics.NormalizeKeyString()");
532
539
  {
533
- if (string.IsNullOrWhiteSpace(key))
534
- {
535
- LogWarning($" - Name should not be empty");
536
- Profiler.EndSample();
537
- return;
538
- }
539
-
540
- var tempLog = Config.common.logLevel == LogLevel.None ? null : sbPool.Get();
541
- tempLog?.Clear();
542
- var tempKey = sbPool.Get();
543
- tempKey.Clear();
540
+ var willLog = Config.common.logLevel != LogLevel.None;
541
+ var tempLog = willLog ? GDKPool.GetStringBuilder() : null;
542
+ var tempKey = GDKPool.GetStringBuilder();
544
543
 
545
544
  var hasChanged = false;
546
545
  var trimKey = key.Trim();
@@ -563,16 +562,16 @@ namespace Amanotes.Core
563
562
  }
564
563
  }
565
564
 
566
- if (hasChanged)
565
+ if (hasChanged && willLog)
567
566
  {
568
- tempLog?.Append(" - Name may only contain alphanumeric characters and underscores");
567
+ tempLog.Append(" - Name may only contain alphanumeric characters and underscores");
569
568
  }
570
569
 
571
570
  if (tempKey.Length > 0 && !IsAlphabet(tempKey[0]))
572
571
  {
573
572
  hasChanged = true;
574
573
  tempKey.Insert(0, 'E');
575
- tempLog?.Append($" - Name must start with a letter: <{key}>");
574
+ if (willLog) tempLog.Append($" - Name must start with a letter: <{key}>");
576
575
  }
577
576
 
578
577
  foreach (var prefix in reservedPrefixes)
@@ -581,13 +580,13 @@ namespace Amanotes.Core
581
580
  hasChanged = true;
582
581
  tempKey.Remove(0, prefix.Key.Length);
583
582
  tempKey.Insert(0, prefix.Value);
584
- tempLog?.AppendLine($" - <{key}> starts with reserved prefix <{prefix}>");
583
+ if (willLog) tempLog.AppendLine($" - <{key}> starts with reserved prefix <{prefix}>");
585
584
  }
586
585
 
587
586
  if (trimKey.Length > 40)
588
587
  {
589
588
  hasChanged = true;
590
- tempLog?.AppendLine($" - Name is too long ({key.Length} > 40): <{key}> ");
589
+ if (willLog) tempLog.AppendLine($" - Name is too long ({key.Length} > 40): <{key}> ");
591
590
  tempKey.Length = 40;
592
591
  }
593
592
 
@@ -598,8 +597,8 @@ namespace Amanotes.Core
598
597
  if (hasChanged) key = tempKey.ToString();
599
598
  }
600
599
 
601
- if (tempLog != null) sbPool.Return(tempLog);
602
- sbPool.Return(tempKey);
600
+ GDKPool.Return(tempLog);
601
+ GDKPool.Return(tempKey);
603
602
  }
604
603
  Profiler.EndSample();
605
604
  }
@@ -702,9 +701,17 @@ namespace Amanotes.Core
702
701
  adapters = adapterIDs;
703
702
  }
704
703
 
705
- public string ToTsv()
704
+ public void AppendAsTSV(StringBuilder sb)
706
705
  {
707
- return $"{eventNameToSend}\t{string.Join(",", adapters)}\t{parameters}";
706
+ sb.Append(eventNameToSend);
707
+ sb.Append("\t");
708
+ for (var i = 0; i < adapters.Count; i++)
709
+ {
710
+ sb.Append(adapters[i]);
711
+ if (i < adapters.Count - 1) sb.Append(",");
712
+ }
713
+ sb.Append("\t");
714
+ sb.Append(parameters);
708
715
  }
709
716
  }
710
717
 
@@ -856,8 +863,8 @@ namespace Amanotes.Core
856
863
  {
857
864
  private const string TSV_HEADER_ROW = "EventName\tAdapters\tParameters";
858
865
  private readonly string fileName = nameof(SessionStat) + DateTime.Now.ToString("_yyMMdd_HHmmss") + ".tsv";
859
- private StringBuilder contents = new StringBuilder();
860
866
  private bool isFirstSave = true;
867
+ private StringBuilder contents = new StringBuilder(8192);
861
868
  private readonly ConcurrentQueue<EventStat> lastFrameEventStats = new ConcurrentQueue<EventStat>();
862
869
  internal readonly ConcurrentQueue<EventStat> savedEventStatsInLastFrame = new ConcurrentQueue<EventStat>();
863
870
 
@@ -878,10 +885,10 @@ namespace Amanotes.Core
878
885
  }
879
886
  while (lastFrameEventStats.TryDequeue(out var stat))
880
887
  {
881
- contents.AppendLine(stat.ToTsv());
888
+ stat.AppendAsTSV(contents);
882
889
  savedEventStatsInLastFrame.Enqueue(stat);
883
890
  }
884
-
891
+
885
892
  GDKFileUtils.SaveAppend(fileName, contents.ToString());
886
893
  }
887
894
 
@@ -972,7 +979,7 @@ namespace Amanotes.Core
972
979
  }
973
980
 
974
981
  eventData.overrideConfig = true;
975
- eventData.ignoreAdapterIds ??= new HashSet<string>();
982
+ eventData.ignoreAdapterIds ??= GDKPool.GetHashSet();
976
983
 
977
984
  foreach (string mId in analyticIds)
978
985
  {
@@ -998,7 +1005,7 @@ namespace Amanotes.Core
998
1005
  }
999
1006
 
1000
1007
  eventData.overrideConfig = true;
1001
- eventData.allowAdapterIds ??= new HashSet<string>();
1008
+ eventData.allowAdapterIds ??= GDKPool.GetHashSet();
1002
1009
 
1003
1010
  foreach (string mId in analyticIds)
1004
1011
  {
@@ -1080,12 +1087,12 @@ namespace Amanotes.Core
1080
1087
  {
1081
1088
  string eventName = (ap as EventParams)?.eventName;
1082
1089
 
1083
- var sb = sbPool.Get();
1090
+ var sb = GDKPool.GetStringBuilder();
1084
1091
  sb.Clear();
1085
1092
  NormalizeKeyString(ref key, sb, AmaGDK.Config.analytics.normalizeEventName);
1086
1093
 
1087
1094
  if (sb.Length > 0) LogWarningOnce($"Warning for event <{eventName}>, param <{key}>:\n{sb}", $"NormalizeParamKey <{eventName}>");
1088
- sbPool.Return(sb);
1095
+ GDKPool.Return(sb);
1089
1096
 
1090
1097
  if (string.IsNullOrWhiteSpace(key))
1091
1098
  {