diff --git a/OpenRA.Server/Connection.cs b/OpenRA.Server/Connection.cs index 50962f5d26..0d1b61e114 100644 --- a/OpenRA.Server/Connection.cs +++ b/OpenRA.Server/Connection.cs @@ -28,6 +28,7 @@ namespace OpenRA.Server /* file server state */ public int NextChunk = 0; public int NumChunks = 0; + public int RemainingBytes = 0; public Stream Stream = null; } diff --git a/OpenRA.Server/Server.cs b/OpenRA.Server/Server.cs index a78f7dcc5d..a52c01d6ae 100644 --- a/OpenRA.Server/Server.cs +++ b/OpenRA.Server/Server.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; using OpenRa.FileFormats; +using System.Security.Cryptography; namespace OpenRA.Server { @@ -18,8 +19,11 @@ namespace OpenRA.Server static Session lobbyInfo = new Session(); static bool GameStarted = false; - const int DownloadChunkInterval = 200000; - const int DownloadChunkSize = 4096; + const int DownloadChunkInterval = 20000; + const int DownloadChunkSize = 16384; + + static readonly string[] packages = { }; + //static readonly string[] packages = { "testpkg.mix" }; public static void Main(string[] args) { @@ -171,7 +175,7 @@ namespace OpenRA.Server { try { - var data = c.Stream.Read(DownloadChunkSize); + var data = c.Stream.Read(Math.Min(DownloadChunkSize, c.RemainingBytes)); if (data.Length != 0) { var chunk = new Chunk @@ -186,11 +190,14 @@ namespace OpenRA.Server FieldSaver.Save(chunk).Nodes.WriteToString()).Serialize()); } - if (data.Length < DownloadChunkSize) + c.RemainingBytes -= data.Length; + if (c.RemainingBytes == 0) { GetClient(c).State = Session.ClientState.NotReady; c.Stream.Dispose(); c.Stream = null; + + SyncLobbyInfo(); } } catch (Exception e) { DropClient(c, e); } @@ -392,16 +399,19 @@ namespace OpenRA.Server client.State = Session.ClientState.Downloading; var filename = so.Data.Split(':')[0]; - // todo: validate that the SHA1 they asked for matches what we've got. - - var length = (int) new FileInfo(filename).Length; - conn.NextChunk = 0; - conn.NumChunks = (length + DownloadChunkSize - 1) / DownloadChunkSize; if (conn.Stream != null) conn.Stream.Dispose(); conn.Stream = File.OpenRead(filename); + // todo: validate that the SHA1 they asked for matches what we've got. + + var length = (int) new FileInfo(filename).Length; + conn.NextChunk = 0; + conn.NumChunks = (length + DownloadChunkSize - 1) / DownloadChunkSize; + conn.RemainingBytes = length; + + SyncLobbyInfo(); } break; } @@ -447,9 +457,23 @@ namespace OpenRA.Server Console.WriteLine("Server emptied out; doing a bit of housekeeping to prepare for next game.."); inFlightFrames.Clear(); lobbyInfo = new Session(); + + lobbyInfo.GlobalSettings.Packages = + packages.Select(a => "{0}:{1}".F(a, CalculateSHA1(a))).ToArray(); + + foreach( var p in lobbyInfo.GlobalSettings.Packages ) + Console.WriteLine("Package: `{0}`", p); + GameStarted = false; } + static string CalculateSHA1(string filename) + { + using (var csp = SHA1.Create()) + return new string(csp.ComputeHash(File.ReadAllBytes(filename)) + .SelectMany(a => a.ToString("x2")).ToArray()); + } + static void SyncLobbyInfo() { var clientData = lobbyInfo.Clients.ToDictionary( diff --git a/OpenRa.Game/Chrome.cs b/OpenRa.Game/Chrome.cs index 15a65ba70a..0a0ef0bf5f 100644 --- a/OpenRa.Game/Chrome.cs +++ b/OpenRa.Game/Chrome.cs @@ -146,6 +146,8 @@ namespace OpenRa.Game public void Draw() { + DrawDownloadBar(); + if (!Game.orderManager.GameStarted) { DrawLobby(); @@ -184,6 +186,36 @@ namespace OpenRa.Game DrawOptionsMenu(); } + public void DrawDownloadBar() + { + if (PackageDownloader.IsIdle()) + return; + + var r = new Rectangle((Game.viewport.Width - 400) / 2, Game.viewport.Height - 110, 400, 100); + DrawDialogBackground(r, optionsSprites, true); + + DrawCentered("Downloading: {0} (+{1} more)".F( + PackageDownloader.CurrentPackage.Split(':')[0], + PackageDownloader.RemainingPackages), + new int2( Game.viewport.Width /2, Game.viewport.Height - 90), + Color.White); + + DrawDialogBackground(new Rectangle(r.Left + 30, r.Top + 50, r.Width - 60, 20), + panelSprites, false); + + var x1 = r.Left + 35; + var x2 = r.Right - 35; + var x = float2.Lerp(x1, x2, PackageDownloader.Fraction); + + for (var y = r.Top + 55; y < r.Top + 65; y++) + lineRenderer.DrawLine( + new float2(x1, y) + Game.viewport.Location, + new float2(x, y) + Game.viewport.Location, + Color.White, Color.White); + + lineRenderer.Flush(); + } + public void DrawLobby() { var w = 800; @@ -199,17 +231,17 @@ namespace OpenRa.Game panelSprites, false); renderer.DrawText2("Name", new int2(r.Left + 30, r.Top + 50), Color.White); - renderer.DrawText2("Color", new int2(r.Left + 250, r.Top + 50), Color.White); - renderer.DrawText2("Faction", new int2(r.Left + 320, r.Top + 50), Color.White); - renderer.DrawText2("Status", new int2(r.Left + 390, r.Top + 50), Color.White); + renderer.DrawText2("Color", new int2(r.Left + 230, r.Top + 50), Color.White); + renderer.DrawText2("Faction", new int2(r.Left + 300, r.Top + 50), Color.White); + renderer.DrawText2("Status", new int2(r.Left + 370, r.Top + 50), Color.White); var y = r.Top + 80; foreach (var client in Game.LobbyInfo.Clients) { renderer.DrawText(client.Name, new int2(r.Left + 30, y), Color.White); - renderer.DrawText(((PaletteType)client.Palette).ToString(), new int2(r.Left + 250, y), Color.White); - renderer.DrawText(((Race)client.Race).ToString(), new int2(r.Left + 320, y), Color.White); - renderer.DrawText(client.State.ToString(), new int2(r.Left + 390, y), Color.White); + renderer.DrawText(((PaletteType)client.Palette).ToString(), new int2(r.Left + 230, y), Color.White); + renderer.DrawText(((Race)client.Race).ToString(), new int2(r.Left + 300, y), Color.White); + renderer.DrawText(client.State.ToString(), new int2(r.Left + 370, y), Color.White); y += 30; } diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index 75a4c7ed86..32e7026741 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -53,9 +53,11 @@ namespace OpenRa.Game public static Minimap minimap; public static Session LobbyInfo = new Session(); public static int2[] SpawnPoints; + static bool changePending; public static void ChangeMap(string mapName) { + Game.changePending = false; Game.mapName = mapName; SheetBuilder.Initialize(renderer); SpriteSheetBuilder.Initialize(); @@ -170,6 +172,12 @@ namespace OpenRa.Game public static void Tick() { + if (changePending && PackageDownloader.IsIdle()) + { + ChangeMap(LobbyInfo.GlobalSettings.Map); + return; + } + int t = Environment.TickCount; int dt = t - lastTime; if (dt >= Settings.Timestep) @@ -392,7 +400,10 @@ namespace OpenRa.Game PackageDownloader.SetPackageList(LobbyInfo.GlobalSettings.Packages); if (!PackageDownloader.IsIdle()) + { + changePending = true; return; + } if (mapName != LobbyInfo.GlobalSettings.Map) { diff --git a/OpenRa.Game/PackageDownloader.cs b/OpenRa.Game/PackageDownloader.cs index 680829d398..c747bee96b 100644 --- a/OpenRa.Game/PackageDownloader.cs +++ b/OpenRa.Game/PackageDownloader.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Security.Cryptography; using OpenRa.FileFormats; +using System.Drawing; namespace OpenRa.Game { @@ -14,6 +15,11 @@ namespace OpenRa.Game static string currentPackage = null; static MemoryStream content = null; + public static string CurrentPackage { get { return currentPackage; } } + public static int RemainingPackages { get { return missingPackages.Count; } } + public static float Fraction { get; private set; } + public static int DownloadedBytes { get { return (int)content.Length; } } + public static void SetPackageList(string[] packages) { allPackages = packages; @@ -21,6 +27,8 @@ namespace OpenRa.Game if (currentPackage == null || !missingPackages.Contains(currentPackage)) BeginDownload(); + else + missingPackages.Remove(currentPackage); } class Chunk { public int Index = 0; public int Count = 0; public string Data = ""; } @@ -32,6 +40,8 @@ namespace OpenRa.Game var bytes = Convert.FromBase64String(c.Data); content.Write(bytes, 0, bytes.Length); + Fraction = (float)c.Index / c.Count; + if (c.Index == c.Count - 1) EndDownload(); } @@ -49,9 +59,12 @@ namespace OpenRa.Game content = new MemoryStream(); + Game.chat.AddLine(Color.White, "Debug", "Requesting package: {0}".F(currentPackage)); + Game.controller.AddOrder( - new Order("RequestFile", null, null, int2.Zero, currentPackage) - { IsImmediate = true }); + new Order("RequestFile", Game.LocalPlayer.PlayerActor, null, int2.Zero, currentPackage) { IsImmediate = true }); + + Fraction = 0f; } static void EndDownload() @@ -63,6 +76,8 @@ namespace OpenRa.Game if (CalculateSHA1(parts[0]) != parts[1]) throw new InvalidOperationException("Broken download"); + Game.chat.AddLine(Color.White, "Debug", "Finished receiving package: {0}".F(currentPackage)); + currentPackage = null; BeginDownload();