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).
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.
Overhauled the internal map management and preview generation code.
Server:
Message of the day is now shared between all mods and a default motd.txt gets created in the user directory.
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.
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.
Added support for custom map previews that replace the minimap in the game browser and lobby. Add map.png inside the map package.
Packaging:
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.

View File

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

View File

@@ -52,6 +52,20 @@ namespace OpenRA
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()
{
Game.OnQuit -= Cancel;

View File

@@ -370,7 +370,7 @@ namespace OpenRA
modData = new ModData(mod);
Renderer.InitializeFonts(modData.Manifest);
modData.InitializeLoaders();
modData.LoadMaps();
modData.MapCache.LoadMaps();
PerfHistory.items["render"].hasNormalTick = false;
PerfHistory.items["batches"].hasNormalTick = false;
@@ -401,7 +401,7 @@ namespace OpenRA
if (Settings.Server.DedicatedLoop)
{
Console.WriteLine("Starting a new server instance...");
modData.LoadMaps();
modData.MapCache.LoadMaps();
continue;
}
@@ -426,13 +426,14 @@ namespace OpenRA
static string ChooseShellmap()
{
var shellmaps = modData.AvailableMaps
.Where(m => m.Value.UseAsShellmap);
var shellmaps = modData.MapCache
.Where(m => m.Status == MapStatus.Available && m.Map.UseAsShellmap)
.Select(m => m.Uid);
if (!shellmaps.Any())
throw new InvalidDataException("No valid shellmaps available");
return shellmaps.Random(CosmeticRandom).Key;
return shellmaps.Random(CosmeticRandom);
}
static bool quit;
@@ -553,7 +554,7 @@ namespace OpenRA
WebClient webClient = new WebClient();
webClient.DownloadFile(url, tempFile);
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);
return true;

View File

