Rewrite launcher web tasks to use asyncdownloader

This commit is contained in:
UnknownShadow200 2018-01-16 23:09:44 +11:00
parent fd5427edb4
commit 2b3fb48f54
25 changed files with 407 additions and 446 deletions

View File

@ -127,14 +127,14 @@ namespace ClassicalSharp.Gui.Screens {
int lastDownloadStatus = int.MinValue; int lastDownloadStatus = int.MinValue;
StringBuffer lastDownload = new StringBuffer(48); StringBuffer lastDownload = new StringBuffer(48);
void CheckOtherStatuses() { void CheckOtherStatuses() {
Request item = game.AsyncDownloader.CurrentItem; Request item = game.Downloader.CurrentItem;
if (item == null || !(item.Identifier == "terrain" || item.Identifier == "texturePack")) { if (item == null || !(item.Identifier == "terrain" || item.Identifier == "texturePack")) {
if (status.Textures[1].IsValid) status.SetText(1, null); if (status.Textures[1].IsValid) status.SetText(1, null);
lastDownloadStatus = int.MinValue; lastDownloadStatus = int.MinValue;
return; return;
} }
int progress = game.AsyncDownloader.CurrentItemProgress; int progress = game.Downloader.CurrentItemProgress;
if (progress == lastDownloadStatus) return; if (progress == lastDownloadStatus) return;
lastDownloadStatus = progress; lastDownloadStatus = progress;
SetFetchStatus(progress); SetFetchStatus(progress);

View File

@ -221,7 +221,7 @@ namespace ClassicalSharp.Entities {
row[x] = dist < half * half ? inPix : outPix; row[x] = dist < half * half ? inPix : outPix;
} }
} }
shadowTex = gfx.CreateTexture(fastBmp, true, false); shadowTex = gfx.CreateTexture(fastBmp, false, false);
} }
} }
} }

View File

