Rework MapPreview custom rule handling.
The previous asynchronous approach did not work particularly well, leading to large janks when switching to custom maps or opening the mission browser. This commit introduces two key changes: * Rule loading for WorldActorInfo and PlayerActorInfo is made synchronous, in preparation for the next commit which will significantly optimize this path. * The full ruleset loading, which is required for map validation, is moved to the server-side and managed by a new ServerMapStatusCache. The previous syntax check is expanded to include the ability to run lint tests.
This commit is contained in:
100
OpenRA.Game/Server/MapStatusCache.cs
Normal file
100
OpenRA.Game/Server/MapStatusCache.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenRA.Network;
|
||||
|
||||
namespace OpenRA.Server
|
||||
{
|
||||
public interface ILintServerMapPass { void Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules); }
|
||||
|
||||
public class MapStatusCache
|
||||
{
|
||||
readonly Dictionary<MapPreview, Session.MapStatus> cache = new Dictionary<MapPreview, Session.MapStatus>();
|
||||
readonly Action<string, Session.MapStatus> onStatusChanged;
|
||||
readonly bool enableRemoteLinting;
|
||||
readonly ModData modData;
|
||||
|
||||
public MapStatusCache(ModData modData, Action<string, Session.MapStatus> onStatusChanged, bool enableRemoteLinting)
|
||||
{
|
||||
this.modData = modData;
|
||||
this.enableRemoteLinting = enableRemoteLinting;
|
||||
this.onStatusChanged = onStatusChanged;
|
||||
}
|
||||
|
||||
void RunLintTests(MapPreview map, Ruleset rules)
|
||||
{
|
||||
var status = cache[map];
|
||||
var failed = false;
|
||||
|
||||
Action<string> onLintFailure = message =>
|
||||
{
|
||||
Log.Write("server", "Map {0} failed lint with error: {1}", map.Title, message);
|
||||
failed = true;
|
||||
};
|
||||
|
||||
Action<string> onLintWarning = _ => { };
|
||||
|
||||
foreach (var customMapPassType in modData.ObjectCreator.GetTypesImplementing<ILintServerMapPass>())
|
||||
{
|
||||
try
|
||||
{
|
||||
var customMapPass = (ILintServerMapPass)modData.ObjectCreator.CreateBasic(customMapPassType);
|
||||
customMapPass.Run(onLintFailure, onLintWarning, modData, map, rules);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
onLintFailure(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
status &= ~Session.MapStatus.Validating;
|
||||
status |= failed ? Session.MapStatus.Incompatible : Session.MapStatus.Playable;
|
||||
|
||||
cache[map] = status;
|
||||
onStatusChanged?.Invoke(map.Uid, status);
|
||||
}
|
||||
|
||||
public Session.MapStatus this[MapPreview map]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cache.TryGetValue(map, out var status))
|
||||
return status;
|
||||
|
||||
Ruleset rules = null;
|
||||
try
|
||||
{
|
||||
rules = map.LoadRuleset();
|
||||
|
||||
// Locally installed maps are assumed to not require linting
|
||||
status = enableRemoteLinting && map.Status != MapStatus.Available ? Session.MapStatus.Validating : Session.MapStatus.Playable;
|
||||
if (map.DefinesUnsafeCustomRules())
|
||||
status |= Session.MapStatus.UnsafeCustomRules;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("server", "Failed to load rules for `{0}` with error :{1}", map.Title, e.Message);
|
||||
status = Session.MapStatus.Incompatible;
|
||||
}
|
||||
|
||||
cache[map] = status;
|
||||
|
||||
if ((status & Session.MapStatus.Validating) != 0)
|
||||
ThreadPool.QueueUserWorkItem(_ => RunLintTests(map, rules));
|
||||
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user