diff --git a/src/Modules/SpectatorTargetInfoModule/Controllers/SpectatorTargetInfoEventController.cs b/src/Modules/SpectatorTargetInfoModule/Controllers/SpectatorTargetInfoEventController.cs
index 7631644ea..533fc9b98 100644
--- a/src/Modules/SpectatorTargetInfoModule/Controllers/SpectatorTargetInfoEventController.cs
+++ b/src/Modules/SpectatorTargetInfoModule/Controllers/SpectatorTargetInfoEventController.cs
@@ -15,11 +15,14 @@ public class SpectatorTargetInfoEventController(ISpectatorTargetInfoService spec
{
[Subscribe(GbxRemoteEvent.PlayerDisconnect)]
public Task OnPlayerDisconnectAsync(object sender, PlayerGbxEventArgs eventArgs) =>
- spectatorTargetInfoService.RemovePlayerFromSpectatorsListAsync(eventArgs.Login);
+ spectatorTargetInfoService.RemovePlayerAsync(eventArgs.Login);
[Subscribe(GbxRemoteEvent.BeginMap)]
- public Task OnBeginMapAsync(object sender, MapGbxEventArgs eventArgs) =>
- spectatorTargetInfoService.UpdateIsTeamsModeAsync();
+ public async Task OnBeginMapAsync(object sender, MapGbxEventArgs eventArgs)
+ {
+ await spectatorTargetInfoService.DetectIsTeamsModeAsync();
+ await spectatorTargetInfoService.DetectIsTimeAttackModeAsync();
+ }
[Subscribe(ModeScriptEvent.WayPoint)]
public Task OnWayPointAsync(object sender, WayPointEventArgs wayPointEventArgs) =>
@@ -44,4 +47,16 @@ public async Task OnNewWarmUpRoundAsync(object sender, WarmUpRoundEventArgs roun
await spectatorTargetInfoService.FetchAndCacheTeamInfoAsync();
await spectatorTargetInfoService.ResetWidgetForSpectatorsAsync();
}
+
+ [Subscribe(ModeScriptEvent.WarmUpStart)]
+ public Task OnWarmUpStartAsync(object sender, EventArgs args) =>
+ spectatorTargetInfoService.UpdateIsTimeAttackModeAsync(true);
+
+ [Subscribe(ModeScriptEvent.WarmUpEnd)]
+ public Task OnWarmUpEndAsync(object sender, EventArgs args) =>
+ spectatorTargetInfoService.DetectIsTimeAttackModeAsync();
+
+ [Subscribe(ModeScriptEvent.GiveUp)]
+ public Task OnPlayerGiveUpAsync(object sender, PlayerUpdateEventArgs args) =>
+ spectatorTargetInfoService.ClearCheckpointsAsync(args.Login);
}
diff --git a/src/Modules/SpectatorTargetInfoModule/Controllers/SpectatorTargetInfoManialinkController.cs b/src/Modules/SpectatorTargetInfoModule/Controllers/SpectatorTargetInfoManialinkController.cs
index 04b17fb77..47f6c5fd2 100644
--- a/src/Modules/SpectatorTargetInfoModule/Controllers/SpectatorTargetInfoManialinkController.cs
+++ b/src/Modules/SpectatorTargetInfoModule/Controllers/SpectatorTargetInfoManialinkController.cs
@@ -19,7 +19,7 @@ public async Task ReportSpectatorTargetAsync(string targetLogin)
}
else
{
- await spectatorTargetInfoService.RemovePlayerFromSpectatorsListAsync(spectatorLogin);
+ await spectatorTargetInfoService.RemovePlayerAsync(spectatorLogin);
await spectatorTargetInfoService.HideSpectatorInfoWidgetAsync(spectatorLogin);
}
}
diff --git a/src/Modules/SpectatorTargetInfoModule/Interfaces/ISpectatorTargetInfoService.cs b/src/Modules/SpectatorTargetInfoModule/Interfaces/ISpectatorTargetInfoService.cs
index 3be2de777..dcc42f2b1 100644
--- a/src/Modules/SpectatorTargetInfoModule/Interfaces/ISpectatorTargetInfoService.cs
+++ b/src/Modules/SpectatorTargetInfoModule/Interfaces/ISpectatorTargetInfoService.cs
@@ -25,6 +25,12 @@ public interface ISpectatorTargetInfoService
///
///
public Task ClearCheckpointsAsync();
+
+ ///
+ /// Clears all registered checkpoint times of the given player.
+ ///
+ ///
+ public Task ClearCheckpointsAsync(string playerLogin);
///
/// Retrieve an IOnlinePlayer instance by their login.
@@ -59,9 +65,9 @@ public interface ISpectatorTargetInfoService
///
/// Remove a player from the spectators list.
///
- ///
+ ///
///
- public Task RemovePlayerFromSpectatorsListAsync(string spectatorLogin);
+ public Task RemovePlayerAsync(string playerLogin);
///
/// Gets the logins of a players spectating the given target.
@@ -98,6 +104,12 @@ public interface ISpectatorTargetInfoService
///
public Dictionary GetCheckpointTimes();
+ ///
+ /// Returns the current spectator targets.
+ ///
+ ///
+ public Dictionary GetSpectatorTargets();
+
///
/// Resets the widget for all spectating players.
///
@@ -166,10 +178,23 @@ public interface ISpectatorTargetInfoService
public Task FetchAndCacheTeamInfoAsync();
///
- /// Updates whether team mode is active or not.
+ /// Updates whether team mode is active.
+ ///
+ ///
+ public Task DetectIsTeamsModeAsync();
+
+ ///
+ /// Detects whether time attack mode is active.
+ ///
+ ///
+ public Task DetectIsTimeAttackModeAsync();
+
+ ///
+ /// Manually sets active state of time attack mode.
///
+ ///
///
- public Task UpdateIsTeamsModeAsync();
+ public Task UpdateIsTimeAttackModeAsync(bool isTimeAttack);
///
/// Hides the default game mode UI.
diff --git a/src/Modules/SpectatorTargetInfoModule/Models/CheckpointsGroup.cs b/src/Modules/SpectatorTargetInfoModule/Models/CheckpointsGroup.cs
index 07f882846..b2db04336 100644
--- a/src/Modules/SpectatorTargetInfoModule/Models/CheckpointsGroup.cs
+++ b/src/Modules/SpectatorTargetInfoModule/Models/CheckpointsGroup.cs
@@ -18,9 +18,16 @@ public int GetRank(string playerLogin)
{
return rank;
}
+
rank++;
}
return rank;
}
+
+ public bool ForgetPlayer(string playerLogin) =>
+ (from checkpointData in this
+ where checkpointData.player.GetLogin() == playerLogin
+ select this.Remove(checkpointData)
+ ).FirstOrDefault();
}
diff --git a/src/Modules/SpectatorTargetInfoModule/Services/SpectatorTargetInfoService.cs b/src/Modules/SpectatorTargetInfoModule/Services/SpectatorTargetInfoService.cs
index 9af83f349..f8489efd6 100644
--- a/src/Modules/SpectatorTargetInfoModule/Services/SpectatorTargetInfoService.cs
+++ b/src/Modules/SpectatorTargetInfoModule/Services/SpectatorTargetInfoService.cs
@@ -33,14 +33,18 @@ ILogger logger
private const string ReportTargetTemplate = "SpectatorTargetInfoModule.ReportSpecTarget";
private const string WidgetTemplate = "SpectatorTargetInfoModule.SpectatorTargetInfo";
+ private readonly object _checkpointTimesMutex = new();
+ private readonly object _spectatorTargetsMutex = new();
private readonly Dictionary _checkpointTimes = new(); // cp-id -> CheckpointsGroup
private readonly Dictionary _spectatorTargets = new(); // login -> IOnlinePlayer
private readonly Dictionary _teamInfos = new();
+ private bool _isTimeAttackMode;
private bool _isTeamsMode;
public async Task InitializeAsync()
{
- await UpdateIsTeamsModeAsync();
+ await DetectIsTeamsModeAsync();
+ await DetectIsTimeAttackModeAsync();
await FetchAndCacheTeamInfoAsync();
await SendReportSpectatorTargetManialinkAsync();
await HideGameModeUiAsync();
@@ -53,15 +57,18 @@ public async Task AddCheckpointAsync(string playerLogin, int checkpointIndex, in
{
var player = await GetOnlinePlayerByLoginAsync(playerLogin);
var newCheckpointData = new CheckpointData(player, checkpointTime);
+ CheckpointsGroup checkpointsGroup = [];
- if (!_checkpointTimes.TryGetValue(checkpointIndex, out var checkpointGroup))
+ lock (_checkpointTimesMutex)
{
- checkpointGroup = [];
- _checkpointTimes.Add(checkpointIndex, checkpointGroup);
- }
+ if (_checkpointTimes.TryGetValue(checkpointIndex, out var existingCheckpointGroup))
+ {
+ checkpointsGroup = existingCheckpointGroup;
+ }
- checkpointGroup.Add(newCheckpointData);
- _checkpointTimes[checkpointIndex] = checkpointGroup;
+ checkpointsGroup.Add(newCheckpointData);
+ _checkpointTimes[checkpointIndex] = checkpointsGroup;
+ }
var spectatorLogins = GetLoginsOfPlayersSpectatingTarget(player).ToList();
if (spectatorLogins.IsNullOrEmpty())
@@ -69,16 +76,42 @@ public async Task AddCheckpointAsync(string playerLogin, int checkpointIndex, in
return;
}
- var leadingCheckpointData = checkpointGroup.First();
+ var leadingCheckpointData = checkpointsGroup.First();
var timeDifference = GetTimeDifference(leadingCheckpointData.time, newCheckpointData.time);
- await SendSpectatorInfoWidgetAsync(spectatorLogins, player, checkpointGroup.GetRank(playerLogin),
- timeDifference);
+ await SendSpectatorInfoWidgetAsync(
+ spectatorLogins,
+ player,
+ checkpointsGroup.GetRank(playerLogin),
+ timeDifference
+ );
}
public Task ClearCheckpointsAsync()
{
- _checkpointTimes.Clear();
+ lock (_checkpointTimesMutex)
+ {
+ _checkpointTimes.Clear();
+ }
+
+ return Task.CompletedTask;
+ }
+
+ public Task ClearCheckpointsAsync(string playerLogin)
+ {
+ if (!_isTimeAttackMode)
+ {
+ //New round event is going to clear the entries.
+ return Task.CompletedTask;
+ }
+
+ lock (_checkpointTimesMutex)
+ {
+ foreach (var checkpointGroup in _checkpointTimes.Values)
+ {
+ checkpointGroup.ForgetPlayer(playerLogin);
+ }
+ }
return Task.CompletedTask;
}
@@ -101,12 +134,15 @@ public Task ClearCheckpointsAsync()
var targetPlayer = await GetOnlinePlayerByLoginAsync(targetLogin);
- if (_spectatorTargets.TryGetValue(spectatorLogin, out var target) && target == targetPlayer)
+ lock (_spectatorTargetsMutex)
{
- return null; //Player is already spectating target
- }
+ if (_spectatorTargets.TryGetValue(spectatorLogin, out var target) && target == targetPlayer)
+ {
+ return null; //Player is already spectating target
+ }
- _spectatorTargets[spectatorLogin] = targetPlayer;
+ _spectatorTargets[spectatorLogin] = targetPlayer;
+ }
logger.LogTrace("Updated spectator target {spectatorLogin} -> {targetLogin}.", spectatorLogin,
targetLogin);
@@ -123,11 +159,26 @@ public async Task SetSpectatorTargetAndSendAsync(string spectatorLogin, string t
}
}
- public Task RemovePlayerFromSpectatorsListAsync(string spectatorLogin)
+ public Task RemovePlayerAsync(string playerLogin)
{
- if (_spectatorTargets.Remove(spectatorLogin))
+ lock (_spectatorTargetsMutex)
{
- logger.LogTrace("Removed spectator {spectatorLogin}.", spectatorLogin);
+ if (_spectatorTargets.Remove(playerLogin))
+ {
+ //Player was spectator
+ logger.LogTrace("Removed spectator {spectatorLogin}.", playerLogin);
+
+ return Task.CompletedTask;
+ }
+
+ //Player is driver, get all spectators
+ var spectatorLoginsToRemove = _spectatorTargets.Where(kv => kv.Value.GetLogin() == playerLogin)
+ .Select(kv => kv.Key);
+
+ foreach (var spectatorLogin in spectatorLoginsToRemove)
+ {
+ _spectatorTargets.Remove(spectatorLogin);
+ }
}
return Task.CompletedTask;
@@ -135,13 +186,14 @@ public Task RemovePlayerFromSpectatorsListAsync(string spectatorLogin)
public IEnumerable GetLoginsOfPlayersSpectatingTarget(IOnlinePlayer targetPlayer)
{
- return _spectatorTargets.Where(specTarget => specTarget.Value.AccountId == targetPlayer.AccountId)
+ return GetSpectatorTargets()
+ .Where(specTarget => specTarget.Value.AccountId == targetPlayer.AccountId)
.Select(specTarget => specTarget.Key);
}
public int GetTimeDifference(int leadingCheckpointTime, int targetCheckpointTime)
{
- return targetCheckpointTime - leadingCheckpointTime;
+ return int.Abs(targetCheckpointTime - leadingCheckpointTime);
}
public string GetTeamColor(PlayerTeam team)
@@ -152,7 +204,8 @@ public string GetTeamColor(PlayerTeam team)
public int GetLastCheckpointIndexOfPlayer(IOnlinePlayer player)
{
var playerLogin = player.GetLogin();
- foreach (var (checkpointIndex, checkpointsGroup) in _checkpointTimes.Reverse())
+
+ foreach (var (checkpointIndex, checkpointsGroup) in GetCheckpointTimes().Reverse())
{
if (checkpointsGroup.GetPlayerCheckpointData(playerLogin) != null)
{
@@ -163,12 +216,25 @@ public int GetLastCheckpointIndexOfPlayer(IOnlinePlayer player)
return -1;
}
- public Dictionary GetCheckpointTimes() =>
- _checkpointTimes;
+ public Dictionary GetCheckpointTimes()
+ {
+ lock (_checkpointTimesMutex)
+ {
+ return _checkpointTimes;
+ }
+ }
+
+ public Dictionary GetSpectatorTargets()
+ {
+ lock (_spectatorTargetsMutex)
+ {
+ return _spectatorTargets;
+ }
+ }
public async Task ResetWidgetForSpectatorsAsync()
{
- foreach (var (spectatorLogin, targetPlayer) in _spectatorTargets)
+ foreach (var (spectatorLogin, targetPlayer) in GetSpectatorTargets())
{
var widgetData = GetWidgetData(targetPlayer, 1, 0);
await SendSpectatorInfoWidgetAsync(spectatorLogin, targetPlayer, widgetData);
@@ -182,7 +248,7 @@ public async Task SendSpectatorInfoWidgetAsync(string spectatorLogin, IOnlinePla
var targetRank = 1;
var timeDifference = 0;
- if (_checkpointTimes.TryGetValue(checkpointIndex, out var checkpointsGroup))
+ if (GetCheckpointTimes().TryGetValue(checkpointIndex, out var checkpointsGroup))
{
var leadingCpData = checkpointsGroup.First();
var targetCpData = checkpointsGroup.GetPlayerCheckpointData(targetLogin);
@@ -236,13 +302,22 @@ public async Task FetchAndCacheTeamInfoAsync()
_teamInfos[PlayerTeam.Team2] = await server.Remote.GetTeamInfoAsync((int)PlayerTeam.Team2 + 1);
}
- public async Task UpdateIsTeamsModeAsync()
+ public async Task DetectIsTeamsModeAsync()
{
- _isTeamsMode =
- await matchSettingsService.GetCurrentModeAsync() is DefaultModeScriptName.Teams
- or DefaultModeScriptName.TmwtTeams;
+ _isTeamsMode = await matchSettingsService.GetCurrentModeAsync() is DefaultModeScriptName.Teams
+ or DefaultModeScriptName.TmwtTeams;
+ }
- logger.LogInformation("Team mode is {state}", _isTeamsMode ? "active" : "not active");
+ public async Task DetectIsTimeAttackModeAsync()
+ {
+ _isTimeAttackMode = await matchSettingsService.GetCurrentModeAsync() is DefaultModeScriptName.TimeAttack;
+ }
+
+ public Task UpdateIsTimeAttackModeAsync(bool isTimeAttack)
+ {
+ _isTimeAttackMode = isTimeAttack;
+
+ return Task.CompletedTask;
}
public Task HideGameModeUiAsync() =>
diff --git a/src/Modules/SpectatorTargetInfoModule/Templates/Scripts/SpectatorTargetInfo.ms b/src/Modules/SpectatorTargetInfoModule/Templates/Scripts/SpectatorTargetInfo.ms
index aba5f6be8..1f4e2d572 100644
--- a/src/Modules/SpectatorTargetInfoModule/Templates/Scripts/SpectatorTargetInfo.ms
+++ b/src/Modules/SpectatorTargetInfoModule/Templates/Scripts/SpectatorTargetInfo.ms
@@ -73,7 +73,7 @@ Void FocusPlayer(CSmPlayer _Player) {
Void SpecPrevious(CMlLabel button, Boolean focus){
AnimatePop(button);
declare CSmPlayer target <=> GetNextSpawnedPlayer();
- if(target == Null && focus){
+ if(target != Null && focus){
FocusPlayer(target);
}
}
diff --git a/tests/Modules/SpectatorTargetInfoModule.Tests/Controllers/SpectatorTargetEventControllerTests.cs b/tests/Modules/SpectatorTargetInfoModule.Tests/Controllers/SpectatorTargetEventControllerTests.cs
index b2e30ce90..62441c19a 100644
--- a/tests/Modules/SpectatorTargetInfoModule.Tests/Controllers/SpectatorTargetEventControllerTests.cs
+++ b/tests/Modules/SpectatorTargetInfoModule.Tests/Controllers/SpectatorTargetEventControllerTests.cs
@@ -24,27 +24,28 @@ public async Task Removes_Player_From_Spectators_On_Disconnect()
{
var login = "*fakeplayer_unittest*";
- await Controller.OnPlayerDisconnectAsync(null, new PlayerGbxEventArgs { Login = login });
+ await Controller.OnPlayerDisconnectAsync(null!, new PlayerGbxEventArgs { Login = login });
- _spectatorTargetService.Verify(sts => sts.RemovePlayerFromSpectatorsListAsync(login));
+ _spectatorTargetService.Verify(sts => sts.RemovePlayerAsync(login));
}
[Fact]
- public async Task Updates_Team_Mode_On_New_Map()
+ public async Task Detects_Team_And_TimeAttack_Mode_On_New_Map()
{
- await Controller.OnBeginMapAsync(null, new MapGbxEventArgs());
+ await Controller.OnBeginMapAsync(null!, new MapGbxEventArgs());
- _spectatorTargetService.Verify(sts => sts.UpdateIsTeamsModeAsync());
+ _spectatorTargetService.Verify(sts => sts.DetectIsTeamsModeAsync());
+ _spectatorTargetService.Verify(sts => sts.DetectIsTimeAttackModeAsync());
}
[Fact]
- public async Task Registers_Ceckpoint_Times()
+ public async Task Registers_Checkpoint_Times()
{
var login = "*fakeplayer_unittest*";
var checkpointId = 3;
var lapTime = 1234;
- await Controller.OnWayPointAsync(null,
+ await Controller.OnWayPointAsync(null!,
new WayPointEventArgs
{
Login = login,
@@ -55,9 +56,9 @@ await Controller.OnWayPointAsync(null,
CheckpointInLap = checkpointId,
IsEndRace = false,
IsEndLap = false,
- CurrentRaceCheckpoints = null,
- CurrentLapCheckpoints = null,
- BlockId = null,
+ CurrentRaceCheckpoints = [],
+ CurrentLapCheckpoints = [],
+ BlockId = "",
Speed = 0,
Time = 0
});
@@ -68,18 +69,47 @@ await Controller.OnWayPointAsync(null,
[Fact]
public async Task Resets_Collected_Data_On_New_Round()
{
- await Controller.OnNewRoundAsync(null, new RoundEventArgs { Count = 0, Time = 0 });
+ await Controller.OnNewRoundAsync(null!, new RoundEventArgs { Count = 0, Time = 0 });
_spectatorTargetService.Verify(sts => sts.ClearCheckpointsAsync());
_spectatorTargetService.Verify(sts => sts.FetchAndCacheTeamInfoAsync());
+ _spectatorTargetService.Verify(sts => sts.ResetWidgetForSpectatorsAsync());
}
[Fact]
public async Task Resets_Collected_Data_On_New_Warm_Up_Round()
{
- await Controller.OnNewWarmUpRoundAsync(null, new WarmUpRoundEventArgs { Total = 3, Current = 1 });
+ await Controller.OnNewWarmUpRoundAsync(null!, new WarmUpRoundEventArgs { Total = 3, Current = 1 });
_spectatorTargetService.Verify(sts => sts.ClearCheckpointsAsync());
_spectatorTargetService.Verify(sts => sts.FetchAndCacheTeamInfoAsync());
+ _spectatorTargetService.Verify(sts => sts.ResetWidgetForSpectatorsAsync());
+ }
+
+ [Fact]
+ public async Task Sets_TimeAttack_Mode_To_Active_At_Warm_Up_Start()
+ {
+ await Controller.OnWarmUpStartAsync(null!, EventArgs.Empty);
+
+ _spectatorTargetService.Verify(sts => sts.UpdateIsTimeAttackModeAsync(true));
+ }
+
+ [Fact]
+ public async Task Detects_TimeAttack_Mode_At_Warm_Up_End()
+ {
+ await Controller.OnWarmUpEndAsync(null!, EventArgs.Empty);
+
+ _spectatorTargetService.Verify(sts => sts.DetectIsTimeAttackModeAsync());
+ }
+
+ [Fact]
+ public async Task Clears_Checkpoints_Of_Player_On_Give_Up()
+ {
+ var eventArgs = new PlayerUpdateEventArgs { Login = "*fakeplayer1*", AccountId = "*fakeplayer1*", Time = 0 };
+
+ await _spectatorTargetService.Object.UpdateIsTimeAttackModeAsync(true);
+ await Controller.OnPlayerGiveUpAsync(null!, eventArgs);
+
+ _spectatorTargetService.Verify(sts => sts.ClearCheckpointsAsync(eventArgs.Login));
}
}
diff --git a/tests/Modules/SpectatorTargetInfoModule.Tests/Controllers/SpectatorTargetInfoManialinkControllerTests.cs b/tests/Modules/SpectatorTargetInfoModule.Tests/Controllers/SpectatorTargetInfoManialinkControllerTests.cs
index 355a7f8cb..6be76d32c 100644
--- a/tests/Modules/SpectatorTargetInfoModule.Tests/Controllers/SpectatorTargetInfoManialinkControllerTests.cs
+++ b/tests/Modules/SpectatorTargetInfoModule.Tests/Controllers/SpectatorTargetInfoManialinkControllerTests.cs
@@ -22,7 +22,7 @@ public SpectatorTargetInfoManialinkControllerTests()
}
[Fact]
- public async Task SetsSpectatorTargetIfGivenLoginIsValid()
+ public async Task Sets_Spectator_Target_If_Given_Login_Is_Valid()
{
_actor.Setup(actor => actor.AccountId)
.Returns("*fakeplayer_spectator*");
@@ -33,12 +33,12 @@ public async Task SetsSpectatorTargetIfGivenLoginIsValid()
await Controller.ReportSpectatorTargetAsync(targetLogin);
_spectatorTargetService.Verify(st => st.SetSpectatorTargetAndSendAsync(spectatorLogin, targetLogin), Times.Once);
- _spectatorTargetService.Verify(st => st.RemovePlayerFromSpectatorsListAsync(spectatorLogin), Times.Never);
+ _spectatorTargetService.Verify(st => st.RemovePlayerAsync(spectatorLogin), Times.Never);
_spectatorTargetService.Verify(st => st.HideSpectatorInfoWidgetAsync(spectatorLogin), Times.Never);
}
[Fact]
- public async Task RemoveSpectatorIfTargetIsEmpty()
+ public async Task Remove_Spectator_If_Target_Is_Empty()
{
_actor.Setup(actor => actor.AccountId)
.Returns("*fakeplayer_spectator*");
@@ -49,12 +49,12 @@ public async Task RemoveSpectatorIfTargetIsEmpty()
await Controller.ReportSpectatorTargetAsync(targetLogin);
_spectatorTargetService.Verify(st => st.SetSpectatorTargetAndSendAsync(spectatorLogin, targetLogin), Times.Never);
- _spectatorTargetService.Verify(st => st.RemovePlayerFromSpectatorsListAsync(spectatorLogin), Times.Once);
+ _spectatorTargetService.Verify(st => st.RemovePlayerAsync(spectatorLogin), Times.Once);
_spectatorTargetService.Verify(st => st.HideSpectatorInfoWidgetAsync(spectatorLogin), Times.Once);
}
[Fact]
- public async Task RemoveSpectatorIfTargetIsSpectatorThemselvesEmpty()
+ public async Task Remove_Spectator_If_Target_Is_Spectator_Themselves()
{
_actor.Setup(actor => actor.AccountId)
.Returns("*fakeplayer_spectator*");
@@ -65,7 +65,7 @@ public async Task RemoveSpectatorIfTargetIsSpectatorThemselvesEmpty()
await Controller.ReportSpectatorTargetAsync(targetLogin);
_spectatorTargetService.Verify(st => st.SetSpectatorTargetAndSendAsync(spectatorLogin, targetLogin), Times.Never);
- _spectatorTargetService.Verify(st => st.RemovePlayerFromSpectatorsListAsync(spectatorLogin), Times.Once);
+ _spectatorTargetService.Verify(st => st.RemovePlayerAsync(spectatorLogin), Times.Once);
_spectatorTargetService.Verify(st => st.HideSpectatorInfoWidgetAsync(spectatorLogin), Times.Once);
}
}
diff --git a/tests/Modules/SpectatorTargetInfoModule.Tests/Models/CheckpointsGroupTests.cs b/tests/Modules/SpectatorTargetInfoModule.Tests/Models/CheckpointsGroupTests.cs
index 4d8c077fc..f83318498 100644
--- a/tests/Modules/SpectatorTargetInfoModule.Tests/Models/CheckpointsGroupTests.cs
+++ b/tests/Modules/SpectatorTargetInfoModule.Tests/Models/CheckpointsGroupTests.cs
@@ -21,7 +21,7 @@ public Task Gets_Player_Data_From_Group()
new CheckpointData(new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer12*" }, 2));
var time = checkpointsGroup.GetPlayerCheckpointData(targetPlayer.GetLogin())?.time;
-
+
Assert.Equal(1, time);
return Task.CompletedTask;
@@ -51,7 +51,7 @@ public Task Gets_Rank_Of_Player()
new CheckpointData(new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer12*" }, 2));
var rank = checkpointsGroup.GetRank(targetPlayer.AccountId);
-
+
Assert.Equal(2, rank);
return Task.CompletedTask;
@@ -72,7 +72,7 @@ public Task Gets_Rank_Of_Player_Correctly_If_Another_Player_Has_The_Same_Time_Be
new CheckpointData(new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer12*" }, 2));
var rank = checkpointsGroup.GetRank(targetPlayer.AccountId);
-
+
Assert.Equal(3, rank);
return Task.CompletedTask;
@@ -93,9 +93,38 @@ public Task Gets_Rank_Of_Player_Correctly_If_Another_Player_Has_The_Same_Time_Af
new CheckpointData(new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer12*" }, 2));
var rank = checkpointsGroup.GetRank(targetPlayer.AccountId);
-
+
Assert.Equal(2, rank);
return Task.CompletedTask;
}
+
+ [Fact]
+ public Task Forgets_Given_Player()
+ {
+ var checkpointsGroup = new CheckpointsGroup();
+ var targetPlayer = new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer1*" };
+
+ checkpointsGroup.Add(new CheckpointData(new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer10*" }, 0));
+ checkpointsGroup.Add(new CheckpointData(targetPlayer, 1));
+ checkpointsGroup.Add(new CheckpointData(new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer11*" }, 1));
+ checkpointsGroup.Add(new CheckpointData(new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer12*" }, 2));
+
+ var entryRemoved = checkpointsGroup.ForgetPlayer(targetPlayer.GetLogin());
+
+ Assert.True(entryRemoved);
+
+ return Task.CompletedTask;
+ }
+
+ [Fact]
+ public Task Does_Not_Forget_Non_Existent_Player()
+ {
+ var checkpointsGroup = new CheckpointsGroup();
+ var entryRemoved = checkpointsGroup.ForgetPlayer("*fakeplayer1*");
+
+ Assert.False(entryRemoved);
+
+ return Task.CompletedTask;
+ }
}
diff --git a/tests/Modules/SpectatorTargetInfoModule.Tests/Services/SpectatorTargetInfoServiceTests.cs b/tests/Modules/SpectatorTargetInfoModule.Tests/Services/SpectatorTargetInfoServiceTests.cs
index 66f1e7f8e..8fb9cb192 100644
--- a/tests/Modules/SpectatorTargetInfoModule.Tests/Services/SpectatorTargetInfoServiceTests.cs
+++ b/tests/Modules/SpectatorTargetInfoModule.Tests/Services/SpectatorTargetInfoServiceTests.cs
@@ -72,7 +72,7 @@ public async Task Adds_And_Clears_Checkpoint_Data()
await spectatorTargetService.ClearCheckpointsAsync();
var checkpointTime = spectatorTargetService.GetCheckpointTimes();
-
+
Assert.Empty(checkpointTime);
}
@@ -143,6 +143,58 @@ public async Task Removes_Spectator_If_Target_Login_Is_Null()
Assert.DoesNotContain("*fakeplayer1*", spectatorOfPlayer);
}
+ [Fact]
+ public async Task Removes_Spectator_From_Spectator_Target_Repository()
+ {
+ var spectatorTargetService = ServiceMock();
+ var spectatorLogin = "*fakeplayer_spec*";
+
+ await spectatorTargetService.AddCheckpointAsync("*fakeplayer1*", 1, 1);
+ await spectatorTargetService.SetSpectatorTargetAsync(spectatorLogin, "*fakeplayer1*");
+ await spectatorTargetService.RemovePlayerAsync(spectatorLogin);
+
+ var spectatorInRepo = spectatorTargetService
+ .GetSpectatorTargets()
+ .ContainsKey(spectatorLogin);
+
+ Assert.False(spectatorInRepo);
+ }
+
+ [Fact]
+ public async Task Removes_Driver_From_Spectator_Target_Repository()
+ {
+ var spectatorTargetService = ServiceMock();
+ var spectatorLogin1 = "*fakeplayer99*";
+ var spectatorLogin2 = "*fakeplayer98*";
+ var targetLogin = "*fakeplayer2*";
+
+ _playerManager.Setup(pm => pm.GetOnlinePlayerAsync("*fakeplayer1*"))
+ .ReturnsAsync(new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer1*" });
+ _playerManager.Setup(pm => pm.GetOnlinePlayerAsync("*fakeplayer2*"))
+ .ReturnsAsync(new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer2*" });
+ _playerManager.Setup(pm => pm.GetOnlinePlayerAsync("*fakeplayer3*"))
+ .ReturnsAsync(new OnlinePlayer { State = PlayerState.Playing, AccountId = "*fakeplayer3*" });
+
+ await spectatorTargetService.AddCheckpointAsync("*fakeplayer1*", 1, 1);
+ await spectatorTargetService.AddCheckpointAsync(targetLogin, 2, 2);
+ await spectatorTargetService.AddCheckpointAsync("*fakeplayer3*", 3, 3);
+ await spectatorTargetService.SetSpectatorTargetAsync(spectatorLogin1, targetLogin);
+ await spectatorTargetService.SetSpectatorTargetAsync(spectatorLogin2, targetLogin);
+
+ await spectatorTargetService.RemovePlayerAsync(targetLogin);
+
+ var targetPlayerInRepo = spectatorTargetService
+ .GetSpectatorTargets()
+ .Any(kv => kv.Value.GetLogin() == targetLogin);
+
+ var spectatorsOfTargetInRepo = spectatorTargetService
+ .GetSpectatorTargets()
+ .Any(kv => kv.Key == spectatorLogin1 || kv.Key == spectatorLogin2);
+
+ Assert.False(targetPlayerInRepo);
+ Assert.False(spectatorsOfTargetInRepo);
+ }
+
[Fact]
public async Task Gets_Logins_Spectating_The_Given_Target()
{
@@ -201,7 +253,7 @@ public Task Gets_Rank_From_Sorted_Checkpoints_List()
var player2Rank = checkpointsList.GetRank("*fakeplayer2*");
var player3Rank = checkpointsList.GetRank("*fakeplayer3*");
var player4Rank = checkpointsList.GetRank("*fakeplayer4*");
-
+
Assert.Equal(1, player1Rank);
Assert.Equal(2, player2Rank);
Assert.Equal(4, player3Rank);
@@ -213,7 +265,7 @@ public Task Gets_Rank_From_Sorted_Checkpoints_List()
[Theory]
[InlineData(900, 1_000, 100)]
[InlineData(100, 999, 899)]
- [InlineData(400, 200, -200)]
+ [InlineData(400, 200, 200)]
public Task Calculates_Time_Difference(int leadingTime, int trailingTime, int expectedTime)
{
var spectatorTargetService = ServiceMock();
@@ -265,12 +317,12 @@ public async Task Gets_The_Team_Color()
_server.Remote.Setup(remote => remote.GetTeamInfoAsync(2))
.ReturnsAsync(new TmTeamInfo { RGB = "111111" });
- await spectatorTargetService.UpdateIsTeamsModeAsync();
+ await spectatorTargetService.DetectIsTeamsModeAsync();
await spectatorTargetService.FetchAndCacheTeamInfoAsync();
var team1Color = spectatorTargetService.GetTeamColor(PlayerTeam.Team1);
var team2Color = spectatorTargetService.GetTeamColor(PlayerTeam.Team2);
-
+
Assert.Equal("FF0066", team1Color);
Assert.Equal("111111", team2Color);
}
@@ -304,12 +356,13 @@ public async Task Sends_The_Widget_To_The_Given_Player_With_Arguments()
.ReturnsAsync(new TmTeamInfo { RGB = "FF0066" });
var spectatorTargetService = ServiceMock();
- await spectatorTargetService.UpdateIsTeamsModeAsync();
+ await spectatorTargetService.DetectIsTeamsModeAsync();
await spectatorTargetService.FetchAndCacheTeamInfoAsync();
var widgetData = spectatorTargetService.GetWidgetData(targetPlayer, 2, 150);
await spectatorTargetService.SendSpectatorInfoWidgetAsync(spectatorLogin, targetPlayer, widgetData);
- _manialinkManager.Verify(mm => mm.SendManialinkAsync(spectatorLogin, "SpectatorTargetInfoModule.SpectatorTargetInfo",widgetData));
+ _manialinkManager.Verify(mm =>
+ mm.SendManialinkAsync(spectatorLogin, "SpectatorTargetInfoModule.SpectatorTargetInfo", widgetData));
}
[Fact]
@@ -332,7 +385,7 @@ public async Task Sends_The_Widget_To_The_Given_Players_With_Arguments()
.ReturnsAsync(new TmTeamInfo { RGB = "111111" });
var spectatorTargetService = ServiceMock();
- await spectatorTargetService.UpdateIsTeamsModeAsync();
+ await spectatorTargetService.DetectIsTeamsModeAsync();
await spectatorTargetService.FetchAndCacheTeamInfoAsync();
await spectatorTargetService.SendSpectatorInfoWidgetAsync(spectatorLogins, targetPlayer, 2, 150);
@@ -373,15 +426,17 @@ public async Task Sends_The_Widget_To_The_Given_Player_Without_Time_And_Checkpoi
.ReturnsAsync(otherPlayer);
var spectatorTargetService = ServiceMock();
- await spectatorTargetService.UpdateIsTeamsModeAsync();
+ await spectatorTargetService.DetectIsTeamsModeAsync();
await spectatorTargetService.FetchAndCacheTeamInfoAsync();
await spectatorTargetService.AddCheckpointAsync(otherPlayer.GetLogin(), 2, 1000);
await spectatorTargetService.AddCheckpointAsync(targetPlayer.GetLogin(), 2, 1234);
await spectatorTargetService.SendSpectatorInfoWidgetAsync(spectatorLogin, targetPlayer);
-
+
_manialinkManager.Verify(mm =>
- mm.SendManialinkAsync(spectatorLogin, "SpectatorTargetInfoModule.SpectatorTargetInfo", It.IsAny