@ -111,6 +111,7 @@ namespace ClassicalSharp.Entities {
if (List[i] == null) continue; if (List[i] == null) continue;
List[i].ContextLost(); List[i].ContextLost();
} }
game.Graphics.DeleteTexture(ref ShadowComponent.shadowTex);
} }
void ContextRecreated() { void ContextRecreated() {

View File

@ -112,7 +112,7 @@ namespace ClassicalSharp.Entities {
if (!fetchedSkin && Model.UsesSkin) { if (!fetchedSkin && Model.UsesSkin) {
Player first = FirstOtherWithSameSkinAndFetchedSkin(); Player first = FirstOtherWithSameSkinAndFetchedSkin();
if (first == null) { if (first == null) {
game.AsyncDownloader.DownloadSkin(SkinName, SkinName); game.Downloader.AsyncGetSkin(SkinName, SkinName);
} else { } else {
ApplySkin(first); ApplySkin(first);
} }
@ -120,7 +120,7 @@ namespace ClassicalSharp.Entities {
} }
Request item; Request item;
if (!game.AsyncDownloader.TryGetItem(SkinName, out item)) return; if (!game.Downloader.TryGetItem(SkinName, out item)) return;
if (item == null || item.Data == null) { SetSkinAll(true); return; } if (item == null || item.Data == null) { SetSkinAll(true); return; }
Bitmap bmp = (Bitmap)item.Data; Bitmap bmp = (Bitmap)item.Data;

View File

@ -73,7 +73,7 @@ namespace ClassicalSharp {
BlockInfo.Init(); BlockInfo.Init();
ModelCache = new ModelCache(this); ModelCache = new ModelCache(this);
ModelCache.InitCache(); ModelCache.InitCache();
AsyncDownloader = new AsyncDownloader(Drawer2D); Components.Add(AsyncDownloader); Downloader = new AsyncDownloader(Drawer2D); Components.Add(Downloader);
Lighting = new BasicLighting(); Components.Add(Lighting); Lighting = new BasicLighting(); Components.Add(Lighting);
Drawer2D.UseBitmappedChat = ClassicMode || !Options.GetBool(OptionsKey.UseChatFont, false); Drawer2D.UseBitmappedChat = ClassicMode || !Options.GetBool(OptionsKey.UseChatFont, false);
@ -225,7 +225,7 @@ namespace ClassicalSharp {
const double defTicks = 1.0 / 20; const double defTicks = 1.0 / 20;
const double netTicks = 1.0 / 60; const double netTicks = 1.0 / 60;
AddScheduledTask(30, AsyncDownloader.PurgeOldEntriesTask); AddScheduledTask(30, Downloader.PurgeOldEntriesTask);
AddScheduledTask(netTicks, Server.Tick); AddScheduledTask(netTicks, Server.Tick);
entTask = AddScheduledTask(defTicks, Entities.Tick); entTask = AddScheduledTask(defTicks, Entities.Tick);

View File

@ -164,7 +164,7 @@ namespace ClassicalSharp {
public int Vertices; public int Vertices;
public FrustumCulling Culling; public FrustumCulling Culling;
public AsyncDownloader AsyncDownloader; public AsyncDownloader Downloader;
/// <summary> How sensitive the client is to changes in the player's mouse position. </summary> /// <summary> How sensitive the client is to changes in the player's mouse position. </summary>
public int MouseSensitivity = 30; public int MouseSensitivity = 30;

View File

@ -65,7 +65,7 @@ namespace ClassicalSharp {
protected void WarningScreenTick(Overlay warning) { protected void WarningScreenTick(Overlay warning) {
string identifier = warning.Metadata; string identifier = warning.Metadata;
Request item; Request item;
if (!game.AsyncDownloader.TryGetItem(identifier, out item) || item.Data == null) return; if (!game.Downloader.TryGetItem(identifier, out item) || item.Data == null) return;
long contentLength = (long)item.Data; long contentLength = (long)item.Data;
if (contentLength <= 0) return; if (contentLength <= 0) return;
@ -78,7 +78,7 @@ namespace ClassicalSharp {
protected internal void RetrieveTexturePack(string url) { protected internal void RetrieveTexturePack(string url) {
if (!game.AcceptedUrls.HasEntry(url) && !game.DeniedUrls.HasEntry(url)) { if (!game.AcceptedUrls.HasEntry(url) && !game.DeniedUrls.HasEntry(url)) {
game.AsyncDownloader.RetrieveContentLength(url, true, "CL_" + url); game.Downloader.AsyncGetContentLength(url, true, "CL_" + url);
string address = url; string address = url;
if (url.StartsWith("https://")) address = url.Substring(8); if (url.StartsWith("https://")) address = url.Substring(8);
if (url.StartsWith("http://")) address = url.Substring(7); if (url.StartsWith("http://")) address = url.Substring(7);
@ -120,20 +120,18 @@ namespace ClassicalSharp {
TexturePack.ExtractCurrent(game, url); TexturePack.ExtractCurrent(game, url);
if (url.Contains(".zip")) { if (url.Contains(".zip")) {
game.AsyncDownloader.DownloadData(url, true, "texturePack", game.Downloader.AsyncGetData(url, true, "texturePack", lastModified, etag);
lastModified, etag);
} else { } else {
game.AsyncDownloader.DownloadImage(url, true, "terrain", game.Downloader.AsyncGetImage(url, true, "terrain", lastModified, etag);
lastModified, etag);
} }
} }
protected void CheckAsyncResources() { protected void CheckAsyncResources() {
Request item; Request item;
if (game.AsyncDownloader.TryGetItem("terrain", out item)) { if (game.Downloader.TryGetItem("terrain", out item)) {
TexturePack.ExtractTerrainPng(game, item); TexturePack.ExtractTerrainPng(game, item);
} }
if (game.AsyncDownloader.TryGetItem("texturePack", out item)) { if (game.Downloader.TryGetItem("texturePack", out item)) {
TexturePack.ExtractTexturePack(game, item); TexturePack.ExtractTexturePack(game, item);
} }
} }

View File

@ -17,7 +17,7 @@ namespace ClassicalSharp.Network.Protocols {
public override void Tick() { public override void Tick() {
Request item; Request item;
game.AsyncDownloader.TryGetItem(womEnvIdentifier, out item); game.Downloader.TryGetItem(womEnvIdentifier, out item);
if (item != null && item.Data != null) { if (item != null && item.Data != null) {
ParseWomConfig((string)item.Data); ParseWomConfig((string)item.Data);
} }
@ -37,7 +37,7 @@ namespace ClassicalSharp.Network.Protocols {
// new world if the async 'get request' didn't complete before the new world was loaded. // new world if the async 'get request' didn't complete before the new world was loaded.
womCounter++; womCounter++;
womEnvIdentifier = "womenv_" + womCounter; womEnvIdentifier = "womenv_" + womCounter;
game.AsyncDownloader.DownloadPage(url, true, womEnvIdentifier); game.Downloader.AsyncGetString(url, true, womEnvIdentifier);
sendWomId = true; sendWomId = true;
} }

View File

@ -33,6 +33,7 @@ namespace ClassicalSharp.Network {
public int CurrentItemProgress = -3; public int CurrentItemProgress = -3;
public IDrawer2D Drawer; public IDrawer2D Drawer;
public CookieContainer Cookies; public CookieContainer Cookies;
public bool KeepAlive;
public AsyncDownloader(IDrawer2D drawer) { this.drawer = drawer; } public AsyncDownloader(IDrawer2D drawer) { this.drawer = drawer; }
#if !LAUNCHER #if !LAUNCHER
@ -63,59 +64,73 @@ namespace ClassicalSharp.Network {
/// <summary> Asynchronously downloads a skin. If 'skinName' points to the url then the skin is /// <summary> Asynchronously downloads a skin. If 'skinName' points to the url then the skin is
/// downloaded from that url, otherwise it is downloaded from the url 'defaultSkinServer'/'skinName'.png </summary> /// downloaded from that url, otherwise it is downloaded from the url 'defaultSkinServer'/'skinName'.png </summary>
/// <remarks> Identifier is skin_'skinName'.</remarks> /// <remarks> Identifier is skin_'skinName'.</remarks>
public void DownloadSkin(string identifier, string skinName) { public void AsyncGetSkin(string identifier, string skinName) {
string strippedSkinName = Utils.StripColours(skinName); string url = Utils.IsUrlPrefix(skinName, 0) ? skinName
string url = Utils.IsUrlPrefix(skinName, 0) ? skinName : : skinServer + Utils.StripColours(skinName) + ".png";
skinServer + strippedSkinName + ".png";
AddRequest(url, false, identifier, RequestType.Bitmap, AddRequest(url, false, identifier, RequestType.Bitmap,
DateTime.MinValue , null); DateTime.MinValue, null, null);
} }
#endif #endif
/// <summary> Asynchronously downloads a bitmap image from the specified url. </summary> /// <summary> Asynchronously downloads a bitmap image from the specified url. </summary>
public void DownloadImage(string url, bool priority, string identifier) { public void AsyncGetImage(string url, bool priority, string identifier) {
AddRequest(url, priority, identifier, RequestType.Bitmap, AddRequest(url, priority, identifier, RequestType.Bitmap,
DateTime.MinValue, null); DateTime.MinValue, null, null);
} }
/// <summary> Asynchronously downloads a string from the specified url. </summary> /// <summary> Asynchronously downloads a string from the specified url. </summary>
public void DownloadPage(string url, bool priority, string identifier) { public void AsyncGetString(string url, bool priority, string identifier) {
AddRequest(url, priority, identifier, RequestType.String, AddRequest(url, priority, identifier, RequestType.String,
DateTime.MinValue, null); DateTime.MinValue, null, null);
} }
/// <summary> Asynchronously downloads a byte array. </summary> /// <summary> Asynchronously downloads a byte array. </summary>
public void DownloadData(string url, bool priority, string identifier) { public void AsyncGetData(string url, bool priority, string identifier) {
AddRequest(url, priority, identifier, RequestType.ByteArray, AddRequest(url, priority, identifier, RequestType.ByteArray,
DateTime.MinValue, null); DateTime.MinValue, null, null);
} }
/// <summary> Asynchronously downloads a bitmap image. </summary> /// <summary> Asynchronously downloads a bitmap image. </summary>
public void DownloadImage(string url, bool priority, string identifier, public void AsyncGetImage(string url, bool priority, string identifier,
DateTime lastModified, string etag) { DateTime lastModified, string etag) {
AddRequest(url, priority, identifier, RequestType.Bitmap, AddRequest(url, priority, identifier, RequestType.Bitmap,
lastModified, etag); lastModified, etag, null);
} }
/// <summary> Asynchronously downloads a byte array. </summary> /// <summary> Asynchronously downloads a byte array. </summary>
public void DownloadData(string url, bool priority, string identifier, public void AsyncGetData(string url, bool priority, string identifier,
DateTime lastModified, string etag) { DateTime lastModified, string etag) {
AddRequest(url, priority, identifier, RequestType.ByteArray, AddRequest(url, priority, identifier, RequestType.ByteArray,
lastModified, etag); lastModified, etag, null);
} }
#if !LAUNCHER #if !LAUNCHER
/// <summary> Asynchronously retrieves the content length of the body response. </summary> /// <summary> Asynchronously retrieves the content length of the body response. </summary>
public void RetrieveContentLength(string url, bool priority, string identifier) { public void AsyncGetContentLength(string url, bool priority, string identifier) {
AddRequest(url, priority, identifier, RequestType.ContentLength, AddRequest(url, priority, identifier, RequestType.ContentLength,
DateTime.MinValue, null); DateTime.MinValue, null, null);
}
#else
/// <summary> Asynchronously retrieves the content length of the body response. </summary>
public void AsyncPostString(string url, bool priority, string identifier, string contents) {
AddRequest(url, priority, identifier, RequestType.String,
DateTime.MinValue, null, contents);
} }
#endif #endif
void AddRequest(string url, bool priority, string identifier, void AddRequest(string url, bool priority, string identifier,
RequestType type, DateTime lastModified, string etag) { RequestType type, DateTime lastModified, string etag, object data) {
lock (pendingLocker) { lock (pendingLocker) {
Request request = new Request(url, identifier, type, lastModified, etag); Request request = new Request();
request.Url = url;
request.Identifier = identifier;
request.Type = type;
request.LastModified = lastModified;
request.ETag = etag;
request.Data = data;
request.TimeAdded = DateTime.UtcNow;
if (priority) { if (priority) {
pending.Insert(0, request); pending.Insert(0, request);
} else { } else {
@ -208,7 +223,6 @@ namespace ClassicalSharp.Network {
string url = request.Url; string url = request.Url;
Utils.LogDebug("Downloading {0} from: {1}", request.Type, url); Utils.LogDebug("Downloading {0} from: {1}", request.Type, url);
HttpStatusCode status = HttpStatusCode.OK; HttpStatusCode status = HttpStatusCode.OK;
request.Data = null;
try { try {
HttpWebRequest req = MakeRequest(request); HttpWebRequest req = MakeRequest(request);
@ -228,6 +242,7 @@ namespace ClassicalSharp.Network {
status = ((HttpWebResponse)webEx.Response).StatusCode; status = ((HttpWebResponse)webEx.Response).StatusCode;
webEx.Response.Close(); webEx.Response.Close();
} }
request.WebEx = webEx;
} }
if (status != HttpStatusCode.OK) { if (status != HttpStatusCode.OK) {
@ -285,19 +300,31 @@ namespace ClassicalSharp.Network {
req.Proxy = null; req.Proxy = null;
req.UserAgent = Program.AppName; req.UserAgent = Program.AppName;
req.CookieContainer = Cookies; req.CookieContainer = Cookies;
req.KeepAlive = KeepAlive;
if (request.LastModified != DateTime.MinValue) if (request.LastModified != DateTime.MinValue) {
req.IfModifiedSince = request.LastModified; req.IfModifiedSince = request.LastModified;
if (request.ETag != null) }
if (request.ETag != null) {
req.Headers["If-None-Match"] = request.ETag; req.Headers["If-None-Match"] = request.ETag;
}
if (request.Data != null) {
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded; charset=UTF-8;";
byte[] encodedData = Encoding.UTF8.GetBytes((string)request.Data);
req.ContentLength = encodedData.Length;
using (Stream stream = req.GetRequestStream()) {
stream.Write(encodedData, 0, encodedData.Length);
}
}
return req; return req;
} }
static byte[] buffer = new byte[4096 * 8]; static byte[] buffer = new byte[4096 * 8];
MemoryStream DownloadBytes(HttpWebResponse response) { MemoryStream DownloadBytes(HttpWebResponse response) {
int length = (int)response.ContentLength; int length = (int)response.ContentLength;
MemoryStream dst = length > 0 ? MemoryStream dst = length > 0 ? new MemoryStream(length) : new MemoryStream();
new MemoryStream(length) : new MemoryStream();
CurrentItemProgress = length > 0 ? 0 : -1; CurrentItemProgress = length > 0 ? 0 : -1;
using (Stream src = response.GetResponseStream()) { using (Stream src = response.GetResponseStream()) {
@ -335,14 +362,7 @@ namespace ClassicalSharp.Network {
/// <summary> ETag of the item most recently cached. (if any) </summary> /// <summary> ETag of the item most recently cached. (if any) </summary>
public string ETag; public string ETag;
public Request(string url, string identifier, RequestType type, DateTime lastModified, string etag) { public WebException WebEx;
Url = url;
Identifier = identifier;
Type = type;
TimeAdded = DateTime.UtcNow;
LastModified = lastModified;
ETag = etag;
}
public void Dispose() { public void Dispose() {
Bitmap bmp = Data as Bitmap; Bitmap bmp = Data as Bitmap;

View File

@ -31,8 +31,7 @@ namespace ClassicalSharp.Singleplayer {
BlockInfo.CanPlace[i] = true; BlockInfo.CanPlace[i] = true;
BlockInfo.CanDelete[i] = true; BlockInfo.CanDelete[i] = true;
} }
game.AsyncDownloader.DownloadSkin(game.LocalPlayer.SkinName, game.Downloader.AsyncGetSkin(game.LocalPlayer.SkinName, game.LocalPlayer.SkinName);
game.LocalPlayer.SkinName);
game.Events.RaiseBlockPermissionsChanged(); game.Events.RaiseBlockPermissionsChanged();
int seed = new Random().Next(); int seed = new Random().Next();

View File

@ -9,6 +9,11 @@ using Launcher.Web;
namespace Launcher.Gui.Screens { namespace Launcher.Gui.Screens {
public sealed partial class MainScreen : InputScreen { public sealed partial class MainScreen : InputScreen {
GetCSRFTokenTask getTask;
SignInTask postTask;
FetchServersTask fetchTask;
bool signingIn = false;
public override void Tick() { public override void Tick() {
base.Tick(); base.Tick();
if (game.checkTask != null && game.checkTask.Completed && !updateDone) { if (game.checkTask != null && game.checkTask.Completed && !updateDone) {
@ -17,26 +22,72 @@ namespace Launcher.Gui.Screens {
else FailedUpdateCheck(game.checkTask); else FailedUpdateCheck(game.checkTask);
updateDone = true; updateDone = true;
} }
if (!signingIn) return; if (!signingIn) return;
ClassicubeSession session = game.Session;
string status = session.Status;
if (status != lastStatus)
SetStatus(status);
if (session.Working) return; if (getTask != null) {
if (session.Exception != null) { LoginGetTick();
DisplayWebException(session.Exception, session.Status); } else if (postTask != null) {
} else if (HasServers) { LoginPostTick();
game.SetScreen(new ServersScreen(game)); } else if (fetchTask != null) {
return; FetchTick();
}
} }
signingIn = false; void LoginGetTick() {
getTask.Tick();
if (!getTask.Completed) return;
if (getTask.Success) {
postTask = new SignInTask();
postTask.Username = Get(0);
postTask.Password = Get(1);
postTask.Token = getTask.Token;
postTask.RunAsync(game);
} else {
DisplayWebException(getTask.WebEx, "sign in");
}
getTask = null;
game.RedrawBackground(); game.RedrawBackground();
Resize(); Resize();
} }
void LoginPostTick() {
postTask.Tick();
if (!postTask.Completed) return;
if (postTask.Error != null) {
SetStatus("&c" + postTask.Error);
} else if (postTask.Success) {
game.Username = postTask.Username;
fetchTask = new FetchServersTask();
fetchTask.RunAsync(game);
SetStatus("&eRetrieving servers list..");
} else {
DisplayWebException(postTask.WebEx, "sign in");
}
postTask = null;
game.RedrawBackground();
Resize();
}
void FetchTick() {
fetchTask.Tick();
if (!fetchTask.Completed) return;
if (fetchTask.Success) {
game.Servers = fetchTask.Servers;
game.SetScreen(new ServersScreen(game));
} else {
DisplayWebException(fetchTask.WebEx, "retrieving servers list");
game.RedrawBackground();
Resize();
}
fetchTask = null;
}
string lastStatus; string lastStatus;
void SetStatus(string text) { void SetStatus(string text) {
lastStatus = text; lastStatus = text;
@ -48,11 +99,6 @@ namespace Launcher.Gui.Screens {
game.Dirty = true; game.Dirty = true;
} }
bool HasServers {
get { return game.Session.Servers != null && game.Session.Servers.Count != 0; }
}
bool signingIn;
void LoginAsync(int mouseX, int mouseY) { void LoginAsync(int mouseX, int mouseY) {
if (String.IsNullOrEmpty(Get(0))) { if (String.IsNullOrEmpty(Get(0))) {
SetStatus("&eUsername required"); return; SetStatus("&eUsername required"); return;
@ -60,7 +106,9 @@ namespace Launcher.Gui.Screens {
if (String.IsNullOrEmpty(Get(1))) { if (String.IsNullOrEmpty(Get(1))) {
SetStatus("&ePassword required"); return; SetStatus("&ePassword required"); return;
} }
if (signingIn) return; if (getTask != null) return;
game.Username = Get(0);
UpdateSignInInfo(Get(0), Get(1)); UpdateSignInInfo(Get(0), Get(1));
CheckboxWidget skip = widgets[view.sslIndex] as CheckboxWidget; CheckboxWidget skip = widgets[view.sslIndex] as CheckboxWidget;
@ -71,9 +119,11 @@ namespace Launcher.Gui.Screens {
ServicePointManager.ServerCertificateValidationCallback = null; ServicePointManager.ServerCertificateValidationCallback = null;
} }
game.Session.LoginAsync(Get(0), Get(1)); getTask = new GetCSRFTokenTask();
getTask.RunAsync(game);
game.RedrawBackground(); game.RedrawBackground();
Resize(); Resize();
SetStatus("&eSigning in.."); SetStatus("&eSigning in..");
signingIn = true; signingIn = true;
} }
@ -82,6 +132,8 @@ namespace Launcher.Gui.Screens {
ErrorHandler.LogError(action, ex); ErrorHandler.LogError(action, ex);
bool sslCertError = ex.Status == WebExceptionStatus.TrustFailure || bool sslCertError = ex.Status == WebExceptionStatus.TrustFailure ||
(ex.Status == WebExceptionStatus.SendFailure && OpenTK.Configuration.RunningOnMono); (ex.Status == WebExceptionStatus.SendFailure && OpenTK.Configuration.RunningOnMono);
signingIn = false;
if (ex.Status == WebExceptionStatus.Timeout) { if (ex.Status == WebExceptionStatus.Timeout) {
string text = "&cTimed out when connecting to classicube.net."; string text = "&cTimed out when connecting to classicube.net.";
SetStatus(text); SetStatus(text);
@ -162,8 +214,7 @@ namespace Launcher.Gui.Screens {
void UpdateSignInInfo(string user, string password) { void UpdateSignInInfo(string user, string password) {
// If the client has changed some settings in the meantime, make sure we keep the changes // If the client has changed some settings in the meantime, make sure we keep the changes
if (!Options.Load()) if (!Options.Load()) return;
return;
Options.Set("launcher-cc-username", user); Options.Set("launcher-cc-username", user);
Options.Set("launcher-cc-password", Secure.Encode(password, user)); Options.Set("launcher-cc-password", Secure.Encode(password, user));

View File

@ -2,6 +2,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using ClassicalSharp; using ClassicalSharp;
using Launcher.Web;
using Launcher.Gui.Views; using Launcher.Gui.Views;
using Launcher.Gui.Widgets; using Launcher.Gui.Widgets;
using OpenTK.Input; using OpenTK.Input;
@ -11,6 +12,7 @@ namespace Launcher.Gui.Screens {
const int tableX = 10, tableY = 50; const int tableX = 10, tableY = 50;
ServersView view; ServersView view;
FetchServersTask fetchTask;
public ServersScreen(LauncherWindow game) : base(game) { public ServersScreen(LauncherWindow game) : base(game) {
enterIndex = 3; enterIndex = 3;
@ -20,7 +22,7 @@ namespace Launcher.Gui.Screens {
public override void Tick() { public override void Tick() {
base.Tick(); base.Tick();
if (fetchingList) CheckFetchStatus(); if (fetchTask != null) CheckFetchStatus();
TableWidget table = (TableWidget)widgets[view.tableIndex]; TableWidget table = (TableWidget)widgets[view.tableIndex];
if (!game.Window.Mouse[MouseButton.Left]) { if (!game.Window.Mouse[MouseButton.Left]) {
@ -134,12 +136,10 @@ namespace Launcher.Gui.Screens {
game.ConnectToServer(table.servers, Get(view.hashIndex)); game.ConnectToServer(table.servers, Get(view.hashIndex));
} }
bool fetchingList = false;
void RefreshList(int mouseX, int mouseY) { void RefreshList(int mouseX, int mouseY) {
if (fetchingList) return; if (fetchTask != null) return;
fetchingList = true; fetchTask = new FetchServersTask();
game.Session.FetchServersAsync(); fetchTask.RunAsync(game);
view.RefreshText = "&eWorking.."; view.RefreshText = "&eWorking..";
Resize(); Resize();
} }
@ -187,10 +187,12 @@ namespace Launcher.Gui.Screens {
} }
void CheckFetchStatus() { void CheckFetchStatus() {
if (!game.Session.Done) return; fetchTask.Tick();
fetchingList = false; if (!fetchTask.Completed) return;
if (fetchTask.Success) game.Servers = fetchTask.Servers;
view.RefreshText = game.Session.Exception == null ? "Refresh" : "&cFailed"; view.RefreshText = fetchTask.Success ? "Refresh" : "&cFailed";
fetchTask = null;
Resize(); Resize();
// needed to ensure 'highlighted server hash' is over right entry after refresh // needed to ensure 'highlighted server hash' is over right entry after refresh

View File

@ -34,7 +34,7 @@ namespace Launcher.Gui.Screens {
SuccessfulUpdateCheck(game.checkTask); SuccessfulUpdateCheck(game.checkTask);
} }
checkTask = new UpdateCheckTask(); checkTask = new UpdateCheckTask();
checkTask.Init(game); checkTask.RunAsync(game);
} }
Build dev, stable; Build dev, stable;

View File

@ -79,7 +79,7 @@ namespace Launcher.Gui.Views {
TableWidget widget; TableWidget widget;
if (widgets[tableIndex] != null) { if (widgets[tableIndex] != null) {
widget = (TableWidget)widgets[tableIndex]; widget = (TableWidget)widgets[tableIndex];
if (widget.servers != game.Session.Servers) ResetTable(widget); if (widget.servers != game.Servers) ResetTable(widget);
} else { } else {
widget = new TableWidget(game); widget = new TableWidget(game);
ResetTable(widget); ResetTable(widget);
@ -91,7 +91,7 @@ namespace Launcher.Gui.Views {
} }
void ResetTable(TableWidget widget) { void ResetTable(TableWidget widget) {
widget.SetEntries(game.Session.Servers); widget.SetEntries(game.Servers);
widget.SetDrawData(drawer, tableFont, textFont, widget.SetDrawData(drawer, tableFont, textFont,
Anchor.LeftOrTop, Anchor.LeftOrTop, tableX, tableY); Anchor.LeftOrTop, Anchor.LeftOrTop, tableX, tableY);
widget.SortDefault(); widget.SortDefault();

View File

@ -145,15 +145,13 @@
<Compile Include="Patcher\ZipWriter.cs" /> <Compile Include="Patcher\ZipWriter.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WebTasks.cs" />
<Compile Include="Updater\Scripts.cs" /> <Compile Include="Updater\Scripts.cs" />
<Compile Include="Updater\Applier.cs" /> <Compile Include="Updater\Applier.cs" />
<Compile Include="Utils\Client.cs" /> <Compile Include="Utils\Client.cs" />
<Compile Include="Utils\JSON.cs" /> <Compile Include="Utils\JSON.cs" />
<Compile Include="Utils\LauncherSkin.cs" /> <Compile Include="Utils\LauncherSkin.cs" />
<Compile Include="Utils\Secure.cs" /> <Compile Include="Utils\Secure.cs" />
<Compile Include="WebService\ClassiCubeSession.cs" />
<Compile Include="WebService\IWebTask.cs" />
<Compile Include="WebService\UpdateCheckTask.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\OpenTK\OpenTK.csproj"> <ProjectReference Include="..\OpenTK\OpenTK.csproj">
@ -173,7 +171,6 @@
<Folder Include="Updater" /> <Folder Include="Updater" />
<Folder Include="Utils" /> <Folder Include="Utils" />
<Folder Include="Patcher" /> <Folder Include="Patcher" />
<Folder Include="WebService" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project> </Project>

View File

@ -37,7 +37,7 @@ namespace Launcher {
public Rectangle DirtyArea; public Rectangle DirtyArea;
/// <summary> Currently active logged in session with classicube.net. </summary> /// <summary> Currently active logged in session with classicube.net. </summary>
public ClassicubeSession Session = new ClassicubeSession(); public string Username;
/// <summary> Queue used to download resources asynchronously. </summary> /// <summary> Queue used to download resources asynchronously. </summary>
public AsyncDownloader Downloader; public AsyncDownloader Downloader;
@ -55,6 +55,8 @@ namespace Launcher {
public bool ShouldExit; public bool ShouldExit;
public bool ShouldUpdate; public bool ShouldUpdate;
public List<ServerListEntry> Servers = new List<ServerListEntry>();
public string FontName = "Arial"; public string FontName = "Arial";
public bool Minimised { public bool Minimised {
@ -136,7 +138,7 @@ namespace Launcher {
ServerListEntry entry = publicServers[i]; ServerListEntry entry = publicServers[i];
if (entry.Hash != hash) continue; if (entry.Hash != hash) continue;
data = new ClientStartData(Session.Username, entry.Mppass, data = new ClientStartData(Username, entry.Mppass,
entry.IPAddress, entry.Port, entry.Name); entry.IPAddress, entry.Port, entry.Name);
Client.Start(data, true, ref ShouldExit); Client.Start(data, true, ref ShouldExit);
return true; return true;
@ -144,7 +146,14 @@ namespace Launcher {
// Fallback to private server handling // Fallback to private server handling
try { try {
data = Session.GetConnectInfo(hash); // TODO: Rewrite to be async
FetchServerTask task = new FetchServerTask(Username, hash);
task.RunAsync(this);
while (!task.Completed) { task.Tick(); Thread.Sleep(10); }
if (task.WebEx != null) throw task.WebEx;
data = task.Info;
} catch (WebException ex) { } catch (WebException ex) {
ErrorHandler.LogError("retrieving server information", ex); ErrorHandler.LogError("retrieving server information", ex);
return false; return false;
@ -165,12 +174,15 @@ namespace Launcher {
platformDrawer.info = Window.WindowInfo; platformDrawer.info = Window.WindowInfo;
platformDrawer.Init(); platformDrawer.Init();
fetcher = new ResourceFetcher();
fetcher.CheckResourceExistence();
Downloader = new AsyncDownloader(Drawer); Downloader = new AsyncDownloader(Drawer);
Downloader.Init(""); Downloader.Init("");
Downloader.Cookies = new CookieContainer();
Downloader.KeepAlive = true;
fetcher = new ResourceFetcher();
fetcher.CheckResourceExistence();
checkTask = new UpdateCheckTask(); checkTask = new UpdateCheckTask();
checkTask.Init(this); checkTask.RunAsync(this);
if (!fetcher.AllResourcesExist) { if (!fetcher.AllResourcesExist) {
SetScreen(new ResourcesScreen(this)); SetScreen(new ResourcesScreen(this));

View File

@ -15,7 +15,7 @@ namespace Launcher.Patcher {
public List<string> FilesToDownload = new List<string>(); public List<string> FilesToDownload = new List<string>();
public void QueueItem(string url, string identifier) { public void QueueItem(string url, string identifier) {
downloader.DownloadData(url, false, identifier); downloader.AsyncGetData(url, false, identifier);
FilesToDownload.Add(identifier); FilesToDownload.Add(identifier);
} }
@ -63,7 +63,7 @@ namespace Launcher.Patcher {
} }
public void AddDownload(string url, string identifier) { public void AddDownload(string url, string identifier) {
downloader.DownloadData(url, false, identifier); downloader.AsyncGetData(url, false, identifier);
} }

View File

@ -4,21 +4,21 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using ClassicalSharp; using ClassicalSharp;
using ClassicalSharp.Textures; using ClassicalSharp.Textures;
using Launcher.Web;
namespace Launcher.Updater { namespace Launcher.Updater {
public static class Applier { public static class Applier {
public static DateTime PatchTime; public static DateTime PatchTime;
public const string UpdatesUri = "http://cs.classicube.net/";
public static void FetchUpdate(string dir) { public static void FetchUpdate(string dir) {
WebRequest.DefaultWebProxy = null; WebRequest.DefaultWebProxy = null;
// TODO: Rewrite to be async
using (WebClient client = new WebClient()) { using (WebClient client = new WebClient()) {
byte[] zipData = client.DownloadData(UpdateCheckTask.UpdatesUri + dir); byte[] zipData = client.DownloadData(UpdatesUri + dir);
MakeUpdatesFolder(zipData); MakeUpdatesFolder(zipData);
} }
} }

View File

@ -1,161 +0,0 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using JsonObject = System.Collections.Generic.Dictionary<string, object>;
namespace Launcher.Web {
public class ServerListEntry {
public string Hash, Name, Players, MaxPlayers;
public string Uptime, IPAddress, Port, Mppass, Software;
public bool Featured;
}
public sealed class ClassicubeSession : IWebTask {
const string classicubeNetUri = "https://www.classicube.net/",
loginUri = "https://www.classicube.net/api/login/",
listUri = "https://www.classicube.net/api/servers",
serverUri = "https://www.classicube.net/api/server/";
public List<ServerListEntry> Servers = new List<ServerListEntry>();
public void LoginAsync(string user, string password) {
Username = user;
Status = "&eSigning in..";
Servers = new List<ServerListEntry>();
BeginWorking();
Thread thread = new Thread(LoginWorker, 256 * 1024);
thread.Name = "Launcher.CCLoginAsync";
thread.Start(password);
}
public void FetchServersAsync() {
BeginWorking();
Thread thread = new Thread(FetchServersWorker, 256 * 1024);
thread.Name = "Launcher.CCFetchAsync";
thread.Start();
}
void LoginWorker(object password) {
// Sign in to classicube.net
try {
Login(Username, (string)password);
} catch (WebException ex) {
Finish(false, ex, "sign in"); return;
} catch (InvalidOperationException ex) {
Finish(false, null, "&c" + ex.Message); return;
}
FetchServersWorker();
if (!Success) Servers = new List<ServerListEntry>();
}
void FetchServersWorker() {
// Retrieve list of public servers
Status = "&eRetrieving public servers list..";
try {
Servers = GetPublicServers();
} catch (WebException ex) {
Finish(false, ex, "retrieving servers list"); return;
}
Finish(true, null, "&eFetched list");
}
void Login(string user, string password) {
Username = user;
// Step 1: GET csrf token from login page.
DateTime start = DateTime.UtcNow;
string getResponse = Get(loginUri, classicubeNetUri);
int index = 0; bool success = true;
JsonObject data = (JsonObject)Json.ParseValue(getResponse, ref index, ref success);
string token = (string)data["token"];
DateTime end = DateTime.UtcNow;
Log("cc login took " + (end - start).TotalMilliseconds);
// Step 2: POST to login page with csrf token.
string loginData = String.Format(
"username={0}&password={1}&token={2}",
Uri.EscapeDataString(user),
Uri.EscapeDataString(password),
Uri.EscapeDataString(token)
);
start = DateTime.UtcNow;
string response = Post(loginUri, loginUri, loginData);
index = 0; success = true;
data = (JsonObject)Json.ParseValue(response, ref index, ref success);
end = DateTime.UtcNow;
Log("cc login took " + (end - start).TotalMilliseconds);
string err = GetLoginError(data);
if (err != null) throw new InvalidOperationException(err);
Username = (string)data["username"];
}
static string GetLoginError(JsonObject obj) {
List<object> errors = (List<object>)obj["errors"];
if (errors.Count == 0) return null;
string err = (string)errors[0];
if (err == "username" || err == "password") return "Wrong username or password";
if (err == "verification") return "Account verification required";
return "Unknown error occurred";
}
ServerListEntry ParseEntry(JsonObject obj) {
ServerListEntry entry = new ServerListEntry();
entry.Hash = (string)obj["hash"];
entry.Name = (string)obj["name"];
entry.Players = (string)obj["players"];
entry.MaxPlayers = (string)obj["maxplayers"];
entry.Uptime = (string)obj["uptime"];
entry.Mppass = (string)obj["mppass"];
entry.IPAddress = (string)obj["ip"];
entry.Port = (string)obj["port"];
entry.Software = (string)obj["software"];
if (obj.ContainsKey("featured")) {
entry.Featured = (bool)obj["featured"];
}
return entry;
}
public ClientStartData GetConnectInfo(string hash) {
string uri = serverUri + hash;
string response = Get(uri, classicubeNetUri);
int index = 0; bool success = true;
JsonObject root = (JsonObject)Json.ParseValue(response, ref index, ref success);
List<object> list = (List<object>)root["servers"];
JsonObject obj = (JsonObject)list[0];
ServerListEntry entry = ParseEntry(obj);
return new ClientStartData(Username, entry.Mppass, entry.IPAddress, entry.Port, entry.Name);
}
public List<ServerListEntry> GetPublicServers() {
DateTime start = DateTime.UtcNow;
List<ServerListEntry> servers = new List<ServerListEntry>();
string response = Get(listUri, classicubeNetUri);
int index = 0; bool success = true;
JsonObject root = (JsonObject)Json.ParseValue(response, ref index, ref success);
List<object> list = (List<object>)root["servers"];
for (int i = 0; i < list.Count; i++) {
JsonObject obj = (JsonObject)list[i];
ServerListEntry entry = ParseEntry(obj);
servers.Add(entry);
}
DateTime end = DateTime.UtcNow;
Log("cc servers took " + (end - start).TotalMilliseconds);
return servers;
}
}
}

View File

@ -1,99 +0,0 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using ClassicalSharp;
namespace Launcher.Web {
/// <summary> Represents a task that performs a series of GET or POST requests asynchronously. </summary>
public abstract class IWebTask {
public virtual void ResetSession() {
Username = null;
cookies = new CookieContainer();
}
/// <summary> Whether this web task is still performing GET or POST requests asynchronously. </summary>
public bool Working;
/// <summary> Whether this web task has finished. </summary>
public bool Done;
/// <summary> Whether the web task finished with an error or not. </summary>
public bool Success;
/// <summary> Handled exception that was generated by the last GET or POST request. </summary>
public WebException Exception;
/// <summary> Current status of this web task (e.g. downloading page X) </summary>
public string Status;
/// <summary> Username used when performing GET or POST requests, can be left null. </summary>
public string Username;
public void BeginWorking() {
Working = true;
Done = false;
Exception = null;
}
protected void Finish(bool success, WebException ex, string status) {
if (!success)
Username = null;
Working = false;
Done = true;
Success = success;
Exception = ex;
Status = status;
}
protected CookieContainer cookies = new CookieContainer();
protected HttpWebResponse MakeRequest(string uri, string referer, string data) {
WebRequest.DefaultWebProxy = null;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.UserAgent = Program.AppName;
request.ReadWriteTimeout = 90 * 1000;
request.Timeout = 90 * 1000;
request.Referer = referer;
request.KeepAlive = true;
request.CookieContainer = cookies;
request.AutomaticDecompression = DecompressionMethods.GZip;
if (data != null) {
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8;";
byte[] encodedData = Encoding.UTF8.GetBytes(data);
request.ContentLength = encodedData.Length;
using (Stream stream = request.GetRequestStream()) {
stream.Write(encodedData, 0, encodedData.Length);
}
}
return (HttpWebResponse)request.GetResponse();
}
protected string Get(string uri, string referer) {
HttpWebResponse response = MakeRequest(uri, referer, null);
return GetResponse(response);
}
protected string Post(string uri, string referer, string data) {
HttpWebResponse response = MakeRequest(uri, referer, data);
return GetResponse(response);
}
protected string GetResponse(HttpWebResponse response) {
using (Stream stream = response.GetResponseStream()) {
using (StreamReader reader = new StreamReader(stream)) {
return reader.ReadToEnd();
}
}
}
protected static void Log(string text) { Utils.LogDebug(text); }
}
}

View File

@ -1,79 +0,0 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using ClassicalSharp;
using ClassicalSharp.Network;
using System;
using System.Collections.Generic;
using System.Globalization;
using JsonObject = System.Collections.Generic.Dictionary<string, object>;
namespace Launcher.Web {
public class Build {
public DateTime TimeBuilt;
public string DirectXPath, OpenGLPath;
public int DirectXSize, OpenGLSize;
public string Version;
}
public sealed class UpdateCheckTask {
public const string UpdatesIdentifier = "cc-update";
public const string UpdatesUri = "http://cs.classicube.net/";
public const string BuildsUri = "http://cs.classicube.net/builds.json";
public Build LatestDev, LatestStable;
public LauncherWindow Game;
public bool Completed = false, Success = false;
public void Init(LauncherWindow game) {
Game = game;
Completed = false;
Success = false;
Game.Downloader.DownloadPage(BuildsUri, false, UpdatesIdentifier);
}
public void Tick() {
if (Completed) return;
Request req;
if (!Game.Downloader.TryGetItem(UpdatesIdentifier, out req)) return;
Completed = true;
Success = req != null && req.Data != null;
if (!Success) return;
ProcessUpdate((string)req.Data);
}
void ProcessUpdate(string response) {
int index = 0; bool success = true;
JsonObject data = (JsonObject)Json.ParseValue(response, ref index, ref success);
JsonObject devBuild = (JsonObject)data["latest"];
JsonObject releaseBuilds = (JsonObject)data["releases"];
LatestDev = MakeBuild(devBuild, false);
DateTime releaseTime = DateTime.MinValue;
foreach (KeyValuePair<string, object> pair in releaseBuilds) {
Build build = MakeBuild((JsonObject)pair.Value, true);
if (build.TimeBuilt < releaseTime) continue;
LatestStable = build;
releaseTime = build.TimeBuilt;
}
}
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], CultureInfo.InvariantCulture);
build.TimeBuilt = epoch.AddSeconds(rawTime).ToLocalTime();
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"];
if (obj.ContainsKey("version"))
build.Version = (string)obj["version"];
return build;
}
}
}

219
Launcher2/WebTasks.cs Normal file
View File

@ -0,0 +1,219 @@
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using ClassicalSharp;
using ClassicalSharp.Network;
using System.Collections.Generic;
using System.Globalization;
using System;
using System.Net;
using JsonObject = System.Collections.Generic.Dictionary<string, object>;
namespace Launcher.Web {
public abstract class WebTask {
public LauncherWindow Game;
public bool Completed = false, Success = false;
public WebException WebEx;
DateTime start;
protected string identifier, uri, section;
public void RunAsync(LauncherWindow game) {
Game = game; Completed = false; Success = false;
start = DateTime.UtcNow;
Init();
Begin();
}
public void Tick() {
if (Completed) return;
Request req;
if (!Game.Downloader.TryGetItem(identifier, out req)) return;
Utils.LogDebug(identifier + " took " + (DateTime.UtcNow - start).TotalMilliseconds);
WebEx = req.WebEx;
Completed = true;
Success = req != null && req.Data != null;
if (Success) Handle(req);
}
protected abstract void Init();
protected virtual void Begin() {
Game.Downloader.AsyncGetString(uri, false, identifier);
}
protected abstract void Handle(Request req);
}
public sealed class GetCSRFTokenTask : WebTask {
public GetCSRFTokenTask() {
identifier = "CC get login";
uri = "https://www.classicube.net/api/login/";
}
public string Token;
protected override void Init() { }
protected override void Handle(Request req) {
int index = 0; bool success = true;
JsonObject data = (JsonObject)Json.ParseValue((string)req.Data, ref index, ref success);
Token = (string)data["token"];
}
}
public sealed class SignInTask : WebTask {
public SignInTask() {
identifier = "CC post login";
uri = "https://www.classicube.net/api/login/";
}
public string Username, Password, Token, Error;
protected override void Init() { }
protected override void Begin() {
string data = String.Format(
"username={0}&password={1}&token={2}",
Uri.EscapeDataString(Username),
Uri.EscapeDataString(Password),
Uri.EscapeDataString(Token)
);
Game.Downloader.AsyncPostString(uri, false, identifier, data);
}
protected override void Handle(Request req) {
int index = 0; bool success = true;
JsonObject data = (JsonObject)Json.ParseValue((string)req.Data, ref index, ref success);
Error = GetLoginError(data);
Username = (string)data["username"];
}
static string GetLoginError(JsonObject obj) {
List<object> errors = (List<object>)obj["errors"];
if (errors.Count == 0) return null;
string err = (string)errors[0];
if (err == "username" || err == "password") return "Wrong username or password";
if (err == "verification") return "Account verification required";
return "Unknown error occurred";
}
}
public class ServerListEntry {
public string Hash, Name, Players, MaxPlayers;
public string Uptime, IPAddress, Port, Mppass, Software;
public bool Featured;
}
public sealed class FetchServerTask : WebTask {
public FetchServerTask(string user, string hash) {
Username = user;
identifier = "CC get servers";
uri = "https://www.classicube.net/api/server/" + hash;
}
public static ServerListEntry ParseEntry(JsonObject obj) {
ServerListEntry entry = new ServerListEntry();
entry.Hash = (string)obj["hash"];
entry.Name = (string)obj["name"];
entry.Players = (string)obj["players"];
entry.MaxPlayers = (string)obj["maxplayers"];
entry.Uptime = (string)obj["uptime"];
entry.Mppass = (string)obj["mppass"];
entry.IPAddress = (string)obj["ip"];
entry.Port = (string)obj["port"];
entry.Software = (string)obj["software"];
if (obj.ContainsKey("featured")) {
entry.Featured = (bool)obj["featured"];
}
return entry;
}
public string Username;
public ClientStartData Info;
protected override void Init() { }
protected override void Handle(Request req) {
int index = 0; bool success = true;
JsonObject root = (JsonObject)Json.ParseValue((string)req.Data, ref index, ref success);
List<object> list = (List<object>)root["servers"];
JsonObject obj = (JsonObject)list[0];
ServerListEntry entry = ParseEntry(obj);
Info = new ClientStartData(Username, entry.Mppass, entry.IPAddress, entry.Port, entry.Name);
}
}
public sealed class FetchServersTask : WebTask {
public FetchServersTask() {
identifier = "CC get servers";
uri = "https://www.classicube.net/api/servers";
}
public List<ServerListEntry> Servers;
protected override void Init() {
Servers = new List<ServerListEntry>();
}
protected override void Handle(Request req) {
int index = 0; bool success = true;
JsonObject root = (JsonObject)Json.ParseValue((string)req.Data, ref index, ref success);
List<object> list = (List<object>)root["servers"];
for (int i = 0; i < list.Count; i++) {
JsonObject obj = (JsonObject)list[i];
ServerListEntry entry = FetchServerTask.ParseEntry(obj);
Servers.Add(entry);
}
}
}
public class Build {
public DateTime TimeBuilt;
public string DirectXPath, OpenGLPath;
public int DirectXSize, OpenGLSize;
public string Version;
}
public sealed class UpdateCheckTask : WebTask {
public UpdateCheckTask() {
identifier = "CC update check";
uri = "http://cs.classicube.net/builds.json";
}
public Build LatestDev, LatestStable;
protected override void Init() { }
protected override void Handle(Request req) {
int index = 0; bool success = true;
JsonObject data = (JsonObject)Json.ParseValue((string)req.Data, ref index, ref success);
JsonObject latest = (JsonObject)data["latest"], releases = (JsonObject)data["releases"];
LatestDev = MakeBuild(latest, false);
DateTime releaseTime = DateTime.MinValue;
foreach (KeyValuePair<string, object> pair in releases) {
Build build = MakeBuild((JsonObject)pair.Value, true);
if (build.TimeBuilt < releaseTime) continue;
LatestStable = build;
releaseTime = build.TimeBuilt;
}
}
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], CultureInfo.InvariantCulture);
build.TimeBuilt = epoch.AddSeconds(rawTime).ToLocalTime();
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"];
if (obj.ContainsKey("version"))
build.Version = (string)obj["version"];
return build;
}
}
}

View File

@ -292,6 +292,7 @@ void Entities_ContextLost(void) {
if (Entities_List[i] == NULL) continue; if (Entities_List[i] == NULL) continue;
Entities_List[i]->VTABLE->ContextLost(Entities_List[i]); Entities_List[i]->VTABLE->ContextLost(Entities_List[i]);
} }
Gfx_DeleteTexture(&ShadowComponent_ShadowTex);
} }
void Entities_ContextRecreated(void) { void Entities_ContextRecreated(void) {

View File

@ -595,7 +595,7 @@ void ShadowComponent_MakeTex(void) {
row[x] = dist < half * half ? inPix : outPix; row[x] = dist < half * half ? inPix : outPix;
} }
} }
ShadowComponent_ShadowTex = Gfx_CreateTexture(&bmp, true, false); ShadowComponent_ShadowTex = Gfx_CreateTexture(&bmp, false, false);
} }
void ShadowComponent_Draw(Entity* entity) { void ShadowComponent_Draw(Entity* entity) {

View File

@ -155,7 +155,7 @@ void Gfx_BindTexture(GfxResourceID texId) {
} }
void Gfx_DeleteTexture(GfxResourceID* texId) { void Gfx_DeleteTexture(GfxResourceID* texId) {
if (*texId <= 0) return; if (*texId == NULL) return;
glDeleteTextures(1, texId); glDeleteTextures(1, texId);
*texId = NULL; *texId = NULL;
} }
@ -312,7 +312,7 @@ void Gfx_BindIb(GfxResourceID ib) {
} }
void Gfx_DeleteVb(GfxResourceID* vb) { void Gfx_DeleteVb(GfxResourceID* vb) {
if (*vb <= 0) return; if (*vb == NULL) return;
if (gl_lists) { if (gl_lists) {
if (*vb != gl_DYNAMICLISTID) glDeleteLists(*vb, 1); if (*vb != gl_DYNAMICLISTID) glDeleteLists(*vb, 1);
@ -323,7 +323,7 @@ void Gfx_DeleteVb(GfxResourceID* vb) {
} }
void Gfx_DeleteIb(GfxResourceID* ib) { void Gfx_DeleteIb(GfxResourceID* ib) {
if (gl_lists || *ib <= 0) return; if (gl_lists || *ib == NULL) return;
glDeleteBuffers(1, ib); glDeleteBuffers(1, ib);
*ib = NULL; *ib = NULL;
} }