Skip to content
Draft
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
3 changes: 2 additions & 1 deletion DXMainClient/DXGUI/GameClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using Rampastring.Tools;
using Rampastring.XNAUI;
using System;
using ClientGUI;

Check warning on line 13 in DXMainClient/DXGUI/GameClass.cs

View workflow job for this annotation

GitHub Actions / build-clients (Ares)

The using directive for 'ClientGUI' appeared previously in this namespace

Check warning on line 13 in DXMainClient/DXGUI/GameClass.cs

View workflow job for this annotation

GitHub Actions / build-clients (TS)

The using directive for 'ClientGUI' appeared previously in this namespace

Check warning on line 13 in DXMainClient/DXGUI/GameClass.cs

View workflow job for this annotation

GitHub Actions / build-clients (YR)

The using directive for 'ClientGUI' appeared previously in this namespace
using DTAClient.Domain.Multiplayer;
using DTAClient.Domain.Multiplayer.CnCNet;
using DTAClient.DXGUI.Multiplayer;
Expand Down Expand Up @@ -257,7 +257,8 @@
.AddSingleton<TunnelHandler>()
.AddSingleton<DiscordHandler>()
.AddSingleton<PrivateMessageHandler>()
.AddSingleton<MapLoader>();
.AddSingleton<MapLoader>()
.AddSingleton<MapTextureCacheManager>();

// singleton xna controls - same instance on each request
services
Expand Down
25 changes: 18 additions & 7 deletions DXMainClient/DXGUI/Multiplayer/GameInformationPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public GameInformationPanel(WindowManager windowManager, MapLoader mapLoader)
DrawMode = ControlDrawMode.UNIQUE_RENDER_TARGET;
}

private MapLoader mapLoader;
private readonly MapLoader mapLoader;

private XNALabel lblGameInformation;
private XNALabel lblGameMode;
Expand All @@ -43,7 +43,7 @@ public GameInformationPanel(WindowManager windowManager, MapLoader mapLoader)

private GenericHostedGame game = null;

private bool disposeTextures = false;
private bool disposeTextures = true;
private Texture2D mapTexture = null;
private Texture2D noMapPreviewTexture = null;

Expand Down Expand Up @@ -194,17 +194,28 @@ public void SetInfo(GenericHostedGame game)

if (mapLoader != null)
{
mapTexture = mapLoader.GameModeMaps.Find(m => m.Map.UntranslatedName.Equals(game.Map, StringComparison.InvariantCultureIgnoreCase) && m.Map.IsPreviewTextureCached())?.Map?.LoadPreviewTexture();
Map map = mapLoader.GameModeMaps.Find(m => m.Map.UntranslatedName.Equals(game.Map, StringComparison.InvariantCultureIgnoreCase))?.Map;

if (map != null)
{
if (map.IsPreviewTextureAvailableAsFile())
{
mapTexture = map.LoadPreviewTexture();
disposeTextures = true;
}
else
{
mapTexture = AssetLoader.TextureFromImage(mapLoader.MapTextureCacheManager.GetMapTextureIfAvailable(map));
disposeTextures = true;
}
}

if (mapTexture == null && noMapPreviewTexture != null)
{
Debug.Assert(!noMapPreviewTexture.IsDisposed, "noMapPreviewTexture should not be disposed.");
mapTexture = noMapPreviewTexture;
disposeTextures = false;
}
else
{
disposeTextures = true;
}
}
}

Expand Down
75 changes: 75 additions & 0 deletions DXMainClient/DXGUI/Multiplayer/MapTextureCacheManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using DTAClient.Domain.Multiplayer;

using SixLabors.ImageSharp;

