com.amanotes.gdk 0.2.56 → 0.2.58
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 +19 -0
- package/Editor/AmaGDKEditor.cs +12 -22
- package/Editor/Extra/GDKLocalBuild.cs +7 -6
- package/Extra/AmaGDKInstaller.unitypackage +0 -0
- package/Extra/CheckDiskSpace.unitypackage +0 -0
- package/Extra/ForceUpdate.unitypackage +0 -0
- package/Extra/PostProcessor.unitypackage +0 -0
- package/Extra/SDKVersionTracking.unitypackage +0 -0
- package/Packages/AmaGDKConfig.unitypackage +0 -0
- package/Packages/AmaGDKExample.unitypackage +0 -0
- package/Packages/AmaGDKTest.unitypackage +0 -0
- package/Packages/AppsFlyerAdapter.unitypackage +0 -0
- package/Packages/FirebaseAnalyticsAdapter.unitypackage +0 -0
- package/Packages/FirebaseRemoteConfigAdapter.unitypackage +0 -0
- package/Packages/IronSourceAdapter.unitypackage +0 -0
- package/Packages/RevenueCatAdapter.unitypackage +0 -0
- package/Packages/SqliteAnalyticsAdapter.unitypackage +0 -0
- package/Runtime/AmaGDK.Adapters.cs +1 -1
- package/Runtime/AmaGDK.Ads.cs +69 -45
- package/Runtime/AmaGDK.Analytics.cs +8 -10
- package/Runtime/AmaGDK.RemoteConfig.cs +5 -16
- package/Runtime/AmaGDK.UserProfile.cs +15 -2
- package/Runtime/AmaGDK.cs +36 -3
- package/Runtime/Internal/AmaGDK.Internal.cs +29 -0
- package/Runtime/Internal/AmaGDK.Utils.cs +42 -0
- package/Runtime/Internal/AmaGDK.WebUtils.cs +2 -1
- package/Runtime/Internal/GDKGeoLocationcs.cs +119 -0
- package/Runtime/Internal/GDKGeoLocationcs.cs.meta +3 -0
- package/Runtime/Internal/GDKServerTime.cs +211 -0
- package/Runtime/Internal/GDKServerTime.cs.meta +3 -0
- package/Runtime/Klavar/Attributes/ToStringAttribute.cs +15 -7
- package/Runtime/Klavar/KlavarContainer.cs +4 -3
- package/Runtime/Klavar/KlavarEvent.cs +1 -1
- package/Runtime/Klavar/KlavarEventUtils.cs +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.58 - 2024-04-09]
|
|
4
|
+
- [DEV] combine code coverage report
|
|
5
|
+
- [Dev] Improve GDK Utils
|
|
6
|
+
- [Dev] Add hints to AdCallback APIs
|
|
7
|
+
- [Fix] timeDiffLastFire always returns 0 in adShowCalled callbacks
|
|
8
|
+
- [Fix] Various warnings fixes due to invalid examples
|
|
9
|
+
- [Fix] Warning on AdContext destroyed caused by invalid editor ad callback
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## [0.2.57 - 2024-04-08]
|
|
13
|
+
- [Dev] Fix bug in value conversion within KlavarEvent
|
|
14
|
+
- [Dev] Auto get change log
|
|
15
|
+
- [Dev] Auto publish command
|
|
16
|
+
- [Dev] Upgrade to Unity 2021.3.35f1
|
|
17
|
+
- [Dev] Add support for various banner sizes in IronsourceAdapter
|
|
18
|
+
- [Dev] Clean up and add tooltip for options
|
|
19
|
+
- [Fix] Null exception in LocalBuild Version
|
|
20
|
+
|
|
21
|
+
|
|
3
22
|
## [0.2.56 - 2024-04-03]
|
|
4
23
|
- Release AmaGDK v0.2.56
|
|
5
24
|
|
package/Editor/AmaGDKEditor.cs
CHANGED
|
@@ -369,20 +369,7 @@ namespace Amanotes.Editor
|
|
|
369
369
|
{
|
|
370
370
|
configOpen.Add(adapterId, isOpen = false);
|
|
371
371
|
}
|
|
372
|
-
|
|
373
|
-
// if (adapter == null)
|
|
374
|
-
// {
|
|
375
|
-
// // EditorGUILayout.HelpBox("Adapter<" + adapterId + "> not imported!", MessageType.Info);
|
|
376
|
-
// string adapterName = FindBestAdapterFor(adapterId);
|
|
377
|
-
// var package = $"{Res.PACKAGE_PATH}{adapterName}";
|
|
378
|
-
//
|
|
379
|
-
// if (GUILayout.Button(adapterName))
|
|
380
|
-
// {
|
|
381
|
-
// AssetDatabase.ImportPackage(package, false);
|
|
382
|
-
// }
|
|
383
|
-
// return;
|
|
384
|
-
// }
|
|
385
|
-
|
|
372
|
+
|
|
386
373
|
SDKStatus status = SDKStatus.Get(adapterId);
|
|
387
374
|
bool changed = AmaGUI.Foldout(ObjectNames.NicifyVariableName(adapterId), ref isOpen, () =>
|
|
388
375
|
{
|
|
@@ -461,18 +448,21 @@ namespace Amanotes.Editor
|
|
|
461
448
|
EditorGUILayout.HelpBox($"{adapterId} SDK not installed", MessageType.Warning);
|
|
462
449
|
return;
|
|
463
450
|
}
|
|
451
|
+
|
|
464
452
|
if (!status.adapterInstalled)
|
|
465
453
|
{
|
|
466
454
|
EditorGUILayout.HelpBox($"{adapterId}-GDKAdapter not installed", MessageType.Warning);
|
|
455
|
+
if (adapterId == IRONSOURCE)
|
|
456
|
+
{
|
|
457
|
+
AmaGUI.Foldout("Enabled AdNetworks", ref showIRSAdapter, () =>
|
|
458
|
+
{
|
|
459
|
+
DrawIRSAdapterVersions();
|
|
460
|
+
GUILayout.Space(8f);
|
|
461
|
+
});
|
|
462
|
+
}
|
|
467
463
|
return;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
if (!status.adapterInstalled)
|
|
471
|
-
{
|
|
472
|
-
EditorGUILayout.HelpBox($"{adapterId}-GDKAdapter not installed", MessageType.Warning);
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
|
|
464
|
+
}
|
|
465
|
+
|
|
476
466
|
DrawModuleConfig(adapter, adapterId);
|
|
477
467
|
}
|
|
478
468
|
|
|
@@ -11,7 +11,7 @@ using UnityObject = UnityEngine.Object;
|
|
|
11
11
|
|
|
12
12
|
namespace Amanotes.Editor
|
|
13
13
|
{
|
|
14
|
-
[CreateAssetMenu(menuName = "Ama GDK/Build Config", fileName = "LocalBuild")]
|
|
14
|
+
[CreateAssetMenu(menuName = "Ama GDK/Local Build Config", fileName = "LocalBuild")]
|
|
15
15
|
public class GDKLocalBuild : ScriptableObject
|
|
16
16
|
{
|
|
17
17
|
private const string DEFAULT_BUILD_FOLDER = "../../_Build";
|
|
@@ -37,7 +37,6 @@ namespace Amanotes.Editor
|
|
|
37
37
|
public List<UnityObject> listUnused;
|
|
38
38
|
public BuildRequestTS buildRQ;
|
|
39
39
|
public UnityEvent beforeBuild;
|
|
40
|
-
public TextAsset shTemplate;
|
|
41
40
|
|
|
42
41
|
[NonSerialized] private BuildInfo _buildInfo;
|
|
43
42
|
[NonSerialized] private int _lockCount;
|
|
@@ -493,12 +492,14 @@ namespace Amanotes.Editor
|
|
|
493
492
|
buildNumber++;
|
|
494
493
|
if (string.IsNullOrEmpty(buildVersion)) buildVersion = "1.0.0";
|
|
495
494
|
|
|
496
|
-
string
|
|
497
|
-
|
|
495
|
+
List<string> list = buildVersion.Split('.').ToList();
|
|
496
|
+
if (list.Count < 2) list.Add("0");
|
|
497
|
+
if (list.Count < 3) list.Add("0");
|
|
498
|
+
int last = int.Parse(list[2]);
|
|
498
499
|
last++;
|
|
499
500
|
|
|
500
|
-
|
|
501
|
-
buildVersion = string.Join(".",
|
|
501
|
+
list[2] = last.ToString();
|
|
502
|
+
buildVersion = string.Join(".", list);
|
|
502
503
|
}
|
|
503
504
|
}
|
|
504
505
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -13,7 +13,7 @@ namespace Amanotes.Core.Internal
|
|
|
13
13
|
[HideInInspector] [SerializeField] internal bool enabled = true;
|
|
14
14
|
[NonSerialized] internal string adapterId;
|
|
15
15
|
[NonSerialized] internal string adapterVersion;
|
|
16
|
-
[NonSerialized] protected
|
|
16
|
+
[NonSerialized] internal protected Status status = Status.None;
|
|
17
17
|
|
|
18
18
|
protected abstract void Init(Action<bool> onComplete);
|
|
19
19
|
|
package/Runtime/AmaGDK.Ads.cs
CHANGED
|
@@ -9,10 +9,11 @@ using static Amanotes.Core.AmaGDK;
|
|
|
9
9
|
|
|
10
10
|
namespace Amanotes.Core
|
|
11
11
|
{
|
|
12
|
-
|
|
13
|
-
|
|
14
12
|
public static class AdExtension
|
|
15
13
|
{
|
|
14
|
+
/// <summary>
|
|
15
|
+
/// Invoke when ad view has opened
|
|
16
|
+
/// </summary>
|
|
16
17
|
public static IAdCallback OnAdOpened(this IAdCallback ad, Action callback)
|
|
17
18
|
{
|
|
18
19
|
if (!MakeSureAdNotNull(ad)) return null;
|
|
@@ -21,6 +22,9 @@ namespace Amanotes.Core
|
|
|
21
22
|
return ad;
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
/// <summary>
|
|
26
|
+
/// The user finished watching the video, and should be rewarded.
|
|
27
|
+
/// </summary>
|
|
24
28
|
public static IAdCallback OnRewardReceived(this IAdCallback ad, Action callback)
|
|
25
29
|
{
|
|
26
30
|
if (!MakeSureAdNotNull(ad)) return null;
|
|
@@ -29,6 +33,9 @@ namespace Amanotes.Core
|
|
|
29
33
|
return ad;
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
/// <summary>
|
|
37
|
+
/// Invoked when the video ad was clicked.
|
|
38
|
+
/// </summary>
|
|
32
39
|
public static IAdCallback OnAdClicked(this IAdCallback ad, Action callback)
|
|
33
40
|
{
|
|
34
41
|
if (!MakeSureAdNotNull(ad)) return null;
|
|
@@ -37,6 +44,9 @@ namespace Amanotes.Core
|
|
|
37
44
|
return ad;
|
|
38
45
|
}
|
|
39
46
|
|
|
47
|
+
/// <summary>
|
|
48
|
+
/// Callback when the rewarded video ad was failed to show
|
|
49
|
+
/// </summary>
|
|
40
50
|
public static IAdCallback OnAdShowFailed(this IAdCallback ad, Action callback)
|
|
41
51
|
{
|
|
42
52
|
if (!MakeSureAdNotNull(ad)) return null;
|
|
@@ -45,6 +55,9 @@ namespace Amanotes.Core
|
|
|
45
55
|
return ad;
|
|
46
56
|
}
|
|
47
57
|
|
|
58
|
+
/// <summary>
|
|
59
|
+
/// Invoked before OnAdOpened but is not reliable (not supported by all adNetworks)
|
|
60
|
+
/// </summary>
|
|
48
61
|
public static IAdCallback OnAdShowSuccess(this IAdCallback ad, Action callback)
|
|
49
62
|
{
|
|
50
63
|
if (!MakeSureAdNotNull(ad)) return null;
|
|
@@ -53,6 +66,9 @@ namespace Amanotes.Core
|
|
|
53
66
|
return ad;
|
|
54
67
|
}
|
|
55
68
|
|
|
69
|
+
/// <summary>
|
|
70
|
+
/// Invoke when ad view is about to be closed
|
|
71
|
+
/// </summary>
|
|
56
72
|
public static IAdCallback OnAdClosed(this IAdCallback ad, Action callback)
|
|
57
73
|
{
|
|
58
74
|
if (!MakeSureAdNotNull(ad)) return null;
|
|
@@ -61,6 +77,11 @@ namespace Amanotes.Core
|
|
|
61
77
|
return ad;
|
|
62
78
|
}
|
|
63
79
|
|
|
80
|
+
/// <summary>
|
|
81
|
+
/// This callback may invokes several times according to the AdShowReadyStatus
|
|
82
|
+
/// - When there is no ad cached (wifi disabled / no internet / wait4Ad / timeout)
|
|
83
|
+
/// - When the ad is cached & ready to show
|
|
84
|
+
/// </summary>
|
|
64
85
|
public static IAdCallback OnAdShowReadyStatus(this IAdCallback ad, Action<AdShowReadyStatus> callback)
|
|
65
86
|
{
|
|
66
87
|
if (!MakeSureAdNotNull(ad)) return null;
|
|
@@ -69,6 +90,10 @@ namespace Amanotes.Core
|
|
|
69
90
|
return ad;
|
|
70
91
|
}
|
|
71
92
|
|
|
93
|
+
/// <summary>
|
|
94
|
+
/// The finalize callback that will always trigger to mark an end for the AdShowRoutine
|
|
95
|
+
///
|
|
96
|
+
/// </summary>
|
|
72
97
|
public static IAdCallback OnAdResult(this IAdCallback ad, Action<bool> callback)
|
|
73
98
|
{
|
|
74
99
|
if (!MakeSureAdNotNull(ad)) return null;
|
|
@@ -79,7 +104,7 @@ namespace Amanotes.Core
|
|
|
79
104
|
|
|
80
105
|
/// <summary>
|
|
81
106
|
/// Call right before the ad actually open (that means Ad has already been cached & available to show).
|
|
82
|
-
/// This callback will not be triggered in case Ad is not available or no internet
|
|
107
|
+
/// This callback will not be triggered in case Ad is not available or no internet (in those case use OnAdShowReadyStatus callback)
|
|
83
108
|
/// </summary>
|
|
84
109
|
public static IAdCallback OnBeforeAdOpen(this IAdCallback ad, Action callback)
|
|
85
110
|
{
|
|
@@ -126,7 +151,8 @@ namespace Amanotes.Core
|
|
|
126
151
|
internal class AdStat
|
|
127
152
|
{
|
|
128
153
|
// per session
|
|
129
|
-
internal float
|
|
154
|
+
internal float prevCallTime;
|
|
155
|
+
internal float currentCallTime;
|
|
130
156
|
internal int callCount;
|
|
131
157
|
|
|
132
158
|
internal float lastSuccessTime;
|
|
@@ -156,7 +182,18 @@ namespace Amanotes.Core
|
|
|
156
182
|
|
|
157
183
|
internal int TimeSinceLastShowCalled(AdType adType)
|
|
158
184
|
{
|
|
159
|
-
|
|
185
|
+
AdStat stat = GetStat(adType);
|
|
186
|
+
AdShowContext context = Ads.context;
|
|
187
|
+
|
|
188
|
+
// Right inside XXX_ShowCalled() callbacks
|
|
189
|
+
if (context != null && context.adType == adType)
|
|
190
|
+
{
|
|
191
|
+
if (stat.callCount == 1) return 0; // first call
|
|
192
|
+
return (int)(stat.currentCallTime - stat.prevCallTime);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (stat.callCount == 0) return 0; // not yet called
|
|
196
|
+
return (int)(Time.realtimeSinceStartup - stat.prevCallTime);
|
|
160
197
|
}
|
|
161
198
|
|
|
162
199
|
internal int TimeSinceLastShowSuccess(AdType adType)
|
|
@@ -173,20 +210,7 @@ namespace Amanotes.Core
|
|
|
173
210
|
[Serializable]
|
|
174
211
|
public class AdAdapterConfig
|
|
175
212
|
{
|
|
176
|
-
// public bool manualInit;
|
|
177
|
-
public int adLoadTimeoutInSecs = 60;
|
|
178
|
-
public int adShowTimeoutInSecs = 5;
|
|
179
|
-
public BannerPosition bannerPosition = BannerPosition.BOTTOM;
|
|
180
|
-
public bool autoLogEvent = true;
|
|
181
|
-
|
|
182
|
-
public bool enableInterstitial = true;
|
|
183
|
-
public bool enableRewarded = true;
|
|
184
|
-
public bool enableBanner = true;
|
|
185
|
-
public bool autoMute = true;
|
|
186
|
-
public bool autoTimeScale = true;
|
|
187
213
|
|
|
188
|
-
[Header("EDITOR")]
|
|
189
|
-
[Range(0f, 1f)] public float rewardAdShowSuccessRate = 1;
|
|
190
214
|
}
|
|
191
215
|
|
|
192
216
|
public partial class Ads
|
|
@@ -255,7 +279,7 @@ namespace Amanotes.Core
|
|
|
255
279
|
if (_isBannerShowing)
|
|
256
280
|
{
|
|
257
281
|
_isBannerShowing = false;
|
|
258
|
-
ShowBanner(
|
|
282
|
+
ShowBanner();
|
|
259
283
|
}
|
|
260
284
|
}
|
|
261
285
|
|
|
@@ -298,22 +322,22 @@ namespace Amanotes.Core
|
|
|
298
322
|
|
|
299
323
|
|
|
300
324
|
private static bool _isBannerShowing;
|
|
301
|
-
private static BannerPosition _showingBannerPosition;
|
|
302
325
|
|
|
303
|
-
public static void ShowBanner(
|
|
326
|
+
public static void ShowBanner()
|
|
304
327
|
{
|
|
328
|
+
if (_config.ad.enableBanner) return;
|
|
329
|
+
|
|
305
330
|
if (_isBannerShowing) return;
|
|
306
331
|
_isBannerShowing = true;
|
|
307
|
-
_showingBannerPosition = bannerPosition ?? Config.ad.bannerPosition;
|
|
308
|
-
|
|
309
332
|
if (_adapter == null) return;
|
|
310
333
|
|
|
311
334
|
dispatcher.Dispatch(Event.BANNER_SHOW);
|
|
312
|
-
_adapter.ShowBanner(
|
|
335
|
+
_adapter.ShowBanner();
|
|
313
336
|
}
|
|
314
337
|
|
|
315
338
|
public static void HideBanner()
|
|
316
339
|
{
|
|
340
|
+
if (_config.ad.enableBanner) return;
|
|
317
341
|
if (!_isBannerShowing) return;
|
|
318
342
|
_isBannerShowing = false;
|
|
319
343
|
if (_adapter == null) return;
|
|
@@ -369,22 +393,27 @@ namespace Amanotes.Core.Internal
|
|
|
369
393
|
{
|
|
370
394
|
[Serializable] public class AdModuleConfig
|
|
371
395
|
{
|
|
372
|
-
|
|
373
|
-
// public bool manualInit;
|
|
396
|
+
[Tooltip("Automatically retry requesting ad if an ad failed to response in this amount of time (in secs)")]
|
|
374
397
|
public int adLoadTimeoutInSecs = 60;
|
|
398
|
+
|
|
399
|
+
[Tooltip("When an ad is not ready to show, keep waiting for this amount of time (in secs)\n\nIf the ad is ready during that time, it would be shown normally.")]
|
|
375
400
|
public int adShowTimeoutInSecs = 5;
|
|
376
|
-
|
|
377
|
-
|
|
401
|
+
|
|
402
|
+
[Tooltip("When an ad is not ready to show, verify that there is an internet connection by ping the internet check URL .\nIf there is no internet connection, we will not need to wait for adShowTimeout secs")]
|
|
378
403
|
public bool checkInternet = true;
|
|
379
|
-
|
|
380
|
-
public bool enableInterstitial = true;
|
|
381
|
-
public bool enableRewarded = true;
|
|
382
|
-
public bool enableBanner = true;
|
|
383
|
-
|
|
404
|
+
|
|
405
|
+
[HideInInspector] public bool enableInterstitial = true;
|
|
406
|
+
[HideInInspector] public bool enableRewarded = true;
|
|
407
|
+
[HideInInspector] public bool enableBanner = true;
|
|
408
|
+
|
|
409
|
+
[Tooltip("Before showing an ad: set AudioListener.volume to 0\nAfter the ad is closed: restore the previously saved AudioListener.volume")]
|
|
384
410
|
public bool autoMute = true;
|
|
411
|
+
|
|
412
|
+
[Tooltip("Before showing an ad: set Time.timeScale to 0\nAfter the ad is closed: restore the previously saved Time.timeScale")]
|
|
385
413
|
public bool autoTimeScale = true;
|
|
386
414
|
|
|
387
415
|
[Header("EDITOR")]
|
|
416
|
+
[Tooltip("Only applicable in Editor\n0 = 0% success\n1 = 100% success")]
|
|
388
417
|
[Range(0f, 1f)] public float rewardAdShowSuccessRate = 1;
|
|
389
418
|
}
|
|
390
419
|
|
|
@@ -399,14 +428,14 @@ namespace Amanotes.Core.Internal
|
|
|
399
428
|
protected AdModuleConfig moduleConfig => Config.ad;
|
|
400
429
|
|
|
401
430
|
// APIs
|
|
402
|
-
protected
|
|
403
|
-
protected
|
|
431
|
+
internal protected abstract AdLogic interstitial { get; }
|
|
432
|
+
internal protected abstract AdLogic rewardVideo { get; }
|
|
404
433
|
|
|
405
|
-
public abstract void ShowBanner(
|
|
434
|
+
public abstract void ShowBanner();
|
|
406
435
|
public abstract void HideBanner();
|
|
407
436
|
|
|
408
437
|
// Internally used
|
|
409
|
-
protected
|
|
438
|
+
internal protected abstract void SetUserId(string userId);
|
|
410
439
|
}
|
|
411
440
|
|
|
412
441
|
public interface IAdCallback { }
|
|
@@ -443,13 +472,6 @@ namespace Amanotes.Core.Internal
|
|
|
443
472
|
Fail = 256
|
|
444
473
|
}
|
|
445
474
|
|
|
446
|
-
public enum BannerPosition
|
|
447
|
-
{
|
|
448
|
-
TOP = 1,
|
|
449
|
-
BOTTOM = 2,
|
|
450
|
-
RECTANGLE = 3
|
|
451
|
-
}
|
|
452
|
-
|
|
453
475
|
public enum AdType
|
|
454
476
|
{
|
|
455
477
|
Interstitial,
|
|
@@ -643,7 +665,9 @@ namespace Amanotes.Core.Internal
|
|
|
643
665
|
|
|
644
666
|
AdStat stat = Ads.localData.GetStat(adType);
|
|
645
667
|
stat.callCount++;
|
|
646
|
-
stat.
|
|
668
|
+
stat.prevCallTime = stat.currentCallTime;
|
|
669
|
+
stat.currentCallTime = Time.realtimeSinceStartup;
|
|
670
|
+
|
|
647
671
|
dispatcher.Dispatch(
|
|
648
672
|
_isInterstitial ? AmaGDK.Event.INTER_SHOW_CALLED : AmaGDK.Event.REWARD_VIDEO_SHOW_CALLED
|
|
649
673
|
);
|
|
@@ -2,7 +2,6 @@ using System;
|
|
|
2
2
|
using System.Collections.Concurrent;
|
|
3
3
|
using System.Collections.Generic;
|
|
4
4
|
using System.Text;
|
|
5
|
-
using System.Text.RegularExpressions;
|
|
6
5
|
using Amanotes.Core.Internal;
|
|
7
6
|
using UnityEngine;
|
|
8
7
|
using UnityEngine.Profiling;
|
|
@@ -1050,10 +1049,10 @@ namespace Amanotes.Core.Internal
|
|
|
1050
1049
|
{
|
|
1051
1050
|
// [Header("----- MODULE CONFIG -----")]
|
|
1052
1051
|
public bool showAnalyticsLog;
|
|
1053
|
-
public bool normalizeEventName = true;
|
|
1054
|
-
public bool checkMigrationIntegrity = true;
|
|
1055
|
-
public bool migrateAccumulatedCount = true;
|
|
1056
|
-
public bool enableEventStat;
|
|
1052
|
+
[HideInInspector] public bool normalizeEventName = true;
|
|
1053
|
+
[HideInInspector] public bool checkMigrationIntegrity = true;
|
|
1054
|
+
[HideInInspector] public bool migrateAccumulatedCount = true;
|
|
1055
|
+
[HideInInspector] public bool enableEventStat;
|
|
1057
1056
|
public AnalyticQualityConfig quality;
|
|
1058
1057
|
}
|
|
1059
1058
|
|
|
@@ -1079,11 +1078,10 @@ namespace Amanotes.Core.Internal
|
|
|
1079
1078
|
|
|
1080
1079
|
public abstract class AnalyticsAdapter : Adapter2
|
|
1081
1080
|
{
|
|
1082
|
-
public
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1081
|
+
[HideInInspector] public List<string> NeverSend = new List<string>();
|
|
1082
|
+
[HideInInspector] public List<string> AlwaysSend = new List<string>();
|
|
1083
|
+
protected abstract bool SendEventByDefault { get; }
|
|
1084
|
+
|
|
1087
1085
|
public bool IsForbidden(string eventName)
|
|
1088
1086
|
{
|
|
1089
1087
|
return SendEventByDefault
|
|
@@ -361,20 +361,6 @@ namespace Amanotes.Core
|
|
|
361
361
|
}
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
|
-
|
|
365
|
-
// public static class RemoteConfigExtension
|
|
366
|
-
// {
|
|
367
|
-
// public static void InitRemoteConfigModule(this RemoteConfigAdapter ap, Action<bool> onCompleteCallback)
|
|
368
|
-
// {
|
|
369
|
-
// if (AmaGDK.RemoteConfig.fetchState != AmaGDK.RemoteConfig.FetchState.None)
|
|
370
|
-
// {
|
|
371
|
-
// LogWarning($"[RemoteConfig] Current state {AmaGDK.RemoteConfig.fetchState.ToString()}\nCannot start to initialize");
|
|
372
|
-
// return;
|
|
373
|
-
// }
|
|
374
|
-
//
|
|
375
|
-
// AmaGDK.RemoteConfig.NextState();
|
|
376
|
-
// }
|
|
377
|
-
// }
|
|
378
364
|
}
|
|
379
365
|
|
|
380
366
|
namespace Amanotes.Core.Internal
|
|
@@ -382,10 +368,13 @@ namespace Amanotes.Core.Internal
|
|
|
382
368
|
[Serializable]
|
|
383
369
|
public class RemoteConfigConfig
|
|
384
370
|
{
|
|
385
|
-
|
|
386
|
-
|
|
371
|
+
[Tooltip("Fetch timeout for first session (in secs)")]
|
|
387
372
|
public float firstFetchTimeOutInSeconds = 10;
|
|
373
|
+
|
|
374
|
+
[Tooltip("Fetch timeout for 2nd+ session (in secs)")]
|
|
388
375
|
public float defaultTimeOutInSeconds = 3;
|
|
376
|
+
|
|
377
|
+
[Tooltip("To create embed Remote config:\n\n- Right click in Project panel\n- Create > AmaGDK > Embed Remote Config\n- Drag & drop the asset here")]
|
|
389
378
|
[SerializeField] internal EmbedRemoteConfigAsset embedRemoteConfig;
|
|
390
379
|
}
|
|
391
380
|
|
|
@@ -120,7 +120,17 @@ namespace Amanotes.Core
|
|
|
120
120
|
{
|
|
121
121
|
get => _session;
|
|
122
122
|
}
|
|
123
|
-
|
|
123
|
+
|
|
124
|
+
public string Country
|
|
125
|
+
{
|
|
126
|
+
get => _country;
|
|
127
|
+
set
|
|
128
|
+
{
|
|
129
|
+
_country = ValidateId(value, COUNTRY_CODE_PATTERN);
|
|
130
|
+
SaveIfDirty();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
124
134
|
public void UpdateUserId()
|
|
125
135
|
{
|
|
126
136
|
#if UNITY_ANDROID
|
|
@@ -199,12 +209,15 @@ namespace Amanotes.Core
|
|
|
199
209
|
private string _appsFlyerId;
|
|
200
210
|
[SerializeField]
|
|
201
211
|
private string _installationId;
|
|
202
|
-
[SerializeField]
|
|
212
|
+
[SerializeField]
|
|
203
213
|
private int _session;
|
|
214
|
+
[SerializeField]
|
|
215
|
+
private string _country;
|
|
204
216
|
|
|
205
217
|
private const string ALPHANUMERIC_HYPHEN_PATTERN = @"^(?=.*\d)(?=.*[a-zA-Z])(?=.*-).+$";
|
|
206
218
|
private const string ALPHANUMERIC_PATTERN = @"^(?=.*\d)(?=.*[a-zA-Z])[a-zA-Z\d]+$";
|
|
207
219
|
private const string NUMERIC_HYPHEN_PATTERN = @"^(?=.*\d)(?=.*-)[\d-]+$";
|
|
220
|
+
private const string COUNTRY_CODE_PATTERN = @"^[A-Z]{2}$";
|
|
208
221
|
|
|
209
222
|
private const string AMA_DEVICE_ID_KEY = "ama_device_id";
|
|
210
223
|
|
package/Runtime/AmaGDK.cs
CHANGED
|
@@ -17,13 +17,14 @@ namespace Amanotes.Core
|
|
|
17
17
|
{
|
|
18
18
|
public partial class AmaGDK : MonoBehaviour
|
|
19
19
|
{
|
|
20
|
-
public const string VERSION = "0.2.
|
|
20
|
+
public const string VERSION = "0.2.58";
|
|
21
21
|
|
|
22
22
|
internal static AmaGDK _instance;
|
|
23
23
|
internal static Status _status = Status.None;
|
|
24
24
|
internal static bool _allowInit = false;
|
|
25
25
|
public bool autoInit = true;
|
|
26
|
-
|
|
26
|
+
internal static readonly GDKGeoLocation GeoLocation = new GDKGeoLocation();
|
|
27
|
+
internal static readonly GDKServerTime ServerTime = new GDKServerTime();
|
|
27
28
|
private static ConfigAsset _config = null;
|
|
28
29
|
|
|
29
30
|
public static partial class Event
|
|
@@ -173,7 +174,6 @@ namespace Amanotes.Core
|
|
|
173
174
|
}
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
|
|
177
177
|
IEnumerator InitRoutine()
|
|
178
178
|
{
|
|
179
179
|
if (Config == null)
|
|
@@ -192,12 +192,39 @@ namespace Amanotes.Core
|
|
|
192
192
|
yield return InitAdapters(sb);
|
|
193
193
|
|
|
194
194
|
StartModules();
|
|
195
|
+
|
|
196
|
+
ConfigGeoLocation();
|
|
197
|
+
if (Config.enableServerTime)
|
|
198
|
+
{
|
|
199
|
+
ServerTime.UpdateTime();
|
|
200
|
+
}
|
|
201
|
+
|
|
195
202
|
dispatcher.Dispatch(Event.GDK_INIT_END);
|
|
196
203
|
|
|
197
204
|
_status = Status.Ready;
|
|
198
205
|
dispatcher.Dispatch(Event.GDK_READY);
|
|
199
206
|
Debug.Log($"AmaGDK v{VERSION} | {READY} in {Time.realtimeSinceStartup - startTime:#0.00}s\n\n{sb}");
|
|
200
207
|
}
|
|
208
|
+
|
|
209
|
+
private void ConfigGeoLocation()
|
|
210
|
+
{
|
|
211
|
+
if (Config.enableGeoLocation)
|
|
212
|
+
{
|
|
213
|
+
var countryCode = User.Country;
|
|
214
|
+
if (string.IsNullOrWhiteSpace(countryCode))
|
|
215
|
+
{
|
|
216
|
+
GeoLocation.GetCountryCode(remoteCountryCode =>
|
|
217
|
+
{
|
|
218
|
+
User.Country = remoteCountryCode;
|
|
219
|
+
GeoLocation.SetCountryCode(remoteCountryCode);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
else
|
|
223
|
+
{
|
|
224
|
+
GeoLocation.SetCountryCode(countryCode);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
201
228
|
}
|
|
202
229
|
|
|
203
230
|
public partial class AmaGDK // PUBLIC APIS
|
|
@@ -461,4 +488,10 @@ namespace Amanotes.Core
|
|
|
461
488
|
}
|
|
462
489
|
}
|
|
463
490
|
}
|
|
491
|
+
|
|
492
|
+
public class GDKException : Exception
|
|
493
|
+
{
|
|
494
|
+
public GDKException() { }
|
|
495
|
+
public GDKException(string message) : base(message) { }
|
|
496
|
+
}
|
|
464
497
|
}
|
|
@@ -182,4 +182,33 @@ namespace Amanotes.Core.Internal
|
|
|
182
182
|
public interface IOnApplicationQuit { void OnApplicationQuit(); }
|
|
183
183
|
public interface IOnApplicationFocus { void OnApplicationFocus(bool hasFocus); }
|
|
184
184
|
public interface IOnFrameUpdate { void OnFrameUpdate(); }
|
|
185
|
+
|
|
186
|
+
public static class Utils
|
|
187
|
+
{
|
|
188
|
+
/// <summary>
|
|
189
|
+
/// Registers the list of server time providers.
|
|
190
|
+
/// </summary>
|
|
191
|
+
/// <param name="providers">List of server time providers implementing IServerTimeProvider interface or extending BaseServerTimeProvider.</param>
|
|
192
|
+
/// <remarks>
|
|
193
|
+
/// It is mandatory to call RegisterServerTimeProvider before initializing AmaGDK by calling AmaGDK.Init().
|
|
194
|
+
/// </remarks>
|
|
195
|
+
public static void RegisterServerTimeProvider(List<IServerTimeProvider> providers)
|
|
196
|
+
{
|
|
197
|
+
GDKUtils.ThrowIf(AmaGDK._status != Status.None, "Please call RegisterServerTimeProvider() before initializing AmaGDK by calling AmaGDK.Init()");
|
|
198
|
+
ServerTime.SetTimeProviders(providers);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// <summary>
|
|
202
|
+
/// Set the GeoLocation Provider.
|
|
203
|
+
/// </summary>
|
|
204
|
+
/// <param name="provider">provider implementing IGeoLocationProvider interface.</param>
|
|
205
|
+
/// <remarks>
|
|
206
|
+
/// It is mandatory to call SetGeoLocationProvider before initializing AmaGDK by calling AmaGDK.Init().
|
|
207
|
+
/// </remarks>
|
|
208
|
+
public static void SetGeoLocationProvider(IGeoLocationProvider provider)
|
|
209
|
+
{
|
|
210
|
+
GDKUtils.ThrowIf(AmaGDK._status != Status.None, "Please call SetGeoLocationProvider() before initializing AmaGDK by calling AmaGDK.Init()");
|
|
211
|
+
GeoLocation.SetGeoLocationProvider(provider);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
185
214
|
}
|
|
@@ -77,6 +77,42 @@ namespace Amanotes.Core.Internal
|
|
|
77
77
|
// Check if each segment exists in the output string in the correct order
|
|
78
78
|
return AreSegmentsPresentInOrder(segments, formattedString);
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
/// <summary>
|
|
82
|
+
/// Retrieves the current server time adjusted to the user's timezone.
|
|
83
|
+
/// </summary>
|
|
84
|
+
/// <returns>The current server time adjusted to the user's timezone if available; otherwise, null.</returns>
|
|
85
|
+
public static DateTime? GetServerTime()
|
|
86
|
+
{
|
|
87
|
+
return AmaGDK.ServerTime.GetTime();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// <summary>
|
|
91
|
+
/// Asynchronously retrieves the current server time with a callback function, adjusted to the user's timezone.
|
|
92
|
+
/// </summary>
|
|
93
|
+
/// <param name="result">Callback function invoked with the server time adjusted to the user's timezone if available; otherwise, null is passed in case of network or server errors.</param>
|
|
94
|
+
public static void GetServerTime(Action<DateTime?> result)
|
|
95
|
+
{
|
|
96
|
+
AmaGDK.ServerTime.GetTime(result);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// <summary>
|
|
100
|
+
/// Gets the country code.
|
|
101
|
+
/// </summary>
|
|
102
|
+
/// <returns>The country code if available; otherwise, null.</returns>
|
|
103
|
+
public static string GetCountryCode()
|
|
104
|
+
{
|
|
105
|
+
return AmaGDK.GeoLocation.GetCountryCode();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// <summary>
|
|
109
|
+
/// Gets the country code asynchronously with a callback function.
|
|
110
|
+
/// </summary>
|
|
111
|
+
/// <param name="result">Callback function to be invoked with the country code if available; otherwise, null will be passed in case of network or server errors.</param>
|
|
112
|
+
public static void GetCountryCode(Action<string> result)
|
|
113
|
+
{
|
|
114
|
+
AmaGDK.GeoLocation.GetCountryCode(result);
|
|
115
|
+
}
|
|
80
116
|
|
|
81
117
|
static bool AreSegmentsPresentInOrder(string[] segments, string outputString)
|
|
82
118
|
{
|
|
@@ -117,6 +153,12 @@ namespace Amanotes.Core.Internal
|
|
|
117
153
|
segments[matches.Count] = template.Substring(lastIndex);
|
|
118
154
|
return segments;
|
|
119
155
|
}
|
|
156
|
+
|
|
157
|
+
internal static void ThrowIf(bool condition, string message)
|
|
158
|
+
{
|
|
159
|
+
if (condition)
|
|
160
|
+
throw new GDKException(message);
|
|
161
|
+
}
|
|
120
162
|
}
|
|
121
163
|
|
|
122
164
|
public static class GDKReflection
|
|
@@ -151,6 +151,7 @@ namespace Amanotes.Core.Internal
|
|
|
151
151
|
|
|
152
152
|
public partial class SDKConfig
|
|
153
153
|
{
|
|
154
|
-
|
|
154
|
+
[Tooltip("The URL we would use to ping to verify if the app is actually connected to the internet")]
|
|
155
|
+
public string internetCheckUrl = "http://clients3.google.com/generate_204";
|
|
155
156
|
}
|
|
156
157
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections;
|
|
3
|
+
using UnityEngine;
|
|
4
|
+
using UnityEngine.Networking;
|
|
5
|
+
|
|
6
|
+
namespace Amanotes.Core.Internal
|
|
7
|
+
{
|
|
8
|
+
public partial class ConfigAsset
|
|
9
|
+
{
|
|
10
|
+
[SerializeField] public bool enableGeoLocation = true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
internal class GDKGeoLocation
|
|
14
|
+
{
|
|
15
|
+
private IGeoLocationProvider provider = new CountryIS_GeoLocationProvider();
|
|
16
|
+
private string localCountryCode;
|
|
17
|
+
|
|
18
|
+
public void SetGeoLocationProvider(IGeoLocationProvider provider)
|
|
19
|
+
{
|
|
20
|
+
this.provider = provider;
|
|
21
|
+
}
|
|
22
|
+
public string GetCountryCode()
|
|
23
|
+
{
|
|
24
|
+
GDKUtils.ThrowIf(!AmaGDK.Config.enableGeoLocation, "GeoLocation is disabled, Please enable it in the AmaGDKConfig");
|
|
25
|
+
return localCountryCode;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public void GetCountryCode(Action<string> result)
|
|
29
|
+
{
|
|
30
|
+
GDKUtils.ThrowIf(!AmaGDK.Config.enableGeoLocation, "GeoLocation is disabled, Please enable it in the AmaGDKConfig");
|
|
31
|
+
if (!string.IsNullOrWhiteSpace(localCountryCode))
|
|
32
|
+
{
|
|
33
|
+
result?.Invoke(localCountryCode);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
provider.FetchGeoLocation(data =>
|
|
37
|
+
{
|
|
38
|
+
result?.Invoke(data.countryCode?.ToUpper());
|
|
39
|
+
if (data.errorMessage != null)
|
|
40
|
+
Logging.Log(data.errorMessage);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
internal void SetCountryCode(string countryCode)
|
|
45
|
+
{
|
|
46
|
+
localCountryCode = countryCode;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
namespace Amanotes.Core
|
|
52
|
+
{
|
|
53
|
+
public class GeoLocationData
|
|
54
|
+
{
|
|
55
|
+
public string countryCode;
|
|
56
|
+
public string errorMessage;
|
|
57
|
+
|
|
58
|
+
public GeoLocationData(string countryCode, string errorMessage = null)
|
|
59
|
+
{
|
|
60
|
+
this.countryCode = countryCode;
|
|
61
|
+
this.errorMessage = errorMessage;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public static GeoLocationData WithErrorMessage(string message)
|
|
65
|
+
{
|
|
66
|
+
return new GeoLocationData(null, message);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public interface IGeoLocationProvider
|
|
71
|
+
{
|
|
72
|
+
public void FetchGeoLocation(Action<GeoLocationData> result);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public class CountryIS_GeoLocationProvider: IGeoLocationProvider
|
|
76
|
+
{
|
|
77
|
+
public const string URL = "https://api.country.is/";
|
|
78
|
+
[Serializable]
|
|
79
|
+
private class LocationData
|
|
80
|
+
{
|
|
81
|
+
public string country;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
void IGeoLocationProvider.FetchGeoLocation(Action<GeoLocationData> result)
|
|
85
|
+
{
|
|
86
|
+
AmaGDK._instance.StartCoroutine(FetchGeoLocationRoutine(result));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private IEnumerator FetchGeoLocationRoutine(Action<GeoLocationData> result)
|
|
90
|
+
{
|
|
91
|
+
using (var request = UnityWebRequest.Get(URL))
|
|
92
|
+
{
|
|
93
|
+
yield return request.SendWebRequest();
|
|
94
|
+
|
|
95
|
+
if (request.result != UnityWebRequest.Result.Success)
|
|
96
|
+
{
|
|
97
|
+
result?.Invoke(GeoLocationData.WithErrorMessage($"Error fetching location: {request.error}"));
|
|
98
|
+
yield break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try
|
|
102
|
+
{
|
|
103
|
+
var data = ParseResponse(request.downloadHandler.text);
|
|
104
|
+
result?.Invoke(data);
|
|
105
|
+
}
|
|
106
|
+
catch (Exception e)
|
|
107
|
+
{
|
|
108
|
+
result?.Invoke(GeoLocationData.WithErrorMessage($"Error parsing location data: {e.Message}"));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private GeoLocationData ParseResponse(string response)
|
|
114
|
+
{
|
|
115
|
+
var locationData = JsonUtility.FromJson<LocationData>(response);
|
|
116
|
+
return new GeoLocationData(locationData.country);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Collections;
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using UnityEngine;
|
|
5
|
+
using UnityEngine.Networking;
|
|
6
|
+
|
|
7
|
+
namespace Amanotes.Core
|
|
8
|
+
{
|
|
9
|
+
public class ServerTimeData
|
|
10
|
+
{
|
|
11
|
+
public DateTime? time;
|
|
12
|
+
public string errorMessage;
|
|
13
|
+
|
|
14
|
+
public ServerTimeData(DateTime time)
|
|
15
|
+
{
|
|
16
|
+
this.time = time;
|
|
17
|
+
errorMessage = null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public ServerTimeData(string errorMessage)
|
|
21
|
+
{
|
|
22
|
+
time = null;
|
|
23
|
+
this.errorMessage = errorMessage;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public interface IServerTimeProvider
|
|
28
|
+
{
|
|
29
|
+
public void FetchServerTime(Action<ServerTimeData> result);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public abstract class BaseServerTimeProvider : IServerTimeProvider
|
|
33
|
+
{
|
|
34
|
+
public abstract string GetURL();
|
|
35
|
+
public abstract ServerTimeData ParseResponse(string response);
|
|
36
|
+
|
|
37
|
+
public void FetchServerTime(Action<ServerTimeData> result)
|
|
38
|
+
{
|
|
39
|
+
AmaGDK._instance.StartCoroutine(FetchTimeRoutine(result));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private IEnumerator FetchTimeRoutine(Action<ServerTimeData> result)
|
|
43
|
+
{
|
|
44
|
+
using (var request = UnityWebRequest.Get(GetURL()))
|
|
45
|
+
{
|
|
46
|
+
yield return request.SendWebRequest();
|
|
47
|
+
|
|
48
|
+
if (request.result != UnityWebRequest.Result.Success)
|
|
49
|
+
{
|
|
50
|
+
result?.Invoke(new ServerTimeData($"Error fetching time: {request.error}"));
|
|
51
|
+
yield break;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
result?.Invoke(ParseResponse(request.downloadHandler.text));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
internal class WorldTimeServer_TimeProvider : BaseServerTimeProvider
|
|
60
|
+
{
|
|
61
|
+
private const string URL = "https://www.worldtimeserver.com/current_time_in_LA.aspx";
|
|
62
|
+
|
|
63
|
+
public override ServerTimeData ParseResponse(string response)
|
|
64
|
+
{
|
|
65
|
+
string startStr = "id=\"serverTimeStamp\" value=\"";
|
|
66
|
+
int startIndex = response.IndexOf(startStr) + startStr.Length;
|
|
67
|
+
string unixTimeStr = string.Empty;
|
|
68
|
+
char currentChar = char.MinValue;
|
|
69
|
+
for (int i = startIndex, n = response.Length; i < n; i++)
|
|
70
|
+
{
|
|
71
|
+
currentChar = response[i];
|
|
72
|
+
if (currentChar == '.' || currentChar == '\"') break;
|
|
73
|
+
unixTimeStr += currentChar;
|
|
74
|
+
}
|
|
75
|
+
if (long.TryParse(unixTimeStr, out long unixTime))
|
|
76
|
+
{
|
|
77
|
+
DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(unixTime);
|
|
78
|
+
return new ServerTimeData(dateTimeOffset.DateTime);
|
|
79
|
+
}
|
|
80
|
+
else
|
|
81
|
+
{
|
|
82
|
+
return new ServerTimeData("Error parsing WorldTimeServer data");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
public override string GetURL()
|
|
86
|
+
{
|
|
87
|
+
return URL;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
internal class WorldTimeAPI_TimeProvider : BaseServerTimeProvider
|
|
92
|
+
{
|
|
93
|
+
private const string URL = "https://worldtimeapi.org/api/ip";
|
|
94
|
+
|
|
95
|
+
[Serializable]
|
|
96
|
+
private class TimeData
|
|
97
|
+
{
|
|
98
|
+
public string datetime;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public override string GetURL()
|
|
102
|
+
{
|
|
103
|
+
return URL;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public override ServerTimeData ParseResponse(string response)
|
|
107
|
+
{
|
|
108
|
+
try
|
|
109
|
+
{
|
|
110
|
+
var data = JsonUtility.FromJson<TimeData>(response);
|
|
111
|
+
DateTime utcTime = DateTime.ParseExact(data.datetime, "yyyy-MM-dd'T'HH:mm:ss.ffffffzzz", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AdjustToUniversal);
|
|
112
|
+
return new ServerTimeData(utcTime);
|
|
113
|
+
}
|
|
114
|
+
catch (Exception e)
|
|
115
|
+
{
|
|
116
|
+
return new ServerTimeData($"Error parsing time data: {e.Message}");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
namespace Amanotes.Core.Internal
|
|
123
|
+
{
|
|
124
|
+
public partial class ConfigAsset
|
|
125
|
+
{
|
|
126
|
+
[SerializeField] public bool enableServerTime = true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
internal class GDKServerTime
|
|
130
|
+
{
|
|
131
|
+
private const float MAX_WAIT_TIME = 60;
|
|
132
|
+
private List<IServerTimeProvider> providers = new List<IServerTimeProvider>() { new WorldTimeAPI_TimeProvider(), new WorldTimeServer_TimeProvider() };
|
|
133
|
+
private int currentIndex = 0;
|
|
134
|
+
private IServerTimeProvider provider
|
|
135
|
+
{
|
|
136
|
+
get { return providers[currentIndex]; }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private DateTime? serverTime;
|
|
140
|
+
private float timeSinceFetchServerTime;
|
|
141
|
+
private int retryCount = 0;
|
|
142
|
+
|
|
143
|
+
internal GDKServerTime()
|
|
144
|
+
{
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public void SetTimeProviders(List<IServerTimeProvider> providers)
|
|
148
|
+
{
|
|
149
|
+
GDKUtils.ThrowIf(providers == null || providers.Count == 0, "Server time providers list cannot be null or empty.");
|
|
150
|
+
this.providers = providers;
|
|
151
|
+
currentIndex = 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public DateTime? GetTime()
|
|
155
|
+
{
|
|
156
|
+
GDKUtils.ThrowIf(!AmaGDK.Config.enableServerTime, "ServerTime is disabled, Please enable it in the AmaGDKConfig");
|
|
157
|
+
if (serverTime == null)
|
|
158
|
+
return null;
|
|
159
|
+
float duration = Time.realtimeSinceStartup - timeSinceFetchServerTime;
|
|
160
|
+
return ((DateTime)serverTime).AddSeconds(duration);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public void GetTime(Action<DateTime?> result)
|
|
164
|
+
{
|
|
165
|
+
GDKUtils.ThrowIf(!AmaGDK.Config.enableServerTime, "ServerTime is disabled, Please enable it in the AmaGDKConfig");
|
|
166
|
+
if (serverTime == null)
|
|
167
|
+
{
|
|
168
|
+
UpdateTime(result);
|
|
169
|
+
}
|
|
170
|
+
else
|
|
171
|
+
{
|
|
172
|
+
result(GetTime());
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public void UpdateTime(Action<DateTime?> result = null)
|
|
177
|
+
{
|
|
178
|
+
provider.FetchServerTime(data =>
|
|
179
|
+
{
|
|
180
|
+
if (data.time != null)
|
|
181
|
+
{
|
|
182
|
+
serverTime = data.time;
|
|
183
|
+
timeSinceFetchServerTime = Time.realtimeSinceStartup;
|
|
184
|
+
result?.Invoke(data.time);
|
|
185
|
+
}
|
|
186
|
+
else
|
|
187
|
+
{
|
|
188
|
+
result?.Invoke(null);
|
|
189
|
+
Logging.Log(data.errorMessage);
|
|
190
|
+
FetchTimeWithOtherProvider();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private void FetchTimeWithOtherProvider()
|
|
196
|
+
{
|
|
197
|
+
retryCount++;
|
|
198
|
+
currentIndex = (currentIndex + 1) % providers.Count;
|
|
199
|
+
if (retryCount < providers.Count)
|
|
200
|
+
{
|
|
201
|
+
UpdateTime();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
float waitTime = Mathf.Min(MAX_WAIT_TIME, Mathf.Pow(2, retryCount - providers.Count + 1));
|
|
205
|
+
GDKUtils.DelayCall(waitTime, () =>
|
|
206
|
+
{
|
|
207
|
+
UpdateTime();
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -43,14 +43,22 @@ namespace Amanotes.Core
|
|
|
43
43
|
{
|
|
44
44
|
this.converter = converter;
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
private string GetValueAsString(object
|
|
46
|
+
|
|
47
|
+
private string GetValueAsString(object fieldValue)
|
|
48
48
|
{
|
|
49
|
-
|
|
50
|
-
var
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
var fieldStringValue = fieldValue.ToString();
|
|
50
|
+
var fieldType = fieldValue.GetType();
|
|
51
|
+
if (fieldType.IsEnum)
|
|
52
|
+
{
|
|
53
|
+
var members = fieldType.GetMember(fieldStringValue);
|
|
54
|
+
if (members.Length > 0)
|
|
55
|
+
{
|
|
56
|
+
var attribute = (ValueAttribute)GetCustomAttribute(members[0], typeof(ValueAttribute));
|
|
57
|
+
if (attribute != null)
|
|
58
|
+
return convertMethods[converter](attribute.sValue);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return convertMethods[converter](fieldStringValue);
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
public override object Convert(object parent, FieldInfo fieldInfo, object param)
|
|
@@ -11,9 +11,10 @@ namespace Amanotes.Core
|
|
|
11
11
|
public string lastEventName;
|
|
12
12
|
|
|
13
13
|
public static void LogEventError(string message)
|
|
14
|
-
{
|
|
15
|
-
if
|
|
16
|
-
|
|
14
|
+
{
|
|
15
|
+
#if !AMAGDK_DEV
|
|
16
|
+
Logging.LogError(message);
|
|
17
|
+
#endif
|
|
17
18
|
throw new KlavarException(message);
|
|
18
19
|
}
|
|
19
20
|
}
|
|
@@ -87,7 +87,7 @@ namespace Amanotes.Core
|
|
|
87
87
|
{
|
|
88
88
|
return this
|
|
89
89
|
.GetType()
|
|
90
|
-
.GetFields()
|
|
90
|
+
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
|
91
91
|
.Where(field => field.GetCustomAttribute<IgnoreAttribute>() == null)
|
|
92
92
|
.Select(field => Validate(field))
|
|
93
93
|
.ToDictionary(
|
|
@@ -39,14 +39,14 @@ namespace Amanotes.Core
|
|
|
39
39
|
public static string ToCamelCase(string text)
|
|
40
40
|
{
|
|
41
41
|
var tokens = ToTokens(text);
|
|
42
|
-
return string.Join("", tokens.Select(t
|
|
42
|
+
return string.Join("", tokens.Select((t, i) =>
|
|
43
|
+
(i == 0 ? Char.ToLower(t[0]) : Char.ToUpper(t[0])) + t.Substring(1).ToLower()));
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
public static string ToPascalCase(string text)
|
|
46
47
|
{
|
|
47
48
|
var tokens = ToTokens(text);
|
|
48
|
-
return string.Join("", tokens.Select((t
|
|
49
|
-
(i == 0 ? Char.ToLower(t[0]) : Char.ToUpper(t[0])) + t.Substring(1).ToLower()));
|
|
49
|
+
return string.Join("", tokens.Select(t => Char.ToUpper(t[0]) + t.Substring(1).ToLower()));
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
public static string ToKebabCase(string text)
|