Compare commits
1 Commits
main
...
feature/In
| Author | SHA1 | Date |
|---|---|---|
|
|
3f10cf718e |
|
|
@ -114,6 +114,7 @@ namespace Guru
|
|||
// 经济相关
|
||||
public static readonly string ParameterBalance = "balance"; // 用于余额
|
||||
public static readonly string ParameterSku = "sku"; // sku
|
||||
public static readonly string ParameterScene = "scene"; // sku
|
||||
public static readonly string ParameterVirtualCurrencyName = "virtual_currency_name"; // 虚拟货币名称
|
||||
}
|
||||
}
|
||||
|
|
@ -72,65 +72,59 @@ namespace Guru
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// 获取虚拟货币
|
||||
/// 获取道具/货币
|
||||
/// </summary>
|
||||
/// <param name="currencyName"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="balance"></param>
|
||||
/// <param name="virtualCurrencyName"></param>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="levelName"></param>
|
||||
/// <param name="isIap"></param>
|
||||
/// <param name="sku"></param>
|
||||
/// <param name="scene"></param>
|
||||
public static void EarnVirtualCurrency(string currencyName, int value, int balance,
|
||||
string method = "",
|
||||
string levelName = "",
|
||||
bool isIap = false,
|
||||
string sku = "",
|
||||
string scene = "")
|
||||
/// <param name="balance"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="methodDetails"></param>
|
||||
public static void EarnVirtualCurrency(string virtualCurrencyName, string method, int balance, int value,
|
||||
string methodDetails = "")
|
||||
{
|
||||
if (isIap) method = "iap_buy";
|
||||
var data = new Dictionary<string, dynamic>()
|
||||
{
|
||||
{ ParameterVirtualCurrencyName, currencyName },
|
||||
{ ParameterVirtualCurrencyName, virtualCurrencyName },
|
||||
{ ParameterItemCategory, method },
|
||||
{ ParameterItemName, methodDetails },
|
||||
{ ParameterValue, value },
|
||||
{ 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 });
|
||||
|
||||
// FB 上报收入点
|
||||
FB.LogAppEvent(EventEarnVirtualCurrency, value, data);
|
||||
// FB.LogAppEvent(EventEarnVirtualCurrency, value, data);
|
||||
}
|
||||
|
||||
|
||||
public static void SpendVirtualCurrency(string currencyName, int value, int balance,
|
||||
string method = "",
|
||||
string levelName = "",
|
||||
string scene = "")
|
||||
/// <summary>
|
||||
/// 消耗道具/货币
|
||||
/// </summary>
|
||||
/// <param name="contentId"></param>
|
||||
/// <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>()
|
||||
{
|
||||
{ ParameterVirtualCurrencyName, currencyName },
|
||||
{ ParameterValue, value },
|
||||
{ ParameterVirtualCurrencyName, virtualCurrencyName },
|
||||
{ ParameterValue, price },
|
||||
{ ParameterBalance, balance },
|
||||
{ ParameterLevelName, levelName },
|
||||
{ ParameterItemCategory, method },
|
||||
{ ParameterItemName, contentId },
|
||||
{ ParameterItemCategory, contentType },
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(scene)) data[ParameterItemName] = scene; // 获取的虚拟货币或者道具的场景
|
||||
if (!string.IsNullOrEmpty(scene)) data[ParameterScene] = scene; // 获取的虚拟货币或者道具的场景
|
||||
|
||||
LogEvent(EventSpendVirtualCurrency, data, new EventSetting() { EnableFirebaseAnalytics = true });
|
||||
|
||||
// FB 上报消费点
|
||||
FB.LogAppEvent(EventSpendVirtualCurrency, value, data);
|
||||
// FB.LogAppEvent(EventSpendVirtualCurrency, value, data);
|
||||
|
||||
// FB 上报消耗事件买量点
|
||||
FBSpentCredits(value, scene, method); // 点位信息有变化
|
||||
FBSpentCredits(contentId, contentType, price); // 点位信息有变化
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -140,7 +134,7 @@ namespace Guru
|
|||
/// <param name="amount"></param>
|
||||
/// <param name="contentId"></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,
|
||||
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