Skip to content

Commit 3074107

Browse files
committed
Async Checking for Updates
1 parent 9c7cc22 commit 3074107

5 files changed

Lines changed: 110 additions & 74 deletions

File tree

source/funkin/backend/system/github/GitHub.hx

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ import haxe.Exception;
1212
* Requires the `GITHUB_API` macro to be defined.
1313
* This has no authentication, so it's limited to unauthenticated requests, and rate limits.
1414
**/
15-
class GitHub {
15+
final class GitHub {
1616
/**
1717
* Gets all the releases from a specific GitHub repository using the GitHub API.
18-
* @param user The user/organization that owns the repository
19-
* @param repository The repository name
20-
* @param onError Error Callback
21-
* @return Releases
18+
* @param user The user/organization that owns the repository.
19+
* @param repository The repository name.
20+
* @param onError Error Callback.
21+
* @return Releases in Array.
2222
*/
2323
public static function getReleases(user:String, repository:String, ?onError:Exception->Void):Array<GitHubRelease> {
2424
#if GITHUB_API
@@ -38,17 +38,16 @@ class GitHub {
3838

3939
/**
4040
* Gets the contributors list from a specific GitHub repository using the GitHub API.
41-
* @param user The user/organization that owns the repository
42-
* @param repository The repository name
43-
* @param onError Error Callback
44-
* @return Contributors List
41+
* @param user The user/organization that owns the repository.
42+
* @param repository The repository name.
43+
* @param onError Error Callback.
44+
* @return Contributors List as Array.
4545
*/
4646
public static function getContributors(user:String, repository:String, ?onError:Exception->Void):Array<GitHubContributor> {
4747
#if GITHUB_API
4848
try {
4949
var data = Json.parse(HttpUtil.requestText('https://api.github.com/repos/${user}/${repository}/contributors'));
50-
if (!(data is Array))
51-
throw __parseGitHubException(data);
50+
if (!(data is Array)) throw __parseGitHubException(data);
5251

5352
return data;
5453
} catch(e) {
@@ -61,16 +60,15 @@ class GitHub {
6160

6261
/**
6362
* Gets a specific GitHub organization using the GitHub API.
64-
* @param org The organization to get
65-
* @param onError Error Callback
66-
* @return Organization
63+
* @param org The organization to get.
64+
* @param onError Error Callback.
65+
* @return Organization.
6766
*/
6867
public static function getOrganization(org:String, ?onError:Exception->Void):GitHubOrganization {
6968
#if GITHUB_API
7069
try {
7170
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org'));
72-
if (Reflect.hasField(data, "documentation_url"))
73-
throw __parseGitHubException(data);
71+
if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
7472

7573
return data;
7674
} catch(e) {
@@ -84,16 +82,15 @@ class GitHub {
8482
/**
8583
* Gets the members list from a specific GitHub organization using the GitHub API.
8684
* NOTE: Members use Contributors' structure!
87-
* @param org The organization to get the members from
88-
* @param onError Error Callback
89-
* @return Members List
85+
* @param org The organization to get the members from.
86+
* @param onError Error Callback.
87+
* @return Members List as Array
9088
*/
9189
public static function getOrganizationMembers(org:String, ?onError:Exception->Void):Array<GitHubContributor> {
9290
#if GITHUB_API
9391
try {
9492
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org/members'));
95-
if (Reflect.hasField(data, "documentation_url"))
96-
throw __parseGitHubException(data);
93+
if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
9794

9895
return data;
9996
} catch(e) {
@@ -107,18 +104,15 @@ class GitHub {
107104
/**
108105
* Gets a specific GitHub user/organization using the GitHub API.
109106
* NOTE: If organization, it will be returned with the structure of a normal user; use `getOrganization` if you specifically want an organization!
110-
* @param user The user/organization to get
111-
* @param onError Error Callback
112-
* @return User/Organization
107+
* @param user The user/organization to get.
108+
* @param onError Error Callback.
109+
* @return User/Organization.
113110
*/
114111
public static function getUser(user:String, ?onError:Exception->Void):GitHubUser {
115112
#if GITHUB_API
116113
try {
117-
var url = 'https://api.github.com/users/$user';
118-
119-
var data = Json.parse(HttpUtil.requestText(url));
120-
if (Reflect.hasField(data, "documentation_url"))
121-
throw __parseGitHubException(data);
114+
var data = Json.parse(HttpUtil.requestText('https://api.github.com/users/$user'));
115+
if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
122116

123117
return data;
124118
} catch(e) {

source/funkin/backend/system/updating/UpdateAvailableScreen.hx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ class UpdateAvailableScreen extends MusicBeatState {
2424

2525
public function new(check:UpdateCheckCallback) {
2626
super();
27+
2728
this.check = check;
29+
if (Date.now().getTime() - check.date.getTime() > 15000) {
30+
if ((check = UpdateUtil.checkForUpdates(true)).newUpdate) this.check = check;
31+
}
2832
}
2933

3034
public override function create() {

source/funkin/backend/system/updating/UpdateUtil.hx

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,97 @@ package funkin.backend.system.updating;
22

33
import funkin.backend.system.github.GitHub;
44
import funkin.backend.system.github.GitHubRelease;
5-
import haxe.io.Path;
5+
66
import lime.app.Application;
7+
8+
import sys.thread.Mutex;
9+
import sys.thread.Thread;
710
import sys.FileSystem;
811

12+
import haxe.io.Path;
13+
914
using funkin.backend.system.github.GitHub;
1015

1116
class UpdateUtil {
17+
public static var lastUpdateCheck:Null<UpdateCheckCallback>;
18+
19+
private static var __waitCallbacks:Array<UpdateCheckCallback->Void>;
20+
private static var __mutex:Mutex;
21+
1222
public static function init() {
1323
// deletes old bak file if it exists
1424
#if sys
1525
var bakPath = '${Path.withoutExtension(Sys.programPath())}.bak';
16-
if (FileSystem.exists(bakPath))
17-
FileSystem.deleteFile(bakPath);
26+
if (FileSystem.exists(bakPath)) FileSystem.deleteFile(bakPath);
1827
#end
28+
29+
__waitCallbacks = [];
30+
__mutex = new Mutex();
31+
Thread.create(checkForUpdates.bind(true, false));
32+
}
33+
34+
public static function waitForUpdates(force = false, callback:UpdateCheckCallback->Void, lazy = false) {
35+
if (__mutex.tryAcquire()) {
36+
__mutex.release();
37+
if (__shouldCheck(lazy) || force) {
38+
__waitCallbacks.push(callback);
39+
Thread.create(checkForUpdates.bind(force, false));
40+
}
41+
else
42+
callback(lastUpdateCheck);
43+
}
44+
else
45+
__waitCallbacks.push(callback);
1946
}
2047

21-
public static function checkForUpdates():UpdateCheckCallback {
22-
var curTag = 'v${Flags.VERSION}';
23-
trace("Current version: " + curTag);
48+
public static function checkForUpdates(force = false, lazy = false):UpdateCheckCallback {
49+
var wasAcquired = !__mutex.tryAcquire();
50+
if (wasAcquired) __mutex.acquire();
2451

25-
var error = false;
52+
if ((!force || wasAcquired) && !__shouldCheck(lazy)) {
53+
__mutex.release();
54+
return lastUpdateCheck;
55+
}
2656

27-
var newUpdates = __doReleaseFiltering(GitHub.getReleases(Flags.REPO_OWNER, Flags.REPO_NAME, function(e) {
57+
lastUpdateCheck = __checkForUpdates();
58+
__mutex.release();
59+
60+
FlxG.signals.preUpdate.addOnce(__callWaitCallbacks);
61+
62+
return lastUpdateCheck;
63+
}
64+
65+
static function __checkForUpdates():UpdateCheckCallback {
66+
var curTag = 'v${Flags.VERSION}', error = false;
67+
var newUpdates = __doReleaseFiltering(GitHub.getReleases(Flags.REPO_OWNER, Flags.REPO_NAME, (e) -> {
2868
error = true;
2969
}), curTag);
3070

31-
if (error) return {
32-
success: false,
33-
newUpdate: false
34-
};
35-
36-
if (newUpdates.length <= 0) {
37-
return {
38-
success: true,
39-
newUpdate: false
40-
};
71+
var updateCheck:UpdateCheckCallback = {
72+
success: !error,
73+
newUpdate: !error && newUpdates.length > 0,
74+
currentVersionTag: curTag,
75+
date: Date.now()
4176
}
4277

43-
return {
44-
success: true,
45-
newUpdate: true,
46-
currentVersionTag: curTag,
47-
newVersionTag: newUpdates.last().tag_name,
48-
updates: newUpdates
49-
};
78+
if (updateCheck.newUpdate) updateCheck.newVersionTag = (updateCheck.updates = newUpdates).last().tag_name;
79+
return updateCheck;
80+
}
81+
82+
static function __shouldCheck(lazy:Bool):Bool
83+
return lastUpdateCheck == null || !lazy && (!lastUpdateCheck.newUpdate || Date.now().getTime() - lastUpdateCheck.date.getTime() > 1800000);
84+
85+
static function __callWaitCallbacks() {
86+
for (callback in __waitCallbacks) callback(lastUpdateCheck);
87+
__waitCallbacks.resize(0);
5088
}
5189

52-
static var __curVersionPos = -2;
5390
static function __doReleaseFiltering(releases:Array<GitHubRelease>, currentVersionTag:String) {
5491
releases = releases.filterReleases(Options.betaUpdates, false);
5592
if (releases.length <= 0)
5693
return releases;
5794

58-
var newArray:Array<GitHubRelease> = [];
95+
var newArray:Array<GitHubRelease> = [], __curVersionPos = -2;
5996

6097
var skipNextBinaryChecks:Bool = false;
6198
for(index in 0...releases.length) {
@@ -73,12 +110,9 @@ class UpdateUtil {
73110
}
74111
if (containsBinary) {
75112
skipNextBinaryChecks = true; // no need to check for older versions
76-
if (release.tag_name == currentVersionTag) {
77-
__curVersionPos = -1;
78-
}
113+
if (release.tag_name == currentVersionTag) __curVersionPos = -1;
79114
newArray.insert(0, release);
80-
if (__curVersionPos > -2)
81-
__curVersionPos++;
115+
if (__curVersionPos > -2) __curVersionPos++;
82116
}
83117
}
84118
if (__curVersionPos < -1)
@@ -98,4 +132,6 @@ typedef UpdateCheckCallback = {
98132
@:optional var newVersionTag:String;
99133

100134
@:optional var updates:Array<GitHubRelease>;
135+
136+
@:optional var date:Date;
101137
}

source/funkin/menus/TitleState.hx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class TitleState extends MusicBeatState
129129

130130
if (pressedEnter && transitioning && skippedIntro) {
131131
FlxG.camera.stopFX();// FlxG.camera.visible = false;
132-
goToMainMenu();
132+
goToMainMenu(false);
133133
}
134134

135135
if (pressedEnter && !transitioning && skippedIntro)
@@ -152,22 +152,24 @@ class TitleState extends MusicBeatState
152152
transitioning = true;
153153
// FlxG.sound.music.stop();
154154

155-
new FlxTimer().start(2, (_) -> goToMainMenu());
155+
new FlxTimer().start(2, (_) -> goToMainMenu(false));
156156
}
157157

158-
function goToMainMenu() {
158+
function goToMainMenu(force = true) {
159159
#if UPDATE_CHECKING
160-
var report = hasCheckedUpdates ? null : funkin.backend.system.updating.UpdateUtil.checkForUpdates();
161-
hasCheckedUpdates = true;
162-
163-
if (report != null && report.newUpdate) {
164-
FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
165-
} else {
160+
if (!force) {
161+
funkin.backend.system.updating.UpdateUtil.waitForUpdates(false, (report) -> {
162+
hasCheckedUpdates = true;
163+
if (FlxG.state != this) return;
164+
165+
if (!report.newUpdate) goToMainMenu(true);
166+
else FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
167+
}, true);
168+
}
169+
else
170+
#end {
166171
FlxG.switchState(new MainMenuState());
167172
}
168-
#else
169-
FlxG.switchState(new MainMenuState());
170-
#end
171173
}
172174

173175
public function createCoolText(textArray:Array<String>)

source/funkin/options/categories/MiscOptions.hx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class MiscOptions extends TreeMenuScreen {
1111
#if UPDATE_CHECKING
1212
add(new Checkbox(getNameID('betaUpdates'), getDescID('betaUpdates'), 'betaUpdates'));
1313
add(new TextOption(getNameID('checkForUpdates'), getDescID('checkForUpdates'), () -> {
14-
var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates();
14+
var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates(true);
1515
if (report.newUpdate) FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
1616
else {
1717
CoolUtil.playMenuSFX(CANCEL);

0 commit comments

Comments
 (0)