using System; using Mono.Data.Sqlite; using UnityEngine; using System.Collections.Generic; using System.IO; namespace LocalRank { public class RankService { private const string DatabaseName = "rank.db"; private static string DbFullPath; static RankService() { #if UNITY_EDITOR DbFullPath = Path.Combine(Application.streamingAssetsPath, DatabaseName); #else var filepath = Path.Combine(Application.persistentDataPath, DatabaseName); if (!File.Exists(filepath)) { Debug.Log("Database not in Persistent path, copying from StreamingAssets..."); #if UNITY_ANDROID var sourcePath = "jar:file://" + Application.dataPath + "!/assets/" + DatabaseName; var www = new WWW(sourcePath); while (!www.isDone) { } File.WriteAllBytes(filepath, www.bytes); #elif UNITY_IOS var sourcePath = Path.Combine(Application.dataPath + "/Raw", DatabaseName); File.Copy(sourcePath, filepath); #elif UNITY_STANDALONE_OSX var sourcePath = Path.Combine(Application.dataPath + "/Resources/Data/StreamingAssets", DatabaseName); File.Copy(sourcePath, filepath); #else var sourcePath = Path.Combine(Application.streamingAssetsPath, DatabaseName); File.Copy(sourcePath, filepath); #endif Debug.Log("Database copied to: " + filepath); } DbFullPath = filepath; #endif } //private static string DbPath => $"URI=file:{Application.persistentDataPath}/rank.db"; private static string DbPath => $"URI=file:{DbFullPath}"; // 检查表是否存在 public static bool IsTableExists(int gameId) { using var conn = new SqliteConnection(DbPath); conn.Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = $@" SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'rank_game_{gameId}'; "; return Convert.ToInt32(cmd.ExecuteScalar()) > 0; } // 创建表格(如果表不存在) public static void CreateTableIfNotExists(int gameId) { if (IsTableExists(gameId)) return; // 如果表已存在,直接返回 using var conn = new SqliteConnection(DbPath); conn.Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = $@" CREATE TABLE IF NOT EXISTS rank_game_{gameId} ( id INTEGER PRIMARY KEY AUTOINCREMENT, userId INTEGER NOT NULL, nickname TEXT, avatarUrl TEXT, score INTEGER, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ); "; cmd.ExecuteNonQuery(); } // 清空表格 public static void ClearTable(int gameId) { using var conn = new SqliteConnection(DbPath); conn.Open(); // 检查表是否存在 using var checkCmd = conn.CreateCommand(); checkCmd.CommandText = $"SELECT name FROM sqlite_master WHERE type='table' AND name='rank_game_{gameId}';"; var result = checkCmd.ExecuteScalar(); if (result != null) { using var cmd = conn.CreateCommand(); cmd.CommandText = $"DELETE FROM rank_game_{gameId};"; cmd.ExecuteNonQuery(); } // 如果表不存在则跳过,无需操作 } /// /// 获取当前存在的全部id /// /// public static List GetAllGameIds() { var gameIds = new List(); using var conn = new SqliteConnection(DbPath); conn.Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = @" SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'rank_game_%'; "; using var reader = cmd.ExecuteReader(); while (reader.Read()) { var tableName = reader.GetString(0); // 例如 "rank_game_5" if (tableName.StartsWith("rank_game_")) { var idPart = tableName.Substring("rank_game_".Length); if (int.TryParse(idPart, out int gameId)) { gameIds.Add(gameId); } } } return gameIds; } // 上传分数(如果是新用户,插入;如果是高分,更新) public static void UploadScore(int gameId, int userId, string nickname, string avatarUrl, int score) { CreateTableIfNotExists(gameId); using var conn = new SqliteConnection(DbPath); conn.Open(); using var cmd = conn.CreateCommand(); // 查询旧分数 cmd.CommandText = $"SELECT score FROM rank_game_{gameId} WHERE userId = @userId;"; cmd.Parameters.AddWithValue("@userId", userId); var reader = cmd.ExecuteReader(); int? oldScore = null; if (reader.Read()) oldScore = reader.GetInt32(0); reader.Close(); if (oldScore == null) { // 新用户插入 cmd.CommandText = $@"INSERT INTO rank_game_{gameId} (userId, nickname, avatarUrl, score) VALUES (@userId, @nickname, @avatarUrl, @score);"; } else if (score > oldScore) { // 更新高分 cmd.CommandText = $@"UPDATE rank_game_{gameId} SET score = @score, update_time = CURRENT_TIMESTAMP WHERE userId = @userId;"; } else { return; // 分数没提升 } cmd.Parameters.AddWithValue("@nickname", nickname); cmd.Parameters.AddWithValue("@avatarUrl", avatarUrl); cmd.Parameters.AddWithValue("@score", score); cmd.ExecuteNonQuery(); } public class RankEntry { public string Nickname; public string AvatarUrl; public int Score; public int Rank; public bool IsSelf; } public static (List resultList, int userRank, int totalCount) GetRankList(int gameId, int userId) { var result = new List(); using var conn = new SqliteConnection(DbPath); conn.Open(); using var cmd = conn.CreateCommand(); cmd.CommandText = $"SELECT userId, nickname, avatarUrl, score, update_time FROM rank_game_{gameId} ORDER BY score DESC, update_time DESC;"; using var reader = cmd.ExecuteReader(); var allRanks = new List(); int currentRank = 1; int userIndex = -1; while (reader.Read()) { var entry = new RankEntry { Nickname = reader.GetString(1), AvatarUrl = reader.GetString(2), Score = reader.GetInt32(3), Rank = currentRank++, IsSelf = reader.GetInt32(0) == userId }; if (entry.IsSelf) userIndex = allRanks.Count; allRanks.Add(entry); } reader.Close(); // 加入前三名 for (int i = 0; i < Math.Min(3, allRanks.Count); i++) { result.Add(allRanks[i]); } // 加入中间的自己 + 周围 7 条数据 if (userIndex == -1) { // 用户不存在 result.Add(new RankEntry { Rank = -1, IsSelf = true, Nickname = "None", AvatarUrl = "", Score = 0 }); } else { // 起始从第4条开始找 int centerStart = Math.Max(3, userIndex - 3); int centerEnd = Math.Min(allRanks.Count, centerStart + 7); for (int i = centerStart; i < centerEnd; i++) { result.Add(allRanks[i]); } } return (result, userIndex == -1 ? -1 : allRanks[userIndex].Rank, allRanks.Count); } /// /// 用设备唯一标识(如 SystemInfo.deviceUniqueIdentifier)做基础,然后加时间戳或随机数避免重复 /// /// public static int GenerateLocalUserId() { return Math.Abs($"{SystemInfo.deviceUniqueIdentifier}_{DateTime.UtcNow.Ticks}".GetHashCode()); } } }