Skip to content

Commit

Permalink
Rewrote description caching to fix broken cache
Browse files Browse the repository at this point in the history
  • Loading branch information
isXander committed Mar 31, 2024
1 parent 7c40482 commit 3b41185
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 101 deletions.
12 changes: 2 additions & 10 deletions src/client/java/dev/isxander/debugify/client/DebugifyClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,8 @@ public class DebugifyClient {

public static void onInitializeClient() {
bugFixDescriptionCache = new BugFixDescriptionCache();

if (Debugify.configWasDirty) {
Debugify.LOGGER.info("Re-caching descriptions because json doesn't match config.");
bugFixDescriptionCache.cacheDescriptions();
} else {
if (!bugFixDescriptionCache.load()) {
Debugify.LOGGER.info("Failed to load descriptions, re-caching.");
bugFixDescriptionCache.cacheDescriptions();
}
}
bugFixDescriptionCache.loadDescriptions();
bugFixDescriptionCache.cacheMissingDescriptions();
}

public static boolean isGameplayFixesEnabled() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import net.fabricmc.loader.api.FabricLoader;
import java.net.ConnectException;
import java.net.NetworkInterface;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

public class BugFixDescriptionCache {
Expand All @@ -25,102 +27,81 @@ public class BugFixDescriptionCache {
private final Map<String, String> descriptionHolder = new HashMap<>();
private final String url = "https://bugs.mojang.com/rest/api/2/issue/%s";

public void cacheDescriptions() {
Debugify.LOGGER.info("Connecting to 'bugs.mojang.com' to cache bug descriptions!");

CompletableFuture.runAsync(() -> {
HttpClient client = HttpClient.newHttpClient();

boolean givenUp = false;
for (BugFixData bugData : Debugify.CONFIG.getBugFixes().keySet()) {
String id = bugData.bugId();
int attempts = 0;
boolean success = true;

do {
try {
if (attempts >= 2) {
Debugify.LOGGER.error("Giving up trying to cache bug descriptions. Consecutive network failures.");
givenUp = true;
break;
}
attempts++;

HttpRequest request = HttpRequest.newBuilder(new URI(String.format(url, id)))
.build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

if (response.statusCode() != 200) {
Debugify.LOGGER.error("Description Cache: {} - {}", response.statusCode(), response.body());
continue;
}

JsonObject json = gson.fromJson(response.body(), JsonObject.class);
JsonObject fields = json.getAsJsonObject("fields");
String summary = fields.get("summary").getAsString();

descriptionHolder.put(id, summary);

success = true;
} catch (ConnectException e) {
Debugify.LOGGER.error("Couldn't connect to `bugs.mojang.com`", e);
success = false;
} catch (Exception e) {
e.printStackTrace();
success = false;
}
} while (!success);

if (givenUp)
break;
}
}).thenAccept((returnVal) -> save());
}
public boolean loadDescriptions() {
if (Files.notExists(file)) {
return false;
}

public void save() {
try {
Files.deleteIfExists(file);

JsonObject json = new JsonObject();
descriptionHolder.forEach(json::addProperty);

Files.createFile(file);
Files.writeString(file, gson.toJson(json));
} catch (Exception e) {
String json = Files.readString(file);
gson.fromJson(json, JsonObject.class)
.entrySet()
.forEach(entry ->
descriptionHolder.put(entry.getKey(), entry.getValue().getAsString()));

return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}

public boolean load() {
Debugify.LOGGER.info("Loading Description Cache");
public void cacheMissingDescriptions() {
ExecutorService executor = Executors.newFixedThreadPool(4, r -> new Thread(r, "Debugify Description Cache"));
HttpClient client = HttpClient.newHttpClient();

if (!Files.exists(file)) {
return false;
List<CompletableFuture<?>> futures = new ArrayList<>();
for (BugFixData bugFix : Debugify.CONFIG.getBugFixes().keySet()) {
String id = bugFix.bugId();
if (descriptionHolder.containsKey(id)) continue;

Debugify.LOGGER.info("Caching description for bug {}", id);

futures.add(CompletableFuture.runAsync(() -> cacheBugDescription(client, id), executor));
}

CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRun(() -> {
save();
executor.shutdown();
});
}

private void cacheBugDescription(HttpClient client, String bugId) {
try {
String jsonString = Files.readString(file);
JsonObject json = gson.fromJson(jsonString, JsonObject.class);
HttpRequest request = HttpRequest.newBuilder(new URI(String.format(url, bugId)))
.setHeader("User-Agent", "Debugify/%s mod https://github.com/isXander/Debugify/blob/1.20/src/client/java/dev/isxander/debugify/client/utils/BugFixDescriptionCache.java".formatted(Debugify.VERSION.getFriendlyString()))
.build();

Map<String, String> loadedDescriptions = json.entrySet().stream()
.map((entry) -> new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue().getAsString()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

if (loadedDescriptions.values().stream().anyMatch(desc -> desc.contains("\n"))) {
Debugify.LOGGER.warn("Outdated description cache format, re-caching!");
cacheDescriptions();
return true;
if (response.statusCode() != 200) {
Debugify.LOGGER.error("Description Cache fail: HTTP status {} - {}", response.statusCode(), response.body());
return;
}

descriptionHolder.putAll(loadedDescriptions);
JsonObject json = gson.fromJson(response.body(), JsonObject.class);
JsonObject fields = json.getAsJsonObject("fields");
String summary = fields.get("summary").getAsString();

descriptionHolder.put(bugId, summary);
} catch (URISyntaxException | IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}

public void save() {
try {
Files.deleteIfExists(file);

JsonObject json = new JsonObject();
descriptionHolder.forEach(json::addProperty);

Files.createFile(file);
Files.writeString(file, gson.toJson(json));
} catch (Exception e) {
Debugify.LOGGER.error("Couldn't load description cache!");
e.printStackTrace();
return false;
}

return true;
}

public String get(String id) {
Expand Down
9 changes: 2 additions & 7 deletions src/main/java/dev/isxander/debugify/Debugify.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import dev.isxander.debugify.mixinplugin.DebugifyErrorHandler;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongepowered.asm.mixin.Mixins;
Expand All @@ -14,8 +15,8 @@

public class Debugify {
public static final Logger LOGGER = LoggerFactory.getLogger("Debugify");
public static final Version VERSION = FabricLoader.getInstance().getModContainer("debugify").orElseThrow().getMetadata().getVersion();
public static final DebugifyConfig CONFIG = new DebugifyConfig();
public static boolean configWasDirty = false;

/**
* Called from mixin plugin to manage
Expand All @@ -27,12 +28,6 @@ public static void onPreInitialize() {
}

public static void onInitialize() {
configWasDirty = !CONFIG.doesJsonHaveIdenticalKeys();
if (configWasDirty) {
LOGGER.info("Saving config because the loaded bug fixes are different to stored json.");
CONFIG.save();
}

List<String> enabledBugs = CONFIG.getBugFixes().entrySet()
.stream()
.filter(Map.Entry::getValue)
Expand Down

0 comments on commit 3b41185

Please sign in to comment.