Save pack configs in the packs directory (SomePack.zip -> SomePack.zip.rpo) if possible, needs testing

This commit is contained in:
JFronny 2021-11-19 15:16:08 +01:00
parent 283b7ba176
commit e24405b611
No known key found for this signature in database
GPG Key ID: BEC5ACBBD4EE17E5
25 changed files with 296 additions and 183 deletions

1
.gitignore vendored
View File

@ -122,6 +122,7 @@ gradle-app.setting
!/run/saves/New World
/run/saves/New World/*
!/run/saves/New World/datapacks
/run/saves/New World/datapacks/battys-coordinates.rpo
/test/
logs/

View File

@ -23,5 +23,5 @@ You will then need to include this file in the places you want to access its val
## Using the values
All values respackotps exposes follow the form: `<id>_<entry>` or `<id>_<category>_<entry>`
To view the code respackotps generates for your pack, you can run the `/rpo dump frex` command in minecraft (You must enable the `debugCommands` config option for this).
To view the code respackotps generates for your pack, you can run the `/rpo dump glsl` command in minecraft (You must enable the `debugCommands` config option for this).
This will create a frex.frag file in your .minecraft/respackotps directory containing the generated shader code (available since 2.7.0).

View File

@ -2,21 +2,25 @@ package io.gitlab.jfronny.respackopts;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.gitlab.jfronny.respackopts.integration.FrexCompat;
import io.gitlab.jfronny.respackopts.model.*;
import io.gitlab.jfronny.respackopts.model.condition.Condition;
import io.gitlab.jfronny.respackopts.model.enums.ConfigSyncMode;
import io.gitlab.jfronny.respackopts.model.enums.PackCapability;
import io.gitlab.jfronny.respackopts.filters.DirFilterEventImpl;
import io.gitlab.jfronny.respackopts.filters.FileFilterEventImpl;
import io.gitlab.jfronny.respackopts.gson.*;
import io.gitlab.jfronny.respackopts.gson.entry.BooleanEntrySerializer;
import io.gitlab.jfronny.respackopts.gson.entry.ConfigBranchSerializer;
import io.gitlab.jfronny.respackopts.gson.entry.EnumEntrySerializer;
import io.gitlab.jfronny.respackopts.gson.entry.NumericEntrySerializer;
import io.gitlab.jfronny.respackopts.util.RpoCommand;
import io.gitlab.jfronny.respackopts.model.tree.*;
import io.gitlab.jfronny.respackopts.filters.FileFilterEventImpl;
import io.gitlab.jfronny.respackopts.integration.FrexCompat;
import io.gitlab.jfronny.respackopts.model.ConfigFile;
import io.gitlab.jfronny.respackopts.model.DirRpo;
import io.gitlab.jfronny.respackopts.model.FileRpo;
import io.gitlab.jfronny.respackopts.model.condition.Condition;
import io.gitlab.jfronny.respackopts.model.tree.ConfigBooleanEntry;
import io.gitlab.jfronny.respackopts.model.tree.ConfigBranch;
import io.gitlab.jfronny.respackopts.model.tree.ConfigEnumEntry;
import io.gitlab.jfronny.respackopts.model.tree.ConfigNumericEntry;
import io.gitlab.jfronny.respackopts.util.GuiFactory;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import io.gitlab.jfronny.respackopts.util.RpoCommand;
import meteordevelopment.starscript.Script;
import meteordevelopment.starscript.StandardLib;
import meteordevelopment.starscript.Starscript;
@ -26,29 +30,20 @@ import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.impl.gui.FabricGuiEntry;
import net.minecraft.client.MinecraftClient;
import net.minecraft.resource.ResourcePack;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@Environment(EnvType.CLIENT)
public class Respackopts implements ClientModInitializer {
public static final Map<String, ConfigBranch> CONFIG_BRANCH = new HashMap<>();
public static final Map<String, PackMeta> PACK_METAS = new HashMap<>();
public static final Map<String, String> DISPLAY_NAME_LOOKUP = new HashMap<>();
public static final Map<String, String> PACK_NAME_LOOKUP = new HashMap<>();
public static final Integer META_VERSION = 6;
public static final String FILE_EXTENSION = ".rpo";
public static final Gson GSON;
@ -61,7 +56,7 @@ public class Respackopts implements ClientModInitializer {
public static final GuiFactory GUI_FACTORY = new GuiFactory();
public static boolean forcePackReload = false;
public static Path CONF_DIR;
public static Path FALLBACK_CONF_DIR;
public static ConfigFile CONFIG;
public static final Identifier RPO_SHADER_ID = new Identifier(Respackopts.ID, "config_supplier");
private static String shaderImportSource;
@ -81,7 +76,7 @@ public class Respackopts implements ClientModInitializer {
.setPrettyPrinting()
.create();
try {
CONF_DIR = FabricLoader.getInstance().getConfigDir().resolve(ID);
FALLBACK_CONF_DIR = FabricLoader.getInstance().getConfigDir().resolve(ID);
CONFIG = ConfigFile.load();
} catch (Throwable e) {
LOGGER.error("Could not resolve config directory", e);
@ -94,29 +89,17 @@ public class Respackopts implements ClientModInitializer {
StandardLib.init(STAR_SCRIPT);
}
public static String getId(ResourcePack pack) {
return PACK_NAME_LOOKUP.get(pack.getName());
}
public static boolean hasCapability(ResourcePack pack, PackCapability capability) {
String id = getId(pack);
if (id == null) return false;
return PACK_METAS.get(id).capabilities.contains(capability);
}
@Override
public void onInitializeClient() {
try {
Files.createDirectories(CONF_DIR);
Files.createDirectories(FALLBACK_CONF_DIR);
} catch (IOException e) {
LOGGER.error("Could not initialize config directory", e);
}
if (CONFIG.debugLogs)
SAVE_ACTIONS.add(() -> LOGGER.info("Save"));
SAVE_ACTIONS.add(() -> {
for (Map.Entry<String, ConfigBranch> e : CONFIG_BRANCH.entrySet()) {
STAR_SCRIPT.set(sanitizeString(e.getKey()), () -> e.getValue().buildStarscript());
}
MetaCache.forEach((id, branch) -> STAR_SCRIPT.set(sanitizeString(id), branch::buildStarscript));
});
SAVE_ACTIONS.add(() -> {
if (CONFIG.debugLogs)
@ -124,9 +107,7 @@ public class Respackopts implements ClientModInitializer {
StringBuilder sb = new StringBuilder();
sb.append("#ifndef respackopts_loaded");
sb.append("\n#define respackopts_loaded");
for (Map.Entry<String, ConfigBranch> e : CONFIG_BRANCH.entrySet()) {
e.getValue().buildShader(sb, sanitizeString(e.getKey()));
}
MetaCache.forEach((id, branch) -> branch.buildShader(sb, sanitizeString(id)));
sb.append("\n#endif");
shaderImportSource = sb.toString();
});
@ -139,38 +120,6 @@ public class Respackopts implements ClientModInitializer {
}
}
public static void save() {
if (CONFIG.debugLogs)
LOGGER.info("Saving configs");
for (Map.Entry<String, ConfigBranch> e : CONFIG_BRANCH.entrySet()) {
try (Writer writer = Files.newBufferedWriter(CONF_DIR.resolve(e.getKey() + ".json"))) {
GSON.toJson(e.getValue(), writer);
writer.flush();
} catch (IOException ex) {
LOGGER.error("Could not save config", ex);
}
}
for (Runnable action : SAVE_ACTIONS) {
action.run();
}
}
public static void load(String id) {
Path q = CONF_DIR.resolve(id + ".json");
if (Files.exists(q)) {
if (CONFIG.debugLogs)
LOGGER.info("Loading configs for: " + id);
try (Reader reader = Files.newBufferedReader(q)) {
ConfigBranch b = GSON.fromJson(reader, ConfigBranch.class);
if (CONFIG_BRANCH.containsKey(id))
CONFIG_BRANCH.get(id).sync(b, ConfigSyncMode.CONF_LOAD);
STAR_SCRIPT.set(sanitizeString(id), b::buildStarscript);
} catch (IOException e) {
LOGGER.error("Failed to load " + id, e);
}
}
}
public static String sanitizeString(String s) {
// This trims whitespace/underscores and removes non-alphabetical or underscore characters

View File

@ -2,6 +2,7 @@ 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.util.MetaCache;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.model.DirRpo;
import io.gitlab.jfronny.respackopts.model.enums.PackCapability;
@ -12,15 +13,16 @@ import net.minecraft.util.Identifier;
import java.io.*;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
public class DirFilterEventImpl {
public static void init() {
UserResourceEvents.OPEN.register((type, id, previous, pack) -> {
if (!Respackopts.hasCapability(pack, PackCapability.DirFilter))
if (!MetaCache.hasCapability(pack, PackCapability.DirFilter))
return previous;
String path = new ResourcePath(type, id).getName();
DirRpo rpo = findDirRpo(pack, path);
if (rpo != null && dirHidden(rpo, Respackopts.getId(pack))) {
if (rpo != null && dirHidden(rpo, MetaCache.getId(pack))) {
path = findReplacementDir(path, rpo);
if (path == null) throw new FileNotFoundException();
ResourcePath rp = new ResourcePath(path);
@ -31,15 +33,15 @@ public class DirFilterEventImpl {
UserResourceEvents.FIND_RESOURCE.register((type, namespace, prefix, maxDepth, pathFilter, previous, 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 (!Respackopts.hasCapability(pack, PackCapability.DirFilter))
if (!MetaCache.hasCapability(pack, PackCapability.DirFilter))
return previous;
Collection<Identifier> nextRes = new LinkedHashSet<>(previous);
boolean dirFilterAdditive = Respackopts.hasCapability(pack, PackCapability.DirFilterAdditive);
boolean dirFilterAdditive = MetaCache.hasCapability(pack, PackCapability.DirFilterAdditive);
for (Identifier identifier : previous) {
String path = type.getDirectory() + "/" + identifier.getNamespace() + "/" + identifier.getPath();
DirRpo rpo = findDirRpo(pack, path);
if (rpo != null) {
if (dirHidden(rpo, Respackopts.getId(pack))) {
if (dirHidden(rpo, MetaCache.getId(pack))) {
path = findReplacementDir(path, rpo);
if (path == null)
nextRes.remove(identifier);
@ -63,11 +65,11 @@ public class DirFilterEventImpl {
return nextRes;
});
UserResourceEvents.CONTAINS.register((type, id, previous, pack) -> {
if (!Respackopts.hasCapability(pack, PackCapability.DirFilter))
if (!MetaCache.hasCapability(pack, PackCapability.DirFilter))
return previous;
String path = new ResourcePath(type, id).getName();
DirRpo rpo = findDirRpo(pack, path);
if (rpo != null && dirHidden(rpo, Respackopts.getId(pack))) {
if (rpo != null && dirHidden(rpo, MetaCache.getId(pack))) {
path = findReplacementDir(path, rpo);
if (path == null)
return false;
@ -95,13 +97,17 @@ public class DirFilterEventImpl {
}
private static DirRpo findDirRpo(ResourcePack pack, String name) {
Map<String, DirRpo> drpReg = MetaCache.DIR_RPOS.get(MetaCache.getId(pack));
int li = name.lastIndexOf('/');
if (li <= 0)
return null;
name = name.substring(0, li);
if (drpReg.containsKey(name)) return drpReg.get(name);
DirRpo drp = findDirRpo(pack, name);
if (drp != null)
if (drp != null) {
drpReg.put(name, drp);
return drp;
}
ResourcePath rp;
try {
rp = new ResourcePath(name + "/.rpo");
@ -115,6 +121,7 @@ public class DirFilterEventImpl {
drp.path = name;
if (drp.fallback != null && !drp.fallback.endsWith("/"))
drp.fallback += "/";
drpReg.put(name, drp);
return drp;
} catch (IOException e) {
Respackopts.LOGGER.error("Couldn't open dir rpo", e);

View File

@ -2,7 +2,7 @@ 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.util.MetaCache;
import io.gitlab.jfronny.respackopts.model.enums.PackCapability;
import io.gitlab.jfronny.respackopts.filters.util.FileExclusionProvider;
import io.gitlab.jfronny.respackopts.filters.util.FileExpansionProvider;
@ -74,6 +74,6 @@ public class FileFilterEventImpl {
}
private static boolean skip(ResourcePack pack) {
return !(pack instanceof AbstractFileResourcePack) || !Respackopts.hasCapability(pack, PackCapability.FileFilter);
return !(pack instanceof AbstractFileResourcePack) || !MetaCache.hasCapability(pack, PackCapability.FileFilter);
}
}

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.respackopts.filters.util;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.util.RpoFormatException;
import net.minecraft.resource.ResourcePack;
@ -10,7 +11,7 @@ public class FileExclusionProvider {
if (rpo.conditions == null)
return false;
try {
return !rpo.conditions.evaluate(Respackopts.getId(pack));
return !rpo.conditions.evaluate(MetaCache.getId(pack));
} catch (RpoFormatException e) {
Respackopts.LOGGER.error("Could not evaluate condition " + name, e);
return false;

View File

@ -3,11 +3,13 @@ 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.FileRpo;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import net.minecraft.resource.ResourcePack;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Map;
public class FileRpoSearchProvider {
public static boolean isRpo(String fileName) {
@ -17,9 +19,13 @@ 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.getId(pack));
String rpPathName = fileName + Respackopts.FILE_EXTENSION;
if (frpReg.containsKey(rpPathName))
return getModified.getModified(frpReg.get(rpPathName));
ResourcePath rpoPath;
try {
rpoPath = new ResourcePath(fileName + Respackopts.FILE_EXTENSION);
rpoPath = new ResourcePath(rpPathName);
if (!pack.contains(rpoPath.getType(), rpoPath.getId()))
return defaultValue;
} catch (Throwable e) {
@ -27,7 +33,10 @@ public class FileRpoSearchProvider {
return defaultValue;
}
try (InputStream stream = pack.open(rpoPath.getType(), rpoPath.getId()); Reader w = new InputStreamReader(stream)) {
return getModified.getModified(Respackopts.GSON.fromJson(w, FileRpo.class));
FileRpo frp = Respackopts.GSON.fromJson(w, FileRpo.class);
frp.path = rpPathName;
frpReg.put(rpPathName, frp);
return getModified.getModified(frp);
}
catch (Exception e) {
Respackopts.LOGGER.error("Could not generate replacement for " + rpoPath.getName() + " in " + pack.getName(), e);

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.respackopts.gson;
import com.google.gson.*;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.model.DirRpo;
import io.gitlab.jfronny.respackopts.model.condition.Condition;

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.respackopts.gson;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.model.FileRpo;
import io.gitlab.jfronny.respackopts.model.condition.Condition;
import meteordevelopment.starscript.Script;

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.respackopts.integration;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.model.ConfigFile;
import io.gitlab.jfronny.respackopts.model.enums.PackReloadType;
@ -25,7 +26,7 @@ public class ModMenuCompat implements ModMenuApi {
builder.setSavingRunnable(() -> {
if (Respackopts.CONFIG.debugLogs) Respackopts.LOGGER.info("ModMenuCompat SavingRunnable " + agg.get());
Respackopts.CONFIG.save();
Respackopts.save();
MetaCache.save();
if (agg.get() == PackReloadType.Resource) {
DashLoaderCompat.requestForceReload();
Respackopts.forceReloadResources();
@ -54,8 +55,8 @@ public class ModMenuCompat implements ModMenuApi {
.build()
);
//Pack config screens
Respackopts.CONFIG_BRANCH.forEach((id, conf) -> {
ConfigCategory config = builder.getOrCreateCategory(new TranslatableText((Respackopts.PACK_METAS.get(id).version >= 5 ? "rpo." : "respackopts.title.") + id));
MetaCache.forEach((id, conf) -> {
ConfigCategory config = builder.getOrCreateCategory(new TranslatableText((MetaCache.getMeta(id).version >= 5 ? "rpo." : "respackopts.title.") + id));
Respackopts.GUI_FACTORY.buildCategory(conf, id, config::addEntry, agg, entryBuilder, "");
});
return builder.build();

View File

@ -0,0 +1,13 @@
package io.gitlab.jfronny.respackopts.mixin;
import net.minecraft.resource.AbstractFileResourcePack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.io.File;
@Mixin(AbstractFileResourcePack.class)
public interface AbstractFileResourcePackAccessor {
@Accessor("base")
File getBase();
}

View File

@ -1,6 +1,7 @@
package io.gitlab.jfronny.respackopts.mixin;
import com.mojang.blaze3d.systems.RenderSystem;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import io.gitlab.jfronny.respackopts.Respackopts;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawableHelper;
@ -27,7 +28,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() && Respackopts.DISPLAY_NAME_LOOKUP.containsKey(pack.getDisplayName().asString())) {
if (this.isSelectable() && MetaCache.getPackIdByDisplayName(pack.getDisplayName().asString()) != null) {
int d = mouseX - x;
d = widget.getRowWidth() - d;
int e = mouseY - y;
@ -41,11 +42,11 @@ public abstract class ResourcePackEntryMixin {
if (!info.getReturnValue()) {
if (this.isSelectable()) {
String k = pack.getDisplayName().asString();
if (Respackopts.DISPLAY_NAME_LOOKUP.containsKey(k) && rpo$selected) {
k = Respackopts.DISPLAY_NAME_LOOKUP.get(k);
k = MetaCache.getPackIdByDisplayName(k);
if (k != null && rpo$selected) {
info.setReturnValue(true);
MinecraftClient c = MinecraftClient.getInstance();
c.setScreen(Respackopts.GUI_FACTORY.buildGui(Respackopts.CONFIG_BRANCH.get(k), k, c.currentScreen));
c.setScreen(Respackopts.GUI_FACTORY.buildGui(MetaCache.getBranch(k), k, c.currentScreen));
}
}
}

View File

@ -1,14 +1,9 @@
package io.gitlab.jfronny.respackopts.mixin;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.model.enums.ConfigSyncMode;
import io.gitlab.jfronny.respackopts.model.enums.PackCapability;
import io.gitlab.jfronny.respackopts.util.MetadataLocateResult;
import io.gitlab.jfronny.respackopts.model.PackMeta;
import net.minecraft.resource.ResourcePack;
import net.minecraft.resource.ResourcePackManager;
import net.minecraft.resource.ResourcePackProfile;
import net.minecraft.resource.ResourceType;
import net.minecraft.resource.*;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@ -17,6 +12,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.util.Map;
@Mixin(ResourcePackManager.class)
@ -25,47 +21,39 @@ public class ResourcePackManagerMixin {
@Inject(at = @At("TAIL"), method = "scanPacks()V")
private void scanPacks(CallbackInfo info) {
Respackopts.DISPLAY_NAME_LOOKUP.clear();
MetaCache.clear();
profiles.forEach((s, v) -> {
MetadataLocateResult scan = rpo$locateMetadata(v);
if (scan.hasMeta()) {
try (InputStream is = scan.pack().open(scan.type(), Respackopts.CONF_ID); InputStreamReader isr = new InputStreamReader(is)) {
ResourcePack rpi = v.createResourcePack();
ResourceType packConfType = null;
for (ResourceType type : ResourceType.values()) {
if (rpi.contains(type, Respackopts.CONF_ID)) {
packConfType = type;
}
}
if (packConfType != null) {
try (InputStream is = rpi.open(packConfType, Respackopts.CONF_ID); InputStreamReader isr = new InputStreamReader(is)) {
String displayName = v.getDisplayName().asString();
String packName = rpi.getName();
PackMeta conf = Respackopts.GSON.fromJson(isr, PackMeta.class);
if (Respackopts.CONFIG.debugLogs)
Respackopts.LOGGER.info("Discovered pack: " + conf.id);
if (Respackopts.META_VERSION < conf.version) {
Respackopts.LOGGER.error(s + " was not loaded as it specifies a newer respackopts version than is installed");
Respackopts.LOGGER.error(displayName + " was not loaded as it specifies a newer respackopts version than is installed");
return;
}
if (Respackopts.META_VERSION > conf.version) {
Respackopts.LOGGER.warn(s + " uses an outdated RPO format (" + conf.version + "). Although this is supported, using the latest version (" + Respackopts.META_VERSION + ") is recommended");
if (rpi instanceof AbstractFileResourcePack arr) {
Path pack = ((AbstractFileResourcePackAccessor)arr).getBase().toPath();
Path packData = pack.getParent().resolve(pack.getFileName().toString() + Respackopts.FILE_EXTENSION);
MetaCache.addFromScan(displayName, packName, conf, packData);
}
else {
MetaCache.addFromScan(displayName, packName, conf, Respackopts.FALLBACK_CONF_DIR.resolve(conf.id + ".json"));
}
conf.conf.setVersion(conf.version);
if (conf.version < 5)
conf.capabilities.add(PackCapability.DirFilter);
if (!Respackopts.CONFIG_BRANCH.containsKey(conf.id))
Respackopts.CONFIG_BRANCH.put(conf.id, conf.conf);
else
Respackopts.CONFIG_BRANCH.get(conf.id).sync(conf.conf, ConfigSyncMode.RESPACK_LOAD);
Respackopts.DISPLAY_NAME_LOOKUP.put(v.getDisplayName().asString(), conf.id);
Respackopts.PACK_NAME_LOOKUP.put(v.createResourcePack().getName(), conf.id);
Respackopts.PACK_METAS.put(conf.id, conf);
Respackopts.load(conf.id);
} catch (Throwable e) {
Respackopts.LOGGER.error("Could not initialize pack meta for " + s, e);
}
}
});
Respackopts.save();
MetaCache.save();
}
private MetadataLocateResult rpo$locateMetadata(ResourcePackProfile v) {
ResourcePack pack = v.createResourcePack();
for (ResourceType type : ResourceType.values()) {
if (pack.contains(type, Respackopts.CONF_ID))
return new MetadataLocateResult(pack, true, type);
}
return new MetadataLocateResult(pack, false, null);
}
}

View File

@ -12,7 +12,7 @@ public class ConfigFile {
public boolean debugCommands = false;
public boolean debugLogs = false;
public boolean dashloaderCompat = true;
private static final Path rpoPath = Respackopts.CONF_DIR.resolve("_respackopts.conf");
private static final Path rpoPath = Respackopts.FALLBACK_CONF_DIR.resolve("_respackopts.conf");
public static ConfigFile load() {
if (!Files.exists(rpoPath))
@ -26,9 +26,9 @@ public class ConfigFile {
}
public void save() {
if (!Files.exists(Respackopts.CONF_DIR)) {
if (!Files.exists(Respackopts.FALLBACK_CONF_DIR)) {
try {
Files.createDirectories(Respackopts.CONF_DIR);
Files.createDirectories(Respackopts.FALLBACK_CONF_DIR);
} catch (IOException e) {
Respackopts.LOGGER.error("Could not create config dir", e);
return;

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.respackopts.model;
import io.gitlab.jfronny.libjf.gson.GsonHidden;
import io.gitlab.jfronny.respackopts.model.condition.Condition;
import meteordevelopment.starscript.Script;
@ -10,4 +11,7 @@ public class FileRpo {
public Condition conditions;
public Set<String> fallbacks;
public Map<String, Script> expansions;
@GsonHidden
public String path;
}

View File

@ -0,0 +1,5 @@
package io.gitlab.jfronny.respackopts.model;
public interface ThrowingBiConsumer<T, U, TEx extends Throwable> {
void accept(T var1, U var2) throws TEx;
}

View File

@ -1,12 +1,13 @@
package io.gitlab.jfronny.respackopts.model.condition;
import com.google.gson.JsonParseException;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.util.MetaCache;
import io.gitlab.jfronny.respackopts.model.ThrowingBiConsumer;
import io.gitlab.jfronny.respackopts.model.tree.ConfigBranch;
import io.gitlab.jfronny.respackopts.util.RpoFormatException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
public record RpoBooleanCondition(String name) implements Condition {
public RpoBooleanCondition {
@ -22,15 +23,19 @@ public record RpoBooleanCondition(String name) implements Condition {
}
String sourcePack = condition.split(":")[0];
condition = condition.substring(condition.indexOf(':') + 1);
for (Map.Entry<String, ConfigBranch> e : Respackopts.CONFIG_BRANCH.entrySet()) {
if (Objects.equals(e.getKey(), sourcePack)) {
final String finalCondition = condition;
AtomicReference<Boolean> result = new AtomicReference<>(null);
MetaCache.forEach((ThrowingBiConsumer<String, ConfigBranch, ? extends RpoFormatException>) (id, branch) -> {
if (Objects.equals(id, sourcePack)) {
try {
return e.getValue().getBoolean(condition);
result.set(branch.getBoolean(finalCondition));
} catch (RpoFormatException ex) {
throw new RpoFormatException("Could not get value", ex);
}
}
}
throw new RpoFormatException("Could not find pack with specified ID: " + packId);
});
if (result.get() == null)
throw new RpoFormatException("Could not find pack with specified ID: " + packId);
return result.get();
}
}

View File

@ -139,7 +139,7 @@ public class ConfigBranch extends ConfigEntry<Map<String, ConfigEntry<?>>> {
}
@Override
public ConfigEntry<Map<String, ConfigEntry<?>>> clone() {
public ConfigBranch clone() {
ConfigBranch branch = new ConfigBranch();
for (Map.Entry<String, ConfigEntry<?>> e : getValue().entrySet()) {
branch.add(e.getKey(), e.getValue().clone());

View File

@ -56,7 +56,7 @@ public class GuiFactory {
ConfigEntryBuilder entryBuilder = builder.entryBuilder();
PackReloadType.Aggregator agg = new PackReloadType.Aggregator();
builder.setSavingRunnable(() -> {
Respackopts.save();
MetaCache.save();
if (Respackopts.CONFIG.debugLogs) Respackopts.LOGGER.info("GuiFactory SavingRunnable " + agg.get());
if (agg.get() == PackReloadType.Resource) {
Respackopts.forcePackReload = true;

View File

@ -0,0 +1,139 @@
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.enums.ConfigSyncMode;
import io.gitlab.jfronny.respackopts.model.enums.PackCapability;
import io.gitlab.jfronny.respackopts.model.tree.ConfigBranch;
import net.minecraft.resource.ResourcePack;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
public class MetaCache {
//TODO use the resource pack path as a key here
private static final Map<String, ConfigBranch> CONFIG_BRANCH = new HashMap<>(); // pack id -> active config branch
private static final Map<String, PackMeta> PACK_METAS = new HashMap<>(); // pack id -> original pack metadata
private static final Map<String, Path> DATA_LOCATIONS = new HashMap<>(); // pack id -> pack.rpo (config save location)
private static final Map<String, String> DISPLAY_NAME_LOOKUP = new HashMap<>(); // display name -> pack id
private static final Map<String, String> PACK_NAME_LOOKUP = new HashMap<>(); // pack name -> pack id
public static final Map<String, Map<String, DirRpo>> DIR_RPOS = new HashMap<>(); // pack id -> .rpo path -> parsed .rpo
public static final Map<String, Map<String, FileRpo>> FILE_RPOS = new HashMap<>(); // pack id -> .rpo path -> parsed .rpo
public static void clear() {
CONFIG_BRANCH.clear();
PACK_METAS.clear();
DATA_LOCATIONS.clear();
DISPLAY_NAME_LOOKUP.clear();
PACK_NAME_LOOKUP.clear();
DIR_RPOS.clear();
FILE_RPOS.clear();
}
public static void addFromScan(String displayName, String packName, PackMeta meta, Path dataLocation) {
if (Respackopts.META_VERSION > meta.version) {
Respackopts.LOGGER.warn(displayName + " uses an outdated RPO format (" + meta.version + "). Although this is supported, using the latest version (" + Respackopts.META_VERSION + ") is recommended");
}
meta.conf.setVersion(meta.version);
if (meta.version < 5) meta.capabilities.add(PackCapability.DirFilter);
if (CONFIG_BRANCH.containsKey(meta.id))
CONFIG_BRANCH.get(meta.id).sync(meta.conf, ConfigSyncMode.RESPACK_LOAD);
else
CONFIG_BRANCH.put(meta.id, meta.conf.clone());
DISPLAY_NAME_LOOKUP.put(displayName, meta.id);
PACK_NAME_LOOKUP.put(packName, meta.id);
PACK_METAS.put(meta.id, meta);
DIR_RPOS.put(meta.id, new HashMap<>());
FILE_RPOS.put(meta.id, new HashMap<>());
DATA_LOCATIONS.put(meta.id, dataLocation);
if (!dataLocation.startsWith(Respackopts.FALLBACK_CONF_DIR)) {
Path legacyLocation = Respackopts.FALLBACK_CONF_DIR.resolve(meta.id + ".json");
if (Files.exists(legacyLocation) && !Files.exists(dataLocation)) {
try {
Files.move(legacyLocation, dataLocation);
} catch (IOException e) {
Respackopts.LOGGER.error("Could not move data to new location", e);
}
}
}
load(meta.id);
save(meta.id, meta.conf);
}
public static void save() {
if (Respackopts.CONFIG.debugLogs)
Respackopts.LOGGER.info("Saving configs");
for (Map.Entry<String, ConfigBranch> e : CONFIG_BRANCH.entrySet()) {
save(e.getKey(), e.getValue());
}
for (Runnable action : Respackopts.SAVE_ACTIONS) {
action.run();
}
}
private static void save(String id, ConfigBranch branch) {
try (Writer writer = Files.newBufferedWriter(getDataLocation(id))) {
Respackopts.GSON.toJson(branch, writer);
writer.flush();
} catch (IOException ex) {
Respackopts.LOGGER.error("Could not save config", ex);
}
}
public static void load(String id) {
Path q = getDataLocation(id);
if (Files.exists(q)) {
if (Respackopts.CONFIG.debugLogs)
Respackopts.LOGGER.info("Loading configs for: " + id);
try (Reader reader = Files.newBufferedReader(q)) {
ConfigBranch b = Respackopts.GSON.fromJson(reader, ConfigBranch.class);
if (CONFIG_BRANCH.containsKey(id))
CONFIG_BRANCH.get(id).sync(b, ConfigSyncMode.CONF_LOAD);
Respackopts.STAR_SCRIPT.set(Respackopts.sanitizeString(id), b::buildStarscript);
} catch (IOException e) {
Respackopts.LOGGER.error("Failed to load " + id, e);
}
}
}
public static String getId(ResourcePack pack) {
return PACK_NAME_LOOKUP.get(pack.getName());
}
public static PackMeta getMeta(String id) {
return PACK_METAS.get(id);
}
public static @Nullable String getPackIdByDisplayName(String displayName) {
if (!DISPLAY_NAME_LOOKUP.containsKey(displayName)) return null;
return DISPLAY_NAME_LOOKUP.get(displayName);
}
public static ConfigBranch getBranch(String id) {
return CONFIG_BRANCH.get(id);
}
public static Path getDataLocation(String id) {
return DATA_LOCATIONS.get(id);
}
public static boolean hasCapability(ResourcePack pack, PackCapability capability) {
String id = getId(pack);
if (id == null) return false;
return PACK_METAS.get(id).capabilities.contains(capability);
}
public static <TEx extends Exception> void forEach(ThrowingBiConsumer<String, ConfigBranch, TEx> idAndBranchConsumer) throws TEx {
for (Map.Entry<String, ConfigBranch> entry : CONFIG_BRANCH.entrySet()) {
idAndBranchConsumer.accept(entry.getKey(), entry.getValue());
}
}
}

View File

@ -1,7 +0,0 @@
package io.gitlab.jfronny.respackopts.util;
import net.minecraft.resource.ResourcePack;
import net.minecraft.resource.ResourceType;
public record MetadataLocateResult(ResourcePack pack, boolean hasMeta, ResourceType type) {
}

View File

@ -1,7 +1,6 @@
package io.gitlab.jfronny.respackopts.util;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.model.tree.ConfigBranch;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.text.Text;
@ -11,35 +10,30 @@ import java.io.BufferedWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import static net.fabricmc.fabric.api.client.command.v1.ClientCommandManager.*;
public class RpoCommand {
private static final ModContainer respackotps = FabricLoader.getInstance().getModContainer(Respackopts.ID).get();
private static final ModContainer respackopts = FabricLoader.getInstance().getModContainer(Respackopts.ID).get();
public static void register() {
DISPATCHER.register(literal("rpo").then(literal("dump").then(literal("frex").executes(ctx -> {
DISPATCHER.register(literal("rpo").then(literal("dump").then(literal("glsl").executes(ctx -> {
ctx.getSource().sendFeedback(dump(Respackopts.getShaderImportSource(), "frex.glsl"));
return 1;
}))));
DISPATCHER.register(literal("rpo").then(literal("dump").then(literal("config").executes(ctx -> {
for (Map.Entry<String, ConfigBranch> entry : Respackopts.CONFIG_BRANCH.entrySet()) {
ctx.getSource().sendFeedback(dump(entry.getValue().toString(), entry.getKey() + ".txt"));
}
MetaCache.forEach((id, branch) -> ctx.getSource().sendFeedback(dump(branch.toString(), id + ".txt")));
return 1;
}))));
DISPATCHER.register(literal("rpo").then(literal("dump").executes(ctx -> {
for (Map.Entry<String, ConfigBranch> entry : Respackopts.CONFIG_BRANCH.entrySet()) {
ctx.getSource().sendFeedback(dump(entry.getValue().toString(), entry.getKey() + ".txt"));
}
MetaCache.forEach((id, branch) -> ctx.getSource().sendFeedback(dump(branch.toString(), id + ".txt")));
return 1;
})));
DISPATCHER.register(literal("rpo").then(literal("version").executes(ctx -> {
ctx.getSource().sendFeedback(new TranslatableText("respackopts.versionText", respackotps.getMetadata().getVersion(), Respackopts.META_VERSION));
ctx.getSource().sendFeedback(new TranslatableText("respackopts.versionText", respackopts.getMetadata().getVersion(), Respackopts.META_VERSION));
return 1;
})));
DISPATCHER.register(literal("rpo").executes(ctx -> {
ctx.getSource().sendFeedback(new TranslatableText("respackopts.versionText", respackotps.getMetadata().getVersion(), Respackopts.META_VERSION));
ctx.getSource().sendFeedback(new TranslatableText("respackopts.versionText", respackopts.getMetadata().getVersion(), Respackopts.META_VERSION));
return 1;
}));
}

View File

@ -4,12 +4,13 @@
"package": "io.gitlab.jfronny.respackopts.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
"AbstractFileResourcePackAccessor"
],
"client": [
"GLImportProcessorMixin",
"OptionsScreenMixin",
"ResourcePackEntryMixin",
"ResourcePackManagerMixin",
"GLImportProcessorMixin"
"ResourcePackManagerMixin"
],
"injectors": {
"defaultRequire": 1

View File

@ -17,10 +17,10 @@ class ConditionSerializationTest {
@BeforeAll
static void initialize() {
Respackopts.LOGGER.info("Expected error end");
Respackopts.CONF_DIR = Paths.get("./test");
Respackopts.FALLBACK_CONF_DIR = Paths.get("./test");
Respackopts.CONFIG = new ConfigFile();
SAVE_ACTIONS.add(() -> Respackopts.LOGGER.info("Save"));
Respackopts.LOGGER.info(Respackopts.CONF_DIR);
Respackopts.LOGGER.info(Respackopts.FALLBACK_CONF_DIR);
}
@Test

View File

@ -1,40 +1,40 @@
package io.gitlab.jfronny.respackopts;
import io.gitlab.jfronny.respackopts.model.ConfigFile;
//import io.gitlab.jfronny.respackopts.model.ConfigFile;
import io.gitlab.jfronny.respackopts.model.tree.*;
import io.gitlab.jfronny.respackopts.model.enums.ConfigSyncMode;
import io.gitlab.jfronny.respackopts.model.enums.NumericEntryType;
import org.junit.jupiter.api.AfterEach;
//import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
//import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Paths;
//import java.nio.file.Files;
//import java.nio.file.Paths;
import static io.gitlab.jfronny.respackopts.Respackopts.*;
import static io.gitlab.jfronny.respackopts.Respackopts.CONFIG_BRANCH;
import static org.junit.jupiter.api.Assertions.*;
//import static io.gitlab.jfronny.respackopts.Respackopts.CONFIG_BRANCH;
//import static org.junit.jupiter.api.Assertions.*;
class ConfigTreeTest {
private static final String testEntryName = "test";
private static final String testEntry1Name = "test1";
//private static final String testEntry1Name = "test1";
@BeforeAll
static void initialize() {
LOGGER.info("Expected error end");
CONF_DIR = Paths.get("./test");
/*LOGGER.info("Expected error end");
FALLBACK_CONF_DIR = Paths.get("./test");
CONFIG = new ConfigFile();
assertDoesNotThrow(() -> Files.deleteIfExists(CONF_DIR.resolve(testEntry1Name + ".json")));
assertDoesNotThrow(() -> Files.createDirectories(CONF_DIR));
assertDoesNotThrow(() -> Files.deleteIfExists(FALLBACK_CONF_DIR.resolve(testEntry1Name + ".json")));
assertDoesNotThrow(() -> Files.createDirectories(FALLBACK_CONF_DIR));
SAVE_ACTIONS.add(() -> LOGGER.info("Save"));
LOGGER.info(CONF_DIR);
LOGGER.info(FALLBACK_CONF_DIR);*/
}
@AfterEach
/*@AfterEach
void reset() {
CONFIG_BRANCH.clear();
save();
MetaCache.clear();
MetaCache.save();
assertEquals(CONFIG_BRANCH.keySet().size(), 0);
}
@ -49,12 +49,12 @@ class ConfigTreeTest {
CONFIG_BRANCH.get(testEntryName).add(testEntry1Name, new ConfigBooleanEntry(false));
assertTrue(CONFIG_BRANCH.get(testEntryName).has(testEntry1Name));
assertFalse((Boolean) CONFIG_BRANCH.get(testEntryName).get(testEntry1Name).getValue());
save();
MetaCache.save();
CONFIG_BRANCH.remove(testEntryName);
assertEquals(CONFIG_BRANCH.keySet().size(), 0);
CONFIG_BRANCH.put(testEntryName, new ConfigBranch());
CONFIG_BRANCH.get(testEntryName).add(testEntry1Name, new ConfigBooleanEntry(false));
load(testEntryName);
MetaCache.load(testEntryName);
assertEquals(CONFIG_BRANCH.keySet().size(), 1);
assertTrue(CONFIG_BRANCH.get(testEntryName).has(testEntry1Name));
assertFalse((Boolean) CONFIG_BRANCH.get(testEntryName).get(testEntry1Name).getValue());
@ -65,8 +65,8 @@ class ConfigTreeTest {
ConfigBranch test = new ConfigBranch();
test.add(testEntry1Name, new ConfigBooleanEntry(false));
CONFIG_BRANCH.get(testEntryName).sync(test, ConfigSyncMode.RESPACK_LOAD);
save();
load(testEntryName);
MetaCache.save();
MetaCache.load(testEntryName);
assertFalse((Boolean) CONFIG_BRANCH.get(testEntryName).get(testEntry1Name).getValue());
assertEquals(CONFIG_BRANCH.keySet().size(), 1);
assertEquals(test.getValue().size(), 1);
@ -80,10 +80,10 @@ class ConfigTreeTest {
CONFIG_BRANCH.get(testEntryName).sync(test, ConfigSyncMode.RESPACK_LOAD);
assertFalse((Boolean) CONFIG_BRANCH.get(testEntryName).get(testEntry1Name).getValue());
CONFIG_BRANCH.get(testEntryName).sync(test, ConfigSyncMode.CONF_LOAD);
save();
load(testEntryName);
MetaCache.save();
MetaCache.load(testEntryName);
assertTrue((Boolean) CONFIG_BRANCH.get(testEntryName).get(testEntry1Name).getValue());
}
}*/
@Test
void syncWrongType() {