update: 完善库存系统, 和中台接口统一, 接入 Sqlit4Unity 插件
							parent
							
								
									0888bc4658
								
							
						
					
					
						commit
						3f10cf718e
					
				|  | @ -114,6 +114,7 @@ namespace Guru | ||||||
| 		// 经济相关 | 		// 经济相关 | ||||||
| 		public static readonly string ParameterBalance = "balance"; // 用于余额 | 		public static readonly string ParameterBalance = "balance"; // 用于余额 | ||||||
| 		public static readonly string ParameterSku = "sku"; // sku | 		public static readonly string ParameterSku = "sku"; // sku | ||||||
|  | 		public static readonly string ParameterScene = "scene"; // sku | ||||||
| 		public static readonly string ParameterVirtualCurrencyName = "virtual_currency_name"; // 虚拟货币名称 | 		public static readonly string ParameterVirtualCurrencyName = "virtual_currency_name"; // 虚拟货币名称 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -72,65 +72,59 @@ namespace Guru | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// 获取虚拟货币 |         /// 获取道具/货币 | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="currencyName"></param> |         /// <param name="virtualCurrencyName"></param> | ||||||
|         /// <param name="value"></param> |  | ||||||
|         /// <param name="balance"></param> |  | ||||||
|         /// <param name="method"></param> |         /// <param name="method"></param> | ||||||
|         /// <param name="levelName"></param> |         /// <param name="balance"></param> | ||||||
|         /// <param name="isIap"></param> |         /// <param name="value"></param> | ||||||
|         /// <param name="sku"></param> |         /// <param name="methodDetails"></param> | ||||||
|         /// <param name="scene"></param> |         public static void EarnVirtualCurrency(string virtualCurrencyName, string method, int balance, int value,   | ||||||
|         public static void EarnVirtualCurrency(string currencyName, int value, int balance,  |             string methodDetails = "") | ||||||
|             string method = "",  |  | ||||||
|             string levelName = "", |  | ||||||
|             bool isIap = false, |  | ||||||
|             string sku = "", |  | ||||||
|             string scene = "") |  | ||||||
|         { |         { | ||||||
|             if (isIap) method = "iap_buy";   |  | ||||||
|             var data = new Dictionary<string, dynamic>() |             var data = new Dictionary<string, dynamic>() | ||||||
|             { |             { | ||||||
|                 { ParameterVirtualCurrencyName, currencyName }, |                 { ParameterVirtualCurrencyName, virtualCurrencyName }, | ||||||
|  |                 { ParameterItemCategory, method }, | ||||||
|  |                 { ParameterItemName, methodDetails }, | ||||||
|                 { ParameterValue, value }, |                 { ParameterValue, value }, | ||||||
|                 { ParameterBalance, balance }, |                 { ParameterBalance, balance }, | ||||||
|                 { ParameterLevelName, levelName }, |  | ||||||
|                 { ParameterItemCategory, method }, |  | ||||||
|             }; |             }; | ||||||
|              |  | ||||||
|             if (!string.IsNullOrEmpty(scene)) data[ParameterItemName] = scene; // 获取的虚拟货币或者道具的场景 |  | ||||||
|             if (!string.IsNullOrEmpty(sku)) data[ParameterSku] = sku; // 商品的 sku |  | ||||||
|              |  | ||||||
|             LogEvent(EventEarnVirtualCurrency, data, new EventSetting() { EnableFirebaseAnalytics = true }); |             LogEvent(EventEarnVirtualCurrency, data, new EventSetting() { EnableFirebaseAnalytics = true }); | ||||||
|              |              | ||||||
|             // FB 上报收入点 |             // FB 上报收入点 | ||||||
|             FB.LogAppEvent(EventEarnVirtualCurrency, value, data); |             // FB.LogAppEvent(EventEarnVirtualCurrency, value, data); | ||||||
|         } |         } | ||||||
|          |          | ||||||
|          |         /// <summary> | ||||||
|         public static void SpendVirtualCurrency(string currencyName, int value, int balance,  |         /// 消耗道具/货币 | ||||||
|             string method = "",  |         /// </summary> | ||||||
|             string levelName = "", |         /// <param name="contentId"></param> | ||||||
|             string scene = "") |         /// <param name="contentType"></param> | ||||||
|  |         /// <param name="price"></param> | ||||||
|  |         /// <param name="virtualCurrencyName"></param> | ||||||
|  |         /// <param name="balance"></param> | ||||||
|  |         /// <param name="scene"></param> | ||||||
|  |         public static void SpendVirtualCurrency(string contentId, string contentType, int price, | ||||||
|  |             string virtualCurrencyName, int balance, string scene = "") | ||||||
|         { |         { | ||||||
|             var data = new Dictionary<string, dynamic>() |             var data = new Dictionary<string, dynamic>() | ||||||
|             { |             { | ||||||
|                 { ParameterVirtualCurrencyName, currencyName }, |                 { ParameterVirtualCurrencyName, virtualCurrencyName }, | ||||||
|                 { ParameterValue, value }, |                 { ParameterValue, price }, | ||||||
|                 { ParameterBalance, balance }, |                 { ParameterBalance, balance }, | ||||||
|                 { ParameterLevelName, levelName }, |                 { ParameterItemName, contentId }, | ||||||
|                 { ParameterItemCategory, method }, |                 { ParameterItemCategory, contentType }, | ||||||
|             }; |             }; | ||||||
|              |             if (!string.IsNullOrEmpty(scene)) data[ParameterScene] = scene; // 获取的虚拟货币或者道具的场景 | ||||||
|             if (!string.IsNullOrEmpty(scene)) data[ParameterItemName] = scene; // 获取的虚拟货币或者道具的场景 |  | ||||||
|              |              | ||||||
|             LogEvent(EventSpendVirtualCurrency, data, new EventSetting() { EnableFirebaseAnalytics = true }); |             LogEvent(EventSpendVirtualCurrency, data, new EventSetting() { EnableFirebaseAnalytics = true }); | ||||||
|              |              | ||||||
|             // FB 上报消费点 |             // FB 上报消费点 | ||||||
|             FB.LogAppEvent(EventSpendVirtualCurrency, value, data); |             // FB.LogAppEvent(EventSpendVirtualCurrency, value, data); | ||||||
|  |              | ||||||
|             // FB 上报消耗事件买量点 |             // FB 上报消耗事件买量点 | ||||||
|             FBSpentCredits(value, scene, method);  // 点位信息有变化 |             FBSpentCredits(contentId, contentType, price);  // 点位信息有变化 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -140,7 +134,7 @@ namespace Guru | ||||||
|         /// <param name="amount"></param> |         /// <param name="amount"></param> | ||||||
|         /// <param name="contentId"></param> |         /// <param name="contentId"></param> | ||||||
|         /// <param name="contentType"></param> |         /// <param name="contentType"></param> | ||||||
|         private static void FBSpentCredits(int amount, string contentId, string contentType) |         private static void FBSpentCredits(string contentId, string contentType, float amount) | ||||||
|         { |         { | ||||||
|             FB.LogAppEvent(AppEventName.SpentCredits, amount,  |             FB.LogAppEvent(AppEventName.SpentCredits, amount,  | ||||||
|                 new Dictionary<string, object>() |                 new Dictionary<string, object>() | ||||||
|  |  | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 93bb1ade31b44cc9bb3dbad3051cbe78 | ||||||
|  | timeCreated: 1706922645 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 1f4820a46a3a4b02ac85a48a4e25014c | ||||||
|  | timeCreated: 1706922699 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: e921121b06404396b164c6538e68c6aa | ||||||
|  | timeCreated: 1706922657 | ||||||
|  | @ -0,0 +1,131 @@ | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using System; | ||||||
|  |     using System.Collections.Generic; | ||||||
|  |     using Newtonsoft.Json; | ||||||
|  | 
 | ||||||
|  |     [Serializable] | ||||||
|  |     public class InventoryData: IJsonData | ||||||
|  |     { | ||||||
|  |         [JsonProperty("v")]  | ||||||
|  |         public List<LimitedBalance> valid; | ||||||
|  |         [JsonProperty("e")]  | ||||||
|  |         public List<LimitedBalance> expired; | ||||||
|  | 
 | ||||||
|  |         public static InventoryData FromJson(string json) | ||||||
|  |         { | ||||||
|  |             if (JsonDataHelper.Parse<InventoryData>(json, out var d)) | ||||||
|  |             { | ||||||
|  |                 return d; | ||||||
|  |             } | ||||||
|  |             return new InventoryData(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public InventoryData() | ||||||
|  |         { | ||||||
|  |             valid = new List<LimitedBalance>(); | ||||||
|  |             expired = new List<LimitedBalance>(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public InventoryData(List<LimitedBalance> valid, List<LimitedBalance> expired) | ||||||
|  |         { | ||||||
|  |             this.valid = valid; | ||||||
|  |             this.expired = expired; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public static InventoryData Create(LimitedBalance balance = null) | ||||||
|  |         { | ||||||
|  |             var t = new InventoryData(); | ||||||
|  |             if (balance != null) | ||||||
|  |             { | ||||||
|  |                 if (balance.expireAt > InventoryManager.CurrentTimeInMillis) | ||||||
|  |                 { | ||||||
|  |                     t.valid.Add(balance); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return t; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |          | ||||||
|  |         public InventoryData AttachLimitedBalance(LimitedBalance balance) | ||||||
|  |         { | ||||||
|  |             if (balance.expireAt > InventoryManager.CurrentTimeInMillis) | ||||||
|  |             { | ||||||
|  |                 valid.Add(balance); | ||||||
|  |                 return new InventoryData() | ||||||
|  |                 { | ||||||
|  |                     valid =  this.valid, | ||||||
|  |                     expired = this.expired, | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 回收过期的道具 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public RecycleResult RecycleExpiredBalance() | ||||||
|  |         { | ||||||
|  |             long now = InventoryManager.CurrentTimeInMillis; | ||||||
|  |             List<LimitedBalance> newValid = new List<LimitedBalance>(); | ||||||
|  |             List<LimitedBalance> newExpired = new List<LimitedBalance>(); | ||||||
|  |             int expiredBalance = 0; | ||||||
|  |             foreach(var item in valid) { | ||||||
|  |                 if (now >= item.expireAt) { | ||||||
|  |                     expiredBalance += item.amount; | ||||||
|  |                     newExpired.Add(item); | ||||||
|  |                 } else { | ||||||
|  |                     newValid.Add(item); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return new RecycleResult(expiredBalance, new InventoryData(newValid, newExpired)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     [Serializable] | ||||||
|  |     public class LimitedBalance | ||||||
|  |     { | ||||||
|  |         [JsonProperty("a")] | ||||||
|  |         public int amount = 0; | ||||||
|  |         [JsonProperty("e")] | ||||||
|  |         public long expireAt = -1; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     public class RecycleResult { | ||||||
|  |         public int expiredBalance; | ||||||
|  |         public InventoryData InventoryData; | ||||||
|  |          | ||||||
|  |         public RecycleResult(int expiredBalance, InventoryData inventoryData) { | ||||||
|  |             this.expiredBalance = expiredBalance; | ||||||
|  |             this.InventoryData = inventoryData; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public class ConsumeResult { | ||||||
|  |         public bool consumed; | ||||||
|  |         public InventoryItem item; | ||||||
|  |          | ||||||
|  |         public ConsumeResult(InventoryItem item, bool consumed) | ||||||
|  |         { | ||||||
|  |             this.item = item; | ||||||
|  |             this.consumed = consumed; | ||||||
|  |          | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public static ConsumeResult Success(InventoryItem item) | ||||||
|  |         { | ||||||
|  |             return new ConsumeResult(item, true); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static ConsumeResult Error(InventoryItem item) | ||||||
|  |         { | ||||||
|  |             return new ConsumeResult(item, false); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 54c27103f7594aa9bc337f5e076fb550 | ||||||
|  | timeCreated: 1706959298 | ||||||
|  | @ -0,0 +1,55 @@ | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 交易方式 | ||||||
|  |     /// </summary> | ||||||
|  |     public enum TransactionMethod | ||||||
|  |     { | ||||||
|  |         unknown  = 0, | ||||||
|  |         iap, // IAP购买 | ||||||
|  |         igc, // In-game currency 购买(coin/gems..) | ||||||
|  |         reward, // 奖励获得 | ||||||
|  |         bonus, // 优惠 | ||||||
|  |         prop, // 道具 | ||||||
|  |         free, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// 道具列别 | ||||||
|  |     /// </summary> | ||||||
|  |     public class InventoryCategory | ||||||
|  |     { | ||||||
|  |         public const string Prop = "prop"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public partial class InventoryManager | ||||||
|  |     { | ||||||
|  |       | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取交易方式的字段值 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="method"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         private string ConvertTransactionMethodName(TransactionMethod method) { | ||||||
|  |             switch (method) { | ||||||
|  |                 case TransactionMethod.iap: | ||||||
|  |                     return "iap_buy"; | ||||||
|  |                 case TransactionMethod.igc: | ||||||
|  |                     return "igc"; | ||||||
|  |                 case TransactionMethod.reward: | ||||||
|  |                     return "reward"; | ||||||
|  |                 case TransactionMethod.bonus: | ||||||
|  |                     return "bonus"; | ||||||
|  |                 case TransactionMethod.prop: | ||||||
|  |                     return "prop"; | ||||||
|  |                 case TransactionMethod.free: | ||||||
|  |                     return "prop"; | ||||||
|  |                 default: | ||||||
|  |                     return "unknown"; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 0b4e29d06a7f4ddb860e08d3aa81610e | ||||||
|  | timeCreated: 1706945881 | ||||||
|  | @ -0,0 +1,578 @@ | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using SQLite4Unity3d; | ||||||
|  |     using UnityEngine; | ||||||
|  |     using System.Collections.Generic; | ||||||
|  |     using Newtonsoft.Json; | ||||||
|  |     using System; | ||||||
|  | 
 | ||||||
|  |     public interface IInventoryDelegate | ||||||
|  |     { | ||||||
|  |         string GetInventoryCategory(string id); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// 道具管理器 | ||||||
|  |     /// </summary> | ||||||
|  |     public partial class InventoryManager | ||||||
|  |     { | ||||||
|  |          | ||||||
|  |         private static InventoryManager _instance; | ||||||
|  |         public  static InventoryManager Instance | ||||||
|  |         { | ||||||
|  |             get | ||||||
|  |             { | ||||||
|  |                 if(_instance == null) _instance = new InventoryManager(); | ||||||
|  |                 return _instance; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         private IInventoryDelegate _delegate; | ||||||
|  |         private InventoryTable _table; | ||||||
|  | 
 | ||||||
|  |         public bool IsReady { get; private set; } = false; | ||||||
|  |          | ||||||
|  |         public static long CurrentTimeInMillis => TimeUtil.GetCurrentTimeStamp(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 初始化 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="service"></param> | ||||||
|  |         public static void Install(DBService service) | ||||||
|  |         { | ||||||
|  |             if (service != null) | ||||||
|  |             { | ||||||
|  |                 Instance._table = InventoryTable.LoadOrCreate(service); | ||||||
|  |                 Instance.IsReady = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取道具分类 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public string GetInventoryCategory(string id) | ||||||
|  |         { | ||||||
|  |             return _delegate?.GetInventoryCategory(id) ?? InventoryCategory.Prop; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private InventoryItem GetData(string sku) => _table.GetItem(sku); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取道具(组) | ||||||
|  |         /// 通过[method]中的的特定[specific]方式 获得了指定的 [items] | ||||||
|  |         /// * method: iap -> specific: sku | ||||||
|  |         /// * method: igc -> specific: coin/gems... | ||||||
|  |         /// * method: reward -> specific: ads/lottery/daily/... | ||||||
|  |         /// * method: bonus -> specific: ads/other/... | ||||||
|  |         /// * method: prop -> specific: hint/hammer/swap/magic/.. | ||||||
|  |         /// | ||||||
|  |         /// method 最终会在 earnVirtualCurrency 中成为 item_category | ||||||
|  |         /// specific 最终会在 earnVirtualCurrency 中成为 item_name | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="items"></param> | ||||||
|  |         /// <param name="method"></param> | ||||||
|  |         /// <param name="specific"></param> | ||||||
|  |         public void Acquire(List<StockItem> items, TransactionMethod method, string specific = "") | ||||||
|  |         { | ||||||
|  |             if (!IsReady) return; | ||||||
|  |              | ||||||
|  |             List<InventoryItem> acquired = new List<InventoryItem>(items.Count); | ||||||
|  |             string category; | ||||||
|  |             StockItem item; | ||||||
|  |             InventoryItem invItem; | ||||||
|  |              | ||||||
|  |             for (int i = 0; i < items.Count; i++) | ||||||
|  |             { | ||||||
|  |                 item = items[i]; | ||||||
|  |                 category = GetInventoryCategory(item.sku); | ||||||
|  |                 invItem = GetData(item.sku)?.Acquire(method, item.amount)  | ||||||
|  |                           ?? InventoryItem.Create(item.sku, category, item.attr,  balance:item.amount, method: method); | ||||||
|  |                 acquired.Add(invItem); | ||||||
|  |                  | ||||||
|  |                 Analytics.EarnVirtualCurrency( | ||||||
|  |                     item.sku, | ||||||
|  |                     method:ConvertTransactionMethodName(method), | ||||||
|  |                     methodDetails:specific, | ||||||
|  |                     balance: invItem.balance, | ||||||
|  |                     value: item.amount); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             _table.UpdateInventoryItems(acquired); // 更新数据库信息 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public bool Consume(List<StockItem> items, string contentId, string scene, string category = "") | ||||||
|  |         { | ||||||
|  |             if (!IsReady) return false; | ||||||
|  | 
 | ||||||
|  |             List<InventoryItem> consumed = new List<InventoryItem>(items.Count); | ||||||
|  |             bool isConsumed = true; | ||||||
|  | 
 | ||||||
|  |             StockItem item; | ||||||
|  |             InventoryItem invItem; | ||||||
|  | 
 | ||||||
|  |             for (int i = 0; i < items.Count; i++) | ||||||
|  |             { | ||||||
|  |                 item = items[i]; | ||||||
|  |                 var result = GetData(item.sku)?.Consume(scene, item.amount) ?? null; | ||||||
|  |                 if (result == null) | ||||||
|  |                 { | ||||||
|  |                     Debug.LogError($"consume item not found: {item.sku}"); | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 consumed.Add(result.item); | ||||||
|  |                 // 这里如果没有消耗掉,将跳出 | ||||||
|  |                 if (!result.consumed) | ||||||
|  |                 { | ||||||
|  |                     Debug.Log($"consume failed: {item.sku}:{item.amount}"); | ||||||
|  |                     isConsumed = false; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (consumed.Count == 0) | ||||||
|  |             { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (isConsumed && consumed.Count == items.Count) | ||||||
|  |                 { | ||||||
|  |                     for (int i = 0; i < items.Count; ++i) | ||||||
|  |                     { | ||||||
|  |                         Analytics.SpendVirtualCurrency( | ||||||
|  |                             contentId, category, items[i].amount, | ||||||
|  |                             virtualCurrencyName: consumed[i].sku, balance: consumed[i].balance); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             _table.UpdateInventoryItems(consumed); // 更新数据库信息 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public bool Consume(List<StockItem> items, Manifest redeem) | ||||||
|  |         { | ||||||
|  |             return Consume(items, redeem.scene, redeem.contentId, redeem.category); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 判断是否可以支付 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id"></param> | ||||||
|  |         /// <param name="amount"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public bool CanAfford(String id, int amount) { | ||||||
|  |             var item = GetData(id); | ||||||
|  |             return item != null && item.balance > amount; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // <summary> | ||||||
|  |     /// 库存道具 | ||||||
|  |     /// </summary> | ||||||
|  |     public class StockItem | ||||||
|  |     { | ||||||
|  |         [PrimaryKey]  | ||||||
|  |         public string sku { get; set; } | ||||||
|  |         public int amount { get; set; } | ||||||
|  |         public int attr { get; set; } | ||||||
|  |         public long expired { get; set; } | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         public static StockItem Consumable(string sku, int amount) | ||||||
|  |         { | ||||||
|  |             return Create(sku, amount, DetailsAttr.Consumable); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public static StockItem Permanent(string sku, int amount) | ||||||
|  |         { | ||||||
|  |             return Create (sku, amount, DetailsAttr.Permanent); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         public static StockItem Create(string sku, int amount, int att, long expiredStamp = 0) | ||||||
|  |         { | ||||||
|  |             return new StockItem | ||||||
|  |             { | ||||||
|  |                 sku = sku, | ||||||
|  |                 amount = amount, | ||||||
|  |                 attr = att, | ||||||
|  |                 expired = expiredStamp | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     [Serializable] | ||||||
|  |     public class InventoryItem: IJsonData | ||||||
|  |     { | ||||||
|  |         [JsonProperty(InventoryTable.dbSku)] | ||||||
|  |         public string sku { get; set; } | ||||||
|  |         [JsonProperty(InventoryTable.dbBalance)] | ||||||
|  |         public int balance { get; set; } | ||||||
|  |         [JsonProperty(InventoryTable.dbCategory)] | ||||||
|  |         public string category { get; set; } | ||||||
|  |         [JsonProperty(InventoryTable.dbAttr)] | ||||||
|  |         public int attr { get; set; } | ||||||
|  |         [JsonProperty(InventoryTable.dbTimeSensitive)] | ||||||
|  |         public InventoryData Inventory { get; set; } | ||||||
|  |         [JsonProperty(InventoryTable.dbUpdateAt)] | ||||||
|  |         public long updateAt{ get; set; } | ||||||
|  |         [JsonProperty(InventoryTable.dbCreateAt)] | ||||||
|  |         public long createAt{ get; set; } | ||||||
|  |         [JsonProperty(InventoryTable.dbDetails)] | ||||||
|  |         public InventoryDetails details { get; set; } | ||||||
|  |          | ||||||
|  |         public static Action<Exception> ExceptionHandler; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |          | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         public static InventoryItem FromJson(string json) | ||||||
|  |         { | ||||||
|  |             if (JsonDataHelper.Parse<InventoryItem>(json, out var d)) | ||||||
|  |             { | ||||||
|  |                 return d; | ||||||
|  |             } | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public InventoryItem(string sku, int balance, string category, int attr, InventoryDetails details = null, InventoryData inventory = null, long createAt = -1, long updateAt = -1) | ||||||
|  |         { | ||||||
|  |             this.sku = sku; | ||||||
|  |             this.category = category; | ||||||
|  |             this.balance = balance; | ||||||
|  |             this.attr = attr; | ||||||
|  |             this.details = details; | ||||||
|  |             this.Inventory = inventory; | ||||||
|  |             this.createAt = createAt; | ||||||
|  |             this.updateAt = updateAt; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public static InventoryItem Create(string sku,  string category, int attr,  | ||||||
|  |             int balance = 0, InventoryDetails details = null,  | ||||||
|  |             InventoryData inventory = null, long createAt = -1, long updateAt = -1,  | ||||||
|  |             int expireAt = -1, TransactionMethod method = TransactionMethod.unknown) | ||||||
|  |         { | ||||||
|  |             long stamp = InventoryManager.CurrentTimeInMillis; | ||||||
|  |             return new InventoryItem( | ||||||
|  |                 sku, | ||||||
|  |                 balance, | ||||||
|  |                 category, | ||||||
|  |                 attr, | ||||||
|  |                 details ?? InventoryDetails.Create(balance), | ||||||
|  |                 inventory?? InventoryData.Create(new LimitedBalance() { amount = balance}), | ||||||
|  |                 stamp, | ||||||
|  |                 stamp | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 获取道具 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="method"></param> | ||||||
|  |         /// <param name="amount"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public InventoryItem Acquire(TransactionMethod method, int amount) | ||||||
|  |         { | ||||||
|  |             var now = InventoryManager.CurrentTimeInMillis; | ||||||
|  |             var recycled = Inventory.RecycleExpiredBalance(); | ||||||
|  |             var target = balance + amount; | ||||||
|  |             var newBalance = Math.Clamp(target - recycled.expiredBalance, 0, target); | ||||||
|  |             return new InventoryItem(this.sku, newBalance,this.category, this.attr,  | ||||||
|  |                 details.Acquire(method, amount), | ||||||
|  |                 recycled.InventoryData, | ||||||
|  |                 createAt, now); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 消耗道具 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="scene"></param> | ||||||
|  |         /// <param name="amount"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public ConsumeResult Consume(string scene, int amount) | ||||||
|  |         { | ||||||
|  |             var now = InventoryManager.CurrentTimeInMillis; | ||||||
|  |             var recycled = Inventory.RecycleExpiredBalance(); | ||||||
|  |             var target = Math.Clamp(balance - recycled.expiredBalance, 0, balance); | ||||||
|  |             if (target > amount && attr == DetailsAttr.Consumable) | ||||||
|  |             { | ||||||
|  |                 target -= amount; | ||||||
|  |                 return ConsumeResult.Success(new InventoryItem(sku, target, category, attr, | ||||||
|  |                     details.Consume(scene, amount), recycled.InventoryData, createAt, now)); | ||||||
|  |             } | ||||||
|  |             return ConsumeResult.Error(new InventoryItem( | ||||||
|  |                 sku, target, category, attr, details, recycled.InventoryData, createAt, now)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     [Serializable] | ||||||
|  |     public class InventoryDetails: IJsonData | ||||||
|  |     { | ||||||
|  |         [JsonProperty("a")] | ||||||
|  |         public Dictionary<TransactionMethod, int> acquired { get; set; }  | ||||||
|  |          | ||||||
|  |         [JsonProperty("c")] | ||||||
|  |         public Dictionary<string, int> consumed { get; set; } | ||||||
|  |          | ||||||
|  |         [JsonProperty("d")] | ||||||
|  |         public Dictionary<string, dynamic> data { get; set; } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public InventoryDetails() | ||||||
|  |         { | ||||||
|  |             acquired = new Dictionary<TransactionMethod, int>(10); | ||||||
|  |             consumed = new Dictionary<string, int>(10); | ||||||
|  |             data = new Dictionary<string, dynamic>(10); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static InventoryDetails Create(int amount) | ||||||
|  |         { | ||||||
|  |             var d = new InventoryDetails(); | ||||||
|  |             if (amount > 0) | ||||||
|  |             { | ||||||
|  |                 d.acquired[TransactionMethod.unknown] = amount; | ||||||
|  |             } | ||||||
|  |             return d; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public InventoryDetails Acquire(TransactionMethod method, int amount) | ||||||
|  |         { | ||||||
|  |             if (acquired.TryGetValue(method, out var a)) | ||||||
|  |             { | ||||||
|  |                 acquired[method] = a + amount; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 acquired[method] = amount; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public InventoryDetails Consume(string scene, int amount) | ||||||
|  |         { | ||||||
|  |             if (consumed.TryGetValue(scene, out var a)) | ||||||
|  |             { | ||||||
|  |                 consumed[scene] = a + amount; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 consumed[scene] = amount; | ||||||
|  |             } | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 从 JSON 中解析 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="json"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public InventoryDetails FromJson(string json) | ||||||
|  |         { | ||||||
|  |             if (JsonDataHelper.Parse<InventoryDetails>(json, out var d)) | ||||||
|  |             { | ||||||
|  |                 return d; | ||||||
|  |             } | ||||||
|  |             return new InventoryDetails(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void SetValue(String key, object value) { | ||||||
|  |             data[key] = value; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public int GetInt(string key, int defaultValue = 0) | ||||||
|  |         { | ||||||
|  |             if(data.TryGetValue(key, out var value)) | ||||||
|  |             { | ||||||
|  |                 return (int)value; | ||||||
|  |             } | ||||||
|  |             return defaultValue;       | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public double GetDouble(string key, double defaultValue = 0) | ||||||
|  |         { | ||||||
|  |             if(data.TryGetValue(key, out var value)) | ||||||
|  |             { | ||||||
|  |                 return (double)value; | ||||||
|  |             } | ||||||
|  |             return defaultValue;       | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public string GetString(string key, string defaultValue = "") | ||||||
|  |         { | ||||||
|  |             if(data.TryGetValue(key, out var value)) | ||||||
|  |             { | ||||||
|  |                 return (string)value; | ||||||
|  |             } | ||||||
|  |             return defaultValue;       | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public bool GetBool(string key, bool defaultValue = false) | ||||||
|  |         { | ||||||
|  |             if(data.TryGetValue(key, out var value)) | ||||||
|  |             { | ||||||
|  |                 return (bool)value; | ||||||
|  |             } | ||||||
|  |             return defaultValue;       | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /// <summary> | ||||||
|  |     /// 内置物品表维护器 | ||||||
|  |     /// </summary> | ||||||
|  |     internal class InventoryTable | ||||||
|  |     { | ||||||
|  |          | ||||||
|  |         internal const string tbName = "inventory"; // Product Transaction Table | ||||||
|  |         internal const string dbSku = "sku"; | ||||||
|  |         internal const string dbBalance = "balance"; | ||||||
|  |         internal const string dbCategory = "cat"; | ||||||
|  |         internal const string dbAttr = "attr"; | ||||||
|  |         internal const string dbDetails = "details"; | ||||||
|  |         internal const string dbTimeSensitive = "tsv"; | ||||||
|  |         internal const string dbUpdateAt = "update_at"; | ||||||
|  |         internal const string dbCreateAt = "create_at"; | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         public static InventoryTable LoadOrCreate(DBService db) | ||||||
|  |         { | ||||||
|  |             var table = new InventoryTable(); | ||||||
|  |             table.Setup(db); | ||||||
|  |             return table; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private List<inventory> _dataList; | ||||||
|  |          | ||||||
|  |         private DBService _db; | ||||||
|  |         private void Setup(DBService db) | ||||||
|  |         { | ||||||
|  |             _db = db; | ||||||
|  |             Refresh(); | ||||||
|  |              | ||||||
|  |             // 执行命令 | ||||||
|  |             // _db.Execute($"CREATE INDEX inventory_item_idx ON {tbName} ({dbSku});", ts => | ||||||
|  |             // { | ||||||
|  |             //     _db.Execute($"CREATE INDEX inventory_item_category_idx ON {tbName} ({dbCategory});"); | ||||||
|  |             // }); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Refresh() | ||||||
|  |         { | ||||||
|  |             _dataList = _db.GetTableList<inventory>(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private bool HasItem(string id) | ||||||
|  |         { | ||||||
|  |             return _dataList.Exists(c => c.id == id); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 更新道具 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="items"></param> | ||||||
|  |         public void UpdateInventoryItems(List<InventoryItem> items) | ||||||
|  |         { | ||||||
|  |             List<inventory> updates = new List<inventory>(items.Count); | ||||||
|  |             List<inventory> insets = new List<inventory>(items.Count); | ||||||
|  |             InventoryItem item; | ||||||
|  |             inventory inv; | ||||||
|  |             for (int i = 0; i < items.Count; i++) | ||||||
|  |             { | ||||||
|  |                 item = items[i]; | ||||||
|  |                 inv = inventory.Create(item); | ||||||
|  |                 if (!HasItem(item.sku)) | ||||||
|  |                 { | ||||||
|  |                     insets.Add(inv); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     updates.Add(inv); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             _db.InsertAll(insets); | ||||||
|  |             _db.UpdateAll(updates); | ||||||
|  |              | ||||||
|  |             Refresh(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public InventoryItem GetItem(string sku) | ||||||
|  |         { | ||||||
|  |             if (HasItem(sku)) | ||||||
|  |             { | ||||||
|  |                 var inv = _dataList.Find(c => c.sku == sku); | ||||||
|  |                 if (inv != null) return inv.ToItem(); | ||||||
|  |             } | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// 表头及数据结构 | ||||||
|  |     /// </summary> | ||||||
|  |     internal class inventory | ||||||
|  |     { | ||||||
|  |         [Indexed(InventoryTable.dbSku, 1)] | ||||||
|  |         public string sku { get; set; } | ||||||
|  |         public int balance { get; set; } | ||||||
|  |         [Indexed(InventoryTable.dbCategory, 2)] | ||||||
|  |         public string cat{ get; set; } | ||||||
|  |         public int attr { get; set; } | ||||||
|  |         public string tsv { get; set; } | ||||||
|  |         public long update_at{ get; set; } | ||||||
|  |         public long create_at{ get; set; } | ||||||
|  | 
 | ||||||
|  |         [PrimaryKey] | ||||||
|  |         public string id => sku; | ||||||
|  | 
 | ||||||
|  |         public static inventory Create(InventoryItem item) | ||||||
|  |         { | ||||||
|  |             return new inventory() | ||||||
|  |             { | ||||||
|  |                 sku = item.sku, | ||||||
|  |                 balance = item.balance, | ||||||
|  |                 cat = item.category, | ||||||
|  |                 attr = item.attr, | ||||||
|  |                 tsv = item.Inventory.ToJson(), | ||||||
|  |                 update_at = item.updateAt, | ||||||
|  |                 create_at = item.createAt | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public InventoryItem ToItem() | ||||||
|  |         { | ||||||
|  |             return InventoryItem.Create( | ||||||
|  |                 sku, cat, attr, balance, | ||||||
|  |                 null, | ||||||
|  |                 InventoryData.FromJson(tsv), | ||||||
|  |                 this.update_at, | ||||||
|  |                 this.create_at); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 21e2bced768541b5befa3b4397783424 | ||||||
|  | timeCreated: 1706944831 | ||||||
|  | @ -0,0 +1,47 @@ | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using Unity.Plastic.Newtonsoft.Json; | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |      | ||||||
|  |     public class DetailsAttr | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// 永久物品 | ||||||
|  |         /// </summary> | ||||||
|  |         public const int Permanent = 1; | ||||||
|  |         /// <summary> | ||||||
|  |         /// 可消耗 | ||||||
|  |         /// </summary> | ||||||
|  |         public const int Consumable = 2; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     public class ExtraReservedField { | ||||||
|  |         public const string scene = "__scene"; | ||||||
|  |         public const string offerId = "__offer_id"; | ||||||
|  |         public const string basePlanId = "__base_plan_id"; | ||||||
|  |         public const string sales = "__sales"; | ||||||
|  |         public const string rate = "__rate"; | ||||||
|  |         public const string contentId = "__content_id"; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /// <summary> | ||||||
|  |     /// 商品清单 | ||||||
|  |     /// </summary> | ||||||
|  |     public class Manifest: IJsonData | ||||||
|  |     { | ||||||
|  |          | ||||||
|  |         [JsonProperty("category")] | ||||||
|  |         public string category; | ||||||
|  | 
 | ||||||
|  |         [JsonProperty("extra")] | ||||||
|  |         public Dictionary<string, dynamic> extra; | ||||||
|  | 
 | ||||||
|  |         public string scene => extra.TryGetValue(ExtraReservedField.scene, out var v)? v : ""; | ||||||
|  |          | ||||||
|  |         public string basePlanId => extra.TryGetValue(ExtraReservedField.basePlanId, out var v)? v : ""; | ||||||
|  |          | ||||||
|  |         public string offerId => extra.TryGetValue(ExtraReservedField.offerId, out var v)? v : ""; | ||||||
|  |         public string contentId => extra.TryGetValue(ExtraReservedField.contentId, out var v)? v : ""; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: a1e83edde24c4a46bd8ce00aaf09f273 | ||||||
|  | timeCreated: 1706945145 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: c176d24ea24445f9ab67763aa8ef3f7c | ||||||
|  | timeCreated: 1706925964 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 42b7e7382b1d45d39f1542ed00f4e890 | ||||||
|  | timeCreated: 1706923167 | ||||||
|  | @ -0,0 +1,190 @@ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using SQLite4Unity3d; | ||||||
|  |     using UnityEngine; | ||||||
|  |     using System.Collections; | ||||||
|  |     using System; | ||||||
|  |     using System.IO; | ||||||
|  |     using System.Collections.Generic; | ||||||
|  |     using System.Linq; | ||||||
|  |     using System.Linq.Expressions; | ||||||
|  |     using UnityEngine.Networking; | ||||||
|  |      | ||||||
|  |     public class DBService | ||||||
|  |     { | ||||||
|  | 	    internal const string DefaultDBName = "data"; | ||||||
|  | 	    internal const string DefaultDBExtension = ".db"; | ||||||
|  | 	    internal static string DebugDBPath = "mocks_dir/db"; | ||||||
|  | 	    private SQLiteConnection _connection; | ||||||
|  |         private string _dbName; | ||||||
|  |         private string _dbPath; | ||||||
|  | 		private bool _isDebug = false; | ||||||
|  | 		 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// 获取数据库的路径 | ||||||
|  | 		/// </summary> | ||||||
|  | 		public string DatabasePath => _dbPath; | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 启动服务 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="name"></param> | ||||||
|  |         /// <param name="isDebug"></param> | ||||||
|  |         public DBService(string name, bool isDebug = false) | ||||||
|  |         { | ||||||
|  | 	        _isDebug = isDebug; | ||||||
|  | 	        if (string.IsNullOrEmpty(name)) name = DefaultDBName; | ||||||
|  | 	        _dbName = name; | ||||||
|  | 	        _dbPath = Path.GetFullPath($"{Application.persistentDataPath}/{_dbName}{DefaultDBExtension}"); | ||||||
|  | 
 | ||||||
|  | 	        if (isDebug) | ||||||
|  | 	        { | ||||||
|  | 		        _dbPath = Path.GetFullPath($"{Application.persistentDataPath}/{_dbName}_debug{DefaultDBExtension}"); | ||||||
|  | 	        } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public static DBService Open(string name, bool isDebug = false) | ||||||
|  |         { | ||||||
|  | 	        var ds = new DBService(name, isDebug); | ||||||
|  | 	        ds.Connect(); | ||||||
|  | 	        return ds; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// 关闭连接 | ||||||
|  |         /// </summary> | ||||||
|  |         public void Close() => _connection?.Close(); | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  | 		/// 建立连接, 若不存在则创建数据库 | ||||||
|  | 		/// </summary> | ||||||
|  |         public void Connect(string dbPath = "") | ||||||
|  |         { | ||||||
|  | 	        if(!string.IsNullOrEmpty(dbPath)) _dbPath = dbPath; | ||||||
|  | 	        _connection = new SQLiteConnection(_dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool Exists => File.Exists(_dbPath); | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// 部署默认的 Database | ||||||
|  | 		/// 将内置在 StreamingAssets 中的数据库文件部署到 PersistentDataPath 中 | ||||||
|  | 		/// </summary> | ||||||
|  | 		/// <returns></returns> | ||||||
|  |         public void DeployEmbeddedDB(string embeddedPath, Action<bool> onComplete) | ||||||
|  |         { | ||||||
|  | 	        string from = Path.Combine(Application.streamingAssetsPath, embeddedPath); | ||||||
|  | 
 | ||||||
|  | 	        var uwr = new UnityWebRequest(from); | ||||||
|  | 	        uwr.SendWebRequest().completed += ao => | ||||||
|  | 	        { | ||||||
|  | 		        var success = false; | ||||||
|  | 		        try | ||||||
|  | 		        { | ||||||
|  | 			        if ( uwr.result == UnityWebRequest.Result.Success) | ||||||
|  | 			        { | ||||||
|  | 				        File.WriteAllBytes(_dbPath, uwr.downloadHandler.data); | ||||||
|  | 				        success = true; | ||||||
|  | 			        } | ||||||
|  | 		        } | ||||||
|  | 		        catch (Exception e) | ||||||
|  | 		        { | ||||||
|  | 			        Debug.LogError(e); | ||||||
|  | 		        } | ||||||
|  | 		        onComplete?.Invoke(success); | ||||||
|  | 	        }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// 获取表格 | ||||||
|  | 		/// </summary> | ||||||
|  | 		/// <typeparam name="T"></typeparam> | ||||||
|  | 		/// <returns></returns> | ||||||
|  |         public IEnumerable<T> CreatOrLoadTable<T>() where T: new() | ||||||
|  |         { | ||||||
|  | 	       _connection.CreateTable<T>(); | ||||||
|  | 	       return  _connection.Table<T>(); | ||||||
|  |         } | ||||||
|  | 		 | ||||||
|  |         public List<T> GetTableList<T>()  where T: new() | ||||||
|  |         { | ||||||
|  | 	        var tb = CreatOrLoadTable<T>(); | ||||||
|  | 	        return tb?.ToList() ?? new List<T>(); | ||||||
|  |         } | ||||||
|  | 		 | ||||||
|  | 		 | ||||||
|  | 		 | ||||||
|  |         public int Insert<T>(T value) | ||||||
|  |         { | ||||||
|  | 	        return _connection.Insert(value); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public int InsertAll(IEnumerable data) | ||||||
|  |         { | ||||||
|  | 	        return _connection.InsertAll(data); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public int Remove(object primaryKey) | ||||||
|  |         { | ||||||
|  | 	        return _connection.Delete(primaryKey); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public int Remove<T>(object primaryKey) | ||||||
|  |         { | ||||||
|  | 	        return _connection.Delete<T>(primaryKey); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public int UpdateAll(IEnumerable data) | ||||||
|  |         { | ||||||
|  | 	        return _connection.UpdateAll(data); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public T Get<T>(object primaryKey) where T : new() | ||||||
|  |         { | ||||||
|  | 	        return _connection.Get<T>(primaryKey); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public T Find<T>(Expression<Func<T, bool>> predicate) where T : new() | ||||||
|  |         { | ||||||
|  | 	        return _connection.Find<T>(predicate); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private bool _onCmdExecution = false; | ||||||
|  |         /// <summary> | ||||||
|  |         /// 执行 SQL 语句 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="query"></param> | ||||||
|  |         /// <param name="onExecutionComplete"></param> | ||||||
|  |         /// <param name="args"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public int Execute(string query, Action<double> onExecutionComplete = null,  params object[] args) | ||||||
|  |         { | ||||||
|  | 	        if (_onCmdExecution) return -1; | ||||||
|  | 	         | ||||||
|  | 	        SQLiteConnection.TimeExecutionHandler del = null; | ||||||
|  | 	        del = (t1, t2) => | ||||||
|  | 	        { | ||||||
|  | 		        _onCmdExecution = false; | ||||||
|  | 		        _connection.TimeExecutionEvent -= del; | ||||||
|  | 		        onExecutionComplete?.Invoke(t2.TotalSeconds); | ||||||
|  | 	        }; | ||||||
|  | 	        _connection.TimeExecutionEvent += del; | ||||||
|  | 	        _onCmdExecution = true; | ||||||
|  | 	        return _connection.Execute(query, args); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |      | ||||||
|  |      | ||||||
|  |      | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 230a328bcc91476ea24d47a0ecbdf441 | ||||||
|  | timeCreated: 1706926988 | ||||||
|  | @ -0,0 +1,54 @@ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | using SQLite4Unity3d; | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using System.Collections.Generic; | ||||||
|  |      | ||||||
|  |      | ||||||
|  |      | ||||||
|  |     public class GuruDB | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         private static GuruDB _instance; | ||||||
|  |         public static GuruDB Instance | ||||||
|  |         { | ||||||
|  |             get | ||||||
|  |             { | ||||||
|  |                 if(_instance == null) _instance = new GuruDB(); | ||||||
|  |                 return _instance; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public int dbVersion = 1; | ||||||
|  |         public string dbName = "guru"; | ||||||
|  |         private DBService _dbService; | ||||||
|  |         internal DBService Service => _dbService; | ||||||
|  |         public bool IsDebug { get; private set; } = false; | ||||||
|  |         public bool IsReady { get; private set; } | ||||||
|  | 
 | ||||||
|  |         public GuruDB() | ||||||
|  |         { | ||||||
|  | #if UNITY_EDITOR | ||||||
|  |             IsDebug = true; | ||||||
|  | #endif | ||||||
|  |             _dbService = DBService.Open(dbName, IsDebug); | ||||||
|  | 
 | ||||||
|  |             IsReady = _dbService != null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public interface IDBItem | ||||||
|  |     { | ||||||
|  |         [PrimaryKey] | ||||||
|  |         string id { get; set; } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 1c3e8542bfad4731b0c54abf3afe7cf3 | ||||||
|  | timeCreated: 1706923244 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: efa1c022e13449558b8d4d620fae4e69 | ||||||
|  | timeCreated: 1706927602 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 84ef5b243482457ab96d4c376fec23d6 | ||||||
|  | timeCreated: 1706949578 | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     public interface IJsonData | ||||||
|  |     { | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 68451c547db14e748cb50b5e773c6f00 | ||||||
|  | timeCreated: 1706949587 | ||||||
|  | @ -0,0 +1,62 @@ | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using UnityEngine; | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     using Newtonsoft.Json; | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  |      | ||||||
|  |     public static class JsonDataHelper | ||||||
|  |     { | ||||||
|  |         public static Action<Exception> ExceptionHandler; | ||||||
|  | 
 | ||||||
|  |          | ||||||
|  |         public static string ToJsonString(object obj) | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 return JsonConvert.SerializeObject(obj); | ||||||
|  |             } | ||||||
|  |             catch (Exception e) | ||||||
|  |             { | ||||||
|  |                 ExceptionHandler?.Invoke(e); | ||||||
|  |                 Debug.LogError(e); | ||||||
|  |             } | ||||||
|  |             return ""; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public static bool Parse<T>(string json, out T result) | ||||||
|  |         { | ||||||
|  |             bool success = false; | ||||||
|  |             result = default(T); | ||||||
|  |              | ||||||
|  |             if(string.IsNullOrEmpty(json)) return false; | ||||||
|  |              | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 result =  JsonConvert.DeserializeObject<T>(json); | ||||||
|  |                 success = true; | ||||||
|  |             } | ||||||
|  |             catch (Exception e) | ||||||
|  |             { | ||||||
|  |                 ExceptionHandler?.Invoke(e); | ||||||
|  |                 Debug.LogError(e); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return success; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public static string ToJson(this IJsonData  obj) | ||||||
|  |         { | ||||||
|  |             return ToJsonString((object)obj); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: ce50b36309b34a41b8bb844efbba1a17 | ||||||
|  | timeCreated: 1706949612 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 043ca2bcb28a45fba7edf46e5819a53b | ||||||
|  | timeCreated: 1706923871 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: e4425fb6790b4f16b10a6ac9545f087c | ||||||
|  | timeCreated: 1706925944 | ||||||
|  | @ -0,0 +1,48 @@ | ||||||
|  | using System; | ||||||
|  | 
 | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |      | ||||||
|  |     public partial class PropertyDatabase | ||||||
|  |     { | ||||||
|  |          | ||||||
|  |          | ||||||
|  |          | ||||||
|  |          | ||||||
|  |          | ||||||
|  |          | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     [Serializable] | ||||||
|  |     public class PropertyEntity | ||||||
|  |     { | ||||||
|  |         const string TableName = "properties"; | ||||||
|  |         // internal const string dbKey = "key"; | ||||||
|  |         // internal const string dbValue = "value"; | ||||||
|  |         // internal const string dbGroup = "gp"; | ||||||
|  |         // internal const string dbUsage = "usage"; | ||||||
|  |         // internal const string dbTag = "tag"; | ||||||
|  |         // internal const string dbUpdateAt = "upt"; | ||||||
|  | 
 | ||||||
|  |         public string key; | ||||||
|  |         public string value; | ||||||
|  |         public string gp = PropertyKey.DefaultGroup; | ||||||
|  |         public int usage = PropertyKey.UsageGeneral; | ||||||
|  |         public string tag; | ||||||
|  |         public string upt; | ||||||
|  |         public int updateAt = 0; | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         | ||||||
|  |          | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 357ec7a04d0a454086a09bd2f2fcfe79 | ||||||
|  | timeCreated: 1706926072 | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     public struct PropertyKey | ||||||
|  |     { | ||||||
|  |         public const int UsageGeneral = 0; | ||||||
|  |         public const int UsageSettings = 1; | ||||||
|  |         public const string DefaultGroup = "guru"; | ||||||
|  | 
 | ||||||
|  |         public string name { get; private set; } | ||||||
|  |         public string key { get; private set; } | ||||||
|  |         public string group { get; private set; } | ||||||
|  |         public string tag { get; private set; } | ||||||
|  |         public int usage { get; private set; } | ||||||
|  |          | ||||||
|  |         public static PropertyKey General(string name, string group = DefaultGroup, string tag = "") | ||||||
|  |         { | ||||||
|  |             return new PropertyKey | ||||||
|  |             { | ||||||
|  |                 name = name, | ||||||
|  |                 key = $"{group}@{name}", | ||||||
|  |                 group = group, | ||||||
|  |                 tag = tag, | ||||||
|  |                 usage = UsageGeneral | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         public static PropertyKey Setting(string name, string group = DefaultGroup, string tag = "") | ||||||
|  |         { | ||||||
|  |             return new PropertyKey | ||||||
|  |             { | ||||||
|  |                 name = name, | ||||||
|  |                 key = $"{group}@{name}", | ||||||
|  |                 group = group, | ||||||
|  |                 tag = tag, | ||||||
|  |                 usage = UsageSettings | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         /// 判断两个属性 Key 是否相等 | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="other"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public  bool Equals(PropertyKey other) | ||||||
|  |         { | ||||||
|  |             return name == other.name &&  | ||||||
|  |                    key == other.key &&  | ||||||
|  |                    group == other.group &&  | ||||||
|  |                    tag == other.tag && | ||||||
|  |                    usage == other.usage; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override string ToString() | ||||||
|  |         { | ||||||
|  |             return $"#{tag}#[{key}]({usage})"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 5b1517229c974c46ac85c3c8f8b3b330 | ||||||
|  | timeCreated: 1706924244 | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: d50e6dd9a1324036af9e950f5ec489ed | ||||||
|  | timeCreated: 1706925986 | ||||||
|  | @ -0,0 +1,19 @@ | ||||||
|  | namespace Guru | ||||||
|  | { | ||||||
|  |     public interface IPropertyStorage | ||||||
|  |     { | ||||||
|  |         void SetDouble(PropertyKey key, double value); | ||||||
|  |         void SetInt(PropertyKey key, int value); | ||||||
|  |         void SetBool(PropertyKey key, bool value); | ||||||
|  |         void SetString(PropertyKey key, string value); | ||||||
|  |         double? GetDouble(PropertyKey key, double? defaultValue); | ||||||
|  |         int? GetInt(PropertyKey key, int? defaultValue); | ||||||
|  |         bool? GetBool(PropertyKey key, bool? defaultValue); | ||||||
|  |         string GetString(PropertyKey key, string defaultValue); | ||||||
|  |          | ||||||
|  |         void remove(PropertyKey key); | ||||||
|  |         void removeAllWithTag(string tag); | ||||||
|  |          | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 761fa4520c9746b5b14e27078be64003 | ||||||
|  | timeCreated: 1706923906 | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 36d6b6e33eeef40509c8647e2d7c555d | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 4475ec4c51944461ba8479536dfcda61 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: f288c6262551743be968c8c546dd065e | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: c1aacb5a55fc94e4da727839f3ea4d24 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 8a0518887aa4d45798babfb2249cdd6d | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 035fff0fb0daa454f882f263a44d8a71 | ||||||
|  | PluginImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   iconMap: {} | ||||||
|  |   executionOrder: {} | ||||||
|  |   defineConstraints: [] | ||||||
|  |   isPreloaded: 0 | ||||||
|  |   isOverridable: 0 | ||||||
|  |   isExplicitlyReferenced: 0 | ||||||
|  |   validateReferences: 1 | ||||||
|  |   platformData: | ||||||
|  |   - first: | ||||||
|  |       Any:  | ||||||
|  |     second: | ||||||
|  |       enabled: 1 | ||||||
|  |       settings: {} | ||||||
|  |   - first: | ||||||
|  |       Editor: Editor | ||||||
|  |     second: | ||||||
|  |       enabled: 0 | ||||||
|  |       settings: | ||||||
|  |         DefaultValueInitialized: true | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: a8bf69720c6da40ff9d7ada1a6e52198 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: fbfa6b2ad32f74cb9ba69b1613f0ac8b | ||||||
|  | PluginImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   iconMap: {} | ||||||
|  |   executionOrder: {} | ||||||
|  |   defineConstraints: [] | ||||||
|  |   isPreloaded: 0 | ||||||
|  |   isOverridable: 0 | ||||||
|  |   isExplicitlyReferenced: 0 | ||||||
|  |   validateReferences: 1 | ||||||
|  |   platformData: | ||||||
|  |   - first: | ||||||
|  |       Any:  | ||||||
|  |     second: | ||||||
|  |       enabled: 1 | ||||||
|  |       settings: {} | ||||||
|  |   - first: | ||||||
|  |       Editor: Editor | ||||||
|  |     second: | ||||||
|  |       enabled: 0 | ||||||
|  |       settings: | ||||||
|  |         DefaultValueInitialized: true | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: c0bc4319db8fc4e87b92d8849e10effa | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 533c36573354e4e82a0871a3ae353c64 | ||||||
|  | PluginImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   iconMap: {} | ||||||
|  |   executionOrder: {} | ||||||
|  |   defineConstraints: [] | ||||||
|  |   isPreloaded: 0 | ||||||
|  |   isOverridable: 0 | ||||||
|  |   isExplicitlyReferenced: 0 | ||||||
|  |   validateReferences: 1 | ||||||
|  |   platformData: | ||||||
|  |   - first: | ||||||
|  |       Any:  | ||||||
|  |     second: | ||||||
|  |       enabled: 1 | ||||||
|  |       settings: {} | ||||||
|  |   - first: | ||||||
|  |       Editor: Editor | ||||||
|  |     second: | ||||||
|  |       enabled: 0 | ||||||
|  |       settings: | ||||||
|  |         DefaultValueInitialized: true | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 87d29778372204305920a77087f262a4 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 25884d71c32604d7987793cbcbe801ac | ||||||
|  | PluginImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   iconMap: {} | ||||||
|  |   executionOrder: {} | ||||||
|  |   defineConstraints: [] | ||||||
|  |   isPreloaded: 0 | ||||||
|  |   isOverridable: 0 | ||||||
|  |   isExplicitlyReferenced: 0 | ||||||
|  |   validateReferences: 1 | ||||||
|  |   platformData: | ||||||
|  |   - first: | ||||||
|  |       Any:  | ||||||
|  |     second: | ||||||
|  |       enabled: 1 | ||||||
|  |       settings: {} | ||||||
|  |   - first: | ||||||
|  |       Editor: Editor | ||||||
|  |     second: | ||||||
|  |       enabled: 0 | ||||||
|  |       settings: | ||||||
|  |         CPU: x86_64 | ||||||
|  |         DefaultValueInitialized: true | ||||||
|  |   - first: | ||||||
|  |       Standalone: Linux64 | ||||||
|  |     second: | ||||||
|  |       enabled: 1 | ||||||
|  |       settings: | ||||||
|  |         CPU: x86_64 | ||||||
|  |   - first: | ||||||
|  |       Standalone: OSXUniversal | ||||||
|  |     second: | ||||||
|  |       enabled: 1 | ||||||
|  |       settings: | ||||||
|  |         CPU: x86_64 | ||||||
|  |   - first: | ||||||
|  |       Standalone: Win | ||||||
|  |     second: | ||||||
|  |       enabled: 0 | ||||||
|  |       settings: | ||||||
|  |         CPU: None | ||||||
|  |   - first: | ||||||
|  |       Standalone: Win64 | ||||||
|  |     second: | ||||||
|  |       enabled: 1 | ||||||
|  |       settings: | ||||||
|  |         CPU: x86_64 | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: f7a5439d4fb9e4828937865037857668 | ||||||
|  | folderAsset: yes | ||||||
|  | DefaultImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 8f6a8ec41de2b4f569e23b20011eec8c | ||||||
|  | PluginImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   iconMap: {} | ||||||
|  |   executionOrder: {} | ||||||
|  |   defineConstraints: [] | ||||||
|  |   isPreloaded: 0 | ||||||
|  |   isOverridable: 0 | ||||||
|  |   isExplicitlyReferenced: 0 | ||||||
|  |   validateReferences: 1 | ||||||
|  |   platformData: | ||||||
|  |   - first: | ||||||
|  |       Any:  | ||||||
|  |     second: | ||||||
|  |       enabled: 1 | ||||||
|  |       settings: {} | ||||||
|  |   - first: | ||||||
|  |       Editor: Editor | ||||||
|  |     second: | ||||||
|  |       enabled: 0 | ||||||
|  |       settings: | ||||||
|  |         CPU: x86 | ||||||
|  |         DefaultValueInitialized: true | ||||||
|  |   - first: | ||||||
|  |       Standalone: Linux64 | ||||||
|  |     second: | ||||||
|  |       enabled: 0 | ||||||
|  |       settings: | ||||||
|  |         CPU: None | ||||||
|  |   - first: | ||||||
|  |       Standalone: OSXUniversal | ||||||
|  |     second: | ||||||
|  |       enabled: 0 | ||||||
|  |       settings: | ||||||
|  |         CPU: x86 | ||||||
|  |   - first: | ||||||
|  |       Standalone: Win | ||||||
|  |     second: | ||||||
|  |       enabled: 1 | ||||||
|  |       settings: | ||||||
|  |         CPU: x86 | ||||||
|  |   - first: | ||||||
|  |       Standalone: Win64 | ||||||
|  |     second: | ||||||
|  |       enabled: 0 | ||||||
|  |       settings: | ||||||
|  |         CPU: None | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | fileFormatVersion: 2 | ||||||
|  | guid: 26aa670e0ee4a47a8bb1d3dfcbec8533 | ||||||
|  | MonoImporter: | ||||||
|  |   externalObjects: {} | ||||||
|  |   serializedVersion: 2 | ||||||
|  |   defaultReferences: [] | ||||||
|  |   executionOrder: 0 | ||||||
|  |   icon: {instanceID: 0} | ||||||
|  |   userData:  | ||||||
|  |   assetBundleName:  | ||||||
|  |   assetBundleVariant:  | ||||||
		Loading…
	
		Reference in New Issue