From 2b3fb48f543be3471e71c4cb43a03717f284e925 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Tue, 16 Jan 2018 23:09:44 +1100 Subject: [PATCH] Rewrite launcher web tasks to use asyncdownloader --- ClassicalSharp/2D/Screens/ChatScreen.cs | 4 +- .../Entities/Components/ShadowComponent.cs | 2 +- ClassicalSharp/Entities/EntityList.cs | 1 + ClassicalSharp/Entities/Player.cs | 4 +- ClassicalSharp/Game/Game.Init.cs | 4 +- ClassicalSharp/Game/Game.Properties.cs | 2 +- ClassicalSharp/Network/IServerConnection.cs | 14 +- ClassicalSharp/Network/Protocols/WoM.cs | 4 +- .../Network/Utils/AsyncDownloader.cs | 84 ++++--- ClassicalSharp/Singleplayer/Server.cs | 3 +- .../Gui/Screens/MainScreen.Classicube.cs | 97 ++++++-- Launcher2/Gui/Screens/ServersScreen.cs | 22 +- Launcher2/Gui/Screens/UpdatesScreen.cs | 2 +- Launcher2/Gui/Views/ServersView.cs | 4 +- Launcher2/Launcher2.csproj | 5 +- Launcher2/LauncherWindow.cs | 24 +- Launcher2/Patcher/ResourceFetcher.cs | 4 +- Launcher2/Updater/Applier.cs | 6 +- Launcher2/WebService/ClassiCubeSession.cs | 161 ------------- Launcher2/WebService/IWebTask.cs | 99 -------- Launcher2/WebService/UpdateCheckTask.cs | 79 ------- Launcher2/WebTasks.cs | 219 ++++++++++++++++++ src/Client/Entity.c | 1 + src/Client/EntityComponents.c | 2 +- src/Client/OpenGLApi.c | 6 +- 25 files changed, 407 insertions(+), 446 deletions(-) delete mode 100644 Launcher2/WebService/ClassiCubeSession.cs delete mode 100644 Launcher2/WebService/IWebTask.cs delete mode 100644 Launcher2/WebService/UpdateCheckTask.cs create mode 100644 Launcher2/WebTasks.cs diff --git a/ClassicalSharp/2D/Screens/ChatScreen.cs b/ClassicalSharp/2D/Screens/ChatScreen.cs index 05bf7cf51..f8fcf34ad 100644 --- a/ClassicalSharp/2D/Screens/ChatScreen.cs +++ b/ClassicalSharp/2D/Screens/ChatScreen.cs @@ -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); diff --git a/ClassicalSharp/Entities/Components/ShadowComponent.cs b/ClassicalSharp/Entities/Components/ShadowComponent.cs index 1ac3b711c..c4d0ef751 100644 --- a/ClassicalSharp/Entities/Components/ShadowComponent.cs +++ b/ClassicalSharp/Entities/Components/ShadowComponent.cs @@ -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); } } } diff --git a/ClassicalSharp/Entities/EntityList.cs b/ClassicalSharp/Entities/EntityList.cs index 5b762da74..21820a4dc 100644 --- a/ClassicalSharp/Entities/EntityList.cs +++ b/ClassicalSharp/Entities/EntityList.cs @@ -111,6 +111,7 @@ namespace ClassicalSharp.Entities { if (List[i] == null) continue; List[i].ContextLost(); } + game.Graphics.DeleteTexture(ref ShadowComponent.shadowTex); } void ContextRecreated() { diff --git a/ClassicalSharp/Entities/Player.cs b/ClassicalSharp/Entities/Player.cs index 03573ad8e..875969a80 100644 --- a/ClassicalSharp/Entities/Player.cs +++ b/ClassicalSharp/Entities/Player.cs @@ -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; diff --git a/ClassicalSharp/Game/Game.Init.cs b/ClassicalSharp/Game/Game.Init.cs index 1b6dd707f..3f1044c32 100644 --- a/ClassicalSharp/Game/Game.Init.cs +++ b/ClassicalSharp/Game/Game.Init.cs @@ -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); diff --git a/ClassicalSharp/Game/Game.Properties.cs b/ClassicalSharp/Game/Game.Properties.cs index 7d70a7414..af79aa4f9 100644 --- a/ClassicalSharp/Game/Game.Properties.cs +++ b/ClassicalSharp/Game/Game.Properties.cs @@ -164,7 +164,7 @@ namespace ClassicalSharp { public int Vertices; public FrustumCulling Culling; - public AsyncDownloader AsyncDownloader; + public AsyncDownloader Downloader; /// How sensitive the client is to changes in the player's mouse position. public int MouseSensitivity = 30; diff --git a/ClassicalSharp/Network/IServerConnection.cs b/ClassicalSharp/Network/IServerConnection.cs index 6db5371fa..d18d3b582 100644 --- a/ClassicalSharp/Network/IServerConnection.cs +++ b/ClassicalSharp/Network/IServerConnection.cs @@ -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); } } diff --git a/ClassicalSharp/Network/Protocols/WoM.cs b/ClassicalSharp/Network/Protocols/WoM.cs index 9d44e6e9b..0786b83f2 100644 --- a/ClassicalSharp/Network/Protocols/WoM.cs +++ b/ClassicalSharp/Network/Protocols/WoM.cs @@ -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; } diff --git a/ClassicalSharp/Network/Utils/AsyncDownloader.cs b/ClassicalSharp/Network/Utils/AsyncDownloader.cs index 67a8f0b5e..74d92cc13 100644 --- a/ClassicalSharp/Network/Utils/AsyncDownloader.cs +++ b/ClassicalSharp/Network/Utils/AsyncDownloader.cs @@ -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 { /// 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 /// Identifier is skin_'skinName'. - 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 /// Asynchronously downloads a bitmap image from the specified url. - 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); } /// Asynchronously downloads a string from the specified url. - 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); } /// Asynchronously downloads a byte array. - 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); } /// Asynchronously downloads a bitmap image. - 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); } /// Asynchronously downloads a byte array. - 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 /// Asynchronously retrieves the content length of the body response. - 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 + /// Asynchronously retrieves the content length of the body response. + 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 { /// ETag of the item most recently cached. (if any) 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; diff --git a/ClassicalSharp/Singleplayer/Server.cs b/ClassicalSharp/Singleplayer/Server.cs index e82c38a13..8f4705516 100644 --- a/ClassicalSharp/Singleplayer/Server.cs +++ b/ClassicalSharp/Singleplayer/Server.cs @@ -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(); diff --git a/Launcher2/Gui/Screens/MainScreen.Classicube.cs b/Launcher2/Gui/Screens/MainScreen.Classicube.cs index c9b7e8e49..97547d87a 100644 --- a/Launcher2/Gui/Screens/MainScreen.Classicube.cs +++ b/Launcher2/Gui/Screens/MainScreen.Classicube.cs @@ -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)); diff --git a/Launcher2/Gui/Screens/ServersScreen.cs b/Launcher2/Gui/Screens/ServersScreen.cs index b129c3725..7d7aa2914 100644 --- a/Launcher2/Gui/Screens/ServersScreen.cs +++ b/Launcher2/Gui/Screens/ServersScreen.cs @@ -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 diff --git a/Launcher2/Gui/Screens/UpdatesScreen.cs b/Launcher2/Gui/Screens/UpdatesScreen.cs index 6bd3dc240..aa53c69e7 100644 --- a/Launcher2/Gui/Screens/UpdatesScreen.cs +++ b/Launcher2/Gui/Screens/UpdatesScreen.cs @@ -34,7 +34,7 @@ namespace Launcher.Gui.Screens { SuccessfulUpdateCheck(game.checkTask); } checkTask = new UpdateCheckTask(); - checkTask.Init(game); + checkTask.RunAsync(game); } Build dev, stable; diff --git a/Launcher2/Gui/Views/ServersView.cs b/Launcher2/Gui/Views/ServersView.cs index 0d4f0e7ec..2b8492afe 100644 --- a/Launcher2/Gui/Views/ServersView.cs +++ b/Launcher2/Gui/Views/ServersView.cs @@ -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(); diff --git a/Launcher2/Launcher2.csproj b/Launcher2/Launcher2.csproj index ac1bc004e..44f6a5e0a 100644 --- a/Launcher2/Launcher2.csproj +++ b/Launcher2/Launcher2.csproj @@ -145,15 +145,13 @@ + - - - @@ -173,7 +171,6 @@ - \ No newline at end of file diff --git a/Launcher2/LauncherWindow.cs b/Launcher2/LauncherWindow.cs index e7aa81a96..68cb8ae9d 100644 --- a/Launcher2/LauncherWindow.cs +++ b/Launcher2/LauncherWindow.cs @@ -37,7 +37,7 @@ namespace Launcher { public Rectangle DirtyArea; /// Currently active logged in session with classicube.net. - public ClassicubeSession Session = new ClassicubeSession(); + public string Username; /// Queue used to download resources asynchronously. public AsyncDownloader Downloader; @@ -55,6 +55,8 @@ namespace Launcher { public bool ShouldExit; public bool ShouldUpdate; + public List Servers = new List(); + 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)); diff --git a/Launcher2/Patcher/ResourceFetcher.cs b/Launcher2/Patcher/ResourceFetcher.cs index bd5b0966c..cd82f6123 100644 --- a/Launcher2/Patcher/ResourceFetcher.cs +++ b/Launcher2/Patcher/ResourceFetcher.cs @@ -15,7 +15,7 @@ namespace Launcher.Patcher { public List FilesToDownload = new List(); 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); } diff --git a/Launcher2/Updater/Applier.cs b/Launcher2/Updater/Applier.cs index 9aedbb7bd..c9936de3d 100644 --- a/Launcher2/Updater/Applier.cs +++ b/Launcher2/Updater/Applier.cs @@ -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); } } diff --git a/Launcher2/WebService/ClassiCubeSession.cs b/Launcher2/WebService/ClassiCubeSession.cs deleted file mode 100644 index 9a0377f09..000000000 --- a/Launcher2/WebService/ClassiCubeSession.cs +++ /dev/null @@ -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; - -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 Servers = new List(); - - public void LoginAsync(string user, string password) { - Username = user; - Status = "&eSigning in.."; - Servers = new List(); - - 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(); - } - - 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 errors = (List)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 list = (List)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 GetPublicServers() { - DateTime start = DateTime.UtcNow; - List servers = new List(); - string response = Get(listUri, classicubeNetUri); - int index = 0; bool success = true; - JsonObject root = (JsonObject)Json.ParseValue(response, ref index, ref success); - List list = (List)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; - } - } -} \ No newline at end of file diff --git a/Launcher2/WebService/IWebTask.cs b/Launcher2/WebService/IWebTask.cs deleted file mode 100644 index bdeeb9896..000000000 --- a/Launcher2/WebService/IWebTask.cs +++ /dev/null @@ -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 { - - /// Represents a task that performs a series of GET or POST requests asynchronously. - public abstract class IWebTask { - - public virtual void ResetSession() { - Username = null; - cookies = new CookieContainer(); - } - - /// Whether this web task is still performing GET or POST requests asynchronously. - public bool Working; - - /// Whether this web task has finished. - public bool Done; - - /// Whether the web task finished with an error or not. - public bool Success; - - /// Handled exception that was generated by the last GET or POST request. - public WebException Exception; - - /// Current status of this web task (e.g. downloading page X) - public string Status; - - /// Username used when performing GET or POST requests, can be left null. - 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); } - } -} diff --git a/Launcher2/WebService/UpdateCheckTask.cs b/Launcher2/WebService/UpdateCheckTask.cs deleted file mode 100644 index 848a6d987..000000000 --- a/Launcher2/WebService/UpdateCheckTask.cs +++ /dev/null @@ -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; - -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 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; - } - } -} \ No newline at end of file diff --git a/Launcher2/WebTasks.cs b/Launcher2/WebTasks.cs new file mode 100644 index 000000000..fbb3dc005 --- /dev/null +++ b/Launcher2/WebTasks.cs @@ -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; + +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 errors = (List)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 list = (List)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 Servers; + protected override void Init() { + Servers = new List(); + } + + 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 list = (List)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 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; + } + } +} \ No newline at end of file diff --git a/src/Client/Entity.c b/src/Client/Entity.c index c53b662b0..271552231 100644 --- a/src/Client/Entity.c +++ b/src/Client/Entity.c @@ -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) { diff --git a/src/Client/EntityComponents.c b/src/Client/EntityComponents.c index fc55ef8b3..2336667be 100644 --- a/src/Client/EntityComponents.c +++ b/src/Client/EntityComponents.c @@ -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) { diff --git a/src/Client/OpenGLApi.c b/src/Client/OpenGLApi.c index 2207fa414..53abb32c5 100644 --- a/src/Client/OpenGLApi.c +++ b/src/Client/OpenGLApi.c @@ -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; }