@@ -67,6 +67,14 @@ namespace OpenRA.Graphics
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)
{
var data = new byte[size.Width * size.Height];

View File

@@ -9,6 +9,8 @@
#endregion
using System;
using System.Drawing;
using System.Drawing.Imaging;
using OpenRA.FileFormats.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()
{
return Exts.MakeArray(16, j => (j % 5 == 0) ? 1.0f : 0);

View File

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

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 ObjectCreator ObjectCreator;
public Dictionary<string, Map> AvailableMaps { get; private set; }
public readonly WidgetLoader WidgetLoader;
public readonly MapCache MapCache;
public ILoadScreen LoadScreen = null;
public SheetBuilder SheetBuilder;
public SpriteLoader SpriteLoader;
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)
{
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.Display();
WidgetLoader = new WidgetLoader(this);
MapCache = new MapCache(Manifest);
// HACK: Mount only local folders so we have a half-working environment for the asset installer
FileSystem.UnmountAll();
@@ -119,17 +99,13 @@ namespace OpenRA
FieldLoader.Translations = translations;
}
public void LoadMaps()
{
AvailableMaps = FindMaps();
}
public Map PrepareMap(string uid)
{
LoadScreen.Display();
if (!AvailableMaps.ContainsKey(uid))
var map = MapCache[uid].Map;
if (map == null)
throw new InvalidDataException("Invalid map uid: {0}".F(uid));
var map = new Map(AvailableMaps[uid].Path);
LoadTranslations(map);
@@ -148,33 +124,6 @@ namespace OpenRA
VoxelProvider.Initialize(Manifest.VoxelSequences, map.VoxelSequences);
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

View File

@@ -37,7 +37,7 @@ namespace OpenRA.Network
// Don't have the map locally
// 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 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\OrderIO.cs" />
<Compile Include="Network\OrderManager.cs" />
<Compile Include="Network\Replay.cs" />
<Compile Include="Network\ReplayConnection.cs" />
<Compile Include="Network\ServerList.cs" />
<Compile Include="Network\Session.cs" />
<Compile Include="Network\SyncReport.cs" />
<Compile Include="Network\UnitOrders.cs" />
@@ -232,6 +230,8 @@
<Compile Include="Traits\World\ActorMap.cs" />
<Compile Include="Widgets\HotkeyEntryWidget.cs" />
<Compile Include="Effects\MoveFlash.cs" />
<Compile Include="MapCache.cs" />
<Compile Include="MapPreview.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -21,8 +21,8 @@ namespace OpenRA.Widgets
{
public class MapPreviewWidget : Widget
{
public Func<Map> Map = () => null;
public Func<Dictionary<int2, Session.Client>> SpawnClients = () => new Dictionary<int2, Session.Client>();
public Func<MapPreview> Preview = () => null;
public Func<Dictionary<CPos, Session.Client>> SpawnClients = () => new Dictionary<CPos, Session.Client>();
public Action<MouseInput> OnMouseDown = _ => {};
public bool IgnoreMouseInput = false;
public bool ShowSpawnPoints = true;
@@ -32,6 +32,9 @@ namespace OpenRA.Widgets
Lazy<TooltipContainerWidget> tooltipContainer;
public int TooltipSpawnIndex = -1;
Rectangle MapRect;
float PreviewScale = 0;
public MapPreviewWidget()
{
tooltipContainer = Lazy.New(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
@@ -40,8 +43,7 @@ namespace OpenRA.Widgets
protected MapPreviewWidget(MapPreviewWidget other)
: base(other)
{
lastMap = other.lastMap;
Map = other.Map;
Preview = other.Preview;
SpawnClients = other.SpawnClients;
ShowSpawnPoints = other.ShowSpawnPoints;
TooltipTemplate = other.TooltipTemplate;
@@ -65,74 +67,51 @@ namespace OpenRA.Widgets
public override void MouseEntered()
{
if (TooltipContainer == null) return;
tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs() {{ "preview", this }});
if (TooltipContainer != null)
tooltipContainer.Value.SetTooltip(TooltipTemplate, new WidgetArgs() {{ "preview", this }});
}
public override void MouseExited()
{
if (TooltipContainer == null) return;
tooltipContainer.Value.RemoveTooltip();
if (TooltipContainer != null)
tooltipContainer.Value.RemoveTooltip();
}
public int2 ConvertToPreview(int2 point)
public int2 ConvertToPreview(CPos point)
{
var map = Map();
return new int2(MapRect.X + (int)(PreviewScale*(point.X - map.Bounds.Left)) , MapRect.Y + (int)(PreviewScale*(point.Y - map.Bounds.Top)));
var preview = Preview();
return new int2(MapRect.X + (int)(PreviewScale*(point.X - preview.Bounds.Left)) , MapRect.Y + (int)(PreviewScale*(point.Y - preview.Bounds.Top)));
}
Sheet mapChooserSheet;
Sprite mapChooserSprite;
Map lastMap;
Rectangle MapRect;
float PreviewScale = 0;
Sprite minimap;
public override void Draw()
{
var map = Map();
if (map == null)
var preview = Preview();
if (preview == null)
return;
// Preview unavailable
if (!Loaded)
{
GeneratePreview();
// Stash a copy of the minimap to ensure consistency
// (it may be modified by another thread)
minimap = preview.Minimap;
if (minimap == null)
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
PreviewScale = Math.Min(RenderBounds.Width * 1.0f / map.Bounds.Width, RenderBounds.Height * 1.0f / map.Bounds.Height);
var size = Math.Max(map.Bounds.Width, map.Bounds.Height);
var dw = (int)(PreviewScale * (size - map.Bounds.Width)) / 2;
var dh = (int)(PreviewScale * (size - map.Bounds.Height)) / 2;
MapRect = new Rectangle(RenderBounds.X + dw, RenderBounds.Y + dh, (int)(map.Bounds.Width * PreviewScale), (int)(map.Bounds.Height * PreviewScale));
PreviewScale = Math.Min(RenderBounds.Width / minimap.size.X, RenderBounds.Height / minimap.size.Y);
var w = (int)(PreviewScale * minimap.size.X);
var h = (int)(PreviewScale * minimap.size.Y);
var x = RenderBounds.X + (RenderBounds.Width - w) / 2;
var y = RenderBounds.Y + (RenderBounds.Height - h) / 2;
MapRect = new Rectangle(x, y, w, h);
Game.Renderer.RgbaSpriteRenderer.DrawSprite(mapChooserSprite,
new float2(MapRect.Location),
new float2(MapRect.Size));
Game.Renderer.RgbaSpriteRenderer.DrawSprite(minimap, new float2(MapRect.Location), new float2(MapRect.Size));
TooltipSpawnIndex = -1;
if (ShowSpawnPoints)
{
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)
{
var owned = colors.ContainsKey(p);
@@ -151,79 +130,6 @@ namespace OpenRA.Widgets
}
}
// Async map preview generation bits
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; } }
public bool Loaded { get { return minimap != null; } }
}
}

View File

@@ -225,14 +225,13 @@ namespace OpenRA.Widgets
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(map) || !availableMaps.ContainsKey(map))
if (string.IsNullOrEmpty(initialUid) || Game.modData.MapCache[initialUid].Status != MapStatus.Available)
{
Func<Map, bool> isIdealMap = m =>
Func<MapPreview, bool> isIdealMap = m =>
{
if (!m.Selectable)
if (m.Status != MapStatus.Available || !m.Map.Selectable)
return false;
// Other map types may have confusing settings or gameplay
@@ -240,22 +239,22 @@ namespace OpenRA.Widgets
return false;
// 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;
// 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 true;
};
var selected = availableMaps.Values.Where(m => isIdealMap(m)).RandomOrDefault(Game.CosmeticRandom) ??
availableMaps.Values.First(m => m.Selectable);
var selected = Game.modData.MapCache.Where(m => isIdealMap(m)).RandomOrDefault(Game.CosmeticRandom) ??
Game.modData.MapCache.First(m => m.Status == MapStatus.Available && m.Map.Selectable);
return selected.Uid;
}
return map;
return initialUid;
}
}