namespace DTAClient.DXGUI.Multiplayer
{
public class MapTextureCacheManager : IDisposable
{
public const int MaxCacheSize = 100;
public const int SleepIntervalMS = 100;

private readonly ConcurrentDictionary<Map, Image> mapTextures = [];

private readonly ConcurrentDictionary<Map, byte> missedMaps = [];

private readonly CancellationTokenSource cancellationTokenSource = new();

public MapTextureCacheManager() =>
Task.Run(() => MapTextureLoadingService(cancellationTokenSource.Token));

public void Dispose() =>
cancellationTokenSource?.Cancel();

public Image GetMapTextureIfAvailable(Map map)
{
if (mapTextures.TryGetValue(map, out Image image))
return image;

if (missedMaps.Count < MaxCacheSize)
missedMaps.TryAdd(map, 0);

return null;
}

private async Task MapTextureLoadingService(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();

// Clear cache if it's too big
if (mapTextures.Count > MaxCacheSize)
mapTextures.Clear();

if (!missedMaps.IsEmpty)
{
var missedMapCopy = missedMaps.ToArray();
foreach ((Map missedMap, _) in missedMapCopy)
{
if (mapTextures.Count > MaxCacheSize)
break;

missedMaps.TryRemove(missedMap, out _);

if (mapTextures.ContainsKey(missedMap))
continue;

Image image = await Task.Run(missedMap.ExtractMapPreview);
mapTextures.TryAdd(missedMap, image);
}

}

await Task.Delay(SleepIntervalMS);
}
}

}
}
3 changes: 3 additions & 0 deletions DXMainClient/Domain/Multiplayer/GameModeMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public GameModeMap(GameMode gameMode, Map map, bool isFavorite)
IsFavorite = isFavorite;
}

public override string ToString()
=> $"{GameMode?.Name} - {Map?.Name}";

protected bool Equals(GameModeMap other) => Equals(GameMode, other.GameMode) && Equals(Map, other.Map);

public override int GetHashCode()
Expand Down
10 changes: 6 additions & 4 deletions DXMainClient/Domain/Multiplayer/Map.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
using Color = Microsoft.Xna.Framework.Color;
using Point = Microsoft.Xna.Framework.Point;
using Utilities = Rampastring.Tools.Utilities;
using static System.Collections.Specialized.BitVector32;
using System.Diagnostics;

namespace DTAClient.Domain.Multiplayer
Expand Down Expand Up @@ -680,9 +679,12 @@ private void ParseSpawnIniOptions(IniFile forcedOptionsIni, string spawnIniOptio
}
}

public bool IsPreviewTextureCached() =>
public bool IsPreviewTextureAvailableAsFile() =>
SafePath.GetFile(ProgramConstants.GamePath, PreviewPath).Exists;

public Image ExtractMapPreview() =>
MapPreviewExtractor.ExtractMapPreview(GetCustomMapIniFile());

/// <summary>
/// Loads and returns the map preview texture.
/// </summary>
Expand All @@ -694,7 +696,7 @@ public Texture2D LoadPreviewTexture()
if (!Official)
{
// Extract preview from the map itself
using Image preview = MapPreviewExtractor.ExtractMapPreview(GetCustomMapIniFile());
using Image preview = ExtractMapPreview();

if (preview != null)
{
Expand Down Expand Up @@ -917,6 +919,6 @@ private static Point GetIsoTilePixelCoord(int isoTileX, int isoTileY, string[] a

protected bool Equals(Map other) => string.Equals(SHA1, other?.SHA1, StringComparison.InvariantCultureIgnoreCase);

public override int GetHashCode() => SHA1 != null ? SHA1.GetHashCode() : 0;
public override int GetHashCode() => SHA1 != null ? SHA1.GetHashCode() : BaseFilePath.GetHashCode();
}
}
11 changes: 11 additions & 0 deletions DXMainClient/Domain/Multiplayer/MapLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;

using ClientCore;

using DTAClient.DXGUI.Multiplayer;

using Rampastring.Tools;

namespace DTAClient.Domain.Multiplayer
Expand Down Expand Up @@ -55,6 +59,13 @@ public class MapLoader
/// </summary>
private string[] AllowedGameModes = ClientConfiguration.Instance.AllowedCustomGameModes.Split(',');

public readonly MapTextureCacheManager MapTextureCacheManager;

public MapLoader(MapTextureCacheManager mapTextureCacheManager)
{
MapTextureCacheManager = mapTextureCacheManager;
}

/// <summary>
/// Loads multiplayer map info asynchonously.
/// </summary>
Expand Down
Loading