Reimplement ingame map downloading.

This commit is contained in:
Paul Chote
2014-03-13 12:43:42 +13:00
parent 6b199d3376
commit c3ba27ef6c
7 changed files with 563 additions and 67 deletions

View File

@@ -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;
}
}
}