Merge pull request #4878 from pchote/mapcache

Reorganize map management and preview generation
This commit is contained in:
Matthias Mailänder
2014-03-17 20:49:58 +01:00
26 changed files with 725 additions and 529 deletions

View File

@@ -117,6 +117,7 @@ NEW:
Updated RenderBuildingCharge so you can set a custom charge sequence name (default is active). Updated RenderBuildingCharge so you can set a custom charge sequence name (default is active).
Added IEffectiveOwner interface for traits/logic that temporarily alter an actor's apparent owner. Added IEffectiveOwner interface for traits/logic that temporarily alter an actor's apparent owner.
Renamed Spy trait to Disguise, SpyToolTip trait to DisguiseToolTip, and RenderSpy trait to RenderDisguise. Renamed Spy trait to Disguise, SpyToolTip trait to DisguiseToolTip, and RenderSpy trait to RenderDisguise.
Overhauled the internal map management and preview generation code.
Server: Server:
Message of the day is now shared between all mods and a default motd.txt gets created in the user directory. Message of the day is now shared between all mods and a default motd.txt gets created in the user directory.
Asset Browser: Asset Browser:
@@ -137,6 +138,7 @@ NEW:
If you spot black tiles in your Dune 2000 ARRAKIS maps, replace them with the remaining sand and rock tiles. Go to Map → Fix Open Areas to randomize them. If you spot black tiles in your Dune 2000 ARRAKIS maps, replace them with the remaining sand and rock tiles. Go to Map → Fix Open Areas to randomize them.
The TestFile check in mod.yaml has been renamed to TestFiles (plural!) and now supports a comma-separated list of assets that are required to load the game. The TestFile check in mod.yaml has been renamed to TestFiles (plural!) and now supports a comma-separated list of assets that are required to load the game.
DisabledOverlay has been split from RenderBuilding. Use it together with RequiresPower and CanPowerDown for buildings or directly with Husk. DisabledOverlay has been split from RenderBuilding. Use it together with RequiresPower and CanPowerDown for buildings or directly with Husk.
Added support for custom map previews that replace the minimap in the game browser and lobby. Add map.png inside the map package.
Packaging: Packaging:
Removed portable install option from Windows installer as the game left without write access breaks content download and error log generation. Removed portable install option from Windows installer as the game left without write access breaks content download and error log generation.
Added HTML documentation to the Windows installer. Added HTML documentation to the Windows installer.

View File

@@ -45,7 +45,7 @@ namespace OpenRA.Editor
if (DirectoryIsEmpty(MapFolderPath)) if (DirectoryIsEmpty(MapFolderPath))
return; return;
foreach (var map in ModData.FindMapsIn(MapFolderPath)) foreach (var map in MapCache.FindMapsIn(MapFolderPath))
{ {
ListViewItem map1 = new ListViewItem(); ListViewItem map1 = new ListViewItem();
map1.Tag = map; map1.Tag = map;

View File

@@ -52,6 +52,20 @@ namespace OpenRA
wc.DownloadFileAsync(new Uri(url), path); wc.DownloadFileAsync(new Uri(url), path);
} }
public Download(string url, Action<DownloadProgressChangedEventArgs> onProgress, Action<DownloadDataCompletedEventArgs, bool> onComplete)
{
wc = new WebClient();
wc.Proxy = null;
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadDataCompleted += (_, a) => onComplete(a, cancelled);
Game.OnQuit += Cancel;
wc.DownloadDataCompleted += (_, a) => { Game.OnQuit -= Cancel; };
wc.DownloadDataAsync(new Uri(url));
}
public void Cancel() public void Cancel()
{ {
Game.OnQuit -= Cancel; Game.OnQuit -= Cancel;

View File

@@ -370,7 +370,7 @@ namespace OpenRA
modData = new ModData(mod); modData = new ModData(mod);
Renderer.InitializeFonts(modData.Manifest); Renderer.InitializeFonts(modData.Manifest);
modData.InitializeLoaders(); modData.InitializeLoaders();
modData.LoadMaps(); modData.MapCache.LoadMaps();
PerfHistory.items["render"].hasNormalTick = false; PerfHistory.items["render"].hasNormalTick = false;
PerfHistory.items["batches"].hasNormalTick = false; PerfHistory.items["batches"].hasNormalTick = false;
@@ -401,7 +401,7 @@ namespace OpenRA
if (Settings.Server.DedicatedLoop) if (Settings.Server.DedicatedLoop)
{ {
Console.WriteLine("Starting a new server instance..."); Console.WriteLine("Starting a new server instance...");
modData.LoadMaps(); modData.MapCache.LoadMaps();
continue; continue;
} }
@@ -426,13 +426,14 @@ namespace OpenRA
static string ChooseShellmap() static string ChooseShellmap()
{ {
var shellmaps = modData.AvailableMaps var shellmaps = modData.MapCache
.Where(m => m.Value.UseAsShellmap); .Where(m => m.Status == MapStatus.Available && m.Map.UseAsShellmap)
.Select(m => m.Uid);
if (!shellmaps.Any()) if (!shellmaps.Any())
throw new InvalidDataException("No valid shellmaps available"); throw new InvalidDataException("No valid shellmaps available");
return shellmaps.Random(CosmeticRandom).Key; return shellmaps.Random(CosmeticRandom);
} }
static bool quit; static bool quit;
@@ -553,7 +554,7 @@ namespace OpenRA
WebClient webClient = new WebClient(); WebClient webClient = new WebClient();
webClient.DownloadFile(url, tempFile); webClient.DownloadFile(url, tempFile);
File.Move(tempFile, mapPath); File.Move(tempFile, mapPath);
Game.modData.AvailableMaps.Add(mapHash, new Map(mapPath)); Game.modData.MapCache[mapHash].UpdateFromMap(new Map(mapPath));
Log.Write("debug", "New map has been downloaded to '{0}'", mapPath); Log.Write("debug", "New map has been downloaded to '{0}'", mapPath);
return true; return true;

View File

@@ -67,6 +67,14 @@ namespace OpenRA.Graphics
return rect; return rect;
} }
public Sprite Add(Bitmap src)
{
var rect = Allocate(src.Size);
Util.FastCopyIntoSprite(rect, src);
current.CommitData();
return rect;
}
public Sprite Add(Size size, byte paletteIndex) public Sprite Add(Size size, byte paletteIndex)
{ {
var data = new byte[size.Width * size.Height]; var data = new byte[size.Width * size.Height];

View File

@@ -9,6 +9,8 @@
#endregion #endregion
using System; using System;
using System.Drawing;
using System.Drawing.Imaging;
using OpenRA.FileFormats.Graphics; using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics namespace OpenRA.Graphics
@@ -59,6 +61,36 @@ namespace OpenRA.Graphics
} }
} }
public static void FastCopyIntoSprite(Sprite dest, Bitmap src)
{
var destStride = dest.sheet.Size.Width;
var width = dest.bounds.Width;
var height = dest.bounds.Height;
var srcData = src.LockBits(src.Bounds(),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
var c = (int*)srcData.Scan0;
// Cast the data to an int array so we can copy the src data directly
fixed (byte* bd = &dest.sheet.Data[0])
{
var data = (int*)bd;
var x = dest.bounds.Left;
var y = dest.bounds.Top;
for (var j = 0; j < height; j++)
for (var i = 0; i < width; i++)
data[(y + j) * destStride + x + i] = *(c + (j * srcData.Stride >> 2) + i);
}
}
src.UnlockBits(srcData);
}
public static float[] IdentityMatrix() public static float[] IdentityMatrix()
{ {
return Exts.MakeArray(16, j => (j % 5 == 0) ? 1.0f : 0); return Exts.MakeArray(16, j => (j % 5 == 0) ? 1.0f : 0);

View File

@@ -70,6 +70,7 @@ namespace OpenRA
public string Author; public string Author;
public string Tileset; public string Tileset;
public bool AllowStartUnitConfig = true; public bool AllowStartUnitConfig = true;
public Bitmap CustomPreview;
[FieldLoader.LoadUsing("LoadOptions")] [FieldLoader.LoadUsing("LoadOptions")]
public MapOptions Options; public MapOptions Options;
@@ -226,13 +227,16 @@ namespace OpenRA
Save(path); Save(path);
Uid = ComputeHash(); Uid = ComputeHash();
if (Container.Exists("map.png"))
CustomPreview = new Bitmap(Container.GetContent("map.png"));
} }
public int2[] GetSpawnPoints() public CPos[] GetSpawnPoints()
{ {
return Actors.Value.Values return Actors.Value.Values
.Where(a => a.Type == "mpspawn") .Where(a => a.Type == "mpspawn")
.Select(a => a.InitDict.Get<LocationInit>().value) .Select(a => (CPos)a.InitDict.Get<LocationInit>().value)
.ToArray(); .ToArray();
} }

148
OpenRA.Game/MapCache.cs Executable file
View File

