com.amanotes.gdk 0.2.14 → 0.2.16

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.
@@ -142,7 +142,7 @@ namespace Amanotes.Editor
142
142
  {
143
143
  try
144
144
  {
145
- string[] lines = rawText.Split("\n");
145
+ string[] lines = rawText.Split('\n');
146
146
  KeyValue[] arrKeyValue = new KeyValue[lines.Length];
147
147
  for (int i = 0; i < lines.Length; i++)
148
148
  {
@@ -0,0 +1,151 @@
1
+ using System.Collections.Generic;
2
+ using UnityEditor;
3
+ using UnityEngine;
4
+ using System.IO;
5
+ using System.Linq;
6
+ using System;
7
+ using System.Text.RegularExpressions;
8
+ using Amanotes.Core.Internal;
9
+ using Amanotes.Editor;
10
+
11
+ namespace Amanotes.Core
12
+ {
13
+ internal class MatchSDKVersion
14
+ {
15
+ private static string defineVer = "#define VER_";
16
+ private static string comment = "//";
17
+
18
+ private static Dictionary<string, string> sdkToClassMap = new Dictionary<string, string>
19
+ {
20
+ { "FirebaseAnalytics", "FirebaseAnalyticsAdapter" },
21
+ { "FirebaseRemoteConfig", "FirebaseRemoteConfigAdapter" },
22
+ { "appsflyer-unity-plugin", "AppsFlyerAdapter" },
23
+ { "IronSource_IntegrationManager", "IronSourceAdapter" },
24
+ { "Adjust", "AdjustAdapter" },
25
+ { "Purchases", "RevenueCatAdapter" }
26
+ };
27
+
28
+ private static FileInfo[] files = null;
29
+
30
+ [InitializeOnLoadMethod]
31
+ private static void Initialize()
32
+ {
33
+ AssetDatabase.importPackageCompleted += UpdateAdapter;
34
+ }
35
+ private static void UncommentDefineAtLine(string filePath, int lineIndex)
36
+ {
37
+ string[] lines = File.ReadAllLines(filePath);
38
+
39
+ for (int i = 0; i < lines.Length; i++)
40
+ {
41
+ if (i == lineIndex)
42
+ {
43
+ lines[i] = lines[i].TrimStart(new[] { '/', ' ' });
44
+ }
45
+ else if (Regex.IsMatch(lines[i], @"^#define\s+"))
46
+ {
47
+ lines[i] = "//" + lines[i];
48
+ }
49
+ }
50
+
51
+ File.WriteAllLines(filePath, lines);
52
+ }
53
+
54
+ private static KeyValuePair<int, SemVer> FindNearestLowerVersion(SemVer inputVersion, Dictionary<int, SemVer> defineLines)
55
+ {
56
+ KeyValuePair<int, SemVer> versionLessAndNearest = default;
57
+ SemVer bestMatch = SemVer.ZERO;
58
+
59
+ foreach (var kvp in defineLines)
60
+ {
61
+ if(inputVersion.CompareTo(kvp.Value) < 0)
62
+ continue;
63
+ if(kvp.Value.CompareTo(bestMatch) <= 0)
64
+ continue;
65
+
66
+ bestMatch = kvp.Value;
67
+ versionLessAndNearest = kvp;
68
+ }
69
+
70
+ return versionLessAndNearest;
71
+ }
72
+
73
+ private static Dictionary<int, SemVer> GetSemVersDefine(string code)
74
+ {
75
+ Dictionary<int, SemVer> defineLines = new Dictionary<int, SemVer>();
76
+
77
+ string[] lines = code.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
78
+ for (int i = 0; i < lines.Length; i++)
79
+ {
80
+ string line = lines[i].Trim();
81
+
82
+ if (Regex.IsMatch(line, @"^(\/\/\s*)?#define\s+"))
83
+ {
84
+ defineLines.Add(i, new SemVer(GetVersionNumber(line)));
85
+ }
86
+ }
87
+
88
+ return defineLines;
89
+ }
90
+
91
+ private static string GetVersionNumber(string defineString)
92
+ {
93
+ string pattern = @"VER_(\d+)_(\d+)_(\d+)";
94
+
95
+ Match match = Regex.Match(defineString, pattern);
96
+
97
+ if (match.Success)
98
+ {
99
+ string major = match.Groups[1].Value;
100
+ string minor = match.Groups[2].Value;
101
+ string patch = match.Groups[3].Value;
102
+ string versionNumber = $"{major}.{minor}.{patch}";
103
+
104
+ return versionNumber;
105
+ }
106
+
107
+ return string.Empty;
108
+ }
109
+
110
+ private static void UpdateAdapter(string packageName)
111
+ {
112
+ if (files == null)
113
+ {
114
+ string dataPath = Path.Combine(Application.dataPath, "AmaGDK/Adapters");
115
+ if (!Directory.Exists(dataPath))
116
+ {
117
+ Logging.Log($"{dataPath} is not found");
118
+ return;
119
+ }
120
+ DirectoryInfo directory = new DirectoryInfo(dataPath);
121
+ files = directory.GetFiles("*Adapter.cs", SearchOption.AllDirectories);
122
+ }
123
+
124
+ if (sdkToClassMap.TryGetValue(GetSDKName(packageName), out string adapterClass))
125
+ {
126
+ var version = SDKStatus.Get(adapterClass.Replace("Adapter", "")).sdkVersion;
127
+ FileInfo file = files.FirstOrDefault(x => x.FullName.Contains($"{adapterClass}.cs"));
128
+
129
+ if (file != null)
130
+ {
131
+ Dictionary<int, SemVer> defineLines = GetSemVersDefine(File.ReadAllText(file.FullName));
132
+
133
+ KeyValuePair<int, SemVer> r = FindNearestLowerVersion(version, defineLines);
134
+
135
+ if (r.Value.isValid)
136
+ {
137
+ return;
138
+ }
139
+ UncommentDefineAtLine(file.FullName, r.Key);
140
+ }
141
+ }
142
+
143
+ AssetDatabase.Refresh();
144
+ }
145
+
146
+ private static string GetSDKName(string packageName)
147
+ {
148
+ return sdkToClassMap.Keys.FirstOrDefault(sdk => packageName.Contains(sdk));
149
+ }
150
+ }
151
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: d5546420c6797492fb3cf9ea07b24f1a
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -24,7 +24,7 @@ namespace Amanotes.Editor
24
24
  }
25
25
  }
26
26
 
27
- [Serializable] internal class SDKStatus
27
+ [Serializable] public class SDKStatus
28
28
  {
29
29
 
30
30
  //
@@ -44,40 +44,43 @@ namespace Amanotes.Editor
44
44
 
45
45
  private static IEnumerator GetCoroutine(string url, Dictionary<string, string> headers = null, Action<float> onProgress = null, Action<string> onSuccess = null, Action<string> onError = null)
46
46
  {
47
- using var request = UnityWebRequest.Get(url);
48
- // Add headers to the request
49
- if (headers != null)
47
+ using (var request = UnityWebRequest.Get(url))
50
48
  {
51
- foreach (var header in headers)
49
+ // Add headers to the request
50
+ if (headers != null)
52
51
  {
53
- request.SetRequestHeader(header.Key, header.Value);
52
+ foreach (var header in headers)
53
+ {
54
+ request.SetRequestHeader(header.Key, header.Value);
55
+ }
56
+ }
57
+
58
+ // Send the request
59
+ request.timeout = 60;
60
+ yield return request.SendWebRequest();
61
+
62
+ while (request.isDone == false)
63
+ {
64
+ onProgress?.Invoke(request.downloadProgress);
65
+ yield return null;
66
+ }
67
+
68
+ // Check for errors
69
+ #pragma warning disable CS0618
70
+ if (request.isNetworkError || request.isHttpError)
71
+ #pragma warning restore CS0618
72
+ {
73
+ Debug.LogError("Error: " + request.error);
74
+ onError?.Invoke(request.error);
75
+ }
76
+ else
77
+ {
78
+ // Process the response
79
+ string responseText = request.downloadHandler.text;
80
+ onSuccess?.Invoke(responseText);
54
81
  }
55
82
  }
56
-
57
- // Send the request
58
- request.timeout = 60;
59
- yield return request.SendWebRequest();
60
-
61
- while (request.isDone == false)
62
- {
63
- onProgress?.Invoke(request.downloadProgress);
64
- yield return null;
65
- }
66
-
67
- // Check for errors
68
- #pragma warning disable CS0618
69
- if (request.isNetworkError || request.isHttpError)
70
- #pragma warning restore CS0618
71
- {
72
- Debug.LogError("Error: " + request.error);
73
- onError?.Invoke(request.error);
74
- }
75
- else
76
- {
77
- // Process the response
78
- string responseText = request.downloadHandler.text;
79
- onSuccess?.Invoke(responseText);
80
- }
83
+
81
84
  }
82
85
  }
83
86
  }
@@ -336,6 +336,7 @@ namespace Amanotes.Core
336
336
  public static string IpAddress => User.amaPassport.device.ipAddress;
337
337
  public static string Country => User.amaPassport.device.country;
338
338
  public static string DeviceUniqueId => User.amaPassport.device.deviceUniqueId;
339
+ public static string AppsflyerId => User.amaPassport.device.appsflyerId;
339
340
 
340
341
  internal static void Init()
341
342
  {
@@ -0,0 +1,423 @@
1
+ using System;
2
+ using System.Collections;
3
+ using System.Collections.Generic;
4
+ using UnityEngine;
5
+ using Amanotes.Core.Internal;
6
+
7
+ using static Amanotes.Core.AmaGDK.IAP;
8
+ using static Amanotes.Core.Internal.Logging;
9
+
10
+ namespace Amanotes.Core
11
+ {
12
+ public partial class AmaGDK //In-app purchase
13
+ {
14
+ public partial class IAP //Public
15
+ {
16
+ public static bool IsVIP => !string.IsNullOrEmpty(localData.activeSubscriptionId);
17
+
18
+ public static string ActiveSubscriptionId => localData.activeSubscriptionId;
19
+
20
+ public static Dictionary<string, ProductInfo> AllProducts => dicProducts;
21
+
22
+ public static ISetupBuilder GetProducts()
23
+ {
24
+ var result = new SetupContext();
25
+ ExecuteInNextFrame(result.GetProducts);
26
+ return result;
27
+ }
28
+
29
+ public static IPurchaseBuilder Purchase(string productId)
30
+ {
31
+ var result = new PurchaseContext();
32
+ ExecuteInNextFrame(() => result.Purchase(productId));
33
+ return result;
34
+ }
35
+
36
+ public static IPurchaseBuilder RestorePurchase()
37
+ {
38
+ var result = new PurchaseContext();
39
+ ExecuteInNextFrame(result.RestorePurchase);
40
+ return result;
41
+ }
42
+
43
+ public static ProductInfo GetProduct(string productId)
44
+ {
45
+ if (!dicProducts.ContainsKey(productId))
46
+ {
47
+ LogWarning($"[IAP] Not found product with id <{productId}>. Make sure you have called Setup() first.");
48
+ return null;
49
+ }
50
+
51
+ return dicProducts[productId];
52
+ }
53
+
54
+ public static List<string> GetActiveSubscriptions()
55
+ {
56
+ return IAPAdapter?.GetActiveSubscriptions();
57
+ }
58
+
59
+ public static List<string> GetPurchasedProductIds()
60
+ {
61
+ return localData.purchasedProducts;
62
+ }
63
+
64
+ public static bool IsPurchased(string productId)
65
+ {
66
+ var purchasedProductIds = GetPurchasedProductIds();
67
+ return purchasedProductIds != null && purchasedProductIds.Contains(productId);
68
+ }
69
+ }
70
+
71
+ public partial class IAP //Internal
72
+ {
73
+ internal static IIAPAdapter adapter;
74
+ internal static readonly IAPData localData = new IAPData().LoadJsonFromFile();
75
+ internal static IIAPAdapter IAPAdapter
76
+ {
77
+ get
78
+ {
79
+ if (adapter != null) return adapter;
80
+ LogWarning("[IAP] Adapter is not available.");
81
+ return null;
82
+ }
83
+ }
84
+
85
+ private static IAPAdapterConfig _adapterConfig;
86
+ internal static IAPAdapterConfig AdapterConfig
87
+ {
88
+ get
89
+ {
90
+ if (_adapterConfig != null) return _adapterConfig;
91
+ LogWarning("[IAP] Adapter config is not available.");
92
+ return null;
93
+ }
94
+ }
95
+
96
+ internal static Dictionary<string, ProductInfo> dicProducts = new Dictionary<string, ProductInfo>();
97
+
98
+ internal static void Init()
99
+ {
100
+ adapter = Adapter.GetSingleAdapter<IIAPAdapter>();
101
+ var adapterContext = adapter as AdapterContext;
102
+
103
+ if (adapterContext == null) return;
104
+ _adapterConfig = adapterContext?.GetAdapterConfig<IAPAdapterConfig>();
105
+
106
+ if (!adapterContext.IsReady) return;
107
+ localData.Sync();
108
+ if (_adapterConfig.autoGetProducts)
109
+ GetProducts();
110
+ }
111
+
112
+ internal static void ExecuteInNextFrame(Action method)
113
+ {
114
+ _instance.StartCoroutine(ExecuteRoutine(method));
115
+ }
116
+
117
+ static IEnumerator ExecuteRoutine(Action method)
118
+ {
119
+ yield return null;
120
+ method?.Invoke();
121
+ }
122
+ }
123
+
124
+ public partial class IAP //Persistent
125
+ {
126
+ [Serializable]
127
+ internal class IAPData : IJsonFile
128
+ {
129
+ public string activeSubscriptionId;
130
+ public List<string> purchasedProducts;
131
+
132
+ public void Sync()
133
+ {
134
+ activeSubscriptionId = adapter.GetActiveSubscriptionId();
135
+ purchasedProducts = adapter.GetPurchasedProducts();
136
+ this.SaveJsonToFile();
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ public enum ProductType
143
+ {
144
+ Subscription,
145
+ Consumable,
146
+ NonConsumable
147
+ }
148
+
149
+ [Serializable]
150
+ public class ProductInfo
151
+ {
152
+ public string id;
153
+ public ProductType type;
154
+ [HideInInspector] public string title;
155
+ [HideInInspector] public string description;
156
+ [HideInInspector] public float price;
157
+ [HideInInspector] public string currencyCode;
158
+ [HideInInspector] public string[] discountIds;
159
+ }
160
+
161
+ public class PurchaseSuccess
162
+ {
163
+ public string productId;
164
+ public string transactionId;
165
+ public string receipt;
166
+ public string purchaseToken;
167
+ }
168
+
169
+ public class PurchaseError
170
+ {
171
+ public string productId;
172
+ public bool isCancelled;
173
+ public string error;
174
+
175
+ public PurchaseError()
176
+ {
177
+ }
178
+
179
+ public PurchaseError(string errorMessage)
180
+ {
181
+ error = errorMessage;
182
+ }
183
+ }
184
+
185
+ public static class IAPExtension
186
+ {
187
+ //GetProducts extensions
188
+ public static ISetupBuilder SetSubscriptions(this ISetupBuilder sc, params string[] subs)
189
+ {
190
+ if (sc == null) return null;
191
+
192
+ foreach (var pId in subs)
193
+ {
194
+ AdapterConfig?.products.Add(new ProductInfo() { id = pId, type = ProductType.Subscription });
195
+ }
196
+ return sc;
197
+ }
198
+
199
+ public static ISetupBuilder SetConsumables(this ISetupBuilder sc, params string[] cons)
200
+ {
201
+ if (sc == null) return null;
202
+
203
+ foreach (var pId in cons)
204
+ {
205
+ AdapterConfig?.products.Add(new ProductInfo() { id = pId, type = ProductType.Consumable });
206
+ }
207
+ return sc;
208
+ }
209
+
210
+ public static ISetupBuilder SetNonComsumables(this ISetupBuilder sc, params string[] nons)
211
+ {
212
+ if (sc == null) return null;
213
+
214
+ foreach (var pId in nons)
215
+ {
216
+ AdapterConfig?.products.Add(new ProductInfo() { id = pId, type = ProductType.NonConsumable });
217
+ }
218
+ return sc;
219
+ }
220
+
221
+ public static ISetupBuilder OnSuccess(this ISetupBuilder sc, Action<Dictionary<string, ProductInfo>> callback)
222
+ {
223
+ if (sc == null) return null;
224
+
225
+ var setupContext = sc as SetupContext;
226
+ setupContext.onSuccess = callback;
227
+ return sc;
228
+ }
229
+
230
+ public static ISetupBuilder OnFail(this ISetupBuilder sc, Action<string> callback)
231
+ {
232
+ if (sc == null) return null;
233
+
234
+ var setupContext = sc as SetupContext;
235
+ setupContext.onFail = callback;
236
+ return sc;
237
+ }
238
+
239
+ //Purchase extensions
240
+ public static IPurchaseBuilder OnSuccess(this IPurchaseBuilder pb, Action<PurchaseSuccess> callback)
241
+ {
242
+ if (pb == null) return null;
243
+
244
+ var purchaseContext = pb as PurchaseContext;
245
+ purchaseContext.onSuccess = callback;
246
+ return pb;
247
+ }
248
+
249
+ public static IPurchaseBuilder OnFail(this IPurchaseBuilder pb, Action<PurchaseError> callback)
250
+ {
251
+ if (pb == null) return null;
252
+
253
+ var purchaseContext = pb as PurchaseContext;
254
+ purchaseContext.onFail = callback;
255
+ return pb;
256
+ }
257
+
258
+ public static IPurchaseBuilder SetDiscount(this IPurchaseBuilder pb, string discountId)
259
+ {
260
+ if (pb == null) return null;
261
+
262
+ var purchaseContext = pb as PurchaseContext;
263
+ purchaseContext.discountId = discountId;
264
+ return pb;
265
+ }
266
+ }
267
+ }
268
+
269
+ namespace Amanotes.Core.Internal
270
+ {
271
+ [Serializable]
272
+ public class IAPAdapterConfig
273
+ {
274
+ [SerializeField] internal bool allPurchaseSuccessInEditor;
275
+ [SerializeField] internal List<ProductInfo> products;
276
+ [SerializeField] internal bool autoGetProducts;
277
+ }
278
+
279
+ public interface ISetupBuilder { }
280
+
281
+ public class SetupContext: ISetupBuilder
282
+ {
283
+ public Action<Dictionary<string, ProductInfo>> onSuccess;
284
+ public Action<string> onFail;
285
+
286
+ private readonly Queue<(List<string> pIds, ProductType pType)> getProdQueue = new Queue<(List<string> pIds, ProductType pType)>();
287
+
288
+ public void GetProducts()
289
+ {
290
+ if (AdapterConfig?.products.Count == 0)
291
+ {
292
+ LogWarning("There is no IAP product");
293
+ return;
294
+ }
295
+
296
+ getProdQueue.Clear();
297
+ var subs = new List<string>();
298
+ var cons = new List<string>();
299
+ var nons = new List<string>();
300
+
301
+ foreach (var p in AdapterConfig.products)
302
+ {
303
+ switch (p.type)
304
+ {
305
+ case ProductType.Subscription:
306
+ subs.Add(p.id);
307
+ break;
308
+ case ProductType.Consumable:
309
+ cons.Add(p.id);
310
+ break;
311
+ case ProductType.NonConsumable:
312
+ nons.Add(p.id);
313
+ break;
314
+ default:
315
+ LogWarning($"[IAP] Product <{p.id}> has not specified ProductType.");
316
+ break;
317
+ }
318
+ }
319
+
320
+ getProdQueue.Enqueue((subs, ProductType.Subscription));
321
+ getProdQueue.Enqueue((cons, ProductType.Consumable));
322
+ getProdQueue.Enqueue((nons, ProductType.NonConsumable));
323
+ GetNextProductsInQueue();
324
+ }
325
+
326
+ void GetNextProductsInQueue()
327
+ {
328
+ if (getProdQueue.Count <= 0)
329
+ {
330
+ onSuccess?.Invoke(dicProducts);
331
+ return;
332
+ }
333
+
334
+ var item = getProdQueue.Dequeue();
335
+ GetProductsByType(item.pIds, item.pType);
336
+ }
337
+
338
+ void GetProductsByType(List<string> lsProduct, ProductType type)
339
+ {
340
+ try
341
+ {
342
+ IAPAdapter?.GetProducts(lsProduct.ToArray(), type, (pList) =>
343
+ {
344
+ foreach (var pI in pList)
345
+ {
346
+ dicProducts.Add(pI.id, pI);
347
+ }
348
+ ExecuteInNextFrame(GetNextProductsInQueue);
349
+ });
350
+ }
351
+ catch (Exception e)
352
+ {
353
+ LogWarning($"[IAP] Error on getting products of type <{type}>. Error detail:\n{e.Message}");
354
+ onFail?.Invoke(e.Message);
355
+ throw;
356
+ }
357
+ }
358
+ }
359
+
360
+ public interface IPurchaseBuilder { }
361
+
362
+ public class PurchaseContext : IPurchaseBuilder
363
+ {
364
+ public Action<PurchaseSuccess> onSuccess;
365
+ public Action<PurchaseError> onFail;
366
+ public string discountId;
367
+
368
+ public void Purchase(string productId)
369
+ {
370
+ if (!dicProducts.ContainsKey(productId))
371
+ {
372
+ LogWarning($"[IAP] Product {productId} is not found");
373
+ return;
374
+ }
375
+
376
+ #if UNITY_EDITOR
377
+ if (AdapterConfig.allPurchaseSuccessInEditor)
378
+ {
379
+ DoOnSuccess(new PurchaseSuccess
380
+ {
381
+ productId = productId
382
+ });
383
+ return;
384
+ }
385
+ #endif
386
+
387
+ if (!string.IsNullOrWhiteSpace(discountId))
388
+ {
389
+ IAPAdapter?.PurchaseDiscount(productId, dicProducts[productId].type, discountId, DoOnSuccess, DoOnFail);
390
+ return;
391
+ }
392
+
393
+ IAPAdapter?.Purchase(productId, dicProducts[productId].type, DoOnSuccess, DoOnFail);
394
+ }
395
+
396
+ public void RestorePurchase()
397
+ {
398
+ IAPAdapter?.RestorePurchase(DoOnSuccess, DoOnFail);
399
+ }
400
+
401
+ void DoOnSuccess(PurchaseSuccess success)
402
+ {
403
+ localData.Sync();
404
+ onSuccess?.Invoke(success);
405
+ }
406
+
407
+ void DoOnFail(PurchaseError error)
408
+ {
409
+ onFail?.Invoke(error);
410
+ }
411
+ }
412
+
413
+ public interface IIAPAdapter
414
+ {
415
+ void GetProducts(string[] productIds, ProductType productType, Action<ProductInfo[]> onComplete);
416
+ void Purchase(string productId, ProductType productType, Action<PurchaseSuccess> onSuccess, Action<PurchaseError> onFail);
417
+ void PurchaseDiscount(string productId, ProductType productType, string discountId, Action<PurchaseSuccess> onSuccess, Action<PurchaseError> onFail);
418
+ void RestorePurchase(Action<PurchaseSuccess> onSuccess, Action<PurchaseError> onFail);
419
+ List<string> GetActiveSubscriptions();
420
+ List<string> GetPurchasedProducts();
421
+ string GetActiveSubscriptionId();
422
+ }
423
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: a49a40b8b04e3450b9dac915b22533de
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
package/Runtime/AmaGDK.cs CHANGED
@@ -109,6 +109,7 @@ namespace Amanotes.Core
109
109
  AmaPassport.Init();
110
110
  Analytics.Init();
111
111
  Ads.Init();
112
+ IAP.Init();
112
113
 
113
114
  _status = Status.Ready;
114
115
  _onReadyCallback?.Invoke();
@@ -242,7 +243,7 @@ namespace Amanotes.Core
242
243
  public void ChangeDevMode()
243
244
  {
244
245
  string variableName = "GDK_HOME";
245
- string output = RunShellCommand($"source ~/.zshrc && echo ${variableName}");
246
+ string output = RunShellCommand($"source ~/.zshrc && echo file:${variableName}");
246
247
  output = output.Trim();
247
248
 
248
249
  if (string.IsNullOrEmpty(output))
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "com.amanotes.gdk",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "displayName": "AmaGDK",
5
5
  "description": "Amanotes Game Development Kit",
6
- "unity": "2020.3",
6
+ "unity": "2019.4",
7
7
  "keywords": [
8
8
  "Amanotes",
9
9
  "Amanotes GDK",