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;
StringBuffer lastDownload = new StringBuffer(48);
void CheckOtherStatuses() {
Request item = game.AsyncDownloader.CurrentItem;
Request item = game.Downloader.CurrentItem;
if (item == null || !(item.Identifier == "terrain" || item.Identifier == "texturePack")) {
if (status.Textures[1].IsValid) status.SetText(1, null);
lastDownloadStatus = int.MinValue;
return;
}
int progress = game.AsyncDownloader.CurrentItemProgress;
int progress = game.Downloader.CurrentItemProgress;
if (progress == lastDownloadStatus) return;
lastDownloadStatus = progress;
SetFetchStatus(progress);

View File

@ -221,7 +221,7 @@ namespace ClassicalSharp.Entities {
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;
List[i].ContextLost();
}
game.Graphics.DeleteTexture(ref ShadowComponent.shadowTex);
}
void ContextRecreated() {

View File

@ -112,7 +112,7 @@ namespace ClassicalSharp.Entities {
if (!fetchedSkin && Model.UsesSkin) {
Player first = FirstOtherWithSameSkinAndFetchedSkin();
if (first == null) {
game.AsyncDownloader.DownloadSkin(SkinName, SkinName);
game.Downloader.AsyncGetSkin(SkinName, SkinName);
} else {
ApplySkin(first);
}
@ -120,7 +120,7 @@ namespace ClassicalSharp.Entities {
}
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; }
Bitmap bmp = (Bitmap)item.Data;

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@ namespace ClassicalSharp.Network.Protocols {
public override void Tick() {
Request item;
game.AsyncDownloader.TryGetItem(womEnvIdentifier, out item);
game.Downloader.TryGetItem(womEnvIdentifier, out item);
if (item != null && item.Data != null) {
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.
womCounter++;
womEnvIdentifier = "womenv_" + womCounter;
game.AsyncDownloader.DownloadPage(url, true, womEnvIdentifier);
game.Downloader.AsyncGetString(url, true, womEnvIdentifier);
sendWomId = true;
}

View File

@ -33,6 +33,7 @@ namespace ClassicalSharp.Network {
public int CurrentItemProgress = -3;
public IDrawer2D Drawer;
public CookieContainer Cookies;
public bool KeepAlive;
public AsyncDownloader(IDrawer2D drawer) { this.drawer = drawer; }
#if !LAUNCHER
@ -63,59 +64,73 @@ namespace ClassicalSharp.Network {
/// <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>
/// <remarks> Identifier is skin_'skinName'.</remarks>
public void DownloadSkin(string identifier, string skinName) {
string strippedSkinName = Utils.StripColours(skinName);
string url = Utils.IsUrlPrefix(skinName, 0) ? skinName :
skinServer + strippedSkinName + ".png";
public void AsyncGetSkin(string identifier, string skinName) {
string url = Utils.IsUrlPrefix(skinName, 0) ? skinName
: skinServer + Utils.StripColours(skinName) + ".png";
AddRequest(url, false, identifier, RequestType.Bitmap,
DateTime.MinValue , null);
DateTime.MinValue, null, null);
}
#endif
/// <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,
DateTime.MinValue, null);
DateTime.MinValue, null, null);
}
/// <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,
DateTime.MinValue, null);
DateTime.MinValue, null, null);
}
/// <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,
DateTime.MinValue, null);
DateTime.MinValue, null, null);
}
/// <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) {
AddRequest(url, priority, identifier, RequestType.Bitmap,
lastModified, etag);
lastModified, etag, null);
}
/// <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) {
AddRequest(url, priority, identifier, RequestType.ByteArray,
lastModified, etag);
lastModified, etag, null);
}
#if !LAUNCHER
/// <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,
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
void AddRequest(string url, bool priority, string identifier,
RequestType type, DateTime lastModified, string etag) {
RequestType type, DateTime lastModified, string etag, object data) {
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) {
pending.Insert(0, request);
} else {
@ -208,7 +223,6 @@ namespace ClassicalSharp.Network {
string url = request.Url;
Utils.LogDebug("Downloading {0} from: {1}", request.Type, url);
HttpStatusCode status = HttpStatusCode.OK;
request.Data = null;
try {
HttpWebRequest req = MakeRequest(request);
@ -228,6 +242,7 @@ namespace ClassicalSharp.Network {
status = ((HttpWebResponse)webEx.Response).StatusCode;
webEx.Response.Close();
}
request.WebEx = webEx;
}
if (status != HttpStatusCode.OK) {
@ -285,19 +300,31 @@ namespace ClassicalSharp.Network {
req.Proxy = null;
req.UserAgent = Program.AppName;
req.CookieContainer = Cookies;
req.KeepAlive = KeepAlive;
if (request.LastModified != DateTime.MinValue)
if (request.LastModified != DateTime.MinValue) {
req.IfModifiedSince = request.LastModified;
if (request.ETag != null)
}
if (request.ETag != null) {
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;
}
static byte[] buffer = new byte[4096 * 8];
MemoryStream DownloadBytes(HttpWebResponse response) {
int length = (int)response.ContentLength;
MemoryStream dst = length > 0 ?
new MemoryStream(length) : new MemoryStream();
MemoryStream dst = length > 0 ? new MemoryStream(length) : new MemoryStream();
CurrentItemProgress = length > 0 ? 0 : -1;
using (Stream src = response.GetResponseStream()) {
@ -335,14 +362,7 @@ namespace ClassicalSharp.Network {
/// <summary> ETag of the item most recently cached. (if any) </summary>
public string ETag;
public Request(string url, string identifier, RequestType type, DateTime lastModified, string etag) {
Url = url;
Identifier = identifier;
Type = type;
TimeAdded = DateTime.UtcNow;
LastModified = lastModified;
ETag = etag;
}
public WebException WebEx;
public void Dispose() {
Bitmap bmp = Data as Bitmap;

View File

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

View File

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

View File

@ -2,6 +2,7 @@
using System;
using System.Drawing;
using ClassicalSharp;
using Launcher.Web;
using Launcher.Gui.Views;
using Launcher.Gui.Widgets;
using OpenTK.Input;
@ -11,6 +12,7 @@ namespace Launcher.Gui.Screens {
const int tableX = 10, tableY = 50;
ServersView view;
FetchServersTask fetchTask;
public ServersScreen(LauncherWindow game) : base(game) {
enterIndex = 3;
@ -20,7 +22,7 @@ namespace Launcher.Gui.Screens {
public override void Tick() {
base.Tick();
if (fetchingList) CheckFetchStatus();
if (fetchTask != null) CheckFetchStatus();
TableWidget table = (TableWidget)widgets[view.tableIndex];
if (!game.Window.Mouse[MouseButton.Left]) {
@ -134,12 +136,10 @@ namespace Launcher.Gui.Screens {
game.ConnectToServer(table.servers, Get(view.hashIndex));
}
bool fetchingList = false;
void RefreshList(int mouseX, int mouseY) {
if (fetchingList) return;
fetchingList = true;
game.Session.FetchServersAsync();
if (fetchTask != null) return;
fetchTask = new FetchServersTask();
fetchTask.RunAsync(game);
view.RefreshText = "&eWorking..";
Resize();
}
@ -187,10 +187,12 @@ namespace Launcher.Gui.Screens {
}
void CheckFetchStatus() {
if (!game.Session.Done) return;
fetchingList = false;
view.RefreshText = game.Session.Exception == null ? "Refresh" : "&cFailed";
fetchTask.Tick();
if (!fetchTask.Completed) return;
if (fetchTask.Success) game.Servers = fetchTask.Servers;
view.RefreshText = fetchTask.Success ? "Refresh" : "&cFailed";
fetchTask = null;
Resize();
// 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);
}
checkTask = new UpdateCheckTask();
checkTask.Init(game);
checkTask.RunAsync(game);
}
Build dev, stable;

View File

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

View File

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

View File

@ -37,7 +37,7 @@ namespace Launcher {
public Rectangle DirtyArea;
/// <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>
public AsyncDownloader Downloader;
@ -55,6 +55,8 @@ namespace Launcher {
public bool ShouldExit;
public bool ShouldUpdate;
public List<ServerListEntry> Servers = new List<ServerListEntry>();
public string FontName = "Arial";
public bool Minimised {
@ -136,7 +138,7 @@ namespace Launcher {
ServerListEntry entry = publicServers[i];
if (entry.Hash != hash) continue;
data = new ClientStartData(Session.Username, entry.Mppass,
data = new ClientStartData(Username, entry.Mppass,
entry.IPAddress, entry.Port, entry.Name);
Client.Start(data, true, ref ShouldExit);
return true;
@ -144,7 +146,14 @@ namespace Launcher {
// Fallback to private server handling
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) {
ErrorHandler.LogError("retrieving server information", ex);
return false;
@ -165,12 +174,15 @@ namespace Launcher {
platformDrawer.info = Window.WindowInfo;
platformDrawer.Init();
fetcher = new ResourceFetcher();
fetcher.CheckResourceExistence();
Downloader = new AsyncDownloader(Drawer);
Downloader.Init("");
Downloader.Cookies = new CookieContainer();
Downloader.KeepAlive = true;
fetcher = new ResourceFetcher();
fetcher.CheckResourceExistence();
checkTask = new UpdateCheckTask();
checkTask.Init(this);
checkTask.RunAsync(this);
if (!fetcher.AllResourcesExist) {
SetScreen(new ResourcesScreen(this));

View File

@ -15,7 +15,7 @@ namespace Launcher.Patcher {
public List<string> FilesToDownload = new List<string>();
public void QueueItem(string url, string identifier) {
downloader.DownloadData(url, false, identifier);
downloader.AsyncGetData(url, false, identifier);
FilesToDownload.Add(identifier);
}
@ -63,7 +63,7 @@ namespace Launcher.Patcher {
}
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.Net;
using System.Runtime.InteropServices;
using System.Threading;
using ClassicalSharp;
using ClassicalSharp.Textures;
using Launcher.Web;
namespace Launcher.Updater {
public static class Applier {
public static DateTime PatchTime;
public const string UpdatesUri = "http://cs.classicube.net/";
public static void FetchUpdate(string dir) {
WebRequest.DefaultWebProxy = null;
// TODO: Rewrite to be async
using (WebClient client = new WebClient()) {
byte[] zipData = client.DownloadData(UpdateCheckTask.UpdatesUri + dir);
byte[] zipData = client.DownloadData(UpdatesUri + dir);
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;
Entities_List[i]->VTABLE->ContextLost(Entities_List[i]);
}
Gfx_DeleteTexture(&ShadowComponent_ShadowTex);
}
void Entities_ContextRecreated(void) {

View File

@ -595,7 +595,7 @@ void ShadowComponent_MakeTex(void) {
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) {

View File

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