View File

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

View File

@@ -28,9 +28,10 @@ namespace OpenRA.Mods.RA
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)
.Select(c => (CPos) world.Map.GetSpawnPoints()[c.SpawnPoint-1]).ToList();
var available = world.Map.GetSpawnPoints().Select(c => (CPos)c).Except(taken).ToList();
.Select(c => spawns[c.SpawnPoint-1]).ToList();
var available = spawns.Except(taken).ToList();
// Set spawn
foreach (var kv in world.LobbyInfo.Slots)
@@ -41,7 +42,7 @@ namespace OpenRA.Mods.RA
var client = world.LobbyInfo.ClientInSlot(kv.Key);
var spid = (client == null || client.SpawnPoint == 0)
? ChooseSpawnPoint(world, available, taken)
: (CPos)world.Map.GetSpawnPoints()[client.SpawnPoint-1];
: world.Map.GetSpawnPoints()[client.SpawnPoint-1];
Start.Add(player, spid);
}

View File

@@ -280,7 +280,7 @@ namespace OpenRA.Mods.RA.Server
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");
return true;
@@ -720,7 +720,7 @@ namespace OpenRA.Mods.RA.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
.Select(p => MakeSlotFromPlayerReference(p.Value))
.Where(s => s != null)

View File

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

View File

@@ -130,26 +130,23 @@ namespace OpenRA.Mods.RA.Widgets.Logic
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
.Where(c => c.SpawnPoint != 0)
.ToDictionary(
c => spawns[c.SpawnPoint - 1],
c => c);
.ToDictionary(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)
return;
if (!orderManager.LocalClient.IsObserver && orderManager.LocalClient.State == Session.ClientState.Ready)
return;
var selectedSpawn = map.GetSpawnPoints()
var selectedSpawn = preview.SpawnPoints
.Select((sp, i) => Pair.New(mapPreview.ConvertToPreview(sp), i))
.Where(a => (a.First - mi.Location).LengthSquared < 64)
.Select(a => a.Second + 1)

