Use CacheKey/CachedPackState abstractions for MetaCache

This commit is contained in:
Johannes Frohnmeyer 2022-03-27 11:39:01 +02:00
parent dc8fae467b
commit 998056636c
Signed by: Johannes
GPG Key ID: E76429612C2929F4
11 changed files with 124 additions and 78 deletions

View File

@ -2,7 +2,6 @@ package io.gitlab.jfronny.respackopts;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import io.gitlab.jfronny.respackopts.filters.DirFilterEventImpl;
import io.gitlab.jfronny.respackopts.filters.FileFilterEventImpl;
@ -36,7 +35,6 @@ import net.fabricmc.loader.impl.gui.FabricGuiEntry;
import net.minecraft.client.MinecraftClient;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -104,14 +102,14 @@ public class Respackopts implements ClientModInitializer {
}
if (CONFIG.debugLogs)
SAVE_ACTIONS.add(() -> LOGGER.info("Save"));
SAVE_ACTIONS.add(() -> MetaCache.forEach((dataLocation, branch) -> STAR_SCRIPT.set(sanitizeString(MetaCache.getId(dataLocation)), branch::buildStarScript)));
SAVE_ACTIONS.add(() -> MetaCache.forEach((key, state) -> STAR_SCRIPT.set(sanitizeString(state.packId()), state.configBranch()::buildStarScript)));
SAVE_ACTIONS.add(() -> {
if (CONFIG.debugLogs)
LOGGER.info("Generating shader code");
StringBuilder sb = new StringBuilder();
sb.append("#ifndef respackopts_loaded");
sb.append("\n#define respackopts_loaded");
MetaCache.forEach((dataLocation, branch) -> branch.buildShader(sb, sanitizeString(MetaCache.getId(dataLocation))));
MetaCache.forEach((key, state) -> state.configBranch().buildShader(sb, sanitizeString(state.packId())));
sb.append("\n#endif");
shaderImportSource = sb.toString();
});

View File

