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

View File

@@ -196,6 +196,7 @@ namespace OpenRA
}
static readonly CPos[] NoSpawns = [];
readonly object syncRoot = new();
readonly MapCache cache;
readonly ModData modData;
IReadOnlyPackage package;
@@ -452,28 +453,37 @@ namespace OpenRA
newData.ModifiedDate = File.GetLastWriteTime(p.Name);
// 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();
newData.Status = status;
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
{
var r = FieldLoader.Load<RemoteMapData>(yaml);
// Map download has been disabled server side
if (!r.downloading)
{
newData.Status = MapStatus.Unavailable;
return;
}
newData.Status = r.downloading ? MapStatus.DownloadAvailable : MapStatus.Unavailable;
newData.Title = r.title;
newData.Categories = r.categories;
newData.Author = r.author;
@@ -520,19 +530,27 @@ namespace OpenRA
{
Log.Write("debug", "Failed parsing mapserver response:");
Log.Write("debug", e);
newData.Status = MapStatus.Unavailable;
}
}
// Commit updated data before running the callbacks
innerData = newData;
// We may have been resolved to a local/generated map by another
// 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)
cache.CacheMinimap(this);
parseMetadata?.Invoke(this);
}
// Update the status and class unconditionally
innerData = newData;
}
public void Install(string mapRepositoryUrl)
@@ -605,7 +623,11 @@ namespace OpenRA
public void Invalidate()
{
innerData.Status = MapStatus.Unavailable;
lock (syncRoot)
{
innerData.Class = MapClassification.Unknown;
innerData.Status = MapStatus.Unavailable;
}
}
public void Dispose()