mirror of
https://gitee.com/xiarenalofs/squad-rain-ops-mini.git
synced 2026-08-09 06:35:58 +08:00
563 lines
27 KiB
C#
563 lines
27 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using RainOpsMini.Helpers.SquadGameLog;
|
|
using SqlSugar;
|
|
|
|
namespace RainOpsMini.Helpers.SquadGameLog
|
|
{
|
|
public class GameLogEntry
|
|
{
|
|
public DateTime? Timestamp { get; set; }
|
|
public string LogType { get; set; }
|
|
public object Data { get; set; }
|
|
public string RawContent { get; set; }
|
|
}
|
|
|
|
public class PlayerGameStats
|
|
{
|
|
public int Kills { get; set; }
|
|
public int Deaths { get; set; }
|
|
public int Wounds { get; set; }
|
|
public int Revives { get; set; }
|
|
}
|
|
|
|
public class LogSummary
|
|
{
|
|
public DateTime? LogTime { get; set; }
|
|
public string LogType { get; set; }
|
|
public string RawContent { get; set; }
|
|
}
|
|
|
|
public class LogStorage
|
|
{
|
|
private static readonly Lazy<LogStorage> _instance = new Lazy<LogStorage>(() => new LogStorage());
|
|
public static LogStorage Instance => _instance.Value;
|
|
|
|
private LogStorage() { }
|
|
|
|
public PlayerGameStats GetPlayerStats(string steamId, DateTime startTime)
|
|
{
|
|
if (string.IsNullOrEmpty(steamId)) return new PlayerGameStats();
|
|
|
|
var stats = new PlayerGameStats();
|
|
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
// Kills: Assuming SteamID in Log_Die is the Killer's SteamID
|
|
// Exclude Team Kills
|
|
stats.Kills = db.Queryable<Log_Die>()
|
|
.Where(x => x.SteamID == steamId && x.LogTime >= startTime && x.IsTK != true)
|
|
.Count();
|
|
|
|
// Deaths: Use VictimSteamID to count deaths
|
|
stats.Deaths = db.Queryable<Log_Die>()
|
|
.Where(x => x.VictimSteamID == steamId && x.LogTime >= startTime)
|
|
.Count();
|
|
|
|
// Wounds (Inflicted?): Log_Wound has SteamID (Attacker?)
|
|
// If Log_Wound SteamID is Attacker:
|
|
// stats.Wounds = db.Queryable<Log_Wound>()
|
|
// .Where(x => x.SteamID == steamId && x.LogTime >= startTime)
|
|
// .Count();
|
|
|
|
// Revives (Performed): Log_Revived has ReviverSteamID
|
|
stats.Revives = db.Queryable<Log_Revived>()
|
|
.Where(x => x.ReviverSteamID == steamId && x.LogTime >= startTime)
|
|
.Count();
|
|
}
|
|
|
|
return stats;
|
|
}
|
|
|
|
public void RegisterPlayer(string name, string steamId)
|
|
{
|
|
// Memory optimization: Do not store player mapping in memory
|
|
}
|
|
|
|
public void RegisterDieEvent(DateTime timestamp, string killerSteamId, string victimName)
|
|
{
|
|
// Memory optimization: Do not store die events in memory
|
|
}
|
|
|
|
public void RegisterKill(string killerSteamId) { }
|
|
public void RegisterDeath(string victimName) { }
|
|
|
|
public void AddLog(string logType, object data, string rawContent, DateTime? timestamp = null)
|
|
{
|
|
if (string.IsNullOrEmpty(rawContent)) return;
|
|
|
|
// Generate Hash ID from RawContent
|
|
string hashId = SquadLogHelper.GetMD5Hash(rawContent);
|
|
|
|
// Memory optimization: Removed in-memory deduplication (_processedLogs)
|
|
// We rely on database unique constraints (if any) or accept duplicates if file is re-processed.
|
|
// Usually FileTimestampAndSizeWatcher avoids re-processing.
|
|
|
|
// Set the Hash ID to the entity's Id property via Reflection
|
|
var idProperty = data.GetType().GetProperty("Id");
|
|
if (idProperty != null && idProperty.PropertyType == typeof(string))
|
|
{
|
|
idProperty.SetValue(data, hashId);
|
|
}
|
|
|
|
// Save to DB
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
try
|
|
{
|
|
switch (logType)
|
|
{
|
|
case "Disconnect": db.Insertable((Log_Disconnect)data).ExecuteCommand(); break;
|
|
case "MatchLost": db.Insertable((Log_MatchLost)data).ExecuteCommand(); break;
|
|
case "CreateSquad": db.Insertable((Log_CreateSquad)data).ExecuteCommand(); break;
|
|
case "NewPlayerConnect": db.Insertable((Log_NewPlayerConnect)data).ExecuteCommand(); break;
|
|
case "Wound": db.Insertable((Log_Wound)data).ExecuteCommand(); break;
|
|
case "Die": db.Insertable((Log_Die)data).ExecuteCommand(); break;
|
|
case "Revived": db.Insertable((Log_Revived)data).ExecuteCommand(); break;
|
|
case "Attack": db.Insertable((Log_Attack)data).ExecuteCommand(); break;
|
|
case "Tick": db.Insertable((Log_Tick)data).ExecuteCommand(); break;
|
|
case "BattleEnd": db.Insertable((Log_BattleEnd)data).ExecuteCommand(); break;
|
|
case "RemovedPlayer": db.Insertable((Log_RemovedPlayer)data).ExecuteCommand(); break;
|
|
case "SetNextLayer": db.Insertable((Log_SetNextLayer)data).ExecuteCommand(); break;
|
|
case "ChangeLayer": db.Insertable((Log_ChangeLayer)data).ExecuteCommand(); break;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Ignore duplicate key errors (SQLite error code 19 or generic message containing "constraint")
|
|
if (!ex.Message.Contains("constraint") && !ex.Message.Contains("UNIQUE"))
|
|
{
|
|
RainOpsMini.Helpers.RainOpsLog.Log($"[Error] Failed to insert log {logType}: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<GameLogEntry> GetLogsForType<T>(ISqlSugarClient db, string typeName, string search, int page, int pageSize, out int totalCount) where T : class, new()
|
|
{
|
|
var query = db.Queryable<T>();
|
|
if (!string.IsNullOrEmpty(search))
|
|
{
|
|
query = query.Where("RawContent like @search", new { search = "%" + search + "%" });
|
|
}
|
|
|
|
totalCount = query.Count();
|
|
var list = query.OrderBy("LogTime DESC").ToPageList(page, pageSize);
|
|
|
|
return list.Select(x => {
|
|
dynamic d = x;
|
|
return new GameLogEntry
|
|
{
|
|
Timestamp = d.LogTime,
|
|
LogType = typeName,
|
|
Data = x,
|
|
RawContent = d.RawContent
|
|
};
|
|
}).ToList();
|
|
}
|
|
|
|
public IEnumerable<GameLogEntry> GetLogs(string type, string search, int page, int pageSize, out int totalCount)
|
|
{
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
if (!string.IsNullOrEmpty(type))
|
|
{
|
|
switch (type)
|
|
{
|
|
case "Disconnect": return GetLogsForType<Log_Disconnect>(db, type, search, page, pageSize, out totalCount);
|
|
case "MatchLost": return GetLogsForType<Log_MatchLost>(db, type, search, page, pageSize, out totalCount);
|
|
case "CreateSquad": return GetLogsForType<Log_CreateSquad>(db, type, search, page, pageSize, out totalCount);
|
|
case "NewPlayerConnect": return GetLogsForType<Log_NewPlayerConnect>(db, type, search, page, pageSize, out totalCount);
|
|
case "Wound": return GetLogsForType<Log_Wound>(db, type, search, page, pageSize, out totalCount);
|
|
case "Die": return GetLogsForType<Log_Die>(db, type, search, page, pageSize, out totalCount);
|
|
case "Revived": return GetLogsForType<Log_Revived>(db, type, search, page, pageSize, out totalCount);
|
|
case "Attack": return GetLogsForType<Log_Attack>(db, type, search, page, pageSize, out totalCount);
|
|
case "Tick": return GetLogsForType<Log_Tick>(db, type, search, page, pageSize, out totalCount);
|
|
case "BattleEnd": return GetLogsForType<Log_BattleEnd>(db, type, search, page, pageSize, out totalCount);
|
|
case "RemovedPlayer": return GetLogsForType<Log_RemovedPlayer>(db, type, search, page, pageSize, out totalCount);
|
|
case "SetNextLayer": return GetLogsForType<Log_SetNextLayer>(db, type, search, page, pageSize, out totalCount);
|
|
case "ChangeLayer": return GetLogsForType<Log_ChangeLayer>(db, type, search, page, pageSize, out totalCount);
|
|
default:
|
|
totalCount = 0;
|
|
return new List<GameLogEntry>();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var q1 = db.Queryable<Log_Disconnect>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "Disconnect", RawContent = x.RawContent });
|
|
var q2 = db.Queryable<Log_MatchLost>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "MatchLost", RawContent = x.RawContent });
|
|
var q3 = db.Queryable<Log_CreateSquad>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "CreateSquad", RawContent = x.RawContent });
|
|
var q4 = db.Queryable<Log_NewPlayerConnect>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "NewPlayerConnect", RawContent = x.RawContent });
|
|
var q5 = db.Queryable<Log_Wound>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "Wound", RawContent = x.RawContent });
|
|
var q6 = db.Queryable<Log_Die>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "Die", RawContent = x.RawContent });
|
|
var q7 = db.Queryable<Log_Revived>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "Revived", RawContent = x.RawContent });
|
|
var q8 = db.Queryable<Log_Attack>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "Attack", RawContent = x.RawContent });
|
|
var q9 = db.Queryable<Log_Tick>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "Tick", RawContent = x.RawContent });
|
|
var q10 = db.Queryable<Log_BattleEnd>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "BattleEnd", RawContent = x.RawContent });
|
|
var q11 = db.Queryable<Log_RemovedPlayer>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "RemovedPlayer", RawContent = x.RawContent });
|
|
var q12 = db.Queryable<Log_SetNextLayer>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "SetNextLayer", RawContent = x.RawContent });
|
|
var q13 = db.Queryable<Log_ChangeLayer>().Select(x => new LogSummary { LogTime = x.LogTime, LogType = "ChangeLayer", RawContent = x.RawContent });
|
|
|
|
var all = db.UnionAll(q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12, q13);
|
|
|
|
if (!string.IsNullOrEmpty(search))
|
|
{
|
|
all = all.Where(x => x.RawContent.Contains(search));
|
|
}
|
|
|
|
totalCount = all.Count();
|
|
var res = all.OrderByDescending(x => x.LogTime).ToPageList(page, pageSize);
|
|
|
|
return res.Select(x => new GameLogEntry
|
|
{
|
|
Timestamp = x.LogTime,
|
|
LogType = x.LogType,
|
|
RawContent = x.RawContent,
|
|
Data = null
|
|
}).ToList();
|
|
}
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, int> GetLogStats()
|
|
{
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
var stats = new Dictionary<string, int>();
|
|
stats["Disconnect"] = db.Queryable<Log_Disconnect>().Count();
|
|
stats["MatchLost"] = db.Queryable<Log_MatchLost>().Count();
|
|
stats["CreateSquad"] = db.Queryable<Log_CreateSquad>().Count();
|
|
stats["NewPlayerConnect"] = db.Queryable<Log_NewPlayerConnect>().Count();
|
|
stats["Wound"] = db.Queryable<Log_Wound>().Count();
|
|
stats["Die"] = db.Queryable<Log_Die>().Count();
|
|
stats["Revived"] = db.Queryable<Log_Revived>().Count();
|
|
stats["Attack"] = db.Queryable<Log_Attack>().Count();
|
|
stats["Tick"] = db.Queryable<Log_Tick>().Count();
|
|
stats["BattleEnd"] = db.Queryable<Log_BattleEnd>().Count();
|
|
stats["RemovedPlayer"] = db.Queryable<Log_RemovedPlayer>().Count();
|
|
stats["SetNextLayer"] = db.Queryable<Log_SetNextLayer>().Count();
|
|
stats["ChangeLayer"] = db.Queryable<Log_ChangeLayer>().Count();
|
|
return stats;
|
|
}
|
|
}
|
|
|
|
public DateTime? GetLastSquadCreationTime(string steamId)
|
|
{
|
|
if (string.IsNullOrEmpty(steamId)) return null;
|
|
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
var log = db.Queryable<Log_CreateSquad>()
|
|
.Where(x => x.SteamID == steamId)
|
|
.OrderByDescending(x => x.LogTime)
|
|
.First();
|
|
return log?.LogTime;
|
|
}
|
|
}
|
|
|
|
public void SaveMatchStats(DateTime matchEndTime, string mapName)
|
|
{
|
|
try
|
|
{
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
// 1. Determine Match Start Time (Time of previous BattleEnd)
|
|
var lastMatchEnd = db.Queryable<Log_BattleEnd>()
|
|
.Where(x => x.LogTime < matchEndTime)
|
|
.OrderBy(x => x.LogTime, OrderByType.Desc)
|
|
.First();
|
|
|
|
var startTime = lastMatchEnd?.LogTime ?? matchEndTime.AddHours(-2); // Default to 2 hours if no previous match
|
|
|
|
// 2. Aggregate Stats from Logs
|
|
// Kills
|
|
var kills = db.Queryable<Log_Die>()
|
|
.Where(x => x.LogTime > startTime && x.LogTime <= matchEndTime && x.IsTK != true)
|
|
.GroupBy(x => x.AttackerSteamID)
|
|
.Select(x => new { SteamID = x.AttackerSteamID, Count = SqlFunc.AggregateCount(x.Id) })
|
|
.ToList();
|
|
|
|
// Deaths
|
|
var deaths = db.Queryable<Log_Die>()
|
|
.Where(x => x.LogTime > startTime && x.LogTime <= matchEndTime)
|
|
.GroupBy(x => x.VictimSteamID)
|
|
.Select(x => new { SteamID = x.VictimSteamID, Count = SqlFunc.AggregateCount(x.Id) })
|
|
.ToList();
|
|
|
|
// TeamKills
|
|
var teamKills = db.Queryable<Log_Die>()
|
|
.Where(x => x.LogTime > startTime && x.LogTime <= matchEndTime && x.IsTK == true)
|
|
.GroupBy(x => x.AttackerSteamID)
|
|
.Select(x => new { SteamID = x.AttackerSteamID, Count = SqlFunc.AggregateCount(x.Id) })
|
|
.ToList();
|
|
|
|
// Wounds (Knocks)
|
|
var wounds = db.Queryable<Log_Wound>()
|
|
.Where(x => x.LogTime > startTime && x.LogTime <= matchEndTime && x.IsTK != true)
|
|
.GroupBy(x => x.AttackerSteamID)
|
|
.Select(x => new { SteamID = x.AttackerSteamID, Count = SqlFunc.AggregateCount(x.Id) })
|
|
.ToList();
|
|
|
|
// Revives
|
|
var revives = db.Queryable<Log_Revived>()
|
|
.Where(x => x.LogTime > startTime && x.LogTime <= matchEndTime)
|
|
.GroupBy(x => x.ReviverSteamID)
|
|
.Select(x => new { SteamID = x.ReviverSteamID, Count = SqlFunc.AggregateCount(x.Id) })
|
|
.ToList();
|
|
|
|
// 3. Get Player Info (Name, Team) - Try to get from RconCache first, then fallback to logs
|
|
var playerStatsDict = new Dictionary<string, MatchPlayerStats>();
|
|
var currentPlayers = RainOpsMini.Program.RconCache?.Players?.ToList() ?? new List<RainOpsMini.Models.PlayerInfo>();
|
|
|
|
// Helper to get or create stats entry
|
|
MatchPlayerStats GetStats(string steamId)
|
|
{
|
|
if (string.IsNullOrEmpty(steamId)) return null;
|
|
if (!playerStatsDict.ContainsKey(steamId))
|
|
{
|
|
var pInfo = currentPlayers.FirstOrDefault(p => p.SteamId == steamId);
|
|
playerStatsDict[steamId] = new MatchPlayerStats
|
|
{
|
|
MatchId = $"{mapName}_{matchEndTime:yyyyMMddHHmmss}",
|
|
MatchDate = matchEndTime,
|
|
MapName = mapName,
|
|
SteamId = steamId,
|
|
PlayerName = pInfo?.Name ?? "Unknown",
|
|
TeamId = pInfo?.TeamId ?? 0,
|
|
Role = pInfo?.Role ?? "",
|
|
EosId = pInfo?.EosId ?? ""
|
|
};
|
|
}
|
|
return playerStatsDict[steamId];
|
|
}
|
|
|
|
// Populate Stats
|
|
foreach (var k in kills) { var s = GetStats(k.SteamID); if (s != null) s.Kills = k.Count; }
|
|
foreach (var d in deaths) { var s = GetStats(d.SteamID); if (s != null) s.Deaths = d.Count; }
|
|
foreach (var tk in teamKills) { var s = GetStats(tk.SteamID); if (s != null) s.TeamKills = tk.Count; }
|
|
foreach (var w in wounds) { var s = GetStats(w.SteamID); if (s != null) s.Wounds = w.Count; }
|
|
foreach (var r in revives) { var s = GetStats(r.SteamID); if (s != null) s.Revives = r.Count; }
|
|
|
|
// Fix missing names from logs if not in RconCache
|
|
var allSteamIds = playerStatsDict.Keys.ToList();
|
|
foreach (var steamId in allSteamIds)
|
|
{
|
|
var stats = playerStatsDict[steamId];
|
|
if (stats.PlayerName == "Unknown")
|
|
{
|
|
// Try to find name in logs
|
|
var nameLog = db.Queryable<Log_NewPlayerConnect>()
|
|
.Where(x => x.SteamID == steamId)
|
|
.OrderBy(x => x.LogTime, OrderByType.Desc)
|
|
.First();
|
|
if (nameLog == null)
|
|
{
|
|
// Try Die/Wound logs
|
|
var dieLog = db.Queryable<Log_Die>()
|
|
.Where(x => x.AttackerSteamID == steamId || x.VictimSteamID == steamId)
|
|
.OrderBy(x => x.LogTime, OrderByType.Desc)
|
|
.First();
|
|
if (dieLog != null)
|
|
{
|
|
stats.PlayerName = dieLog.AttackerSteamID == steamId ? dieLog.AttackerName : dieLog.VictimName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Log_NewPlayerConnect doesn't have Name usually (based on entity definition earlier)?
|
|
// Wait, Log_NewPlayerConnect definition: PlayerController, IP, EOSID, SteamID. No Name.
|
|
// Log_CreateSquad has UserName.
|
|
// Log_Die has PlayerName/AttackerName.
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save to DB
|
|
if (playerStatsDict.Any())
|
|
{
|
|
db.Insertable(playerStatsDict.Values.ToList()).ExecuteCommand();
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RainOpsMini.Helpers.RainOpsLog.Log($"Error saving match stats: {ex.Message}");
|
|
}
|
|
}
|
|
private ConcurrentDictionary<string, DateTime> _playerConnectTimes = new ConcurrentDictionary<string, DateTime>();
|
|
|
|
public void UpdatePlayerConnectTime(string steamId, DateTime time)
|
|
{
|
|
_playerConnectTimes.AddOrUpdate(steamId, time, (k, v) => time);
|
|
}
|
|
|
|
public DateTime? GetPlayerConnectTime(string steamId)
|
|
{
|
|
if (_playerConnectTimes.TryGetValue(steamId, out var time))
|
|
{
|
|
return time;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void UpdatePlayerConnect(string steamId, string eosId, string name, DateTime time)
|
|
{
|
|
try
|
|
{
|
|
UpdatePlayerConnectTime(steamId, time);
|
|
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
var profile = db.Queryable<PlayerProfile>().Single(p => p.SteamID == steamId);
|
|
if (profile == null)
|
|
{
|
|
profile = new PlayerProfile
|
|
{
|
|
SteamID = steamId,
|
|
EOSID = eosId,
|
|
LastName = name ?? "Unknown",
|
|
FirstSeen = time,
|
|
LastSeen = time,
|
|
TotalPlaytimeSeconds = 0,
|
|
PlayStickiness = 0
|
|
};
|
|
db.Insertable(profile).ExecuteCommand();
|
|
}
|
|
else
|
|
{
|
|
profile.LastSeen = time;
|
|
profile.EOSID = eosId;
|
|
if (!string.IsNullOrEmpty(name)) profile.LastName = name;
|
|
db.Updateable(profile).ExecuteCommand();
|
|
}
|
|
|
|
// Start Session
|
|
var openSession = db.Queryable<PlayerSession>()
|
|
.Where(s => s.SteamID == steamId && s.EndTime == null)
|
|
.OrderBy(s => s.StartTime, OrderByType.Desc)
|
|
.First();
|
|
|
|
if (openSession != null)
|
|
{
|
|
openSession.EndTime = time;
|
|
openSession.DurationSeconds = (time - openSession.StartTime).TotalSeconds;
|
|
db.Updateable(openSession).ExecuteCommand();
|
|
}
|
|
|
|
var newSession = new PlayerSession
|
|
{
|
|
SteamID = steamId,
|
|
StartTime = time,
|
|
EndTime = null
|
|
};
|
|
db.Insertable(newSession).ExecuteCommand();
|
|
}
|
|
|
|
Task.Run(() => {
|
|
try {
|
|
var hours = RainOpsMini.Helpers.SteamHelper.GetSquadRunTime(steamId);
|
|
if (hours > 0) UpdateSquadPlaytime(steamId, hours);
|
|
} catch {}
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RainOpsMini.Helpers.RainOpsLog.Log($"Error updating player connect: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public void UpdatePlayerDisconnect(string eosId, DateTime time)
|
|
{
|
|
try
|
|
{
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
var profile = db.Queryable<PlayerProfile>().First(p => p.EOSID == eosId);
|
|
if (profile == null) return;
|
|
|
|
var openSession = db.Queryable<PlayerSession>()
|
|
.Where(s => s.SteamID == profile.SteamID && s.EndTime == null)
|
|
.OrderBy(s => s.StartTime, OrderByType.Desc)
|
|
.First();
|
|
|
|
if (openSession != null)
|
|
{
|
|
openSession.EndTime = time;
|
|
openSession.DurationSeconds = (time - openSession.StartTime).TotalSeconds;
|
|
db.Updateable(openSession).ExecuteCommand();
|
|
|
|
profile.TotalPlaytimeSeconds += openSession.DurationSeconds;
|
|
|
|
double days = (time - profile.FirstSeen).TotalDays;
|
|
if (days < 1) days = 1;
|
|
profile.PlayStickiness = (profile.TotalPlaytimeSeconds / 3600.0) / days;
|
|
|
|
db.Updateable(profile).ExecuteCommand();
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RainOpsMini.Helpers.RainOpsLog.Log($"Error updating player disconnect: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public void UpdateSquadPlaytime(string steamId, int hours)
|
|
{
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
db.Updateable<PlayerProfile>()
|
|
.SetColumns(p => p.SquadPlaytimeHours == hours)
|
|
.Where(p => p.SteamID == steamId)
|
|
.ExecuteCommand();
|
|
}
|
|
}
|
|
|
|
public void UpdatePlayerGameTimeInfo(string steamId, int hours, string name)
|
|
{
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
var existing = db.Queryable<PlayerProfile>().First(p => p.SteamID == steamId);
|
|
if (existing != null)
|
|
{
|
|
existing.SquadPlaytimeHours = hours;
|
|
if (!string.IsNullOrWhiteSpace(name))
|
|
{
|
|
existing.LastName = name;
|
|
}
|
|
db.Updateable(existing).ExecuteCommand();
|
|
}
|
|
else
|
|
{
|
|
var newProfile = new PlayerProfile
|
|
{
|
|
SteamID = steamId,
|
|
SquadPlaytimeHours = hours,
|
|
LastName = !string.IsNullOrWhiteSpace(name) ? name : "Unknown",
|
|
FirstSeen = DateTime.Now,
|
|
LastSeen = DateTime.Now,
|
|
TotalPlaytimeSeconds = 0,
|
|
PlayStickiness = 0,
|
|
EOSID = ""
|
|
};
|
|
db.Insertable(newProfile).ExecuteCommand();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void UpdatePlayerName(string steamId, string name)
|
|
{
|
|
using (var db = DbHelper.GetInstance())
|
|
{
|
|
db.Updateable<PlayerProfile>()
|
|
.SetColumns(p => p.LastName == name)
|
|
.Where(p => p.SteamID == steamId)
|
|
.ExecuteCommand();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|