Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/uncreated-networking-cleanup' in…
Browse files Browse the repository at this point in the history
…to uncreated-networking-cleanup
  • Loading branch information
benvalkin committed Jan 4, 2025
2 parents 67eae89 + 9e04f3c commit 56c02f3
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 57 deletions.
183 changes: 136 additions & 47 deletions UncreatedWarfare/Layouts/UI/Leaderboards/DualSidedLeaderboardUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,29 @@
using Uncreated.Warfare.Squads;
using Uncreated.Warfare.Stats;
using Uncreated.Warfare.Translations;
using Uncreated.Warfare.Translations.Util;
using Uncreated.Warfare.Util;

namespace Uncreated.Warfare.Layouts.UI.Leaderboards;

[UnturnedUI(BasePath = "Container")]
public partial class DualSidedLeaderboardUI : UnturnedUI, ILeaderboardUI, IEventListener<PlayerJoined>, IEventListener<PlayerLeft>, ILayoutPhaseListener<ILayoutPhase>, IDisposable
{
[Ignore]
private LeaderboardSet[]? _sets;
[Ignore]
private List<PlayerGlobalStat> _globalStats;
[Ignore] private LeaderboardSet[]? _sets;
[Ignore] private List<PlayerGlobalStat>? _globalStats;

[Ignore]
private DateTime _startTimestamp;
[Ignore] private DateTime _startTimestamp;

[Ignore]
private readonly Layout _layout;
[Ignore] private readonly Layout _layout;

private readonly ChatService _chatService;
private readonly IPlayerService _playerService;
private readonly SquadManager _squadManager;
private readonly PointsService _pointsService;
private readonly ITranslationService _translationService;
private readonly Func<CSteamID, DualSidedLeaderboardPlayerData> _createData;
[Ignore] private readonly ChatService _chatService;
[Ignore] private readonly IPlayerService _playerService;
[Ignore] private readonly SquadManager _squadManager;
[Ignore] private readonly PointsService _pointsService;
[Ignore] private readonly ITranslationService _translationService;
[Ignore] private readonly Func<CSteamID, DualSidedLeaderboardPlayerData> _createData;

public readonly UnturnedUIElement TopSquadsParent = new UnturnedUIElement("GameInfo/Squads");
public readonly TopSquad[] TopSquads = ElementPatterns.CreateArray<TopSquad>("GameInfo/Squads/Squad_T{0}", 1, to: 2);

public readonly UnturnedLabel TopSquadsTitle = new UnturnedLabel("GameInfo/Squads/Title");
Expand All @@ -75,7 +73,16 @@ public partial class DualSidedLeaderboardUI : UnturnedUI, ILeaderboardUI, IEvent

public bool IsActive { get; private set; }

public DualSidedLeaderboardUI(AssetConfiguration assetConfig, ILoggerFactory loggerFactory, Layout layout, ChatService chatService, IPlayerService playerService, SquadManager squadManager, PointsService pointsService, ITranslationService translationService)
public DualSidedLeaderboardUI(
AssetConfiguration assetConfig,
ILoggerFactory loggerFactory,
Layout layout,
ChatService chatService,
IPlayerService playerService,
SquadManager squadManager,
PointsService pointsService,
ITranslationService translationService)

: base(loggerFactory, assetConfig.GetAssetLink<EffectAsset>("UI:DualSidedLeaderboardUI"), staticKey: true, debugLogging: false)
{
_createData = CreateData;
Expand Down Expand Up @@ -165,6 +172,7 @@ private void SendToPlayers(LanguageSet set)
while (set.MoveNext())
{
LayoutName.SetText(set.Next.Connection, _layout.LayoutInfo.DisplayName);
SendPointsSection(set.Next);
}
set.Reset();

Expand All @@ -185,9 +193,7 @@ private void SendToPlayers(LanguageSet set)
WinnerTeamName.SetText(c, teamName);
GameDuration.SetText(c, countdown);

// todo team stats


// todo: game stats
}

set.Reset();
Expand All @@ -204,7 +210,18 @@ private void SendToPlayers(LanguageSet set)

private void SendTopSquads(LanguageSet set)
{
for (int i = 0; i < _sets.Length; i++)
// no squads or at least one team has no squads
if (_squadManager.Squads.Count == 0 || _layout.TeamManager.AllTeams.Any(x => _squadManager.Squads.All(y => y.Team != x)))
{
while (set.MoveNext())
TopSquadsParent.SetVisibility(set.Next.Connection, false);
return;
}

while (set.MoveNext())
TopSquadsParent.SetVisibility(set.Next.Connection, true);

for (int i = 0; i < _sets!.Length; i++)
{
LeaderboardSet leaderboardSet = _sets[i];

Expand All @@ -220,7 +237,8 @@ private void SendTopSquads(LanguageSet set)

double totalSquadXP = topSquad.Members.Sum(m => leaderboardSet.GetStatisticValue(xpStatIndex, m.Steam64));
string totalSquadXPWithHeader = $"Squad XP: <#ccffd4>{totalSquadXP.ToString("F0", set.Culture)}</color>";


set.Reset();
while (set.MoveNext())
{
squadElement.Name.SetText(set.Next.Connection, topSquad.Name);
Expand All @@ -243,15 +261,15 @@ private void SendTopSquads(LanguageSet set)

squadElement.ImportantStatistic.SetText(set.Next.Connection, totalSquadXPWithHeader);
}
set.Reset();
}
}

private void SendGlobalStats(LanguageSet set)
{
for (int index = 0; index < ValuablePlayers.Length; index++)
{
ValuablePlayer element = ValuablePlayers[index];
if (index >= _globalStats.Count)
if (index >= _globalStats!.Count)
{
while (set.MoveNext())
{
Expand All @@ -267,11 +285,14 @@ private void SendGlobalStats(LanguageSet set)

while (set.MoveNext())
{
element.Root.Show(set.Next.Connection);
element.Name.SetText(set.Next.Connection, stat.Player.Names.CharacterName);
element.Avatar.SetImage(set.Next.Connection, stat.Player.SteamSummary.AvatarUrlSmall);
element.Role.SetText(set.Next.Connection, stat.StatHeader);
element.Value.SetText(set.Next.Connection, formattedStatValue);
ITransportConnection c = set.Next.Connection;
element.Name.SetText(c, set.Next.Equals(stat.Player)
? TranslationFormattingUtility.Colorize(stat.Player.Names.CharacterName, new Color32(204, 255, 212, 255))
: stat.Player.Names.CharacterName);
element.Avatar.SetImage(c, stat.Player.SteamSummary.AvatarUrlSmall);
element.Role.SetText(c, stat.StatHeader);
element.Value.SetText(c, formattedStatValue);
element.Root.Show(c);
}
set.Reset();
}
Expand All @@ -280,27 +301,36 @@ private void SendGlobalStats(LanguageSet set)
// make sure to only call this after _sets is initialized!
private List<PlayerGlobalStat> ComputeGlobalStats()
{
PlayerGlobalStat mvp = GetPlayerWithHighestStat(KnownStatNames.XP, "MVP", "{0} XP");
PlayerGlobalStat bestTeammate = GetPlayerWithHighestStat(KnownStatNames.Reputation, "Medal of Honor", "{0} Rep");
PlayerGlobalStat mostKills = GetPlayerWithHighestStat(KnownStatNames.Kills, "Most Deadly", "{0} Kills");
PlayerGlobalStat mostDeaths = GetPlayerWithHighestStat(KnownStatNames.Deaths, "Unluckiest", "{0} Deaths");
// todo: longest shot

List<PlayerGlobalStat> stats = [ mvp, bestTeammate, mostKills, mostDeaths ];

PlayerGlobalStat mvp = GetPlayerWithHighestStat(KnownStatNames.XP, "MVP", "{0:F0} XP");
PlayerGlobalStat bestTeammate = GetPlayerWithHighestStat(KnownStatNames.Reputation, "Medal of Honor", "{0:F0} Rep");
PlayerGlobalStat mostKills = GetPlayerWithHighestStat(KnownStatNames.Kills, "Most Deadly", "{0:F0} Kills");
PlayerGlobalStat mostDeaths = GetPlayerWithHighestStat(KnownStatNames.Deaths, "Unluckiest", "{0:F0} Deaths");
PlayerGlobalStat longestShot = GetPlayerWithHighestStat(
player => player.Player.Component<PlayerGameStatsComponent>().LongestShot.SquaredDistance,
"Sharpshooter", string.Empty /* special override later */);

List<PlayerGlobalStat> stats = [ mvp, bestTeammate, mostKills, mostDeaths ];

if (longestShot.StatValue > 100)
{
LongestShot shot = longestShot.Player.Component<PlayerGameStatsComponent>().LongestShot;
longestShot.ValueFormatString = shot.ToString();
stats.Add(longestShot);
}

WarfarePlayer? smelliest = _playerService.GetOnlinePlayerOrNull(76561198839009178);
if (smelliest != null && RandomUtility.GetFloat(0, 1) < 0.1f) // 10% chance of displaying if online
stats.Add(new PlayerGlobalStat(smelliest, RandomUtility.GetInteger(3, 24), "Smelliest Soldier", "{0} days without shower"));
stats.Insert(2, new PlayerGlobalStat(smelliest, RandomUtility.GetInteger(3, 24), "Smelliest Soldier", "{0} days without shower"));

return stats;
}

public struct PlayerGlobalStat
private struct PlayerGlobalStat
{
public WarfarePlayer Player { get; }
public double StatValue { get; }
public string StatHeader { get; }
public string ValueFormatString { get; }
public readonly WarfarePlayer Player;
public readonly double StatValue;
public readonly string StatHeader;
public string ValueFormatString;
public PlayerGlobalStat(WarfarePlayer player, double statValue, string statHeader, string valueFormatString)
{
Player = player;
Expand All @@ -313,21 +343,21 @@ public PlayerGlobalStat(WarfarePlayer player, double statValue, string statHeade
private PlayerGlobalStat GetPlayerWithHighestStat(string statName, string statLabelText, string valueFormatString)
{
double globalStatValue = double.MinValue;
WarfarePlayer globalHighest = null!;
LeaderboardPlayer globalHighest = null!;

foreach (LeaderboardSet leaderboardSet in _sets)
foreach (LeaderboardSet leaderboardSet in _sets!)
{
int statIndex = leaderboardSet.GetStatisticIndex(statName);

WarfarePlayer localHighest = _playerService.OnlinePlayers.Aggregate((p1, p2) =>
LeaderboardPlayer localHighest = leaderboardSet.Players.Aggregate((p1, p2) =>
{
double p1Value = leaderboardSet.GetStatisticValue(statIndex, p1.Steam64);
double p2Value = leaderboardSet.GetStatisticValue(statIndex, p2.Steam64);
double p1Value = leaderboardSet.GetStatisticValue(statIndex, p1.Player.Steam64);
double p2Value = leaderboardSet.GetStatisticValue(statIndex, p2.Player.Steam64);

return p1Value > p2Value ? p1 : p2;
});

double localStatValue = leaderboardSet.GetStatisticValue(statIndex, localHighest.Steam64);
double localStatValue = leaderboardSet.GetStatisticValue(statIndex, localHighest.Player.Steam64);

if (localStatValue <= globalStatValue)
continue;
Expand All @@ -336,9 +366,68 @@ private PlayerGlobalStat GetPlayerWithHighestStat(string statName, string statLa
globalHighest = localHighest;
}

return new PlayerGlobalStat(globalHighest.Player, globalStatValue, statLabelText, valueFormatString);
}

private PlayerGlobalStat GetPlayerWithHighestStat(Func<LeaderboardPlayer, double> selector, string statLabelText, string valueFormatString)
{
double globalStatValue = double.MinValue;
WarfarePlayer globalHighest = null!;

foreach (LeaderboardSet leaderboardSet in _sets!)
{
LeaderboardPlayer localHighest = leaderboardSet.Players.Aggregate((p1, p2) => selector(p1) > selector(p2) ? p1 : p2);

double localStatValue = selector(localHighest);

if (localStatValue <= globalStatValue)
continue;

globalStatValue = localStatValue;
globalHighest = localHighest.Player;
}

return new PlayerGlobalStat(globalHighest, globalStatValue, statLabelText, valueFormatString);
}

private void SendPointsSection(WarfarePlayer player)
{
Team team = player.Team;
LeaderboardSet? set = _sets!.FirstOrDefault(x => x.Team == team);
WarfareRank rank = _pointsService.GetRankFromExperience(player.CachedPoints.XP);
if (set == null)
{
PointsCreditsGained.SetText(player, string.Empty);
PointsExperienceGained.SetText(player, string.Empty);
PointsDowngradeArrow.SetVisibility(player, false);
PointsUpgradeArrow.SetVisibility(player, false);
PointsCurrentRank.SetText(player, string.Empty);
PointsNextRank.SetText(player, string.Empty);
PointsProgressBar.SetVisibility(player, false);
return;
}

PointsProgressBar.SetVisibility(player, true);
PointsProgressBar.SetProgress(player.Connection, (float)rank.GetProgress(player.CachedPoints.XP));

double deltaCredits = set.GetStatisticValue(KnownStatNames.Credits, player.Steam64);
double deltaXP = set.GetStatisticValue(KnownStatNames.XP, player.Steam64);

PointsCreditsGained.SetText(player, deltaCredits > 0
? $"+{deltaCredits.ToString("F0", player.Locale.CultureInfo)} {TranslationFormattingUtility.Colorize("C", _pointsService.CreditsColor)}"
: $"{deltaCredits.ToString("F0", player.Locale.CultureInfo)} {TranslationFormattingUtility.Colorize("C", _pointsService.CreditsColor)}");
PointsExperienceGained.SetText(player, deltaXP > 0
? $"+{deltaXP.ToString("F0", player.Locale.CultureInfo)} {TranslationFormattingUtility.Colorize("XP", _pointsService.ExperienceColor)}"
: $"{deltaXP.ToString("F0", player.Locale.CultureInfo)} {TranslationFormattingUtility.Colorize("XP", _pointsService.ExperienceColor)}");

PointsCurrentRank.SetText(player, rank.Name);
PointsNextRank.SetText(player, rank.Next?.Name ?? string.Empty);

WarfareRank startingRank = _pointsService.GetRankFromExperience(player.CachedPoints.XP - deltaXP);
PointsDowngradeArrow.SetVisibility(player, startingRank.RankIndex > rank.RankIndex);
PointsUpgradeArrow.SetVisibility(player, startingRank.RankIndex < rank.RankIndex);
}

public void UpdateSort(WarfarePlayer player, int setIndex, int column)
{
DualSidedLeaderboardPlayerData data = GetOrAddData(player.Steam64, _createData);
Expand Down
16 changes: 8 additions & 8 deletions UncreatedWarfare/Layouts/UI/Leaderboards/LeaderboardSet.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
Expand All @@ -15,7 +15,7 @@ public class LeaderboardSet
public readonly int ColumnCount;
private readonly LeaderboardPhaseStatInfo[] _stats;

private readonly LeaderboardPlayer[] _players;
public readonly LeaderboardPlayer[] Players;
private readonly double[,] _data;
private readonly Dictionary<int, string[,]> _formattedData;
private readonly int[] _sortMapBuffer;
Expand All @@ -39,8 +39,8 @@ public LeaderboardSet(CreateRow callback, LeaderboardPhaseStatInfo[] stats, IEnu

Stats = stats;

_players = players.ToArray();
LeaderboardRow[] rows = new LeaderboardRow[_players.Length];
Players = players.ToArray();
LeaderboardRow[] rows = new LeaderboardRow[Players.Length];

int visibleColumns = 0;
for (int i = 0; i < stats.Length; ++i)
Expand Down Expand Up @@ -70,7 +70,7 @@ public LeaderboardSet(CreateRow callback, LeaderboardPhaseStatInfo[] stats, IEnu
{
LeaderboardRow row = new LeaderboardRow(i, this);
rows[i] = row;
LeaderboardPlayer player = _players[i];
LeaderboardPlayer player = Players[i];
callback(in row, visibleStats, row.Data);
_indexCache[player.Player.Steam64.m_SteamID] = i;
}
Expand Down Expand Up @@ -110,7 +110,7 @@ public double GetStatisticValue(int statIndex, CSteamID player)
if (rowIndex == -1)
return 0;

return _players[rowIndex].Player.Component<PlayerGameStatsComponent>().Stats[statIndex];
return Players[rowIndex].Player.Component<PlayerGameStatsComponent>().Stats[statIndex];
}

public int GetRowIndex(CSteamID player)
Expand Down Expand Up @@ -183,7 +183,7 @@ public readonly struct LeaderboardRow(int index, LeaderboardSet set)
private readonly LeaderboardSet _set = set;
private readonly int _index = index;

public LeaderboardPlayer Player { get => _set._players[_index]; }
public LeaderboardPlayer Player { get => _set.Players[_index]; }
public Span<double> Data { get => MemoryMarshal.CreateSpan(ref _set._data[_index, 0], _set.ColumnCount); }
public Span<string> FormatData(CultureInfo culture)
{
Expand All @@ -193,7 +193,7 @@ public Span<string> FormatData(CultureInfo culture)
return MemoryMarshal.CreateSpan(ref formats[_index, 0], _set.ColumnCount);
}

formats = new string[_set._players.Length, _set.ColumnCount];
formats = new string[_set.Players.Length, _set.ColumnCount];

Span<string> allStrings = MemoryMarshal.CreateSpan(ref formats[0, 0], formats.Length);
Span<double> allData = MemoryMarshal.CreateSpan(ref _set._data[0, 0], _set._data.Length);
Expand Down
Loading

0 comments on commit 56c02f3

Please sign in to comment.