RizzeBattleRoyale/Assets/Editor/PlayersDevTools.cs

206 lines
6.9 KiB
C#
Raw Permalink Normal View History

2025-10-15 20:28:33 +04:00
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Text.Json;
using UnityEditor;
using UnityEngine;
using Supabase;
using Postgrest;
using Postgrest.Attributes;
using Postgrest.Models;
#region DB Model
[Table("players")]
public class PlayerRow : BaseModel
{
[PrimaryKey("wallet_address", false)]
[Column("wallet_address")] public string WalletAddress { get; set; }
[Column("total_kills")] public int TotalKills { get; set; }
[Column("average_placement")] public decimal? AveragePlacement { get; set; }
[Column("win_percentage")] public decimal? WinPercentage { get; set; }
[Column("in_game_currency")] public long InGameCurrency { get; set; }
// jsonb we'll send/receive as a plain object map
[Column("purchased_items")] public Dictionary<string, object> PurchasedItems { get; set; } = new();
[Column("updated_at")] public DateTimeOffset? UpdatedAt { get; set; }
}
#endregion
public class PlayersDevTools : EditorWindow
{
// Inputs
string wallet = "dev_wallet_test_1";
int totalKills = 3;
double avgPlacement = 2.5;
double winPct = 55.25;
long currency = 1000;
string purchasedJson = "{\"starter_pack\":true,\"sword\":\"iron\"}";
Supabase.Client client;
[MenuItem("Tools/Supabase/Players Tester")]
public static void Open() => GetWindow<PlayersDevTools>("Players Tester");
async void OnEnable() => await EnsureClient();
async Task EnsureClient()
{
if (client != null) return;
var s = await SupabaseSecretsLoader.LoadAsync(); // editor dev json or Resources
var opts = new SupabaseOptions { AutoRefreshToken = true, AutoConnectRealtime = false, Schema = "public" };
client = new Supabase.Client(s.url, s.anonKey, opts);
await client.InitializeAsync();
}
void OnGUI()
{
EditorGUILayout.LabelField("Upsert / Read / Delete: public.players", EditorStyles.boldLabel);
EditorGUILayout.Space();
wallet = EditorGUILayout.TextField("wallet_address", wallet);
totalKills = EditorGUILayout.IntField("total_kills", totalKills);
avgPlacement = EditorGUILayout.DoubleField("average_placement", avgPlacement);
winPct = EditorGUILayout.DoubleField("win_percentage", winPct);
currency = EditorGUILayout.LongField("in_game_currency", currency);
purchasedJson = EditorGUILayout.TextField("purchased_items (json)", purchasedJson);
EditorGUILayout.Space();
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("UPSERT")) _ = UpsertAsync();
if (GUILayout.Button("READ")) _ = ReadAsync();
if (GUILayout.Button("DELETE")) _ = DeleteAsync();
}
}
async Task UpsertAsync()
{
try
{
await EnsureClient();
var row = new PlayerRow
{
WalletAddress = wallet,
TotalKills = totalKills,
AveragePlacement = (decimal)avgPlacement,
WinPercentage = (decimal)winPct,
InGameCurrency = currency,
PurchasedItems = ParseDict(purchasedJson),
UpdatedAt = DateTimeOffset.UtcNow
};
// postgrest-csharp 3.5.x Upsert by PK, no onConflict param
var resp = await client.From<PlayerRow>().Upsert(row);
var saved = resp.Models.Count > 0 ? resp.Models[0] : row;
Debug.Log($"✅ UPSERT OK -> wallet={saved.WalletAddress} kills={saved.TotalKills} currency={saved.InGameCurrency}");
ShowNotification(new GUIContent("UPSERT OK"));
}
catch (Exception e)
{
Debug.LogError($"UPSERT FAILED: {e.Message}");
ShowNotification(new GUIContent("UPSERT FAILED"));
}
}
async Task ReadAsync()
{
try
{
await EnsureClient();
var resp = await client
.From<PlayerRow>()
.Select("*") // 3.5.x needs a string
.Filter("wallet_address", Postgrest.Constants.Operator.Equals, wallet)
.Get();
if (resp.Models.Count == 0)
{
Debug.LogWarning("No row found.");
ShowNotification(new GUIContent("NOT FOUND"));
return;
}
var p = resp.Models[0];
Debug.Log($"✅ READ -> wallet={p.WalletAddress}, kills={p.TotalKills}, avg={p.AveragePlacement}, win%={p.WinPercentage}, cur={p.InGameCurrency}, items={JsonSerializer.Serialize(p.PurchasedItems)}");
ShowNotification(new GUIContent("READ OK"));
}
catch (Exception e)
{
Debug.LogError($"READ FAILED: {e.Message}");
ShowNotification(new GUIContent("READ FAILED"));
}
}
async Task DeleteAsync()
{
try
{
await EnsureClient();
await client
.From<PlayerRow>()
.Filter("wallet_address", Postgrest.Constants.Operator.Equals, wallet)
.Delete();
Debug.Log("✅ DELETE OK");
ShowNotification(new GUIContent("DELETE OK"));
}
catch (Exception e)
{
Debug.LogError($"DELETE FAILED: {e.Message}");
ShowNotification(new GUIContent("DELETE FAILED"));
}
}
// -------- JSON helpers (System.Text.Json) --------
static Dictionary<string, object> ParseDict(string json)
{
var result = new Dictionary<string, object>();
if (string.IsNullOrWhiteSpace(json)) return result;
try
{
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.ValueKind != JsonValueKind.Object) return result;
foreach (var p in doc.RootElement.EnumerateObject())
result[p.Name] = ToPlain(p.Value);
}
catch { /* ignore -> empty dict */ }
return result;
}
static object ToPlain(JsonElement e)
{
switch (e.ValueKind)
{
case JsonValueKind.String: return e.GetString();
case JsonValueKind.Number:
if (e.TryGetInt64(out var l)) return l;
if (e.TryGetDouble(out var d)) return d;
return e.GetRawText();
case JsonValueKind.True: return true;
case JsonValueKind.False: return false;
case JsonValueKind.Null: return null;
case JsonValueKind.Array:
var list = new List<object>();
foreach (var it in e.EnumerateArray()) list.Add(ToPlain(it));
return list;
case JsonValueKind.Object:
var dict = new Dictionary<string, object>();
foreach (var p in e.EnumerateObject()) dict[p.Name] = ToPlain(p.Value);
return dict;
default: return e.GetRawText();
}
}
}
#endif