Skip to content

Commit

Permalink
Added compatiiblity for modpacks from curseforge
Browse files Browse the repository at this point in the history
  • Loading branch information
FlowArg committed Oct 21, 2020
1 parent 61a9e4b commit 4138427
Show file tree
Hide file tree
Showing 20 changed files with 362 additions and 111 deletions.
2 changes: 1 addition & 1 deletion CurseForgePlugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ shadowJar {
configurations = [project.configurations.shadow]
}

version '1.0.0'
version '1.0.1'
archivesBaseName = "CurseForgePlugin"

dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
package fr.flowarg.flowupdater.curseforgeplugin;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.therandomlabs.curseapi.CurseAPI;
import com.therandomlabs.curseapi.CurseException;
import com.therandomlabs.curseapi.util.OkHttpUtils;
import fr.flowarg.flowio.FileUtils;
import fr.flowarg.pluginloaderapi.plugin.Plugin;
import okhttp3.HttpUrl;

import javax.net.ssl.HttpsURLConnection;
import java.io.IOException;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class CurseForgePlugin extends Plugin
{
Expand All @@ -21,7 +34,7 @@ public void onStart()
this.getLogger().info("Starting CFP (CurseForgePlugin) for FlowUpdater...");
}

public URL getURLOfMod(int projectID, int fileID)
public URL getURLOfFile(int projectID, int fileID)
{
try
{
Expand All @@ -35,7 +48,7 @@ public URL getURLOfMod(int projectID, int fileID)

public CurseMod getCurseMod(int projectID, int fileID)
{
final URL url = this.getURLOfMod(projectID, fileID);
final URL url = this.getURLOfFile(projectID, fileID);
HttpsURLConnection connection = null;

try
Expand All @@ -45,10 +58,93 @@ public CurseMod getCurseMod(int projectID, int fileID)
connection.setInstanceFollowRedirects(true);
connection.setUseCaches(false);
final String md5 = connection.getHeaderField("ETag").replace("\"", "");
final int length = Integer.parseInt(connection.getHeaderField("Content-Length"));
final int length = Integer.parseInt(connection.getHeaderField("Content-Length"));
return new CurseMod(url.getFile().substring(url.getFile().lastIndexOf('/') + 1), url.toExternalForm(), md5, length);
} catch (Exception e)
{
this.getLogger().printStackTrace(e);
} finally
{
if (connection != null) connection.disconnect();
}
catch (Exception e)

return new CurseMod("", "", "", -1);
}

public CurseModPack getCurseModPack(int projectID, int fileID, boolean installExtFiles)
{
final URL link = this.getURLOfFile(projectID, fileID);
final String linkStr = link.toExternalForm();

HttpsURLConnection connection = null;

try
{
connection = (HttpsURLConnection)link.openConnection();
connection.setRequestMethod("GET");
connection.setInstanceFollowRedirects(true);
connection.setUseCaches(false);
final String md5 = connection.getHeaderField("ETag").replace("\"", "");
final File out = new File(this.getDataPluginFolder(), linkStr.substring(linkStr.lastIndexOf('/') + 1));

if(!out.exists() || !FileUtils.getMD5ofFile(out).equalsIgnoreCase(md5))
{
this.getLogger().info(String.format("Downloading %s from %s...", out.getName(), link.toExternalForm()));
out.getParentFile().mkdirs();
Files.copy(this.catchForbidden(link), out.toPath(), StandardCopyOption.REPLACE_EXISTING);
}

this.getLogger().info("Extracting mod pack...");
final ZipFile zipFile = new ZipFile(out, ZipFile.OPEN_READ, StandardCharsets.UTF_8);
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
final File dir = this.getDataPluginFolder().getParentFile().getParentFile();
while (entries.hasMoreElements())
{
final ZipEntry entry = entries.nextElement();
final File fl = new File(dir, entry.getName().replace("overrides/", ""));
if(entry.getName().equalsIgnoreCase("manifest.json") && fl.exists() && entry.getCrc() == FileUtils.getCRC32(fl))
break;
if(installExtFiles && !entry.getName().equals("modlist.html"))
{
if(!fl.exists())
{
if (fl.getName().endsWith(File.separator)) fl.mkdirs();
if (!fl.exists()) fl.getParentFile().mkdirs();
if (entry.isDirectory()) continue;

final InputStream is = zipFile.getInputStream(entry);
final BufferedOutputStream fo = new BufferedOutputStream(new FileOutputStream(fl));
while (is.available() > 0) fo.write(is.read());
fo.close();
is.close();
}
}
else if(entry.getName().equals("manifest.json"))
{
final InputStream is = zipFile.getInputStream(entry);
final BufferedOutputStream fo = new BufferedOutputStream(new FileOutputStream(fl));
while (is.available() > 0) fo.write(is.read());
fo.close();
is.close();
}
}
zipFile.close();

final BufferedReader reader = new BufferedReader(new FileReader(new File(dir, "manifest.json")));
final JsonObject obj = JsonParser.parseReader(reader).getAsJsonObject();
final String name = obj.get("name").getAsString();
final String version = obj.get("version").getAsString();
final String author = obj.get("author").getAsString();
final List<CurseModPack.CurseModPackMod> mods = new ArrayList<>();

this.getLogger().info("Fetching mods...");
obj.getAsJsonArray("files").forEach(jsonElement -> {
final JsonObject object = jsonElement.getAsJsonObject();
mods.add(new CurseModPack.CurseModPackMod(this.getCurseMod(object.get("projectID").getAsInt(), object.get("fileID").getAsInt()), object.get("required").getAsBoolean()));
});
reader.close();
return new CurseModPack(name, version, author, mods, installExtFiles);
} catch (Exception e)
{
this.getLogger().printStackTrace(e);
}
Expand All @@ -57,13 +153,12 @@ public CurseMod getCurseMod(int projectID, int fileID)
if(connection != null)
connection.disconnect();
}

return new CurseMod("", "", "", -1);
return new CurseModPack("", "", "", Collections.emptyList(), false);
}

