com.amanotes.gdk 0.2.60 → 0.2.62

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,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.62 - 2024-05-27]
4
+ - [Dev] Remove pipeline for unity 2020
5
+ - [Dev] Dispatch changes / support callback for when UserId updated
6
+ - Also: Improve log to show UserId in GDK Ready message
7
+ - [Dev] Add GDK Init callback
8
+ - [Feature] Generate Klavar using template
9
+ - [Dev] Add Scriptable object release config
10
+ - [Dev] Add UserContext
11
+ - [Dev] Add Project Auditor
12
+ - [Dev] Various API improvements & update Unity 2021.3.36f1
13
+ - [Dev] Update Klavar generated files
14
+ - [Feature] Download Klavar config from Airtable
15
+ - [Feature] Add checkpoint event
16
+ - [Dev] Get google target sheet every build time
17
+ - [Dev] Update GDK Example project with Unity2021.3.36f1
18
+ - Also: Update AppsFlyer + GMA SDK
19
+ - [Dev] Fix change log post JSON error
20
+
21
+ ## [0.2.61 - 2024-04-25]
22
+ - Add immediate event logging logic
23
+ - [Feature] Improve handling of locked parameters in Klavar
24
+
3
25
  ## [0.2.60 - 2024-04-23]
4
26
  - [Feature] Ensured container's context values remain unchanged
5
27
  - [Feature] Check sent funnel event
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -126,7 +126,7 @@ namespace Amanotes.Core
126
126
  }
127
127
  public partial class AmaGDK // Ads module
