Rework exporting
This commit is contained in:
parent
da621e0eba
commit
f7a3d5be53
|
@ -28,7 +28,7 @@ println("Using Inceptum Build Script $version")
|
|||
|
||||
val lwjglVersion by extra("3.3.1")
|
||||
val imguiVersion by extra("1.86.4")
|
||||
val jfCommonsVersion by extra("2022.9.6+19-13-24")
|
||||
val jfCommonsVersion by extra("2022.9.18+12-38-21")
|
||||
val jgitVersion by extra("6.2.0.202206071550-r")
|
||||
val flavorProp: String by extra(if (project.hasProperty("flavor")) "${project.property("flavor")}" else "custom")
|
||||
if (flavorProp != "custom" && flavorProp != "maven" && flavorProp != "fat" && flavorProp != "windows" && flavorProp != "linux" && flavorProp != "macos")
|
||||
|
|
|
@ -9,7 +9,7 @@ public class MetaHolder {
|
|||
|
||||
static {
|
||||
if (System.getProperty("inceptum.base") == null) {
|
||||
Path runDir = Path.of(".").resolve("run").toAbsolutePath();
|
||||
Path runDir = getPath("run");
|
||||
if (!BuildMetadata.IS_RELEASE) BASE_PATH = runDir;
|
||||
else if (Files.exists(runDir)) BASE_PATH = runDir;
|
||||
else {
|
||||
|
@ -43,7 +43,7 @@ public class MetaHolder {
|
|||
private static boolean isWrapper = false;
|
||||
|
||||
private static Path getPath(String text) {
|
||||
return Paths.get(text).toAbsolutePath();
|
||||
return Paths.get(text).toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
public static void setWrapperFlag() {
|
||||
|
|
|
@ -3,9 +3,9 @@ package io.gitlab.jfronny.inceptum.cli.commands;
|
|||
import io.gitlab.jfronny.inceptum.cli.*;
|
||||
import io.gitlab.jfronny.inceptum.common.R;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.export.InstanceExporter;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.export.Exporters;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -15,6 +15,7 @@ import java.util.List;
|
|||
public class ExportCommand extends BaseInstanceCommand {
|
||||
public ExportCommand() {
|
||||
this(List.of("export"), List.of(
|
||||
new ExportCommand(List.of("curseforge", "cf"), List.of()),
|
||||
new MultiMCExportCommand(List.of("multimc", "mmc"), List.of())
|
||||
));
|
||||
}
|
||||
|
@ -31,7 +32,7 @@ public class ExportCommand extends BaseInstanceCommand {
|
|||
ProcessState state = new ProcessState();
|
||||
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
|
||||
mds.runOnce(R::nop);
|
||||
InstanceExporter.exportCurseZip(state, instancePath, meta, mds, Paths.get(args.get(0)), args.get(1));
|
||||
Exporters.CURSE_FORGE.generate(state, instancePath, meta, mds, Paths.get(args.get(0)), args.get(1));
|
||||
}
|
||||
|
||||
private static class MultiMCExportCommand extends BaseInstanceCommand {
|
||||
|
@ -46,7 +47,7 @@ public class ExportCommand extends BaseInstanceCommand {
|
|||
ProcessState state = new ProcessState();
|
||||
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
|
||||
mds.runOnce(R::nop);
|
||||
InstanceExporter.exportMultiMCZip(state, instancePath, meta, mds, Paths.get(args.get(0)));
|
||||
Exporters.MULTI_MC.generate(state, instancePath, meta, mds, Paths.get(args.get(0)), "1.0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.gitlab.jfronny.inceptum.imgui;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.imgui.window.MicrosoftLoginWindow;
|
||||
import io.gitlab.jfronny.inceptum.imgui.window.dialog.AlertWindow;
|
||||
|
@ -20,7 +21,7 @@ public class GuiEnvBackend implements LauncherEnv.EnvBackend {
|
|||
@Override
|
||||
public void showError(String message, Throwable t) {
|
||||
Utils.LOGGER.error(message, t);
|
||||
GuiMain.WINDOWS.add(new AlertWindow(message, t.toString()));
|
||||
GuiMain.WINDOWS.add(new AlertWindow(message, StringFormatter.toString(t)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -179,8 +179,7 @@ public class GuiMain {
|
|||
io.addConfigFlags(ImGuiConfigFlags.ViewportsEnable);
|
||||
io.setConfigViewportsNoAutoMerge(true);
|
||||
try (InputStream is = LauncherEnv.class.getClassLoader().getResourceAsStream("font.ttf")) {
|
||||
assert is != null;
|
||||
io.setFontDefault(io.getFonts().addFontFromMemoryTTF(is.readAllBytes(), 16f));
|
||||
io.setFontDefault(io.getFonts().addFontFromMemoryTTF(Objects.requireNonNull(is).readAllBytes(), 16f));
|
||||
} catch (IOException e) {
|
||||
Utils.LOGGER.error("Could not load font", e);
|
||||
}
|
||||
|
@ -202,8 +201,15 @@ public class GuiMain {
|
|||
for (Window window : WINDOWS.toArray(new Window[0])) {
|
||||
if (window.isNew()) window.preFirstDraw();
|
||||
String title = window.getName() + "##" + System.identityHashCode(window);
|
||||
if (ImGui.begin(title, window.getOpenState(), window.getFlags()))
|
||||
window.draw();
|
||||
if (window.isCloseable()) {
|
||||
if (ImGui.begin(title, window.getOpenState(), window.getFlags())) {
|
||||
window.draw();
|
||||
}
|
||||
} else {
|
||||
if (ImGui.begin(title, window.getFlags())) {
|
||||
window.draw();
|
||||
}
|
||||
}
|
||||
ImGui.end();
|
||||
if (!window.getOpenState().get() && !window.isClosed()) window.close();
|
||||
}
|
||||
|
|
|
@ -51,6 +51,10 @@ public abstract class Window implements Closeable {
|
|||
return openState;
|
||||
}
|
||||
|
||||
public boolean isCloseable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public enum State {
|
||||
New, Open, Closed
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.gitlab.jfronny.inceptum.imgui.window.dialog;
|
||||
|
||||
import imgui.ImGui;
|
||||
import imgui.flag.ImGuiWindowFlags;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
|
||||
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
|
||||
import io.gitlab.jfronny.inceptum.imgui.window.Window;
|
||||
|
@ -13,7 +14,6 @@ public class ProcessStateWatcherWindow extends Window {
|
|||
private final ProcessState state;
|
||||
private final Runnable cancel;
|
||||
private final AtomicBoolean canceled = new AtomicBoolean(false);
|
||||
private boolean finished;
|
||||
|
||||
public ProcessStateWatcherWindow(String title, String errorMessage, ProcessState state, ThrowingConsumer<AtomicBoolean, ?> executor, @Nullable Runnable cancel) {
|
||||
super(title);
|
||||
|
@ -22,12 +22,12 @@ public class ProcessStateWatcherWindow extends Window {
|
|||
new Thread(() -> {
|
||||
try {
|
||||
executor.accept(canceled);
|
||||
if (canceled.get() && cancel != null) cancel.run();
|
||||
finished = true;
|
||||
} catch (Throwable e) {
|
||||
canceled.set(true);
|
||||
LauncherEnv.showError(errorMessage, e);
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
close();
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
@ -41,15 +41,18 @@ public class ProcessStateWatcherWindow extends Window {
|
|||
ImGui.textUnformatted(state.getCurrentStep());
|
||||
if (cancel != null && ImGui.button("Cancel")) {
|
||||
canceled.set(true);
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (finished) {
|
||||
super.close();
|
||||
} else if (cancel != null) {
|
||||
canceled.set(true);
|
||||
}
|
||||
super.close();
|
||||
if (canceled.get() && cancel != null) cancel.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCloseable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package io.gitlab.jfronny.inceptum.imgui.window.edit;
|
||||
|
||||
import imgui.ImGui;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingBiConsumer;
|
||||
import io.gitlab.jfronny.inceptum.common.R;
|
||||
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
|
||||
import io.gitlab.jfronny.inceptum.imgui.GuiMain;
|
||||
import io.gitlab.jfronny.inceptum.imgui.control.Tab;
|
||||
import io.gitlab.jfronny.inceptum.imgui.window.dialog.ProcessStateWatcherWindow;
|
||||
import io.gitlab.jfronny.inceptum.imgui.window.dialog.TextBoxWindow;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.export.InstanceExporter;
|
||||
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.export.Exporter;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.export.Exporters;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
@ -16,8 +16,6 @@ import org.lwjgl.util.tinyfd.TinyFileDialogs;
|
|||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ExportTab extends Tab {
|
||||
private final InstanceEditWindow window;
|
||||
|
@ -30,41 +28,34 @@ public class ExportTab extends Tab {
|
|||
@Override
|
||||
protected void renderInner() {
|
||||
if (window.mds.isComplete()) {
|
||||
AtomicReference<String> cfVersion = new AtomicReference<>();
|
||||
exportVariant("CurseForge", "zip", it -> {
|
||||
GuiMain.WINDOWS.add(new TextBoxWindow("Version", "Please enter the version to list in the modpack file", "1.0", version -> {
|
||||
cfVersion.set(version);
|
||||
it.run();
|
||||
}, R::nop));
|
||||
}, (state, exportPath) -> {
|
||||
InstanceExporter.exportCurseZip(state, window.path, window.instance, window.mds, exportPath, cfVersion.get());
|
||||
});
|
||||
exportVariant("MultiMC", "zip", Runnable::run, (state, exportPath) -> {
|
||||
InstanceExporter.exportMultiMCZip(state, window.path, window.instance, window.mds, exportPath);
|
||||
});
|
||||
for (Exporter<?> exporter : Exporters.EXPORTERS) {
|
||||
if (ImGui.button(exporter.getName())) {
|
||||
GuiMain.open(new TextBoxWindow("Version", "Please enter the current version of your modpack", "1.0", version -> {
|
||||
String defaultName = window.name + " " + version + " (" + exporter.getName() + ")." + exporter.getFileExtension();
|
||||
String filter = "*." + exporter.getFileExtension();
|
||||
String file = saveFileDialog("Export " + exporter.getName() + " Pack", defaultName, filter, exporter.getName() + " packs (" + filter + ")");
|
||||
if (file != null) {
|
||||
ProcessState state = new ProcessState(Exporters.STEP_COUNT, "Initializing...");
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Exporting", "Could not export pack", state, cToken -> {
|
||||
Path exportPath = Paths.get(file);
|
||||
exporter.generate(state, window.path, window.instance, window.mds, exportPath, version);
|
||||
LauncherEnv.showInfo(window.name + " has been successfully exported to " + exportPath, "Successfully exported");
|
||||
}, null));
|
||||
}
|
||||
}, R::nop));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImGui.text("The mods directory scan must be completed.\nThe progress for this can be observed in the mods tab");
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Throwable> void exportVariant(String name, String packExt, Consumer<Runnable> beforeSave, ThrowingBiConsumer<ProcessState, Path, T> export) {
|
||||
if (ImGui.button(name)) {
|
||||
beforeSave.accept(() -> {
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
PointerBuffer aFilterPatterns = stack.mallocPointer(2);
|
||||
aFilterPatterns.put(stack.UTF8("*." + packExt));
|
||||
aFilterPatterns.flip();
|
||||
String p = TinyFileDialogs.tinyfd_saveFileDialog("Export Pack", "", aFilterPatterns, name + " packs (*." + packExt + ")");
|
||||
if (p != null) {
|
||||
ProcessState state = new ProcessState(InstanceExporter.STEP_COUNT, "Initializing...");
|
||||
GuiMain.open(new ProcessStateWatcherWindow("Exporting", "Could not export pack", state, cToken -> {
|
||||
Path exportPath = Paths.get(p);
|
||||
export.accept(state, exportPath);
|
||||
LauncherEnv.showInfo(window.path.getFileName().toString() + " has been successfully exported to " + exportPath, "Successfully exported");
|
||||
}, null));
|
||||
}
|
||||
}
|
||||
});
|
||||
private String saveFileDialog(String title, String defaultName, String filter, String filterDescription) {
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
PointerBuffer filterPatterns = stack.mallocPointer(2);
|
||||
filterPatterns.put(stack.UTF8(filter));
|
||||
filterPatterns.flip();
|
||||
return TinyFileDialogs.tinyfd_saveFileDialog(title, defaultName, filterPatterns, filterDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.export;
|
||||
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
public class CleanupFileVisitor implements FileVisitor<Path> {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes basicFileAttributes) throws IOException {
|
||||
if (JFiles.list(path).isEmpty()) {
|
||||
Files.delete(path);
|
||||
return FileVisitResult.SKIP_SUBTREE;
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFileFailed(Path path, IOException e) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path path, IOException e) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.export;
|
||||
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
import io.gitlab.jfronny.inceptum.common.InceptumConfig;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeModpackManifest;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.source.CurseforgeModSource;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class CurseForgeExporter extends Exporter<CurseforgeModpackManifest> {
|
||||
private static final String OVERRIDES_DIR_DEFAULT = "overrides";
|
||||
public CurseForgeExporter() {
|
||||
super("CurseForge", "zip", "overrides");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CurseforgeModpackManifest generateManifests(Path root, InstanceMeta instance, String name, String version) throws IOException {
|
||||
CurseforgeModpackManifest manifest = new CurseforgeModpackManifest();
|
||||
manifest.minecraft = new CurseforgeModpackManifest.Minecraft();
|
||||
manifest.minecraft.version = instance.getMinecraftVersion();
|
||||
manifest.manifestType = "minecraftModpack";
|
||||
manifest.manifestVersion = 1;
|
||||
manifest.name = name;
|
||||
manifest.version = version;
|
||||
manifest.author = InceptumConfig.authorName;
|
||||
manifest.overrides = OVERRIDES_DIR_DEFAULT;
|
||||
manifest.minecraft.modLoaders = new LinkedHashSet<>();
|
||||
if (instance.isFabric()) {
|
||||
CurseforgeModpackManifest.Minecraft.ModLoader loader = new CurseforgeModpackManifest.Minecraft.ModLoader();
|
||||
loader.id = "fabric-" + instance.getLoaderVersion();
|
||||
loader.primary = true;
|
||||
manifest.minecraft.modLoaders.add(loader);
|
||||
}
|
||||
JFiles.writeObject(root.resolve("manifest.json"), manifest);
|
||||
return manifest;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addMods(Path root, InstanceMeta instance, Iterable<IWModDescription> mods, CurseforgeModpackManifest manifest, Path modsOverrides) throws IOException {
|
||||
modsLoop: for(IWModDescription mod : mods) {
|
||||
if (ModPath.isImod(mod.path())) {
|
||||
Set<ModSource> sources = mod.mod().orElseThrow().sources.keySet();
|
||||
for (ModSource source : sources) {
|
||||
if (source instanceof CurseforgeModSource cms) {
|
||||
manifest.files.add(cms.toManifest());
|
||||
continue modsLoop;
|
||||
}
|
||||
}
|
||||
// Not available on CF
|
||||
for (ModSource source : sources) {
|
||||
Files.createDirectories(modsOverrides);
|
||||
Files.copy(source.getJarPath(), modsOverrides.resolve(mod.path().getFileName().toString()));
|
||||
continue modsLoop;
|
||||
}
|
||||
} else {
|
||||
Files.createDirectories(modsOverrides);
|
||||
Files.copy(mod.path(), modsOverrides.resolve(mod.path().getFileName().toString()));
|
||||
continue modsLoop;
|
||||
}
|
||||
throw new FileNotFoundException("Could not find mod file for " + mod.path());
|
||||
}
|
||||
JFiles.writeObject(root.resolve("manifest.json"), manifest);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.export;
|
||||
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.*;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ignore.IgnoringWalk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class Exporter<Manifest> {
|
||||
private final String name;
|
||||
private final String fileExtension;
|
||||
private final String overridesDirName;
|
||||
|
||||
public Exporter(String name, String fileExtension, String overridesDirName) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
this.fileExtension = Objects.requireNonNull(fileExtension);
|
||||
this.overridesDirName = Objects.requireNonNull(overridesDirName);
|
||||
}
|
||||
|
||||
protected abstract Manifest generateManifests(Path root, InstanceMeta instance, String name, String version) throws IOException;
|
||||
protected abstract void addMods(Path root, InstanceMeta instance, Iterable<IWModDescription> mods, Manifest manifest, Path modsOverrides) throws IOException;
|
||||
|
||||
public void generate(ProcessState state, Path instanceDir, InstanceMeta meta, ModsDirScanner mds, Path exportPath, String version) throws IOException {
|
||||
if (Files.exists(exportPath)) Files.delete(exportPath);
|
||||
try (FileSystem fs = Utils.openZipFile(exportPath, true)) {
|
||||
Path root = fs.getPath(".");
|
||||
Path overrides = fs.getPath(overridesDirName);
|
||||
state.incrementStep("Preparing manifests");
|
||||
Manifest manifest = generateManifests(root, meta, instanceDir.getFileName().toString(), version);
|
||||
if (meta.isFabric()) {
|
||||
state.incrementStep("Adding mods");
|
||||
if (!mds.isComplete()) throw new IOException("Mods dir scan is not yet completed");
|
||||
addMods(root, meta, new StreamIterable<>(mds.getMods().stream().filter(mod -> {
|
||||
if (!ModPath.isEnabled(mod.path())) return false;
|
||||
state.updateStep(mod.path().toString());
|
||||
return true;
|
||||
})), manifest, overrides.resolve("mods"));
|
||||
}
|
||||
state.incrementStep("Adding files");
|
||||
filesLoop: for (Path path : new StreamIterable<>(IgnoringWalk.walk(instanceDir))) {
|
||||
Path relativePath = instanceDir.relativize(path).normalize();
|
||||
Path target = overrides;
|
||||
for (Path segment : relativePath) {
|
||||
if (target == overrides && segment.toString().equals("mods")) continue filesLoop;
|
||||
if (segment.toString().startsWith(".")) continue filesLoop;
|
||||
target = target.resolve(segment.toString());
|
||||
}
|
||||
state.updateStep(relativePath.toString());
|
||||
Files.createDirectories(target.getParent());
|
||||
Files.copy(path, target);
|
||||
}
|
||||
state.incrementStep("Cleaning up");
|
||||
Files.walkFileTree(root, new CleanupFileVisitor());
|
||||
} catch (URISyntaxException se) {
|
||||
// Can only be thrown by openZipFile, the target file therefore cannot exist
|
||||
throw new IOException("Could not open export path", se);
|
||||
} catch (Throwable t) {
|
||||
if (Files.exists(exportPath)) Files.delete(exportPath);
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getFileExtension() {
|
||||
return fileExtension;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.export;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//TODO modrinth export
|
||||
public class Exporters {
|
||||
public static final int STEP_COUNT = 4;
|
||||
public static final CurseForgeExporter CURSE_FORGE = new CurseForgeExporter();
|
||||
public static final MultiMCExporter MULTI_MC = new MultiMCExporter();
|
||||
public static final List<Exporter<?>> EXPORTERS = List.of(CURSE_FORGE, MULTI_MC);
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.export;
|
||||
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
import io.gitlab.jfronny.inceptum.common.InceptumConfig;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeModpackManifest;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.multimc.MMCPackMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ignore.IgnoringWalk;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.ModsDirScanner;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.source.CurseforgeModSource;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.*;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
|
||||
//TODO deduplicate
|
||||
//TODO modrinth export
|
||||
|
||||
public class InstanceExporter {
|
||||
public static final int STEP_COUNT = 3;
|
||||
private static final String OVERRIDES_DIR_DEFAULT = "overrides";
|
||||
private static final String DOT_MINECRAFT = ".minecraft";
|
||||
|
||||
public static void exportCurseZip(ProcessState state, Path instanceDir, InstanceMeta meta, ModsDirScanner mds, Path exportPath, String version) throws IOException, URISyntaxException {
|
||||
if (Files.exists(exportPath)) Files.delete(exportPath);
|
||||
try (FileSystem fs = Utils.openZipFile(exportPath, true)) {
|
||||
state.incrementStep("Preparing basic manifest");
|
||||
CurseforgeModpackManifest manifest = new CurseforgeModpackManifest();
|
||||
manifest.minecraft = new CurseforgeModpackManifest.Minecraft();
|
||||
manifest.minecraft.version = meta.getMinecraftVersion();
|
||||
manifest.minecraft.modLoaders = new LinkedHashSet<>();
|
||||
if (meta.isFabric()) {
|
||||
CurseforgeModpackManifest.Minecraft.ModLoader loader = new CurseforgeModpackManifest.Minecraft.ModLoader();
|
||||
loader.id = "fabric-" + meta.getLoaderVersion();
|
||||
loader.primary = true;
|
||||
manifest.minecraft.modLoaders.add(loader);
|
||||
}
|
||||
manifest.manifestType = "minecraftModpack";
|
||||
manifest.manifestVersion = 1;
|
||||
manifest.name = instanceDir.getFileName().toString();
|
||||
manifest.version = version;
|
||||
manifest.author = InceptumConfig.authorName;
|
||||
manifest.overrides = OVERRIDES_DIR_DEFAULT;
|
||||
Path overrides = fs.getPath(OVERRIDES_DIR_DEFAULT);
|
||||
Files.createDirectories(overrides);
|
||||
if (meta.isFabric()) {
|
||||
state.incrementStep("Adding mods");
|
||||
manifest.files = new LinkedHashSet<>();
|
||||
if (!mds.isComplete()) throw new IOException("Mods dir scan is not yet completed");
|
||||
modsLoop:
|
||||
for (IWModDescription mod : mds.getMods()) {
|
||||
if (!ModPath.isEnabled(mod.path())) continue;
|
||||
state.updateStep(mod.path().toString());
|
||||
if (ModPath.isImod(mod.path())) {
|
||||
Set<ModSource> sources = mod.mod().orElseThrow().sources.keySet();
|
||||
for (ModSource source : sources) {
|
||||
if (source instanceof CurseforgeModSource cms) {
|
||||
CurseforgeModpackManifest.File f = new CurseforgeModpackManifest.File();
|
||||
f.projectID = cms.getProjectId();
|
||||
f.fileID = cms.getFileId();
|
||||
f.required = true;
|
||||
manifest.files.add(f);
|
||||
continue modsLoop;
|
||||
}
|
||||
}
|
||||
// Not available on CF
|
||||
for (ModSource source : sources) {
|
||||
Path jarPath = source.getJarPath();
|
||||
Files.copy(jarPath, jarPath.resolve(mod.path().getFileName().toString()));
|
||||
continue modsLoop;
|
||||
}
|
||||
}
|
||||
Path md = overrides.resolve("mods");
|
||||
Files.createDirectories(md);
|
||||
Files.copy(mod.path(), md.resolve(mod.path().getFileName().toString()));
|
||||
}
|
||||
}
|
||||
state.incrementStep("Adding files");
|
||||
for (Path l : IgnoringWalk.walk(instanceDir).toList()) {
|
||||
String fn = l.getFileName().toString();
|
||||
if (fn.equals("mods") || fn.startsWith(".")) continue;
|
||||
state.updateStep(fn);
|
||||
Path target = overrides.resolve(fn);
|
||||
JFiles.copyContent(l, target);
|
||||
}
|
||||
JFiles.writeObject(fs.getPath("manifest.json"), manifest);
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportMultiMCZip(ProcessState state, Path instanceDir, InstanceMeta meta, ModsDirScanner mds, Path exportPath) throws IOException, URISyntaxException {
|
||||
if (Files.exists(exportPath)) Files.delete(exportPath);
|
||||
try (FileSystem fs = Utils.openZipFile(exportPath, true)) {
|
||||
{
|
||||
state.incrementStep("Preparing basic manifest");
|
||||
MMCPackMeta manifest = new MMCPackMeta();
|
||||
manifest.formatVersion = 1;
|
||||
manifest.components = new ArrayList<>();
|
||||
MMCPackMeta.Component lwjgl = new MMCPackMeta.Component();
|
||||
lwjgl.dependencyOnly = true;
|
||||
lwjgl.uid = "org.lwjgl3";
|
||||
lwjgl.version = "3.2.2"; //TODO get automatically
|
||||
manifest.components.add(lwjgl);
|
||||
MMCPackMeta.Component minecraft = new MMCPackMeta.Component();
|
||||
minecraft.important = true;
|
||||
minecraft.uid = "net.minecraft";
|
||||
minecraft.version = meta.getMinecraftVersion();
|
||||
manifest.components.add(minecraft);
|
||||
if (meta.isFabric()) {
|
||||
MMCPackMeta.Component intermediary = new MMCPackMeta.Component();
|
||||
intermediary.dependencyOnly = true;
|
||||
intermediary.uid = "net.fabricmc.intermediary";
|
||||
intermediary.version = meta.getMinecraftVersion();
|
||||
manifest.components.add(intermediary);
|
||||
MMCPackMeta.Component fabric = new MMCPackMeta.Component();
|
||||
fabric.uid = "net.fabricmc.fabric-loader";
|
||||
fabric.version = meta.getLoaderVersion();
|
||||
manifest.components.add(fabric);
|
||||
}
|
||||
JFiles.writeObject(fs.getPath("mmc-pack.json"), manifest);
|
||||
}
|
||||
{
|
||||
state.updateStep("Preparing instance config");
|
||||
Files.writeString(fs.getPath("instance.cfg"), String.format("""
|
||||
ForgeVersion=
|
||||
InstanceType=OneSix
|
||||
IntendedVersion=
|
||||
JoinServerOnLaunch=false
|
||||
LWJGLVersion=
|
||||
LiteloaderVersion=
|
||||
LogPrePostOutput=true
|
||||
MCLaunchMethod=LauncherPart
|
||||
OverrideCommands=false
|
||||
OverrideConsole=false
|
||||
OverrideGameTime=false
|
||||
OverrideJavaArgs=false
|
||||
OverrideJavaLocation=false
|
||||
OverrideMCLaunchMethod=false
|
||||
OverrideMemory=false
|
||||
OverrideNativeWorkarounds=false
|
||||
OverrideWindow=false
|
||||
iconKey=default
|
||||
lastLaunchTime=%s
|
||||
lastTimePlayed=0
|
||||
name=%s
|
||||
notes=
|
||||
totalTimePlayed=0
|
||||
""", Instant.now().toEpochMilli(), instanceDir.getFileName()));
|
||||
}
|
||||
Path overrides = fs.getPath(DOT_MINECRAFT);
|
||||
Files.createDirectories(overrides);
|
||||
if (meta.isFabric()) {
|
||||
state.incrementStep("Adding mods");
|
||||
if (!mds.isComplete()) throw new IOException("Mods dir scan is not yet completed");
|
||||
modsLoop:
|
||||
for (IWModDescription mod : mds.getMods()) {
|
||||
if (!ModPath.isEnabled(mod.path())) continue;
|
||||
state.updateStep(mod.path().toString());
|
||||
if (ModPath.isImod(mod.path())) {
|
||||
Set<ModSource> sources = mod.mod().orElseThrow().sources.keySet();
|
||||
for (ModSource source : sources) {
|
||||
Path jarPath = source.getJarPath();
|
||||
Files.copy(jarPath, jarPath.resolve(mod.path().getFileName().toString()));
|
||||
continue modsLoop;
|
||||
}
|
||||
}
|
||||
Path md = overrides.resolve("mods");
|
||||
Files.createDirectories(md);
|
||||
Files.copy(mod.path(), md.resolve(mod.path().getFileName().toString()));
|
||||
}
|
||||
}
|
||||
state.incrementStep("Adding files");
|
||||
for (Path l : IgnoringWalk.walk(instanceDir).toList()) {
|
||||
String fn = l.getFileName().toString();
|
||||
if (fn.equals("mods") || fn.startsWith(".")) continue;
|
||||
state.updateStep(fn);
|
||||
Path target = overrides.resolve(fn);
|
||||
JFiles.copyContent(l, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.system.export;
|
||||
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.multimc.MMCPackMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.IWModDescription;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ModPath;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
public class MultiMCExporter extends Exporter<MMCPackMeta> {
|
||||
public MultiMCExporter() {
|
||||
super("MultiMC", "zip", ".minecraft");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MMCPackMeta generateManifests(Path root, InstanceMeta instance, String name, String version) throws IOException {
|
||||
{
|
||||
Files.writeString(root.resolve("instance.cfg"), String.format("""
|
||||
ForgeVersion=
|
||||
InstanceType=OneSix
|
||||
IntendedVersion=
|
||||
JoinServerOnLaunch=false
|
||||
LWJGLVersion=
|
||||
LiteloaderVersion=
|
||||
LogPrePostOutput=true
|
||||
MCLaunchMethod=LauncherPart
|
||||
OverrideCommands=false
|
||||
OverrideConsole=false
|
||||
OverrideGameTime=false
|
||||
OverrideJavaArgs=false
|
||||
OverrideJavaLocation=false
|
||||
OverrideMCLaunchMethod=false
|
||||
OverrideMemory=false
|
||||
OverrideNativeWorkarounds=false
|
||||
OverrideWindow=false
|
||||
iconKey=default
|
||||
lastLaunchTime=%s
|
||||
lastTimePlayed=0
|
||||
name=%s
|
||||
notes=
|
||||
totalTimePlayed=0
|
||||
""", Instant.now().toEpochMilli(), name));
|
||||
}
|
||||
{
|
||||
MMCPackMeta manifest = new MMCPackMeta();
|
||||
manifest.formatVersion = 1;
|
||||
manifest.components = new ArrayList<>();
|
||||
MMCPackMeta.Component lwjgl = new MMCPackMeta.Component();
|
||||
lwjgl.dependencyOnly = true;
|
||||
lwjgl.uid = "org.lwjgl3";
|
||||
lwjgl.version = "3.2.2"; //TODO get automatically
|
||||
manifest.components.add(lwjgl);
|
||||
MMCPackMeta.Component minecraft = new MMCPackMeta.Component();
|
||||
minecraft.important = true;
|
||||
minecraft.uid = "net.minecraft";
|
||||
minecraft.version = instance.getMinecraftVersion();
|
||||
manifest.components.add(minecraft);
|
||||
if (instance.isFabric()) {
|
||||
MMCPackMeta.Component intermediary = new MMCPackMeta.Component();
|
||||
intermediary.dependencyOnly = true;
|
||||
intermediary.uid = "net.fabricmc.intermediary";
|
||||
intermediary.version = instance.getMinecraftVersion();
|
||||
manifest.components.add(intermediary);
|
||||
MMCPackMeta.Component fabric = new MMCPackMeta.Component();
|
||||
fabric.uid = "net.fabricmc.fabric-loader";
|
||||
fabric.version = instance.getLoaderVersion();
|
||||
manifest.components.add(fabric);
|
||||
}
|
||||
JFiles.writeObject(root.resolve("mmc-pack.json"), manifest);
|
||||
return manifest;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addMods(Path root, InstanceMeta instance, Iterable<IWModDescription> mods, MMCPackMeta mmcPackMeta, Path modsOverrides) throws IOException {
|
||||
modsLoop: for (IWModDescription mod : mods) {
|
||||
if (ModPath.isImod(mod.path())) {
|
||||
Set<ModSource> sources = mod.mod().orElseThrow().sources.keySet();
|
||||
for (ModSource source : sources) {
|
||||
Files.createDirectories(modsOverrides);
|
||||
Files.copy(source.getJarPath(), modsOverrides.resolve(mod.path().getFileName().toString()));
|
||||
continue modsLoop;
|
||||
}
|
||||
} else {
|
||||
Files.createDirectories(modsOverrides);
|
||||
Files.copy(mod.path(), modsOverrides.resolve(mod.path().getFileName().toString()));
|
||||
continue modsLoop;
|
||||
}
|
||||
throw new FileNotFoundException("Could not find mod file for " + mod.path());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,6 +43,6 @@ public class DownloadJavaStep implements Step {
|
|||
}
|
||||
}
|
||||
//TODO link these in using a pre-launch class added to the launch classpath instead
|
||||
JFiles.copyContent(MetaHolder.FORCE_LOAD_PATH, jvmDir.resolve("bin"));
|
||||
JFiles.copyRecursive(MetaHolder.FORCE_LOAD_PATH, jvmDir.resolve("bin"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ public class DownloadLibrariesStep implements Step {
|
|||
if (artifact.isNative) {
|
||||
currentState.updateStep("Extracting natives");
|
||||
try (FileSystem libFs = Utils.openZipFile(path, false)) {
|
||||
JFiles.copyContent(libFs.getPath("."), MetaHolder.NATIVES_DIR.resolve(InstanceMeta.getMinecraftVersion(version.id)));
|
||||
JFiles.copyRecursive(libFs.getPath("."), MetaHolder.NATIVES_DIR.resolve(InstanceMeta.getMinecraftVersion(version.id)));
|
||||
} catch (Throwable t) {
|
||||
Files.delete(path);
|
||||
throw new IOException("Could not extract native", t);
|
||||
|
|
|
@ -6,8 +6,7 @@ import io.gitlab.jfronny.commons.tuple.Triple;
|
|||
import io.gitlab.jfronny.commons.tuple.Tuple;
|
||||
import io.gitlab.jfronny.inceptum.common.Net;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeFile;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.CurseforgeMod;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.curseforge.*;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.CurseforgeApi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -115,4 +114,12 @@ public final class CurseforgeModSource implements ModSource {
|
|||
public int getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public CurseforgeModpackManifest.File toManifest() {
|
||||
CurseforgeModpackManifest.File f = new CurseforgeModpackManifest.File();
|
||||
f.projectID = getProjectId();
|
||||
f.fileID = getFileId();
|
||||
f.required = true;
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.util;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
final class OnceSupplier<T> implements Supplier<T> {
|
||||
private final T value;
|
||||
private boolean supplied = false;
|
||||
|
||||
public OnceSupplier(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
if (supplied) throw new IllegalStateException("Attempted to use already used OnceSupplier");
|
||||
supplied = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (!(obj instanceof OnceSupplier<?> that)) return false;
|
||||
return Objects.equals(this.value, that.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record StreamIterable<T>(Supplier<Stream<T>> source) implements Iterable<T> {
|
||||
public StreamIterable(Stream<T> source) {
|
||||
this(new OnceSupplier<>(source));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return source.get().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super T> action) {
|
||||
this.source.get().forEach(action);
|
||||
}
|
||||
}
|
|
@ -55,7 +55,11 @@ public class IgnoreRule {
|
|||
}
|
||||
negate = isNegate;
|
||||
for (Replacer replacer : REPLACERS) {
|
||||
pattern = replacer.invoke(pattern);
|
||||
try {
|
||||
pattern = replacer.invoke(pattern);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Could not execute replacer " + replacer.getName() + " (" + replacer.getRegex() + ") on " + pattern, t);
|
||||
}
|
||||
}
|
||||
|
||||
parsedRegex = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE).asMatchPredicate();
|
||||
|
|
|
@ -2,8 +2,7 @@ package io.gitlab.jfronny.inceptum.launcher.util.ignore;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
@ -24,7 +23,7 @@ public class IgnoringWalk implements Iterator<Path> {
|
|||
|
||||
private IgnoringWalk(Path repositoryRoot) throws IOException {
|
||||
this.ref = repositoryRoot;
|
||||
enqueueContent(repositoryRoot);
|
||||
enqueueDirectory(repositoryRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,7 +41,7 @@ public class IgnoringWalk implements Iterator<Path> {
|
|||
if (next == null) return false;
|
||||
if (Files.isDirectory(next)) {
|
||||
try {
|
||||
enqueueContent(next);
|
||||
enqueueDirectory(next);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
|
@ -50,7 +49,7 @@ public class IgnoringWalk implements Iterator<Path> {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void enqueueContent(Path directory) throws IOException {
|
||||
private void enqueueDirectory(Path directory) throws IOException {
|
||||
Path gitignorePath = directory.resolve(GITIGNORE);
|
||||
Path iceignorePath = directory.resolve(ICEIGNORE);
|
||||
Ignore ignore = null;
|
||||
|
@ -63,8 +62,8 @@ public class IgnoringWalk implements Iterator<Path> {
|
|||
ignore.add(Files.readAllLines(iceignorePath));
|
||||
}
|
||||
if (ignore != null) ignores.put(directory, ignore);
|
||||
try (Stream<Path> files = Files.list(directory)) {
|
||||
for (Path path : files.toList()) {
|
||||
try (DirectoryStream<Path> files = Files.newDirectoryStream(directory)) {
|
||||
for (Path path : files) {
|
||||
String fileName = path.getFileName().toString();
|
||||
if (!fileName.equals(GITIGNORE)
|
||||
&& !fileName.equals(ICEIGNORE)
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package io.gitlab.jfronny.inceptum.launcher.util.ignore;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.*;
|
||||
|
||||
public class Replacer {
|
||||
private final String name;
|
||||
|
@ -16,11 +15,19 @@ public class Replacer {
|
|||
}
|
||||
|
||||
public String invoke(String pattern) {
|
||||
return regex.matcher(pattern).replaceAll(matchEvaluator);
|
||||
return regex.matcher(pattern).replaceAll(result -> Matcher.quoteReplacement(matchEvaluator.apply(result)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getRegex() {
|
||||
return regex.toString();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue