Skip to content

Commit

Permalink
Add auto map refresh
Browse files Browse the repository at this point in the history
  • Loading branch information
PunkPun committed Jan 22, 2022
1 parent c6ab6d0 commit 5716d67
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 28 deletions.
173 changes: 153 additions & 20 deletions OpenRA.Game/Map/MapCache.cs
Expand Up @@ -39,6 +39,11 @@ public sealed class MapCache : IEnumerable<MapPreview>, IDisposable

public Dictionary<string, string> StringPool { get; } = new Dictionary<string, string>();

readonly List<MapDirectoryTracker> mapDirectoryTrackers = new List<MapDirectoryTracker>();
public string LastUpdatedUid;

public Action<string, string> MapUpdated = (x, y) => { };

public MapCache(ModData modData)
{
this.modData = modData;
Expand All @@ -48,12 +53,22 @@ public MapCache(ModData modData)
sheetBuilder = new SheetBuilder(SheetType.BGRA);
}

public void UpdateMaps()
{
foreach (var tracker in mapDirectoryTrackers)
{
tracker.UpdateMaps();
}
}

public void LoadMaps()
{
// Utility mod that does not support maps
if (!modData.Manifest.Contains<MapGrid>())
return;

var mapGrid = modData.Manifest.Get<MapGrid>();

// Enumerate map directories
foreach (var kv in modData.Manifest.MapFolders)
{
Expand Down Expand Up @@ -85,36 +100,44 @@ public void LoadMaps()
}

mapLocations.Add(package, classification);
mapDirectoryTrackers.Add(new MapDirectoryTracker(mapGrid, package, classification));
}

var mapGrid = modData.Manifest.Get<MapGrid>();
foreach (var kv in MapLocations)
{
foreach (var map in kv.Key.Contents)
{
IReadOnlyPackage mapPackage = null;
try
{
using (new Support.PerfTimer(map))
{
mapPackage = kv.Key.OpenPackage(map, modData.ModFiles);
if (mapPackage == null)
continue;
LoadMap(map, kv.Key, kv.Value, mapGrid);
}
}
}

var uid = Map.ComputeUID(mapPackage);
previews[uid].UpdateFromMap(mapPackage, kv.Key, kv.Value, modData.Manifest.MapCompatibility, mapGrid.Type);
}
}
catch (Exception e)
public string LoadMap(string map, IReadOnlyPackage package, MapClassification classification, MapGrid mapGrid)
{
IReadOnlyPackage mapPackage = null;
try
{
using (new Support.PerfTimer(map))
{
mapPackage = package.OpenPackage(map, modData.ModFiles);
if (mapPackage != null)
{
mapPackage?.Dispose();
Console.WriteLine("Failed to load map: {0}", map);
Console.WriteLine("Details: {0}", e);
Log.Write("debug", "Failed to load map: {0}", map);
Log.Write("debug", "Details: {0}", e);
var uid = Map.ComputeUID(mapPackage);
previews[uid].UpdateFromMap(mapPackage, package, classification, modData.Manifest.MapCompatibility, mapGrid.Type);
return uid;
}
}
}
catch (Exception e)
{
mapPackage?.Dispose();
Console.WriteLine("Failed to load map: {0}", map);
Console.WriteLine("Details: {0}", e);
Log.Write("debug", "Failed to load map: {0}", map);
Log.Write("debug", "Details: {0}", e);
}

return null;
}

public IEnumerable<IReadWritePackage> EnumerateMapDirPackages(MapClassification classification = MapClassification.System)
Expand Down Expand Up @@ -345,10 +368,18 @@ public string ChooseInitialMap(string initialUid, MersenneTwister random)
return initialUid;
}

public MapPreview this[string key] => previews[key];
public MapPreview this[string key]
{
get
{
UpdateMaps();
return previews[key];
}
}

public IEnumerator<MapPreview> GetEnumerator()
{
UpdateMaps();
return previews.Values.GetEnumerator();
}

