44import com .falsepattern .lib .StableAPI ;
55import com .falsepattern .lib .dependencies .DependencyLoader ;
66import com .falsepattern .lib .dependencies .SemanticVersion ;
7- import com .falsepattern .lib .internal .FalsePatternLib ;
87import com .falsepattern .lib .internal .Internet ;
98import com .falsepattern .lib .internal .LibraryConfig ;
109import com .falsepattern .lib .internal .Tags ;
1110import com .falsepattern .lib .text .FormattedText ;
12- import com .falsepattern .lib .util .AsyncUtil ;
1311import cpw .mods .fml .common .Loader ;
1412import lombok .val ;
1513import net .minecraft .client .resources .I18n ;
1614import net .minecraft .event .ClickEvent ;
1715import net .minecraft .util .IChatComponent ;
1816
19- import java .io .ByteArrayOutputStream ;
20- import java .io .IOException ;
2117import java .net .MalformedURLException ;
2218import java .net .URL ;
2319import java .util .ArrayList ;
2420import java .util .Collections ;
2521import java .util .List ;
26- import java .util .concurrent .Future ;
27- import java .util .concurrent .atomic . AtomicInteger ;
28- import java .util .concurrent .atomic .AtomicReference ;
22+ import java .util .concurrent .CompletableFuture ;
23+ import java .util .concurrent .CompletionException ;
24+ import java .util .concurrent .atomic .AtomicBoolean ;
2925
3026@ StableAPI (since = "0.8.0" )
3127public class UpdateChecker {
32- private static final AtomicInteger jsonLibraryLoaded = new AtomicInteger (0 );
33- /**
34- * Same this as {@link #fetchUpdates(String)}, but defers the check to a different thread. Useful for asynchronous
35- * update checks, if you don't want to block loading.
36- * @param url The URL to check
37- * @return A future that will contain the update info about mods that were both available on the URL and installed
38- */
39- public static Future <List <ModUpdateInfo >> fetchUpdatesAsync (String url ) {
40- return AsyncUtil .asyncWorker .submit (() -> fetchUpdates (url ));
41- }
28+ private static final AtomicBoolean jsonLibraryLoaded = new AtomicBoolean (false );
29+
4230
4331 /**
4432 * Checks for updates. The URL should be a JSON file that contains a list of mods, each with a mod ID, one or more
@@ -62,21 +50,20 @@ public static Future<List<ModUpdateInfo>> fetchUpdatesAsync(String url) {
6250 * @param url The URL to check
6351 * @return A list of mods that were both available on the URL and installed
6452 */
65- public static List <ModUpdateInfo > fetchUpdates (String url ) {
66- if (!LibraryConfig .ENABLE_UPDATE_CHECKER ) {
67- return null ;
68- }
69- URL URL ;
70- try {
71- URL = new URL (url );
72- } catch (MalformedURLException e ) {
73- FalsePatternLib .getLog ().error ("Invalid URL: {}" , url , e );
74- return null ;
75- }
76- switch (jsonLibraryLoaded .get ()) {
77- case 0 :
78- DependencyLoader .addMavenRepo ("https://maven.falsepattern.com/" );
53+ public static CompletableFuture <List <ModUpdateInfo >> fetchUpdatesAsync (String url ) {
54+ return CompletableFuture .supplyAsync (() -> {
55+ if (!LibraryConfig .ENABLE_UPDATE_CHECKER ) {
56+ throw new CompletionException (new UpdateCheckException ("Update checker is disabled in config!" ));
57+ }
58+ URL URL ;
59+ try {
60+ URL = new URL (url );
61+ } catch (MalformedURLException e ) {
62+ throw new CompletionException (new UpdateCheckException ("Invalid URL: " + url , e ));
63+ }
64+ if (!jsonLibraryLoaded .get ()) {
7965 try {
66+ DependencyLoader .addMavenRepo ("https://maven.falsepattern.com/" );
8067 DependencyLoader .builder ()
8168 .loadingModId (Tags .MODID )
8269 .groupId ("com.falsepattern" )
@@ -86,61 +73,71 @@ public static List<ModUpdateInfo> fetchUpdates(String url) {
8673 .preferredVersion (new SemanticVersion (0 , 4 , 1 ))
8774 .build ();
8875 } catch (Exception e ) {
89- FalsePatternLib .getLog ().error ("Failed to load json library for update checker!" , e );
90- jsonLibraryLoaded .set (-1 );
91- return null ;
76+ throw new CompletionException (new UpdateCheckException ("Failed to load json library for update checker!" , e ));
9277 }
93- jsonLibraryLoaded .set (1 );
94- break ;
95- case -1 :
96- return null ;
97- }
98- AtomicReference <String > loadedData = new AtomicReference <>(null );
99- Internet .connect (URL , (ex ) -> FalsePatternLib .getLog ().warn ("Failed to check for updates from URL {}" , url , ex ), (input ) -> {
100- val data = new ByteArrayOutputStream ();
78+ jsonLibraryLoaded .set (true );
79+ }
80+ val result = new ArrayList <ModUpdateInfo >();
81+ JsonNode parsed ;
10182 try {
102- Internet .transferAndClose ( input , data );
103- } catch (IOException e ) {
104- throw new RuntimeException ( e );
83+ parsed = JsonNode . parse ( Internet .download ( URL ). thenApply ( String :: new ). join () );
84+ } catch (CompletionException e ) {
85+ throw new CompletionException ( new UpdateCheckException ( "Failed to download update checker JSON file!" , e . getCause () == null ? e : e . getCause ()) );
10586 }
106- loadedData .set (data .toString ());
107- });
108- if (loadedData .get () == null ) return null ;
109- val result = new ArrayList <ModUpdateInfo >();
110- val parsed = JsonNode .parse (loadedData .get ());
111- List <JsonNode > modList ;
112- if (parsed .isList ()) {
113- modList = parsed .getJavaList ();
114- } else {
115- modList = Collections .singletonList (parsed );
116- }
117- val installedMods = Loader .instance ().getIndexedModList ();
118- for (val node : modList ) {
119- if (!node .isObject ()) continue ;
120- if (!node .containsKey ("modid" )) continue ;
121- if (!node .containsKey ("latestVersion" )) continue ;
122- val modid = node .getString ("modid" );
123- if (!installedMods .containsKey (modid )) continue ;
124- val mod = installedMods .get (modid );
125- val latestVersionsNode = node .get ("latestVersion" );
126- List <String > latestVersions ;
127- if (latestVersionsNode .isString ()) {
128- latestVersions = Collections .singletonList (latestVersionsNode .stringValue ());
129- } else if (latestVersionsNode .isList ()) {
130- latestVersions = new ArrayList <>();
131- for (val version : latestVersionsNode .getJavaList ()) {
132- if (!version .isString ()) continue ;
133- latestVersions .add (version .stringValue ());
134- }
87+ List <JsonNode > modList ;
88+ if (parsed .isList ()) {
89+ modList = parsed .getJavaList ();
13590 } else {
136- continue ;
91+ modList = Collections .singletonList (parsed );
92+ }
93+ val installedMods = Loader .instance ().getIndexedModList ();
94+ for (val node : modList ) {
95+ if (!node .isObject ()) continue ;
96+ if (!node .containsKey ("modid" )) continue ;
97+ if (!node .containsKey ("latestVersion" )) continue ;
98+ val modid = node .getString ("modid" );
99+ if (!installedMods .containsKey (modid )) continue ;
100+ val mod = installedMods .get (modid );
101+ val latestVersionsNode = node .get ("latestVersion" );
102+ List <String > latestVersions ;
103+ if (latestVersionsNode .isString ()) {
104+ latestVersions = Collections .singletonList (latestVersionsNode .stringValue ());
105+ } else if (latestVersionsNode .isList ()) {
106+ latestVersions = new ArrayList <>();
107+ for (val version : latestVersionsNode .getJavaList ()) {
108+ if (!version .isString ()) continue ;
109+ latestVersions .add (version .stringValue ());
110+ }
111+ } else {
112+ continue ;
113+ }
114+ val currentVersion = mod .getVersion ();
115+ if (latestVersions .contains (currentVersion )) continue ;
116+ val updateURL = node .containsKey ("updateURL" ) && node .get ("updateURL" ).isString () ? node .getString ("updateURL" ) : "" ;
117+ result .add (new ModUpdateInfo (modid , currentVersion , latestVersions .get (0 ), updateURL ));
118+ }
119+ return result ;
120+ });
121+ }
122+
123+ /**
124+ * Same this as {@link #fetchUpdatesAsync(String)}, but returns the result in a blocking fashion.
125+ * @param url The URL to check
126+ * @return A future that will contain the update info about mods that were both available on the URL and installed
127+ * @throws UpdateCheckException If the update checker is disabled in config, the URL is invalid, or
128+ */
129+ public static List <ModUpdateInfo > fetchUpdates (String url ) throws UpdateCheckException {
130+ try {
131+ return fetchUpdatesAsync (url ).join ();
132+ } catch (CompletionException e ) {
133+ try {
134+ throw e .getCause ();
135+ } catch (UpdateCheckException e1 ) {
136+ throw e1 ;
137+ } catch (Throwable e1 ) {
138+ throw new UpdateCheckException ("Failed to check for updates!" , e1 );
137139 }
138- val currentVersion = mod .getVersion ();
139- if (latestVersions .contains (currentVersion )) continue ;
140- val updateURL = node .containsKey ("updateURL" ) && node .get ("updateURL" ).isString () ? node .getString ("updateURL" ) : "" ;
141- result .add (new ModUpdateInfo (modid , currentVersion , latestVersions .get (0 ), updateURL ));
142140 }
143- return result ;
144141 }
145142
146143 /**
0 commit comments