1
0
mirror of https://github.com/GeyserMC/PackConverter.git synced 2025-12-19 14:59:21 +00:00

Add JsonMappings to order to allow other converters to have mappings

This commit is contained in:
Aurora
2025-10-31 10:39:31 +00:00
parent 544daf6396
commit c00da110aa
9 changed files with 166 additions and 118 deletions

View File

@@ -26,6 +26,7 @@
package org.geysermc.pack.converter.bootstrap;
import org.geysermc.pack.converter.util.DefaultLogListener;
import org.geysermc.pack.converter.util.LogListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -33,7 +34,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.PrintWriter;
import java.io.StringWriter;
public class BootstrapLogListener implements LogListener {
public class BootstrapLogListener extends DefaultLogListener {
private final ThunderGUI gui;
public BootstrapLogListener(ThunderGUI gui) {
@@ -45,21 +46,26 @@ public class BootstrapLogListener implements LogListener {
if (gui.debugMode.get()) {
appendText("DEBUG: " + message);
}
super.debug(message);
}
@Override
public void info(@NotNull String message) {
appendText(message);
super.info(message);
}
@Override
public void warn(@NotNull String message) {
appendText("WARNING: " + message);
super.warn(message);
}
@Override
public void error(@NotNull String message) {
appendText("ERROR: " + message);
super.error(message);
}
@Override
@@ -73,6 +79,7 @@ public class BootstrapLogListener implements LogListener {
appendText(writer.toString());
}
super.error(message, exception);
}
private void appendText(String text) {

View File

@@ -80,7 +80,7 @@ public final class AssetConverters {
public static final ConverterPipeline<Model, BedrockModel> MODEL = create(ModelConverter.INSTANCE);
public static final ConverterPipeline<SoundRegistry, Map<String, SoundDefinitions>> SOUND_REGISTRY = create(
(pack, context) -> pack.soundRegistries(), SoundRegistryConverter.INSTANCE);
public static final ConverterPipeline<Sound, Sound> SOUND = create(extractor(SoundSerializer.CATEGORY), SoundConverter.INSTANCE);
public static final ConverterPipeline<Sound, Sound> SOUND = create(SoundConverter.INSTANCE);
public static final ConverterPipeline<Texture, TransformedTexture> TEXTURE = create(TextureConverter.INSTANCE);
private static <JavaAsset, BedrockAsset> ConverterPipeline<JavaAsset, BedrockAsset> createSingle(BiFunction<ResourcePack, ExtractionContext, JavaAsset> extractor,

View File

@@ -27,11 +27,10 @@
package org.geysermc.pack.converter.type.sound;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import org.geysermc.pack.converter.pipeline.AssetCombiner;
import org.geysermc.pack.converter.pipeline.AssetConverter;
import org.geysermc.pack.converter.pipeline.CombineContext;
import org.geysermc.pack.converter.pipeline.ConversionContext;
import org.geysermc.pack.converter.pipeline.*;
import org.geysermc.pack.converter.util.JsonMappings;
import org.jetbrains.annotations.Nullable;
import team.unnamed.creative.ResourcePack;
import team.unnamed.creative.sound.Sound;
import java.io.FileOutputStream;
@@ -40,11 +39,17 @@ import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class SoundConverter implements AssetConverter<Sound, Sound>, AssetCombiner<Sound> {
public class SoundConverter implements AssetExtractor<Sound>, AssetConverter<Sound, Sound>, AssetCombiner<Sound> {
public static final SoundConverter INSTANCE = new SoundConverter();
@Override
public Collection<Sound> extract(ResourcePack pack, ExtractionContext context) {
return pack.sounds();
}
@Override
public @Nullable Sound convert(Sound sound, ConversionContext context) throws Exception {
return sound;
@@ -52,11 +57,15 @@ public class SoundConverter implements AssetConverter<Sound, Sound>, AssetCombin
@Override
public void include(BedrockResourcePack pack, List<Sound> sounds, CombineContext context) {
JsonMappings mappings = JsonMappings.getMapping("sounds");
List<String> exported = new ArrayList<>();
Path output = pack.directory().resolve(SoundRegistryConverter.BEDROCK_SOUNDS_LOCATION);
for (Sound sound : sounds) {
String path = sound.key().value();
String javaPath = sound.key().value();
List<String> paths = mappings.map(javaPath);
for (String path : paths) {
if (exported.contains(path)) {
context.warn("Conflicting sound file " + sound.key() + "!");
continue;
@@ -75,4 +84,5 @@ public class SoundConverter implements AssetConverter<Sound, Sound>, AssetCombin
exported.add(path);
}
}
}
}

View File

@@ -37,6 +37,8 @@ import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TransformedTexture;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.JsonMappings;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import team.unnamed.creative.ResourcePack;
import team.unnamed.creative.texture.Texture;
@@ -78,7 +80,7 @@ public class TextureConverter implements AssetExtractor<Texture>, AssetConverter
// TODO ideally textures should be transformed individually in the convert process, and not together in the extraction process, but this is hard to achieve,
// TODO and will need another big refactor to the texture transformation code
// TODO for now this will work, but for library users it might be nice to be able to properly convert singular textures with transformations
TextureMappings mappings = TextureMappings.textureMappings();
JsonMappings mappings = JsonMappings.getMapping("textures");
List<Texture> textures = new ArrayList<>(pack.textures());
context.info("Transforming textures...");
@@ -103,8 +105,8 @@ public class TextureConverter implements AssetExtractor<Texture>, AssetConverter
}
@Override
public @Nullable TransformedTexture convert(Texture texture, ConversionContext context) throws Exception {
TextureMappings mappings = TextureMappings.textureMappings();
public @NotNull TransformedTexture convert(Texture texture, ConversionContext context) throws Exception {
JsonMappings mappings = JsonMappings.getMapping("textures");
TransformedTexture transformed = new TransformedTexture(texture);
String input = texture.key().value();
@@ -113,36 +115,13 @@ public class TextureConverter implements AssetExtractor<Texture>, AssetConverter
String rootPath = relativePath.substring(0, relativePath.indexOf('/'));
String bedrockRoot = DIRECTORY_LOCATIONS.getOrDefault(rootPath, rootPath);
Object mappingObject = mappings.textures(relativePath);
if (mappingObject == null) {
mappingObject = mappings.textures(rootPath);
List<String> mapping = mappings.map(relativePath);
List<String> transformedOutputs = new ArrayList<>();
for (String item : mapping) {
transformedOutputs.add(bedrockRoot + item.substring(item.indexOf('/')) + ".png");
}
String fallbackPath = bedrockRoot + "/" + relativePath.substring(relativePath.indexOf('/') + 1) + ".png";
if (mappingObject instanceof Map<?,?> keyMappings) { // Handles common subdirectories
String sanitizedName = input.substring(input.indexOf('/') + 1);
if (sanitizedName.endsWith(".png")) sanitizedName = sanitizedName.substring(0, sanitizedName.length() - 4);
Object bedrockOutput = keyMappings.get(sanitizedName);
if (bedrockOutput instanceof String bedrockPath) {
transformed.output(bedrockRoot + "/" + bedrockPath + ".png");
} else if (bedrockOutput instanceof List<?> paths) {
for (String bedrockPath : (List<String>) paths) {
transformed.output(bedrockRoot + "/" + bedrockPath + ".png");
}
} else { // Fallback
transformed.output(fallbackPath);
}
} else if (mappingObject instanceof String str) { // Direct mappings
transformed.output(str + ".png");
} else if (mappingObject instanceof List<?> paths) { // Mappings where duplicate code paths exist
for (String path : (List<String>) paths) {
transformed.output(path + ".png");
}
} else { // Fallback
transformed.output(fallbackPath);
}
transformed.output(transformedOutputs);
return transformed;
}
@@ -206,6 +185,8 @@ public class TextureConverter implements AssetExtractor<Texture>, AssetConverter
}
}
Files.createDirectories(output.getParent());
try (OutputStream stream = Files.newOutputStream(output)) {
ImageIO.write(bedrockImage, "png", stream);
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/PackConverter
*
*/
package org.geysermc.pack.converter.type.texture;
import com.google.gson.Gson;
import lombok.ToString;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedHashMap;
@ToString
public class TextureMappings extends LinkedHashMap<String, Object> {
private static TextureMappings INSTANCE;
@Nullable
public Object textures(@NotNull String key) {
return this.get(key);
}
public static TextureMappings textureMappings() {
if (INSTANCE != null) {
return INSTANCE;
}
InputStream mappingsStream = TextureMappings.class.getResourceAsStream("/mappings/textures.json");
if (mappingsStream == null) {
throw new RuntimeException("Could not find textures.json mappings file!");
}
return INSTANCE = new Gson().fromJson(new InputStreamReader(mappingsStream), TextureMappings.class);
}
}

View File

@@ -28,8 +28,8 @@ package org.geysermc.pack.converter.type.texture.transformer;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import org.geysermc.pack.converter.type.texture.TextureMappings;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.JsonMappings;
import org.geysermc.pack.converter.util.LogListener;
import org.geysermc.pack.converter.util.LogListenerHelper;
import org.jetbrains.annotations.NotNull;
@@ -46,7 +46,7 @@ import java.util.Map;
import java.util.Optional;
public class TransformContext implements LogListenerHelper {
private final TextureMappings mappings;
private final JsonMappings mappings;
private final Collection<Texture> textures;
// TODO figure out how to handle this, this is executed in the extraction phase and ideally bedrock pack wouldn't be accessed then
@Deprecated(forRemoval = true)
@@ -57,7 +57,7 @@ public class TransformContext implements LogListenerHelper {
private final Map<Key, Texture> byKey = new HashMap<>();
public TransformContext(
TextureMappings mappings,
JsonMappings mappings,
Collection<Texture> textures,
BedrockResourcePack bedrockPack,
ResourcePack javaPack,
@@ -76,7 +76,7 @@ public class TransformContext implements LogListenerHelper {
}
}
public TextureMappings mappings() {
public JsonMappings mappings() {
return this.mappings;
}

View File

@@ -34,7 +34,7 @@ import java.util.List;
public class TransformedTexture {
private final Texture texture;
private final List<String> outputs = new ArrayList<>();
private List<String> outputs = new ArrayList<>();
public TransformedTexture(@NotNull Texture texture) {
this.texture = texture;
@@ -50,7 +50,7 @@ public class TransformedTexture {
return outputs;
}
public void output(@NotNull String output) {
outputs.add(output);
public void output(@NotNull List<String> output) {
outputs = new ArrayList<>(output);
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2025 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/PackConverter
*
*/
package org.geysermc.pack.converter.util;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JsonMappings {
private static final Map<String, JsonMappings> CACHE = new HashMap<>();
public static JsonMappings getMapping(String name) {
if (CACHE.containsKey(name)) {
return CACHE.get(name);
}
InputStream mappingsStream = JsonMappings.class.getResourceAsStream("/mappings/%s.json".formatted(name));
if (mappingsStream == null) {
throw new RuntimeException("Could not find %s.json mappings file!".formatted(name));
}
JsonObject jsonMappings = JsonParser.parseReader(new InputStreamReader(mappingsStream)).getAsJsonObject();
Map<String, List<String>> mappings = new HashMap<>();
for (Map.Entry<String, JsonElement> entry : jsonMappings.entrySet()) {
mappings.putAll(extractMapping(entry.getValue(), entry.getKey(), List.of()));
}
JsonMappings instance = new JsonMappings(mappings);
CACHE.put(name, instance);
return instance;
}
private static Map<String, List<String>> extractMapping(JsonElement element, String key, List<String> parents) {
if (element.isJsonObject()) {
Map<String, List<String>> mappings = new HashMap<>();
for (Map.Entry<String, JsonElement> entry : element.getAsJsonObject().entrySet()) {
List<String> newParents = new ArrayList<>(parents);
newParents.add(key);
mappings.putAll(extractMapping(entry.getValue(), key + "/" + entry.getKey(), newParents));
}
return mappings;
} else if (element.isJsonArray()) {
List<String> paths = new ArrayList<>();
for (JsonElement arrayElement : element.getAsJsonArray()) {
if (arrayElement.isJsonPrimitive()) {
paths.add(arrayElement.getAsString());
} else {
throw new RuntimeException("Invalid item found within mapping file, items in an array must be primitives.");
}
}
return Map.of(key, paths);
} else if (element.isJsonPrimitive()) {
String prefix = "";
if (!parents.isEmpty()) prefix = String.join("/", parents) + "/";
return Map.of(key, List.of(prefix + element.getAsString()));
}
return Map.of();
}
private final Map<String, List<String>> mappings;
private JsonMappings(Map<String, List<String>> mappings) {
this.mappings = mappings;
}
public List<String> map(String input) {
return mappings.getOrDefault(input, List.of(input));
}
}

View File

@@ -0,0 +1 @@
{}