diff --git a/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs b/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs
index f5beaa2ba..ccf64a601 100644
--- a/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs
+++ b/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs
@@ -1,13 +1,11 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Launcher;
-using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Models.Spt.Launcher;
using SPTarkov.Server.Core.Models.Spt.Mod;
using SPTarkov.Server.Core.Models.Spt.Tables;
using SPTarkov.Server.Core.Servers;
-using SPTarkov.Server.Core.Services;
using SPTarkov.Server.Core.Services.Locales;
using SPTarkov.Server.Core.Utils;
using Info = SPTarkov.Server.Core.Models.Eft.Profile.Info;
@@ -174,11 +172,6 @@ protected MongoId GetSessionId(LoginRequestData info)
return MongoId.Empty();
}
- public SptProfile GetProfile(MongoId sessionId)
- {
- return saveServer.GetProfile(sessionId);
- }
-
public MiniProfile? GetMiniProfileFromUsername(LoginRequestData info)
{
return profileController.GetMiniProfile(GetSessionId(info));
diff --git a/Libraries/SPTarkov.Server.Core/Controllers/ProfileController.cs b/Libraries/SPTarkov.Server.Core/Controllers/ProfileController.cs
index f1f83afa3..4b4a0c236 100644
--- a/Libraries/SPTarkov.Server.Core/Controllers/ProfileController.cs
+++ b/Libraries/SPTarkov.Server.Core/Controllers/ProfileController.cs
@@ -73,6 +73,8 @@ public MiniProfile GetMiniProfile(MongoId sessionId)
InvalidOrUnloadableProfile = profile.ProfileInfo?.InvalidOrUnloadableProfile,
SptData = profileHelper.GetDefaultSptDataObject(),
Wipe = true,
+ ProfileCurrency = profileHelper.GetAccountCurrency(profile),
+ ProfileStats = profileHelper.GetProfileStats(profile),
};
}
@@ -91,6 +93,8 @@ public MiniProfile GetMiniProfile(MongoId sessionId)
InvalidOrUnloadableProfile = profile.ProfileInfo?.InvalidOrUnloadableProfile,
SptData = profile.SptData,
Wipe = profile.ProfileInfo?.IsWiped ?? false,
+ ProfileCurrency = profileHelper.GetAccountCurrency(profile),
+ ProfileStats = profileHelper.GetProfileStats(profile),
};
}
diff --git a/Libraries/SPTarkov.Server.Core/Helpers/Profile/ProfileHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/Profile/ProfileHelper.cs
index 974ba73ee..362c5bac3 100644
--- a/Libraries/SPTarkov.Server.Core/Helpers/Profile/ProfileHelper.cs
+++ b/Libraries/SPTarkov.Server.Core/Helpers/Profile/ProfileHelper.cs
@@ -1,3 +1,4 @@
+using System.Globalization;
using Microsoft.Extensions.Logging;
using SPTarkov.Common.Models.Logging;
using SPTarkov.DI.Annotations;
@@ -5,9 +6,11 @@
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
+using SPTarkov.Server.Core.Models.Eft.Launcher;
using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Models.Spt.Config;
+using SPTarkov.Server.Core.Models.Spt.Helper;
using SPTarkov.Server.Core.Models.Spt.Tables;
using SPTarkov.Server.Core.Servers;
using SPTarkov.Server.Core.Services.Locales;
@@ -837,4 +840,212 @@ public bool GetProfileTemplateFlagValue(string accountEdition, string flagKey)
return matchingProfileTemplate.CustomFlags.GetValueOrDefault(flagKey, false);
}
+
+ ///
+ /// Gets the currencies in the profiles inventory
+ ///
+ /// Full profile
+ ///
+ public ProfileCurrency GetAccountCurrency(SptProfile profile)
+ {
+ var returnObject = new ProfileCurrency
+ {
+ Roubles = 0,
+ Euros = 0,
+ Dollars = 0,
+ GP = 0,
+ };
+
+ foreach (var item in profile.CharacterData?.PmcData?.Inventory?.Items ?? [])
+ {
+ var stackSize = item.GetItemStackSize();
+
+ if (item.Template == Money.ROUBLES)
+ {
+ returnObject.Roubles += stackSize;
+ }
+
+ if (item.Template == Money.DOLLARS)
+ {
+ returnObject.Dollars += stackSize;
+ }
+
+ if (item.Template == Money.EUROS)
+ {
+ returnObject.Euros += stackSize;
+ }
+
+ if (item.Template == Money.GP)
+ {
+ returnObject.GP += stackSize;
+ }
+ }
+
+ return returnObject;
+ }
+
+ ///
+ /// Currently returns a template of data for the profiles stats
+ ///
+ /// Full profile
+ ///
+ public ProfileStats GetProfileStats(SptProfile profile)
+ {
+ var pmcStats = ReadStats(profile.CharacterData?.PmcData);
+ var scavStats = ReadStats(profile.CharacterData?.ScavData);
+ var overallStats = CombineStats(pmcStats, scavStats);
+ var accountLifetime = FormatOverallLifetime(profile.CharacterData?.PmcData?.Info?.RegistrationDate ?? 0);
+
+ return new ProfileStats
+ {
+ Overall = FormatStats(overallStats, accountLifetime),
+ Pmc = FormatStats(pmcStats, accountLifetime),
+ Scav = FormatStats(scavStats, accountLifetime),
+ };
+ }
+
+ private static CharacterStats ReadStats(PmcData? character)
+ {
+ var eft = character?.Stats?.Eft;
+ var counters = eft?.OverallCounters?.Items;
+
+ var transitCounter = GetStat(counters, "ExitStatus", "Transit");
+
+ return new CharacterStats
+ {
+ Raids = GetStat(counters, "Sessions"),
+ Survived = GetStat(counters, "ExitStatus", "Survived") + transitCounter,
+ Killed = GetStat(counters, "ExitStatus", "Killed"),
+ Deaths = GetStat(counters, "Deaths"),
+ Kills = GetStat(counters, "Kills"),
+ RunThrough = GetStat(counters, "ExitStatus", "Runner"),
+ Awols = GetStat(counters, "ExitStatus", "Left"),
+ Mias = GetStat(counters, "ExitStatus", "MissingInAction"),
+ LongestWinStreak = GetStat(counters, "LongestWinStreak"),
+ TotalInGameTime = eft?.TotalInGameTime ?? 0,
+ };
+ }
+
+ private static CharacterStats CombineStats(CharacterStats pmcStats, CharacterStats scavStats)
+ {
+ return new CharacterStats
+ {
+ Raids = pmcStats.Raids + scavStats.Raids,
+ Survived = pmcStats.Survived + scavStats.Survived,
+ Killed = pmcStats.Killed + scavStats.Killed,
+ Deaths = pmcStats.Deaths + scavStats.Deaths,
+ Kills = pmcStats.Kills + scavStats.Kills,
+ RunThrough = pmcStats.RunThrough + scavStats.RunThrough,
+ Awols = pmcStats.Awols + scavStats.Awols,
+ Mias = pmcStats.Mias + scavStats.Mias,
+ LongestWinStreak = pmcStats.LongestWinStreak + scavStats.LongestWinStreak,
+ TotalInGameTime = pmcStats.TotalInGameTime + scavStats.TotalInGameTime,
+ };
+ }
+
+ private static Dictionary FormatStats(CharacterStats stats, string accountLifetime)
+ {
+ var killDeathRatio =
+ stats.Deaths > 0
+ ? Math.Round(stats.Kills / (double)stats.Deaths, 2).ToString(CultureInfo.InvariantCulture)
+ : stats.Kills.ToString(CultureInfo.InvariantCulture);
+ var averageLifeSpan = stats.Raids > 0 ? FormatMinSeconds(stats.TotalInGameTime / stats.Raids) : "-";
+
+ return new Dictionary
+ {
+ { "Raids", stats.Raids.ToString(CultureInfo.InvariantCulture) },
+ { "Survived", stats.Survived.ToString(CultureInfo.InvariantCulture) },
+ { "KIA", stats.Killed.ToString(CultureInfo.InvariantCulture) },
+ { "Kills", stats.Kills.ToString(CultureInfo.InvariantCulture) },
+ { "RunThrough", stats.RunThrough.ToString(CultureInfo.InvariantCulture) },
+ { "AWOL", stats.Awols.ToString(CultureInfo.InvariantCulture) },
+ { "MIA", stats.Mias.ToString(CultureInfo.InvariantCulture) },
+ { "KD", killDeathRatio },
+ { "SurvivalRate", FormatPercentage(stats.Survived, stats.Raids) },
+ { "AverageLifeSpan", averageLifeSpan },
+ { "Online", FormatHoursMins(stats.TotalInGameTime) },
+ { "LeaveRate", FormatPercentage(stats.Awols, stats.Raids) },
+ { "SurvivalsInARow", stats.LongestWinStreak.ToString(CultureInfo.InvariantCulture) },
+ { "AccountLifetime", accountLifetime },
+ };
+ }
+
+ private static string FormatPercentage(long value, long total)
+ {
+ if (total <= 0)
+ {
+ return "-";
+ }
+
+ var percentage = (long)(value / (double)total * 100);
+ return $"{percentage.ToString(CultureInfo.InvariantCulture)}%";
+ }
+
+ private static long GetStat(IEnumerable? counters, params string[] requiredTags)
+ {
+ var value = counters?.FirstOrDefault(counter => counter.Key?.IsSupersetOf(requiredTags) == true)?.Value;
+
+ return value ?? 0;
+ }
+
+ ///
+ /// Taken from the client with interpolation instead, returns a format of MM:SS
+ ///
+ ///
+ ///
+ private static string FormatMinSeconds(long seconds)
+ {
+ var timeSpan = TimeSpan.FromSeconds(seconds);
+ return $"{timeSpan.Minutes:00}:{timeSpan.Seconds:00}";
+ }
+
+ ///
+ /// Taken from the client with interpolation instead, returns a format of HH:MM
+ ///
+ ///
+ ///
+ private static string FormatHoursMins(long seconds)
+ {
+ var timeSpan = TimeSpan.FromSeconds(seconds);
+ return $"{timeSpan.Hours:00}:{timeSpan.Minutes:00}";
+ }
+
+ ///
+ /// Taken from the client with interpolation instead, returns a format of - or HH:MM or DD or YYDD
+ ///
+ ///
+ ///
+ private static string FormatOverallLifetime(int regDate)
+ {
+ var localDateTimeFromUnixTime = DateTime.UnixEpoch.AddMilliseconds(regDate * 1000.0).ToLocalTime();
+ var ts = DateTime.UtcNow - localDateTimeFromUnixTime;
+
+ if (ts <= TimeSpan.FromHours(48))
+ {
+ return $"{ts.TotalHours}h{ts.Minutes:00}m";
+ }
+
+ if (ts <= TimeSpan.FromDays(365))
+ {
+ return $"{(int)ts.TotalDays}d";
+ }
+
+ var num = (int)(ts.TotalDays / 365);
+ var num2 = (int)(ts.TotalDays - 365 * num);
+ return $"{num}y{num2}d";
+ }
+}
+
+public record CharacterStats
+{
+ public long Raids { get; init; }
+ public long Survived { get; init; }
+ public long Killed { get; init; }
+ public long Deaths { get; init; }
+ public long Kills { get; init; }
+ public long RunThrough { get; init; }
+ public long Awols { get; init; }
+ public long Mias { get; init; }
+ public long LongestWinStreak { get; init; }
+ public long TotalInGameTime { get; init; }
}
diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/BotBase.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/BotBase.cs
index 51f72cde0..bea35f02c 100644
--- a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/BotBase.cs
+++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/BotBase.cs
@@ -568,7 +568,7 @@ public record CounterKeyValue
{
public HashSet? Key { get; set; }
- public double? Value { get; set; }
+ public long? Value { get; set; }
}
public record Aggressor
diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Launcher/MiniProfile.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Launcher/MiniProfile.cs
index 84109a8d1..5f3a87393 100644
--- a/Libraries/SPTarkov.Server.Core/Models/Eft/Launcher/MiniProfile.cs
+++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Launcher/MiniProfile.cs
@@ -42,4 +42,37 @@ public record MiniProfile
[JsonPropertyName("wipe")]
public bool Wipe { get; set; }
+
+ [JsonPropertyName("profileCurrency")]
+ public ProfileCurrency? ProfileCurrency { get; set; }
+
+ [JsonPropertyName("profileStats")]
+ public ProfileStats? ProfileStats { get; set; }
+}
+
+public record ProfileStats
+{
+ [JsonPropertyName("overall")]
+ public required Dictionary Overall { get; set; }
+
+ [JsonPropertyName("pmc")]
+ public required Dictionary Pmc { get; set; }
+
+ [JsonPropertyName("scav")]
+ public required Dictionary Scav { get; set; }
+}
+
+public record ProfileCurrency
+{
+ [JsonPropertyName("roubles")]
+ public required int Roubles { get; set; }
+
+ [JsonPropertyName("euros")]
+ public required int Euros { get; set; }
+
+ [JsonPropertyName("dollars")]
+ public required int Dollars { get; set; }
+
+ [JsonPropertyName("gp")]
+ public required int GP { get; set; }
}