Skip to content

Commit

Permalink
Rework the resource pack scanner to produce compatibility reports
Browse files Browse the repository at this point in the history
  • Loading branch information
jellysquid3 committed Jan 22, 2024
1 parent cf27e14 commit 5c04268
Showing 1 changed file with 132 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,30 @@
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;

public class ResourcePackScanner {

private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-InGameChecks");
private static final List<String> VSH_FSH_BLACKLIST = Arrays.asList(
"rendertype_solid.vsh", "rendertype_solid.fsh",
"rendertype_cutout_mipped.vsh", "rendertype_cutout_mipped.fsh",
"rendertype_cutout.vsh", "rendertype_cutout.fsh",
"rendertype_translucent.vsh", "rendertype_translucent.fsh",
"rendertype_tripwire.vsh", "rendertype_tripwire.fsh"
private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-ResourcePackScanner");

private static final Set<String> SHADER_PROGRAM_BLACKLIST = Set.of(
"rendertype_solid.vsh",
"rendertype_solid.fsh",
"rendertype_cutout_mipped.vsh",
"rendertype_cutout_mipped.fsh",
"rendertype_cutout.vsh",
"rendertype_cutout.fsh",
"rendertype_translucent.vsh",
"rendertype_translucent.fsh",
"rendertype_tripwire.vsh",
"rendertype_tripwire.fsh"
);
private static final List<String> GLSL_BLACKLIST = Arrays.asList(

private static final Set<String> SHADER_INCLUDE_BLACKLIST = Set.of(
"light.glsl",
"fog.glsl"
);
Expand All @@ -40,85 +44,138 @@ public class ResourcePackScanner {
* Detailed information on shader files replaced by resource packs is printed in the client log.
*/
public static void checkIfCoreShaderLoaded(ResourceManager manager) {
HashMap<String, MessageLevel> detectedResourcePacks = new HashMap<>();
var customResourcePacks = manager.streamResourcePacks();

customResourcePacks.forEach(resourcePack -> {
// Omit 'vanilla' and 'fabric' resource packs
if (!resourcePack.getName().equals("vanilla") && !resourcePack.getName().equals("fabric")) {
var resourcePackName = resourcePack.getName();
var ignoredShaders = determineIgnoredShaders(resourcePack);
var outputs = manager.streamResourcePacks()
.filter(pack -> !isBuiltInResourcePack(pack))
.collect(Collectors.toMap(ResourcePack::getName, ResourcePackScanner::scanResources));

resourcePack.findResources(ResourceType.CLIENT_RESOURCES, Identifier.DEFAULT_NAMESPACE, "shaders", (path, ignored) -> {
// Trim full shader file path to only contain the filename
var shaderName = path.getPath().substring(path.getPath().lastIndexOf('/') + 1);
printToasts(outputs);
printCompatibilityReport(outputs);
}

// Check if the pack has already acknowledged the warnings in this file,
// in this case we report a different info log about the situation
if (ignoredShaders.contains(shaderName)) {
if (VSH_FSH_BLACKLIST.contains(shaderName)) {
LOGGER.info("Resource pack '{}' replaces core shader '{}' but indicates it can be ignored", resourcePackName, shaderName);
}
private static void printToasts(Map<String, ScanResults> scanResults) {
var incompatibleResourcePacks = new ArrayList<String>();
var likelyIncompatibleResourcePacks = new ArrayList<String>();

if (GLSL_BLACKLIST.contains(shaderName)) {
LOGGER.info("Resource pack '{}' replaces shader '{}' but indicates it can be ignored", resourcePackName, shaderName);
}
return;
}
for (var entry : scanResults.entrySet()) {
var path = entry.getKey();
var result = entry.getValue();

if (VSH_FSH_BLACKLIST.contains(shaderName)) {
if (!result.shaderPrograms.isEmpty()) {
incompatibleResourcePacks.add(path);
} else if (!result.shaderIncludes.isEmpty()) {
likelyIncompatibleResourcePacks.add(path);
}
}

if (!detectedResourcePacks.containsKey(resourcePackName)) {
detectedResourcePacks.put(resourcePackName, MessageLevel.SEVERE);
} else if (detectedResourcePacks.get(resourcePackName) == MessageLevel.WARN) {
detectedResourcePacks.replace(resourcePackName, MessageLevel.SEVERE);
}
boolean shown = false;

LOGGER.error("Resource pack '{}' replaces core shader '{}'", resourcePackName, shaderName);
}
if (!incompatibleResourcePacks.isEmpty()) {
showConsoleMessage(Text.translatable("sodium.console.core_shaders_error"), MessageLevel.SEVERE);

if (GLSL_BLACKLIST.contains(shaderName)) {
for (var pack : incompatibleResourcePacks) {
showConsoleMessage(Text.literal(getResourcePackName(pack)), MessageLevel.SEVERE);
}

if (!detectedResourcePacks.containsKey(resourcePackName)) {
detectedResourcePacks.put(resourcePackName, MessageLevel.WARN);
}
shown = true;
}

LOGGER.error("Resource pack '{}' replaces shader '{}'", resourcePackName, shaderName);
if (!likelyIncompatibleResourcePacks.isEmpty()) {
showConsoleMessage(Text.translatable("sodium.console.core_shaders_warn"), MessageLevel.WARN);

}
});
for (var pack : likelyIncompatibleResourcePacks) {
showConsoleMessage(Text.literal(getResourcePackName(pack)), MessageLevel.WARN);
}
});

if (detectedResourcePacks.containsValue(MessageLevel.SEVERE)) {
showConsoleMessage(Text.translatable("sodium.console.core_shaders_error"), MessageLevel.SEVERE);
shown = true;
}

if (shown) {
showConsoleMessage(Text.translatable("sodium.console.core_shaders_info"), MessageLevel.INFO);
}
}

private static void printCompatibilityReport(Map<String, ScanResults> scanResults) {
var builder = new StringBuilder();

for (var entry : scanResults.entrySet()) {
var path = entry.getKey();
var result = entry.getValue();

for (Map.Entry<String, MessageLevel> entry : detectedResourcePacks.entrySet()) {
builder.append("- Resource pack: ").append(getResourcePackName(path)).append("\n");

if (!result.shaderPrograms.isEmpty()) {
emitProblem(builder,
"The resource pack replaces terrain shaders, which are not supported",
"https://github.com/CaffeineMC/sodium-fabric/wiki/Resource-Packs",
result.shaderPrograms);
}

if (entry.getValue() == MessageLevel.SEVERE) {
// Omit 'file/' prefix for the in-game message
var message = entry.getKey().startsWith("file/") ? entry.getKey().substring(5) : entry.getKey();
showConsoleMessage(Text.literal(message), MessageLevel.SEVERE);
}
if (!result.shaderIncludes.isEmpty()) {
emitProblem(builder,
"The resource pack modifies shader include files, which are not fully supported",
"https://github.com/CaffeineMC/sodium-fabric/wiki/Resource-Packs",
result.shaderIncludes);
}
}

if (detectedResourcePacks.containsValue(MessageLevel.WARN)) {
showConsoleMessage(Text.translatable("sodium.console.core_shaders_warn"), MessageLevel.WARN);
if (!builder.isEmpty()) {
LOGGER.error("The following compatibility issues were found with installed resource packs:\n{}", builder);
}
}

for (Map.Entry<String, MessageLevel> entry : detectedResourcePacks.entrySet()) {
private static void emitProblem(StringBuilder builder, String description, String url, List<String> resources) {
builder.append("\t- Problem found: ").append("\n");
builder.append("\t\t- Description:\n\t\t\t").append(description).append("\n");
builder.append("\t\t- More information: ").append(url).append("\n");
builder.append("\t\t- Files: ").append("\n");

if (entry.getValue() == MessageLevel.WARN) {
// Omit 'file/' prefix for the in-game message
var message = entry.getKey().startsWith("file/") ? entry.getKey().substring(5) : entry.getKey();
showConsoleMessage(Text.literal(message), MessageLevel.WARN);
}
}
for (var resource : resources) {
builder.append("\t\t\t- ").append(resource).append("\n");
}
}

if (!detectedResourcePacks.isEmpty()) {
showConsoleMessage(Text.translatable("sodium.console.core_shaders_info"), MessageLevel.INFO);
@NotNull
private static ScanResults scanResources(ResourcePack pack) {
final var ignoredShaders = determineIgnoredShaders(pack);

if (!ignoredShaders.isEmpty()) {
LOGGER.warn("Resource pack '{}' indicates the following shaders should be ignored: {}",
getResourcePackName(pack.getName()), String.join(", ", ignoredShaders));
}

final var unsupportedShaderPrograms = new ArrayList<String>();
final var unsupportedShaderIncludes = new ArrayList<String>();

pack.findResources(ResourceType.CLIENT_RESOURCES, Identifier.DEFAULT_NAMESPACE, "shaders", (identifier, supplier) -> {
// Trim full shader file path to only contain the filename
final var path = identifier.getPath();
final var name = path.substring(path.lastIndexOf('/') + 1);

// Check if the pack has already acknowledged the warnings in this file,
// in this case we report a different info log about the situation
if (ignoredShaders.contains(name)) {
return;
}

// Check the path against known problem files
if (SHADER_PROGRAM_BLACKLIST.contains(name)) {
unsupportedShaderPrograms.add(path);
} else if (SHADER_INCLUDE_BLACKLIST.contains(name)) {
unsupportedShaderIncludes.add(path);
}
});

return new ScanResults(unsupportedShaderPrograms, unsupportedShaderIncludes);
}

private static boolean isBuiltInResourcePack(ResourcePack pack) {
var name = pack.getName();
return name.equals("vanilla") || name.equals("fabric");
}

private static String getResourcePackName(String path) {
// Omit 'file/' prefix for the in-game message
return path.startsWith("file/") ? path.substring(5) : path;
}

/**
Expand All @@ -143,7 +200,10 @@ private static List<String> determineIgnoredShaders(ResourcePack resourcePack) {
}

private static void showConsoleMessage(MutableText message, MessageLevel messageLevel) {
Console.instance().logMessage(messageLevel, message, 20.0);
Console.instance().logMessage(messageLevel, message, 12.5);
}

private record ScanResults(ArrayList<String> shaderPrograms, ArrayList<String> shaderIncludes) {

}
}

0 comments on commit 5c04268

Please sign in to comment.