Reimplement ingame map downloading.
This commit is contained in:
@@ -136,7 +136,7 @@ namespace OpenRA.GameRules
|
||||
public bool TeamHealthColors = false;
|
||||
|
||||
public bool AllowDownloading = true;
|
||||
public string[] MapRepositories = { "http://resource.openra.net/map/", "http://resource.ihptru.net:8080/map/" };
|
||||
public string MapRepository = "http://resource.openra.net/map/";
|
||||
}
|
||||
|
||||
public class KeySettings
|
||||
|
||||
@@ -14,10 +14,11 @@ using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -57,6 +58,43 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
public void QueryRemoteMapDetails(IEnumerable<string> uids)
|
||||
{
|
||||
var maps = uids.Distinct()
|
||||
.Select(uid => previews[uid])
|
||||
.Where(p => p.Status == MapStatus.Unavailable)
|
||||
.ToDictionary(p => p.Uid, p => p);
|
||||
|
||||
if (!maps.Any())
|
||||
return;
|
||||
|
||||
foreach (var p in maps.Values)
|
||||
p.UpdateRemoteSearch(MapStatus.Searching, null);
|
||||
|
||||
var url = Game.Settings.Game.MapRepository + "hash/" + string.Join(",", maps.Keys.ToArray()) + "/yaml";
|
||||
|
||||
Action<DownloadDataCompletedEventArgs, bool> onInfoComplete = (i, cancelled) =>
|
||||
{
|
||||
if (cancelled || i.Error != null)
|
||||
{
|
||||
Log.Write("debug", "Remote map query failed with error: {0}", i.Error != null ? i.Error.Message : "cancelled");
|
||||
Log.Write("debug", "URL was: {0}", url);
|
||||
foreach (var p in maps.Values)
|
||||
p.UpdateRemoteSearch(MapStatus.Unavailable, null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var data = Encoding.UTF8.GetString(i.Result);
|
||||
var yaml = MiniYaml.FromString(data);
|
||||
|
||||
foreach (var kv in yaml)
|
||||
maps[kv.Key].UpdateRemoteSearch(MapStatus.DownloadAvailable, kv.Value);
|
||||
};
|
||||
|
||||
new Download(url, _ => { }, onInfoComplete);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> FindMapsIn(string dir)
|
||||
{
|
||||
string[] noMaps = { };
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Graphics;
|
||||
@@ -21,10 +23,26 @@ using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public enum MapStatus { Available, Unavailable }
|
||||
public enum MapStatus { Available, Unavailable, Searching, DownloadAvailable, Downloading, DownloadError }
|
||||
|
||||
// Fields names must match the with the remote API
|
||||
public class RemoteMapData
|
||||
{
|
||||
public readonly string title;
|
||||
public readonly string author;
|
||||
public readonly string map_type;
|
||||
public readonly int players;
|
||||
public readonly Rectangle bounds;
|
||||
public readonly int[] spawnpoints = {};
|
||||
public readonly string minimap;
|
||||
public readonly bool downloading;
|
||||
public readonly bool requires_upgrade;
|
||||
}
|
||||
|
||||
public class MapPreview
|
||||
{
|
||||
static readonly List<CPos> NoSpawns = new List<CPos>();
|
||||
MapCache cache;
|
||||
|
||||
public readonly string Uid;
|
||||
public string Title { get; private set; }
|
||||
@@ -37,6 +55,10 @@ namespace OpenRA
|
||||
public Map Map { get; private set; }
|
||||
public MapStatus Status { get; private set; }
|
||||
|
||||
Download download;
|
||||
public long DownloadBytes { get; private set; }
|
||||
public int DownloadPercentage { get; private set; }
|
||||
|
||||
Sprite minimap;
|
||||
bool generatingMinimap;
|
||||
public Sprite Minimap
|
||||
@@ -62,7 +84,6 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
MapCache cache;
|
||||
public MapPreview(string uid, MapCache cache)
|
||||
{
|
||||
this.cache = cache;
|
||||
@@ -89,5 +110,116 @@ namespace OpenRA
|
||||
CustomPreview = m.CustomPreview;
|
||||
Status = MapStatus.Available;
|
||||
}
|
||||
|
||||
public void UpdateRemoteSearch(MapStatus status, MiniYaml yaml)
|
||||
{
|
||||
// Update on the main thread to ensure consistency
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
if (status == MapStatus.DownloadAvailable)
|
||||
{
|
||||
try
|
||||
{
|
||||
var r = FieldLoader.Load<RemoteMapData>(yaml);
|
||||
|
||||
// Map is not useable by the current version
|
||||
if (!r.downloading || r.requires_upgrade)
|
||||
{
|
||||
Status = MapStatus.Unavailable;
|
||||
return;
|
||||
}
|
||||
|
||||
Title = r.title;
|
||||
Type = r.map_type;
|
||||
Author = r.author;
|
||||
PlayerCount = r.players;
|
||||
Bounds = r.bounds;
|
||||
|
||||
var spawns = new List<CPos>();
|
||||
for (var j = 0; j < r.spawnpoints.Length; j += 2)
|
||||
spawns.Add(new CPos(r.spawnpoints[j], r.spawnpoints[j+1]));
|
||||
SpawnPoints = spawns;
|
||||
|
||||
CustomPreview = new Bitmap(new MemoryStream(Convert.FromBase64String(r.minimap)));
|
||||
}
|
||||
catch (Exception) {}
|
||||
|
||||
if (CustomPreview != null)
|
||||
cache.CacheMinimap(this);
|
||||
}
|
||||
Status = status;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public void Install()
|
||||
{
|
||||
if (Status != MapStatus.DownloadAvailable || !Game.Settings.Game.AllowDownloading)
|
||||
return;
|
||||
|
||||
Status = MapStatus.Downloading;
|
||||
var baseMapPath = new[] { Platform.SupportDir, "maps", Game.modData.Manifest.Mod.Id }.Aggregate(Path.Combine);
|
||||
|
||||
// Create the map directory if it doesn't exist
|
||||
if (!Directory.Exists(baseMapPath))
|
||||
Directory.CreateDirectory(baseMapPath);
|
||||
|
||||
new Thread(() =>
|
||||
{
|
||||
// Request the filename from the server
|
||||
// Run in a worker thread to avoid network delays
|
||||
var mapUrl = Game.Settings.Game.MapRepository + Uid;
|
||||
try
|
||||
{
|
||||
|
||||
var request = WebRequest.Create(mapUrl);
|
||||
request.Method = "HEAD";
|
||||
var res = request.GetResponse();
|
||||
|
||||
// Map not found
|
||||
if (res.Headers["Content-Disposition"] == null)
|
||||
{
|
||||
Status = MapStatus.DownloadError;
|
||||
return;
|
||||
}
|
||||
|
||||
var mapPath = Path.Combine(baseMapPath, res.Headers["Content-Disposition"].Replace("attachment; filename = ", ""));
|
||||
|
||||
Action<DownloadProgressChangedEventArgs> onDownloadProgress = i => { DownloadBytes = i.BytesReceived; DownloadPercentage = i.ProgressPercentage; };
|
||||
Action<AsyncCompletedEventArgs, bool> onDownloadComplete = (i, cancelled) =>
|
||||
{
|
||||
download = null;
|
||||
|
||||
if (cancelled || i.Error != null)
|
||||
{
|
||||
Log.Write("debug", "Remote map download failed with error: {0}", i.Error != null ? i.Error.Message : "cancelled");
|
||||
Log.Write("debug", "URL was: {0}", mapUrl);
|
||||
|
||||
Status = MapStatus.DownloadError;
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Write("debug", "Downloaded map to '{0}'", mapPath);
|
||||
Game.RunAfterTick(() => UpdateFromMap(new Map(mapPath)));
|
||||
};
|
||||
|
||||
download = new Download(mapUrl, mapPath, onDownloadProgress, onDownloadComplete);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
Status = MapStatus.DownloadError;
|
||||
}
|
||||
}).Start();
|
||||
}
|
||||
|
||||
public void CancelInstall()
|
||||
{
|
||||
if (download == null)
|
||||
return;
|
||||
|
||||
download.Cancel();
|
||||
download = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user