@@ -0,0 +1,148 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA
{
public class MapCache : IEnumerable<MapPreview>
{
public static readonly MapPreview UnknownMap = new MapPreview(null, null);
readonly Cache<string, MapPreview> previews;
readonly Manifest manifest;
readonly SheetBuilder sheetBuilder;
Thread previewLoaderThread;
object syncRoot = new object();
Queue<MapPreview> generateMinimap = new Queue<MapPreview>();
public MapCache(Manifest m)
{
manifest = m;
previews = new Cache<string, MapPreview>(uid => new MapPreview(uid, this));
sheetBuilder = new SheetBuilder(SheetType.BGRA);
}
public void LoadMaps()
{
var paths = manifest.MapFolders.SelectMany(f => FindMapsIn(f));
foreach (var path in paths)
{
try
{
var map = new Map(path, manifest.Mod.Id);
if (manifest.MapCompatibility.Contains(map.RequiresMod))
previews[map.Uid].UpdateFromMap(map);
}
catch (Exception e)
{
Console.WriteLine("Failed to load map: {0}", path);
Console.WriteLine("Details: {0}", e);
}
}
}
public static IEnumerable<string> FindMapsIn(string dir)
{
string[] noMaps = { };
// Ignore optional flag
if (dir.StartsWith("~"))
dir = dir.Substring(1);
// Paths starting with ^ are relative to the user directory
if (dir.StartsWith("^"))
dir = Platform.SupportDir + dir.Substring(1);
if (!Directory.Exists(dir))
return noMaps;
var dirsWithMaps = Directory.GetDirectories(dir)
.Where(d => Directory.GetFiles(d, "map.yaml").Any() && Directory.GetFiles(d, "map.bin").Any());
return dirsWithMaps.Concat(Directory.GetFiles(dir, "*.oramap"));
}
void LoadAsyncInternal()
{
for (;;)
{
MapPreview p;
lock (syncRoot)
{
if (generateMinimap.Count == 0)
break;
p = generateMinimap.Peek();
// Preview already exists
if (p.Minimap != null)
{
generateMinimap.Dequeue();
continue;
}
}
// Render the minimap into the shared sheet
// Note: this is not generally thread-safe, but it works here because:
// (a) This worker is the only thread writing to this sheet
// (b) The main thread is the only thread reading this sheet
// (c) The sheet is marked dirty after the write is completed,
// which causes the main thread to copy this to the texture during
// the next render cycle.
// (d) Any partially written bytes from the next minimap is in an
// unallocated area, and will be committed in the next cycle.
var bitmap = p.CustomPreview ?? Minimap.RenderMapPreview(p.Map, true);
p.Minimap = sheetBuilder.Add(bitmap);
lock (syncRoot)
generateMinimap.Dequeue();
// Yuck... But this helps the UI Jank when opening the map selector significantly.
Thread.Sleep(50);
}
}
public void CacheMinimap(MapPreview preview)
{
lock (syncRoot)
generateMinimap.Enqueue(preview);
if (previewLoaderThread == null || !previewLoaderThread.IsAlive)
{
previewLoaderThread = new Thread(LoadAsyncInternal);
previewLoaderThread.Start();
}
}
public MapPreview this[string key]
{
get { return previews[key]; }
}
public IEnumerator<MapPreview> GetEnumerator()
{
return previews.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

93
OpenRA.Game/MapPreview.cs Executable file
View File

@@ -0,0 +1,93 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA
{
public enum MapStatus { Available, Unavailable }
public class MapPreview
{
static readonly List<CPos> NoSpawns = new List<CPos>();
public readonly string Uid;
public string Title { get; private set; }
public string Type { get; private set; }
public string Author { get; private set; }
public int PlayerCount { get; private set; }
public List<CPos> SpawnPoints { get; private set; }
public Rectangle Bounds { get; private set; }
public Bitmap CustomPreview { get; private set; }
public Map Map { get; private set; }
public MapStatus Status { get; private set; }
Sprite minimap;
bool generatingMinimap;
public Sprite Minimap
{
get
{
if (minimap != null)
return minimap;
if (!generatingMinimap && Status == MapStatus.Available)
{
generatingMinimap = true;
cache.CacheMinimap(this);
}
return null;
}
set
{
minimap = value;
generatingMinimap = false;
}
}
MapCache cache;
public MapPreview(string uid, MapCache cache)
{
this.cache = cache;
Uid = uid;
Title = "Unknown Map";
Type = "Unknown";
Author = "Unknown Author";
PlayerCount = 0;
Bounds = Rectangle.Empty;
SpawnPoints = NoSpawns;
Status = MapStatus.Unavailable;
}
public void UpdateFromMap(Map m)
{
Map = m;
Title = m.Title;
Type = m.Type;
Type = m.Type;
Author = m.Author;
PlayerCount = m.Players.Count(x => x.Value.Playable);
Bounds = m.Bounds;
SpawnPoints = m.GetSpawnPoints().ToList();
CustomPreview = m.CustomPreview;
Status = MapStatus.Available;
}
}
}

View File

@@ -22,34 +22,13 @@ namespace OpenRA
{ {
public readonly Manifest Manifest; public readonly Manifest Manifest;
public readonly ObjectCreator ObjectCreator; public readonly ObjectCreator ObjectCreator;
public Dictionary<string, Map> AvailableMaps { get; private set; }
public readonly WidgetLoader WidgetLoader; public readonly WidgetLoader WidgetLoader;
public readonly MapCache MapCache;
public ILoadScreen LoadScreen = null; public ILoadScreen LoadScreen = null;
public SheetBuilder SheetBuilder; public SheetBuilder SheetBuilder;
public SpriteLoader SpriteLoader; public SpriteLoader SpriteLoader;
public VoxelLoader VoxelLoader; public VoxelLoader VoxelLoader;
public static IEnumerable<string> FindMapsIn(string dir)
{
string[] noMaps = { };
// ignore optional flag
if (dir.StartsWith("~"))
dir = dir.Substring(1);
// paths starting with ^ are relative to the user directory
if (dir.StartsWith("^"))
dir = Platform.SupportDir + dir.Substring(1);
if (!Directory.Exists(dir))
return noMaps;
var dirsWithMaps = Directory.GetDirectories(dir)
.Where(d => Directory.GetFiles(d, "map.yaml").Any() && Directory.GetFiles(d, "map.bin").Any());
return dirsWithMaps.Concat(Directory.GetFiles(dir, "*.oramap"));
}
public ModData(string mod) public ModData(string mod)
{ {
Languages = new string[0]; Languages = new string[0];
@@ -59,6 +38,7 @@ namespace OpenRA
LoadScreen.Init(Manifest, Manifest.LoadScreen.NodesDict.ToDictionary(x => x.Key, x => x.Value.Value)); LoadScreen.Init(Manifest, Manifest.LoadScreen.NodesDict.ToDictionary(x => x.Key, x => x.Value.Value));
LoadScreen.Display(); LoadScreen.Display();
WidgetLoader = new WidgetLoader(this); WidgetLoader = new WidgetLoader(this);
MapCache = new MapCache(Manifest);
// HACK: Mount only local folders so we have a half-working environment for the asset installer // HACK: Mount only local folders so we have a half-working environment for the asset installer
FileSystem.UnmountAll(); FileSystem.UnmountAll();
@@ -119,17 +99,13 @@ namespace OpenRA
FieldLoader.Translations = translations; FieldLoader.Translations = translations;
} }
public void LoadMaps()
{
AvailableMaps = FindMaps();
}
public Map PrepareMap(string uid) public Map PrepareMap(string uid)
{ {
LoadScreen.Display(); LoadScreen.Display();
if (!AvailableMaps.ContainsKey(uid))
var map = MapCache[uid].Map;
if (map == null)
throw new InvalidDataException("Invalid map uid: {0}".F(uid)); throw new InvalidDataException("Invalid map uid: {0}".F(uid));
var map = new Map(AvailableMaps[uid].Path);
LoadTranslations(map); LoadTranslations(map);
@@ -148,33 +124,6 @@ namespace OpenRA
VoxelProvider.Initialize(Manifest.VoxelSequences, map.VoxelSequences); VoxelProvider.Initialize(Manifest.VoxelSequences, map.VoxelSequences);
return map; return map;
} }
public Dictionary<string, Map> FindMaps()
{
var paths = Manifest.MapFolders.SelectMany(f => FindMapsIn(f));
var ret = new Dictionary<string, Map>();
foreach (var path in paths)
{
try
{
var map = new Map(path, Manifest.Mod.Id);
if (Manifest.MapCompatibility.Contains(map.RequiresMod))
ret.Add(map.Uid, map);
}
catch (Exception e)
{
Console.WriteLine("Failed to load map: {0}", path);
Console.WriteLine("Details: {0}", e);
}
}
return ret;
}
public Map FindMapByUid(string uid)
{
return AvailableMaps.ContainsKey(uid) ? AvailableMaps[uid] : null;
}
} }
public interface ILoadScreen public interface ILoadScreen

View File

@@ -37,7 +37,7 @@ namespace OpenRA.Network
// Don't have the map locally // Don't have the map locally
// TODO: We allow joining, then drop on game start if the map isn't available // TODO: We allow joining, then drop on game start if the map isn't available
if (!Game.modData.AvailableMaps.ContainsKey(Map) && !Game.Settings.Game.AllowDownloading) if (Game.modData.MapCache[Map].Status != MapStatus.Available && !Game.Settings.Game.AllowDownloading)
return false; return false;
return true; return true;

View File

@@ -1,44 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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. For more information,
* see COPYING.
*/
#endregion
using System;
namespace OpenRA.Network
{
public class Replay
{
public readonly string Filename;
public readonly int Duration;
public readonly Session LobbyInfo;
public Replay(string filename)
{
Filename = filename;
using (var conn = new ReplayConnection(filename))
{
Duration = conn.TickCount * Game.NetTickScale;
LobbyInfo = conn.LobbyInfo;
}
}
public Map Map()
{
if (LobbyInfo == null)
return null;
var map = LobbyInfo.GlobalSettings.Map;
if (!Game.modData.AvailableMaps.ContainsKey(map))
return null;
return Game.modData.AvailableMaps[map];
}
}
}

View File

@@ -1,52 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using OpenRA.FileFormats;
namespace OpenRA.Network
{
public static class ServerList
{
public static void Query(Action<GameServer[]> onComplete)
{
var masterServerUrl = Game.Settings.Server.MasterServer;
new Thread(() =>
{
GameServer[] games = null;
try
{
var str = GetData(new Uri(masterServerUrl + "list.php"));
var yaml = MiniYaml.FromString(str);
games = yaml.Select(a => FieldLoader.Load<GameServer>(a.Value))
.Where(gs => gs.Address != null).ToArray();
}
catch { }
Game.RunAfterTick(() => onComplete(games));
}) { IsBackground = true }.Start();
}
static string GetData(Uri uri)
{
var wc = new WebClient();
wc.Proxy = null;
var data = wc.DownloadData(uri);
return Encoding.UTF8.GetString(data);
}
}
}

View File

@@ -129,9 +129,7 @@
<Compile Include="Network\Order.cs" /> <Compile Include="Network\Order.cs" />
<Compile Include="Network\OrderIO.cs" /> <Compile Include="Network\OrderIO.cs" />
<Compile Include="Network\OrderManager.cs" /> <Compile Include="Network\OrderManager.cs" />
<Compile Include="Network\Replay.cs" />
<Compile Include="Network\ReplayConnection.cs" /> <Compile Include="Network\ReplayConnection.cs" />
<Compile Include="Network\ServerList.cs" />
<Compile Include="Network\Session.cs" /> <Compile Include="Network\Session.cs" />
<Compile Include="Network\SyncReport.cs" /> <Compile Include="Network\SyncReport.cs" />
<Compile Include="Network\UnitOrders.cs" /> <Compile Include="Network\UnitOrders.cs" />
@@ -232,6 +230,8 @@
<Compile Include="Traits\World\ActorMap.cs" /> <Compile Include="Traits\World\ActorMap.cs" />
<Compile Include="Widgets\HotkeyEntryWidget.cs" /> <Compile Include="Widgets\HotkeyEntryWidget.cs" />
<Compile Include="Effects\MoveFlash.cs" /> <Compile Include="Effects\MoveFlash.cs" />
<Compile Include="MapCache.cs" />
<Compile Include="MapPreview.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj"> <ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -21,8 +21,8 @@ namespace OpenRA.Widgets
{ {
public class MapPreviewWidget : Widget public class MapPreviewWidget : Widget
{ {
public Func<Map> Map = () => null; public Func<MapPreview> Preview = () => null;
public Func<Dictionary<int2, Session.Client>> SpawnClients = () => new Dictionary<int2, Session.Client>(); public Func<Dictionary<CPos, Session.Client>> SpawnClients = () => new Dictionary<CPos, Session.Client>();
public Action<MouseInput> OnMouseDown = _ => {}; public Action<MouseInput> OnMouseDown = _ => {};
public bool IgnoreMouseInput = false; public bool IgnoreMouseInput = false;
public bool ShowSpawnPoints = true; public bool ShowSpawnPoints = true;
@@ -32,6 +32,9 @@ namespace OpenRA.Widgets
Lazy<TooltipContainerWidget> tooltipContainer; Lazy<TooltipContainerWidget> tooltipContainer;
public int TooltipSpawnIndex = -1; public int TooltipSpawnIndex = -1;
Rectangle MapRect;
float PreviewScale = 0;
public MapPreviewWidget() public MapPreviewWidget()
{ {
tooltipContainer = Lazy.New(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer)); tooltipContainer = Lazy.New(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
@@ -40,8 +43,7 @@ namespace OpenRA.Widgets
protected MapPreviewWidget(MapPreviewWidget other) protected MapPreviewWidget(MapPreviewWidget other)
: base(other) : base(other)
{ {
lastMap = other.lastMap; Preview = other.Preview;
Map = other.Map;
SpawnClients = other.SpawnClients; SpawnClients = other.SpawnClients;
ShowSpawnPoints = other.ShowSpawnPoints; ShowSpawnPoints = other.ShowSpawnPoints;
TooltipTemplate = other.TooltipTemplate; TooltipTemplate = other.TooltipTemplate;
@@ -65,74 +67,51 @@ namespace OpenRA.Widgets
public override void MouseEntered() public override void MouseEntered()
{ {
if (TooltipContainer == null) return; if (TooltipContainer != null)
tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs() {{ "preview", this }}); tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs() {{ "preview", this }});
} }
public override void MouseExited() public override void MouseExited()
{ {
if (TooltipContainer == null) return; if (TooltipContainer != null)
tooltipContainer.Value.RemoveTooltip(); tooltipContainer.Value.RemoveTooltip();
} }
public int2 ConvertToPreview(int2 point) public int2 ConvertToPreview(CPos point)
{ {
var map = Map(); var preview = Preview();
return new int2(MapRect.X + (int)(PreviewScale*(point.X - map.Bounds.Left)) , MapRect.Y + (int)(PreviewScale*(point.Y - map.Bounds.Top))); return new int2(MapRect.X + (int)(PreviewScale*(point.X - preview.Bounds.Left)) , MapRect.Y + (int)(PreviewScale*(point.Y - preview.Bounds.Top)));
} }
Sheet mapChooserSheet; Sprite minimap;
Sprite mapChooserSprite;
Map lastMap;
Rectangle MapRect;
float PreviewScale = 0;
public override void Draw() public override void Draw()
{ {
var map = Map(); var preview = Preview();
if (map == null) if (preview == null)
return; return;
// Preview unavailable // Stash a copy of the minimap to ensure consistency
if (!Loaded) // (it may be modified by another thread)
{ minimap = preview.Minimap;
GeneratePreview(); if (minimap == null)
return; return;
}
if (lastMap != map)
{
lastMap = map;
// Update image data
Bitmap preview;
lock (syncRoot)
preview = Previews[map.Uid];
if (mapChooserSheet == null || mapChooserSheet.Size.Width != preview.Width || mapChooserSheet.Size.Height != preview.Height)
mapChooserSheet = new Sheet(new Size(preview.Width, preview.Height));
mapChooserSheet.Texture.SetData(preview);
mapChooserSprite = new Sprite(mapChooserSheet, new Rectangle(0, 0, map.Bounds.Width, map.Bounds.Height), TextureChannel.Alpha);
}
// Update map rect // Update map rect
PreviewScale = Math.Min(RenderBounds.Width * 1.0f / map.Bounds.Width, RenderBounds.Height * 1.0f / map.Bounds.Height); PreviewScale = Math.Min(RenderBounds.Width / minimap.size.X, RenderBounds.Height / minimap.size.Y);
var size = Math.Max(map.Bounds.Width, map.Bounds.Height); var w = (int)(PreviewScale * minimap.size.X);
var dw = (int)(PreviewScale * (size - map.Bounds.Width)) / 2; var h = (int)(PreviewScale * minimap.size.Y);
var dh = (int)(PreviewScale * (size - map.Bounds.Height)) / 2; var x = RenderBounds.X + (RenderBounds.Width - w) / 2;
MapRect = new Rectangle(RenderBounds.X + dw, RenderBounds.Y + dh, (int)(map.Bounds.Width * PreviewScale), (int)(map.Bounds.Height * PreviewScale)); var y = RenderBounds.Y + (RenderBounds.Height - h) / 2;
MapRect = new Rectangle(x, y, w, h);
Game.Renderer.RgbaSpriteRenderer.DrawSprite(mapChooserSprite, Game.Renderer.RgbaSpriteRenderer.DrawSprite(minimap, new float2(MapRect.Location), new float2(MapRect.Size));
new float2(MapRect.Location),
new float2(MapRect.Size));
TooltipSpawnIndex = -1; TooltipSpawnIndex = -1;
if (ShowSpawnPoints) if (ShowSpawnPoints)
{ {
var colors = SpawnClients().ToDictionary(c => c.Key, c => c.Value.Color.RGB); var colors = SpawnClients().ToDictionary(c => c.Key, c => c.Value.Color.RGB);
var spawnPoints = map.GetSpawnPoints().ToList(); var spawnPoints = preview.SpawnPoints;
foreach (var p in spawnPoints) foreach (var p in spawnPoints)
{ {
var owned = colors.ContainsKey(p); var owned = colors.ContainsKey(p);
@@ -151,79 +130,6 @@ namespace OpenRA.Widgets
} }
} }
// Async map preview generation bits public bool Loaded { get { return minimap != null; } }
enum PreviewStatus { Invalid, Uncached, Generating, Cached }
static Thread previewLoaderThread;
static object syncRoot = new object();
static Queue<string> cacheUids = new Queue<string>();
static readonly Dictionary<string, Bitmap> Previews = new Dictionary<string, Bitmap>();
void LoadAsyncInternal()
{
for (;;)
{
string uid;
lock (syncRoot)
{
if (cacheUids.Count == 0)
break;
uid = cacheUids.Peek();
}
var bitmap = Minimap.RenderMapPreview(Game.modData.AvailableMaps[uid], false);
lock (syncRoot)
{
// TODO: We should add previews to a sheet here (with multiple previews per sheet)
Previews.Add(uid, bitmap);
cacheUids.Dequeue();
}
// Yuck... But this helps the UI Jank when opening the map selector significantly.
Thread.Sleep(50);
}
}
bool compatibleTileset;
void GeneratePreview()
{
var m = Map();
if (m == null)
return;
compatibleTileset = Rules.TileSets.Values.Any(t => t.Id == m.Tileset);
var status = Status(m);
if (status == PreviewStatus.Uncached)
lock (syncRoot)
cacheUids.Enqueue(m.Uid);
if (previewLoaderThread == null || !previewLoaderThread.IsAlive)
{
previewLoaderThread = new Thread(LoadAsyncInternal);
previewLoaderThread.Start();
}
}
PreviewStatus Status(Map m)
{
if (m == null)
return PreviewStatus.Invalid;
lock (syncRoot)
{
if (Previews.ContainsKey(m.Uid))
return PreviewStatus.Cached;
if (cacheUids.Contains(m.Uid))
return PreviewStatus.Generating;
if (!compatibleTileset)
return PreviewStatus.Invalid;
}
return PreviewStatus.Uncached;
}
public bool Loaded { get { return Status(Map()) == PreviewStatus.Cached; } }
} }
} }

View File

@@ -225,14 +225,13 @@ namespace OpenRA.Widgets
public static Action Once( Action a ) { return () => { if (a != null) { a(); a = null; } }; } public static Action Once( Action a ) { return () => { if (a != null) { a(); a = null; } }; }
public static string ChooseInitialMap(string map) public static string ChooseInitialMap(string initialUid)
{ {
var availableMaps = Game.modData.AvailableMaps; if (string.IsNullOrEmpty(initialUid) || Game.modData.MapCache[initialUid].Status != MapStatus.Available)
if (string.IsNullOrEmpty(map) || !availableMaps.ContainsKey(map))
{ {
Func<Map, bool> isIdealMap = m => Func<MapPreview, bool> isIdealMap = m =>
{ {
if (!m.Selectable) if (m.Status != MapStatus.Available || !m.Map.Selectable)
return false; return false;
// Other map types may have confusing settings or gameplay // Other map types may have confusing settings or gameplay
@@ -240,22 +239,22 @@ namespace OpenRA.Widgets
return false; return false;
// Maps with bots disabled confuse new players // Maps with bots disabled confuse new players
if (m.Players.Any(s => !s.Value.AllowBots)) if (m.Map.Players.Any(s => !s.Value.AllowBots))
return false; return false;
// Large maps expose unfortunate performance problems // Large maps expose unfortunate performance problems
if (m.MapSize.X > 128 || m.MapSize.Y > 128) if (m.Bounds.Width > 128 || m.Bounds.Height > 128)
return false; return false;
return true; return true;
}; };
var selected = availableMaps.Values.Where(m => isIdealMap(m)).RandomOrDefault(Game.CosmeticRandom) ?? var selected = Game.modData.MapCache.Where(m => isIdealMap(m)).RandomOrDefault(Game.CosmeticRandom) ??
availableMaps.Values.First(m => m.Selectable); Game.modData.MapCache.First(m => m.Status == MapStatus.Available && m.Map.Selectable);
return selected.Uid; return selected.Uid;
} }
return map; return initialUid;
} }
} }

View File

@@ -9,6 +9,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.FileFormats; using OpenRA.FileFormats;
using OpenRA.Traits; using OpenRA.Traits;
@@ -52,14 +53,16 @@ namespace OpenRA.Lint
AppDomain.CurrentDomain.AssemblyResolve += FileSystem.ResolveAssembly; AppDomain.CurrentDomain.AssemblyResolve += FileSystem.ResolveAssembly;
Game.modData = new ModData(mod); Game.modData = new ModData(mod);
var maps = new Map[] { new Map() }; IEnumerable<Map> maps;
if (!string.IsNullOrEmpty(map)) if (string.IsNullOrEmpty(map))
maps = new Map[] { new Map(map) };
else
{ {
Game.modData.LoadMaps(); Game.modData.MapCache.LoadMaps();
maps = Game.modData.AvailableMaps.Values.ToArray(); maps = Game.modData.MapCache
.Where(m => m.Status == MapStatus.Available)
.Select(m => m.Map);
} }
else
maps = new [] { new Map(map) };
foreach (var testMap in maps) foreach (var testMap in maps)
{ {

View File

@@ -28,9 +28,10 @@ namespace OpenRA.Mods.RA
public void WorldLoaded(World world, WorldRenderer wr) public void WorldLoaded(World world, WorldRenderer wr)
{ {
var spawns = world.Map.GetSpawnPoints();
var taken = world.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0 && c.Slot != null) var taken = world.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0 && c.Slot != null)
.Select(c => (CPos) world.Map.GetSpawnPoints()[c.SpawnPoint-1]).ToList(); .Select(c => spawns[c.SpawnPoint-1]).ToList();
var available = world.Map.GetSpawnPoints().Select(c => (CPos)c).Except(taken).ToList(); var available = spawns.Except(taken).ToList();
// Set spawn // Set spawn
foreach (var kv in world.LobbyInfo.Slots) foreach (var kv in world.LobbyInfo.Slots)
@@ -41,7 +42,7 @@ namespace OpenRA.Mods.RA
var client = world.LobbyInfo.ClientInSlot(kv.Key); var client = world.LobbyInfo.ClientInSlot(kv.Key);
var spid = (client == null || client.SpawnPoint == 0) var spid = (client == null || client.SpawnPoint == 0)
? ChooseSpawnPoint(world, available, taken) ? ChooseSpawnPoint(world, available, taken)
: (CPos)world.Map.GetSpawnPoints()[client.SpawnPoint-1]; : world.Map.GetSpawnPoints()[client.SpawnPoint-1];
Start.Add(player, spid); Start.Add(player, spid);
} }

View File

@@ -280,7 +280,7 @@ namespace OpenRA.Mods.RA.Server
return true; return true;
} }
if (!server.ModData.AvailableMaps.ContainsKey(s)) if (server.ModData.MapCache[s].Status != MapStatus.Available)
{ {
server.SendOrderTo(conn, "Message", "Map was not found on server"); server.SendOrderTo(conn, "Message", "Map was not found on server");
return true; return true;
@@ -720,7 +720,7 @@ namespace OpenRA.Mods.RA.Server
static void LoadMap(S server) static void LoadMap(S server)
{ {
server.Map = new Map(server.ModData.AvailableMaps[server.LobbyInfo.GlobalSettings.Map].Path); server.Map = server.ModData.MapCache[server.LobbyInfo.GlobalSettings.Map].Map;
server.LobbyInfo.Slots = server.Map.Players server.LobbyInfo.Slots = server.Map.Players
.Select(p => MakeSlotFromPlayerReference(p.Value)) .Select(p => MakeSlotFromPlayerReference(p.Value))
.Where(s => s != null) .Where(s => s != null)

View File

@@ -20,27 +20,28 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{ {
public class LobbyLogic public class LobbyLogic
{ {
readonly Action onStart;
readonly Action onExit;
readonly OrderManager orderManager;
readonly bool skirmishMode;
enum PanelType { Players, Options, Kick } enum PanelType { Players, Options, Kick }
PanelType panel = PanelType.Players; PanelType panel = PanelType.Players;
Widget lobby; Widget lobby;
Widget EditablePlayerTemplate, NonEditablePlayerTemplate, EmptySlotTemplate,
EditableSpectatorTemplate, NonEditableSpectatorTemplate, NewSpectatorTemplate; Widget editablePlayerTemplate, nonEditablePlayerTemplate, emptySlotTemplate,
editableSpectatorTemplate, nonEditableSpectatorTemplate, newSpectatorTemplate;
ScrollPanelWidget chatPanel; ScrollPanelWidget chatPanel;
Widget chatTemplate; Widget chatTemplate;
ScrollPanelWidget Players; ScrollPanelWidget players;
Dictionary<string, string> CountryNames; Dictionary<string, string> countryNames;
string MapUid; MapPreview preview = MapCache.UnknownMap;
Map Map;
ColorPreviewManagerWidget colorPreview; ColorPreviewManagerWidget colorPreview;
readonly Action OnGameStart;
readonly Action onExit;
readonly OrderManager orderManager;
readonly bool skirmishMode;
// Listen for connection failures // Listen for connection failures
void ConnectionStateChanged(OrderManager om) void ConnectionStateChanged(OrderManager om)
{ {
@@ -54,7 +55,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
Game.OpenWindow("SERVER_LOBBY", new WidgetArgs() Game.OpenWindow("SERVER_LOBBY", new WidgetArgs()
{ {
{ "onExit", onExit }, { "onExit", onExit },
{ "onStart", OnGameStart }, { "onStart", onStart },
{ "skirmishMode", false } { "skirmishMode", false }
}); });
}; };
@@ -90,7 +91,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{ {
lobby = widget; lobby = widget;
this.orderManager = orderManager; this.orderManager = orderManager;
this.OnGameStart = () => { CloseWindow(); onStart(); }; this.onStart = onStart;
this.onExit = onExit; this.onExit = onExit;
this.skirmishMode = skirmishMode; this.skirmishMode = skirmishMode;
@@ -105,49 +106,43 @@ namespace OpenRA.Mods.RA.Widgets.Logic
name.GetText = () => orderManager.LobbyInfo.GlobalSettings.ServerName; name.GetText = () => orderManager.LobbyInfo.GlobalSettings.ServerName;
UpdateCurrentMap(); UpdateCurrentMap();
Players = Ui.LoadWidget<ScrollPanelWidget>("LOBBY_PLAYER_BIN", lobby.Get("PLAYER_BIN_ROOT"), new WidgetArgs()); players = Ui.LoadWidget<ScrollPanelWidget>("LOBBY_PLAYER_BIN", lobby.Get("PLAYER_BIN_ROOT"), new WidgetArgs());
Players.IsVisible = () => panel == PanelType.Players; players.IsVisible = () => panel == PanelType.Players;
EditablePlayerTemplate = Players.Get("TEMPLATE_EDITABLE_PLAYER"); var playerBinHeaders = lobby.GetOrNull<ContainerWidget>("LABEL_CONTAINER");
NonEditablePlayerTemplate = Players.Get("TEMPLATE_NONEDITABLE_PLAYER"); if (playerBinHeaders != null)
EmptySlotTemplate = Players.Get("TEMPLATE_EMPTY"); playerBinHeaders.IsVisible = () => panel == PanelType.Players;
EditableSpectatorTemplate = Players.Get("TEMPLATE_EDITABLE_SPECTATOR");
NonEditableSpectatorTemplate = Players.Get("TEMPLATE_NONEDITABLE_SPECTATOR"); editablePlayerTemplate = players.Get("TEMPLATE_EDITABLE_PLAYER");
NewSpectatorTemplate = Players.Get("TEMPLATE_NEW_SPECTATOR"); nonEditablePlayerTemplate = players.Get("TEMPLATE_NONEDITABLE_PLAYER");
emptySlotTemplate = players.Get("TEMPLATE_EMPTY");
editableSpectatorTemplate = players.Get("TEMPLATE_EDITABLE_SPECTATOR");
nonEditableSpectatorTemplate = players.Get("TEMPLATE_NONEDITABLE_SPECTATOR");
newSpectatorTemplate = players.Get("TEMPLATE_NEW_SPECTATOR");
colorPreview = lobby.Get<ColorPreviewManagerWidget>("COLOR_MANAGER"); colorPreview = lobby.Get<ColorPreviewManagerWidget>("COLOR_MANAGER");
colorPreview.Color = Game.Settings.Player.Color; colorPreview.Color = Game.Settings.Player.Color;
var mapPreview = lobby.Get<MapPreviewWidget>("MAP_PREVIEW"); var mapPreview = lobby.Get<MapPreviewWidget>("MAP_PREVIEW");
mapPreview.IsVisible = () => Map != null; mapPreview.Preview = () => preview;
mapPreview.Map = () => Map; mapPreview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, mapPreview, preview, mi);
mapPreview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, mapPreview, Map, mi); mapPreview.SpawnClients = () => LobbyUtils.GetSpawnClients(orderManager, preview);
mapPreview.SpawnClients = () => LobbyUtils.GetSpawnClients(orderManager, Map);
var mapTitle = lobby.GetOrNull<LabelWidget>("MAP_TITLE"); var mapTitle = lobby.GetOrNull<LabelWidget>("MAP_TITLE");
if (mapTitle != null) if (mapTitle != null)
{ mapTitle.GetText = () => preview.Title;
mapTitle.IsVisible = () => Map != null;
mapTitle.GetText = () => Map.Title;
}
var mapType = lobby.GetOrNull<LabelWidget>("MAP_TYPE"); var mapType = lobby.GetOrNull<LabelWidget>("MAP_TYPE");
if (mapType != null) if (mapType != null)
{ mapType.GetText = () => preview.Type;
mapType.IsVisible = () => Map != null;
mapType.GetText = () => Map.Type;
}
var mapAuthor = lobby.GetOrNull<LabelWidget>("MAP_AUTHOR"); var mapAuthor = lobby.GetOrNull<LabelWidget>("MAP_AUTHOR");
if (mapAuthor != null) if (mapAuthor != null)
{ mapAuthor.GetText = () => "Created by {0}".F(preview.Author);
mapAuthor.IsVisible = () => Map != null;
mapAuthor.GetText = () => "Created by {0}".F(Map.Author);
}
CountryNames = Rules.Info["world"].Traits.WithInterface<CountryInfo>() countryNames = Rules.Info["world"].Traits.WithInterface<CountryInfo>()
.Where(c => c.Selectable) .Where(c => c.Selectable)
.ToDictionary(a => a.Race, a => a.Name); .ToDictionary(a => a.Race, a => a.Name);
CountryNames.Add("random", "Any"); countryNames.Add("random", "Any");
var gameStarting = false; var gameStarting = false;
Func<bool> configurationDisabled = () => !Game.IsHost || gameStarting || panel == PanelType.Kick || Func<bool> configurationDisabled = () => !Game.IsHost || gameStarting || panel == PanelType.Kick ||
@@ -159,16 +154,16 @@ namespace OpenRA.Mods.RA.Widgets.Logic
mapButton.IsDisabled = configurationDisabled; mapButton.IsDisabled = configurationDisabled;
mapButton.OnClick = () => mapButton.OnClick = () =>
{ {
var onSelect = new Action<Map>(m => var onSelect = new Action<string>(uid =>
{ {
orderManager.IssueOrder(Order.Command("map " + m.Uid)); orderManager.IssueOrder(Order.Command("map " + uid));
Game.Settings.Server.Map = m.Uid; Game.Settings.Server.Map = uid;
Game.Settings.Save(); Game.Settings.Save();
}); });
Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs() Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
{ {
{ "initialMap", Map.Uid }, { "initialMap", preview.Uid },
{ "onExit", () => { } }, { "onExit", () => { } },
{ "onSelect", onSelect } { "onSelect", onSelect }
}); });
@@ -181,7 +176,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
slotsButton.IsDisabled = () => configurationDisabled() || panel != PanelType.Players || slotsButton.IsDisabled = () => configurationDisabled() || panel != PanelType.Players ||
!orderManager.LobbyInfo.Slots.Values.Any(s => s.AllowBots || !s.LockTeam); !orderManager.LobbyInfo.Slots.Values.Any(s => s.AllowBots || !s.LockTeam);
var aiModes = Rules.Info["player"].Traits.WithInterface<IBotInfo>().Select(t => t.Name); var botNames = Rules.Info["player"].Traits.WithInterface<IBotInfo>().Select(t => t.Name);
slotsButton.OnMouseDown = _ => slotsButton.OnMouseDown = _ =>
{ {
var options = new Dictionary<string, IEnumerable<DropDownOption>>(); var options = new Dictionary<string, IEnumerable<DropDownOption>>();
@@ -189,7 +184,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin); var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
if (orderManager.LobbyInfo.Slots.Values.Any(s => s.AllowBots)) if (orderManager.LobbyInfo.Slots.Values.Any(s => s.AllowBots))
{ {
var botOptions = new List<DropDownOption>(){ new DropDownOption() var botOptions = new List<DropDownOption>()
{
new DropDownOption()
{ {
Title = "Add", Title = "Add",
IsSelected = () => false, IsSelected = () => false,
@@ -197,13 +194,14 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{ {
foreach (var slot in orderManager.LobbyInfo.Slots) foreach (var slot in orderManager.LobbyInfo.Slots)
{ {
var bot = aiModes.Random(Game.CosmeticRandom); var bot = botNames.Random(Game.CosmeticRandom);
var c = orderManager.LobbyInfo.ClientInSlot(slot.Key); var c = orderManager.LobbyInfo.ClientInSlot(slot.Key);
if (slot.Value.AllowBots == true && (c == null || c.Bot != null)) if (slot.Value.AllowBots == true && (c == null || c.Bot != null))
orderManager.IssueOrder(Order.Command("slot_bot {0} {1} {2}".F(slot.Key, botController.Index, bot))); orderManager.IssueOrder(Order.Command("slot_bot {0} {1} {2}".F(slot.Key, botController.Index, bot)));
} }
} }
}}; }
};
if (orderManager.LobbyInfo.Clients.Any(c => c.Bot != null)) if (orderManager.LobbyInfo.Clients.Any(c => c.Bot != null))
{ {
@@ -299,7 +297,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (allowCheats != null) if (allowCheats != null)
{ {
allowCheats.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllowCheats; allowCheats.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllowCheats;
allowCheats.IsDisabled = () => Map.Options.Cheats.HasValue || configurationDisabled(); allowCheats.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.Cheats.HasValue || configurationDisabled();
allowCheats.OnClick = () => orderManager.IssueOrder(Order.Command( allowCheats.OnClick = () => orderManager.IssueOrder(Order.Command(
"allowcheats {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllowCheats))); "allowcheats {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllowCheats)));
} }
@@ -308,7 +306,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (crates != null) if (crates != null)
{ {
crates.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Crates; crates.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Crates;
crates.IsDisabled = () => Map.Options.Crates.HasValue || configurationDisabled(); crates.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.Crates.HasValue || configurationDisabled();
crates.OnClick = () => orderManager.IssueOrder(Order.Command( crates.OnClick = () => orderManager.IssueOrder(Order.Command(
"crates {0}".F(!orderManager.LobbyInfo.GlobalSettings.Crates))); "crates {0}".F(!orderManager.LobbyInfo.GlobalSettings.Crates)));
} }
@@ -317,7 +315,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (allybuildradius != null) if (allybuildradius != null)
{ {
allybuildradius.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllyBuildRadius; allybuildradius.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllyBuildRadius;
allybuildradius.IsDisabled = () => Map.Options.AllyBuildRadius.HasValue || configurationDisabled(); allybuildradius.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.AllyBuildRadius.HasValue || configurationDisabled();
allybuildradius.OnClick = () => orderManager.IssueOrder(Order.Command( allybuildradius.OnClick = () => orderManager.IssueOrder(Order.Command(
"allybuildradius {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllyBuildRadius))); "allybuildradius {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllyBuildRadius)));
} }
@@ -326,7 +324,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (fragileAlliance != null) if (fragileAlliance != null)
{ {
fragileAlliance.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.FragileAlliances; fragileAlliance.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.FragileAlliances;
fragileAlliance.IsDisabled = () => Map.Options.FragileAlliances.HasValue || configurationDisabled(); fragileAlliance.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.FragileAlliances.HasValue || configurationDisabled();
fragileAlliance.OnClick = () => orderManager.IssueOrder(Order.Command( fragileAlliance.OnClick = () => orderManager.IssueOrder(Order.Command(
"fragilealliance {0}".F(!orderManager.LobbyInfo.GlobalSettings.FragileAlliances))); "fragilealliance {0}".F(!orderManager.LobbyInfo.GlobalSettings.FragileAlliances)));
} }
@@ -334,12 +332,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var difficulty = optionsBin.GetOrNull<DropDownButtonWidget>("DIFFICULTY_DROPDOWNBUTTON"); var difficulty = optionsBin.GetOrNull<DropDownButtonWidget>("DIFFICULTY_DROPDOWNBUTTON");
if (difficulty != null) if (difficulty != null)
{ {
difficulty.IsVisible = () => Map.Options.Difficulties.Any(); difficulty.IsVisible = () => preview.Status == MapStatus.Available && preview.Map.Options.Difficulties.Any();
difficulty.IsDisabled = configurationDisabled; difficulty.IsDisabled = () => preview.Status != MapStatus.Available || configurationDisabled();
difficulty.GetText = () => orderManager.LobbyInfo.GlobalSettings.Difficulty; difficulty.GetText = () => orderManager.LobbyInfo.GlobalSettings.Difficulty;
difficulty.OnMouseDown = _ => difficulty.OnMouseDown = _ =>
{ {
var options = Map.Options.Difficulties.Select(d => new DropDownOption var options = preview.Map.Options.Difficulties.Select(d => new DropDownOption
{ {
Title = d, Title = d,
IsSelected = () => orderManager.LobbyInfo.GlobalSettings.Difficulty == d, IsSelected = () => orderManager.LobbyInfo.GlobalSettings.Difficulty == d,
@@ -371,8 +369,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var classes = Rules.Info["world"].Traits.WithInterface<MPStartUnitsInfo>() var classes = Rules.Info["world"].Traits.WithInterface<MPStartUnitsInfo>()
.Select(a => a.Class).Distinct(); .Select(a => a.Class).Distinct();
startingUnits.IsDisabled = () => !Map.Options.ConfigurableStartingUnits || configurationDisabled(); startingUnits.IsDisabled = () => preview.Status != MapStatus.Available || !preview.Map.Options.ConfigurableStartingUnits || configurationDisabled();
startingUnits.GetText = () => !Map.Options.ConfigurableStartingUnits ? "Not Available" : className(orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass); startingUnits.GetText = () => preview.Status != MapStatus.Available || !preview.Map.Options.ConfigurableStartingUnits ? "Not Available" : className(orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass);
startingUnits.OnMouseDown = _ => startingUnits.OnMouseDown = _ =>
{ {
var options = classes.Select(c => new DropDownOption var options = classes.Select(c => new DropDownOption
@@ -398,8 +396,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var startingCash = optionsBin.GetOrNull<DropDownButtonWidget>("STARTINGCASH_DROPDOWNBUTTON"); var startingCash = optionsBin.GetOrNull<DropDownButtonWidget>("STARTINGCASH_DROPDOWNBUTTON");
if (startingCash != null) if (startingCash != null)
{ {
startingCash.IsDisabled = () => Map.Options.StartingCash.HasValue || configurationDisabled(); startingCash.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.StartingCash.HasValue || configurationDisabled();
startingCash.GetText = () => Map.Options.StartingCash.HasValue ? "Not Available" : "${0}".F(orderManager.LobbyInfo.GlobalSettings.StartingCash); startingCash.GetText = () => preview.Status != MapStatus.Available || preview.Map.Options.StartingCash.HasValue ? "Not Available" : "${0}".F(orderManager.LobbyInfo.GlobalSettings.StartingCash);
startingCash.OnMouseDown = _ => startingCash.OnMouseDown = _ =>
{ {
var options = Rules.Info["player"].Traits.Get<PlayerResourcesInfo>().SelectableCash.Select(c => new DropDownOption var options = Rules.Info["player"].Traits.Get<PlayerResourcesInfo>().SelectableCash.Select(c => new DropDownOption
@@ -424,7 +422,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (enableShroud != null) if (enableShroud != null)
{ {
enableShroud.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Shroud; enableShroud.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Shroud;
enableShroud.IsDisabled = () => Map.Options.Shroud.HasValue || configurationDisabled(); enableShroud.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.Shroud.HasValue || configurationDisabled();
enableShroud.OnClick = () => orderManager.IssueOrder(Order.Command( enableShroud.OnClick = () => orderManager.IssueOrder(Order.Command(
"shroud {0}".F(!orderManager.LobbyInfo.GlobalSettings.Shroud))); "shroud {0}".F(!orderManager.LobbyInfo.GlobalSettings.Shroud)));
} }
@@ -433,7 +431,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (enableFog != null) if (enableFog != null)
{ {
enableFog.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Fog; enableFog.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Fog;
enableFog.IsDisabled = () => Map.Options.Fog.HasValue || configurationDisabled(); enableFog.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.Fog.HasValue || configurationDisabled();
enableFog.OnClick = () => orderManager.IssueOrder(Order.Command( enableFog.OnClick = () => orderManager.IssueOrder(Order.Command(
"fog {0}".F(!orderManager.LobbyInfo.GlobalSettings.Fog))); "fog {0}".F(!orderManager.LobbyInfo.GlobalSettings.Fog)));
} }
@@ -461,7 +459,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
chatTextField.OnTabKey = () => chatTextField.OnTabKey = () =>
{ {
teamChat ^= true; teamChat ^= true;
chatLabel.Text = (teamChat) ? "Team:" : "Chat:"; chatLabel.Text = teamChat ? "Team:" : "Chat:";
return true; return true;
}; };
@@ -471,8 +469,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var musicButton = lobby.GetOrNull<ButtonWidget>("MUSIC_BUTTON"); var musicButton = lobby.GetOrNull<ButtonWidget>("MUSIC_BUTTON");
if (musicButton != null) if (musicButton != null)
musicButton.OnClick = () => Ui.OpenWindow("MUSIC_PANEL", new WidgetArgs musicButton.OnClick = () => Ui.OpenWindow("MUSIC_PANEL",
{ { "onExit", () => {} } }); new WidgetArgs { { "onExit", () => { } } });
// Add a bot on the first lobbyinfo update // Add a bot on the first lobbyinfo update
if (this.skirmishMode) if (this.skirmishMode)
@@ -523,23 +521,25 @@ namespace OpenRA.Mods.RA.Widgets.Logic
void UpdateCurrentMap() void UpdateCurrentMap()
{ {
if (MapUid == orderManager.LobbyInfo.GlobalSettings.Map) var uid = orderManager.LobbyInfo.GlobalSettings.Map;
if (preview.Uid == uid)
return; return;
MapUid = orderManager.LobbyInfo.GlobalSettings.Map;
if (!Game.modData.AvailableMaps.ContainsKey (MapUid)) preview = Game.modData.MapCache[uid];
if (preview.Status != MapStatus.Available)
{
if (Game.Settings.Game.AllowDownloading) if (Game.Settings.Game.AllowDownloading)
{ {
Game.DownloadMap (MapUid); Game.DownloadMap(uid);
Game.Debug("A new map has been downloaded..."); Game.Debug("A new map has been downloaded...");
} }
else else
throw new InvalidOperationException("Server's new map doesn't exist on your system and Downloading turned off"); throw new InvalidOperationException("Server's new map doesn't exist on your system and Downloading turned off");
Map = new Map(Game.modData.AvailableMaps[MapUid].Path); }
// Restore default starting cash if the last map set it to something invalid // Restore default starting cash if the last map set it to something invalid
var pri = Rules.Info["player"].Traits.Get<PlayerResourcesInfo>(); var pri = Rules.Info["player"].Traits.Get<PlayerResourcesInfo>();
if (!Map.Options.StartingCash.HasValue && !pri.SelectableCash.Contains(orderManager.LobbyInfo.GlobalSettings.StartingCash)) if (!preview.Map.Options.StartingCash.HasValue && !pri.SelectableCash.Contains(orderManager.LobbyInfo.GlobalSettings.StartingCash))
orderManager.IssueOrder(Order.Command("startingcash {0}".F(pri.DefaultCash))); orderManager.IssueOrder(Order.Command("startingcash {0}".F(pri.DefaultCash)));
} }
@@ -554,14 +554,14 @@ namespace OpenRA.Mods.RA.Widgets.Logic
Widget template = null; Widget template = null;
// get template for possible reuse // get template for possible reuse
if (idx < Players.Children.Count) if (idx < players.Children.Count)
template = Players.Children[idx]; template = players.Children[idx];
// Empty slot
if (client == null) if (client == null)
{ {
if (template == null || template.Id != EmptySlotTemplate.Id) // Empty slot
template = EmptySlotTemplate.Clone(); if (template == null || template.Id != emptySlotTemplate.Id)
template = emptySlotTemplate.Clone();
if (Game.IsHost) if (Game.IsHost)
LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager); LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager);
@@ -573,13 +573,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
join.IsDisabled = () => orderManager.LocalClient.IsReady; join.IsDisabled = () => orderManager.LocalClient.IsReady;
join.OnClick = () => orderManager.IssueOrder(Order.Command("slot " + key)); join.OnClick = () => orderManager.IssueOrder(Order.Command("slot " + key));
} }
// Editable player in slot
else if ((client.Index == orderManager.LocalClient.Index) || else if ((client.Index == orderManager.LocalClient.Index) ||
(client.Bot != null && Game.IsHost)) (client.Bot != null && Game.IsHost))
{ {
if (template == null || template.Id != EditablePlayerTemplate.Id) // Editable player in slot
template = EditablePlayerTemplate.Clone(); if (template == null || template.Id != editablePlayerTemplate.Id)
template = editablePlayerTemplate.Clone();
LobbyUtils.SetupClientWidget(template, slot, client, orderManager, client.Bot == null); LobbyUtils.SetupClientWidget(template, slot, client, orderManager, client.Bot == null);
@@ -589,31 +588,32 @@ namespace OpenRA.Mods.RA.Widgets.Logic
LobbyUtils.SetupEditableNameWidget(template, slot, client, orderManager); LobbyUtils.SetupEditableNameWidget(template, slot, client, orderManager);
LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, colorPreview); LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, colorPreview);
LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, CountryNames); LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, countryNames);
LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, Map.GetSpawnPoints().Length); LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, preview.SpawnPoints.Count);
LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager); LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager);
} }
else else
{ // Non-editable player in slot {
if (template == null || template.Id != NonEditablePlayerTemplate.Id) // Non-editable player in slot
template = NonEditablePlayerTemplate.Clone(); if (template == null || template.Id != nonEditablePlayerTemplate.Id)
template = nonEditablePlayerTemplate.Clone();
LobbyUtils.SetupClientWidget(template, slot, client, orderManager, client.Bot == null); LobbyUtils.SetupClientWidget(template, slot, client, orderManager, client.Bot == null);
LobbyUtils.SetupNameWidget(template, slot, client); LobbyUtils.SetupNameWidget(template, slot, client);
LobbyUtils.SetupKickWidget(template, slot, client, orderManager, lobby, LobbyUtils.SetupKickWidget(template, slot, client, orderManager, lobby,
() => panel = PanelType.Kick, () => panel = PanelType.Players); () => panel = PanelType.Kick, () => panel = PanelType.Players);
LobbyUtils.SetupColorWidget(template, slot, client); LobbyUtils.SetupColorWidget(template, slot, client);
LobbyUtils.SetupFactionWidget(template, slot, client, CountryNames); LobbyUtils.SetupFactionWidget(template, slot, client, countryNames);
LobbyUtils.SetupTeamWidget(template, slot, client); LobbyUtils.SetupTeamWidget(template, slot, client);
LobbyUtils.SetupReadyWidget(template, slot, client); LobbyUtils.SetupReadyWidget(template, slot, client);
} }
template.IsVisible = () => true; template.IsVisible = () => true;
if (idx >= Players.Children.Count) if (idx >= players.Children.Count)
Players.AddChild(template); players.AddChild(template);
else if (Players.Children[idx].Id != template.Id) else if (players.Children[idx].Id != template.Id)
Players.ReplaceChild(Players.Children[idx], template); players.ReplaceChild(players.Children[idx], template);
idx++; idx++;
} }
@@ -625,22 +625,22 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var c = client; var c = client;
// get template for possible reuse // get template for possible reuse
if (idx < Players.Children.Count) if (idx < players.Children.Count)
template = Players.Children[idx]; template = players.Children[idx];
// Editable spectator // Editable spectator
if (c.Index == orderManager.LocalClient.Index) if (c.Index == orderManager.LocalClient.Index)
{ {
if (template == null || template.Id != EditableSpectatorTemplate.Id) if (template == null || template.Id != editableSpectatorTemplate.Id)
template = EditableSpectatorTemplate.Clone(); template = editableSpectatorTemplate.Clone();
LobbyUtils.SetupEditableNameWidget(template, null, c, orderManager); LobbyUtils.SetupEditableNameWidget(template, null, c, orderManager);
} }
// Non-editable spectator
else else
{ {
if (template == null || template.Id != NonEditableSpectatorTemplate.Id) // Non-editable spectator
template = NonEditableSpectatorTemplate.Clone(); if (template == null || template.Id != nonEditableSpectatorTemplate.Id)
template = nonEditableSpectatorTemplate.Clone();
LobbyUtils.SetupNameWidget(template, null, client); LobbyUtils.SetupNameWidget(template, null, client);
LobbyUtils.SetupKickWidget(template, null, client, orderManager, lobby, LobbyUtils.SetupKickWidget(template, null, client, orderManager, lobby,
@@ -650,10 +650,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic
LobbyUtils.SetupClientWidget(template, null, c, orderManager, true); LobbyUtils.SetupClientWidget(template, null, c, orderManager, true);
template.IsVisible = () => true; template.IsVisible = () => true;
if (idx >= Players.Children.Count) if (idx >= players.Children.Count)
Players.AddChild(template); players.AddChild(template);
else if (Players.Children[idx].Id != template.Id) else if (players.Children[idx].Id != template.Id)
Players.ReplaceChild(Players.Children[idx], template); players.ReplaceChild(players.Children[idx], template);
idx++; idx++;
} }
@@ -662,10 +662,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (orderManager.LocalClient.Slot != null) if (orderManager.LocalClient.Slot != null)
{ {
Widget spec = null; Widget spec = null;
if (idx < Players.Children.Count) if (idx < players.Children.Count)
spec = Players.Children[idx]; spec = players.Children[idx];
if (spec == null || spec.Id != NewSpectatorTemplate.Id) if (spec == null || spec.Id != newSpectatorTemplate.Id)
spec = NewSpectatorTemplate.Clone(); spec = newSpectatorTemplate.Clone();
LobbyUtils.SetupKickSpectatorsWidget(spec, orderManager, lobby, LobbyUtils.SetupKickSpectatorsWidget(spec, orderManager, lobby,
() => panel = PanelType.Kick, () => panel = PanelType.Players, this.skirmishMode); () => panel = PanelType.Kick, () => panel = PanelType.Players, this.skirmishMode);
@@ -678,16 +678,22 @@ namespace OpenRA.Mods.RA.Widgets.Logic
spec.IsVisible = () => true; spec.IsVisible = () => true;
if (idx >= Players.Children.Count) if (idx >= players.Children.Count)
Players.AddChild(spec); players.AddChild(spec);
else if (Players.Children[idx].Id != spec.Id) else if (players.Children[idx].Id != spec.Id)
Players.ReplaceChild(Players.Children[idx], spec); players.ReplaceChild(players.Children[idx], spec);
idx++; idx++;
} }
while (Players.Children.Count > idx) while (players.Children.Count > idx)
Players.RemoveChild(Players.Children[idx]); players.RemoveChild(players.Children[idx]);
}
void OnGameStart()
{
CloseWindow();
onStart();
} }
class DropDownOption class DropDownOption

View File

@@ -130,26 +130,23 @@ namespace OpenRA.Mods.RA.Widgets.Logic
color.AttachPanel(colorChooser, onExit); color.AttachPanel(colorChooser, onExit);
} }
public static Dictionary<int2, Session.Client> GetSpawnClients(OrderManager orderManager, Map map) public static Dictionary<CPos, Session.Client> GetSpawnClients(OrderManager orderManager, MapPreview preview)
{ {
var spawns = map.GetSpawnPoints(); var spawns = preview.SpawnPoints;
return orderManager.LobbyInfo.Clients return orderManager.LobbyInfo.Clients
.Where(c => c.SpawnPoint != 0) .Where(c => c.SpawnPoint != 0)
.ToDictionary( .ToDictionary(c => spawns[c.SpawnPoint - 1], c => c);
c => spawns[c.SpawnPoint - 1],
c => c);
} }
public static void SelectSpawnPoint(OrderManager orderManager, MapPreviewWidget mapPreview, Map map, MouseInput mi) public static void SelectSpawnPoint(OrderManager orderManager, MapPreviewWidget mapPreview, MapPreview preview, MouseInput mi)
{ {
if (map == null)
return;
if (mi.Button != MouseButton.Left) if (mi.Button != MouseButton.Left)
return; return;
if (!orderManager.LocalClient.IsObserver && orderManager.LocalClient.State == Session.ClientState.Ready) if (!orderManager.LocalClient.IsObserver && orderManager.LocalClient.State == Session.ClientState.Ready)
return; return;
var selectedSpawn = map.GetSpawnPoints() var selectedSpawn = preview.SpawnPoints
.Select((sp, i) => Pair.New(mapPreview.ConvertToPreview(sp), i)) .Select((sp, i) => Pair.New(mapPreview.ConvertToPreview(sp), i))
.Where(a => (a.First - mi.Location).LengthSquared < 64) .Where(a => (a.First - mi.Location).LengthSquared < 64)
.Select(a => a.Second + 1) .Select(a => a.Second + 1)

View File

@@ -18,21 +18,21 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{ {
public class MapChooserLogic public class MapChooserLogic
{ {
Map map; string selectedUid;
// May be a subset of available maps if a mode filter is active // May be a subset of available maps if a mode filter is active
Dictionary<string, Map> visibleMaps; List<string> visibleMaps;
ScrollPanelWidget scrollpanel; ScrollPanelWidget scrollpanel;
ScrollItemWidget itemTemplate; ScrollItemWidget itemTemplate;
string gameMode; string gameMode;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
internal MapChooserLogic(Widget widget, string initialMap, Action onExit, Action<Map> onSelect) internal MapChooserLogic(Widget widget, string initialMap, Action onExit, Action<string> onSelect)
{ {
map = Game.modData.AvailableMaps[WidgetUtils.ChooseInitialMap(initialMap)]; selectedUid = WidgetUtils.ChooseInitialMap(initialMap);
widget.Get<ButtonWidget>("BUTTON_OK").OnClick = () => { Ui.CloseWindow(); onSelect(map); }; widget.Get<ButtonWidget>("BUTTON_OK").OnClick = () => { Ui.CloseWindow(); onSelect(selectedUid); };
widget.Get<ButtonWidget>("BUTTON_CANCEL").OnClick = () => { Ui.CloseWindow(); onExit(); }; widget.Get<ButtonWidget>("BUTTON_CANCEL").OnClick = () => { Ui.CloseWindow(); onExit(); };
scrollpanel = widget.Get<ScrollPanelWidget>("MAP_LIST"); scrollpanel = widget.Get<ScrollPanelWidget>("MAP_LIST");
@@ -44,9 +44,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var gameModeDropdown = widget.GetOrNull<DropDownButtonWidget>("GAMEMODE_FILTER"); var gameModeDropdown = widget.GetOrNull<DropDownButtonWidget>("GAMEMODE_FILTER");
if (gameModeDropdown != null) if (gameModeDropdown != null)
{ {
var selectableMaps = Game.modData.AvailableMaps.Where(m => m.Value.Selectable).ToList(); var selectableMaps = Game.modData.MapCache.Where(m => m.Status == MapStatus.Available && m.Map.Selectable);
var gameModes = selectableMaps var gameModes = selectableMaps
.GroupBy(m => m.Value.Type) .GroupBy(m => m.Type)
.Select(g => Pair.New(g.Key, g.Count())).ToList(); .Select(g => Pair.New(g.Key, g.Count())).ToList();
// 'all game types' extra item // 'all game types' extra item
@@ -75,9 +75,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{ {
randomMapButton.OnClick = () => randomMapButton.OnClick = () =>
{ {
var kv = visibleMaps.Random(Game.CosmeticRandom); var uid = visibleMaps.Random(Game.CosmeticRandom);
map = kv.Value; selectedUid = uid;
scrollpanel.ScrollToItem(kv.Key); scrollpanel.ScrollToItem(uid);
}; };
randomMapButton.IsDisabled = () => visibleMaps == null || visibleMaps.Count == 0; randomMapButton.IsDisabled = () => visibleMaps == null || visibleMaps.Count == 0;
} }
@@ -85,27 +85,27 @@ namespace OpenRA.Mods.RA.Widgets.Logic
EnumerateMaps(onSelect); EnumerateMaps(onSelect);
} }
void EnumerateMaps(Action<Map> onSelect) void EnumerateMaps(Action<string> onSelect)
{ {
var maps = Game.modData.AvailableMaps var maps = Game.modData.MapCache
.Where(kv => kv.Value.Selectable) .Where(m => m.Status == MapStatus.Available && m.Map.Selectable)
.Where(kv => kv.Value.Type == gameMode || gameMode == null) .Where(m => m.Type == gameMode || gameMode == null)
.OrderBy(kv => kv.Value.PlayerCount) .OrderBy(m => m.PlayerCount)
.ThenBy(kv => kv.Value.Title); .ThenBy(m => m.Title);
scrollpanel.RemoveChildren(); scrollpanel.RemoveChildren();
foreach (var kv in maps) foreach (var loop in maps)
{ {
var m = kv.Value; var preview = loop;
var item = ScrollItemWidget.Setup(kv.Key, itemTemplate, () => m == map, () => map = m, () => { Ui.CloseWindow(); onSelect(m); }); var item = ScrollItemWidget.Setup(preview.Uid, itemTemplate, () => selectedUid == preview.Uid, () => selectedUid = preview.Uid, () => { Ui.CloseWindow(); onSelect(preview.Uid); });
var titleLabel = item.Get<LabelWidget>("TITLE"); var titleLabel = item.Get<LabelWidget>("TITLE");
titleLabel.GetText = () => m.Title; titleLabel.GetText = () => preview.Title;
var previewWidget = item.Get<MapPreviewWidget>("PREVIEW"); var previewWidget = item.Get<MapPreviewWidget>("PREVIEW");
previewWidget.IgnoreMouseOver = true; previewWidget.IgnoreMouseOver = true;
previewWidget.IgnoreMouseInput = true; previewWidget.IgnoreMouseInput = true;
previewWidget.Map = () => m; previewWidget.Preview = () => preview;
previewWidget.IsVisible = () => previewWidget.RenderBounds.IntersectsWith(scrollpanel.RenderBounds); previewWidget.IsVisible = () => previewWidget.RenderBounds.IntersectsWith(scrollpanel.RenderBounds);
var previewLoadingWidget = item.GetOrNull<BackgroundWidget>("PREVIEW_PLACEHOLDER"); var previewLoadingWidget = item.GetOrNull<BackgroundWidget>("PREVIEW_PLACEHOLDER");
@@ -114,17 +114,17 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var detailsWidget = item.GetOrNull<LabelWidget>("DETAILS"); var detailsWidget = item.GetOrNull<LabelWidget>("DETAILS");
if (detailsWidget != null) if (detailsWidget != null)
detailsWidget.GetText = () => "{0} ({1} players)".F(m.Type, m.PlayerCount); detailsWidget.GetText = () => "{0} ({1} players)".F(preview.Type, preview.PlayerCount);
var authorWidget = item.GetOrNull<LabelWidget>("AUTHOR"); var authorWidget = item.GetOrNull<LabelWidget>("AUTHOR");
if (authorWidget != null) if (authorWidget != null)
authorWidget.GetText = () => "Created by {0}".F(m.Author); authorWidget.GetText = () => "Created by {0}".F(preview.Author);
var sizeWidget = item.GetOrNull<LabelWidget>("SIZE"); var sizeWidget = item.GetOrNull<LabelWidget>("SIZE");
if (sizeWidget != null) if (sizeWidget != null)
{ {
var size = m.Bounds.Width + "x" + m.Bounds.Height; var size = preview.Bounds.Width + "x" + preview.Bounds.Height;
var numberPlayableCells = m.Bounds.Width * m.Bounds.Height; var numberPlayableCells = preview.Bounds.Width * preview.Bounds.Height;
if (numberPlayableCells >= 120 * 120) size += " (Huge)"; if (numberPlayableCells >= 120 * 120) size += " (Huge)";
else if (numberPlayableCells >= 90 * 90) size += " (Large)"; else if (numberPlayableCells >= 90 * 90) size += " (Large)";
else if (numberPlayableCells >= 60 * 60) size += " (Medium)"; else if (numberPlayableCells >= 60 * 60) size += " (Medium)";
@@ -135,9 +135,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic
scrollpanel.AddChild(item); scrollpanel.AddChild(item);
} }
visibleMaps = maps.ToDictionary(kv => kv.Key, kv => kv.Value); visibleMaps = maps.Select(m => m.Uid).ToList();
if (visibleMaps.ContainsValue(map)) if (visibleMaps.Contains(selectedUid))
scrollpanel.ScrollToItem(visibleMaps.First(m => m.Value == map).Key); scrollpanel.ScrollToItem(selectedUid);
} }
} }
} }

View File

@@ -19,6 +19,11 @@ namespace OpenRA.Mods.RA.Widgets.Logic
public class ReplayBrowserLogic public class ReplayBrowserLogic
{ {
Widget panel; Widget panel;
MapPreview selectedMap = MapCache.UnknownMap;
string selectedFilename;
string selectedDuration;
string selectedPlayers;
bool selectedValid;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public ReplayBrowserLogic(Widget widget, Action onExit, Action onStart) public ReplayBrowserLogic(Widget widget, Action onExit, Action onStart)
@@ -44,15 +49,16 @@ namespace OpenRA.Mods.RA.Widgets.Logic
} }
var watch = panel.Get<ButtonWidget>("WATCH_BUTTON"); var watch = panel.Get<ButtonWidget>("WATCH_BUTTON");
watch.IsDisabled = () => currentReplay == null || currentMap == null || currentReplay.Duration == 0; watch.IsDisabled = () => !selectedValid || selectedMap.Status != MapStatus.Available;
watch.OnClick = () => { WatchReplay(); onStart(); }; watch.OnClick = () => { WatchReplay(); onStart(); };
panel.Get("REPLAY_INFO").IsVisible = () => currentReplay != null; panel.Get("REPLAY_INFO").IsVisible = () => selectedFilename != null;;
panel.Get<LabelWidget>("DURATION").GetText = () => selectedDuration;
panel.Get<MapPreviewWidget>("MAP_PREVIEW").Preview = () => selectedMap;
panel.Get<LabelWidget>("MAP_TITLE").GetText = () => selectedMap.Title;
panel.Get<LabelWidget>("PLAYERS").GetText = () => selectedPlayers;
} }
Replay currentReplay;
Map currentMap;
void SelectReplay(string filename) void SelectReplay(string filename)
{ {
if (filename == null) if (filename == null)
@@ -60,32 +66,31 @@ namespace OpenRA.Mods.RA.Widgets.Logic
try try
{ {
currentReplay = new Replay(filename); using (var conn = new ReplayConnection(filename))
currentMap = currentReplay.Map(); {
selectedFilename = filename;
panel.Get<LabelWidget>("DURATION").GetText = selectedMap = Game.modData.MapCache[conn.LobbyInfo.GlobalSettings.Map];
() => WidgetUtils.FormatTime(currentReplay.Duration); selectedDuration = WidgetUtils.FormatTime(conn.TickCount * Game.NetTickScale);
panel.Get<MapPreviewWidget>("MAP_PREVIEW").Map = () => currentMap; selectedPlayers = conn.LobbyInfo.Slots
panel.Get<LabelWidget>("MAP_TITLE").GetText = .Count(s => conn.LobbyInfo.ClientInSlot(s.Key) != null)
() => currentMap != null ? currentMap.Title : "(Unknown Map)"; .ToString();
selectedValid = conn.TickCount > 0;
var players = currentReplay.LobbyInfo.Slots }
.Count(s => currentReplay.LobbyInfo.ClientInSlot(s.Key) != null);
panel.Get<LabelWidget>("PLAYERS").GetText = () => players.ToString();
} }
catch (Exception e) catch (Exception e)
{ {
Log.Write("debug", "Exception while parsing replay: {0}", e); Log.Write("debug", "Exception while parsing replay: {0}", e);
currentReplay = null; selectedFilename = null;
currentMap = null; selectedValid = false;
selectedMap = MapCache.UnknownMap;
} }
} }
void WatchReplay() void WatchReplay()
{ {
if (currentReplay != null) if (selectedFilename != null)
{ {
Game.JoinReplay(currentReplay.Filename); Game.JoinReplay(selectedFilename);
Ui.CloseWindow(); Ui.CloseWindow();
} }
} }
@@ -93,7 +98,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
void AddReplay(ScrollPanelWidget list, string filename, ScrollItemWidget template) void AddReplay(ScrollPanelWidget list, string filename, ScrollItemWidget template)
{ {
var item = ScrollItemWidget.Setup(template, var item = ScrollItemWidget.Setup(template,
() => currentReplay != null && currentReplay.Filename == filename, () => selectedFilename == filename,
() => SelectReplay(filename), () => SelectReplay(filename),
() => WatchReplay()); () => WatchReplay());
var f = Path.GetFileName(filename); var f = Path.GetFileName(filename);

View File

@@ -12,6 +12,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Drawing; using System.Drawing;
using System.Net;
using System.Text;
using OpenRA.FileFormats; using OpenRA.FileFormats;
using OpenRA.Network; using OpenRA.Network;
using OpenRA.Server; using OpenRA.Server;
@@ -29,6 +31,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
enum SearchStatus { Fetching, Failed, NoGames, Hidden } enum SearchStatus { Fetching, Failed, NoGames, Hidden }
SearchStatus searchStatus = SearchStatus.Fetching; SearchStatus searchStatus = SearchStatus.Fetching;
Download currentQuery;
Widget panel, serverList;
bool showWaiting = true; bool showWaiting = true;
bool showEmpty = true; bool showEmpty = true;
@@ -39,7 +43,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{ {
switch (searchStatus) switch (searchStatus)
{ {
case SearchStatus.Fetching: return "Fetching game list...";
case SearchStatus.Failed: return "Failed to contact master server."; case SearchStatus.Failed: return "Failed to contact master server.";
case SearchStatus.NoGames: return "No games found."; case SearchStatus.NoGames: return "No games found.";
default: return ""; default: return "";
@@ -49,16 +52,18 @@ namespace OpenRA.Mods.RA.Widgets.Logic
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public ServerBrowserLogic(Widget widget, Action onStart, Action onExit) public ServerBrowserLogic(Widget widget, Action onStart, Action onExit)
{ {
var panel = widget; panel = widget;
this.onStart = onStart; this.onStart = onStart;
this.onExit = onExit; this.onExit = onExit;
var sl = panel.Get<ScrollPanelWidget>("SERVER_LIST"); serverList = panel.Get<ScrollPanelWidget>("SERVER_LIST");
serverTemplate = serverList.Get<ScrollItemWidget>("SERVER_TEMPLATE");
// Menu buttons // Menu buttons
var refreshButton = panel.Get<ButtonWidget>("REFRESH_BUTTON"); var refreshButton = panel.Get<ButtonWidget>("REFRESH_BUTTON");
refreshButton.IsDisabled = () => searchStatus == SearchStatus.Fetching; refreshButton.IsDisabled = () => searchStatus == SearchStatus.Fetching;
refreshButton.OnClick = () => ServerList.Query(games => RefreshServerList(panel, games)); refreshButton.GetText = () => searchStatus == SearchStatus.Fetching ? "Refreshing..." : "Refresh";
refreshButton.OnClick = RefreshServerList;
panel.Get<ButtonWidget>("DIRECTCONNECT_BUTTON").OnClick = OpenDirectConnectPanel; panel.Get<ButtonWidget>("DIRECTCONNECT_BUTTON").OnClick = OpenDirectConnectPanel;
panel.Get<ButtonWidget>("CREATE_BUTTON").OnClick = OpenCreateServerPanel; panel.Get<ButtonWidget>("CREATE_BUTTON").OnClick = OpenCreateServerPanel;
@@ -69,9 +74,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
panel.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); }; panel.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };
// Server list
serverTemplate = sl.Get<ScrollItemWidget>("SERVER_TEMPLATE");
// Display the progress label over the server list // Display the progress label over the server list
// The text is only visible when the list is empty // The text is only visible when the list is empty
var progressText = panel.Get<LabelWidget>("PROGRESS_LABEL"); var progressText = panel.Get<LabelWidget>("PROGRESS_LABEL");
@@ -82,33 +84,163 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (showWaitingCheckbox != null) if (showWaitingCheckbox != null)
{ {
showWaitingCheckbox.IsChecked = () => showWaiting; showWaitingCheckbox.IsChecked = () => showWaiting;
showWaitingCheckbox.OnClick = () => { showWaiting ^= true; ServerList.Query(games => RefreshServerList(panel, games)); }; showWaitingCheckbox.OnClick = () => { showWaiting ^= true; RefreshServerList(); };
} }
var showEmptyCheckbox = panel.GetOrNull<CheckboxWidget>("EMPTY"); var showEmptyCheckbox = panel.GetOrNull<CheckboxWidget>("EMPTY");
if (showEmptyCheckbox != null) if (showEmptyCheckbox != null)
{ {
showEmptyCheckbox.IsChecked = () => showEmpty; showEmptyCheckbox.IsChecked = () => showEmpty;
showEmptyCheckbox.OnClick = () => { showEmpty ^= true; ServerList.Query(games => RefreshServerList(panel, games)); }; showEmptyCheckbox.OnClick = () => { showEmpty ^= true; RefreshServerList(); };
} }
var showAlreadyStartedCheckbox = panel.GetOrNull<CheckboxWidget>("ALREADY_STARTED"); var showAlreadyStartedCheckbox = panel.GetOrNull<CheckboxWidget>("ALREADY_STARTED");
if (showAlreadyStartedCheckbox != null) if (showAlreadyStartedCheckbox != null)
{ {
showAlreadyStartedCheckbox.IsChecked = () => showStarted; showAlreadyStartedCheckbox.IsChecked = () => showStarted;
showAlreadyStartedCheckbox.OnClick = () => { showStarted ^= true; ServerList.Query(games => RefreshServerList(panel, games)); }; showAlreadyStartedCheckbox.OnClick = () => { showStarted ^= true; RefreshServerList(); };
} }
var showIncompatibleCheckbox = panel.GetOrNull<CheckboxWidget>("INCOMPATIBLE_VERSION"); var showIncompatibleCheckbox = panel.GetOrNull<CheckboxWidget>("INCOMPATIBLE_VERSION");
if (showIncompatibleCheckbox != null) if (showIncompatibleCheckbox != null)
{ {
showIncompatibleCheckbox.IsChecked = () => showIncompatible; showIncompatibleCheckbox.IsChecked = () => showIncompatible;
showIncompatibleCheckbox.OnClick = () => { showIncompatible ^= true; ServerList.Query(games => RefreshServerList(panel, games)); }; showIncompatibleCheckbox.OnClick = () => { showIncompatible ^= true; RefreshServerList(); };
} }
// Game.LoadWidget(null, "SERVERBROWSER_IRC", panel.Get("IRC_ROOT"), new WidgetArgs()); // Game.LoadWidget(null, "SERVERBROWSER_IRC", panel.Get("IRC_ROOT"), new WidgetArgs());
RefreshServerList();
}
ServerList.Query(games => RefreshServerList(panel, games)); void RefreshServerList()
{
// Query in progress
if (currentQuery != null)
return;
searchStatus = SearchStatus.Fetching;
Action<DownloadDataCompletedEventArgs, bool> onComplete = (i, cancelled) =>
{
currentQuery = null;
if (i.Error != null || cancelled)
{
RefreshServerListInner(null);
return;
}
var data = Encoding.UTF8.GetString(i.Result);
var yaml = MiniYaml.FromString(data);
var games = yaml.Select(a => FieldLoader.Load<GameServer>(a.Value))
.Where(gs => gs.Address != null);
RefreshServerListInner(games);
Game.RunAfterTick(() => RefreshServerListInner(games));
};
currentQuery = new Download(Game.Settings.Server.MasterServer + "list.php", _ => {}, onComplete);
}
public void RefreshServerListInner(IEnumerable<GameServer> games)
{
List<Widget> rows = new List<Widget>();
Game.RunAfterTick(() =>
{
serverList.RemoveChildren();
currentServer = null;
if (games == null)
{
searchStatus = SearchStatus.Failed;
return;
}
if (!games.Any())
{
searchStatus = SearchStatus.NoGames;
return;
}
currentServer = games.FirstOrDefault();
searchStatus = SearchStatus.Hidden;
foreach (var row in rows)
serverList.AddChild(row);
});
foreach (var loop in games.OrderByDescending(g => g.CanJoin()).ThenByDescending(g => g.Players))
{
var game = loop;
if (game == null)
continue;
var canJoin = game.CanJoin();
var item = ScrollItemWidget.Setup(serverTemplate, () => currentServer == game, () => currentServer = game, () => Join(game));
var map = Game.modData.MapCache[game.Map];
var preview = item.GetOrNull<MapPreviewWidget>("MAP_PREVIEW");
if (preview != null)
preview.Preview = () => map;
var title = item.GetOrNull<LabelWidget>("TITLE");
if (title != null)
{
title.GetText = () => game.Name;
title.GetColor = () => canJoin ? title.TextColor : Color.Gray;
}
var maptitle = item.GetOrNull<LabelWidget>("MAP");
if (title != null)
{
maptitle.GetText = () => map.Title;
maptitle.GetColor = () => canJoin ? maptitle.TextColor : Color.Gray;
}
var players = item.GetOrNull<LabelWidget>("PLAYERS");
if (players != null)
{
players.GetText = () => "{0} / {1}".F(game.Players, map.PlayerCount);
players.GetColor = () => canJoin ? players.TextColor : Color.Gray;
}
var state = item.GetOrNull<LabelWidget>("STATE");
if (state != null)
{
state.GetText = () => GetStateLabel(game);
state.GetColor = () => canJoin ? state.TextColor : Color.Gray;
}
var ip = item.GetOrNull<LabelWidget>("IP");
if (ip != null)
{
ip.GetText = () => game.Address;
ip.GetColor = () => canJoin ? ip.TextColor : Color.Gray;
}
var version = item.GetOrNull<LabelWidget>("VERSION");
if (version != null)
{
version.GetText = () => GenerateModLabel(game);
version.IsVisible = () => !game.CompatibleVersion();
version.GetColor = () => canJoin ? version.TextColor : Color.Gray;
}
var location = item.GetOrNull<LabelWidget>("LOCATION");
if (location != null)
{
var cachedServerLocation = LobbyUtils.LookupCountry(game.Address.Split(':')[0]);
location.GetText = () => cachedServerLocation;
location.IsVisible = () => game.CompatibleVersion();
location.GetColor = () => canJoin ? location.TextColor : Color.Gray;
}
if (!Filtered(game))
rows.Add(item);
}
} }
void OpenLobby() void OpenLobby()
@@ -155,10 +287,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (game == null || game.Players == 0) if (game == null || game.Players == 0)
return ""; return "";
var map = Game.modData.FindMapByUid(game.Map); var map = Game.modData.MapCache[game.Map];
return "{0} / {1}".F(game.Players, map.PlayerCount == 0 ? "?" : map.PlayerCount.ToString());
var maxPlayers = map == null ? "?" : (object)map.PlayerCount;
return "{0} / {1}".F(game.Players, maxPlayers);
} }
string GetStateLabel(GameServer game) string GetStateLabel(GameServer game)
@@ -176,11 +306,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
return "Unknown server state"; return "Unknown server state";
} }
Map GetMapPreview(GameServer game)
{
return (game == null) ? null : Game.modData.FindMapByUid(game.Map);
}
public static string GenerateModLabel(GameServer s) public static string GenerateModLabel(GameServer s)
{ {
Mod mod; Mod mod;
@@ -241,20 +366,16 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var item = ScrollItemWidget.Setup(serverTemplate, () => currentServer == game, () => currentServer = game, () => Join(game)); var item = ScrollItemWidget.Setup(serverTemplate, () => currentServer == game, () => currentServer = game, () => Join(game));
var map = Game.modData.MapCache[game.Map];
var preview = item.Get<MapPreviewWidget>("MAP_PREVIEW"); var preview = item.Get<MapPreviewWidget>("MAP_PREVIEW");
preview.Map = () => GetMapPreview(game); preview.Preview = () => map;
preview.IsVisible = () => GetMapPreview(game) != null;
var title = item.Get<LabelWidget>("TITLE"); var title = item.Get<LabelWidget>("TITLE");
title.GetText = () => game.Name; title.GetText = () => game.Name;
// TODO: Use game.MapTitle once the server supports it // TODO: Use game.MapTitle once the server supports it
var maptitle = item.Get<LabelWidget>("MAP"); var maptitle = item.Get<LabelWidget>("MAP");
maptitle.GetText = () => maptitle.GetText = () => map.Title;
{
var map = Game.modData.FindMapByUid(game.Map);
return map == null ? "Unknown Map" : map.Title;
};
// TODO: Use game.MaxPlayers once the server supports it // TODO: Use game.MaxPlayers once the server supports it
var players = item.Get<LabelWidget>("PLAYERS"); var players = item.Get<LabelWidget>("PLAYERS");

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
Widget panel; Widget panel;
Action onCreate; Action onCreate;
Action onExit; Action onExit;
Map map; MapPreview preview = MapCache.UnknownMap;
bool advertiseOnline; bool advertiseOnline;
bool allowPortForward; bool allowPortForward;
@@ -32,12 +32,11 @@ namespace OpenRA.Mods.RA.Widgets.Logic
this.onExit = onExit; this.onExit = onExit;
var settings = Game.Settings; var settings = Game.Settings;
preview = Game.modData.MapCache[WidgetUtils.ChooseInitialMap(Game.Settings.Server.Map)];
panel.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); }; panel.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };
panel.Get<ButtonWidget>("CREATE_BUTTON").OnClick = CreateAndJoin; panel.Get<ButtonWidget>("CREATE_BUTTON").OnClick = CreateAndJoin;
map = Game.modData.AvailableMaps[ WidgetUtils.ChooseInitialMap(Game.Settings.Server.Map) ];
var mapButton = panel.GetOrNull<ButtonWidget>("MAP_BUTTON"); var mapButton = panel.GetOrNull<ButtonWidget>("MAP_BUTTON");
if (mapButton != null) if (mapButton != null)
{ {
@@ -45,14 +44,14 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{ {
Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs() Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
{ {
{ "initialMap", map.Uid }, { "initialMap", preview.Uid },
{ "onExit", () => {} }, { "onExit", () => {} },
{ "onSelect", (Action<Map>)(m => map = m) } { "onSelect", (Action<String>)(uid => preview = Game.modData.MapCache[uid]) }
}); });
}; };
panel.Get<MapPreviewWidget>("MAP_PREVIEW").Map = () => map; panel.Get<MapPreviewWidget>("MAP_PREVIEW").Preview = () => preview;
panel.Get<LabelWidget>("MAP_NAME").GetText = () => map.Title; panel.Get<LabelWidget>("MAP_NAME").GetText = () => preview.Title;
} }
panel.Get<TextFieldWidget>("SERVER_NAME").Text = settings.Server.Name ?? ""; panel.Get<TextFieldWidget>("SERVER_NAME").Text = settings.Server.Name ?? "";
@@ -97,7 +96,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
Game.Settings.Server.ExternalPort = externalPort; Game.Settings.Server.ExternalPort = externalPort;
Game.Settings.Server.AdvertiseOnline = advertiseOnline; Game.Settings.Server.AdvertiseOnline = advertiseOnline;
Game.Settings.Server.AllowPortForward = allowPortForward; Game.Settings.Server.AllowPortForward = allowPortForward;
Game.Settings.Server.Map = map.Uid; Game.Settings.Server.Map = preview.Uid;
Game.Settings.Server.Password = password; Game.Settings.Server.Password = password;
Game.Settings.Save(); Game.Settings.Save();

View File

@@ -301,7 +301,11 @@ namespace OpenRA.Utility
} }
Console.WriteLine("Processing Maps:"); Console.WriteLine("Processing Maps:");
foreach (var map in Game.modData.FindMaps().Values) var maps = Game.modData.MapCache
.Where(m => m.Status == MapStatus.Available)
.Select(m => m.Map);
foreach (var map in maps)
{ {
Console.WriteLine("\t" + map.Path); Console.WriteLine("\t" + map.Path);
UpgradeActorRules(engineDate, ref map.Rules, null, 0); UpgradeActorRules(engineDate, ref map.Rules, null, 0);