Add .fbt fallback textures

This commit is contained in:
JFronny 2021-02-15 11:44:59 +01:00
parent 0bf67f0dd2
commit 685fe0900e
24 changed files with 206 additions and 71 deletions

View File

@ -46,8 +46,14 @@ There have been issues with LibCD in development, so it might be smart not to us
Respackopts allows creating conditional resources by creating a file named `{targetFile}.rpo`.\
This file is a json file that contains an array named "conditions". The resource will be ignored if any of the conditions are not met.\
This allows (for example) overrides for textures to only be loaded if the user enables them through the config.\
It also allows the following operations: `and`, `equal`, `nor`/`not`, `or`, `xor`
As an example can be seen [here](https://gitlab.com/JFronny/respackopts/-/tree/master/run/resourcepacks/lumi/assets/minecraft/lang)
It also allows the following operations: `and`, `equal`, `nor`/`not`, `or`, `xor`.\
An example can be seen [here](https://gitlab.com/JFronny/respackopts/-/tree/master/run/resourcepacks/lumi/assets/minecraft/lang)
### Fallback resources
Respackopts allows creating fallbacks for resources if they are unavailable/disabled.\
To do this, create a file named `{targetFile}.fbt`.\
This file should contain a simple json array where each element is a reference to a possible fallback file.\
Use this in conjunction with the conditional resources feature in order to create multiple options for a single texture.\
An example can be seen [here](https://gitlab.com/JFronny/respackopts/-/tree/master/run/resourcepacks/lumi/assets/minecraft/lang)
## Mod developers
All data is available in static HashMaps in `io.gitlab.jfronny.respackopts.Respackopts`.\
To save information, call `Respackopts.save()`, `Respackopts.load()` to load.

View File

@ -26,19 +26,22 @@ dependencies {
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:0.30.3+1.16"
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
modCompile("com.terraformersmc:modmenu:1.14.15")
modCompile("com.terraformersmc:modmenu:1.16.7")
include modApi("me.shedaniel.cloth:config-2:4.8.3") {
exclude(group: "net.fabricmc.fabric-api")
exclude(group: "io.github.prospector")
}
modApi "io.github.cottonmc:LibCD:3.0.3+1.16.3"
modCompile "grondag:frex-mc116:4.0+"
modCompile "grondag:canvas-mc116:1.0.+"
modCompile("grondag:canvas-mc116:1.0.+") {
exclude(group: "io.github.prospector")
}
}
processResources {

View File

@ -3,12 +3,9 @@ org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.16.5
yarn_mappings=1.16.5+build.3
yarn_mappings=1.16.5+build.4
loader_version=0.11.1
# Mod Properties
mod_version=1.3.1
mod_version=1.4.0
maven_group=io.gitlab.jfronny
archives_base_name=respackopts
# Dependencies
# check this on https://modmuss50.me/fabric.html
fabric_version=0.29.4+1.16
archives_base_name=respackopts

View File

@ -0,0 +1,3 @@
[
"assets/minecraft/lang/en_us_joke.json"
]

View File

@ -0,0 +1,9 @@
{
"respackopts.title.lumi": "Not Lumi Lights",
"respackopts.field.lumi.tonemap": "Not Tonemap mode",
"respackopts.tooltip.lumi.tonemap": "Not Tooltip test",
"respackopts.field.lumi.pbr": "Not Enable PBR",
"respackopts.field.lumi.debugMode": "Not Debug Mode",
"respackopts.field.lumi.waterVertexWavy": "Not Wavy water model",
"respackopts.tooltip.lumi.subcategoryTest.sliderTest": "Not Yayyy"
}

View File

@ -0,0 +1,5 @@
{
"conditions": [
"lumi:subcategoryTest.enableLangJokeFallback"
]
}

View File

@ -32,7 +32,8 @@
"max": 20
},
"enableLang": true,
"enableLangForceDisable": false
"enableLangForceDisable": false,
"enableLangJokeFallback": true
}
}
}

View File

