Skip to content

Commit 02e8b58

Browse files
committed
Introduce simple update checker, using Modrinth api.
1 parent 218d033 commit 02e8b58

File tree

9 files changed

+189
-0
lines changed

9 files changed

+189
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
plugins {
2+
`commons-java-17`
3+
}
4+
5+
dependencies {
6+
implementation(project(":eternalcode-commons-updater"))
7+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.eternalcode.commons.updater.example;
2+
3+
import com.eternalcode.commons.updater.UpdateResult;
4+
5+
public class ExampleChecker {
6+
public static void main(String[] args) {
7+
ExampleUpdateService service = new ExampleUpdateService();
8+
9+
UpdateResult modrinthResult = service.checkModrinth("EternalCombat", "2.2.0");
10+
System.out.println("Modrinth update available: " + modrinthResult.isUpdateAvailable());
11+
if (modrinthResult.isUpdateAvailable()) {
12+
System.out.println("Latest: " + modrinthResult.latestVersion());
13+
System.out.println("Download: " + modrinthResult.downloadUrl());
14+
System.out.println("Release page: " + modrinthResult.releaseUrl());
15+
}
16+
}
17+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.eternalcode.commons.updater.example;
2+
3+
import com.eternalcode.commons.updater.UpdateResult;
4+
import com.eternalcode.commons.updater.Version;
5+
import com.eternalcode.commons.updater.impl.ModrinthUpdateChecker;
6+
7+
public final class ExampleUpdateService {
8+
private final ModrinthUpdateChecker modrinthChecker = new ModrinthUpdateChecker();
9+
10+
public UpdateResult checkModrinth(String projectId, String currentVersion) {
11+
return modrinthChecker.check(projectId, new Version(currentVersion));
12+
}
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
plugins {
2+
`commons-java-17`
3+
`commons-publish`
4+
`commons-repositories`
5+
`commons-java-unit-test`
6+
}
7+
8+
tasks.test {
9+
useJUnitPlatform()
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.eternalcode.commons.updater;
2+
3+
public interface UpdateChecker {
4+
5+
UpdateResult check(String projectId, Version currentVersion);
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.eternalcode.commons.updater;
2+
3+
public record UpdateResult(Version currentVersion, Version latestVersion, String downloadUrl, String releaseUrl) {
4+
5+
public boolean isUpdateAvailable() {
6+
return this.latestVersion.isNewerThan(this.currentVersion);
7+
}
8+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.eternalcode.commons.updater;
2+
3+
public class Version implements Comparable<Version> {
4+
5+
private final String value;
6+
7+
public Version(String version) {
8+
this.value = version.trim();
9+
}
10+
11+
@Override
12+
public int compareTo(Version version) {
13+
return this.value.compareTo(version.value);
14+
}
15+
16+
public boolean isNewerThan(Version version) {
17+
return this.value.compareTo(version.value) > 0;
18+
}
19+
20+
@Override
21+
public boolean equals(Object object) {
22+
return object instanceof Version other && value.equals(other.value);
23+
}
24+
25+
@Override
26+
public int hashCode() {
27+
return value.hashCode();
28+
}
29+
30+
@Override
31+
public String toString() {
32+
return this.value;
33+
}
34+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.eternalcode.commons.updater.impl;
2+
3+
import com.eternalcode.commons.updater.UpdateChecker;
4+
import com.eternalcode.commons.updater.UpdateResult;
5+
import com.eternalcode.commons.updater.Version;
6+
import java.net.URI;
7+
import java.net.http.HttpClient;
8+
import java.net.http.HttpRequest;
9+
import java.net.http.HttpResponse;
10+
import java.time.Duration;
11+
import java.util.Optional;
12+
13+
public final class ModrinthUpdateChecker implements UpdateChecker {
14+
15+
private static final String API_BASE_URL = "https://api.modrinth.com/v2";
16+
private static final String MODRINTH_BASE_URL = "https://modrinth.com/mod";
17+
private static final String USER_AGENT = "UpdateChecker/1.0";
18+
private static final Duration TIMEOUT = Duration.ofSeconds(10);
19+
20+
private final HttpClient client;
21+
22+
public ModrinthUpdateChecker() {
23+
this.client = HttpClient.newBuilder().connectTimeout(TIMEOUT).build();
24+
}
25+
26+
@Override
27+
public UpdateResult check(String projectId, Version currentVersion) {
28+
if (projectId == null || projectId.trim().isEmpty()) {
29+
throw new IllegalArgumentException("Project ID cannot be null or empty");
30+
}
31+
32+
try {
33+
String url = API_BASE_URL + "/project/" + projectId + "/version";
34+
35+
HttpRequest request =
36+
HttpRequest.newBuilder().uri(URI.create(url)).header("User-Agent", USER_AGENT).timeout(TIMEOUT).build();
37+
38+
HttpResponse<String> response = this.client.send(request, HttpResponse.BodyHandlers.ofString());
39+
40+
if (response.statusCode() != 200) {
41+
return createEmptyResult(currentVersion);
42+
}
43+
44+
String json = response.body();
45+
if (json == null || json.trim().isEmpty()) {
46+
return createEmptyResult(currentVersion);
47+
}
48+
49+
Optional<String> versionNumber = extractJsonValue(json, "version_number");
50+
Optional<String> downloadUrl = extractJsonValue(json, "url");
51+
52+
if (versionNumber.isEmpty()) {
53+
return createEmptyResult(currentVersion);
54+
}
55+
56+
String releaseUrl = MODRINTH_BASE_URL + "/" + projectId + "/version/" + versionNumber.get();
57+
Version latestVersion = new Version(versionNumber.get());
58+
59+
return new UpdateResult(currentVersion, latestVersion, downloadUrl.orElse(null), releaseUrl);
60+
}
61+
catch (Exception exception) {
62+
throw new RuntimeException("Failed to check Modrinth updates for project: " + projectId, exception);
63+
}
64+
}
65+
66+
private UpdateResult createEmptyResult(Version currentVersion) {
67+
return new UpdateResult(currentVersion, currentVersion, null, null);
68+
}
69+
70+
private Optional<String> extractJsonValue(String json, String key) {
71+
if (json == null || key == null) {
72+
return Optional.empty();
73+
}
74+
75+
String pattern = "\"" + key + "\":\"";
76+
int start = json.indexOf(pattern);
77+
78+
if (start == -1) {
79+
return Optional.empty();
80+
}
81+
82+
start += pattern.length();
83+
int end = json.indexOf("\"", start);
84+
85+
if (end == -1) {
86+
return Optional.empty();
87+
}
88+
89+
String value = json.substring(start, end);
90+
return value.isEmpty() ? Optional.empty() : Optional.of(value);
91+
}
92+
}

settings.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ include(":eternalcode-commons-bukkit")
44
include(":eternalcode-commons-adventure")
55
include(":eternalcode-commons-shared")
66
include("eternalcode-commons-folia")
7+
include("eternalcode-commons-updater")
8+
include("eternalcode-commons-updater-example")

0 commit comments

Comments
 (0)