Compare commits

...

2 Commits

Author SHA1 Message Date
d6420cdf5f 1.0.3
All checks were successful
ModUpdater/pipeline/head This commit looks good
2020-06-24 14:21:26 -04:00
0a941684f8 Multi-Threading 2020-06-24 14:03:02 -04:00
11 changed files with 228 additions and 40 deletions

View File

@ -1,5 +1,9 @@
# Changelog # Changelog
**1.0.3**
* Add Support For GitHub Releases
* Use Multi-Threading
**1.0.2** **1.0.2**
* Improve Errors * Improve Errors
* Improve GUI * Improve GUI

View File

@ -35,6 +35,19 @@ Place this in your ``fabric.mod.json``:
} }
``` ```
**GitHub Releases**
```json
{
"custom": {
"modupdater": {
"strategy": "github",
"owner": "Repository Owner",
"repository": "Repository Name"
}
}
}
```
Also replace this in ````build.gradle````: Also replace this in ````build.gradle````:
```gradle ```gradle
version = project.mod_version version = project.mod_version
@ -48,4 +61,5 @@ version = "${project.mod_version}+${project.minecraft_version}"
[View Changelog](CHANGELOG.md) [View Changelog](CHANGELOG.md)
## Credits ## Credits
The icon was created by ``ProspectorDev``. - The icon was created by ``ProspectorDev``
- The GitHub Releases strategy was written by ``AppleTheGolden``

View File

@ -9,7 +9,7 @@ org.gradle.jvmargs = -Xmx1G
fabric_loader_version = 0.8.8+build.202 fabric_loader_version = 0.8.8+build.202
# Mod Properties # Mod Properties
mod_version = 1.0.2 mod_version = 1.0.3
maven_group = com.thebrokenrail maven_group = com.thebrokenrail
# Dependencies # Dependencies

View File

@ -6,6 +6,8 @@ import net.fabricmc.api.ModInitializer;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.util.Objects;
public class ModUpdater implements ModInitializer { public class ModUpdater implements ModInitializer {
public static final String NAMESPACE = "modupdater"; public static final String NAMESPACE = "modupdater";
@ -19,21 +21,30 @@ public class ModUpdater implements ModInitializer {
getLogger().warn("Invalid JSON Configuration: " + modID); getLogger().warn("Invalid JSON Configuration: " + modID);
} }
private static ModUpdate[] updates; private static volatile ModUpdate[] updates;
private static Thread updateThread;
public static ModUpdate[] getUpdates() { public static ModUpdate[] getUpdates() {
if (updates == null) { if (updates == null) {
if (Thread.currentThread() == updateThread) {
updates = ModUpdateStrategies.findAvailableUpdates(); updates = ModUpdateStrategies.findAvailableUpdates();
} else {
return null;
}
} }
return updates; return updates;
} }
@Override @Override
public void onInitialize() { public void onInitialize() {
updateThread = new Thread(() -> {
getLogger().info("Checking For Mod Updates..."); getLogger().info("Checking For Mod Updates...");
for (ModUpdate update : getUpdates()) { for (ModUpdate update : Objects.requireNonNull(getUpdates())) {
getLogger().info(update.text + " (" + update.downloadURL + ')'); getLogger().info(update.text + " (" + update.downloadURL + ')');
} }
getLogger().info(updates.length + " Mod Update(s) Found"); getLogger().info(updates.length + " Mod Update(s) Found");
});
updateThread.start();
} }
} }

View File