@ -22,7 +22,7 @@ public class DirFilterEventImpl {
return previous.get();
String path = new ResourcePath(type, id).getName();
DirRpo rpo = findDirRpo(pack, path);
if (rpo != null && dirHidden(rpo, MetaCache.getId(MetaCache.getDataLocation(pack)), path)) {
if (rpo != null && dirHidden(rpo, MetaCache.getId(MetaCache.getKeyByPack(pack)), path)) {
path = findReplacementDir(path, rpo);
if (path == null) throw new FileNotFoundException();
ResourcePath rp = new ResourcePath(path);
@ -42,7 +42,7 @@ public class DirFilterEventImpl {
String path = type.getDirectory() + "/" + identifier.getNamespace() + "/" + identifier.getPath();
DirRpo rpo = findDirRpo(pack, path);
if (rpo != null) {
if (dirHidden(rpo, MetaCache.getId(MetaCache.getDataLocation(pack)), path)) {
if (dirHidden(rpo, MetaCache.getId(MetaCache.getKeyByPack(pack)), path)) {
path = findReplacementDir(path, rpo);
if (path == null)
nextRes.remove(identifier);
@ -70,7 +70,7 @@ public class DirFilterEventImpl {
return previous.get();
String path = new ResourcePath(type, id).getName();
DirRpo rpo = findDirRpo(pack, path);
if (rpo != null && dirHidden(rpo, MetaCache.getId(MetaCache.getDataLocation(pack)), path)) {
if (rpo != null && dirHidden(rpo, MetaCache.getId(MetaCache.getKeyByPack(pack)), path)) {
path = findReplacementDir(path, rpo);
if (path == null)
return false;
@ -98,7 +98,7 @@ public class DirFilterEventImpl {
}
private static DirRpo findDirRpo(ResourcePack pack, String name) {
Map<String, DirRpo> drpReg = MetaCache.DIR_RPOS.get(MetaCache.getDataLocation(pack));
Map<String, DirRpo> drpReg = MetaCache.getState(MetaCache.getKeyByPack(pack)).cachedDirRPOs();
int li = name.lastIndexOf('/');
if (li <= 0)
return null;

View File

@ -11,7 +11,7 @@ public class FileExclusionProvider {
if (rpo.conditions == null)
return false;
try {
return !rpo.conditions.evaluate(MetaCache.getId(MetaCache.getDataLocation(pack)));
return !rpo.conditions.evaluate(MetaCache.getId(MetaCache.getKeyByPack(pack)));
} catch (RpoFormatException e) {
Respackopts.LOGGER.error("Could not evaluate condition " + file, e);
return false;

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.respackopts.filters.util;
import io.gitlab.jfronny.libjf.ResourcePath;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.model.DirRpo;
import io.gitlab.jfronny.respackopts.model.FileRpo;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import net.minecraft.resource.ResourceNotFoundException;
@ -21,7 +22,7 @@ public class FileRpoSearchProvider {
public static <T> T modifyWithRpo(String fileName, ResourcePack pack, ModifiedGenerator<T> getModified, T defaultValue) {
if (FileRpoSearchProvider.isRpo(fileName))
return defaultValue;
Map<String, FileRpo> frpReg = MetaCache.FILE_RPOS.get(MetaCache.getDataLocation(pack));
Map<String, FileRpo> frpReg = MetaCache.getState(MetaCache.getKeyByPack(pack)).cachedFileRPOs();
String rpPathName = fileName + Respackopts.FILE_EXTENSION;
if (frpReg.containsKey(rpPathName))
return getModified.getModified(frpReg.get(rpPathName));

View File

@ -55,10 +55,9 @@ public class ModMenuCompat implements ModMenuApi {
.build()
);
//Pack config screens
MetaCache.forEach((dataLocation, conf) -> {
String id = MetaCache.getId(dataLocation);
ConfigCategory config = builder.getOrCreateCategory(new TranslatableText((MetaCache.getMeta(dataLocation).version >= 5 ? "rpo." : "respackopts.title.") + id));
Respackopts.GUI_FACTORY.buildCategory(conf, id, config::addEntry, agg, entryBuilder, "");
MetaCache.forEach((key, state) -> {
ConfigCategory config = builder.getOrCreateCategory(new TranslatableText((state.metadata().version >= 5 ? "rpo." : "respackopts.title.") + state.packId()));
Respackopts.GUI_FACTORY.buildCategory(state.configBranch(), state.packId(), config::addEntry, agg, entryBuilder, "");
});
return builder.build();
}

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.respackopts.mixin;
import com.mojang.blaze3d.systems.RenderSystem;
import io.gitlab.jfronny.respackopts.model.cache.CacheKey;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import io.gitlab.jfronny.respackopts.Respackopts;
import net.minecraft.client.MinecraftClient;
@ -30,7 +31,7 @@ public abstract class ResourcePackEntryMixin {
@Inject(at = @At("TAIL"), method = "render(Lnet/minecraft/client/util/math/MatrixStack;IIIIIIIZF)V")
private void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta, CallbackInfo info) {
if (this.isSelectable() && MetaCache.getDataLocationByDisplayName(pack.getDisplayName().asString()) != null) {
if (this.isSelectable() && MetaCache.getKeyByDisplayName(pack.getDisplayName().asString()) != null) {
int d = mouseX - x;
d = widget.getRowWidth() - d;
int e = mouseY - y;
@ -44,7 +45,7 @@ public abstract class ResourcePackEntryMixin {
if (!info.getReturnValue()) {
if (this.isSelectable()) {
String displayName = pack.getDisplayName().asString();
Path dataLocation = MetaCache.getDataLocationByDisplayName(displayName);
CacheKey dataLocation = MetaCache.getKeyByDisplayName(displayName);
if (dataLocation != null && rpo$selected) {
info.setReturnValue(true);
MinecraftClient c = MinecraftClient.getInstance();

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.respackopts.mixin;
import io.gitlab.jfronny.respackopts.model.cache.CacheKey;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.model.PackMeta;
@ -63,7 +64,8 @@ public class ResourcePackManagerMixin {
}
});
for (Path s : toRemove) {
MetaCache.remove(s);
CacheKey k = MetaCache.getKeyByDataLocation(s);
if (k != null) MetaCache.remove(k);
}
MetaCache.save();
}

View File

@ -0,0 +1,10 @@
package io.gitlab.jfronny.respackopts.model.cache;
import java.nio.file.Path;
public record CacheKey(String displayName, String packName, Path dataLocation) {
@Override
public String toString() {
return "CacheKey{displayName='" + displayName + "', packName='" + packName + "', dataLocation=" + dataLocation + '}';
}
}

View File

@ -0,0 +1,23 @@
package io.gitlab.jfronny.respackopts.model.cache;
import io.gitlab.jfronny.respackopts.model.DirRpo;
import io.gitlab.jfronny.respackopts.model.FileRpo;
import io.gitlab.jfronny.respackopts.model.PackMeta;
import io.gitlab.jfronny.respackopts.model.tree.ConfigBranch;
import java.util.HashMap;
import java.util.Map;
public record CachedPackState(
String packId,
String displayName,
String packName,
ConfigBranch configBranch,
PackMeta metadata,
Map<String, FileRpo> cachedFileRPOs,
Map<String, DirRpo> cachedDirRPOs
) {
public CachedPackState(CacheKey key, PackMeta meta, ConfigBranch branch) {
this(meta.id, key.displayName(), key.packName(), branch, meta, new HashMap<>(), new HashMap<>());
}
}

View File

@ -2,6 +2,8 @@ package io.gitlab.jfronny.respackopts.model.condition;
import com.google.gson.JsonParseException;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.model.cache.CacheKey;
import io.gitlab.jfronny.respackopts.model.cache.CachedPackState;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import io.gitlab.jfronny.respackopts.model.ThrowingBiConsumer;
import io.gitlab.jfronny.respackopts.model.tree.ConfigBranch;
@ -46,10 +48,10 @@ public record RpoBooleanCondition(String name) implements Condition {
}
}
AtomicReference<Boolean> result = new AtomicReference<>(null);
MetaCache.forEach((ThrowingBiConsumer<Path, ConfigBranch, ? extends RpoFormatException>) (dataLocation, branch) -> {
if (Objects.equals(MetaCache.getId(dataLocation), sourcePack)) {
MetaCache.forEach((ThrowingBiConsumer<CacheKey, CachedPackState, ? extends RpoFormatException>) (key, data) -> {
if (Objects.equals(MetaCache.getId(key), sourcePack)) {
try {
result.set(branch.getBoolean(finalCondition));
result.set(data.configBranch().getBoolean(finalCondition));
} catch (RpoFormatException ex) {
throw new RpoFormatException("Could not get value", ex);
}

View File

@ -1,10 +1,10 @@
package io.gitlab.jfronny.respackopts.util;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.model.DirRpo;
import io.gitlab.jfronny.respackopts.model.FileRpo;
import io.gitlab.jfronny.respackopts.model.PackMeta;
import io.gitlab.jfronny.respackopts.model.ThrowingBiConsumer;
import io.gitlab.jfronny.respackopts.model.cache.CacheKey;
import io.gitlab.jfronny.respackopts.model.cache.CachedPackState;
import io.gitlab.jfronny.respackopts.model.enums.ConfigSyncMode;
import io.gitlab.jfronny.respackopts.model.enums.PackCapability;
import io.gitlab.jfronny.respackopts.model.tree.ConfigBranch;
@ -20,21 +20,18 @@ import java.util.HashMap;
import java.util.Map;
public class MetaCache {
private static final Map<Path, ConfigBranch> CONFIG_BRANCH = new HashMap<>(); // pack.rpo -> active config branch
private static final Map<Path, PackMeta> PACK_METAS = new HashMap<>(); // pack.rpo -> original pack metadata
private static final Map<Path, String> PACK_IDS = new HashMap<>(); // pack.rpo -> pack id
private static final Map<String, Path> DISPLAY_NAME_LOOKUP = new HashMap<>(); // display name -> pack.rpo
private static final Map<String, Path> PACK_NAME_LOOKUP = new HashMap<>(); // pack name -> pack.rpo
public static final Map<Path, Map<String, DirRpo>> DIR_RPOS = new HashMap<>(); // pack.rpo -> .rpo path -> parsed .rpo
public static final Map<Path, Map<String, FileRpo>> FILE_RPOS = new HashMap<>(); // pack.rpo -> .rpo path -> parsed .rpo
public static void remove(Path packRpo) {
CONFIG_BRANCH.remove(packRpo);
PACK_METAS.remove(packRpo);
PACK_IDS.remove(packRpo);
//DISPLAY_NAME_LOOKUP
//PACK_NAME_LOOKUP
DIR_RPOS.remove(packRpo);
FILE_RPOS.remove(packRpo);
private static final Map<CacheKey, CachedPackState> PACK_STATES = new HashMap<>();
private static final Map<String, CacheKey> KEYS_BY_DISPLAY_NAME = new HashMap<>();
private static final Map<String, CacheKey> KEYS_BY_PACK_NAME = new HashMap<>();
private static final Map<Path, CacheKey> KEYS_BY_DATA_LOCATION = new HashMap<>();
public static void remove(CacheKey key) {
CacheKey k;
// Remove the key and ones that share a property
// Example: if an old pack has the same data location but a different name, it should still be removed
if ((k = KEYS_BY_DATA_LOCATION.remove(key.dataLocation())) != null) remove(k);
if ((k = KEYS_BY_PACK_NAME.remove(key.packName())) != null) remove(k);
if ((k = KEYS_BY_DISPLAY_NAME.remove(key.displayName())) != null) remove(k);
PACK_STATES.remove(key);
}
public static void addFromScan(String displayName, String packName, PackMeta meta, Path dataLocation) {
@ -43,17 +40,23 @@ public class MetaCache {
}
meta.conf.setVersion(meta.version);
if (meta.version < 5) meta.capabilities.add(PackCapability.DirFilter);
remove(dataLocation);
if (CONFIG_BRANCH.containsKey(dataLocation))
CONFIG_BRANCH.get(dataLocation).sync(meta.conf, ConfigSyncMode.RESPACK_LOAD);
else
CONFIG_BRANCH.put(dataLocation, meta.conf.clone());
DISPLAY_NAME_LOOKUP.put(displayName, dataLocation);
PACK_NAME_LOOKUP.put(packName, dataLocation);
PACK_METAS.put(dataLocation, meta);
DIR_RPOS.put(dataLocation, new HashMap<>());
FILE_RPOS.put(dataLocation, new HashMap<>());
PACK_IDS.put(dataLocation, meta.id);
// Reuse the existing branch with a RESPACK_LOAD sync if present
ConfigBranch branch;
if (KEYS_BY_DATA_LOCATION.containsKey(dataLocation)) {
branch = getState(KEYS_BY_DATA_LOCATION.get(dataLocation)).configBranch();
branch.sync(meta.conf, ConfigSyncMode.RESPACK_LOAD);
} else {
branch = meta.conf.clone();
}
// Register the key and state in the relevant maps
CacheKey key = new CacheKey(displayName, packName, dataLocation);
CachedPackState state = new CachedPackState(key, meta, branch);
remove(key);
PACK_STATES.put(key, state);
KEYS_BY_DISPLAY_NAME.put(key.displayName(), key);
KEYS_BY_PACK_NAME.put(key.packName(), key);
KEYS_BY_DATA_LOCATION.put(key.dataLocation(), key);
// Move old configs to the new location
if (!dataLocation.startsWith(Respackopts.FALLBACK_CONF_DIR)) {
Path legacyLocation = Respackopts.FALLBACK_CONF_DIR.resolve(meta.id + ".json");
if (Files.exists(legacyLocation) && !Files.exists(dataLocation)) {
@ -64,15 +67,15 @@ public class MetaCache {
}
}
}
load(dataLocation);
load(key);
save(dataLocation, meta.conf);
}
public static void save() {
if (Respackopts.CONFIG.debugLogs)
Respackopts.LOGGER.info("Saving configs");
for (Map.Entry<Path, ConfigBranch> e : CONFIG_BRANCH.entrySet()) {
save(e.getKey(), e.getValue());
for (Map.Entry<CacheKey, CachedPackState> e : PACK_STATES.entrySet()) {
save(e.getKey().dataLocation(), e.getValue().configBranch());
}
for (Runnable action : Respackopts.SAVE_ACTIONS) {
action.run();
@ -88,59 +91,66 @@ public class MetaCache {
}
}
public static void load(Path dataLocation) {
if (Files.exists(dataLocation)) {
public static void load(CacheKey key) {
if (Files.exists(key.dataLocation())) {
if (Respackopts.CONFIG.debugLogs)
Respackopts.LOGGER.info("Loading configs for: " + dataLocation);
try (Reader reader = Files.newBufferedReader(dataLocation)) {
Respackopts.LOGGER.info("Loading configs for: " + key);
try (Reader reader = Files.newBufferedReader(key.dataLocation())) {
ConfigBranch b = Respackopts.GSON.fromJson(reader, ConfigBranch.class);
if (CONFIG_BRANCH.containsKey(dataLocation))
CONFIG_BRANCH.get(dataLocation).sync(b, ConfigSyncMode.CONF_LOAD);
Respackopts.STAR_SCRIPT.set(Respackopts.sanitizeString(getId(dataLocation)), b::buildStarScript);
if (PACK_STATES.containsKey(key))
getBranch(key).sync(b, ConfigSyncMode.CONF_LOAD);
Respackopts.STAR_SCRIPT.set(Respackopts.sanitizeString(getId(key)), b::buildStarScript);
} catch (IOException e) {
Respackopts.LOGGER.error("Failed to load " + dataLocation, e);
Respackopts.LOGGER.error("Failed to load " + key, e);
}
}
}
public static Path getDataLocation(ResourcePack pack) {
return PACK_NAME_LOOKUP.get(pack.getName());
public static PackMeta getMeta(CacheKey key) {
return getState(key).metadata();
}
public static PackMeta getMeta(Path dataLocation) {
return PACK_METAS.get(dataLocation);
public static @Nullable CacheKey getKeyByPack(ResourcePack pack) {
return KEYS_BY_PACK_NAME.get(pack.getName());
}
public static @Nullable Path getDataLocationByDisplayName(String displayName) {
if (!DISPLAY_NAME_LOOKUP.containsKey(displayName)) return null;
return DISPLAY_NAME_LOOKUP.get(displayName);
public static @Nullable CacheKey getKeyByDisplayName(String displayName) {
return KEYS_BY_DISPLAY_NAME.get(displayName);
}
public static ConfigBranch getBranch(Path dataLocation) {
return CONFIG_BRANCH.get(dataLocation);
public static @Nullable CacheKey getKeyByDataLocation(Path dataLocation) {
return KEYS_BY_DATA_LOCATION.get(dataLocation);
}
public static String getId(Path dataLocation) {
return PACK_IDS.get(dataLocation);
public static ConfigBranch getBranch(CacheKey key) {
return getState(key).configBranch();
}
public static String getId(CacheKey key) {
return getState(key).packId();
}
public static CachedPackState getState(CacheKey key) {
return PACK_STATES.get(key);
}
public static boolean hasCapability(ResourcePack pack, PackCapability capability) {
Path dataLocation = getDataLocation(pack);
if (dataLocation == null) return false;
if (!PACK_METAS.containsKey(dataLocation)) {
CacheKey key = getKeyByPack(pack);
if (key == null) return false;
if (!PACK_STATES.containsKey(key)) {
StringBuilder sb = new StringBuilder("Could not get pack with \"");
sb.append(dataLocation);
sb.append(key);
sb.append("\" (available: ");
for (Path path : PACK_METAS.keySet()) {
for (CacheKey path : PACK_STATES.keySet()) {
sb.append(path).append(", ");
}
throw new NullPointerException(sb.substring(0, sb.length() - 2) + ')');
}
return PACK_METAS.get(dataLocation).capabilities.contains(capability);
return getMeta(key).capabilities.contains(capability);
}
public static <TEx extends Exception> void forEach(ThrowingBiConsumer<Path, ConfigBranch, TEx> idAndBranchConsumer) throws TEx {
for (Map.Entry<Path, ConfigBranch> entry : CONFIG_BRANCH.entrySet()) {
public static <TEx extends Exception> void forEach(ThrowingBiConsumer<CacheKey, CachedPackState, TEx> idAndBranchConsumer) throws TEx {
for (Map.Entry<CacheKey, CachedPackState> entry : PACK_STATES.entrySet()) {
idAndBranchConsumer.accept(entry.getKey(), entry.getValue());
}
}