public void shutdownOKHTTP()
{
if (OkHttpUtils.getClient() != null)
if(OkHttpUtils.getClient() != null)
{
OkHttpUtils.getClient().dispatcher().executorService().shutdown();
OkHttpUtils.getClient().connectionPool().evictAll();
Expand All @@ -77,6 +172,14 @@ public void shutdownOKHTTP()
}
}

public InputStream catchForbidden(URL url) throws IOException
{
final HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.addRequestProperty("User-Agent", "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36");
connection.setInstanceFollowRedirects(true);
return connection.getInputStream();
}

@Override
public void onStop()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,14 @@ public class CurseMod
private final String md5;
private final int length;

public CurseMod(String name, String downloadURL, String md5, int length)
CurseMod(String name, String downloadURL, String md5, int length)
{
this.name = name;
this.downloadURL = downloadURL;
this.md5 = md5;
this.length = length;
}

public static CurseMod fromAPI(int projectID, int fileID)
{
return CurseForgePlugin.instance.getCurseMod(projectID, fileID);
}

public String getName()
{
return this.name;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package fr.flowarg.flowupdater.curseforgeplugin;

import java.util.List;

public class CurseModPack
{
private final String name;
private final String version;
private final String author;
private final List<CurseModPackMod> mods;
private final boolean installExtFiles;

public CurseModPack(String name, String version, String author, List<CurseModPackMod> mods, boolean installExtFiles)
{
this.name = name;
this.version = version;
this.author = author;
this.mods = mods;
this.installExtFiles = installExtFiles;
}

public String getName()
{
return this.name;
}

public String getVersion()
{
return this.version;
}

public String getAuthor()
{
return this.author;
}

public List<CurseModPackMod> getMods()
{
return this.mods;
}

public boolean isInstallExtFiles()
{
return this.installExtFiles;
}

public static class CurseModPackMod extends CurseMod
{
private final boolean required;

CurseModPackMod(String name, String downloadURL, String md5, int length, boolean required)
{
super(name, downloadURL, md5, length);
this.required = required;
}

CurseModPackMod(CurseMod base, boolean required)
{
this(base.getName(), base.getDownloadURL(), base.getMd5(), base.getLength(), required);
}

public boolean isRequired()
{
return this.required;
}
}
}
2 changes: 1 addition & 1 deletion CurseForgePlugin/src/main/resources/manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "CurseForgePlugin",
"pluginClass": "fr.flowarg.flowupdater.curseforgeplugin.CurseForgePlugin",
"version": "1.0.0"
"version": "1.0.1"
}
25 changes: 18 additions & 7 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
In your block `repositories`, add this lines :
```groovy
repositories {
jcenter()
jcenter()
maven {
url "https://jitpack.io/"
name = "JitPack"
}
}
```

