4 files changed
+111
-49
No files matched your search
+14
-5
@@ -78,19 +78,27 @@ public class McbbsModpackExportTask extends Task<Void> {
|
||||
LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker");
|
||||
try (var zip = new Zipper(modpackFile)) {
|
||||
Path runDirectory = instance.getRunDirectory();
|
||||
List<McbbsModpackManifest.File> files = new ArrayList<>();
|
||||
zip.putDirectory(runDirectory, "overrides", path -> {
|
||||
|
||||
List<Path> paths = new ArrayList<>();
|
||||
zip.putDirectoryProgressed(runDirectory, "overrides", path -> {
|
||||
if (Modpack.acceptFile(path, blackList, info.getWhitelist())) {
|
||||
Path file = runDirectory.resolve(path);
|
||||
if (Files.isRegularFile(file)) {
|
||||
String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
||||
files.add(new McbbsModpackManifest.AddonFile(true, relativePath, DigestUtils.digestToString("SHA-1", file)));
|
||||
paths.add(file);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}, progress -> updateProgress(0.5 * progress));
|
||||
|
||||
List<McbbsModpackManifest.File> files = new ArrayList<>();
|
||||
long current = 0;
|
||||
for (Path file : paths) {
|
||||
String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
||||
files.add(new McbbsModpackManifest.AddonFile(true, relativePath, DigestUtils.digestToString("SHA-1", file)));
|
||||
updateProgress(0.5 + 0.5 * ++current / paths.size());
|
||||
}
|
||||
|
||||
GameVersionNumber version = instance.getVersion();
|
||||
if (version == GameVersionNumber.unknown()) {
|
||||
@@ -134,6 +142,7 @@ public class McbbsModpackExportTask extends Task<Void> {
|
||||
CurseManifest curseManifest = new CurseManifest(CurseManifest.MINECRAFT_MODPACK, 1, info.getName(), info.getVersion(), info.getAuthor(), "overrides", new CurseManifestMinecraft(gameVersion, modLoaders), Collections.emptyList());
|
||||
zip.putTextFile(JsonUtils.GSON.toJson(curseManifest), "manifest.json");
|
||||
}
|
||||
updateProgress(1);
|
||||
}
|
||||
|
||||
/// Export options supported by the MCBBS format.
|
||||
|
||||
+38
-36
@@ -2,14 +2,10 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH AdditionRef-HMCL
|
||||
package org.jackhuang.hmcl.modpack.modrinth;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
import org.jackhuang.hmcl.addon.LocalAddonManager;
|
||||
import org.jackhuang.hmcl.addon.RemoteAddon;
|
||||
import org.jackhuang.hmcl.addon.mod.ModManager;
|
||||
import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository;
|
||||
import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository;
|
||||
import org.jackhuang.hmcl.game.DefaultGameInstance;
|
||||
import org.jackhuang.hmcl.game.GameComponentAnalyzer;
|
||||
@@ -23,11 +19,16 @@ import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.io.Zipper;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jackhuang.hmcl.addon.RemoteAddon;
|
||||
import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/// Exports one registered game instance as a Modrinth modpack archive.
|
||||
@@ -114,10 +115,8 @@ public class ModrinthModpackExportTask extends Task<Void> {
|
||||
}
|
||||
|
||||
List<String> downloads = new ArrayList<>();
|
||||
if (modrinthVersion.isPresent())
|
||||
downloads.add(modrinthVersion.get().file().url());
|
||||
if (curseForgeVersion.isPresent())
|
||||
downloads.add(curseForgeVersion.get().file().url());
|
||||
modrinthVersion.ifPresent(version -> downloads.add(version.file().url()));
|
||||
curseForgeVersion.ifPresent(version -> downloads.add(version.file().url()));
|
||||
|
||||
long fileSize = Files.size(file);
|
||||
if (fileSize > Integer.MAX_VALUE) {
|
||||
@@ -145,39 +144,42 @@ public class ModrinthModpackExportTask extends Task<Void> {
|
||||
List<ModrinthManifest.File> files = new ArrayList<>();
|
||||
Set<String> filesInManifest = new HashSet<>();
|
||||
|
||||
String[] resourceDirs = {"resourcepacks", "shaderpacks", "mods"};
|
||||
for (String dir : resourceDirs) {
|
||||
Path dirPath = runDirectory.resolve(dir);
|
||||
if (Files.exists(dirPath)) {
|
||||
Files.walk(dirPath)
|
||||
.filter(Files::isRegularFile)
|
||||
.forEach(file -> {
|
||||
try {
|
||||
String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
||||
List<Path> resourceDirs = Stream.of("resourcepacks", "shaderpacks", "mods").map(runDirectory::resolve).filter(Files::exists).toList();
|
||||
|
||||
if (!info.getWhitelist().contains(relativePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModrinthManifest.File fileEntry = tryGetRemoteFile(file, relativePath);
|
||||
if (fileEntry != null) {
|
||||
files.add(fileEntry);
|
||||
filesInManifest.add(relativePath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to process file: " + file, e);
|
||||
}
|
||||
});
|
||||
List<Path> paths = new ArrayList<>();
|
||||
for (Path dir : resourceDirs) {
|
||||
try (var stream = Files.walk(dir)) {
|
||||
paths.addAll(stream.filter(Files::isRegularFile).toList());
|
||||
}
|
||||
}
|
||||
|
||||
zip.putDirectory(runDirectory, "client-overrides", path -> {
|
||||
long current = 0;
|
||||
for (var file : paths) {
|
||||
updateProgress(0.5 * ++current / paths.size());
|
||||
String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/');
|
||||
|
||||
if (!info.getWhitelist().contains(relativePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
ModrinthManifest.File fileEntry = tryGetRemoteFile(file, relativePath);
|
||||
if (fileEntry != null) {
|
||||
files.add(fileEntry);
|
||||
filesInManifest.add(relativePath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to process file: " + file, e);
|
||||
}
|
||||
}
|
||||
|
||||
zip.putDirectoryProgressed(runDirectory, "client-overrides", path -> {
|
||||
String relativePath = path.replace(File.separatorChar, '/');
|
||||
if (filesInManifest.contains(relativePath)) {
|
||||
return false;
|
||||
}
|
||||
return Modpack.acceptFile(path, blackList, info.getWhitelist());
|
||||
});
|
||||
}, progress -> updateProgress(0.5 + 0.5 * progress));
|
||||
|
||||
GameVersionNumber version = instance.getVersion();
|
||||
if (version == GameVersionNumber.unknown()) {
|
||||
|
||||
+2
-2
@@ -70,7 +70,7 @@ public class ServerModpackExportTask extends Task<Void> {
|
||||
try (Zipper zip = new Zipper(modpackFile)) {
|
||||
Path runDirectory = instance.getRunDirectory();
|
||||
List<ModpackConfiguration.FileInformation> files = new ArrayList<>();
|
||||
zip.putDirectory(runDirectory, "overrides", path -> {
|
||||
zip.putDirectoryProgressed(runDirectory, "overrides", path -> {
|
||||
if (Modpack.acceptFile(path, blackList, exportInfo.getWhitelist())) {
|
||||
Path file = runDirectory.resolve(path);
|
||||
if (Files.isRegularFile(file)) {
|
||||
@@ -81,7 +81,7 @@ public class ServerModpackExportTask extends Task<Void> {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}, this::updateProgress);
|
||||
|
||||
GameVersionNumber version = instance.getVersion();
|
||||
if (version == GameVersionNumber.unknown()) {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
package org.jackhuang.hmcl.util.io;
|
||||
|
||||
import org.jackhuang.hmcl.util.function.ExceptionalPredicate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -11,7 +12,10 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.DoubleConsumer;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
@@ -22,11 +26,12 @@ import java.util.zip.ZipOutputStream;
|
||||
*
|
||||
* @author huangyuhui
|
||||
*/
|
||||
@NotNullByDefault
|
||||
public final class Zipper implements Closeable {
|
||||
|
||||
private final ZipOutputStream zos;
|
||||
private final byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
|
||||
private final Set<String> entryNames;
|
||||
private final @Nullable Set<String> entryNames;
|
||||
|
||||
public Zipper(Path zipFile) throws IOException {
|
||||
this(zipFile, false);
|
||||
@@ -88,9 +93,9 @@ public final class Zipper implements Closeable {
|
||||
* @param targetDir the path of the directory in this zip file.
|
||||
* @param filter returns false if you do not want that file or directory
|
||||
*/
|
||||
public void putDirectory(Path source, String targetDir, ExceptionalPredicate<String, IOException> filter) throws IOException {
|
||||
public void putDirectory(Path source, String targetDir, @Nullable ExceptionalPredicate<String, IOException> filter) throws IOException {
|
||||
String root = normalize(targetDir);
|
||||
Files.walkFileTree(source, new SimpleFileVisitor<Path>() {
|
||||
Files.walkFileTree(source, new SimpleFileVisitor<>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (".DS_Store".equals(file.getFileName().toString())) {
|
||||
@@ -121,6 +126,52 @@ public final class Zipper implements Closeable {
|
||||
});
|
||||
}
|
||||
|
||||
/// Compress all the files in source with progress reporting.
|
||||
///
|
||||
/// @param source the directory to be compressed
|
||||
/// @param targetDir the path of the directory in this zip file
|
||||
/// @param filter returns false to skip a file or directory, or null to accept all
|
||||
/// @param onProgressed receives byte-weighted progress from 0.0 to 1.0
|
||||
public void putDirectoryProgressed(Path source, String targetDir, @Nullable ExceptionalPredicate<String, IOException> filter, DoubleConsumer onProgressed) throws IOException {
|
||||
Map<Path, Long> entries = new LinkedHashMap<>();
|
||||
Files.walkFileTree(source, new SimpleFileVisitor<>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (".DS_Store".equals(file.getFileName().toString())) {
|
||||
return FileVisitResult.SKIP_SUBTREE;
|
||||
}
|
||||
String relativePath = normalize(source.relativize(file).normalize().toString());
|
||||
if (filter != null && !filter.test(relativePath)) {
|
||||
return FileVisitResult.SKIP_SUBTREE;
|
||||
}
|
||||
entries.put(file, Math.max(1L, attrs.size()));
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
String relativePath = normalize(source.relativize(dir).normalize().toString());
|
||||
if (filter != null && !filter.test(relativePath)) {
|
||||
return FileVisitResult.SKIP_SUBTREE;
|
||||
}
|
||||
entries.put(dir, 0L);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
|
||||
String root = normalize(targetDir);
|
||||
long total = entries.values().stream().mapToLong(Long::longValue).sum();
|
||||
long completed = 0;
|
||||
onProgressed.accept(0.0);
|
||||
for (var entry : entries.entrySet()) {
|
||||
String relativePath = normalize(source.relativize(entry.getKey()).normalize().toString());
|
||||
putFile(entry.getKey(), resolve(root, relativePath));
|
||||
if (entry.getValue() > 0)
|
||||
onProgressed.accept((double) (completed += entry.getValue()) / total);
|
||||
}
|
||||
onProgressed.accept(1.0);
|
||||
}
|
||||
|
||||
public void putFile(Path file, String path) throws IOException {
|
||||
path = normalize(path);
|
||||
|
||||
@@ -160,11 +211,11 @@ public final class Zipper implements Closeable {
|
||||
zos.write(b);
|
||||
}
|
||||
|
||||
public void write(@NotNull byte[] b) throws IOException {
|
||||
public void write(byte[] b) throws IOException {
|
||||
zos.write(b);
|
||||
}
|
||||
|
||||
public void write(@NotNull byte[] b, int off, int len) throws IOException {
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
zos.write(b, off, len);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user