Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 84 additions & 21 deletions apps/utility-sw/src/Commands/Practice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ private void SaveThrow(ulong steamId, string name)

thrown.name = name;
thrown.map = _library.Map;
thrown.workshop_map_id = Core.Engine.WorkshopId;
thrown.side = player.Controller.Team == Team.CT ? "CT" : "TERRORIST";
thrown.visibility = nameof(eLineupVisibility.Private);
thrown.plugin_version = ModuleVersion;
Expand Down Expand Up @@ -177,26 +178,85 @@ private void SaveThrow(ulong steamId, string name)

private void IngestSave(ulong steamId, LineupRecord thrown)
{
string workshopId = thrown.workshop_map_id ?? Core.Engine.WorkshopId ?? "";

_ = Task.Run(async () =>
{
string? id = await _api.Ingest(thrown);
UtilityApiClient.IngestResult result = await _api.Ingest(thrown, workshopId);

Core.Scheduler.NextTick(() =>
{
if (id != null)
PracticeSaveUtility.GivePanelId(thrown, result.Id);

if (result.Outcome == UtilityApiClient.eIngestOutcome.Held)
{
thrown.id = id;
return;
Tell(
steamId,
$" {ChatColors.Red}{thrown.name} could not reach the panel; it will retry"
);
}
else if (result.Outcome == UtilityApiClient.eIngestOutcome.Refused)
{
Tell(steamId, $" {ChatColors.Red}{thrown.name} was not saved");
}
else if (
result.Outcome == UtilityApiClient.eIngestOutcome.Delivered
&& PracticeSaveUtility.BelongsBackInLibrary(
thrown,
_library.Map,
_library.For(steamId)
)
)
{
_library.Add(steamId, thrown);
}

Tell(
steamId,
$" {ChatColors.Red}{thrown.name} could not reach the panel; it will retry"
);
});
});
}

private void OnSaveRetried(LineupRecord saved, UtilityApiClient.IngestResult result)
{
Core.Scheduler.NextTick(() =>
{
PracticeSaveUtility.GivePanelId(saved, result.Id);

if (!ulong.TryParse(saved.author_steam_id, out ulong steamId))
{
return;
}

if (result.Outcome == UtilityApiClient.eIngestOutcome.Refused)
{
Tell(steamId, $" {ChatColors.Red}{result.Name} was not saved");
return;
}

if (PracticeSaveUtility.BelongsBackInLibrary(saved, _library.Map, _library.For(steamId)))
{
_library.Add(steamId, saved);
}

Tell(steamId, $" {ChatColors.Green}{result.Name} reached the panel");
});
}

private void OnWithdrawnDeleteFailed(string author, string name)
{
if (ulong.TryParse(author, out ulong steamId))
{
Core.Scheduler.NextTick(() => TellStillOnPanel(steamId, name));
}
}

private void TellStillOnPanel(ulong steamId, string name)
{
Tell(
steamId,
$" {ChatColors.Red}{name} is still on the panel; "
+ $"try {ChatColors.Default}TAB ▸ Settings ▸ Reload Library"
);
}

// Leaving does not take a save with it. What was saved goes now, without
// a cloud that may not finish before an empty server stops ticking.
private void SendHeldSaves(ulong steamId)
Expand Down Expand Up @@ -1120,37 +1180,40 @@ private void DeleteLoaded(IPlayer player)
return;
}

_library.Remove(player.SteamID, loaded);
state.Results.RemoveAll(match => match.client_id == loaded.client_id);
Predicate<LineupRecord> isLoaded = PracticeSaveUtility.SameLineup(loaded);

_library.Remove(player.SteamID, isLoaded);
state.Index = PracticeSaveUtility.PruneResults(state.Results, state.Index, isLoaded);
state.Loaded = null;
_replay.ClearGhosts(player.SteamID);

Toast(player, $"Deleted · {DrillUtility.Name(loaded)}");

if (loaded.id == null)
string? id = PracticeSaveUtility.ForgetSaves(
loaded,
save => _heldSaves.Remove(save),
_api.Withdraw
);

_drill.LineupsRemoved(player.SteamID, isLoaded);

if (id == null)
{
return;
}