Expand All @@ -39,9 +43,15 @@ VanillaVersion version = new VanillaVersionBuilder().withName("1.15.2").withSnap
You have to put the version you want as parameter, you can set a snapshot if you want or `latest`.
If you have specified "latest" to the version name, and that the version is a snapshot, replace false by true.

Then, instantiate a new FlowUpdater with ``FlowUpdaterBuilder#withXArguments#withAnotherArguemtn#build``. Check the code for more informations.
Then, instantiate a new FlowUpdater with ``FlowUpdaterBuilder#withXArguments#withAnotherArguemtn#build``. Check the code for more information.
The most of FlowUpdater objects are buildable:
Build a new UpdaterOptions object:
I'm enabling the re-extracting of natives at each update.
```java
FlowUpdater updater = new FlowUpdaterBuilder().withVersion(version).withUpdaterOptions(new UpdaterOptions(false|true, true|false, false)).build();
UpdaterOptions options = new UpdaterOptionsBuilder().withReExtractNatives(true).build();
```
```java
FlowUpdater updater = new FlowUpdaterBuilder().withVersion(version).withUpdaterOptions(options).build();
```

Don't forget to add a progress callback if you want to make a progress bar !
Expand All @@ -67,11 +77,11 @@ You can get a list of mods by providing a json link too : `List<Mod> mods = Mod.

You can get mods from CurseForge too:
```java
List<CurseModInfos> modInfos = new ArrayList<>();
List<CurseFileInfos> modInfos = new ArrayList<>();
// project ID and file ID
modInfos.add(new CurseModInfos(238222, 2988823));
```
You can get a list of curse mods by providing a json link too : `List<CurseModInfos> mods = CurseModInfos.getCurseModsFromJson("https://url.com/launcher/cursemods.json");`.
You can get a list of curse mods by providing a json link too : `List<CurseFileInfos> mods = CurseFileInfos.getFilesFromJson("https://url.com/launcher/cursemods.json");`.

```java
// On updater options :
Expand All @@ -86,6 +96,7 @@ AbstractForgeVersion forgeVersion = new ForgeVersionBuilder(ForgeVersionBuilder.
.withLogger(logger)
.withProgressCallback(callback)
.withCurseMods(modInfos)
.withOptifine("1.16.3_HD_U_G3") // installing optifine for 1.16.3
.withUseFileDeleter(true) // delete bad mods
.withNoGui(true) // only for new forge version: true -> don't show the forge installer gui. false -> show the forge installer gui.
.build();
Expand Down Expand Up @@ -128,15 +139,15 @@ All json files can be generated by the [FlowUpdaterJsonCreator](https://github.c

### Post executions

With FlowUpdater, you can execute some actions after update, like patch a file, kill a process, launch a process, review a config etc etc...
With FlowUpdater, you can execute some actions after update, like patch a file, kill a process, launch a process, review a config etc...
In your FlowUpdaterBuilder, precise a list of Runnable.

And all it's done !

### Informations

This library has some dependencies :
- [FlowMultitools](https://github.com/FlowArg/FlowMultitools).
- [FlowMultiTools](https://github.com/FlowArg/FlowMultitools).
- [PluginLoaderAPI](https://github.com/FlowArg/PluginLoaderAPI).
- [Gson](https://github.com/Google/Gson).

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ allprojects {
}
}

version '1.2.5'
version '1.2.6'
archivesBaseName = "flowupdater"

artifacts {
Expand Down
Loading

0 comments on commit 4138427

Please sign in to comment.