From 59d0b2da74afb5372ef97d851fb9c209287b6320 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Tue, 1 Dec 2015 10:41:48 +1100 Subject: [PATCH] Treat .zip as texture packs in EnvMapAppearance packets, use builds.json for determing latest builds. --- .../2D/Widgets/BlockHotbarWidget.cs | 7 +- .../Network/NetworkProcessor.CPE.cs | 4 +- Launcher2/Gui/Screens/UpdatesScreen.cs | 40 +++++---- Launcher2/Launcher2.csproj | 1 + Launcher2/Utils/JSON.cs | 2 +- Launcher2/WebService/ClassiCubeSession.cs | 28 +++---- Launcher2/WebService/UpdateCheckTask.cs | 83 +++++++++---------- 7 files changed, 85 insertions(+), 80 deletions(-) diff --git a/ClassicalSharp/2D/Widgets/BlockHotbarWidget.cs b/ClassicalSharp/2D/Widgets/BlockHotbarWidget.cs index f4c6049c3..bc17f0c3d 100644 --- a/ClassicalSharp/2D/Widgets/BlockHotbarWidget.cs +++ b/ClassicalSharp/2D/Widgets/BlockHotbarWidget.cs @@ -82,10 +82,11 @@ namespace ClassicalSharp { } void MakeSelectionTexture() { - int size = (int)selBlockSize; - int y = game.Height - size; + int hSize = (int)selBlockSize; + int vSize = (int)Math.Floor( 23 * 2 * game.GuiScale ); + int y = game.Height - vSize; TextureRec rec = new TextureRec( 0, 22/256f, 24/256f, 24/256f ); - selTex = new Texture( game.GuiTexId, 0, y, size, size, rec ); + selTex = new Texture( game.GuiTexId, 0, y, hSize, vSize, rec ); } } } \ No newline at end of file diff --git a/ClassicalSharp/Network/NetworkProcessor.CPE.cs b/ClassicalSharp/Network/NetworkProcessor.CPE.cs index 584b664ed..6c2421300 100644 --- a/ClassicalSharp/Network/NetworkProcessor.CPE.cs +++ b/ClassicalSharp/Network/NetworkProcessor.CPE.cs @@ -322,7 +322,9 @@ namespace ClassicalSharp { if( !game.AcceptedUrls.HasAccepted( url ) ) game.AcceptedUrls.AddAccepted( url ); - if( usingTexturePack ) + // NOTE: This is entirely against the original CPE specification, but we + // do it here as a convenience until EnvMapAppearance v2 is more widely adopted. + if( usingTexturePack || url.EndsWith( ".zip" ) ) game.AsyncDownloader.DownloadData( url, true, "texturePack", lastModified ); else game.AsyncDownloader.DownloadImage( url, true, "terrain", lastModified ); diff --git a/Launcher2/Gui/Screens/UpdatesScreen.cs b/Launcher2/Gui/Screens/UpdatesScreen.cs index ed47f9d58..529dcac4a 100644 --- a/Launcher2/Gui/Screens/UpdatesScreen.cs +++ b/Launcher2/Gui/Screens/UpdatesScreen.cs @@ -18,7 +18,7 @@ namespace Launcher2 { titleFont = new Font( "Arial", 16, FontStyle.Bold ); infoFont = new Font( "Arial", 14, FontStyle.Regular ); buttonFont = titleFont; - widgets = new LauncherWidget[16]; + widgets = new LauncherWidget[17]; } UpdateCheckTask checkTask; @@ -28,18 +28,20 @@ namespace Launcher2 { Resize(); } + + Build dev, stable; public override void Tick() { if( checkTask != null && !checkTask.Working ) { if( checkTask.Exception != null ) { updateCheckFailed = true; } else { - lastStable = DateTime.Parse( checkTask.LatestStableDate, - null, DateTimeStyles.AssumeUniversal ); - lastDev = DateTime.Parse( checkTask.LatestDevDate, - null, DateTimeStyles.AssumeUniversal ); + dev = checkTask.LatestDev; + lastDev = dev.TimeBuilt; + validDev = dev.DirectXSize > 50000 && dev.OpenGLSize > 50000; - validStable = Int32.Parse( checkTask.LatestStableSize ) > 50000; - validDev = Int32.Parse( checkTask.LatestDevSize ) > 50000; + stable = checkTask.LatestStable; + lastStable = stable.TimeBuilt; + validStable = stable.DirectXSize > 50000 && stable.OpenGLSize > 50000; } checkTask = null; game.MakeBackground(); @@ -64,22 +66,24 @@ namespace Launcher2 { widgetIndex = 0; MakeLabelAt( "Your build:", titleFont, Anchor.Centre, Anchor.Centre, -55, -120 ); - string yourBuild = File.GetLastWriteTimeUtc( "ClassicalSharp.exe" ).ToString( dateFormat ); + string yourBuild = File.GetLastWriteTime( "ClassicalSharp.exe" ).ToString( dateFormat ); MakeLabelAt( yourBuild, infoFont, Anchor.Centre, Anchor.Centre, 100, -120 ); MakeLabelAt( "Latest stable:", titleFont, Anchor.Centre, Anchor.Centre, -70, -80 ); string latestStable = GetDateString( lastStable, validStable ); MakeLabelAt( latestStable, infoFont, Anchor.Centre, Anchor.Centre, 100, -80 ); - MakeButtonAt( "Update to stable", 180, 30, titleFont, Anchor.Centre, 0, -40, - (x, y) => UpdateBuild( lastStable, validStable, "latest.Release.zip" ) ); + MakeButtonAt( "Update to D3D9 stable", 260, 30, titleFont, Anchor.Centre, 0, -40, + (x, y) => UpdateBuild( lastStable, validStable, true, true ) ); + MakeButtonAt( "Update to OpenGL stable", 260, 30, titleFont, Anchor.Centre, 0, 0, + (x, y) => UpdateBuild( lastStable, validStable, true, false ) ); MakeLabelAt( "Latest dev:", titleFont, Anchor.Centre, Anchor.Centre, -60, 40 ); string latestDev = GetDateString( lastDev, validDev ); MakeLabelAt( latestDev, infoFont, Anchor.Centre, Anchor.Centre, 100, 40 ); - MakeButtonAt( "Update to OpenGL dev", 240, 30, titleFont, Anchor.Centre, 0, 80, - (x, y) => UpdateBuild( lastDev, validDev, "latest.zip" ) ); - MakeButtonAt( "Update to D3D9 dev", 240, 30, titleFont, Anchor.Centre, 0, 120, - (x, y) => UpdateBuild( lastDev, validDev, "latest.DirectX.zip" ) ); + MakeButtonAt( "Update to D3D9 dev", 240, 30, titleFont, Anchor.Centre, 0, 80, + (x, y) => UpdateBuild( lastDev, validDev, false, true ) ); + MakeButtonAt( "Update to OpenGL dev", 240, 30, titleFont, Anchor.Centre, 0, 120, + (x, y) => UpdateBuild( lastDev, validDev, false, false ) ); MakeButtonAt( "Back", 80, 35, titleFont, Anchor.Centre, 0, 200, (x, y) => game.SetScreen( new MainScreen( game ) ) ); @@ -90,11 +94,15 @@ namespace Launcher2 { if( !valid ) return "Build corrupted"; if( last == DateTime.MinValue ) return "Checking.."; - return last.ToUniversalTime().ToString( dateFormat ); + return last.ToString( dateFormat ); } - void UpdateBuild( DateTime last, bool valid, string dir ) { + void UpdateBuild( DateTime last, bool valid, bool release, bool dx ) { if( last == DateTime.MinValue || !valid ) return; + + Build build = release ? stable : dev; + string dir = dx ? build.DirectXPath : build.OpenGLPath; + Console.WriteLine( "FETCH! " + dir ); Patcher.Update( dir ); } diff --git a/Launcher2/Launcher2.csproj b/Launcher2/Launcher2.csproj index 2849587f0..5cdf53350 100644 --- a/Launcher2/Launcher2.csproj +++ b/Launcher2/Launcher2.csproj @@ -43,6 +43,7 @@ False TRACE obj\ + Project diff --git a/Launcher2/Utils/JSON.cs b/Launcher2/Utils/JSON.cs index b83712176..1efd78af8 100644 --- a/Launcher2/Utils/JSON.cs +++ b/Launcher2/Utils/JSON.cs @@ -162,7 +162,7 @@ namespace Launcher2 { static int GetLastIndexOfNumber( string json, int index ) { int lastIndex = index; for( ; lastIndex < json.Length; lastIndex++ ) { - if( "0123456789+-".IndexOf( json[lastIndex] ) == -1 ) + if( "0123456789+-.".IndexOf( json[lastIndex] ) == -1 ) break; } return lastIndex - 1; diff --git a/Launcher2/WebService/ClassiCubeSession.cs b/Launcher2/WebService/ClassiCubeSession.cs index 68b601fd9..73295769b 100644 --- a/Launcher2/WebService/ClassiCubeSession.cs +++ b/Launcher2/WebService/ClassiCubeSession.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Net; using System.Threading; +using JsonObject = System.Collections.Generic.Dictionary; namespace Launcher2 { @@ -56,8 +57,7 @@ namespace Launcher2 { var swGet = System.Diagnostics.Stopwatch.StartNew(); string getResponse = GetHtmlAll( loginUri, classicubeNetUri ); int index = 0; bool success = true; - Dictionary data = - (Dictionary)Json.ParseValue( getResponse, ref index, ref success ); + JsonObject data = (JsonObject)Json.ParseValue( getResponse, ref index, ref success ); string token = (string)data["token"]; // Step 2: POST to login page with csrf token. @@ -72,7 +72,7 @@ namespace Launcher2 { var sw = System.Diagnostics.Stopwatch.StartNew(); string response = PostHtmlAll( loginUri, loginUri, loginData ); index = 0; success = true; - data = (Dictionary)Json.ParseValue( response, ref index, ref success ); + data = (JsonObject)Json.ParseValue( response, ref index, ref success ); List errors = (List)data["errors"]; if( errors.Count > 0 || (data.ContainsKey( "username" ) && data["username"] == null) ) @@ -88,13 +88,12 @@ namespace Launcher2 { string response = GetHtmlAll( uri, classicubeNetUri ); int index = 0; bool success = true; - Dictionary root = - (Dictionary)Json.ParseValue( response, ref index, ref success ); + JsonObject root = (JsonObject)Json.ParseValue( response, ref index, ref success ); List list = (List)root["servers"]; - Dictionary pairs = (Dictionary)list[0]; - return new ClientStartData( Username, (string)pairs["mppass"], - (string)pairs["ip"], (string)pairs["port"] ); + JsonObject obj = (JsonObject)list[0]; + return new ClientStartData( Username, (string)obj["mppass"], + (string)obj["ip"], (string)obj["port"] ); } public List GetPublicServers() { @@ -102,17 +101,16 @@ namespace Launcher2 { List servers = new List(); string response = GetHtmlAll( publicServersUri, classicubeNetUri ); int index = 0; bool success = true; - Dictionary root = - (Dictionary)Json.ParseValue( response, ref index, ref success ); + JsonObject root = (JsonObject)Json.ParseValue( response, ref index, ref success ); List list = (List)root["servers"]; foreach( object server in list ) { - Dictionary pairs = (Dictionary)server; + JsonObject obj = (JsonObject)server; servers.Add( new ServerListEntry( - (string)pairs["hash"], (string)pairs["name"], - (string)pairs["players"], (string)pairs["maxplayers"], - (string)pairs["uptime"], (string)pairs["mppass"], - (string)pairs["ip"], (string)pairs["port"] ) ); + (string)obj["hash"], (string)obj["name"], + (string)obj["players"], (string)obj["maxplayers"], + (string)obj["uptime"], (string)obj["mppass"], + (string)obj["ip"], (string)obj["port"] ) ); } Log( "cc servers took " + sw.ElapsedMilliseconds ); sw.Stop(); diff --git a/Launcher2/WebService/UpdateCheckTask.cs b/Launcher2/WebService/UpdateCheckTask.cs index 9e4c8ac5c..9a4aeaa0b 100644 --- a/Launcher2/WebService/UpdateCheckTask.cs +++ b/Launcher2/WebService/UpdateCheckTask.cs @@ -1,22 +1,28 @@ using System; +using System.Collections.Generic; using System.Net; using System.Threading; +using JsonObject = System.Collections.Generic.Dictionary; namespace Launcher2 { + public class Build { + public DateTime TimeBuilt; + public string DirectXPath, OpenGLPath; + public int DirectXSize, OpenGLSize; + } + public sealed class UpdateCheckTask : IWebTask { public const string UpdatesUri = "http://cs.classicube.net/"; - StringComparison ordinal = StringComparison.Ordinal; - - public string LatestStableDate, LatestStableSize; - public string LatestDevDate, LatestDevSize; + public const string BuildsUri = "http://cs.classicube.net/builds.json"; + public Build LatestDev, LatestStable; public void CheckForUpdatesAsync() { Working = true; Exception = null; - LatestStableDate = null; LatestStableSize = null; - LatestDevDate = null; LatestDevSize = null; + LatestDev = null; + LatestStable = null; Thread thread = new Thread( UpdateWorker, 256 * 1024 ); thread.Name = "Launcher.UpdateCheck"; @@ -33,46 +39,35 @@ namespace Launcher2 { } void CheckUpdates() { - var response = GetHtml( UpdatesUri, UpdatesUri ); - foreach( string line in response ) { - if( line.StartsWith( @" pair in releaseBuilds ) + stableBuilds[i++] = MakeBuild( (JsonObject)pair.Value, true ); + Array.Sort( stableBuilds, + (a, b) => b.TimeBuilt.CompareTo( a.TimeBuilt ) ); + LatestStable = stableBuilds[0]; } - string ReadToken( string value, ref int index ) { - int start = index; - int wordEnd = -1; - for( ; index < value.Length; index++ ) { - if( value[index] == ' ' ) { - // found end of this word - if( wordEnd == -1 ) - wordEnd = index; - } else { - // found start of next word - if( wordEnd != -1 ) - break; - } - } + static readonly DateTime epoch = new DateTime( 1970, 1, 1, 0, 0, 0, DateTimeKind.Utc ); + Build MakeBuild( JsonObject obj, bool release ) { + Build build = new Build(); + string timeKey = release ? "release_ts" : "ts"; + double rawTime = Double.Parse( (string)obj[timeKey] ); + build.TimeBuilt = epoch.AddSeconds( rawTime ).ToLocalTime(); - if( wordEnd == -1 ) - wordEnd = value.Length; - return value.Substring( start, wordEnd - start ); - } + build.DirectXSize = Int32.Parse( (string)obj["dx_size"] ); + build.DirectXPath = (string)obj["dx_file"]; + build.OpenGLSize = Int32.Parse( (string)obj["ogl_size"] ); + build.OpenGLPath = (string)obj["ogl_file"]; + return build; + } } } \ No newline at end of file