Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add provided update checkers, loader update checkers #711

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/main/java/com/terraformersmc/modmenu/ModMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public void onInitializeClient() {
ModMenuConfigManager.initializeConfig();
Set<String> modpackMods = new HashSet<>();
Map<String, UpdateChecker> updateCheckers = new HashMap<>();
Map<String, UpdateChecker> providedUpdateCheckers = new HashMap<>();

FabricLoader.getInstance().getEntrypointContainers("modmenu", ModMenuApi.class).forEach(entrypoint -> {
ModMetadata metadata = entrypoint.getProvider().getMetadata();
String modId = metadata.getId();
Expand All @@ -78,6 +80,7 @@ public void onInitializeClient() {
configScreenFactories.put(modId, api.getModConfigScreenFactory());
apiImplementations.add(api);
updateCheckers.put(modId, api.getUpdateChecker());
providedUpdateCheckers.putAll(api.getProvidedUpdateCheckers());
api.attachModpackBadges(modpackMods::add);
} catch (Throwable e) {
LOGGER.error("Mod {} provides a broken implementation of ModMenuApi", modId, e);
Expand All @@ -94,9 +97,14 @@ public void onInitializeClient() {
mod = new FabricMod(modContainer, modpackMods);
}

mod.setUpdateChecker(updateCheckers.get(mod.getId()));
var updateChecker = updateCheckers.get(mod.getId());

if (updateChecker == null) {
updateChecker = providedUpdateCheckers.get(mod.getId());
}

MODS.put(mod.getId(), mod);
mod.setUpdateChecker(updateChecker);
}

checkForUpdates();
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/com/terraformersmc/modmenu/ModMenuModMenuCompat.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
package com.terraformersmc.modmenu;

import com.google.common.collect.ImmutableMap;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import com.terraformersmc.modmenu.api.UpdateChecker;
import com.terraformersmc.modmenu.gui.ModMenuOptionsScreen;
import com.terraformersmc.modmenu.util.mod.fabric.FabricLoaderUpdateChecker;
import com.terraformersmc.modmenu.util.mod.quilt.QuiltLoaderUpdateChecker;

import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.option.OptionsScreen;

import java.util.Map;

public class ModMenuModMenuCompat implements ModMenuApi {

@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return ModMenuOptionsScreen::new;
}

@Override
public Map<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
return ImmutableMap.of("minecraft", parent -> new OptionsScreen(parent, MinecraftClient.getInstance().options));
return Map.of("minecraft", parent -> new OptionsScreen(parent, MinecraftClient.getInstance().options));
}

@Override
public Map<String, UpdateChecker> getProvidedUpdateCheckers() {
if (ModMenu.runningQuilt) {
return Map.of("quilt_loader", new QuiltLoaderUpdateChecker());
} else {
return Map.of("fabricloader", new FabricLoaderUpdateChecker());
}
}
}
13 changes: 11 additions & 2 deletions src/main/java/com/terraformersmc/modmenu/api/ModMenuApi.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.terraformersmc.modmenu.api;

import com.google.common.collect.ImmutableMap;
import com.terraformersmc.modmenu.ModMenu;
import com.terraformersmc.modmenu.gui.ModsScreen;
import net.minecraft.client.gui.screen.Screen;
Expand Down Expand Up @@ -66,7 +65,17 @@ default UpdateChecker getUpdateChecker() {
* @return a map of mod ids to screen factories.
*/
default Map<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
return ImmutableMap.of();
return Map.of();
}

/**
* Used to provide update checkers for other mods. A mod registering its own
* update checker will take priority over any provided ones should both exist.
*
* @return a map of mod ids to update checkers.
*/
default Map<String, UpdateChecker> getProvidedUpdateCheckers() {
return Map.of();
}

/**
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/com/terraformersmc/modmenu/util/HttpUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.terraformersmc.modmenu.util;

import java.io.IOException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

import com.terraformersmc.modmenu.ModMenu;

import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants;

public class HttpUtil {
private static final String USER_AGENT = buildUserAgent();
private static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();

private HttpUtil() {}

public static <T> HttpResponse<T> request(HttpRequest.Builder builder, HttpResponse.BodyHandler<T> handler) throws IOException, InterruptedException {
builder.setHeader("User-Agent", USER_AGENT);
return HTTP_CLIENT.send(builder.build(), handler);
}

private static String buildUserAgent() {
String env = ModMenu.devEnvironment ? "/development" : "";
String loader = ModMenu.runningQuilt ? "quilt" : "fabric";

var modMenuVersion = getModMenuVersion();
var minecraftVersion = SharedConstants.getGameVersion().getName();

// -> TerraformersMC/ModMenu/9.1.0 (1.20.3/quilt/development)
return "%s/%s (%s/%s%s)".formatted(ModMenu.GITHUB_REF, modMenuVersion, minecraftVersion, loader, env);
}

private static String getModMenuVersion() {
var container = FabricLoader.getInstance().getModContainer(ModMenu.MOD_ID);

if (container.isEmpty()) {
throw new RuntimeException("Unable to find Modmenu's own mod container!");
}

return VersionUtil.removeBuildMetadata(container.get().getMetadata().getVersion().getFriendlyString());
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/terraformersmc/modmenu/util/JsonUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.terraformersmc.modmenu.util;

import java.util.Optional;

import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;

public class JsonUtil {
private JsonUtil() {}

public static Optional<String> getString(JsonObject parent, String field) {
if (!parent.has(field)) {
return Optional.empty();
}

var value = parent.get(field);

if (!value.isJsonPrimitive() || !((JsonPrimitive)value).isString()) {
return Optional.empty();
}

return Optional.of(value.getAsString());
}

public static Optional<Boolean> getBoolean(JsonObject parent, String field) {
if (!parent.has(field)) {
return Optional.empty();
}

var value = parent.get(field);

if (!value.isJsonPrimitive() || !((JsonPrimitive)value).isBoolean()) {
return Optional.empty();
}

return Optional.of(value.getAsBoolean());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,19 @@
import net.minecraft.client.toast.SystemToast;
import net.minecraft.text.Text;
import net.minecraft.util.Util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.*;

public class UpdateCheckerUtil {
public static final Logger LOGGER = LoggerFactory.getLogger("Mod Menu/Update Checker");

private static final HttpClient client = HttpClient.newHttpClient();
private static boolean modrinthApiV2Deprecated = false;

private static boolean allowsUpdateChecks(Mod mod) {
Expand All @@ -48,10 +47,21 @@ public static void checkForUpdates() {
public static void checkForCustomUpdates() {
ModMenu.MODS.values().stream().filter(UpdateCheckerUtil::allowsUpdateChecks).forEach(mod -> {
UpdateChecker updateChecker = mod.getUpdateChecker();

if (updateChecker == null) {
return;
}
UpdateCheckerThread.run(mod, () -> mod.setUpdateInfo(updateChecker.checkForUpdates()));

UpdateCheckerThread.run(mod, () -> {
var update = updateChecker.checkForUpdates();

if (update == null) {
return;
}

mod.setUpdateInfo(update);
LOGGER.info("Update available for '{}@{}'", mod.getId(), mod.getVersion());
});
});
}

Expand All @@ -77,15 +87,8 @@ public static void checkForModrinthUpdates() {
}
});

String environment = ModMenu.devEnvironment ? "/development" : "";
String primaryLoader = ModMenu.runningQuilt ? "quilt" : "fabric";
List<String> loaders = ModMenu.runningQuilt ? List.of("fabric", "quilt") : List.of("fabric");

String mcVer = SharedConstants.getGameVersion().getName();
String version = FabricLoader.getInstance().getModContainer(ModMenu.MOD_ID)
.get().getMetadata().getVersion().getFriendlyString();
final var modMenuVersion = version.split("\\+", 2)[0]; // Strip build metadata for privacy
final var userAgent = "%s/%s (%s/%s%s)".formatted(ModMenu.GITHUB_REF, modMenuVersion, mcVer, primaryLoader, environment);
List<String> loaders = ModMenu.runningQuilt ? List.of("fabric", "quilt") : List.of("fabric");

List<UpdateChannel> updateChannels;
UpdateChannel preferredChannel = UpdateChannel.getUserPreference();
Expand All @@ -100,20 +103,17 @@ public static void checkForModrinthUpdates() {

String body = ModMenu.GSON_MINIFIED.toJson(new LatestVersionsFromHashesBody(modHashes.keySet(), loaders, mcVer, updateChannels));

LOGGER.debug("User agent: " + userAgent);
LOGGER.debug("Body: " + body);
LOGGER.debug("Body: {}", body);
var latestVersionsRequest = HttpRequest.newBuilder()
.POST(HttpRequest.BodyPublishers.ofString(body))
.header("User-Agent", userAgent)
.header("Content-Type", "application/json")
.uri(URI.create("https://api.modrinth.com/v2/version_files/update"))
.build();
.uri(URI.create("https://api.modrinth.com/v2/version_files/update"));

try {
var latestVersionsResponse = client.send(latestVersionsRequest, HttpResponse.BodyHandlers.ofString());
var latestVersionsResponse = HttpUtil.request(latestVersionsRequest, HttpResponse.BodyHandlers.ofString());

int status = latestVersionsResponse.statusCode();
LOGGER.debug("Status: " + status);
LOGGER.debug("Status: {}", status);
if (status == 410) {
modrinthApiV2Deprecated = true;
LOGGER.warn("Modrinth API v2 is deprecated, unable to check for mod updates.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
public final class VersionUtil {
private static final List<String> PREFIXES = List.of("version", "ver", "v");

private VersionUtil() {
return;
}
private VersionUtil() {}

public static String stripPrefix(String version) {
version = version.trim();
Expand All @@ -24,4 +22,8 @@ public static String stripPrefix(String version) {
public static String getPrefixedVersion(String version) {
return "v" + stripPrefix(version);
}

public static String removeBuildMetadata(String version) {
return version.split("\\+")[0];
}
}
Loading
Loading