/* * Copyright (C) 2024 NotEnoughUpdates contributors * * This file is part of NotEnoughUpdates. * * NotEnoughUpdates is free software: you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * NotEnoughUpdates is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with NotEnoughUpdates. If not, see . */ package io.github.moulberry.notenoughupdates.loader; import lombok.SneakyThrows; import net.minecraft.launchwrapper.ITweaker; import net.minecraft.launchwrapper.Launch; import net.minecraft.launchwrapper.LaunchClassLoader; import org.jetbrains.annotations.Nullable; import java.io.File; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.stream.Stream; /** * Tweaker used to load library JARs conditionally. See the subclasses for concrete implementations. */ public abstract class JARLoadingTweaker implements ITweaker { /** * @return a path pointing to a folder filled with JAR files */ protected abstract Path getFilesToLoad(); protected abstract String getTestClass(); @SneakyThrows protected @Nullable Path getShadowedElement(String path) { URI uri = Objects.requireNonNull(getClass().getResource(path)).toURI(); if ("jar".equals(uri.getScheme())) { FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap()); closeFileSystemLater(fs); return fs.getPath(path); } else { return Paths.get(uri); } } private List toClose = new ArrayList<>(); protected void closeFileSystemLater(FileSystem fileSystem) { toClose.add(fileSystem); } @Override public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { } protected void performLoading(LaunchClassLoader classLoader) { try { if (Launch.blackboard.get("fml.deobfuscatedEnvironment") == Boolean.TRUE) { System.out.println("Skipping JAR loading in development environment."); return; } Path p = getFilesToLoad(); System.out.println("Loading a JAR from " + p.toAbsolutePath()); Path tempDirectory = Files.createTempDirectory("notenoughupdates-extracted-" + getClass().getSimpleName()); System.out.println("Using temporary directory " + tempDirectory + " to store extracted jars."); tempDirectory.toFile().deleteOnExit(); try (Stream libraries = Files.walk(p, 1)) { libraries.filter(it -> it.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".jar")) .forEach(it -> { try { Path extractedPath = tempDirectory.resolve(it.getFileName().toString()); extractedPath.toFile().deleteOnExit(); Files.copy(it, extractedPath); ClassLoaderExtUtil.addClassSourceTwice(classLoader, extractedPath.toUri().toURL()); } catch (Exception e) { throw new RuntimeException(e); } }); } classLoader.loadClass(getTestClass()); System.out.println("Could successfully load a class from loaded library."); } catch (Throwable e) { System.err.println("Failed to load a JAR into NEU. This is most likely a bad thing."); e.printStackTrace(); } finally { Iterator iterator = toClose.iterator(); while (iterator.hasNext()) try { iterator.next().close(); } catch (Throwable e) { e.printStackTrace(); } finally { iterator.remove(); } } } @Override public String getLaunchTarget() { return null; } @Override public String[] getLaunchArguments() { return new String[0]; } }