Skip to content

Commit

Permalink
Initial work on caching scan result
Browse files Browse the repository at this point in the history
  • Loading branch information
Matyrobbrt committed Dec 8, 2024
1 parent 45105b7 commit 5c4663d
Show file tree
Hide file tree
Showing 6 changed files with 429 additions and 12 deletions.
5 changes: 5 additions & 0 deletions loader/src/main/java/net/neoforged/fml/loading/FMLPaths.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public enum FMLPaths {
GAMEDIR(),
MODSDIR("mods"),
CONFIGDIR("config"),
CACHEDIR(".fml/cache"),
FMLCONFIG(false, CONFIGDIR, "fml.toml");

private static final Logger LOGGER = LogUtils.getLogger();
Expand Down Expand Up @@ -89,4 +90,8 @@ public Path relative() {
public Path get() {
return absolutePath;
}

public Path resolve(String path) {
return absolutePath.resolve(path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
package net.neoforged.fml.loading.moddiscovery;

import com.google.common.collect.ImmutableMap;
import com.google.common.hash.Hashing;
import com.mojang.logging.LogUtils;
import cpw.mods.jarhandling.SecureJar;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
Expand Down Expand Up @@ -37,11 +39,13 @@
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;

@ApiStatus.Internal
public class ModFile implements IModFile {
private static final Logger LOGGER = LogUtils.getLogger();
private static final String[] EMPTY_ARRAY = new String[0];

private final String jarVersion;
private final ModFileInfoParser parser;
Expand All @@ -62,21 +66,50 @@ public class ModFile implements IModFile {
public static final Attributes.Name TYPE = new Attributes.Name("FMLModType");
private SecureJar.Status securityStatus;

public ModFile(SecureJar jar, final ModFileInfoParser parser, ModFileDiscoveryAttributes attributes) {
this(jar, parser, parseType(jar), attributes);
private final String[] cacheKeyComponents;

private volatile boolean cacheComputed;
private String cacheKey;

public ModFile(SecureJar jar, final ModFileInfoParser parser, ModFileDiscoveryAttributes attributes, String... cacheKeyComponents) {
this(jar, parser, parseType(jar), attributes, cacheKeyComponents);
}

public ModFile(SecureJar jar, ModFileInfoParser parser, Type type, ModFileDiscoveryAttributes discoveryAttributes) {
public ModFile(SecureJar jar, ModFileInfoParser parser, Type type, ModFileDiscoveryAttributes discoveryAttributes, String... cacheKeyComponents) {
this.jar = Objects.requireNonNull(jar, "jar");
this.parser = Objects.requireNonNull(parser, "parser");
this.discoveryAttributes = Objects.requireNonNull(discoveryAttributes, "discoveryAttributes");
this.cacheKeyComponents = Objects.requireNonNull(cacheKeyComponents, "cache key components").length == 0 ? EMPTY_ARRAY : cacheKeyComponents;

manifest = this.jar.moduleDataProvider().getManifest();
modFileType = Objects.requireNonNull(type, "type");
jarVersion = Optional.ofNullable(manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION)).orElse("0.0NONE");
this.modFileInfo = ModFileParser.readModList(this, this.parser);
}

@Nullable
public String getCacheKey() {
if (!cacheComputed) {
synchronized (this) {
if (this.cacheKey == null && Files.isRegularFile(jar.getPrimaryPath())) {
try {
var hasher = Hashing.sha256().newHasher()
.putBytes(Files.readAllBytes(jar.getPrimaryPath()));
for (int i = 0; i < cacheKeyComponents.length; i++) {
hasher.putString(cacheKeyComponents[i], StandardCharsets.UTF_8);
}
this.cacheKey = hasher.hash().toString();

cacheComputed = true;
} catch (Exception exception) {
throw new RuntimeException(exception);
}
}
}
}
return cacheKey;
}

@Override
public Supplier<Map<String, Object>> getSubstitutionMap() {
return () -> ImmutableMap.<String, Object>builder().put("jarVersion", jarVersion).putAll(fileProperties).build();
Expand Down Expand Up @@ -143,7 +176,7 @@ public List<String> getMixinConfigs() {
* Run in an executor thread to harvest the class and annotation list
*/
public ModFileScanData compileContent() {
return new Scanner(this).scan();
return new Scanner(this).scanCached();
}

public void scanFile(Consumer<Path> pathConsumer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@
package net.neoforged.fml.loading.modscan;

import com.mojang.logging.LogUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import net.neoforged.fml.loading.FMLPaths;
import net.neoforged.fml.loading.LogMarkers;
import net.neoforged.fml.loading.moddiscovery.ModFile;
import net.neoforged.neoforgespi.language.ModFileScanData;
import org.objectweb.asm.ClassReader;
import org.slf4j.Logger;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

public class Scanner {
private static final Logger LOGGER = LogUtils.getLogger();
private final ModFile fileToScan;
Expand All @@ -24,6 +28,29 @@ public Scanner(final ModFile fileToScan) {
this.fileToScan = fileToScan;
}

public ModFileScanData scanCached() {
var key = fileToScan.getCacheKey();
if (key == null) return scan();
var path = FMLPaths.CACHEDIR.resolve(key);
try (var in = new ObjectInputStream(Files.newInputStream(path))) {
var scan = ModFileScanData.read(in);
LOGGER.debug("Reading scan data for file {} from cache at {}", fileToScan, path);
if (scan != null) {
scan.addModFileInfo(fileToScan.getModFileInfo());
return scan;
}
} catch (Exception exception) {
LOGGER.error("Failed to read mod file scan data for file {} from cache at {}", fileToScan, path);
}
var computed = scan();
try (var out = new ObjectOutputStream(Files.newOutputStream(path))) {
computed.write(out);
} catch (Exception exception) {
LOGGER.error("Failed to write mod file scan data for file {} to cache at {}", fileToScan, path);
}
return computed;
}

public ModFileScanData scan() {
ModFileScanData result = new ModFileScanData();
result.addModFileInfo(fileToScan.getModFileInfo());
Expand Down
Loading

0 comments on commit 5c4663d

Please sign in to comment.