Expand Down Expand Up @@ -378,4 +409,106 @@ public void Dispose()
});
}
}

public class MapDirectoryTracker
{
readonly FileSystemWatcher watcher;
readonly MapGrid mapGrid;
readonly IReadOnlyPackage package;
readonly MapClassification classification;

enum MapAction { Add, Delete, Update }
readonly Dictionary<string, MapAction> mapActionQueue = new Dictionary<string, MapAction>();

bool dirty = false;

public MapDirectoryTracker(MapGrid mapGrid, IReadOnlyPackage package, MapClassification classification)
{
this.mapGrid = mapGrid;
this.package = package;
this.classification = classification;

watcher = new FileSystemWatcher(package.Name);
watcher.Changed += (object sender, FileSystemEventArgs e) => AddMapAction(MapAction.Update, e.FullPath);
watcher.Created += (object sender, FileSystemEventArgs e) => AddMapAction(MapAction.Add, e.FullPath);
watcher.Deleted += (object sender, FileSystemEventArgs e) => AddMapAction(MapAction.Delete, e.FullPath);
watcher.Renamed += (object sender, RenamedEventArgs e) => AddMapAction(MapAction.Add, e.FullPath, e.OldFullPath);

watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
}

void AddMapAction(MapAction mapAction, string fullpath, string oldFullPath = null)
{
dirty = true;

// if path is not root, update map instead
var path = RemoveSubDirs(fullpath);
if (fullpath == path)
mapActionQueue[path] = mapAction;
else
mapActionQueue[path] = MapAction.Update;

// called when file has been renamed / changed location
if (oldFullPath != null)
{
var oldpath = RemoveSubDirs(oldFullPath);
if (oldpath != null)
if (oldFullPath == oldpath)
mapActionQueue[oldpath] = MapAction.Delete;
else
mapActionQueue[oldpath] = MapAction.Update;
}
}

public void UpdateMaps()
{
if (dirty)
dirty = false;
else
return;

string lastUid = null;
foreach (var mapAction in mapActionQueue)
{
var map = Game.ModData.MapCache.FirstOrDefault(x => x.Package?.Name == mapAction.Key && x.Status == MapStatus.Available);
if (map != null)
if (mapAction.Value == MapAction.Delete)
{
Console.WriteLine("Delete " + mapAction.Key);
map.Invalidate();
}
else
{
Console.WriteLine("Update " + mapAction.Key);
map.Invalidate();
lastUid = Game.ModData.MapCache.LoadMap(mapAction.Key.Replace(package.Name + Path.DirectorySeparatorChar, ""), package, classification, mapGrid);
if (lastUid != null && map.Uid != lastUid)
{
Game.ModData.MapCache.MapUpdated(map.Uid, lastUid);
}
}
else
if (mapAction.Value != MapAction.Delete)
{
Console.WriteLine("Add " + mapAction.Key);
lastUid = Game.ModData.MapCache.LoadMap(mapAction.Key.Replace(package?.Name + Path.DirectorySeparatorChar, ""), package, classification, mapGrid);
}
}

mapActionQueue.Clear();
Game.ModData.MapCache.LastUpdatedUid = lastUid ?? Game.ModData.MapCache.LastUpdatedUid;
}

string RemoveSubDirs(string path)
{
var endPath = path.Replace(package.Name + Path.DirectorySeparatorChar, "");

// if file moved from out outside directory, ignore it
if (path == endPath)
return null;

return package.Name + Path.DirectorySeparatorChar + endPath.Split(Path.DirectorySeparatorChar)[0];
}
}
}
7 changes: 0 additions & 7 deletions OpenRA.Mods.Common/Widgets/Logic/Editor/SaveMapLogic.cs
Expand Up @@ -174,10 +174,6 @@ public SaveDirectory(Folder folder, string displayName, MapClassification classi
var combinedPath = Platform.ResolvePath(Path.Combine(selectedDirectory.Folder.Name, filename.Text + fileTypes[fileType].Extension));
// Invalidate the old map metadata
if (map.Uid != null && map.Package != null && map.Package.Name == combinedPath)
modData.MapCache[map.Uid].Invalidate();
try
{
if (!(map.Package is IReadWritePackage package) || package.Name != combinedPath)
Expand All @@ -191,9 +187,6 @@ public SaveDirectory(Folder folder, string displayName, MapClassification classi
map.Save(package);
// Update the map cache so it can be loaded without restarting the game
modData.MapCache[map.Uid].UpdateFromMap(map.Package, selectedDirectory.Folder, selectedDirectory.Classification, null, map.Grid.Type);
Console.WriteLine("Saved current map at {0}", combinedPath);
Ui.CloseWindow();
Expand Down
23 changes: 22 additions & 1 deletion OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs
Expand Up @@ -59,6 +59,8 @@ enum PanelType { Players, Options, Music, Servers, Kick, ForceStart }

MapPreview map;
Session.MapStatus mapStatus;
string oldMapUid;
string newMapUid;

bool chatEnabled;
bool addBotOnMapLoad;
Expand Down Expand Up @@ -129,6 +131,15 @@ void ConnectionStateChanged(OrderManager om, string password, NetworkConnection
Game.LobbyInfoChanged += UpdateSpawnOccupants;
Game.BeforeGameStart += OnGameStart;
Game.ConnectionStateChanged += ConnectionStateChanged;
modData.MapCache.MapUpdated = (string oldUid, string newUid) =>
{
if (map.Uid == oldUid || newMapUid == oldUid)
{
if (oldMapUid == null)
oldMapUid = oldUid;
newMapUid = newUid;
}
};

var name = lobby.GetOrNull<LabelWidget>("SERVER_NAME");
if (name != null)
Expand Down Expand Up @@ -189,13 +200,23 @@ void ConnectionStateChanged(OrderManager om, string password, NetworkConnection
orderManager.IssueOrder(Order.Command("map " + uid));
Game.Settings.Server.Map = uid;
Game.Settings.Save();
newMapUid = null;
oldMapUid = null;
});
var onCancel = new Action(() =>
{
if (oldMapUid == map.Uid)
onSelect(newMapUid);
newMapUid = null;
oldMapUid = null;
});
Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
{
{ "initialMap", map.Uid },
{ "initialTab", MapClassification.System },
{ "onExit", DoNothing },
{ "onExit", Game.IsHost ? onCancel : null },
{ "onSelect", Game.IsHost ? onSelect : null },
{ "filter", MapVisibility.Lobby },
});
Expand Down
12 changes: 12 additions & 0 deletions OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs
Expand Up @@ -126,6 +126,18 @@ public class MapChooserLogic : ChromeLogic
SetupMapTab(MapClassification.User, filter, "USER_MAPS_TAB_BUTTON", "USER_MAPS_TAB", itemTemplate);
SetupMapTab(MapClassification.System, filter, "SYSTEM_MAPS_TAB_BUTTON", "SYSTEM_MAPS_TAB", itemTemplate);

if (modData.MapCache.LastUpdatedUid != null)
{
selectedUid = modData.MapCache.LastUpdatedUid;
currentTab = tabMaps.Keys.FirstOrDefault(k => tabMaps[k].Select(mp => mp.Uid).Contains(selectedUid));

if (currentTab != MapClassification.Unknown)
{
SwitchTab(currentTab, itemTemplate);
return;
}
}

if (initialMap == null && tabMaps.Keys.Contains(initialTab) && tabMaps[initialTab].Any())
{
selectedUid = Game.ModData.MapCache.ChooseInitialMap(tabMaps[initialTab].Select(mp => mp.Uid).First(),
Expand Down

0 comments on commit 5716d67

Please sign in to comment.