the files
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
using LiteDB;
|
||||
|
||||
namespace Campus_live_rewrite.api.dbs
|
||||
{
|
||||
/// <summary>
|
||||
/// The room economy a creator sets up in Room Settings: room KEYS (a one-off pass sold for
|
||||
/// tokens or for a room currency), room CURRENCIES (with the purchase offers that sell them
|
||||
/// for tokens), and room CONSUMABLES (stackable items sold for tokens or a room currency).
|
||||
/// Plus who owns what: keys per player, currency balances per player, consumable counts per
|
||||
/// player. Every route that used to answer "[]" or an ack reads and writes this.
|
||||
///
|
||||
/// DTO shapes follow the wire-shape notes (roomkeys-v1-room, roomcurrencies-createcurrency,
|
||||
/// roomcurrencies-purchaseoffersbatch, roomconsumables-by-room).
|
||||
/// </summary>
|
||||
public static class RoomEconDB
|
||||
{
|
||||
private static readonly Lazy<LiteDatabase> _db = new(() => new LiteDatabase(Path.Combine("Data", "DBs", "RoomEcon.db")));
|
||||
|
||||
public class RoomKey
|
||||
{
|
||||
[BsonId] public long RoomKeyId { get; set; }
|
||||
public Guid ReplicationId { get; set; } = Guid.NewGuid();
|
||||
public long RoomId { get; set; }
|
||||
public ulong CreatorId { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public string Description { get; set; } = "";
|
||||
public int Price { get; set; }
|
||||
public Guid? PurchaseCurrencyId { get; set; }
|
||||
public string? ImageName { get; set; }
|
||||
public int Type { get; set; }
|
||||
public bool Deleted { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public class RoomCurrency
|
||||
{
|
||||
[BsonId] public Guid CurrencyId { get; set; } = Guid.NewGuid();
|
||||
public long RoomId { get; set; }
|
||||
public ulong CreatorId { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public string Description { get; set; } = "";
|
||||
public long Limit { get; set; } = 1_000_000;
|
||||
public int Shape { get; set; }
|
||||
public int Color { get; set; } = -1;
|
||||
public string? ImageName { get; set; }
|
||||
public bool Deleted { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime ModifiedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public class CurrencyOffer
|
||||
{
|
||||
[BsonId] public Guid CurrencyPurchaseOfferId { get; set; } = Guid.NewGuid();
|
||||
public Guid CurrencyId { get; set; }
|
||||
public long RoomId { get; set; }
|
||||
public int Order { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public long CurrencyAmount { get; set; }
|
||||
public long Price { get; set; }
|
||||
public bool Deleted { get; set; }
|
||||
public DateTime ModifiedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public class RoomConsumable
|
||||
{
|
||||
[BsonId] public Guid RoomConsumableId { get; set; } = Guid.NewGuid();
|
||||
public long RoomId { get; set; }
|
||||
public ulong CreatorId { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public string Description { get; set; } = "";
|
||||
public string? ImageName { get; set; }
|
||||
public long Price { get; set; }
|
||||
public Guid? PurchaseCurrencyId { get; set; }
|
||||
public int MaximumCountPerPurchase { get; set; } = 10;
|
||||
public bool Deleted { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime ModifiedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public class KeyOwnership
|
||||
{
|
||||
[BsonId] public string Key { get; set; } = ""; // "{player}|{roomKeyId}"
|
||||
public ulong PlayerId { get; set; }
|
||||
public long RoomKeyId { get; set; }
|
||||
public long RoomId { get; set; }
|
||||
public DateTime At { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public class CurrencyBalance
|
||||
{
|
||||
[BsonId] public string Key { get; set; } = ""; // "{player}|{currencyId}"
|
||||
public ulong PlayerId { get; set; }
|
||||
public Guid CurrencyId { get; set; }
|
||||
public long RoomId { get; set; }
|
||||
public long Balance { get; set; }
|
||||
}
|
||||
|
||||
public class ConsumableOwnership
|
||||
{
|
||||
[BsonId] public string Key { get; set; } = ""; // "{player}|{consumableId}"
|
||||
public ulong PlayerId { get; set; }
|
||||
public Guid RoomConsumableId { get; set; }
|
||||
public long RoomId { get; set; }
|
||||
public int Count { get; set; }
|
||||
public DateTime ModifiedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
private static ILiteCollection<RoomKey> Keys() { var c = _db.Value.GetCollection<RoomKey>("keys"); c.EnsureIndex(x => x.RoomId); return c; }
|
||||
private static ILiteCollection<RoomCurrency> Currencies() { var c = _db.Value.GetCollection<RoomCurrency>("currencies"); c.EnsureIndex(x => x.RoomId); return c; }
|
||||
private static ILiteCollection<CurrencyOffer> Offers() { var c = _db.Value.GetCollection<CurrencyOffer>("offers"); c.EnsureIndex(x => x.CurrencyId); return c; }
|
||||
private static ILiteCollection<RoomConsumable> Consumables() { var c = _db.Value.GetCollection<RoomConsumable>("consumables"); c.EnsureIndex(x => x.RoomId); return c; }
|
||||
private static ILiteCollection<KeyOwnership> KeysOwned() { var c = _db.Value.GetCollection<KeyOwnership>("keys_owned"); c.EnsureIndex(x => x.PlayerId); c.EnsureIndex(x => x.RoomKeyId); return c; }
|
||||
private static ILiteCollection<CurrencyBalance> Balances() { var c = _db.Value.GetCollection<CurrencyBalance>("balances"); c.EnsureIndex(x => x.PlayerId); return c; }
|
||||
private static ILiteCollection<ConsumableOwnership> ConsOwned() { var c = _db.Value.GetCollection<ConsumableOwnership>("consumables_owned"); c.EnsureIndex(x => x.PlayerId); c.EnsureIndex(x => x.RoomConsumableId); return c; }
|
||||
|
||||
// ---- keys ----------------------------------------------------------------------------
|
||||
|
||||
public static RoomKey CreateKey(RoomKey k)
|
||||
{
|
||||
var col = Keys();
|
||||
if (k.RoomKeyId == 0)
|
||||
{
|
||||
var last = col.Query().OrderByDescending(x => x.RoomKeyId).Limit(1).FirstOrDefault();
|
||||
k.RoomKeyId = Math.Max(900_000, (last?.RoomKeyId ?? 0) + 1);
|
||||
}
|
||||
col.Insert(k);
|
||||
return k;
|
||||
}
|
||||
public static RoomKey? GetKey(long id) { var k = Keys().FindById(id); return k == null || k.Deleted ? null : k; }
|
||||
public static List<RoomKey> KeysForRoom(long roomId) => Keys().Query().Where(x => x.RoomId == roomId).ToList().Where(x => !x.Deleted).OrderBy(x => x.CreatedAt).ToList();
|
||||
public static RoomKey? UpdateKey(long id, Action<RoomKey> edit) { var col = Keys(); var k = col.FindById(id); if (k == null) return null; edit(k); col.Update(k); return k; }
|
||||
public static bool OwnsKey(ulong player, long keyId) => KeysOwned().FindById($"{player}|{keyId}") != null;
|
||||
public static bool GrantKey(ulong player, RoomKey k)
|
||||
{
|
||||
var col = KeysOwned();
|
||||
string key = $"{player}|{k.RoomKeyId}";
|
||||
if (col.FindById(key) != null) return false;
|
||||
col.Insert(new KeyOwnership { Key = key, PlayerId = player, RoomKeyId = k.RoomKeyId, RoomId = k.RoomId });
|
||||
return true;
|
||||
}
|
||||
public static bool RevokeKey(ulong player, long keyId) => KeysOwned().Delete($"{player}|{keyId}");
|
||||
public static List<RoomKey> KeysOwnedBy(ulong player)
|
||||
{
|
||||
var ids = KeysOwned().Find(x => x.PlayerId == player).Select(x => x.RoomKeyId).ToList();
|
||||
return ids.Select(GetKey).Where(k => k != null).Select(k => k!).ToList();
|
||||
}
|
||||
public static int KeyOwnerCount(long keyId) => KeysOwned().Count(x => x.RoomKeyId == keyId);
|
||||
|
||||
// ---- currencies ----------------------------------------------------------------------
|
||||
|
||||
public static RoomCurrency CreateCurrency(RoomCurrency c) { Currencies().Insert(c); return c; }
|
||||
public static RoomCurrency? GetCurrency(Guid id) { var c = Currencies().FindById(id); return c == null || c.Deleted ? null : c; }
|
||||
public static List<RoomCurrency> CurrenciesForRoom(long roomId) => Currencies().Query().Where(x => x.RoomId == roomId).ToList().Where(x => !x.Deleted).OrderBy(x => x.CreatedAt).ToList();
|
||||
public static RoomCurrency? UpdateCurrency(Guid id, Action<RoomCurrency> edit) { var col = Currencies(); var c = col.FindById(id); if (c == null) return null; edit(c); c.ModifiedAt = DateTime.UtcNow; col.Update(c); return c; }
|
||||
|
||||
public static CurrencyOffer CreateOffer(CurrencyOffer o) { Offers().Insert(o); return o; }
|
||||
public static CurrencyOffer? GetOffer(Guid id) { var o = Offers().FindById(id); return o == null || o.Deleted ? null : o; }
|
||||
public static List<CurrencyOffer> OffersFor(Guid currencyId) => Offers().Query().Where(x => x.CurrencyId == currencyId).ToList().Where(x => !x.Deleted).OrderBy(x => x.Order).ThenBy(x => x.Price).ToList();
|
||||
public static CurrencyOffer? UpdateOffer(Guid id, Action<CurrencyOffer> edit) { var col = Offers(); var o = col.FindById(id); if (o == null) return null; edit(o); o.ModifiedAt = DateTime.UtcNow; col.Update(o); return o; }
|
||||
|
||||
public static long GetCurrencyBalance(ulong player, Guid currencyId) => Balances().FindById($"{player}|{currencyId}")?.Balance ?? 0;
|
||||
public static List<CurrencyBalance> BalancesForRoom(ulong player, long roomId) => Balances().Find(x => x.PlayerId == player && x.RoomId == roomId).ToList();
|
||||
/// <summary>Add (or take, negative) room currency. Returns the new balance, or null when it would go negative / over the limit.</summary>
|
||||
public static long? AddCurrency(ulong player, RoomCurrency cur, long delta)
|
||||
{
|
||||
var col = Balances();
|
||||
string key = $"{player}|{cur.CurrencyId}";
|
||||
var b = col.FindById(key) ?? new CurrencyBalance { Key = key, PlayerId = player, CurrencyId = cur.CurrencyId, RoomId = cur.RoomId, Balance = 0 };
|
||||
long next = b.Balance + delta;
|
||||
if (next < 0) return null;
|
||||
if (cur.Limit > 0 && next > cur.Limit) next = cur.Limit;
|
||||
b.Balance = next;
|
||||
col.Upsert(b);
|
||||
return next;
|
||||
}
|
||||
|
||||
// ---- consumables ---------------------------------------------------------------------
|
||||
|
||||
public static RoomConsumable CreateConsumable(RoomConsumable c) { Consumables().Insert(c); return c; }
|
||||
public static RoomConsumable? GetConsumable(Guid id) { var c = Consumables().FindById(id); return c == null || c.Deleted ? null : c; }
|
||||
public static List<RoomConsumable> ConsumablesForRoom(long roomId) => Consumables().Query().Where(x => x.RoomId == roomId).ToList().Where(x => !x.Deleted).OrderBy(x => x.CreatedAt).ToList();
|
||||
public static RoomConsumable? UpdateConsumable(Guid id, Action<RoomConsumable> edit) { var col = Consumables(); var c = col.FindById(id); if (c == null) return null; edit(c); c.ModifiedAt = DateTime.UtcNow; col.Update(c); return c; }
|
||||
|
||||
public static int ConsumableCount(ulong player, Guid id) => ConsOwned().FindById($"{player}|{id}")?.Count ?? 0;
|
||||
public static List<ConsumableOwnership> ConsumablesOwnedInRoom(ulong player, long roomId) => ConsOwned().Find(x => x.PlayerId == player && x.RoomId == roomId).Where(x => x.Count > 0).ToList();
|
||||
/// <summary>Add (or consume, negative) copies. Returns the new count, or null when there were not enough.</summary>
|
||||
public static int? AddConsumable(ulong player, RoomConsumable c, int delta)
|
||||
{
|
||||
var col = ConsOwned();
|
||||
string key = $"{player}|{c.RoomConsumableId}";
|
||||
var o = col.FindById(key) ?? new ConsumableOwnership { Key = key, PlayerId = player, RoomConsumableId = c.RoomConsumableId, RoomId = c.RoomId, Count = 0 };
|
||||
int next = o.Count + delta;
|
||||
if (next < 0) return null;
|
||||
o.Count = next; o.ModifiedAt = DateTime.UtcNow;
|
||||
col.Upsert(o);
|
||||
return next;
|
||||
}
|
||||
public static int ConsumableSoldCount(Guid id) => ConsOwned().Find(x => x.RoomConsumableId == id).Sum(x => x.Count);
|
||||
|
||||
// ---- DTOs ----------------------------------------------------------------------------
|
||||
|
||||
public static object KeyDto(RoomKey k) => new
|
||||
{
|
||||
k.RoomKeyId, k.ReplicationId, k.RoomId, k.Name, k.Description, k.Price, k.PurchaseCurrencyId,
|
||||
CreatedAt = k.CreatedAt.ToString("o"), ImageName = k.ImageName ?? "", k.Type
|
||||
};
|
||||
public static object CurrencyDto(RoomCurrency c) => new
|
||||
{
|
||||
c.CurrencyId, RoomId = (long?)c.RoomId, c.Name, c.Description, CurrencyType = 300, c.Limit, c.Shape, c.Color,
|
||||
ImageName = c.ImageName ?? "", CreatedAt = c.CreatedAt.ToString("o"), ModifiedAt = c.ModifiedAt.ToString("o")
|
||||
};
|
||||
public static object OfferDto(CurrencyOffer o) => new
|
||||
{
|
||||
o.CurrencyPurchaseOfferId, o.CurrencyId, o.Order, o.Name, o.CurrencyAmount, o.Price, ModifiedAt = o.ModifiedAt.ToString("o")
|
||||
};
|
||||
public static object ConsumableDto(RoomConsumable c) => new
|
||||
{
|
||||
c.RoomConsumableId, c.RoomId, c.Name, c.Description, ImageName = c.ImageName ?? "", c.Price, c.PurchaseCurrencyId,
|
||||
ModifiedAt = c.ModifiedAt.ToString("o"), c.MaximumCountPerPurchase, CreatedAt = c.CreatedAt.ToString("o")
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user