Skip to content

Commit e654793

Browse files
authored
Merge pull request #4 from MrKinau/fix/model-texture-references
Fix/model texture references
2 parents 6a57068 + ee50967 commit e654793

File tree

14 files changed

+389
-43
lines changed

14 files changed

+389
-43
lines changed

.github/workflows/build-publish-docker-image.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
name: Create and publish a Docker image
22

3-
on:
4-
push:
5-
branches: ['master']
3+
on: [push]
64

75
env:
86
REGISTRY: ghcr.io

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![License](https://img.shields.io/github/license/MrKinau/ResourcePackValidator)](https://github.com/MrKinau/ResourcePackValidator/blob/master/LICENSE)
44
[![Discord](https://img.shields.io/discord/550764567282712583?logo=discord)](https://discord.gg/xHpCDYf)
55

6-
A commandline tool to validate a Minecraft Java Edition resource pack. It runs several validations, that normally run while loading the resource pack in the vanilla client as well as some extra validations to identify issues.
6+
A commandline tool to validate a Minecraft Java Edition resource pack. It runs several validations, that normally run when loading the resource pack in the vanilla client as well as some extra validations to identify issues.
77

88
## Commandline Arguments
99
- `-help` Show all available commandline arguments
@@ -90,6 +90,9 @@ Also check all textures if they are used by a model which already is unused?
9090
### Animated texture frames missing / too many sprite frames
9191
Checks if an animated texture has too many / too few frames.
9292

93+
### Version support
94+
There is no way to enforce a specific minecraft version, vanilla resources from different 1.20 and 1.21 versions are merged to validate against.
95+
9396
## Discord
9497
To follow the project, get support or request features or bugs you can join my Discord: https://discord.gg/xHpCDYf
9598

doc/CONFIG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ This is the default config:
2828
"logLevel": "ERROR",
2929
"ignore": []
3030
},
31+
"ModelTextureReferencesResolvableValidator": {
32+
"enabled": true,
33+
"logLevel": "ERROR",
34+
"ignore": []
35+
},
3136
"ModelOverridesExistsValidator": {
3237
"enabled": true,
3338
"logLevel": "ERROR",

doc/VALIDATORS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ Checks if a model has at least one texture assigned to it. This check does not f
1414
## ModelTexturesExistsValidator
1515
Checks if the texture files referenced in the model exists.
1616

17+
## ModelTextureReferencesResolvableValidator
18+
Checks if all hashprefixed referenced textures (e.g. #side) are bound to a texture. This validation is skipped if the file is used as a parent in any other model to allow template models.
19+
1720
## ModelOverridesExistsValidator
1821
Checks if item overrides are correct and the referenced model exists.
1922

src/main/java/dev/kinau/resourcepackvalidator/ResourcePackValidator.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package dev.kinau.resourcepackvalidator;
22

3-
import com.google.gson.Gson;
4-
import com.google.gson.JsonObject;
3+
import com.google.gson.*;
54
import dev.kinau.resourcepackvalidator.cache.AssetDictionary;
65
import dev.kinau.resourcepackvalidator.config.Config;
76
import dev.kinau.resourcepackvalidator.report.ReportGenerator;
@@ -13,9 +12,7 @@
1312
import lombok.extern.slf4j.Slf4j;
1413
import org.apache.commons.cli.*;
1514

16-
import java.io.File;
17-
import java.io.FileReader;
18-
import java.io.IOException;
15+
import java.io.*;
1916
import java.nio.file.Files;
2017
import java.util.logging.LogManager;
2118

@@ -90,12 +87,45 @@ private void adjustLogLevel() {
9087

9188
private void shouldCreateAssetCache() {
9289
if (commandLine.hasOption("createAssetCache")) {
90+
// create new assets
9391
JsonObject assets = new AssetDictionary().createAssets(new File(commandLine.getOptionValue("createAssetCache")));
9492
File assetsFile = new File("vanillaassets.json");
9593
try {
9694
Files.writeString(assetsFile.toPath(), assets.toString());
95+
log.info("Created assets file: {}", assetsFile.getPath());
9796
} catch (IOException ex) {
98-
log.error("Could not saved " + assetsFile.getPath(), ex);
97+
log.error("Could not save {}", assetsFile.getPath(), ex);
98+
}
99+
100+
// merge with old assets
101+
JsonObject oldAssets = null;
102+
try (InputStream stream = AssetDictionary.class.getClassLoader().getResourceAsStream("vanillaassets.json")) {
103+
if (stream == null) throw new IllegalArgumentException("vanillaassets.json is null");
104+
JsonElement root = JsonParser.parseReader(new InputStreamReader(stream));
105+
if (!root.isJsonObject()) throw new IllegalArgumentException("root is not JsonObject");
106+
oldAssets = root.getAsJsonObject();
107+
} catch (IOException | IllegalArgumentException ex) {
108+
log.error("Could not read vanilla assets", ex);
109+
}
110+
if (oldAssets == null) {
111+
System.exit(0);
112+
return;
113+
}
114+
JsonArray newFiles = assets.getAsJsonArray("files");
115+
JsonArray mergedFiles = oldAssets.getAsJsonArray("files");
116+
for (JsonElement newFile : newFiles) {
117+
if (!mergedFiles.contains(newFile)) {
118+
mergedFiles.add(newFile);
119+
}
120+
}
121+
JsonObject merged = new JsonObject();
122+
merged.add("files", mergedFiles);
123+
File mergedAssetsFile = new File("vanillaassets_merged.json");
124+
try {
125+
Files.writeString(mergedAssetsFile.toPath(), merged.toString());
126+
log.info("Created merged assets file: {}", mergedAssetsFile.getPath());
127+
} catch (IOException ex) {
128+
log.error("Could not save {}", mergedAssetsFile.getPath(), ex);
99129
}
100130
System.exit(0);
101131
}

src/main/java/dev/kinau/resourcepackvalidator/ValidationJob.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package dev.kinau.resourcepackvalidator;
22

3-
import com.google.gson.JsonArray;
4-
import com.google.gson.JsonElement;
5-
import com.google.gson.JsonObject;
6-
import com.google.gson.JsonParser;
3+
import com.google.gson.*;
74
import dev.kinau.resourcepackvalidator.atlas.TextureAtlas;
85
import dev.kinau.resourcepackvalidator.cache.AssetDictionary;
96
import dev.kinau.resourcepackvalidator.cache.NamespaceJsonCache;
@@ -28,6 +25,8 @@
2825
@Getter
2926
public class ValidationJob {
3027

28+
@Getter
29+
private final Gson gson = new Gson();
3130
private final File rootDir;
3231
private final ValidatorRegistry registry;
3332
private final McMetaFile mcMetaFile;
@@ -48,9 +47,9 @@ public ValidationJob(File rootDir, ValidatorRegistry registry) {
4847
namespaces.forEach(namespace -> {
4948
jsonCache.put(namespace, new NamespaceJsonCache(namespace));
5049
textureCache.put(namespace, new NamespaceTextureCache(namespace));
51-
textureAtlas.put(namespace, new TextureAtlas(namespace));
50+
textureAtlas.put(namespace, new TextureAtlas(namespace, gson));
5251
});
53-
this.assetDictionary = new AssetDictionary().load();
52+
this.assetDictionary = new AssetDictionary().load(gson);
5453
this.fontProviderFactory = new FontProviderFactory();
5554
}
5655

src/main/java/dev/kinau/resourcepackvalidator/atlas/TextureAtlas.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ public class TextureAtlas {
2222
private final OverlayNamespace namespace;
2323
private AtlasData data;
2424

25-
public TextureAtlas(OverlayNamespace namespace) {
25+
public TextureAtlas(OverlayNamespace namespace, Gson gson) {
2626
this.namespace = namespace;
2727
try {
2828
File atlasDir = FileUtils.Directory.ATLASES.getFile(namespace);
2929
File blocksAtlas = new File(atlasDir, "blocks.json");
3030
if (!blocksAtlas.exists()) return;
31-
this.data = new Gson().fromJson(new FileReader(blocksAtlas), AtlasData.class);
31+
this.data = gson.fromJson(new FileReader(blocksAtlas), AtlasData.class);
3232
data.sources().add(new AtlasSource("directory", "item", "", null, null));
3333
data.sources().add(new AtlasSource("directory", "block", "", null, null));
3434
} catch (Exception ex) {

src/main/java/dev/kinau/resourcepackvalidator/cache/AssetDictionary.java

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
package dev.kinau.resourcepackvalidator.cache;
22

3-
import com.google.gson.JsonArray;
4-
import com.google.gson.JsonElement;
5-
import com.google.gson.JsonObject;
6-
import com.google.gson.JsonParser;
3+
import com.google.gson.*;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Getter;
6+
import lombok.ToString;
7+
import lombok.experimental.Accessors;
78
import lombok.extern.slf4j.Slf4j;
89

9-
import java.io.File;
10-
import java.io.IOException;
11-
import java.io.InputStream;
12-
import java.io.InputStreamReader;
10+
import javax.annotation.Nullable;
11+
import java.io.*;
1312
import java.nio.file.Files;
1413
import java.nio.file.Path;
15-
import java.util.HashSet;
14+
import java.util.Collections;
15+
import java.util.HashMap;
16+
import java.util.Map;
1617
import java.util.Set;
18+
import java.util.stream.Collectors;
1719
import java.util.stream.Stream;
1820

1921
// On Update:
@@ -31,10 +33,12 @@
3133
@Slf4j
3234
public class AssetDictionary {
3335

34-
private final Set<String> assets = new HashSet<>();
36+
private static final Asset EMPTY_ASSET = new Asset(null, null);
37+
38+
private final Map<String, Asset> assets = new HashMap<>();
3539

3640
// TODO: Distinguish resource pack versions?
37-
public AssetDictionary load() {
41+
public AssetDictionary load(Gson gson) {
3842
log.debug("Loading vanilla assets…");
3943
assets.clear();
4044
try (InputStream stream = AssetDictionary.class.getClassLoader().getResourceAsStream("vanillaassets.json")) {
@@ -45,7 +49,23 @@ public AssetDictionary load() {
4549
if (!rootObject.has("files") || !rootObject.get("files").isJsonArray())
4650
throw new IllegalArgumentException("root has no files array");
4751
JsonArray files = rootObject.getAsJsonArray("files");
48-
assets.addAll(files.asList().stream().map(JsonElement::getAsString).toList());
52+
for (JsonElement assetElement : files) {
53+
if (assetElement.isJsonPrimitive()) {
54+
assets.put(assetElement.getAsString(), EMPTY_ASSET);
55+
continue;
56+
}
57+
if (assetElement.isJsonObject()) {
58+
JsonObject assetObject = assetElement.getAsJsonObject();
59+
if (assetObject.isEmpty()) continue;
60+
String key = assetObject.keySet().iterator().next();
61+
try {
62+
Asset asset = gson.fromJson(assetObject.getAsJsonObject(key), Asset.class);
63+
assets.put(key, asset);
64+
} catch (JsonSyntaxException e) {
65+
assets.put(key, EMPTY_ASSET);
66+
}
67+
}
68+
}
4969
log.debug("Loaded {} vanilla assets", assets.size());
5070
} catch (IOException | IllegalArgumentException ex) {
5171
log.error("Could not read vanilla assets", ex);
@@ -62,7 +82,32 @@ public JsonObject createAssets(File file) {
6282
fileTree.filter(path -> !path.toFile().isDirectory())
6383
.filter(path -> !path.toFile().getName().equals(".mcassetsroot"))
6484
.forEach(path -> {
65-
files.add(path.toString().substring(rootPath.toString().length() + 1));
85+
JsonObject assetObject = null;
86+
if (path.toFile().getName().endsWith(".json")) {
87+
try {
88+
JsonElement jsonElement = JsonParser.parseReader(new FileReader(path.toFile()));
89+
if (jsonElement != null && jsonElement.isJsonObject()) {
90+
JsonObject fullObject = jsonElement.getAsJsonObject();
91+
assetObject = new JsonObject();
92+
if (fullObject.has("parent") && fullObject.get("parent").isJsonPrimitive()) {
93+
assetObject.add("parent", fullObject.get("parent"));
94+
}
95+
if (fullObject.has("textures") && fullObject.get("textures").isJsonObject()) {
96+
assetObject.add("textures", fullObject.get("textures"));
97+
}
98+
}
99+
} catch (IOException e) {
100+
throw new RuntimeException(e);
101+
}
102+
}
103+
String relPath = path.toString().substring(rootPath.toString().length() + 1);
104+
if (assetObject == null || assetObject.isEmpty()) {
105+
files.add(relPath);
106+
} else {
107+
JsonObject dataObject = new JsonObject();
108+
dataObject.add(relPath, assetObject);
109+
files.add(dataObject);
110+
}
66111
});
67112
} catch (IOException ex) {
68113
log.error("Could not create assets", ex);
@@ -72,6 +117,34 @@ public JsonObject createAssets(File file) {
72117
}
73118

74119
public boolean contains(String asset) {
75-
return assets.contains(asset);
120+
return assets.containsKey(asset);
121+
}
122+
123+
public Asset getAsset(String asset) {
124+
return assets.get(asset);
125+
}
126+
127+
public Set<String> getChildren(String... asset) {
128+
if (asset == null || asset.length == 0)
129+
return Collections.emptySet();
130+
return assets.entrySet().stream()
131+
.filter(entry -> {
132+
for (String s : asset) {
133+
if (s.equals(entry.getValue().parent()))
134+
return true;
135+
}
136+
return false;
137+
})
138+
.map(Map.Entry::getKey)
139+
.collect(Collectors.toSet());
140+
}
141+
142+
@AllArgsConstructor
143+
@Getter
144+
@ToString
145+
@Accessors(fluent = true)
146+
public static class Asset {
147+
@Nullable private String parent;
148+
@Nullable private Map<String, String> textures;
76149
}
77150
}

src/main/java/dev/kinau/resourcepackvalidator/utils/FileUtils.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.kinau.resourcepackvalidator.utils;
22

33
import dev.kinau.resourcepackvalidator.cache.AssetDictionary;
4+
import dev.kinau.resourcepackvalidator.validator.context.FileContext;
45
import lombok.Getter;
56
import lombok.RequiredArgsConstructor;
67
import lombok.experimental.Accessors;
@@ -167,4 +168,25 @@ public static boolean isArmorModel(File file, OverlayNamespace namespace) {
167168

168169
return file.toPath().startsWith(equipmentModels.toPath());
169170
}
171+
172+
public static String stripNamespace(String path) {
173+
if (path.contains(":"))
174+
path = path.substring(path.indexOf(":") + 1);
175+
return path;
176+
}
177+
178+
public static String getRelPath(FileContext context) {
179+
return getRelPath(context.value(), context.namespace().getNamespaceName());
180+
}
181+
182+
public static String getRelPath(File file, String namespaceName) {
183+
String relPath = "";
184+
String[] parts = file.getPath().split("assets" + File.separator + namespaceName + File.separator + FileUtils.Directory.MODELS.getPath() + File.separator);
185+
if (parts.length > 1)
186+
relPath = parts[1];
187+
if (relPath.endsWith(".json"))
188+
relPath = relPath.substring(0, relPath.length() - 5);
189+
relPath = stripNamespace(relPath);
190+
return relPath;
191+
}
170192
}

src/main/java/dev/kinau/resourcepackvalidator/validator/Validator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ final protected <T extends JsonElement> T configValue(String key, T defaultValue
8484
}
8585

8686
final protected String logPrefix() {
87-
return String.format("[%-30s] ", getClass().getSimpleName());
87+
return String.format("[%s] ", getClass().getSimpleName());
8888
}
8989

9090
final protected ValidationResult<Output> failedError(String error, Object... args) {

0 commit comments

Comments
 (0)