View File

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

View File

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

View File

@@ -12,6 +12,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Net;
using System.Text;
using OpenRA.FileFormats;
using OpenRA.Network;
using OpenRA.Server;
@@ -29,6 +31,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
enum SearchStatus { Fetching, Failed, NoGames, Hidden }
SearchStatus searchStatus = SearchStatus.Fetching;
Download currentQuery;
Widget panel, serverList;
bool showWaiting = true;
bool showEmpty = true;
@@ -39,7 +43,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{
switch (searchStatus)
{
case SearchStatus.Fetching: return "Fetching game list...";
case SearchStatus.Failed: return "Failed to contact master server.";
case SearchStatus.NoGames: return "No games found.";
default: return "";
@@ -49,16 +52,18 @@ namespace OpenRA.Mods.RA.Widgets.Logic
[ObjectCreator.UseCtor]
public ServerBrowserLogic(Widget widget, Action onStart, Action onExit)
{
var panel = widget;
panel = widget;
this.onStart = onStart;
this.onExit = onExit;
var sl = panel.Get<ScrollPanelWidget>("SERVER_LIST");
serverList = panel.Get<ScrollPanelWidget>("SERVER_LIST");
serverTemplate = serverList.Get<ScrollItemWidget>("SERVER_TEMPLATE");
// Menu buttons
var refreshButton = panel.Get<ButtonWidget>("REFRESH_BUTTON");
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>("CREATE_BUTTON").OnClick = OpenCreateServerPanel;
@@ -69,9 +74,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
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
// The text is only visible when the list is empty
var progressText = panel.Get<LabelWidget>("PROGRESS_LABEL");
@@ -82,33 +84,163 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (showWaitingCheckbox != null)
{
showWaitingCheckbox.IsChecked = () => showWaiting;
showWaitingCheckbox.OnClick = () => { showWaiting ^= true; ServerList.Query(games => RefreshServerList(panel, games)); };
showWaitingCheckbox.OnClick = () => { showWaiting ^= true; RefreshServerList(); };
}
var showEmptyCheckbox = panel.GetOrNull<CheckboxWidget>("EMPTY");
if (showEmptyCheckbox != null)
{
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");
if (showAlreadyStartedCheckbox != null)
{
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");
if (showIncompatibleCheckbox != null)
{
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());
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()
@@ -155,10 +287,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (game == null || game.Players == 0)
return "";
var map = Game.modData.FindMapByUid(game.Map);
var maxPlayers = map == null ? "?" : (object)map.PlayerCount;
return "{0} / {1}".F(game.Players, maxPlayers);
var map = Game.modData.MapCache[game.Map];
return "{0} / {1}".F(game.Players, map.PlayerCount == 0 ? "?" : map.PlayerCount.ToString());
}
string GetStateLabel(GameServer game)
@@ -176,11 +306,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
return "Unknown server state";
}
Map GetMapPreview(GameServer game)
{
return (game == null) ? null : Game.modData.FindMapByUid(game.Map);
}
public static string GenerateModLabel(GameServer s)
{
Mod mod;
@@ -241,20 +366,16 @@ namespace OpenRA.Mods.RA.Widgets.Logic
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");
preview.Map = () => GetMapPreview(game);
preview.IsVisible = () => GetMapPreview(game) != null;
preview.Preview = () => map;
var title = item.Get<LabelWidget>("TITLE");
title.GetText = () => game.Name;
// TODO: Use game.MapTitle once the server supports it
var maptitle = item.Get<LabelWidget>("MAP");
maptitle.GetText = () =>
{
var map = Game.modData.FindMapByUid(game.Map);
return map == null ? "Unknown Map" : map.Title;
};
maptitle.GetText = () => map.Title;
// TODO: Use game.MaxPlayers once the server supports it
var players = item.Get<LabelWidget>("PLAYERS");

View File

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

View File

@@ -301,7 +301,11 @@ namespace OpenRA.Utility
}
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);
UpgradeActorRules(engineDate, ref map.Rules, null, 0);