Skip to content

Commit

Permalink
Optimize folder resource packs & remove use of getCanonicalPath
Browse files Browse the repository at this point in the history
Relatd: #21
  • Loading branch information
embeddedt committed Jun 7, 2023
1 parent 2501907 commit ea6cca8
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.embeddedt.vintagefix.mixin.resourcepacks;

import com.teamacronymcoders.base.util.files.DirectoryResourcePack;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ResourceLocation;
import org.embeddedt.vintagefix.annotation.ClientOnlyMixin;
import org.embeddedt.vintagefix.annotation.LateMixin;
import org.embeddedt.vintagefix.util.FolderPackCache;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.io.*;

@Mixin(DirectoryResourcePack.class)
@LateMixin
@ClientOnlyMixin
public class MixinDirectoryResourcePack {
private FolderPackCache vfix_cache;

@Inject(method = "<init>", at = @At("RETURN"))
private void afterConstruct(CallbackInfo ci) {
vfix_cache = new FolderPackCache(new File(Minecraft.getMinecraft().gameDir, "resources"));
}

@Inject(method = "hasResourceName", at = @At("HEAD"), cancellable = true)
private void useFastCheck(String name, CallbackInfoReturnable<Boolean> cir) {
cir.setReturnValue(vfix_cache.hasPath(name.replace("assets/", "")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.embeddedt.vintagefix.mixin.resourcepacks;

import net.minecraft.client.resources.FolderResourcePack;
import org.embeddedt.vintagefix.annotation.ClientOnlyMixin;
import org.embeddedt.vintagefix.util.FolderPackCache;
import org.embeddedt.vintagefix.util.Util;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.io.File;
import java.io.IOException;

@Mixin(FolderResourcePack.class)
@ClientOnlyMixin
public class MixinFolderResourcePack {
private FolderPackCache vfix_cache;

@Inject(method = "<init>*", at = @At("RETURN"))
private void afterConstructor(File folder, CallbackInfo ci) {
vfix_cache = new FolderPackCache(folder);
}

@Redirect(method = "validatePath", at = @At(value = "INVOKE", target = "Ljava/io/File;getCanonicalPath()Ljava/lang/String;"))
private static String getCanonicalPathFast(File file) throws IOException {
return Util.getCanonicalPathFast(file);
}

@Redirect(method = "getFile",
at = @At(value = "INVOKE", target = "Ljava/io/File;isFile()Z", remap = false))
public boolean redirectIsFile(File file, String name) {
return vfix_cache.hasPath(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.embeddedt.vintagefix.mixin.resourcepacks;

import lumien.resourceloader.loader.NormalResourceLoader;
import lumien.resourceloader.loader.OverridingResourceLoader;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ResourceLocation;
import org.embeddedt.vintagefix.annotation.ClientOnlyMixin;
import org.embeddedt.vintagefix.annotation.LateMixin;
import org.embeddedt.vintagefix.util.FolderPackCache;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.io.*;

@Mixin({ NormalResourceLoader.class, OverridingResourceLoader.class })
@LateMixin
@ClientOnlyMixin
public class MixinNormalResourceLoader {
private FolderPackCache vfix_cache;

private String ourFolderName;

@Inject(method = "<init>", at = @At("RETURN"))
private void afterConstruct(CallbackInfo ci) {
ourFolderName = ((Object)this instanceof OverridingResourceLoader) ? "oresources" : "resources";
vfix_cache = new FolderPackCache(new File(Minecraft.getMinecraft().gameDir, ourFolderName));
}

@Inject(method = "resourceExists", at = @At("HEAD"), cancellable = true)
private void useFastCheck(ResourceLocation rl, CallbackInfoReturnable<Boolean> cir) {
cir.setReturnValue(vfix_cache.hasPath(rl.getNamespace() + "/" + rl.getPath()));
}

@Inject(method = "getInputStream", at = @At("HEAD"), cancellable = true)
private void getFastInputStream(ResourceLocation rl, CallbackInfoReturnable<InputStream> cir) throws IOException {
try {
cir.setReturnValue(new FileInputStream(new File(new File(Minecraft.getMinecraft().gameDir, ourFolderName + "/" + rl.getNamespace()), rl.getPath())));
} catch(FileNotFoundException e) {
cir.setReturnValue(null);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.embeddedt.vintagefix.util;

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
Expand All @@ -12,40 +13,9 @@ public class CachedResourcePath {
public static final Interner<String> PATH_COMPONENT_INTERNER = Interners.newStrongInterner();
private static final Splitter SLASH_SPLITTER = Splitter.on('/');

/**
* Normalize a path by removing double slashes, etc.
* <p></p>
* This implementation avoids creating a new string unless there are actually double slashes present
* in the input path.
* @param path input path
* @return a normalized version of the path
*/
public static String normalize(String path) {
char prevChar = 0;
StringBuilder sb = null;
for(int i = 0; i < path.length(); i++) {
char thisChar = path.charAt(i);
if(prevChar != '/' || thisChar != prevChar) {
/* This character should end up in the final string. If we are using the builder, add it there. */
if(sb != null)
sb.append(thisChar);
} else {
/* This character should not end up in the final string. We need to make a buidler if we haven't
* done so yet.
*/
if(sb == null) {
sb = new StringBuilder(path.length());
sb.append(path, 0, i);
}
}
prevChar = thisChar;
}
return sb == null ? path : sb.toString();
}

public CachedResourcePath(String path, boolean intern) {
int numComponents = 1;
path = normalize(path);
path = Util.normalize(path);
for(int i = 0; i < path.length(); i++) {
if(path.charAt(i) == '/')
numComponents++;
Expand All @@ -71,4 +41,9 @@ public boolean equals(Object o) {
CachedResourcePath that = (CachedResourcePath) o;
return Arrays.equals(pathComponents, that.pathComponents);
}

@Override
public String toString() {
return Joiner.on('/').join(pathComponents);
}
}
31 changes: 31 additions & 0 deletions src/main/java/org/embeddedt/vintagefix/util/FolderPackCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.embeddedt.vintagefix.util;

import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;

import java.io.File;

public class FolderPackCache {
private final ObjectOpenHashSet<CachedResourcePath> cachedPaths = new ObjectOpenHashSet<>();
public FolderPackCache(File folderPath) {
explore(folderPath, "");
cachedPaths.trim();
}

private void explore(File folder, String path) {
File[] theFiles = folder.listFiles();
if(theFiles == null)
return;
for(File f : theFiles) {
String myPath = (path.isEmpty() ? "" : path + "/") + f.getName();
CachedResourcePath cPath = new CachedResourcePath(myPath, true);
cachedPaths.add(cPath);
if(f.isDirectory()) {
explore(f, myPath);
}
}
}

public boolean hasPath(String p) {
return cachedPaths.contains(new CachedResourcePath(p, false));
}
}
39 changes: 39 additions & 0 deletions src/main/java/org/embeddedt/vintagefix/util/Util.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.embeddedt.vintagefix.util;

import com.google.common.base.Joiner;
import org.embeddedt.vintagefix.VintageFix;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;

import javax.lang.model.SourceVersion;
Expand All @@ -27,4 +29,41 @@ public static boolean isValidClassName(String className) {
public static String normalizePathToString(Path path) {
return SLASH_JOINER.join(path);
}

public static String normalize(String path) {
char prevChar = 0;
StringBuilder sb = null;
for(int i = 0; i < path.length(); i++) {
char thisChar = path.charAt(i);
if(thisChar == '\\')
thisChar = '/';
if(prevChar != '/' || thisChar != prevChar) {
/* This character should end up in the final string. If we are using the builder, add it there. */
if(sb != null)
sb.append(thisChar);
} else {
/* This character should not end up in the final string. We need to make a buidler if we haven't
* done so yet.
*/
if(sb == null) {
sb = new StringBuilder(path.length());
sb.append(path, 0, i);
}
}
prevChar = thisChar;
}
return sb == null ? path : sb.toString();
}

private static final boolean DEBUG_CANON = false;

public static String getCanonicalPathFast(File file) throws IOException {
String ours = Util.normalize(file.getAbsolutePath());
if(DEBUG_CANON) {
String theirs = file.getCanonicalPath();
if(!theirs.equals(ours))
VintageFix.LOGGER.warn("Path mismatch, expected {} got {}", theirs, ours);
}
return ours;
}
}

0 comments on commit ea6cca8

Please sign in to comment.