From e3666339c4cd73772e33023123eeeb91156346f8 Mon Sep 17 00:00:00 2001 From: Orbyfied Date: Mon, 15 May 2023 23:26:44 +0200 Subject: [PATCH 1/3] rewrote http usage to java.net --- build.gradle | 6 +- gradle/libs.versions.toml | 25 ----- .../redstonetools/macros/MacroManager.java | 8 +- .../redstonetools/mixin/UpdatePopupMixin.java | 49 ++++++---- .../telemetry/TelemetryClient.java | 93 +++++++++++-------- .../telemetry/TelemetryManager.java | 14 +-- 6 files changed, 97 insertions(+), 98 deletions(-) delete mode 100644 gradle/libs.versions.toml diff --git a/build.gradle b/build.gradle index 0a9e1d04..4bd2a3a3 100644 --- a/build.gradle +++ b/build.gradle @@ -23,14 +23,13 @@ repositories { } } + shadowJar { archiveClassifier = "unmapped" configurations = [project.configurations.shadow] from("LICENSE") - - relocate "com.squareup.okhttp3", "tools.redstone.shadow.com.squareup.okhttp3" } remapJar { @@ -38,9 +37,6 @@ remapJar { } dependencies { - // OkHttp - shadow implementation('com.squareup.okhttp3:okhttp:4.11.0') - // jUnit 5 include implementation('org.junit.jupiter:junit-jupiter:5.8.1') diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml deleted file mode 100644 index e7011a26..00000000 --- a/gradle/libs.versions.toml +++ /dev/null @@ -1,25 +0,0 @@ -[versions] -minecraft = "1.18.2" -yarn-mappings = "1.18.2+build.4" -loader = "0.14.14" - -fabric = "0.48.0+1.18.2" -worldedit = "7.2.10" - -junit = "5.8.1" -spring-di = "5.3.19" - -[libraries] -# Fabric Properties -# check these on https://fabricmc.net/develop -minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } -fabric-yarn = { module = "net.fabricmc:yarn", version.ref = "yarn-mappings" } -fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "loader" } - -# Mod dependencies -fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric" } -worldedit = { module = "com.sk89q.worldedit:worldedit-fabric-mc1.18.2", version.ref = "worldedit" } - -# Other dependencies -junit-jupyter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } -spring-di = { module = "org.springframework:spring-context", version.ref = "spring-di" } diff --git a/src/main/java/tools/redstone/redstonetools/macros/MacroManager.java b/src/main/java/tools/redstone/redstonetools/macros/MacroManager.java index 796223d2..e53da2bf 100644 --- a/src/main/java/tools/redstone/redstonetools/macros/MacroManager.java +++ b/src/main/java/tools/redstone/redstonetools/macros/MacroManager.java @@ -30,9 +30,11 @@ public MacroManager() { JsonArray macrosJson = null; try { Files.createDirectories(macrosFilePath.getParent()); - var reader = Json.createReader(new FileReader(macrosFilePath.toFile())); - macrosJson = reader.readArray(); - reader.close(); + if (Files.exists(macrosFilePath)) { + var reader = Json.createReader(new FileReader(macrosFilePath.toFile())); + macrosJson = reader.readArray(); + reader.close(); + } } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/tools/redstone/redstonetools/mixin/UpdatePopupMixin.java b/src/main/java/tools/redstone/redstonetools/mixin/UpdatePopupMixin.java index ae43495d..10228ad0 100644 --- a/src/main/java/tools/redstone/redstonetools/mixin/UpdatePopupMixin.java +++ b/src/main/java/tools/redstone/redstonetools/mixin/UpdatePopupMixin.java @@ -2,14 +2,10 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.TitleScreen; import net.minecraft.text.Text; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -17,16 +13,18 @@ import tools.redstone.redstonetools.RedstoneToolsClient; import tools.redstone.redstonetools.gui.UpdatePopupScreen; -import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; -import java.util.concurrent.TimeUnit; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +import static tools.redstone.redstonetools.RedstoneToolsClient.LOGGER; @Mixin(TitleScreen.class) public class UpdatePopupMixin extends Screen { - private static long timeout = 250; - public boolean updateChecked = false; public UpdatePopupMixin(Text title) { @@ -39,17 +37,23 @@ public void init(CallbackInfo ci) { return; try { - OkHttpClient client = new OkHttpClient.Builder() - .readTimeout(timeout, TimeUnit.MILLISECONDS) - .writeTimeout(timeout, TimeUnit.MILLISECONDS) - .connectTimeout(timeout, TimeUnit.MILLISECONDS) + LOGGER.info("Checking for updates..."); + + // timeout before aborting connection + // gh took quite long to respond on some machines + // so i think the timeout should be 1s + final long timeout = 1000; + + HttpClient client = HttpClient.newBuilder() + .connectTimeout(Duration.of(timeout, ChronoUnit.MILLIS)) .build(); - Request request = new Request.Builder() - .url("https://api.github.com/repos/RedstoneTools/redstonetools/releases/latest") + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("https://api.github.com/repos/RedstoneTools/redstonetools/releases/latest")) + .GET() .build(); - Response response = client.newCall(request).execute(); - String responseBody = response.body().string(); - if (response.code() != 200) + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + String responseBody = response.body(); + if (response.statusCode() != 200) return; Gson gson = new Gson(); @@ -57,11 +61,16 @@ public void init(CallbackInfo ci) { URI uri = new URI(release.get("html_url").getAsString()); String newVersion = release.get("tag_name").getAsString(); - if (RedstoneToolsClient.MOD_VERSION.equals(newVersion) || newVersion.contains("alpha") || newVersion.contains("beta")) + LOGGER.info("Found latest version: " + newVersion); + if (RedstoneToolsClient.MOD_VERSION.equals(newVersion) || newVersion.contains("alpha") || newVersion.contains("beta")) { + LOGGER.info("Already up to date, current version: " + RedstoneToolsClient.MOD_VERSION + ", new version: " + newVersion); return; + } + LOGGER.info("Found newer version, current version: " + RedstoneToolsClient.MOD_VERSION + ", new version: " + newVersion); MinecraftClient.getInstance().setScreen(new UpdatePopupScreen(this, uri, newVersion)); - } catch (JsonSyntaxException | IOException | URISyntaxException e) { + } catch (Exception e) { + LOGGER.warn("Failed to check for RedstoneTools updates"); e.printStackTrace(); } finally { updateChecked = true; diff --git a/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryClient.java b/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryClient.java index 714d1648..4ce8b999 100644 --- a/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryClient.java +++ b/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryClient.java @@ -1,17 +1,19 @@ package tools.redstone.redstonetools.telemetry; import com.google.gson.Gson; -import kotlin.Pair; +import it.unimi.dsi.fastutil.Pair; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; -import okhttp3.*; -import org.jetbrains.annotations.NotNull; import tools.redstone.redstonetools.telemetry.dto.TelemetryAuth; import tools.redstone.redstonetools.telemetry.dto.TelemetryCommand; import tools.redstone.redstonetools.telemetry.dto.TelemetryException; import java.io.IOException; import java.net.ConnectException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.time.Instant; import java.util.Objects; import java.util.Queue; @@ -31,17 +33,22 @@ public class TelemetryClient { private static final int REQUEST_SEND_TIME_MILLISECONDS = 50; private static final int REQUEST_VALID_FOR_SECONDS = 30; + private final TelemetryManager manager = INJECTOR.getInstance(TelemetryManager.class); + private static volatile Instant lastAuthorization = Instant.MIN; private final Gson gson = new Gson(); - private final OkHttpClient httpClient = new OkHttpClient(); - private final Queue> requestQueue = new ConcurrentLinkedQueue<>(); + private final HttpClient httpClient; + private final Queue> requestQueue = new ConcurrentLinkedQueue<>(); private volatile String token; public TelemetryClient() { LOGGER.info("Initializing telemetry client"); + httpClient = HttpClient.newBuilder() + .build(); + Executors.newSingleThreadExecutor() .execute(this::refreshSessionThread); @@ -50,19 +57,19 @@ public TelemetryClient() { } public void sendCommand(TelemetryCommand command) { - if (INJECTOR.getInstance(TelemetryManager.class).telemetryEnabled) { + if (manager.telemetryEnabled) { addRequest(createRequest("/command", command)); } } public void sendException(TelemetryException exception) { - if (INJECTOR.getInstance(TelemetryManager.class).telemetryEnabled) { + if (manager.telemetryEnabled) { addRequest(createRequest("/exception", exception)); } } - private void addRequest(Request.Builder request) { - requestQueue.add(new Pair<>(request, Instant.now())); + private void addRequest(HttpRequest.Builder request) { + requestQueue.add(Pair.of(request, Instant.now())); } public synchronized void waitForQueueToEmpty() { @@ -74,10 +81,17 @@ public synchronized void waitForQueueToEmpty() { } } - private Request.Builder createRequest(String path, Object body) { - return new Request.Builder() - .url(BASE_URL + path) - .post(RequestBody.create(body == null ? "" : gson.toJson(body), MediaType.parse("application/json"))); + private HttpRequest.Builder createRequest(String path, Object body) { + final String bodyData = body == null ? "" : gson.toJson(body); + return HttpRequest.newBuilder() + .header("Content-Type", "application/json") + .uri(URI.create(BASE_URL + path)) + .POST(HttpRequest.BodyPublishers.ofString(bodyData)); + } + + private boolean isSuccessful(HttpResponse response) { + if (response == null) return false; + return response.statusCode() >= 200 && response.statusCode() < 300; } private synchronized CompletableFuture sendQueuedRequestsAsync() { @@ -90,8 +104,8 @@ private synchronized CompletableFuture sendQueuedRequestsAsync() { } var pair = Objects.requireNonNull(requestQueue.peek()); - var request = pair.component1(); - var queuedAt = pair.component2(); + var request = pair.first(); + var queuedAt = pair.second(); if (queuedAt.plusSeconds(REQUEST_VALID_FOR_SECONDS).isBefore(Instant.now())) { requestQueue.remove(); @@ -100,12 +114,12 @@ private synchronized CompletableFuture sendQueuedRequestsAsync() { } if (token != null) { - request.addHeader("Authorization", token); + request.header("Authorization", token); } - var response = sendPostRequestAsync(request).join(); + var response = sendPostRequest(request); - if (response == null || !response.isSuccessful()) { + if (response == null || !isSuccessful(response)) { if (response != null && responseIsUnauthorized(response)) { lastAuthorization = Instant.MIN; } @@ -129,21 +143,26 @@ private synchronized CompletableFuture sendQueuedRequestsAsync() { }); } - private synchronized @NotNull CompletableFuture sendPostRequestAsync(Request.Builder request) { - LOGGER.trace("Sending telemetry request to " + request.build().url()); + private synchronized HttpResponse sendPostRequest(HttpRequest.Builder request) { + LOGGER.trace("Sending telemetry request to " + request.build().uri()); - return CompletableFuture.supplyAsync(() -> { + // this doesnt have to be async as the only place this is + // ever called is from another thread + +// return CompletableFuture.supplyAsync(() -> { try { - return httpClient.newCall(request.build()).execute(); + return httpClient.send(request.build() + /* the json as a string */, + HttpResponse.BodyHandlers.ofString()); } catch (ConnectException e) { // Either the server is down or the user isn't connected to the internet LOGGER.debug("Failed to send telemetry request: " + e.getMessage()); - } catch (IOException e) { + } catch (InterruptedException | IOException e) { LOGGER.error("Failed to send telemetry request", e); } return null; - }); +// }); } private void refreshSessionThread() { @@ -165,27 +184,25 @@ private synchronized CompletableFuture refreshSessionAsync() { var request = createRequest(token == null ? "/session/create" : "/session/refresh", getAuth()); if (token != null) { - request.addHeader("Authorization", token); + request.header("Authorization", token); } - var response = sendPostRequestAsync(request).join(); + var response = sendPostRequest(request); - if (response == null || !response.isSuccessful()) { - if (response != null && responseIsUnauthorized(response)) { + if (response == null) + return false; + if (!isSuccessful(response)) { + LOGGER.warn("Failed to refresh telemetry session, response code: " + response.statusCode() + ", message: " + response.body()); + if (responseIsUnauthorized(response)) { token = null; } return false; } - try (var body = response.body()) { - token = body.string(); - } catch (IOException e) { - LOGGER.error("Failed to read telemetry session response", e); - return false; - } + token = response.body(); - LOGGER.debug("Refreshed telemetry session"); + LOGGER.info("Refreshed telemetry session, new token: " + token.substring(0, 8) + "..."); lastAuthorization = Instant.now(); return true; }); @@ -201,8 +218,8 @@ private TelemetryAuth getAuth() { ); } - private static boolean responseIsUnauthorized(Response response) { - return response.code() == 401 - || response.code() == 403; + private static boolean responseIsUnauthorized(HttpResponse response) { + return response.statusCode() == 401 + || response.statusCode() == 403; } } diff --git a/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryManager.java b/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryManager.java index 7cad4043..dcfc47d3 100644 --- a/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryManager.java +++ b/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryManager.java @@ -24,16 +24,16 @@ public TelemetryManager() { JsonObject telemetryJson = null; try { Files.createDirectories(telemetryFilePath.getParent()); - var reader = Json.createReader(new FileReader(telemetryFilePath.toFile())); - telemetryJson = reader.readObject(); - reader.close(); + if (Files.exists(telemetryFilePath)) { + var reader = Json.createReader(new FileReader(telemetryFilePath.toFile())); + telemetryJson = reader.readObject(); + reader.close(); + + loadSettingsFromJson(telemetryJson); + } } catch (Exception e) { e.printStackTrace(); } - - if (telemetryJson != null) { - loadSettingsFromJson(telemetryJson); - } } private void loadSettingsFromJson(JsonObject json) { From 564ad3cd3932fc99c46f92fefa6cbfcd21705f5f Mon Sep 17 00:00:00 2001 From: Orbyfied Date: Mon, 15 May 2023 23:29:16 +0200 Subject: [PATCH 2/3] final fixes on http rewrite --- .../tools/redstone/redstonetools/telemetry/TelemetryClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryClient.java b/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryClient.java index 4ce8b999..9920b7c8 100644 --- a/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryClient.java +++ b/src/main/java/tools/redstone/redstonetools/telemetry/TelemetryClient.java @@ -202,7 +202,7 @@ private synchronized CompletableFuture refreshSessionAsync() { token = response.body(); - LOGGER.info("Refreshed telemetry session, new token: " + token.substring(0, 8) + "..."); + LOGGER.info("Refreshed telemetry session"); lastAuthorization = Instant.now(); return true; }); From 4c0d37a78aa7104537034621bcc2e2d4008c806e Mon Sep 17 00:00:00 2001 From: Matthias Wijnsma Date: Sat, 20 May 2023 20:43:15 +0200 Subject: [PATCH 3/3] Fixed ambiguous option error --- gradle.properties | 2 +- .../features/arguments/serializers/EnumSerializer.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 7fa6b742..f00d84d4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ org.gradle.jvmargs=-Xmx4G loader_version=0.14.6 # Mod Properties - mod_version = 1.18.2-1.0.2 + mod_version = 1.18.2-1.0.3 maven_group = tools.redstone archives_base_name = redstonetools diff --git a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/EnumSerializer.java b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/EnumSerializer.java index 425fa7d9..be6d2e92 100644 --- a/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/EnumSerializer.java +++ b/src/main/java/tools/redstone/redstonetools/features/arguments/serializers/EnumSerializer.java @@ -45,6 +45,11 @@ public T deserialize(String input) { .filter(elem -> serialize(elem).toLowerCase().startsWith(inputLowerCase)) .toList(); + var exactMatch = matches.stream().filter(elem -> serialize(elem).toLowerCase().equals(input)).findFirst(); + if (exactMatch.isPresent()) { + return exactMatch.get(); + } + if (matches.isEmpty()) { throw new IllegalArgumentException("No such option '" + input + "'"); }