128
128
  {
129
- public static partial class Event
129
+ partial class Event
130
130
  {
131
131
  public const string INTER_REQUEST = nameof(INTER_REQUEST);
132
132
  public const string INTER_SHOW_CALLED = nameof(INTER_SHOW_CALLED);
@@ -364,7 +364,7 @@ namespace Amanotes.Core
364
364
  /// <param name="interstitialCappingTime"></param>
365
365
  /// <param name="rewardedVideoCappingTime"></param>
366
366
  /// <returns></returns>
367
- public static bool IsCapped(float interstitialCappingTime = 0, float rewardedVideoCappingTime = 0)
367
+ internal static bool IsCapped(float interstitialCappingTime = 0, float rewardedVideoCappingTime = 0)
368
368
  {
369
369
  bool interCapped = (interstitialCappingTime > 0) && TimeSinceLastInterstitialSucceed < interstitialCappingTime;
370
370
  bool rewardCapped = (rewardedVideoCappingTime > 0) && TimeSinceLastVideoRewardSucceed < rewardedVideoCappingTime;
@@ -38,8 +38,11 @@ namespace Amanotes.Core
38
38
  public HashSet<string> ignoreAdapterIds = null;
39
39
  public HashSet<string> allowAdapterIds = null;
40
40
 
41
+ public int totalCount => detail.totalCount;
42
+ public int totalCountWithinSession => detail.countInSession;
43
+
41
44
  private EventDetail _detail;
42
- public EventDetail EventDetail
45
+ internal EventDetail detail
43
46
  {
44
47
  get
45
48
  {
@@ -47,6 +50,7 @@ namespace Amanotes.Core
47
50
  return _detail = localData.GetEventDetail(eventName, true);
48
51
  }
49
52
  }
53
+ internal bool isLoggedImmediately = false;
50
54
 
51
55
  internal EventParams Init(string eventName)
52
56
  {
@@ -126,22 +130,22 @@ namespace Amanotes.Core
126
130
  return LogEvent(eventName, parameters).SetAsFunnel(signature);
127
131
  }
128
132
 
133
+ [Obsolete("Please use AmaGDK.Analytics.LogEvent(eventName).LogImmediately() instead")]
129
134
  public static void LogEventImmediately(string eventName)
130
135
  {
131
- var eventParams = PrepareEvent(eventName);
132
- LogEventImmediately(eventParams);
136
+ LogEvent(eventName).LogImmediately();
133
137
  }
134
138
 
139
+ [Obsolete("Please use AmaGDK.Analytics.LogEvent(eventName, parameters).LogImmediately() instead")]
135
140
  public static void LogEventImmediately(string eventName, Dictionary<string, object> parameters)
136
141
  {
137
- var eventParams = PrepareEvent(eventName, parameters);
138
- LogEventImmediately(eventParams);
142
+ LogEvent(eventName, parameters).LogImmediately();
139
143
  }
140
144
 
145
+ [Obsolete("Please use AmaGDK.Analytics.LogEvent(eventName, parameters).LogImmediately() instead")]
141
146
  public static void LogEventImmediately(string eventName, params (string, object)[] parameters)
142
147
  {
143
- var eventParams = PrepareEvent(eventName, parameters);
144
- LogEventImmediately(eventParams);
148
+ LogEvent(eventName, parameters).LogImmediately();
145
149
  }
146
150
 
147
151
  public static int GetAccumulatedCount(string eventName, bool withinSession = false)
@@ -392,6 +396,9 @@ namespace Amanotes.Core
392
396
 
393
397
  while (eventQueue.TryDequeue(out var eventData))
394
398
  {
399
+ if (eventData.isLoggedImmediately) continue;
400
+ if (Config.analytics.immediatelyLoggedEvents.Contains(eventData.eventName))
401
+ Utils.ThrowExceptionIfEditor($"This event '{eventData.eventName}' must be sent immediately by AmaGDK.Analytics.LogEvent(eventName).LogImmediately()");
395
402
  var (eventNameToSend, inUseAdapterIDs) = LogEventActually(eventData);
396
403
  sessionStat?.Add(eventNameToSend, eventData, inUseAdapterIDs);
397
404
  epPool.Return(eventData);
@@ -472,7 +479,7 @@ namespace Amanotes.Core
472
479
  if (Config.analytics.migrateAccumulatedCount && !eventData.withinSession)
473
480
  {
474
481
  Log($"[Analytics] Update event count of event <{eventData.eventName}> from {countByGDK} to {oriAccCount}");
475
- eventData.EventDetail.totalCount = oriAccCount;
482
+ eventData.detail.totalCount = oriAccCount;
476
483
  totalCount = oriAccCount;
477
484
  localData._dirty = true;
478
485
  }
@@ -658,7 +665,7 @@ namespace Amanotes.Core
658
665
  public static partial class Analytics
659
666
  {
660
667
  [Serializable]
661
- public class EventDetail
668
+ internal class EventDetail
662
669
  {
663
670
  public string eventName;
664
671
  public int totalCount;
@@ -696,7 +703,7 @@ namespace Amanotes.Core
696
703
  }
697
704
 
698
705
  [Serializable]
699
- public class MigrationLog
706
+ internal class MigrationLog
700
707
  {
701
708
  public string deviceId;
702
709
  public string appVersion;
@@ -866,11 +873,15 @@ namespace Amanotes.Core
866
873
  {
867
874
  //Clear() is supported from .Net Standard 2.1
868
875
  #if UNITY_2021_3_OR_NEWER
869
- sessionStat?.savedEventStatsInLastFrame.Clear();
876
+ savedEventStatsInLastFrame.Clear();
877
+ lastFrameEventStats.Clear();
870
878
  #else
871
879
  while (savedEventStatsInLastFrame.TryDequeue(out var stat))
872
880
  {
873
881
  }
882
+ while (lastFrameEventStats.TryDequeue(out var stat))
883
+ {
884
+ }
874
885
  #endif
875
886
  }
876
887
  }
@@ -879,6 +890,27 @@ namespace Amanotes.Core
879
890
 
880
891
  public static class EventParamsExtension
881
892
  {
893
+ public static void LogImmediately(this IEventParamsBuilder ap)
894
+ {
895
+ if (ap == null) return;
896
+
897
+ var data = ap as EventParams;
898
+ data!.isLoggedImmediately = true;
899
+ LogEventImmediately(data);
900
+ }
901
+
902
+ public static IEventParamsBuilder AttachParam_TimeDiffLastFire(this IEventParamsBuilder ap)
903
+ {
904
+ var data = ap as EventParams;
905
+ return data?.AddParam("time_diff_last_fire", Utils.GetTimeDiffLastFire(data.eventName));
906
+ }
907
+
908
+ public static IEventParamsBuilder AttachParam_InternetConnectionStatus(this IEventParamsBuilder ap)
909
+ {
910
+ var data = ap as EventParams;
911
+ return data?.AddParam("connection", Application.internetReachability == NetworkReachability.ReachableViaLocalAreaNetwork ? "online" : "offline");
912
+ }
913
+
882
914
  public static IEventParamsBuilder SetAsFunnel(this IEventParamsBuilder ap, string signature = null)
883
915
  {
884
916
  if (ap == null) return null;
@@ -1051,13 +1083,12 @@ namespace Amanotes.Core
1051
1083
  return false;
1052
1084
  }
1053
1085
 
1054
- if (value.GetType() == typeof(string))
1086
+ if (value is string stringValue)
1055
1087
  {
1056
- var v = value.ToString();
1057
1088
  var strLimit = 100;
1058
- if (v.Length > strLimit)
1089
+ if (stringValue.Length > strLimit)
1059
1090
  {
1060
- value = v.Substring(0, strLimit);
1091
+ value = stringValue.Substring(0, strLimit);
1061
1092
  LogWarning($"Param value exceeds the length limit ({strLimit}). Event name: {eventName}, key: {key}!");
1062
1093
  }
1063
1094
  }
@@ -1077,6 +1108,7 @@ namespace Amanotes.Core.Internal
1077
1108
  [HideInInspector] public bool checkMigrationIntegrity = true;
1078
1109
  [HideInInspector] public bool migrateAccumulatedCount = true;
1079
1110
  [HideInInspector] public bool enableEventStat;
1111
+ [HideInInspector] public List<string> immediatelyLoggedEvents = new List<string>(){"ad_impression", "ad_impression_ama"};
1080
1112
  public AnalyticQualityConfig quality;
1081
1113
  }
1082
1114
 
@@ -0,0 +1,63 @@
1
+ using static Amanotes.Core.Internal.Logging;
2
+
3
+ namespace Amanotes.Core
4
+ {
5
+ public partial class AmaGDK
6
+ {
7
+ public static Context Context = new Context();
8
+ }
9
+
10
+ public partial class Context
11
+ {
12
+ public string Location
13
+ {
14
+ get => _location;
15
+ set => SetStringField(ref _location, value, nameof(Location));
16
+ }
17
+
18
+ public string SongACMid
19
+ {
20
+ get => _songACMid;
21
+ set => SetStringField(ref _songACMid, value, nameof(SongACMid));
22
+ }
23
+
24
+ public string SongName
25
+ {
26
+ get => _songName;
27
+ set => SetStringField(ref _songName, value, nameof(SongName));
28
+ }
29
+
30
+ public string SongUnlockType
31
+ {
32
+ get => _songUnlockType;
33
+ set => SetStringField(ref _songUnlockType, value, nameof(SongUnlockType));
34
+ }
35
+
36
+ public string Reward
37
+ {
38
+ get => _reward;
39
+ set => SetStringField(ref _reward, value, nameof(Reward));
40
+ }
41
+ }
42
+
43
+ public partial class Context // PRIVATE
44
+ {
45
+ private string _location;
46
+ private string _songACMid;
47
+ private string _songName;
48
+ private string _songUnlockType;
49
+ private string _reward;
50
+
51
+ private void SetStringField(ref string field, string value, string name)
52
+ {
53
+ if (string.IsNullOrWhiteSpace(value))
54
+ {
55
+ LogWarning($"[Context] {name} is empty");
56
+ }
57
+ else
58
+ {
59
+ field = value;
60
+ }
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: d8fc30da5e864878af47d27c0caaf2a4
3
+ timeCreated: 1715746839
@@ -3,14 +3,28 @@ using System.Text.RegularExpressions;
3
3
  using Amanotes.Core.Internal;
4
4
  using System.Collections.Generic;
5
5
  using UnityEngine;
6
- using UnityEngine.Serialization;
7
6
 
8
7
  namespace Amanotes.Core
9
8
  {
10
9
  public partial class AmaGDK //User profile
11
10
  {
11
+ partial class Event
12
+ {
13
+ public const string GDK_USER_ID_UPDATED = nameof(GDK_USER_ID_UPDATED);
14
+ }
15
+
12
16
  public static readonly UserProfile User = new UserProfile();
13
-
17
+
18
+ /// <summary>
19
+ /// We use multiple IDs to do the user mapping between AppsFlyer, IronSource & Firebase
20
+ /// The callback triggers when a better UserID (gaid, idfv, pseudoId,...) was found and replace the current UserId
21
+ /// </summary>
22
+ /// <param name="onUserIdChange"></param>
23
+ public static void SetCallback_OnUserIdUpdated(Action<string> onUserIdChange)
24
+ {
25
+ dispatcher.AddListener(Event.GDK_USER_ID_UPDATED, onUserIdChange, false);
26
+ }
27
+
14
28
  [Serializable]
15
29
  public partial class UserProfile : IJsonFile
16
30
  {
@@ -56,6 +70,8 @@ namespace Amanotes.Core
56
70
  {
57
71
  if (_userId == value) return;
58
72
  _userId = value;
73
+ Logging.Log($"GDK UserID Updated: {_userId}");
74
+ dispatcher.Dispatch(Event.GDK_USER_ID_UPDATED, _userId);
59
75
  Save();
60
76
  }
61
77
  }
package/Runtime/AmaGDK.cs CHANGED
@@ -17,24 +17,23 @@ namespace Amanotes.Core
17
17
  {
18
18
  public partial class AmaGDK : MonoBehaviour
19
19
  {
20
- public const string VERSION = "0.2.60";
20
+ public const string VERSION = "0.2.62";
21
21
 
22
22
  internal static AmaGDK _instance;
23
- internal static Status _status = Status.None;
24
23
  internal static bool _allowInit = false;
25
- public bool autoInit = true;
24
+ internal static Status _status = Status.None;
25
+ private static ConfigAsset _config = null;
26
26
  internal static readonly GDKGeoLocation GeoLocation = new GDKGeoLocation();
27
27
  internal static readonly GDKServerTime ServerTime = new GDKServerTime();
28
- private static ConfigAsset _config = null;
29
28
 
30
- public static partial class Event
29
+
30
+ public bool autoInit = true;
31
+ internal static partial class Event
31
32
  {
32
33
  public const string GDK_HOOK_START = nameof(GDK_HOOK_START);
33
34
  public const string GDK_HOOK_END = nameof(GDK_HOOK_END);
34
35
 
35
- public const string GDK_INIT_START = nameof(GDK_INIT_START);
36
- public const string GDK_INIT_END = nameof(GDK_INIT_END);
37
-
36
+ public const string GDK_INIT = nameof(GDK_INIT);
38
37
  public const string GDK_READY = nameof(GDK_READY);
39
38
  public const string ADAPTER_READY = nameof(ADAPTER_READY);
40
39
  }
@@ -141,7 +140,6 @@ namespace Amanotes.Core
141
140
 
142
141
  IEnumerator InitAdapters(StringBuilder sb)
143
142
  {
144
- _status = Status.Initialize;
145
143
  float TIMEOUT = 1;
146
144
 
147
145
  // sort on priority
@@ -182,12 +180,19 @@ namespace Amanotes.Core
182
180
  }
183
181
 
184
182
  float startTime = Time.realtimeSinceStartup;
185
-
186
- dispatcher.Dispatch(Event.GDK_INIT_START);
187
183
  var sb = new StringBuilder();
184
+
188
185
  InitModules();
189
186
 
187
+ float moduleTime = Time.realtimeSinceStartup - startTime;
188
+ sb.Append($"- InitModules() took (~ {moduleTime:#0.00}s)\n");
189
+
190
190
  yield return InitHook(); //Google CMP takes long at first time (~ 1.7s)
191
+ float hookTime = Time.realtimeSinceStartup - moduleTime - startTime;
192
+ sb.Append($"- InitHook() took (~ {hookTime:#0.00}s)\n\n");
193
+
194
+ _status = Status.Initialize;
195
+ dispatcher.Dispatch(Event.GDK_INIT);
191
196
  yield return WaitForManualInit();
192
197
  yield return InitAdapters(sb);
193
198
 
@@ -199,10 +204,10 @@ namespace Amanotes.Core
199
204
  ServerTime.UpdateTime();
200
205
  }
201
206
 
202
- dispatcher.Dispatch(Event.GDK_INIT_END);
203
-
204
207
  _status = Status.Ready;
205
208
  dispatcher.Dispatch(Event.GDK_READY);
209
+
210
+ sb.Append($"\nMapping UserId: {User.UserId}\n");
206
211
  Debug.Log($"AmaGDK v{VERSION} | {READY} in {Time.realtimeSinceStartup - startTime:#0.00}s\n\n{sb}");
207
212
  }
208
213
 
@@ -230,7 +235,14 @@ namespace Amanotes.Core
230
235
  public partial class AmaGDK // PUBLIC APIS
231
236
  {
232
237
  public static bool isReady => _status == Status.Ready;
233
-
238
+ public static bool isInitted => _status >= Status.Initialize;
239
+
240
+ public static LogLevel logLevel
241
+ {
242
+ get => Config.common.logLevel;
243
+ set => Config.common.logLevel = value;
244
+ }
245
+
234
246
  public static void Init(Action onReady = null)
235
247
  {
236
248
  if (isReady)
@@ -257,8 +269,28 @@ namespace Amanotes.Core
257
269
  var sdk = new GameObject("AmaGDK");
258
270
  _instance = sdk.AddComponent<AmaGDK>();
259
271
  }
260
-
261
- public static void SetOnReadyCallback(Action onReady)
272
+
273
+ /// <summary>
274
+ /// Will be triggered right after all the cached data being loaded
275
+ /// And before any Adapter has been called to Init
276
+ /// </summary>
277
+ /// <param name="onInit"></param>
278
+ public static void SetCallback_OnGDKInit(Action onInit)
279
+ {
280
+ if (isInitted)
281
+ {
282
+ onInit.Invoke();
283
+ return;
284
+ }
285
+
286
+ dispatcher.AddListener(Event.GDK_INIT, onInit, true);
287
+ }
288
+
289
+ /// <summary>
290
+ /// Will be trigger after all adapters have been ready
291
+ /// </summary>
292
+ /// <param name="onReady"></param>
293
+ public static void SetCallback_OnGDKReady(Action onReady)
262
294
  {
263
295
  if (isReady)
264
296
  {
@@ -22,7 +22,7 @@ namespace Amanotes.Core.Internal
22
22
 
23
23
  public static class Messsage
24
24
  {
25
- public const string READY = "Init success";
25
+ public const string READY = "Ready";
26
26
  public const string SDK_INIT_CALLED = "init has been called!";
27
27
  public const string MULTIPLE_INSTANCE = "multiple instance found!";
28
28
  public const string CONFIG_NOT_FOUND = "AmaGDKConfig not found in Resources!";
@@ -132,7 +132,7 @@ namespace Amanotes.Core.Internal
132
132
  }
133
133
 
134
134
  AmaGDK[] result = Resources.FindObjectsOfTypeAll<AmaGDK>();
135
- for (var i = 0; i < result.Length; i++)
135
+ for (int i = 0; i < result.Length; i++)
136
136
  {
137
137
  if (result[i] == null) continue;
138
138
 
@@ -175,6 +175,12 @@ namespace Amanotes.Core.Internal
175
175
  analyticsData.cachedEventDetails.Clear();
176
176
  analyticsData.migrationLog = new MigrationLog();
177
177
  analyticsData.SaveIfDirty();
178
+ sessionStat.Clear();
179
+ #if UNITY_2021_3_OR_NEWER
180
+ eventQueue.Clear();
181
+ #else
182
+ while (eventQueue.TryDequeue(out var stat)){}
183
+ #endif
178
184
  }
179
185
  }
180
186
 
@@ -183,8 +189,33 @@ namespace Amanotes.Core.Internal
183
189
  public interface IOnApplicationFocus { void OnApplicationFocus(bool hasFocus); }
184
190
  public interface IOnFrameUpdate { void OnFrameUpdate(); }
185
191
 
186
- public static class Utils
192
+ public static class Utils // Hidden Utils
187
193
  {
194
+ private static readonly Dictionary<string, float> LastFireInSessions = new Dictionary<string, float>();
195
+
196
+ public static int GetTimeDiffLastFire(string eventName)
197
+ {
198
+ if (!LastFireInSessions.TryGetValue(eventName, out float lastFire))
199
+ {
200
+ LastFireInSessions[eventName] = Time.realtimeSinceStartup;
201
+ return 0;
202
+ }
203
+
204
+ float current = Time.realtimeSinceStartup;
205
+ int diff = Mathf.RoundToInt(current - lastFire);
206
+ LastFireInSessions[eventName] = current;
207
+ return diff;
208
+ }
209
+
210
+ public static void ThrowExceptionIfEditor(string message)
211
+ {
212
+ #if UNITY_EDITOR
213
+ throw new GDKException(message);
214
+ #else
215
+ Logging.LogError(message);
216
+ #endif
217
+ }
218
+
188
219
  /// <summary>
189
220
  /// Registers the list of server time providers.
190
221
  /// </summary>
@@ -6,22 +6,10 @@ namespace Amanotes.Core
6
6
  {
7
7
  public class TimeDiffLastFireAttribute : ConverterLogEventAttribute
8
8
  {
9
- static readonly Dictionary<string, float> lastFireInSessions = new Dictionary<string, float>();
10
-
11
9
  public override object Convert(object parent, FieldInfo fieldInfo, object param)
12
10
  {
13
- // return AmaGDK.Ads.InterstitialShowCalledInSession;
14
11
  string name = parent.GetType().FullName;
15
- if (!lastFireInSessions.TryGetValue(name, out var lastFire))
16
- {
17
- lastFireInSessions[name] = Time.realtimeSinceStartup;
18
- return 0;
19
- }
20
-
21
- var current = Time.realtimeSinceStartup;
22
- var diff = Mathf.RoundToInt(current - lastFire);
23
- lastFireInSessions[name] = current;
24
- return diff;
12
+ return Internal.Utils.GetTimeDiffLastFire(name);
25
13
  }
26
14
  }
27
15
 
@@ -33,7 +33,15 @@ namespace Amanotes.Core
33
33
  @params[kvp.Key] = kvp.Value;
34
34
  }
35
35
 
36
- AmaGDK.Analytics.LogEvent(eventName, @params);
36
+ if (AmaGDK.Config.analytics.immediatelyLoggedEvents.Contains(eventName))
37
+ {
38
+ AmaGDK.Analytics.LogEvent(eventName, @params).LogImmediately();
39
+ }
40
+ else
41
+ {
42
+ AmaGDK.Analytics.LogEvent(eventName, @params);
43
+ }
44
+
37
45
  }
38
46
 
39
47
 
@@ -107,5 +115,17 @@ namespace Amanotes.Core
107
115
  additionalParameters[kvp.Key] = kvp.Value;
108
116
  }
109
117
  }
118
+
119
+ /// <summary>
120
+ /// AttachContainerParameters method attaches container parameters to the event.
121
+ /// </summary>
122
+ /// <param name="parameters">The container parameters to attach.</param>
123
+ public void AttachContainerParameters(params (string, object)[] parameters)
124
+ {
125
+ foreach ((string key, object value) in parameters)
126
+ {
127
+ additionalParameters[key] = value;
128
+ }
129
+ }
110
130
  }
111
131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.amanotes.gdk",
3
- "version": "0.2.60",
3
+ "version": "0.2.62",
4
4
  "displayName": "AmaGDK",
5
5
  "description": "Amanotes Game Development Kit",
6
6
  "unity": "2019.4",