string id = loaded.id;
ulong steamId = player.SteamID;

_ = Task.Run(async () =>
{
bool deleted = await _api.Delete(id);
bool deleted = await _api.Delete(id, steamId.ToString());

if (deleted)
{
return;
}

Core.Scheduler.NextTick(() =>
Tell(
steamId,
$" {ChatColors.Red}{loaded.name} is still on the panel; "
+ $"try {ChatColors.Default}TAB ▸ Settings ▸ Reload Library"
)
);
Core.Scheduler.NextTick(() => TellStillOnPanel(steamId, loaded.name));
});
}

Expand Down
19 changes: 1 addition & 18 deletions apps/utility-sw/src/Commands/Solver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,24 +297,7 @@ private void Finished(

// The same hand-off .save makes, so a solved lineup reaches the panel by
// the one path that already retries.
_ = Task.Run(async () =>
{
string? id = await _api.Ingest(lineup);

Core.Scheduler.NextTick(() =>
{
if (id != null)
{
lineup.id = id;
return;
}

Tell(
steamId,
$" {ChatColors.Red}{lineup.name} could not reach the panel; it will retry"
);
});
});
IngestSave(steamId, lineup);
}

private void Refuse(ICommandContext context, CalibrationReport report)
Expand Down
43 changes: 43 additions & 0 deletions apps/utility-sw/src/Services/PracticeDrill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,49 @@ public bool Stop(ulong steamId)
return true;
}

// A step that cannot be scored ignores every throw, so a lineup the player
// deleted has to leave the run or the run stalls with no word why.
public void LineupDeleted(ulong steamId, string clientId)
{
if (!_runs.TryGetValue(steamId, out PracticeDrillRun? run))
{
return;
}

bool wasCurrent = run.Remove(clientId);

if (run.Length == 0)
{
run.End(eDrillEnd.Stopped);
Finish(steamId, run);
return;
}

if (wasCurrent)
{
Advance(steamId, run);
}
}

public void LineupsRemoved(ulong steamId, Predicate<LineupRecord> gone)
{
if (!_runs.TryGetValue(steamId, out PracticeDrillRun? run))
{
return;
}

List<string> removed = run
.Queue.Where(row => gone(row))
.Select(row => row.client_id)
.Distinct()
.ToList();

foreach (string clientId in removed)
{
LineupDeleted(steamId, clientId);
}
}

public bool Skip(ulong steamId)
{
if (!_runs.TryGetValue(steamId, out PracticeDrillRun? run) || !run.Skip())
Expand Down
4 changes: 2 additions & 2 deletions apps/utility-sw/src/Services/PracticeLibrary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ public void Add(ulong steamId, LineupRecord lineup)
lineups.Add(lineup);
}

public void Remove(ulong steamId, LineupRecord lineup)
public void Remove(ulong steamId, Predicate<LineupRecord> gone)
{
if (_lineups.TryGetValue(steamId, out List<LineupRecord>? lineups))
{
lineups.RemoveAll(existing => existing.client_id == lineup.client_id);
lineups.RemoveAll(gone);
}
}

Expand Down
3 changes: 3 additions & 0 deletions apps/utility-sw/src/Services/PracticeRecorder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ public void Forget(uint entityIndex)

public void Reset()
{
// A throw belongs to the map it was thrown on, and .save stamps whatever
// map the server is on now.
_history.Clear();
_armed.Clear();
_stationary.Clear();
_settling.Clear();
Expand Down
5 changes: 4 additions & 1 deletion apps/utility-sw/src/Services/PracticeSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ ILogger<PracticeSession> logger
// a changelevel takes.
public string? Map { get; set; }

// Read on the game thread with Map: this class fetches off it.
public string? WorkshopId { get; set; }

private int _refreshing;
private DateTime _lastAttempt = DateTime.MinValue;

Expand Down Expand Up @@ -92,7 +95,7 @@ public async Task Refresh()
return;
}

PracticeSessionData? session = await _api.Session(Map, relay);
PracticeSessionData? session = await _api.Session(Map, WorkshopId, relay);

// A failed fetch must not empty the roster: everyone already connected
// stays connected, and the door keeps the policy it had.
Expand Down
Loading
Loading