Respackopts/src/main/java/io/gitlab/jfronny/respackopts/filters/DirFilterEvents.java

108 lines
5.6 KiB
Java
Raw Normal View History

2022-12-08 19:00:41 +01:00
package io.gitlab.jfronny.respackopts.filters;
import io.gitlab.jfronny.libjf.ResourcePath;
import io.gitlab.jfronny.libjf.data.manipulation.api.UserResourceEvents;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.filters.util.DirRpoResult;
2022-12-08 19:00:41 +01:00
import io.gitlab.jfronny.respackopts.gson.AttachmentHolder;
import io.gitlab.jfronny.respackopts.model.DirRpo;
import io.gitlab.jfronny.respackopts.model.cache.CachedPackState;
import io.gitlab.jfronny.respackopts.model.enums.PackCapability;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import net.minecraft.resource.*;
import net.minecraft.util.Identifier;
import java.io.*;
import java.util.LinkedList;
import java.util.List;
2022-12-08 19:00:41 +01:00
public enum DirFilterEvents implements UserResourceEvents.Open, UserResourceEvents.FindResource {
INSTANCE;
public static void init() {
UserResourceEvents.OPEN.register(INSTANCE);
UserResourceEvents.FIND_RESOURCE.register(INSTANCE);
}
@Override
public InputSupplier<InputStream> open(ResourceType type, Identifier id, InputSupplier<InputStream> previous, ResourcePack pack) {
if (!MetaCache.hasCapability(pack, PackCapability.DirFilter)) return previous;
String path = new ResourcePath(type, id).getName();
List<DirRpo> rpo = findDirRpos(pack, path);
//TODO use pattern matching for switch
DirRpoResult result = DirRpoResult.compute(path, rpo, MetaCache.getKeyByPack(pack));
if (result == DirRpoResult.ORIGINAL) return previous; // Using original file
if (result == DirRpoResult.IGNORE) return null; // No fallback
// Use fallback
DirRpoResult.Replacement replacement = (DirRpoResult.Replacement) result;
ResourcePath rp = new ResourcePath(replacement.toFallback(path));
return pack.open(rp.getType(), rp.getId());
2022-12-08 19:00:41 +01:00
}
@Override
public ResourcePack.ResultConsumer findResources(ResourceType type, String namespace, String prefix, ResourcePack.ResultConsumer previous, ResourcePack pack) {
// Warning: the Identifiers here DON'T CONTAIN THE TYPE!
// Therefore, it needs to be added when calling a method that generates a ResourcePath!
if (!MetaCache.hasCapability(pack, PackCapability.DirFilter)) return previous;
return (identifier, value) -> {
String path = type.getDirectory() + "/" + identifier.getNamespace() + "/" + identifier.getPath();
//TODO use pattern matching for switch
DirRpoResult result = DirRpoResult.compute(path, findDirRpos(pack, path), MetaCache.getKeyByPack(pack));
if (result == DirRpoResult.ORIGINAL) { // Using original file
previous.accept(identifier, value);
return;
2022-12-08 19:00:41 +01:00
}
if (result == DirRpoResult.IGNORE) return; // No fallback
// New search for fallback path
DirRpoResult.Replacement replacement = (DirRpoResult.Replacement) result;
String newPath = replacement.toFallback(path);
if (newPath.split("/", 3).length == 3) {
ResourcePath rp = new ResourcePath(newPath);
pack.findResources(rp.getType(), rp.getId().getNamespace(), rp.getId().getPath(), (resource, resVal) -> {
String fallbackPath = rp.getType().getDirectory() + "/" + resource.getNamespace() + "/" + resource.getPath();
previous.accept(new ResourcePath(replacement.toOriginal(fallbackPath)).getId(), resVal);
});
} else Respackopts.LOGGER.error("Directory fallback path MUST be long enough to support representation as identifier (3 segments), but is too short: " + newPath);
};
2022-12-08 19:00:41 +01:00
}
/**
* Identify all directory RPOs relevant to the file at name (IE in its parent directories), from outermost to innermost
*/
private List<DirRpo> findDirRpos(ResourcePack pack, String name) {
2022-12-08 19:00:41 +01:00
int li = name.lastIndexOf('/');
if (li <= 0) return List.of();
2022-12-08 19:00:41 +01:00
name = name.substring(0, li);
CachedPackState state = MetaCache.getState(MetaCache.getKeyByPack(pack));
var cache = state.cachedDirRPOs();
// This is outside computeIfAbsent because it could cause modification of the map, which is unsupported within it
if (cache.containsKey(name)) return cache.get(name);
List<DirRpo> parentRPOs = findDirRpos(pack, name);
synchronized (cache) { // This is synchronized as multiple resources might be accessed at the same time, potentially causing a CME here
return cache.computeIfAbsent(name, parentName -> {
ResourcePath rp;
try {
rp = new ResourcePath(parentName + "/" + Respackopts.FILE_EXTENSION);
} catch (Exception e) {
return parentRPOs;
}
InputSupplier<InputStream> is = UserResourceEvents.disable(() -> pack.open(rp.getType(), rp.getId()));
if (is == null) return parentRPOs;
try (Reader w = new InputStreamReader(is.get())) {
List<DirRpo> currentRPOs = new LinkedList<>(parentRPOs);
DirRpo newRPO = AttachmentHolder.deserialize(state.metadata().version, w, DirRpo.class);
newRPO.hydrate(parentName);
if (newRPO.fallback != null && !newRPO.fallback.endsWith("/"))
newRPO.fallback += "/";
currentRPOs.add(newRPO);
return currentRPOs;
} catch (IOException e) {
Respackopts.LOGGER.error("Couldn't open dir rpo " + rp.getName(), e);
}
return parentRPOs;
});
2022-12-08 19:00:41 +01:00
}
}
}