Files
OpenRA/OpenRA.Game/PlayerDatabase.cs
RoosterDragon b7e0ed9b87 Improve lookups of nodes by key in MiniYaml.
When handling the Nodes collection in MiniYaml, individual nodes are located via one of two methods:

// Lookup a single key with linear search.
var node = yaml.Nodes.FirstOrDefault(n => n.Key == "SomeKey");

// Convert to dictionary, expecting many key lookups.
var dict = nodes.ToDictionary();

// Lookup a single key in the dictionary.
var node = dict["SomeKey"];

To simplify lookup of individual keys via linear search, provide helper methods NodeWithKeyOrDefault and NodeWithKey. These helpers do the equivalent of Single{OrDefault} searches. Whilst this requires checking the whole list, it provides a useful correctness check. Two duplicated keys in TS yaml are fixed as a result. We can also optimize the helpers to not use LINQ, avoiding allocation of the delegate to search for a key.

Adjust existing code to use either lnear searches or dictionary lookups based on whether it will be resolving many keys. Resolving few keys can be done with linear searches to avoid building a dictionary. Resolving many keys should be done with a dictionary to avoid quaradtic runtime from repeated linear searches.
2023-09-23 14:31:04 +02:00

118 lines
3.1 KiB
C#

#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Threading.Tasks;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA
{
public class PlayerDatabase : IGlobalModData
{
public readonly string Profile = "https://forum.openra.net/openra/info/";
public readonly int IconSize = 24;
// 512x512 is large enough for 49 unique 72x72 badges
// or 100 unique 42x42 badges
// or 441 unique 24x24 badges
// or some combination of the above if the DPI changes ingame
[FieldLoader.Ignore]
SheetBuilder sheetBuilder;
[FieldLoader.Ignore]
Cache<(PlayerBadge, int), Sprite> iconCache;
Sprite LoadSprite(string url, int density)
{
var spriteSize = IconSize * density;
var sprite = sheetBuilder.Allocate(new Size(spriteSize, spriteSize), 1f / density);
Task.Run(async () =>
{
try
{
var client = HttpClientFactory.Create();
var httpResponseMessage = await client.GetAsync(url);
var result = await httpResponseMessage.Content.ReadAsStreamAsync();
var icon = new Png(result);
if (icon.Width == spriteSize && icon.Height == spriteSize)
{
Game.RunAfterTick(() =>
{
Util.FastCopyIntoSprite(sprite, icon);
sprite.Sheet.CommitBufferedData();
});
}
}
catch { }
});
return sprite;
}
Sheet CreateSheet()
{
var sheet = new Sheet(SheetType.BGRA, new Size(512, 512));
// We must manually force the buffer creation to avoid a crash
// that is indirectly triggered by rendering from a Sheet that
// has not yet been written to.
sheet.CreateBuffer();
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
return sheet;
}
public PlayerBadge LoadBadge(MiniYaml yaml)
{
if (sheetBuilder == null)
{
sheetBuilder = new SheetBuilder(SheetType.BGRA, CreateSheet);
iconCache = new Cache<(PlayerBadge Badge, int Density), Sprite>(p =>
{
if (p.Density > 2 && !string.IsNullOrEmpty(p.Badge.Icon3x))
return LoadSprite(p.Badge.Icon3x, 3);
if (p.Density > 1 && !string.IsNullOrEmpty(p.Badge.Icon2x))
return LoadSprite(p.Badge.Icon2x, 2);
return LoadSprite(p.Badge.Icon, 1);
});
}
var labelNode = yaml.NodeWithKeyOrDefault("Label");
var icon24Node = yaml.NodeWithKeyOrDefault("Icon24");
var icon48Node = yaml.NodeWithKeyOrDefault("Icon48");
var icon72Node = yaml.NodeWithKeyOrDefault("Icon72");
if (labelNode == null)
return null;
return new PlayerBadge(
labelNode.Value.Value,
icon24Node?.Value.Value,
icon48Node?.Value.Value,
icon72Node?.Value.Value);
}
public Sprite GetIcon(PlayerBadge badge)
{
var ws = Game.Renderer.WindowScale;
var density = ws > 2 ? 3 : ws > 1 ? 2 : 1;
return iconCache[(badge, density)];
}
}
}