@ -14,9 +14,11 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.TranslatableText; import net.minecraft.text.TranslatableText;
import net.minecraft.util.Util; import net.minecraft.util.Util;
import java.util.Arrays;
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
public class ModUpdateScreen extends Screen { public class ModUpdateScreen extends Screen {
private ModUpdateListWidget list; public ModUpdateListWidget list;
private ButtonWidget download; private ButtonWidget download;
private final Screen parent; private final Screen parent;
@ -56,15 +58,27 @@ public class ModUpdateScreen extends Screen {
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
private static class ModUpdateListWidget extends EntryListWidget<ModUpdateEntry> { private static class ModUpdateListWidget extends EntryListWidget<ModUpdateEntry> {
private final ModUpdateScreen screen; private final ModUpdateScreen screen;
private ModUpdate[] updates = null;
private ModUpdateListWidget(MinecraftClient client, ModUpdateScreen screen) { private ModUpdateListWidget(MinecraftClient client, ModUpdateScreen screen) {
super(client, screen.width, screen.height, 32, screen.height - 40, 18); super(client, screen.width, screen.height, 32, screen.height - 40, 18);
this.screen = screen; this.screen = screen;
for (ModUpdate update : ModUpdater.getUpdates()) { reload();
}
public void reload() {
ModUpdate[] newUpdates = ModUpdater.getUpdates();
if (!Arrays.equals(updates, newUpdates)) {
clearEntries();
if (newUpdates != null) {
for (ModUpdate update : newUpdates) {
addEntry(new ModUpdateEntry(update, screen, this)); addEntry(new ModUpdateEntry(update, screen, this));
} }
} }
updates = newUpdates;
}
}
@Override @Override
public int getRowWidth() { public int getRowWidth() {
@ -100,6 +114,15 @@ public class ModUpdateScreen extends Screen {
protected boolean isFocused() { protected boolean isFocused() {
return screen.getFocused() == this; return screen.getFocused() == this;
} }
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
reload();
super.render(matrices, mouseX, mouseY, delta);
if (updates == null) {
drawCenteredText(matrices, screen.textRenderer, new TranslatableText("gui.modupdater.loading"), width / 2, height / 2 - screen.textRenderer.fontHeight, 16777215);
}
}
} }
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)

View File

@ -2,6 +2,7 @@ package com.thebrokenrail.modupdater.strategy;
import com.mojang.bridge.game.GameVersion; import com.mojang.bridge.game.GameVersion;
import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.Moshi; import com.squareup.moshi.Moshi;
import com.thebrokenrail.modupdater.ModUpdater; import com.thebrokenrail.modupdater.ModUpdater;
import com.thebrokenrail.modupdater.util.ConfigObject; import com.thebrokenrail.modupdater.util.ConfigObject;
@ -43,6 +44,9 @@ class CurseForgeStrategy implements ModUpdateStrategy {
} catch (IOException e) { } catch (IOException e) {
ModUpdater.getLogger().warn("Unable To Access CurseForge: " + name); ModUpdater.getLogger().warn("Unable To Access CurseForge: " + name);
return null; return null;
} catch (JsonDataException e) {
ModUpdater.getLogger().warn("CurseForge Sent Invalid Data: ", e);
return null;
} }
if (files == null) { if (files == null) {
@ -59,6 +63,7 @@ class CurseForgeStrategy implements ModUpdateStrategy {
CurseForgeFile newestFile = null; CurseForgeFile newestFile = null;
for (CurseForgeFile file : files) { for (CurseForgeFile file : files) {
if (Util.isFileCompatible(file.fileName)) {
String fileVersion = Util.getVersionFromFileName(file.fileName); String fileVersion = Util.getVersionFromFileName(file.fileName);
if (Arrays.asList(file.gameVersion).contains(versionStr) || Util.isVersionCompatible(fileVersion)) { if (Arrays.asList(file.gameVersion).contains(versionStr) || Util.isVersionCompatible(fileVersion)) {
if (newestFile != null) { if (newestFile != null) {
@ -74,6 +79,7 @@ class CurseForgeStrategy implements ModUpdateStrategy {
} }
} }
} }
}
if (newestFile != null) { if (newestFile != null) {
String newestFileVersion = Util.getVersionFromFileName(newestFile.fileName); String newestFileVersion = Util.getVersionFromFileName(newestFile.fileName);

View File

@ -0,0 +1,97 @@
package com.thebrokenrail.modupdater.strategy;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.Moshi;
import com.thebrokenrail.modupdater.ModUpdater;
import com.thebrokenrail.modupdater.util.ConfigObject;
import com.thebrokenrail.modupdater.util.ModUpdate;
import com.thebrokenrail.modupdater.util.ModUpdateStrategy;
import com.thebrokenrail.modupdater.util.Util;
import net.fabricmc.loader.api.SemanticVersion;
import net.fabricmc.loader.util.version.VersionParsingException;
import java.io.IOException;
public class GitHubReleasesStrategy implements ModUpdateStrategy {
@SuppressWarnings({"unused", "MismatchedReadAndWriteOfArray"})
private static class GitHubRelease {
private GitHubReleaseAsset[] assets;
}
@SuppressWarnings("unused")
private static class GitHubReleaseAsset {
private String name;
private String browser_download_url;
}
@Override
public ModUpdate checkForUpdate(ConfigObject obj, String oldVersion, String name) {
String owner;
String repo;
try {
owner = obj.getString("owner");
repo = obj.getString("repository");
} catch (ConfigObject.MissingValueException e) {
ModUpdater.invalidModUpdaterConfig(name);
return null;
}
String data = Util.urlToString(String.format("https://api.github.com/repos/%s/%s/releases", owner, repo));
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<GitHubRelease[]> jsonAdapter = moshi.adapter(GitHubRelease[].class);
GitHubRelease[] releases;
try {
// GitHub's API never omits values, they're always null
releases = jsonAdapter.nonNull().fromJson(data);
} catch (IOException e) {
ModUpdater.getLogger().warn("Unable To Access GitHub: " + name);
return null;
} catch (JsonDataException e) {
ModUpdater.getLogger().warn("GitHub Sent Invalid Data: ", e);
return null;
}
if (releases == null) {
return null;
}
GitHubReleaseAsset newestFile = null;
for (GitHubRelease release : releases) {
for (GitHubReleaseAsset asset : release.assets) {
if (Util.isFileCompatible(asset.name)) {
String fileVersion = Util.getVersionFromFileName(asset.name);
if (Util.isVersionCompatible(fileVersion)) {
if (newestFile != null) {
try {
if (SemanticVersion.parse(fileVersion).compareTo(SemanticVersion.parse(fileVersion)) > 0) {
newestFile = asset;
}
} catch (VersionParsingException ignored) {
}
} else {
newestFile = asset;
}
}
}
}
}
if (newestFile != null) {
String newestFileVersion = Util.getVersionFromFileName(newestFile.name);
try {
if (SemanticVersion.parse(newestFileVersion).compareTo(SemanticVersion.parse(oldVersion)) > 0) {
return new ModUpdate(oldVersion, newestFileVersion, newestFile.browser_download_url, name);
} else {
return null;
}
} catch (VersionParsingException e) {
return null;
}
} else {
return null;
}
}
}

View File

@ -31,7 +31,7 @@ public class MavenStrategy implements ModUpdateStrategy {
return null; return null;
} }
String mavenRoot = repository + '/' + group.replaceAll("\\.", "/") + '/' + artifact; String mavenRoot = String.format("%s/%s/%s", repository, group.replaceAll("\\.", "/"), artifact);
Document doc; Document doc;
try { try {

View File

@ -13,6 +13,7 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class ModUpdateStrategies { public class ModUpdateStrategies {
private static final Map<String, ModUpdateStrategy> data = new HashMap<>(); private static final Map<String, ModUpdateStrategy> data = new HashMap<>();
@ -40,7 +41,10 @@ public class ModUpdateStrategies {
public static ModUpdate[] findAvailableUpdates() { public static ModUpdate[] findAvailableUpdates() {
List<ModUpdate> updates = new ArrayList<>(); List<ModUpdate> updates = new ArrayList<>();
AtomicInteger remaining = new AtomicInteger(0);
for (ModContainer mod : FabricLoader.getInstance().getAllMods()) { for (ModContainer mod : FabricLoader.getInstance().getAllMods()) {
Thread thread = new Thread(() -> {
ModMetadata metadata = mod.getMetadata(); ModMetadata metadata = mod.getMetadata();
String name = metadata.getName() + " (" + metadata.getId() + ')'; String name = metadata.getName() + " (" + metadata.getId() + ')';
@ -59,15 +63,37 @@ public class ModUpdateStrategies {
} }
if (update != null) { if (update != null) {
synchronized (updates) {
updates.add(update); updates.add(update);
} }
} }
synchronized (remaining) {
remaining.decrementAndGet();
remaining.notifyAll();
}
});
synchronized (remaining) {
remaining.incrementAndGet();
}
thread.start();
}
synchronized (remaining) {
while (remaining.get() > 0) {
try {
remaining.wait();
} catch (InterruptedException ignored) {
}
}
}
return updates.toArray(new ModUpdate[0]); return updates.toArray(new ModUpdate[0]);
} }
static { static {
data.put("curseforge", new CurseForgeStrategy()); data.put("curseforge", new CurseForgeStrategy());
data.put("maven", new MavenStrategy()); data.put("maven", new MavenStrategy());
data.put("github", new GitHubReleasesStrategy());
} }
} }

View File

@ -35,8 +35,10 @@ public class Util {
public static final String JAR_EXTENSION = ".jar"; public static final String JAR_EXTENSION = ".jar";
public static String getVersionFromFileName(String fileName) { public static String getVersionFromFileName(String fileName) {
while (!Character.isDigit(fileName.charAt(0))) {
int index = fileName.indexOf("-"); int index = fileName.indexOf("-");
fileName = fileName.substring(index != -1 ? index + 1 : 0); fileName = fileName.substring(index != -1 ? index + 1 : 0);
}
if (fileName.endsWith(JAR_EXTENSION)) { if (fileName.endsWith(JAR_EXTENSION)) {
fileName = fileName.substring(0, fileName.length() - JAR_EXTENSION.length()); fileName = fileName.substring(0, fileName.length() - JAR_EXTENSION.length());
} }
@ -73,6 +75,10 @@ public class Util {
return isVersionCompatible(versionStr,'+') || isVersionCompatible(versionStr, '-'); return isVersionCompatible(versionStr,'+') || isVersionCompatible(versionStr, '-');
} }
public static boolean isFileCompatible(String fileName) {
return !fileName.endsWith("-dev" + JAR_EXTENSION) && !fileName.endsWith("-sources" + JAR_EXTENSION) && !fileName.endsWith("-sources-dev" + JAR_EXTENSION);
}
public static GameVersion getMinecraftVersion() { public static GameVersion getMinecraftVersion() {
updateMinecraftVersion(); updateMinecraftVersion();
return minecraftVersion; return minecraftVersion;

View File

@ -1,4 +1,5 @@
{ {
"gui.modupdater.title": "Available Mod Updates", "gui.modupdater.title": "Available Mod Updates",
"gui.modupdater.download": "Download" "gui.modupdater.download": "Download",
"gui.modupdater.loading": "Loading..."
} }