@ -2,7 +2,6 @@ package io.gitlab.jfronny.respackopts;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.gitlab.jfronny.respackopts.conditions.SyntaxError;
import io.gitlab.jfronny.respackopts.data.Config;
import io.gitlab.jfronny.respackopts.data.Respackmeta;
import net.fabricmc.api.ClientModInitializer;
@ -16,7 +15,6 @@ import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@Environment(EnvType.CLIENT)
public class Respackopts implements ClientModInitializer {
@ -32,6 +30,7 @@ public class Respackopts implements ClientModInitializer {
static final Path p = FabricLoader.getInstance().getConfigDir().resolve("respackopts");
public static final Set<Runnable> saveActions = new HashSet<>();
public static final String fileExtension = ".rpo";
public static final String fallbackTextureExtension = ".fbt";
public static boolean forceRespackReload = false;
public static final Logger logger = LogManager.getFormatterLogger(ID);
@Override

View File

@ -0,0 +1,8 @@
package io.gitlab.jfronny.respackopts.abstractions;
import java.io.IOException;
import java.io.InputStream;
public interface FileOpenProvider {
InputStream open(String file) throws IOException;
}

View File

@ -0,0 +1,59 @@
package io.gitlab.jfronny.respackopts.filters;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.abstractions.FileOpenProvider;
import io.gitlab.jfronny.respackopts.filters.conditions.ResourcePackFilter;
import io.gitlab.jfronny.respackopts.filters.fallback.FallbackFilter;
import net.minecraft.resource.ResourceNotFoundException;
import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.function.Predicate;
public class FilterProvider {
ResourcePackFilter rpo;
FallbackFilter fbt;
Predicate<String> containsFileBase;
boolean containsFileWasFallback = false;
public FilterProvider(Predicate<String> containsFileBase, FileOpenProvider openFileBase) {
this.containsFileBase = containsFileBase;
rpo = new ResourcePackFilter(containsFileBase, openFileBase);
fbt = new FallbackFilter(containsFileBase, openFileBase);
}
public void openFile(String name, File base, CallbackInfoReturnable<InputStream> info) throws IOException {
if (containsFileBase.test(name) && containsFileWasFallback) {
info.setReturnValue(fbt.getReplacement(name, new ResourceNotFoundException(base, name)));
}
}
public void containsFile(String name, CallbackInfoReturnable<Boolean> info) {
containsFileWasFallback = false;
if (info.getReturnValueZ()) {
if (!rpo.fileVisible(name)) {
if (fbt.fileVisible(name)) {
containsFileWasFallback = true;
} else {
info.setReturnValue(false);
}
}
}
else {
if (!name.endsWith(Respackopts.fallbackTextureExtension) && fbt.fileVisible(name)) {
containsFileWasFallback = true;
info.setReturnValue(true);
}
}
}
public void findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate<String> pathFilter, CallbackInfoReturnable<Collection<Identifier>> info) {
Collection<Identifier> ret = info.getReturnValue();
ret.removeIf(s -> !rpo.fileVisible(s.getPath()) && !fbt.fileVisible(namespace));
fbt.addFallbackResources(ret, namespace);
}
}

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.respackopts.conditions;
package io.gitlab.jfronny.respackopts.filters.conditions;
import com.google.gson.JsonElement;

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.respackopts.conditions;
package io.gitlab.jfronny.respackopts.filters.conditions;
import com.google.gson.JsonElement;

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.respackopts.conditions;
package io.gitlab.jfronny.respackopts.filters.conditions;
import com.google.gson.JsonElement;
import io.gitlab.jfronny.respackopts.Respackopts;

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.respackopts.conditions;
package io.gitlab.jfronny.respackopts.filters.conditions;
import com.google.gson.JsonElement;

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.respackopts.conditions;
package io.gitlab.jfronny.respackopts.filters.conditions;
import com.google.gson.JsonElement;

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.respackopts.conditions;
package io.gitlab.jfronny.respackopts.filters.conditions;
import com.google.gson.JsonElement;

View File

@ -1,18 +1,18 @@
package io.gitlab.jfronny.respackopts.conditions;
package io.gitlab.jfronny.respackopts.filters.conditions;
import com.google.gson.JsonElement;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.abstractions.FileOpenProvider;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.function.Function;
import java.util.function.Predicate;
public class ResourcePackFilter {
Predicate<String> containsFileBase;
Function<String, InputStream> openFileBase;
public ResourcePackFilter(Predicate<String> containsFileBase, Function<String, InputStream> openFileBase) {
FileOpenProvider openFileBase;
public ResourcePackFilter(Predicate<String> containsFileBase, FileOpenProvider openFileBase) {
this.containsFileBase = containsFileBase;
this.openFileBase = openFileBase;
}
@ -22,7 +22,7 @@ public class ResourcePackFilter {
return true;
if (!containsFileBase.test(name + Respackopts.fileExtension))
return true;
try (InputStream stream = openFileBase.apply(name + Respackopts.fileExtension); Reader w = new InputStreamReader(stream)) {
try (InputStream stream = openFileBase.open(name + Respackopts.fileExtension); Reader w = new InputStreamReader(stream)) {
return ConditionEvaluator.evaluate(Respackopts.g.fromJson(w, JsonElement.class));
}
catch (Throwable e) {

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.respackopts.conditions;
package io.gitlab.jfronny.respackopts.filters.conditions;
public class SyntaxError extends Exception {
public SyntaxError(String message) {

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.respackopts.conditions;
package io.gitlab.jfronny.respackopts.filters.conditions;
import com.google.gson.JsonElement;

View File

@ -0,0 +1,71 @@
package io.gitlab.jfronny.respackopts.filters.fallback;
import com.google.gson.reflect.TypeToken;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.abstractions.FileOpenProvider;
import net.minecraft.resource.ResourceNotFoundException;
import net.minecraft.util.Identifier;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collection;
import java.util.Set;
import java.util.function.Predicate;
public class FallbackFilter {
Predicate<String> containsFileBase;
FileOpenProvider openFileBase;
public FallbackFilter(Predicate<String> containsFileBase, FileOpenProvider openFileBase) {
this.containsFileBase = containsFileBase;
this.openFileBase = openFileBase;
}
public boolean fileVisible(String name) {
String fbt = name + Respackopts.fallbackTextureExtension;
if (containsFileBase.test(fbt)) {
System.out.println(fbt);
try (InputStream stream = openFileBase.open(fbt); Reader w = new InputStreamReader(stream)) {
Set<String> arr = Respackopts.g.fromJson(w, new TypeToken<Set<String>>(){}.getType());
for (String s : arr) {
if (containsFileBase.test(s))
return true;
}
}
catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
public InputStream getReplacement(String name, ResourceNotFoundException ex) throws ResourceNotFoundException {
String fbt = name + Respackopts.fallbackTextureExtension;
try (InputStream stream = openFileBase.open(fbt); Reader w = new InputStreamReader(stream)) {
Set<String> arr = Respackopts.g.fromJson(w, new TypeToken<Set<String>>(){}.getType());
for (String s : arr) {
if (containsFileBase.test(s))
return openFileBase.open(s);
}
}
catch (IOException e) {
e.printStackTrace();
}
throw ex;
}
public void addFallbackResources(Collection<Identifier> ret, String namespace) {
for (Identifier identifier : ret) {
String path = identifier.getPath();
if (path.endsWith(Respackopts.fallbackTextureExtension)) {
String expectedTarget = path.substring(0, path.length() - Respackopts.fallbackTextureExtension.length());
System.out.println(expectedTarget);
if (ret.stream().noneMatch(s -> s.getPath().equals(expectedTarget)) && fileVisible(expectedTarget)) {
ret.add(new Identifier(namespace, expectedTarget));
System.out.println("Added fallback");
}
}
}
}
}

View File

@ -5,8 +5,7 @@ import io.github.cottonmc.libcd.api.CDSyntaxError;
import io.github.cottonmc.libcd.api.condition.ConditionManager;
import io.github.cottonmc.libcd.api.init.ConditionInitializer;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.conditions.ConditionEvaluator;
import io.gitlab.jfronny.respackopts.conditions.SyntaxError;
import io.gitlab.jfronny.respackopts.filters.conditions.ConditionEvaluator;
import net.minecraft.util.Identifier;
public class LibCDCompat implements ConditionInitializer {

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.respackopts.integration;
import io.github.prospector.modmenu.api.ConfigScreenFactory;
import io.github.prospector.modmenu.api.ModMenuApi;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import io.gitlab.jfronny.respackopts.Respackopts;
import io.gitlab.jfronny.respackopts.abstractions.JfConfigCategoryPrimary;
import me.shedaniel.clothconfig2.api.ConfigBuilder;

View File

@ -1,21 +1,17 @@
package io.gitlab.jfronny.respackopts.mixin.conditions;
import io.gitlab.jfronny.respackopts.conditions.ResourcePackFilter;
import io.gitlab.jfronny.respackopts.filters.FilterProvider;
import net.minecraft.resource.AbstractFileResourcePack;
import net.minecraft.resource.DirectoryResourcePack;
import net.minecraft.resource.ResourceNotFoundException;
import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier;
import org.apache.commons.io.input.NullInputStream;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.util.Collection;
import java.util.Set;
import java.util.function.Predicate;
@ -32,28 +28,20 @@ public abstract class DirectoryResourcePackMixin extends AbstractFileResourcePac
@Shadow protected abstract InputStream openFile(String name) throws IOException;
ResourcePackFilter filter = new ResourcePackFilter(this::containsFile, (s) -> {
try {
return openFile(s);
} catch (IOException e) {
e.printStackTrace();
return new NullInputStream(0);
}
});
FilterProvider filter = new FilterProvider(this::containsFile, this::openFile);
@Inject(at = @At("HEAD"), method = "openFile(Ljava/lang/String;)Ljava/io/InputStream;", cancellable = true)
protected void openFile(String name, CallbackInfoReturnable<InputStream> info) throws IOException {
if (!filter.fileVisible(name)) {
throw new ResourceNotFoundException(this.base, name);
}
filter.openFile(name, base, info);
}
@Inject(at = @At("TAIL"), method = "containsFile(Ljava/lang/String;)Z", cancellable = true)
protected void containsFile(String name, CallbackInfoReturnable<Boolean> info) {
info.setReturnValue(info.getReturnValueZ() && filter.fileVisible(name));
filter.containsFile(name, info);
}
@Inject(at = @At("TAIL"), method = "findResources(Lnet/minecraft/resource/ResourceType;Ljava/lang/String;Ljava/lang/String;ILjava/util/function/Predicate;)Ljava/util/Collection;")
private void findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate<String> pathFilter, CallbackInfoReturnable<Collection<Identifier>> info) {
info.getReturnValue().removeIf(s -> !filter.fileVisible(s.getPath()));
filter.findResources(type, namespace, prefix, maxDepth, pathFilter, info);
}
}

View File

@ -1,21 +1,17 @@
package io.gitlab.jfronny.respackopts.mixin.conditions;
import io.gitlab.jfronny.respackopts.conditions.ResourcePackFilter;
import io.gitlab.jfronny.respackopts.filters.FilterProvider;
import net.minecraft.resource.AbstractFileResourcePack;
import net.minecraft.resource.ResourceNotFoundException;
import net.minecraft.resource.ResourceType;
import net.minecraft.resource.ZipResourcePack;
import net.minecraft.util.Identifier;
import org.apache.commons.io.input.NullInputStream;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.util.Collection;
import java.util.Set;
import java.util.function.Predicate;
@ -26,35 +22,26 @@ public abstract class ZipResourcePackMixin extends AbstractFileResourcePack {
super(base);
}
@Shadow
protected abstract boolean containsFile(String name);
@Shadow public abstract boolean containsFile(String name);
@Shadow public abstract Set<String> getNamespaces(ResourceType type);
@Shadow protected abstract InputStream openFile(String name) throws IOException;
ResourcePackFilter filter = new ResourcePackFilter(this::containsFile, (s) -> {
try {
return openFile(s);
} catch (IOException e) {
e.printStackTrace();
return new NullInputStream(0);
}
});
FilterProvider filter = new FilterProvider(this::containsFile, this::openFile);
@Inject(at = @At("HEAD"), method = "openFile(Ljava/lang/String;)Ljava/io/InputStream;", cancellable = true)
protected void openFile(String name, CallbackInfoReturnable<InputStream> info) throws IOException {
if (!filter.fileVisible(name)) {
throw new ResourceNotFoundException(this.base, name);
}
filter.openFile(name, base, info);
}
@Inject(at = @At("TAIL"), method = "containsFile(Ljava/lang/String;)Z", cancellable = true)
protected void containsFile(String name, CallbackInfoReturnable<Boolean> info) {
info.setReturnValue(info.getReturnValueZ() && filter.fileVisible(name));
filter.containsFile(name, info);
}
@Inject(at = @At("TAIL"), method = "findResources(Lnet/minecraft/resource/ResourceType;Ljava/lang/String;Ljava/lang/String;ILjava/util/function/Predicate;)Ljava/util/Collection;")
private void findResources(ResourceType type, String namespace, String prefix, int maxDepth, Predicate<String> pathFilter, CallbackInfoReturnable<Collection<Identifier>> info) {
info.getReturnValue().removeIf(s -> !filter.fileVisible(s.getPath()));
filter.findResources(type, namespace, prefix, maxDepth, pathFilter, info);
}
}