Prevent remote map searches overwriting local or generated state.

This commit is contained in:
Paul Chote
2025-04-29 23:34:41 +01:00
committed by Gustas Kažukauskas
parent 4a4217c3b3
commit e8d710dee4
2 changed files with 49 additions and 35 deletions

View File

@@ -235,7 +235,7 @@ namespace OpenRA
.ToList(); .ToList();
foreach (var uid in queryUids) foreach (var uid in queryUids)
previews[uid].UpdateRemoteSearch(MapStatus.Searching, null, null); previews[uid].BeginRemoteSearch();
Task.Run(async () => Task.Run(async () =>
{ {
@@ -251,29 +251,21 @@ namespace OpenRA
try try
{ {
var result = await client.GetStreamAsync(url); var result = await client.GetStreamAsync(url);
var yaml = MiniYaml.FromStream(result, url, stringPool: stringPool); foreach (var kv in MiniYaml.FromStream(result, url, stringPool: stringPool))
foreach (var kv in yaml) previews[kv.Key].CompleteRemoteSearch(kv.Value, mapDetailsReceived);
previews[kv.Key].UpdateRemoteSearch(MapStatus.DownloadAvailable, kv.Value, mapDetailsReceived);
foreach (var uid in batchUids)
{
var p = previews[uid];
if (p.Status != MapStatus.DownloadAvailable)
p.UpdateRemoteSearch(MapStatus.Unavailable, null, null);
}
} }
catch (Exception e) catch (Exception e)
{ {
Log.Write("debug", "Remote map query failed with error:"); Log.Write("debug", "Remote map query failed with error:");
Log.Write("debug", e); Log.Write("debug", e);
Log.Write("debug", $"URL was: {url}"); Log.Write("debug", $"URL was: {url}");
}
foreach (var uid in batchUids) foreach (var uid in batchUids)
{ {
var p = previews[uid]; var p = previews[uid];
p.UpdateRemoteSearch(MapStatus.Unavailable, null, null); if (p.Status == MapStatus.Searching)
mapQueryFailed?.Invoke(p); p.CompleteRemoteSearch(null, mapQueryFailed);
}
} }
} }
} }

View File

@@ -196,6 +196,7 @@ namespace OpenRA
} }
static readonly CPos[] NoSpawns = []; static readonly CPos[] NoSpawns = [];
readonly object syncRoot = new();
readonly MapCache cache; readonly MapCache cache;
readonly ModData modData; readonly ModData modData;
IReadOnlyPackage package; IReadOnlyPackage package;
@@ -452,28 +453,37 @@ namespace OpenRA
newData.ModifiedDate = File.GetLastWriteTime(p.Name); newData.ModifiedDate = File.GetLastWriteTime(p.Name);
// Assign the new data atomically // Assign the new data atomically
innerData = newData; // Local maps have higher precedence than remote/generated maps,
// so should always replace their metadata
lock (syncRoot)
innerData = newData;
} }
public void UpdateRemoteSearch(MapStatus status, MiniYaml yaml, Action<MapPreview> parseMetadata = null) public void BeginRemoteSearch()
{ {
var newData = innerData.Clone(); var newData = innerData.Clone();
newData.Status = status;
newData.Class = MapClassification.Remote; newData.Class = MapClassification.Remote;
newData.Status = MapStatus.Searching;
if (status == MapStatus.DownloadAvailable) // We may have been resolved to a local/generated map by another
// async task. Make sure we don't stomp over their state!
lock (syncRoot)
if (innerData.Class == MapClassification.Unknown || innerData.Class == MapClassification.Remote)
innerData = newData;
}
public void CompleteRemoteSearch(MiniYaml yaml, Action<MapPreview> parseMetadata = null)
{
var newData = innerData.Clone();
newData.Class = MapClassification.Remote;
newData.Status = MapStatus.Unavailable;
if (yaml != null)
{ {
try try
{ {
var r = FieldLoader.Load<RemoteMapData>(yaml); var r = FieldLoader.Load<RemoteMapData>(yaml);
newData.Status = r.downloading ? MapStatus.DownloadAvailable : MapStatus.Unavailable;
// Map download has been disabled server side
if (!r.downloading)
{
newData.Status = MapStatus.Unavailable;
return;
}
newData.Title = r.title; newData.Title = r.title;
newData.Categories = r.categories; newData.Categories = r.categories;
newData.Author = r.author; newData.Author = r.author;
@@ -520,19 +530,27 @@ namespace OpenRA
{ {
Log.Write("debug", "Failed parsing mapserver response:"); Log.Write("debug", "Failed parsing mapserver response:");
Log.Write("debug", e); Log.Write("debug", e);
newData.Status = MapStatus.Unavailable;
} }
}
// Commit updated data before running the callbacks // We may have been resolved to a local/generated map by another
innerData = newData; // async task. Make sure we don't stomp over their state!
MapClassification mapClassification;
lock (syncRoot)
{
mapClassification = innerData.Class;
if (mapClassification == MapClassification.Remote)
innerData = newData;
}
if (mapClassification == MapClassification.Remote)
{
if (innerData.Preview != null) if (innerData.Preview != null)
cache.CacheMinimap(this); cache.CacheMinimap(this);
parseMetadata?.Invoke(this); parseMetadata?.Invoke(this);
} }
// Update the status and class unconditionally
innerData = newData;
} }
public void Install(string mapRepositoryUrl) public void Install(string mapRepositoryUrl)
@@ -605,7 +623,11 @@ namespace OpenRA
public void Invalidate() public void Invalidate()
{ {
innerData.Status = MapStatus.Unavailable; lock (syncRoot)
{
innerData.Class = MapClassification.Unknown;
innerData.Status = MapStatus.Unavailable;
}
} }
public void Dispose() public void Dispose()