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

Split conversion process into 3 parts, make converters more standalone (#42)

* Work on splitting converters

* Properly split converting of sound registries and sounds

* Testing stuff

* Implement splitting of texture conversion, still some things to do

* Add bedrock pack back to TransformContext

* Transition

* AssetCollector -> AssetCombiner

* Remove unnecessary interfaces

* Re-implement action listeners

* Final things

* Cleanup

* Some Javadocs, fixup copyright

* Relocate pipeline classes and rename converter package -> type, create KeyUtil to lessen code warnings
This commit is contained in:
Eclipse
2025-09-21 19:43:37 +00:00
committed by GitHub
parent 63ac0b141f
commit a344cd37f7
74 changed files with 1672 additions and 1371 deletions

View File

@@ -28,7 +28,7 @@ package org.geysermc.pack.converter.bootstrap;
import com.formdev.flatlaf.intellijthemes.FlatArcDarkIJTheme;
import org.geysermc.pack.converter.PackConverter;
import org.geysermc.pack.converter.converter.Converters;
import org.geysermc.pack.converter.pipeline.AssetConverters;
import java.io.*;
import java.nio.file.Path;
@@ -77,7 +77,7 @@ public class Main {
.input(Path.of(inputPath))
.output(Path.of(outputPath))
.packName(packName)
.converters(Converters.defaultConverters(debug))
.converters(AssetConverters.converters(debug))
.convert()
.pack();
} else {

View File

@@ -28,7 +28,7 @@ package org.geysermc.pack.converter.bootstrap;
import com.twelvemonkeys.image.BufferedImageIcon;
import org.geysermc.pack.converter.PackConverter;
import org.geysermc.pack.converter.converter.Converters;
import org.geysermc.pack.converter.pipeline.AssetConverters;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.ZipUtils;
@@ -128,7 +128,7 @@ public class ThunderGUI extends JFrame {
.output(outputPath)
.packName(packName.getText().isBlank() ? inputPath.getFileName().toString() : packName.getText())
.vanillaPackPath(vanillaPackPath)
.converters(Converters.defaultConverters(this.debugMode.get()))
.converters(AssetConverters.converters(this.debugMode.get()))
.logListener(logListener)
.convert()
.pack();

View File

@@ -1,71 +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;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import org.geysermc.pack.converter.data.ConversionData;
import org.geysermc.pack.converter.util.LogListener;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.ResourcePack;
import java.nio.file.Path;
public record PackConversionContext<T extends ConversionData>(
@NotNull T data,
@NotNull PackConverter packConverter,
@NotNull ResourcePack javaResourcePack,
@NotNull BedrockResourcePack bedrockResourcePack,
@NotNull LogListener logListener) {
public Path inputDirectory() {
return this.data.inputDirectory();
}
public Path outputDirectory() {
return this.data.outputDirectory();
}
public void debug(@NotNull String message) {
this.logListener.debug(message);
}
public void info(@NotNull String message) {
this.logListener.info(message);
}
public void warn(@NotNull String message) {
this.logListener.warn(message);
}
public void error(@NotNull String message) {
this.logListener.error(message);
}
public void error(@NotNull String message, @NotNull Throwable exception) {
this.logListener.error(message, exception);
}
}

View File

@@ -28,10 +28,12 @@ package org.geysermc.pack.converter;
import org.apache.commons.io.file.PathUtils;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import org.geysermc.pack.converter.converter.ActionListener;
import org.geysermc.pack.converter.converter.Converter;
import org.geysermc.pack.converter.data.ConversionData;
import org.geysermc.pack.converter.util.*;
import org.geysermc.pack.converter.pipeline.ConverterPipeline;
import org.geysermc.pack.converter.util.DefaultLogListener;
import org.geysermc.pack.converter.util.LogListener;
import org.geysermc.pack.converter.util.NioDirectoryFileTreeReader;
import org.geysermc.pack.converter.util.VanillaPackProvider;
import org.geysermc.pack.converter.util.ZipUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import team.unnamed.creative.ResourcePack;
@@ -43,9 +45,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
/**
@@ -63,11 +64,9 @@ public final class PackConverter {
private boolean compressed;
private boolean enforcePackCheck = false;
private final Map<Class<?>, List<ActionListener<?>>> actionListeners = new IdentityHashMap<>();
private BiConsumer<ResourcePack, BedrockResourcePack> postProcessor;
private final List<Converter<?>> converters = new ArrayList<>();
private final List<ConverterPipeline<?, ?>> converters = new ArrayList<>();
private Path tmpDir;
@@ -187,7 +186,7 @@ public final class PackConverter {
* @param converter the converter to add
* @return this instance
*/
public PackConverter converter(@NotNull Converter<?> converter) {
public PackConverter converter(@NotNull ConverterPipeline<?, ?> converter) {
this.converters.add(converter);
return this;
}
@@ -198,7 +197,7 @@ public final class PackConverter {
* @param converters the converters to add
* @return this instance
*/
public PackConverter converters(@NotNull List<? extends Converter<?>> converters) {
public PackConverter converters(@NotNull List<? extends ConverterPipeline<?, ?>> converters) {
this.converters.addAll(converters);
return this;
}
@@ -227,42 +226,6 @@ public final class PackConverter {
return this;
}
/**
* Sets a list of action listeners for a specific conversion data class.
* <p>
* This is particularly useful for external programs that may rely on
* various bits of information from the pack converter at different
* stages.
*
* @param clazz the conversion data class
* @param actionListeners the action listeners
* @return this instance
* @param <T> the conversion data type
*/
public <T extends ConversionData> PackConverter actionListeners(@NotNull Class<T> clazz, @NotNull ActionListener<T>... actionListeners) {
this.actionListeners.put(clazz, List.of(actionListeners));
return this;
}
/**
* Sets the action listeners.
* <p>
* This is particularly useful for external programs that may rely on
* various bits of information from the pack converter at different
* stages.
*
* @param actionListeners the action listeners
* @return this instance
* @param <T> the conversion data type
*/
public <T extends ConversionData> PackConverter actionListeners(@NotNull Map<Class<T>, List<ActionListener<T>>> actionListeners) {
for (Map.Entry<Class<T>, List<ActionListener<T>>> entry : actionListeners.entrySet()) {
this.actionListeners.put(entry.getKey(), (List) entry.getValue());
}
return this;
}
/**
* Sets the post processor for the converted resource pack.
* <p>
@@ -320,25 +283,10 @@ public final class PackConverter {
ResourcePack vanillaResourcePack = MinecraftResourcePackReader.minecraft().readFromZipFile(vanillaPackPath);
BedrockResourcePack bedrockResourcePack = new BedrockResourcePack(this.tmpDir);
final Converter.ConversionDataCreationContext conversionDataCreationContext = new Converter.ConversionDataCreationContext(
this, logListener, input, this.tmpDir, javaResourcePack, vanillaResourcePack
);
int errors = 0;
for (Converter converter : this.converters) {
ConversionData data = converter.createConversionData(conversionDataCreationContext);
PackConversionContext<?> context = new PackConversionContext<>(data, this, javaResourcePack, bedrockResourcePack, this.logListener);
List<ActionListener<?>> actionListeners = this.actionListeners.getOrDefault(data.getClass(), List.of());
try {
actionListeners.forEach(actionListener -> actionListener.preConvert((PackConversionContext) context));
converter.convert(context);
actionListeners.forEach(actionListener -> actionListener.postConvert((PackConversionContext) context));
} catch (Throwable t) {
this.logListener.error("Error converting pack!", t);
errors++;
}
}
int errors = converters.stream()
.mapToInt(converter -> converter.convert(javaResourcePack, Optional.of(vanillaResourcePack),
bedrockResourcePack, packName(), textureSubdirectory, logListener))
.sum();
if (this.postProcessor != null) {
this.postProcessor.accept(javaResourcePack, bedrockResourcePack);

View File

@@ -1,75 +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.converter.lang;
import com.google.auto.service.AutoService;
import org.geysermc.pack.converter.PackConversionContext;
import org.geysermc.pack.converter.converter.BaseConverter;
import org.geysermc.pack.converter.converter.Converter;
import org.geysermc.pack.converter.data.BaseConversionData;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.lang.Language;
import java.util.Collection;
import java.util.Map;
import java.util.regex.Pattern;
@AutoService(Converter.class)
public class LangConverter extends BaseConverter {
private static final String BEDROCK_TEXTS_LOCATION = "texts";
private final Pattern positionalStringReplacement = Pattern.compile("%([0-9]+)\\$s");
@Override
public void convert(@NotNull PackConversionContext<BaseConversionData> context) throws Exception {
Collection<Language> languages = context.javaResourcePack().languages();
for (Language language : languages) {
Map<String, String> strings = language.translations();
for (Map.Entry<String, String> entry : strings.entrySet()) {
String value = entry.getValue();
// Replace %d with %s
value = value.replace("%d", "%s");
// Replace `%x$s` with `%x`
value = positionalStringReplacement.matcher(value).replaceAll("%$1");
entry.setValue(value);
}
String languageKey = language.key().value();
// Convert the language key to the Bedrock equivalent
if (languageKey.equals("no_no")) {
languageKey = "nb_no";
}
context.bedrockResourcePack().addLanguage(languageKey, strings);
}
}
}

View File

@@ -1,261 +0,0 @@
/*
* Copyright (c) 2019-2024 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.converter.model;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import org.geysermc.pack.bedrock.resource.models.entity.ModelEntity;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.Geometry;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.Bones;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.Description;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.Cubes;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.Uv;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.Down;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.East;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.North;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.South;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.Up;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.West;
import org.geysermc.pack.converter.PackConversionContext;
import org.geysermc.pack.converter.converter.Converter;
import org.geysermc.pack.converter.data.ModelConversionData;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.ResourcePack;
import team.unnamed.creative.base.CubeFace;
import team.unnamed.creative.model.Element;
import team.unnamed.creative.model.ElementFace;
import team.unnamed.creative.model.ElementRotation;
import team.unnamed.creative.model.Model;
import team.unnamed.creative.texture.TextureUV;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@AutoService(Converter.class)
public class ModelConverter implements Converter<ModelConversionData> {
private static final String FORMAT_VERSION = "1.16.0";
private static final String GEOMETRY_FORMAT = "geometry.%s";
private static final float[] ELEMENT_OFFSET = new float[] { 8, 0, 8 };
@Override
public void convert(@NotNull PackConversionContext<ModelConversionData> context) throws Exception {
ResourcePack javaPack = context.javaResourcePack();
BedrockResourcePack bedrockPack = context.bedrockResourcePack();
Collection<Model> models = javaPack.models();
if (models.isEmpty()) {
return;
}
ModelStitcher.Provider provider = context.data().getModelProvider();
for (Model model : models) {
model = new ModelStitcher(provider, model, context.logListener()).stitch();
List<Element> elements = model.elements();
if (elements.isEmpty()) {
context.debug("Model " + model.key().key() + " has no elements");
continue;
}
String value = model.key().value();
context.debug("Converting model " + model.key().key() + ":" + value);
// TODO: Convert item models but save differently?
if (value.startsWith("item/")) {
continue;
}
ModelEntity modelEntity = new ModelEntity();
modelEntity.formatVersion(FORMAT_VERSION);
Geometry geometry = new Geometry();
String namespace = model.key().namespace();
String fileName = value.substring(value.lastIndexOf('/') + 1);
String geoName = (namespace.equals(Key.MINECRAFT_NAMESPACE) ? "" : namespace + ".") + fileName;
// TODO: Don't hardcode all this
Description description = new Description();
description.identifier(String.format(GEOMETRY_FORMAT, geoName));
description.textureWidth(16);
description.textureHeight(16);
description.visibleBoundsWidth(2);
description.visibleBoundsHeight(2);
description.visibleBoundsOffset(new float[] { 0.0f, 0.25f, 0.0f });
geometry.description(description);
List<Bones> bones = new ArrayList<>();
// TODO: Should each element be its own bone rather
// than its own cube in the same bone?
int i = 0;
for (Element element : elements) {
float[] from = element.from().toArray();
float[] to = element.to().toArray();
Bones bone = new Bones();
bone.name("bone_" + i++);
bone.pivot(new float[] { ELEMENT_OFFSET[0], ELEMENT_OFFSET[1], -ELEMENT_OFFSET[2] });
Cubes cube = new Cubes();
cube.origin(new float[] { ELEMENT_OFFSET[0] - to[0], from[1], from[2] - ELEMENT_OFFSET[2] });
cube.size(new float[] { to[0] - from[0], to[1] - from[1], to[2] - from[2] });
ElementRotation elementRotation = element.rotation();
if (elementRotation != null) {
float[] origin = elementRotation.origin().toArray();
cube.pivot(new float[] { ELEMENT_OFFSET[0] - origin[0], ELEMENT_OFFSET[1] - origin[1], origin[2] - ELEMENT_OFFSET[2] });
float angle = elementRotation.angle();
float[] rotation = new float[3];
switch (elementRotation.axis()) {
case X -> rotation[0] = -angle;
case Y -> rotation[1] = -angle;
case Z -> rotation[2] = -angle;
}
cube.rotation(rotation);
}
Uv uv = new Uv();
for (Map.Entry<CubeFace, ElementFace> entry : element.faces().entrySet()) {
CubeFace face = entry.getKey();
ElementFace elementFace = entry.getValue();
if (elementFace.uv0() == null) {
continue;
}
// The Java pack lib we use does this weird thing where it
// divides the UV by 16, so we need to multiply it by 16
String texture = elementFace.texture().replace("#", "");
applyUv(uv, face, texture, multiplyUv(elementFace.uv0(), 16f));
}
cube.uv(uv);
bone.cubes(List.of(cube));
bones.add(bone);
}
geometry.bones(bones);
modelEntity.geometry(List.of(geometry));
if (model.key().namespace().contains("entity")) {
bedrockPack.addEntityModel(modelEntity, fileName + ".json");
} else {
// Bedrock only has a concept of entity or block models
bedrockPack.addBlockModel(modelEntity, fileName + ".json");
}
context.data().addStitchedModel(model);
}
}
@Override
public ModelConversionData createConversionData(@NotNull ConversionDataCreationContext context) {
return new ModelConversionData(
context.inputDirectory(), context.outputDirectory(),
ModelStitcher.vanillaProvider(context.javaResourcePack(), context.logListener(), context.vanillaResourcePack()),
context.vanillaResourcePack()
);
}
private TextureUV multiplyUv(TextureUV textureUV, float mult) {
return TextureUV.uv(textureUV.from().multiply(mult), textureUV.to().multiply(mult));
}
private static void applyUv(Uv uv, CubeFace face, String texture, TextureUV faceUv) {
float[] uvs;
float[] uvSize;
// These values are flipped for some reason
if (face == CubeFace.DOWN || face == CubeFace.UP) {
uvs = new float[] { faceUv.to().x(), faceUv.to().y() };
uvSize = new float[] { faceUv.from().x() - faceUv.to().x(), faceUv.from().y() - faceUv.to().y() };
} else {
uvs = new float[] { faceUv.from().x(), faceUv.from().y() };
uvSize = new float[] { faceUv.to().x() - faceUv.from().x(), faceUv.to().y() - faceUv.from().y() };
}
switch (face) {
case NORTH -> {
North north = new North();
north.uv(uvs);
north.uvSize(uvSize);
north.materialInstance(texture);
uv.north(north);
}
case SOUTH -> {
South south = new South();
south.uv(uvs);
south.uvSize(uvSize);
south.materialInstance(texture);
uv.south(south);
}
case EAST -> {
East east = new East();
east.uv(uvs);
east.uvSize(uvSize);
east.materialInstance(texture);
uv.east(east);
}
case WEST -> {
West west = new West();
west.uv(uvs);
west.uvSize(uvSize);
west.materialInstance(texture);
uv.west(west);
}
case UP -> {
Up up = new Up();
up.uv(uvs);
up.uvSize(uvSize);
up.materialInstance(texture);
uv.up(up);
}
case DOWN -> {
Down down = new Down();
down.uv(uvs);
down.uvSize(uvSize);
down.materialInstance(texture);
uv.down(down);
}
}
}
}

View File

@@ -1,92 +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.converter.sound;
import com.google.auto.service.AutoService;
import org.apache.commons.io.file.PathUtils;
import org.geysermc.pack.bedrock.resource.sounds.sounddefinitions.SoundDefinitions;
import org.geysermc.pack.bedrock.resource.sounds.sounddefinitions.Sounds;
import org.geysermc.pack.converter.Constants;
import org.geysermc.pack.converter.PackConversionContext;
import org.geysermc.pack.converter.converter.BaseConverter;
import org.geysermc.pack.converter.converter.Converter;
import org.geysermc.pack.converter.data.BaseConversionData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import team.unnamed.creative.sound.SoundEntry;
import team.unnamed.creative.sound.SoundEvent;
import team.unnamed.creative.sound.SoundRegistry;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
@AutoService(Converter.class)
public class SoundConverter extends BaseConverter {
private static final String JAVA_SOUNDS_LOCATION = Constants.JAVA_PACK_LOCATION + "/sounds";
private static final String BEDROCK_SOUNDS_LOCATION = "sounds";
@Override
public void convert(@NotNull PackConversionContext<BaseConversionData> context) throws Exception {
Collection<SoundRegistry> registry = context.javaResourcePack().soundRegistries();
for (SoundRegistry soundRegistry : registry) {
@Unmodifiable @NotNull Collection<SoundEvent> sounds = soundRegistry.sounds();
for (SoundEvent value : sounds) {
String key = value.key().asString();
SoundDefinitions definition = new SoundDefinitions();
definition.useLegacyMaxDistance(true); // TODO: Needed?
definition.maxDistance(64); // ???
for (SoundEntry sound : value.sounds()) {
Sounds bedrockSound = new Sounds();
bedrockSound.name(BEDROCK_SOUNDS_LOCATION + "/" + sound.key().value());
bedrockSound.stream(sound.stream());
bedrockSound.loadOnLowMemory(true);
bedrockSound.volume(sound.volume());
bedrockSound.pitch(sound.pitch());
bedrockSound.weight(sound.weight());
definition.sounds().add(bedrockSound);
}
context.bedrockResourcePack().addSoundDefinition(key, definition);
}
// Relocate sound files
Path input = context.inputDirectory().resolve(String.format(JAVA_SOUNDS_LOCATION, soundRegistry.namespace()));
Path output = context.outputDirectory().resolve(BEDROCK_SOUNDS_LOCATION);
if (Files.notExists(output)) {
Files.createDirectories(output);
}
PathUtils.copyDirectory(input, output, StandardCopyOption.REPLACE_EXISTING);
}
}
}

View File

@@ -1,207 +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.converter.texture;
import com.google.auto.service.AutoService;
import org.geysermc.pack.converter.PackConversionContext;
import org.geysermc.pack.converter.converter.Converter;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.converter.texture.transformer.TransformedTexture;
import org.geysermc.pack.converter.data.TextureConversionData;
import org.geysermc.pack.converter.util.ImageUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import javax.imageio.ImageIO;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.stream.StreamSupport;
@AutoService(Converter.class)
public class TextureConverter implements Converter<TextureConversionData> {
public static final String BEDROCK_TEXTURES_LOCATION = "textures";
private final List<TextureTransformer> transformers = StreamSupport.stream(ServiceLoader.load(TextureTransformer.class).spliterator(), false)
.sorted(Comparator.comparingInt(TextureTransformer::order))
.toList();
public static final Map<String, String> DIRECTORY_LOCATIONS = Map.of(
"block", "blocks",
"item", "items",
"gui", "ui"
);
@Override
public void convert(@NotNull PackConversionContext<TextureConversionData> context) throws Exception {
TextureMappings mappings = TextureMappings.textureMappings();
List<Texture> textures = new ArrayList<>(context.javaResourcePack().textures());
context.info("Transforming textures...");
TransformContext transformContext = new TransformContext(
context,
mappings,
textures,
context.bedrockResourcePack(),
context.javaResourcePack()
);
for (TextureTransformer transformer : this.transformers) {
transformer.transform(transformContext);
}
context.info("Transformed textures!");
context.info("Writing textures...");
for (Texture texture : textures) {
String input = texture.key().value();
Path texturePath = context.outputDirectory().resolve(BEDROCK_TEXTURES_LOCATION);
Path potentialOutput = texturePath.resolve(input);
String relativePath = texturePath.relativize(potentialOutput).toString().replace(File.separatorChar, '/');
if (relativePath.endsWith(".png")) relativePath = relativePath.substring(0, relativePath.length() - 4);
String rootPath = relativePath.substring(0, relativePath.indexOf('/'));
String bedrockRoot = DIRECTORY_LOCATIONS.getOrDefault(rootPath, rootPath);
List<Path> outputs = new ArrayList<>();
List<String> outputPaths = new ArrayList<>();
Object mappingObject = mappings.textures(relativePath);
if (mappingObject == null) {
mappingObject = mappings.textures(rootPath);
}
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) {
outputPaths.add(bedrockRoot + "/" + bedrockPath + ".png");
} else if (bedrockOutput instanceof List<?> paths) {
for (String bedrockPath : (List<String>) paths) {
outputPaths.add(bedrockRoot + "/" + bedrockPath + ".png");
}
} else { // Fallback
outputPaths.add(fallbackPath);
}
} else if (mappingObject instanceof String str) { // Direct mappings
outputPaths.add(str + ".png");
} else if (mappingObject instanceof List<?> paths) { // Mappings where duplicate code paths exist
for (String path : (List<String>) paths) {
outputPaths.add(path + ".png");
}
} else { // Fallback
outputPaths.add(fallbackPath);
}
String bedrockDirectory = "%s/%s";
if (context.data().textureSubdirectory() != null) {
bedrockDirectory = "%s/" + context.data().textureSubdirectory() + "/%s";
}
for (String outputPath : outputPaths) {
context.debug(String.format("Converted %s to %s, writing texture.", input, outputPath));
String root = outputPath.substring(0, outputPath.indexOf('/'));
String value = outputPath.substring(outputPath.indexOf('/') + 1);
outputs.add(texturePath.resolve((
bedrockDirectory.formatted(root, value)
).replace('/', File.separatorChar)));
}
byte[] bytes = texture.data().toByteArray();
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
for (Path output : outputs) {
TransformedTexture transformedTexture = new TransformedTexture(texture, output);
if (output.getParent() != null && Files.notExists(output.getParent())) {
Files.createDirectories(output.getParent());
}
BufferedImage bedrockImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bedrockImage.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(image, 0, 0, null);
g.dispose();
String pngKey = context.outputDirectory().relativize(output).toString().replace(File.separatorChar, '/');
PngToTgaMappings.TgaMapping mapping = PngToTgaMappings.mapping(pngKey);
if (mapping != null) {
Path tgaPath = context.outputDirectory().resolve(mapping.value());
if (Files.notExists(tgaPath.getParent())) {
Files.createDirectories(tgaPath.getParent());
}
ImageUtil.writeTGA(tgaPath, bedrockImage);
if (!mapping.keep()) {
Files.deleteIfExists(output);
continue;
}
}
try (OutputStream stream = Files.newOutputStream(output)) {
ImageIO.write(bedrockImage, "png", stream);
}
context.data().addTransformedTexture(transformedTexture);
}
}
context.info("Written textures!");
context.info("Texture conversion complete!");
}
@Override
public TextureConversionData createConversionData(@NotNull ConversionDataCreationContext context) {
return new TextureConversionData(
context.inputDirectory(),
context.outputDirectory(),
context.converter().textureSubdirectory(),
context.vanillaResourcePack()
);
}
}

View File

@@ -1,62 +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.data;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.ResourcePack;
import java.nio.file.Path;
public class BaseConversionData implements ConversionData {
private final Path inputDirectory;
private final Path outputDirectory;
private final ResourcePack vanillaPack;
public BaseConversionData(@NotNull Path inputDirectory, @NotNull Path outputDirectory, @NotNull ResourcePack vanillaPack) {
this.inputDirectory = inputDirectory;
this.outputDirectory = outputDirectory;
this.vanillaPack = vanillaPack;
}
@NotNull
@Override
public Path inputDirectory() {
return this.inputDirectory;
}
@NotNull
@Override
public Path outputDirectory() {
return this.outputDirectory;
}
@NotNull
@Override
public ResourcePack vanillaPack() {
return this.vanillaPack;
}
}

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.data;
import lombok.Getter;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.model.ModelStitcher;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.ResourcePack;
import team.unnamed.creative.model.Model;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
public class ModelConversionData extends BaseConversionData {
private final Map<Key, Model> stitchedModels = new HashMap<>();
@Getter
private final ModelStitcher.Provider modelProvider;
public ModelConversionData(@NotNull Path inputDirectory, @NotNull Path outputDirectory, ModelStitcher.Provider modelProvider, @NotNull ResourcePack vanillaPack) {
super(inputDirectory, outputDirectory, vanillaPack);
this.modelProvider = modelProvider;
}
public void addStitchedModel(@NotNull Model model) {
this.stitchedModels.put(model.key(), model);
}
@NotNull
public Model model(@NotNull Key key) {
return this.stitchedModels.get(key);
}
}

View File

@@ -1,61 +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.data;
import org.geysermc.pack.converter.converter.texture.transformer.TransformedTexture;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import team.unnamed.creative.ResourcePack;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class TextureConversionData extends BaseConversionData {
private final List<TransformedTexture> transformedTextures = new ArrayList<>();
private final String textureSubdirectory;
public TextureConversionData(@NotNull Path inputDirectory, @NotNull Path outputDirectory, @Nullable String textureSubdirectory, @NotNull ResourcePack vanillaPack) {
super(inputDirectory, outputDirectory, vanillaPack);
this.textureSubdirectory = textureSubdirectory;
}
public void addTransformedTexture(@NotNull TransformedTexture transformedTexture) {
this.transformedTextures.add(transformedTexture);
}
@NotNull
public List<TransformedTexture> transformedTextures() {
return this.transformedTextures;
}
@Nullable
public String textureSubdirectory() {
return this.textureSubdirectory;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2019-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.pipeline;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import team.unnamed.creative.ResourcePack;
import java.util.Collection;
import java.util.List;
/**
* Listeners for actions that occur during execution of a {@link ConverterPipeline}. All implemented listeners should be as pure as possible:
* no side effects should occur, and no modifications should be made to received arguments, unless specifically stated this is allowed.
*/
public interface ActionListener<JavaAsset, BedrockAsset> {
/**
* Executed after a {@link ConverterPipeline} has extracted all applicable {@link JavaAsset}s for conversion of a {@link ResourcePack}. This method
* can be used to add extra {@link JavaAsset}s for conversion by adding them to the {@code extracted} collection, which is mutable.
*
* @param pack the resource pack that is used for extraction of {@link JavaAsset}s
* @param extracted the {@link JavaAsset}s the {@link ConverterPipeline} has extracted from the resource pack
* @param context the {@link ExtractionContext}
*/
default void postExtract(ResourcePack pack, Collection<JavaAsset> extracted, ExtractionContext context) {
}
/**
* Executed after a {@link ConverterPipeline} has converted a {@link JavaAsset} to a {@link BedrockAsset}. This method
* can be used to modify the {@link BedrockAsset} after conversion.
*
* <p><em>Please note that the {@link BedrockAsset} should be considered immutable, and no modifications should be made to it. Instead,
* return a modified copy.</em></p>
*
* @param asset the {@link JavaAsset} that was converted
* @param bedrockAsset the resulting {@link BedrockAsset}
* @param context the {@link ConversionContext}
* @return the modified {@link BedrockAsset}, or return the original if no modifications were made
*/
default BedrockAsset postConvert(JavaAsset asset, BedrockAsset bedrockAsset, ConversionContext context) {
return bedrockAsset;
}
/**
* Executed after a {@link ConverterPipeline} has added all converted {@link BedrockAsset}s to the {@link BedrockResourcePack}. This method
* can be used to add additional assets to the output {@code pack}.
*
* @param pack the bedrock resource pack
* @param assets the assets added to the bedrock resource pack (immutable)
* @param context the {@link CombineContext}
*/
default void postInclude(BedrockResourcePack pack, List<BedrockAsset> assets, CombineContext context) {
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2025-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.pipeline;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import java.util.List;
@FunctionalInterface
public interface AssetCombiner<BedrockAsset> {
void include(BedrockResourcePack pack, List<BedrockAsset> assets, CombineContext context);
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2025-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.pipeline;
import org.jetbrains.annotations.Nullable;
@FunctionalInterface
public interface AssetConverter<JavaAsset, BedrockAsset> {
@Nullable
BedrockAsset convert(JavaAsset asset, ConversionContext context) throws Exception;
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 2025-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.pipeline;
import com.google.gson.JsonElement;
import net.kyori.adventure.key.Keyed;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import org.geysermc.pack.bedrock.resource.Manifest;
import org.geysermc.pack.bedrock.resource.sounds.sounddefinitions.SoundDefinitions;
import org.geysermc.pack.converter.type.base.PackIconConverter;
import org.geysermc.pack.converter.type.base.PackManifestConverter;
import org.geysermc.pack.converter.type.lang.BedrockLanguage;
import org.geysermc.pack.converter.type.lang.LangConverter;
import org.geysermc.pack.converter.type.misc.SplashTextConverter;
import org.geysermc.pack.converter.type.model.BedrockModel;
import org.geysermc.pack.converter.type.model.ModelConverter;
import org.geysermc.pack.converter.type.sound.SoundConverter;
import org.geysermc.pack.converter.type.sound.SoundRegistryConverter;
import org.geysermc.pack.converter.type.texture.TextureConverter;
import org.geysermc.pack.converter.type.texture.transformer.TransformedTexture;
import team.unnamed.creative.ResourcePack;
import team.unnamed.creative.base.Writable;
import team.unnamed.creative.lang.Language;
import team.unnamed.creative.metadata.pack.PackMeta;
import team.unnamed.creative.model.Model;
import team.unnamed.creative.part.ResourcePackPart;
import team.unnamed.creative.serialize.minecraft.ResourceCategory;
import team.unnamed.creative.serialize.minecraft.language.LanguageSerializer;
import team.unnamed.creative.serialize.minecraft.sound.SoundSerializer;
import team.unnamed.creative.sound.Sound;
import team.unnamed.creative.sound.SoundRegistry;
import team.unnamed.creative.texture.Texture;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@SuppressWarnings({"UnstableApiUsage", "unused"})
public final class AssetConverters {
private static final List<ConverterPipeline<?, ?>> CONVERTERS = new ArrayList<>();
private static boolean bootstrapped = false;
public static final ConverterPipeline<PackMeta, Manifest> MANIFEST = createSingle(
(pack, context) -> pack.packMeta(),
PackManifestConverter.INSTANCE,
BedrockResourcePack::manifest);
public static final ConverterPipeline<Writable, byte[]> ICON = createSingle(PackIconConverter::extractIcon, PackIconConverter.INSTANCE, BedrockResourcePack::icon);
public static final ConverterPipeline<Writable, JsonElement> SPLASH_TEXT = createSingle(
(pack, context) -> pack.unknownFile("assets/minecraft/texts/splashes.txt"),
SplashTextConverter.INSTANCE,
(pack, splashes) -> pack.addExtraFile(splashes, "splashes.json"));
public static final ConverterPipeline<Language, BedrockLanguage> LANGUAGE = create(extractor(LanguageSerializer.CATEGORY), LangConverter.INSTANCE);
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<Texture, TransformedTexture> TEXTURE = create(TextureConverter.INSTANCE);
private static <JavaAsset, BedrockAsset> ConverterPipeline<JavaAsset, BedrockAsset> createSingle(BiFunction<ResourcePack, ExtractionContext, JavaAsset> extractor,
AssetConverter<JavaAsset, BedrockAsset> converter,
BiConsumer<BedrockResourcePack, BedrockAsset> collector) {
return create(
(pack, context) -> Optional.ofNullable(extractor.apply(pack, context))
.map(List::of)
.orElse(List.of()),
converter,
(pack, assets, context) -> collector.accept(pack, assets.get(0)));
}
public static <JavaAsset, BedrockAsset,
ConverterCombiner extends AssetConverter<JavaAsset, BedrockAsset>
& AssetCombiner<BedrockAsset>> ConverterPipeline<JavaAsset, BedrockAsset> create(AssetExtractor<JavaAsset> extractor,
ConverterCombiner converterCombiner) {
return create(extractor, converterCombiner, converterCombiner);
}
public static <JavaAsset, BedrockAsset,
Pipeline extends AssetExtractor<JavaAsset> & AssetConverter<JavaAsset, BedrockAsset>
& AssetCombiner<BedrockAsset>> ConverterPipeline<JavaAsset, BedrockAsset> create(Pipeline pipeline) {
return create(pipeline, pipeline, pipeline);
}
public static <JavaAsset, BedrockAsset> ConverterPipeline<JavaAsset, BedrockAsset> create(AssetExtractor<JavaAsset> extractor,
AssetConverter<JavaAsset, BedrockAsset> converter,
AssetCombiner<BedrockAsset> combiner) {
ConverterPipeline<JavaAsset, BedrockAsset> pipeline = new ConverterPipeline<>(extractor, converter, combiner, false, Optional.empty());
if (!bootstrapped) {
CONVERTERS.add(pipeline);
}
return pipeline;
}
public static List<ConverterPipeline<?, ?>> converters(boolean experimental) {
return List.copyOf(CONVERTERS).stream()
.filter(converter -> experimental || !converter.experimental())
.toList();
}
static {
// This will cause 3rd-party converters made using the utility methods here to not be added to the CONVERTERS array
bootstrapped = true;
}
private static <JavaAsset extends Keyed & ResourcePackPart> AssetExtractor<JavaAsset> extractor(ResourceCategory<JavaAsset> category) {
return (pack, context) -> category.lister().apply(pack);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
* Copyright (c) 2025-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
@@ -24,21 +24,14 @@
*
*/
package org.geysermc.pack.converter.data;
package org.geysermc.pack.converter.pipeline;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.ResourcePack;
import java.nio.file.Path;
import java.util.Collection;
public interface ConversionData {
@FunctionalInterface
public interface AssetExtractor<JavaAsset> {
@NotNull
Path inputDirectory();
@NotNull
Path outputDirectory();
@NotNull
ResourcePack vanillaPack();
Collection<JavaAsset> extract(ResourcePack pack, ExtractionContext context);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2025-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.pipeline;
import org.geysermc.pack.converter.util.LogListener;
import org.geysermc.pack.converter.util.LogListenerHelper;
public record CombineContext(String textureSubDirectory, LogListener logListener) implements LogListenerHelper {
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2025-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.pipeline;
import org.geysermc.pack.converter.util.LogListener;
import org.geysermc.pack.converter.util.LogListenerHelper;
public record ConversionContext(String packName, LogListener logListener) implements LogListenerHelper {
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2025-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.pipeline;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import org.geysermc.pack.converter.util.LogListener;
import org.jetbrains.annotations.Nullable;
import team.unnamed.creative.ResourcePack;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
@SuppressWarnings("ClassCanBeRecord") // We don't want to expose the fields here
public final class ConverterPipeline<JavaAsset, BedrockAsset>
implements AssetExtractor<JavaAsset>, AssetConverter<JavaAsset, BedrockAsset>, AssetCombiner<BedrockAsset> {
private final AssetExtractor<JavaAsset> extractor;
private final AssetConverter<JavaAsset, BedrockAsset> converter;
private final AssetCombiner<BedrockAsset> combiner;
private final boolean experimental;
private final Optional<ActionListener<JavaAsset, BedrockAsset>> listener;
public ConverterPipeline(AssetExtractor<JavaAsset> extractor,
AssetConverter<JavaAsset, BedrockAsset> converter,
AssetCombiner<BedrockAsset> combiner,
boolean experimental,
Optional<ActionListener<JavaAsset, BedrockAsset>> listener) {
this.extractor = extractor;
this.converter = converter;
this.combiner = combiner;
this.experimental = experimental;
this.listener = listener;
}
@Override
public Collection<JavaAsset> extract(ResourcePack pack, ExtractionContext context) {
Collection<JavaAsset> extracted = extractor.extract(pack, context);
listener.ifPresent(actionListener -> actionListener.postExtract(pack, extracted, context));
return extracted;
}
@Override
public @Nullable BedrockAsset convert(JavaAsset asset, ConversionContext context) throws Exception {
BedrockAsset converted = converter.convert(asset, context);
return listener.map(actionListener -> actionListener.postConvert(asset, converted, context))
.orElse(converted);
}
@Override
public void include(BedrockResourcePack pack, List<BedrockAsset> assets, CombineContext context) {
combiner.include(pack, assets, context);
listener.ifPresent(actionListener -> actionListener.postInclude(pack, assets, context));
}
public int convert(ResourcePack pack, Optional<ResourcePack> vanillaPack, BedrockResourcePack bedrockPack, String packName, String textureSubDirectory, LogListener logListener) {
ExtractionContext extractionContext = new ExtractionContext(bedrockPack, vanillaPack, logListener);
ConversionContext conversionContext = new ConversionContext(packName, logListener);
CombineContext combineContext = new CombineContext(textureSubDirectory, logListener);
AtomicInteger errors = new AtomicInteger(0);
List<BedrockAsset> converted = extract(pack, extractionContext).stream()
.map(asset -> {
try {
return convert(asset, conversionContext);
} catch (Exception exception) {
errors.incrementAndGet();
logListener.error("Failed to convert asset", exception);
}
return null;
})
.filter(Objects::nonNull)
.toList();
if (!converted.isEmpty()) {
include(bedrockPack, converted, combineContext);
}
return errors.get();
}
public boolean experimental() {
return experimental;
}
public ConverterPipeline<JavaAsset, BedrockAsset> withActionListener(ActionListener<JavaAsset, BedrockAsset> listener) {
return new ConverterPipeline<>(extractor, converter, combiner, experimental, Optional.of(listener));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
* Copyright (c) 2025-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
@@ -24,34 +24,19 @@
*
*/
package org.geysermc.pack.converter.converter;
package org.geysermc.pack.converter.pipeline;
import org.geysermc.pack.converter.PackConversionContext;
import org.geysermc.pack.converter.PackConverter;
import org.geysermc.pack.converter.data.ConversionData;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import org.geysermc.pack.converter.util.LogListener;
import org.jetbrains.annotations.NotNull;
import org.geysermc.pack.converter.util.LogListenerHelper;
import team.unnamed.creative.ResourcePack;
import java.nio.file.Path;
import java.util.Optional;
public interface Converter<T extends ConversionData> {
void convert(@NotNull PackConversionContext<T> context) throws Exception;
T createConversionData(@NotNull ConversionDataCreationContext context);
default boolean isExperimental() {
return false;
}
record ConversionDataCreationContext(
@NotNull PackConverter converter,
@NotNull LogListener logListener,
@NotNull Path inputDirectory,
@NotNull Path outputDirectory,
@NotNull ResourcePack javaResourcePack,
@NotNull ResourcePack vanillaResourcePack
) {
}
/**
* @param bedrockResourcePack should never be used. The bedrock resource pack should not be written to during extraction, rather only during combination of converted assets.
* The bedrock resource pack is only included here for legacy converters still making use of it, which will be rewritten soon.
*/
public record ExtractionContext(@Deprecated(forRemoval = true) BedrockResourcePack bedrockResourcePack,
Optional<ResourcePack> vanillaPack, LogListener logListener) implements LogListenerHelper {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
* Copyright (c) 2019-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
@@ -24,33 +24,41 @@
*
*/
package org.geysermc.pack.converter.converter.base;
package org.geysermc.pack.converter.type.base;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.PackConversionContext;
import org.geysermc.pack.converter.converter.BaseConverter;
import org.geysermc.pack.converter.converter.Converter;
import org.geysermc.pack.converter.data.BaseConversionData;
import org.jetbrains.annotations.NotNull;
import org.geysermc.pack.converter.pipeline.AssetConverter;
import org.geysermc.pack.converter.pipeline.ConversionContext;
import org.geysermc.pack.converter.pipeline.ExtractionContext;
import org.geysermc.pack.converter.util.KeyUtil;
import team.unnamed.creative.ResourcePack;
import team.unnamed.creative.base.Writable;
import team.unnamed.creative.texture.Texture;
@AutoService(Converter.class)
public class PackIconConverter extends BaseConverter {
private static final Key UNKNOWN_PACK = Key.key(Key.MINECRAFT_NAMESPACE, "misc/unknown_pack.png");
import java.util.Optional;
public class PackIconConverter implements AssetConverter<Writable, byte[]> {
public static final PackIconConverter INSTANCE = new PackIconConverter();
private static final Key UNKNOWN_PACK = KeyUtil.key(Key.MINECRAFT_NAMESPACE, "misc/unknown_pack.png");
public static Writable extractIcon(ResourcePack pack, ExtractionContext context) {
Writable packIcon = pack.icon();
if (packIcon == null) {
Texture unknownPackOverride = pack.texture(UNKNOWN_PACK);
if (unknownPackOverride != null) {
packIcon = unknownPackOverride.data();
} else {
packIcon = context.vanillaPack()
.flatMap(vanilla -> Optional.ofNullable(vanilla.texture(UNKNOWN_PACK)))
.map(Texture::data)
.orElse(null);
}
}
return packIcon;
}
@Override
public void convert(@NotNull PackConversionContext<BaseConversionData> context) throws Exception {
Writable packIcon = context.javaResourcePack().icon();
if (packIcon == null) {
if (context.javaResourcePack().texture(UNKNOWN_PACK) != null) {
packIcon = context.javaResourcePack().texture(UNKNOWN_PACK).data();
} else {
packIcon = context.data().vanillaPack().texture(UNKNOWN_PACK).data();
}
}
context.bedrockResourcePack().icon(packIcon.toByteArray());
public byte[] convert(Writable writable, ConversionContext context) throws Exception {
return writable.toByteArray();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
* Copyright (c) 2019-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
@@ -24,36 +24,30 @@
*
*/
package org.geysermc.pack.converter.converter.base;
package org.geysermc.pack.converter.type.base;
import com.google.auto.service.AutoService;
import org.geysermc.pack.bedrock.resource.Manifest;
import org.geysermc.pack.bedrock.resource.manifest.Header;
import org.geysermc.pack.bedrock.resource.manifest.Modules;
import org.geysermc.pack.converter.PackConversionContext;
import org.geysermc.pack.converter.converter.BaseConverter;
import org.geysermc.pack.converter.converter.Converter;
import org.geysermc.pack.converter.data.BaseConversionData;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.ResourcePack;
import org.geysermc.pack.converter.pipeline.AssetConverter;
import org.geysermc.pack.converter.pipeline.ConversionContext;
import team.unnamed.creative.metadata.pack.PackMeta;
import java.util.List;
import java.util.UUID;
@AutoService(Converter.class)
public class PackManifestConverter extends BaseConverter {
public class PackManifestConverter implements AssetConverter<PackMeta, Manifest> {
public static final PackManifestConverter INSTANCE = new PackManifestConverter();
private static final int FORMAT_VERSION = 2;
@Override
public void convert(@NotNull PackConversionContext<BaseConversionData> context) throws Exception {
ResourcePack javaPack = context.javaResourcePack();
public Manifest convert(PackMeta packMeta, ConversionContext context) throws Exception {
Manifest manifest = new Manifest();
manifest.formatVersion(FORMAT_VERSION);
Header header = new Header();
header.description(javaPack.description());
header.name(context.packConverter().packName());
header.description(packMeta.description());
header.name(context.packName());
header.version(new float[] { 1, 0, 0 });
header.minEngineVersion(new float[] { 1, 16, 0 });
header.uuid(UUID.randomUUID().toString());
@@ -61,12 +55,13 @@ public class PackManifestConverter extends BaseConverter {
manifest.header(header);
Modules module = new Modules();
module.description(javaPack.description());
module.description(packMeta.description());
module.type("resources");
module.uuid(UUID.randomUUID().toString());
module.version(new float[] { 1, 0, 0 });
manifest.modules(List.of(module));
context.bedrockResourcePack().manifest(manifest);
return manifest;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
* Copyright (c) 2025-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
@@ -24,8 +24,9 @@
*
*/
package org.geysermc.pack.converter;
package org.geysermc.pack.converter.type.lang;
public class Constants {
public static final String JAVA_PACK_LOCATION = "assets/%s";
import java.util.Map;
public record BedrockLanguage(String language, Map<String, String> strings) {
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2019-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.type.lang;
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 team.unnamed.creative.lang.Language;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class LangConverter implements AssetConverter<Language, BedrockLanguage>, AssetCombiner<BedrockLanguage> {
public static final LangConverter INSTANCE = new LangConverter();
private static final Pattern POSITIONAL_STRING_REPLACEMENT = Pattern.compile("%([0-9]+)\\$s");
@Override
public BedrockLanguage convert(Language language, ConversionContext context) throws Exception {
Map<String, String> strings = language.translations();
for (Map.Entry<String, String> entry : strings.entrySet()) {
String value = entry.getValue();
// Replace %d with %s
value = value.replace("%d", "%s");
// Replace `%x$s` with `%x`
value = POSITIONAL_STRING_REPLACEMENT.matcher(value).replaceAll("%$1");
entry.setValue(value);
}
String languageKey = language.key().value();
// Convert the language key to the Bedrock equivalent
if (languageKey.equals("no_no")) {
languageKey = "nb_no";
}
return new BedrockLanguage(languageKey, strings);
}
@Override
public void include(BedrockResourcePack pack, List<BedrockLanguage> languages, CombineContext context) {
Map<String, Map<String, String>> merged = new HashMap<>();
for (BedrockLanguage language : languages) {
Map<String, String> mergedLanguage = merged.computeIfAbsent(language.language(), name -> new HashMap<>());
for (Map.Entry<String, String> entry : language.strings().entrySet()) {
if (mergedLanguage.containsKey(entry.getKey())) {
context.warn("Conflicting language string " + entry.getKey() + "!");
continue;
}
mergedLanguage.put(entry.getKey(), entry.getValue());
}
}
merged.forEach(pack::addLanguage);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 GeyserMC. http://geysermc.org
* Copyright (c) 2025-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
@@ -24,38 +24,27 @@
*
*/
package org.geysermc.pack.converter.converter.misc;
package org.geysermc.pack.converter.type.misc;
import com.google.auto.service.AutoService;
import com.google.gson.*;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.PackConversionContext;
import org.geysermc.pack.converter.converter.BaseConverter;
import org.geysermc.pack.converter.converter.Converter;
import org.geysermc.pack.converter.data.BaseConversionData;
import org.jetbrains.annotations.NotNull;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.geysermc.pack.converter.pipeline.AssetConverter;
import org.geysermc.pack.converter.pipeline.ConversionContext;
import team.unnamed.creative.base.Writable;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@AutoService(Converter.class)
public class SplashTextConverter extends BaseConverter {
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.create();
public class SplashTextConverter implements AssetConverter<Writable, JsonElement> {
public static final SplashTextConverter INSTANCE = new SplashTextConverter();
@Override
public void convert(@NotNull PackConversionContext<BaseConversionData> context) throws Exception {
Writable javaSplashText = context.javaResourcePack().unknownFile("assets/minecraft/texts/splashes.txt");
if (javaSplashText == null) return;
String[] splashes = javaSplashText.toUTF8String().split("\n");
public JsonElement convert(Writable writable, ConversionContext context) throws Exception {
String[] splashes = writable.toUTF8String().split("\n");
JsonArray splashesArray = new JsonArray();
Arrays.stream(splashes).toList().forEach(splashesArray::add);
JsonObject object = new JsonObject();
object.add("splashes", splashesArray);
context.bedrockResourcePack().addExtraFile(GSON.toJson(object).getBytes(StandardCharsets.UTF_8), "splashes.json");
return object;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
* Copyright (c) 2025-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
@@ -24,19 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter;
package org.geysermc.pack.converter.type.model;
import org.geysermc.pack.converter.PackConversionContext;
import org.geysermc.pack.converter.data.ConversionData;
import org.geysermc.pack.bedrock.resource.models.entity.ModelEntity;
/**
* A listener for actions that occur during pack conversion.
*/
public interface ActionListener<T extends ConversionData> {
public record BedrockModel(ModelType type, String fileName, ModelEntity model) {
default void preConvert(PackConversionContext<T> context) {
}
default void postConvert(PackConversionContext<T> context) {
public enum ModelType {
BLOCK,
ENTITY
}
}

View File

@@ -0,0 +1,278 @@
/*
* Copyright (c) 2019-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.type.model;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import org.geysermc.pack.bedrock.resource.models.entity.ModelEntity;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.Geometry;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.Bones;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.Description;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.Cubes;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.Uv;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.Down;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.East;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.North;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.South;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.Up;
import org.geysermc.pack.bedrock.resource.models.entity.modelentity.geometry.bones.cubes.uv.West;
import org.geysermc.pack.converter.pipeline.AssetCombiner;
import org.geysermc.pack.converter.pipeline.AssetConverter;
import org.geysermc.pack.converter.pipeline.AssetExtractor;
import org.geysermc.pack.converter.pipeline.CombineContext;
import org.geysermc.pack.converter.pipeline.ConversionContext;
import org.geysermc.pack.converter.pipeline.ExtractionContext;
import team.unnamed.creative.ResourcePack;
import team.unnamed.creative.base.CubeFace;
import team.unnamed.creative.model.Element;
import team.unnamed.creative.model.ElementFace;
import team.unnamed.creative.model.ElementRotation;
import team.unnamed.creative.model.Model;
import team.unnamed.creative.texture.TextureUV;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class ModelConverter implements AssetExtractor<Model>, AssetConverter<Model, BedrockModel>, AssetCombiner<BedrockModel> {
public static final ModelConverter INSTANCE = new ModelConverter();
private static final String FORMAT_VERSION = "1.16.0";
private static final String GEOMETRY_FORMAT = "geometry.%s";
private static final float[] ELEMENT_OFFSET = new float[] { 8, 0, 8 };
@Override
public Collection<Model> extract(ResourcePack pack, ExtractionContext context) {
ModelStitcher.Provider modelProvider = context.vanillaPack()
.map(vanilla -> ModelStitcher.vanillaProvider(pack, vanilla))
.orElseGet(() -> ModelStitcher.baseProvider(pack));
// TODO maybe parallel, if model stitching takes a lot of time
return pack.models().stream()
.map(model -> new ModelStitcher(modelProvider, model, context.logListener()).stitch())
.toList();
}
@Override
public BedrockModel convert(Model model, ConversionContext context) throws Exception {
List<Element> elements = model.elements();
if (elements.isEmpty()) {
context.debug("Model " + model.key().key() + " has no elements");
return null;
}
String value = model.key().value();
context.debug("Converting model " + model.key().key() + ":" + value);
// TODO: Convert item models but save differently?
if (value.startsWith("item/")) {
return null;
}
ModelEntity modelEntity = new ModelEntity();
modelEntity.formatVersion(FORMAT_VERSION);
Geometry geometry = new Geometry();
String namespace = model.key().namespace();
String fileName = value.substring(value.lastIndexOf('/') + 1);
String geoName = (namespace.equals(Key.MINECRAFT_NAMESPACE) ? "" : namespace + ".") + fileName;
// TODO: Don't hardcode all this
Description description = new Description();
description.identifier(String.format(GEOMETRY_FORMAT, geoName));
description.textureWidth(16);
description.textureHeight(16);
description.visibleBoundsWidth(2);
description.visibleBoundsHeight(2);
description.visibleBoundsOffset(new float[] { 0.0f, 0.25f, 0.0f });
geometry.description(description);
List<Bones> bones = new ArrayList<>();
// TODO: Should each element be its own bone rather
// than its own cube in the same bone?
int i = 0;
for (Element element : elements) {
float[] from = element.from().toArray();
float[] to = element.to().toArray();
Bones bone = new Bones();
bone.name("bone_" + i++);
bone.pivot(new float[] { ELEMENT_OFFSET[0], ELEMENT_OFFSET[1], -ELEMENT_OFFSET[2] });
Cubes cube = new Cubes();
cube.origin(new float[] { ELEMENT_OFFSET[0] - to[0], from[1], from[2] - ELEMENT_OFFSET[2] });
cube.size(new float[] { to[0] - from[0], to[1] - from[1], to[2] - from[2] });
ElementRotation elementRotation = element.rotation();
if (elementRotation != null) {
float[] origin = elementRotation.origin().toArray();
cube.pivot(new float[] { ELEMENT_OFFSET[0] - origin[0], ELEMENT_OFFSET[1] - origin[1], origin[2] - ELEMENT_OFFSET[2] });
float angle = elementRotation.angle();
float[] rotation = new float[3];
switch (elementRotation.axis()) {
case X -> rotation[0] = -angle;
case Y -> rotation[1] = -angle;
case Z -> rotation[2] = -angle;
}
cube.rotation(rotation);
}
Uv uv = new Uv();
for (Map.Entry<CubeFace, ElementFace> entry : element.faces().entrySet()) {
CubeFace face = entry.getKey();
ElementFace elementFace = entry.getValue();
if (elementFace.uv0() == null) {
continue;
}
// The Java pack lib we use does this weird thing where it
// divides the UV by 16, so we need to multiply it by 16
String texture = elementFace.texture().replace("#", "");
applyUv(uv, face, texture, multiplyUv(elementFace.uv0(), 16f));
}
cube.uv(uv);
bone.cubes(List.of(cube));
bones.add(bone);
}
geometry.bones(bones);
modelEntity.geometry(List.of(geometry));
if (model.key().namespace().contains("entity")) {
return new BedrockModel(BedrockModel.ModelType.ENTITY, fileName + ".json", modelEntity);
} else {
// Bedrock only has a concept of entity or block models
return new BedrockModel(BedrockModel.ModelType.BLOCK, fileName + ".json", modelEntity);
}
}
@Override
public void include(BedrockResourcePack pack, List<BedrockModel> bedrockModels, CombineContext context) {
List<String> entityModels = new ArrayList<>();
List<String> blockModels = new ArrayList<>();
for (BedrockModel model : bedrockModels) {
switch (model.type()) {
case ENTITY -> {
if (entityModels.contains(model.fileName())) {
context.warn("Conflicting entity model " + model.fileName() + "!");
continue;
}
entityModels.add(model.fileName());
pack.addEntityModel(model.model(), model.fileName());
}
case BLOCK -> {
if (blockModels.contains(model.fileName())) {
context.warn("Conflicting entity model " + model.fileName() + "!");
continue;
}
blockModels.add(model.fileName());
pack.addBlockModel(model.model(), model.fileName());
}
}
}
}
private TextureUV multiplyUv(TextureUV textureUV, float mult) {
return TextureUV.uv(textureUV.from().multiply(mult), textureUV.to().multiply(mult));
}
private static void applyUv(Uv uv, CubeFace face, String texture, TextureUV faceUv) {
float[] uvs;
float[] uvSize;
// These values are flipped for some reason
if (face == CubeFace.DOWN || face == CubeFace.UP) {
uvs = new float[] { faceUv.to().x(), faceUv.to().y() };
uvSize = new float[] { faceUv.from().x() - faceUv.to().x(), faceUv.from().y() - faceUv.to().y() };
} else {
uvs = new float[] { faceUv.from().x(), faceUv.from().y() };
uvSize = new float[] { faceUv.to().x() - faceUv.from().x(), faceUv.to().y() - faceUv.from().y() };
}
switch (face) {
case NORTH -> {
North north = new North();
north.uv(uvs);
north.uvSize(uvSize);
north.materialInstance(texture);
uv.north(north);
}
case SOUTH -> {
South south = new South();
south.uv(uvs);
south.uvSize(uvSize);
south.materialInstance(texture);
uv.south(south);
}
case EAST -> {
East east = new East();
east.uv(uvs);
east.uvSize(uvSize);
east.materialInstance(texture);
uv.east(east);
}
case WEST -> {
West west = new West();
west.uv(uvs);
west.uvSize(uvSize);
west.materialInstance(texture);
uv.west(west);
}
case UP -> {
Up up = new Up();
up.uv(uvs);
up.uvSize(uvSize);
up.materialInstance(texture);
uv.up(up);
}
case DOWN -> {
Down down = new Down();
down.uv(uvs);
down.uvSize(uvSize);
down.materialInstance(texture);
uv.down(down);
}
}
}
}

View File

@@ -24,12 +24,11 @@
*
*/
package org.geysermc.pack.converter.converter.model;
package org.geysermc.pack.converter.type.model;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.util.DefaultLogListener;
import org.geysermc.pack.converter.util.LogListener;
import org.geysermc.pack.converter.util.VanillaPackProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import team.unnamed.creative.ResourcePack;
@@ -39,10 +38,7 @@ import team.unnamed.creative.model.ItemTransform;
import team.unnamed.creative.model.Model;
import team.unnamed.creative.model.ModelTexture;
import team.unnamed.creative.model.ModelTextures;
import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -195,7 +191,7 @@ public class ModelStitcher {
return pack::model;
}
public static Provider vanillaProvider(@NotNull ResourcePack pack, @NotNull LogListener log, @NotNull ResourcePack vanillaPack) {
public static Provider vanillaProvider(@NotNull ResourcePack pack, @NotNull ResourcePack vanillaPack) {
return key -> {
Model model = pack.model(key);
if (model == null) {

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2025-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.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.jetbrains.annotations.Nullable;
import team.unnamed.creative.sound.Sound;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class SoundConverter implements AssetConverter<Sound, Sound>, AssetCombiner<Sound> {
public static final SoundConverter INSTANCE = new SoundConverter();
@Override
public @Nullable Sound convert(Sound sound, ConversionContext context) throws Exception {
return sound;
}
@Override
public void include(BedrockResourcePack pack, List<Sound> sounds, CombineContext context) {
List<String> exported = new ArrayList<>();
Path output = pack.directory().resolve(SoundRegistryConverter.BEDROCK_SOUNDS_LOCATION);
for (Sound sound : sounds) {
String path = sound.key().value();
if (exported.contains(path)) {
context.warn("Conflicting sound file " + sound.key() + "!");
continue;
}
Path file = output.resolve(path + ".ogg");
Path directory = file.getParent();
try {
Files.createDirectories(directory);
try (OutputStream outputStream = new FileOutputStream(file.toFile())) {
sound.data().write(outputStream);
}
} catch (IOException exception) {
context.error("Failed to write sound file " + sound.key() + "!", exception);
continue;
}
exported.add(path);
}
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2025-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.type.sound;
import org.geysermc.pack.bedrock.resource.BedrockResourcePack;
import org.geysermc.pack.bedrock.resource.sounds.sounddefinitions.SoundDefinitions;
import org.geysermc.pack.bedrock.resource.sounds.sounddefinitions.Sounds;
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.jetbrains.annotations.Nullable;
import team.unnamed.creative.sound.SoundEntry;
import team.unnamed.creative.sound.SoundEvent;
import team.unnamed.creative.sound.SoundRegistry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SoundRegistryConverter implements AssetConverter<SoundRegistry, Map<String, SoundDefinitions>>, AssetCombiner<Map<String, SoundDefinitions>> {
public static final SoundRegistryConverter INSTANCE = new SoundRegistryConverter();
static final String BEDROCK_SOUNDS_LOCATION = "sounds";
@Override
public @Nullable Map<String, SoundDefinitions> convert(SoundRegistry soundRegistry, ConversionContext context) throws Exception {
Map<String, SoundDefinitions> definitions = new HashMap<>();
for (SoundEvent value : soundRegistry.sounds()) {
String key = value.key().asString();
SoundDefinitions definition = new SoundDefinitions();
definition.useLegacyMaxDistance(true); // TODO: Needed?
definition.maxDistance(64); // ???
for (SoundEntry sound : value.sounds()) {
Sounds bedrockSound = new Sounds();
bedrockSound.name(BEDROCK_SOUNDS_LOCATION + "/" + sound.key().value());
bedrockSound.stream(sound.stream());
bedrockSound.loadOnLowMemory(true);
bedrockSound.volume(sound.volume());
bedrockSound.pitch(sound.pitch());
bedrockSound.weight(sound.weight());
definition.sounds().add(bedrockSound);
}
definitions.put(key, definition);
}
return definitions;
}
@Override
public void include(BedrockResourcePack pack, List<Map<String, SoundDefinitions>> maps, CombineContext context) {
List<String> soundEvents = new ArrayList<>();
for (Map<String, SoundDefinitions> definitions : maps) {
for (Map.Entry<String, SoundDefinitions> entry : definitions.entrySet()) {
if (soundEvents.contains(entry.getKey())) {
context.warn("Conflicting sound event " + entry.getKey() + "!");
}
pack.addSoundDefinition(entry.getKey(), entry.getValue());
soundEvents.add(entry.getKey());
}
}
}
}

View File

@@ -24,7 +24,7 @@
*
*/
package org.geysermc.pack.converter.converter.texture;
package org.geysermc.pack.converter.type.texture;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -189,7 +189,7 @@ public final class PngToTgaMappings {
};
@Nullable
public static TgaMapping mapping(@NotNull String key) {
static TgaMapping mapping(@NotNull String key) {
return MAPPINGS.get(key);
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright (c) 2019-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.type.texture;
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.AssetExtractor;
import org.geysermc.pack.converter.pipeline.CombineContext;
import org.geysermc.pack.converter.pipeline.ConversionContext;
import org.geysermc.pack.converter.pipeline.ExtractionContext;
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.jetbrains.annotations.Nullable;
import team.unnamed.creative.ResourcePack;
import team.unnamed.creative.texture.Texture;
import javax.imageio.ImageIO;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
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.Comparator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.stream.StreamSupport;
public class TextureConverter implements AssetExtractor<Texture>, AssetConverter<Texture, TransformedTexture>, AssetCombiner<TransformedTexture> {
public static final TextureConverter INSTANCE = new TextureConverter();
public static final String BEDROCK_TEXTURES_LOCATION = "textures";
private final List<TextureTransformer> transformers = StreamSupport.stream(ServiceLoader.load(TextureTransformer.class).spliterator(), false)
.sorted(Comparator.comparingInt(TextureTransformer::order))
.toList();
public static final Map<String, String> DIRECTORY_LOCATIONS = Map.of(
"block", "blocks",
"item", "items",
"gui", "ui"
);
@Override
public Collection<Texture> extract(ResourcePack pack, ExtractionContext context) {
// 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();
List<Texture> textures = new ArrayList<>(pack.textures());
context.info("Transforming textures...");
TransformContext transformContext = new TransformContext(
mappings,
textures,
context.bedrockResourcePack(),
pack,
context.vanillaPack(),
context.logListener()
);
for (TextureTransformer transformer : this.transformers) {
try {
transformer.transform(transformContext);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
context.info("Transformed textures!");
return textures;
}
@Override
public @Nullable TransformedTexture convert(Texture texture, ConversionContext context) throws Exception {
TextureMappings mappings = TextureMappings.textureMappings();
TransformedTexture transformed = new TransformedTexture(texture);
String input = texture.key().value();
String relativePath = input.replaceAll("\\.png$", "");
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);
}
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);
}
return transformed;
}
@Override
public void include(BedrockResourcePack pack, List<TransformedTexture> transformedTextures, CombineContext context) {
Path texturePath = pack.directory().resolve(BEDROCK_TEXTURES_LOCATION);
List<String> exportedPaths = new ArrayList<>();
for (TransformedTexture textureToExport : transformedTextures) {
String bedrockDirectory = "%s/%s";
if (context.textureSubDirectory() != null) {
bedrockDirectory = "%s/" + context.textureSubDirectory() + "/%s";
}
List<Path> outputs = new ArrayList<>();
for (String outputPath : textureToExport.output()) {
if (exportedPaths.contains(outputPath)) {
context.warn("Conflicting texture " + outputPath + "!");
continue;
}
exportedPaths.add(outputPath);
String root = outputPath.substring(0, outputPath.indexOf('/'));
String value = outputPath.substring(outputPath.indexOf('/') + 1);
outputs.add(texturePath.resolve((
bedrockDirectory.formatted(root, value)
).replace('/', File.separatorChar)));
}
try {
byte[] bytes = textureToExport.texture().data().toByteArray();
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
for (Path output : outputs) {
if (output.getParent() != null && Files.notExists(output.getParent())) {
Files.createDirectories(output.getParent());
}
BufferedImage bedrockImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bedrockImage.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(image, 0, 0, null);
g.dispose();
String pngKey = pack.directory().relativize(output).toString().replace(File.separatorChar, '/');
PngToTgaMappings.TgaMapping mapping = PngToTgaMappings.mapping(pngKey);
if (mapping != null) {
Path tgaPath = pack.directory().resolve(mapping.value());
if (Files.notExists(tgaPath.getParent())) {
Files.createDirectories(tgaPath.getParent());
}
ImageUtil.writeTGA(tgaPath, bedrockImage);
if (!mapping.keep()) {
Files.deleteIfExists(output);
continue;
}
}
try (OutputStream stream = Files.newOutputStream(output)) {
ImageIO.write(bedrockImage, "png", stream);
}
}
} catch (IOException exception) {
context.error("Failed to write texture " + textureToExport.texture().key() + "!", exception);
}
}
}
}

View File

@@ -24,7 +24,7 @@
*
*/
package org.geysermc.pack.converter.converter.texture;
package org.geysermc.pack.converter.type.texture;
import com.google.gson.Gson;
import lombok.ToString;
@@ -34,7 +34,6 @@ import org.jetbrains.annotations.Nullable;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedHashMap;
import java.util.Map;
@ToString
public class TextureMappings extends LinkedHashMap<String, Object> {

View File

@@ -24,7 +24,7 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer;
package org.geysermc.pack.converter.type.texture.transformer;
import org.geysermc.pack.converter.util.ImageUtil;
import org.jetbrains.annotations.NotNull;

View File

@@ -24,14 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer;
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.PackConversionContext;
import org.geysermc.pack.converter.converter.texture.TextureMappings;
import org.geysermc.pack.converter.data.TextureConversionData;
import org.geysermc.pack.converter.type.texture.TextureMappings;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.LogListener;
import org.geysermc.pack.converter.util.LogListenerHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import team.unnamed.creative.ResourcePack;
@@ -43,27 +43,33 @@ import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class TransformContext {
private final PackConversionContext<TextureConversionData> conversionContext;
public class TransformContext implements LogListenerHelper {
private final TextureMappings 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)
private final BedrockResourcePack bedrockPack;
private final ResourcePack javaPack;
private final Optional<ResourcePack> vanillaPack;
private final LogListener logListener;
private final Map<Key, Texture> byKey = new HashMap<>();
public TransformContext(
PackConversionContext<TextureConversionData> conversionContext,
TextureMappings mappings,
Collection<Texture> textures,
BedrockResourcePack bedrockPack,
ResourcePack javaPack
ResourcePack javaPack,
Optional<ResourcePack> vanillaPack,
LogListener logListener
) {
this.conversionContext = conversionContext;
this.mappings = mappings;
this.textures = textures;
this.bedrockPack = bedrockPack;
this.javaPack = javaPack;
this.vanillaPack = vanillaPack;
this.logListener = logListener;
for (Texture texture : textures) {
this.byKey.put(texture.key(), texture);
@@ -74,16 +80,17 @@ public class TransformContext {
return this.mappings;
}
@Deprecated(forRemoval = true)
public BedrockResourcePack bedrockResourcePack() {
return this.bedrockPack;
return bedrockPack;
}
public ResourcePack javaResourcePack() {
return this.javaPack;
}
public ResourcePack vanillaPack() {
return this.conversionContext.data().vanillaPack();
public Optional<ResourcePack> vanillaPack() {
return vanillaPack;
}
/**
@@ -126,8 +133,7 @@ public class TransformContext {
public Texture pollOrPeekVanilla(@NotNull Key key) {
Texture remove = this.byKey.remove(key);
if (remove == null) {
// This *shouldn't* be null, but if a bad key is inputted, it is possible this value is null
return this.conversionContext.data().vanillaPack().texture(key);
return vanillaPack.map(pack -> pack.texture(key)).orElse(null);
}
this.textures.remove(remove);
@@ -146,8 +152,7 @@ public class TransformContext {
public Texture peekOrVanilla(@NotNull Key key) {
Texture texture = this.byKey.get(key);
if (texture == null) {
// This *shouldn't* be null, but if a bad key is inputted, it is possible this value is null
return this.conversionContext.data().vanillaPack().texture(key);
return vanillaPack.map(pack -> pack.texture(key)).orElse(null);
}
return texture;
@@ -186,23 +191,7 @@ public class TransformContext {
this.byKey.put(texture.key(), texture);
}
public void debug(@NotNull String message) {
this.conversionContext.debug(message);
}
public void info(@NotNull String message) {
this.conversionContext.info(message);
}
public void warn(@NotNull String message) {
this.conversionContext.warn(message);
}
public void error(@NotNull String message) {
this.conversionContext.error(message);
}
public void error(@NotNull String message, @NotNull Throwable throwable) {
this.conversionContext.error(message, throwable);
public LogListener logListener() {
return logListener;
}
}

View File

@@ -24,20 +24,20 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer;
package org.geysermc.pack.converter.type.texture.transformer;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class TransformedTexture {
private final Texture texture;
private Path output;
private final List<String> outputs = new ArrayList<>();
public TransformedTexture(@NotNull Texture texture, @NotNull Path output) {
public TransformedTexture(@NotNull Texture texture) {
this.texture = texture;
this.output = output;
}
@NotNull
@@ -46,11 +46,11 @@ public class TransformedTexture {
}
@NotNull
public Path output() {
return output;
public List<String> output() {
return outputs;
}
public void output(@NotNull Path output) {
this.output = output;
public void output(@NotNull String output) {
outputs.add(output);
}
}

View File

@@ -24,12 +24,13 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type;
package org.geysermc.pack.converter.type.texture.transformer.type;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -54,7 +55,7 @@ public class AtlasTransformer implements TextureTransformer {
BufferedImage atlasImage = null;
for (int i = 0; i <= atlasCount; i++) {
Texture texture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, String.format(javaName, String.format("%1$2s", i).replace(" ", "0"))));
Texture texture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, String.format(javaName, String.format("%1$2s", i).replace(" ", "0"))));
if (texture == null) {
continue;
}
@@ -69,7 +70,7 @@ public class AtlasTransformer implements TextureTransformer {
}
if (atlasImage != null) {
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, bedrockName), atlasImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, bedrockName), atlasImage, "png");
context.debug(String.format("Created atlas %s", bedrockName));
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type;
package org.geysermc.pack.converter.type.texture.transformer.type;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.geysermc.pack.converter.util.UnsafeKey;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -272,7 +273,7 @@ public class ColorizeTransformer implements TextureTransformer {
Color color = overlay.color();
boolean deleteOverlay = overlay.deleteOverlay();
Key key = Key.key(Key.MINECRAFT_NAMESPACE, overlayPath);
Key key = KeyUtil.key(Key.MINECRAFT_NAMESPACE, overlayPath);
Texture texture = deleteOverlay ? context.poll(key) : context.peek(key);
if (texture == null) {
context.debug("Missing overlay texture: " + overlayPath);

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type;
package org.geysermc.pack.converter.type.texture.transformer.type;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -102,8 +103,8 @@ public class OverlayTransformer implements TextureTransformer {
boolean noReplace = overlay.noReplace();
boolean keep = overlay.keep();
Key javaKey = Key.key(Key.MINECRAFT_NAMESPACE, javaName);
Key overlayKey = Key.key(Key.MINECRAFT_NAMESPACE, overlayName);
Key javaKey = KeyUtil.key(Key.MINECRAFT_NAMESPACE, javaName);
Key overlayKey = KeyUtil.key(Key.MINECRAFT_NAMESPACE, overlayName);
// We don't have either textures, skip this
if (!context.isTexturePresent(javaKey) && !context.isTexturePresent(overlayKey)) continue;
@@ -149,7 +150,7 @@ public class OverlayTransformer implements TextureTransformer {
}
}
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, bedrockName), image, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, bedrockName), image, "png");
}
}

View File

@@ -24,17 +24,18 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type;
package org.geysermc.pack.converter.type.texture.transformer.type;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.*;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
@@ -46,8 +47,8 @@ public class WeatherTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
Texture snowTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, SNOW_INPUT));
Texture rainTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, RAIN_INPUT));
Texture snowTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, SNOW_INPUT));
Texture rainTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, RAIN_INPUT));
if (snowTexture == null || rainTexture == null) {
return;
}
@@ -69,6 +70,6 @@ public class WeatherTransformer implements TextureTransformer {
// Rain
graphics.drawImage(ImageUtil.cover(ImageUtil.crop(rainImage, rainImage.getWidth(), (int) (5 * factor)), weatherImage.getWidth(), (int) (5 * factor)), 0, (int) (5 * factor), null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, WEATHER_OUTPUT), weatherImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, WEATHER_OUTPUT), weatherImage, "png");
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.block;
package org.geysermc.pack.converter.type.texture.transformer.type.block;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -65,7 +66,7 @@ public class BedTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
for (String bedColor : BED_COLORS) {
Texture texture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, BED_PATH + "/" + bedColor + ".png"));
Texture texture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, BED_PATH + "/" + bedColor + ".png"));
if (texture == null) {
continue;
}
@@ -110,7 +111,7 @@ public class BedTransformer implements TextureTransformer {
graphics.drawImage(ImageUtil.rotate(ImageUtil.crop(bedImage, ((fromX + 9) * factor), ((fromY + 3) * factor), (3 * factor), (3 * factor)), 180), ((toX + 3) * factor), (toY * factor), null);
}
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, BED_PATH + "/" + bedColor + ".png"), newBedImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, BED_PATH + "/" + bedColor + ".png"), newBedImage, "png");
}
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.block;
package org.geysermc.pack.converter.type.texture.transformer.type.block;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -50,8 +51,8 @@ public class ChestDoubleTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
for (ChestData chest : CHEST_DATA) {
Texture leftTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, chest.javaNameLeft()));
Texture rightTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, chest.javaNameRight()));
Texture leftTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, chest.javaNameLeft()));
Texture rightTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, chest.javaNameRight()));
if (leftTexture == null || rightTexture == null) {
continue;
}
@@ -101,7 +102,7 @@ public class ChestDoubleTransformer implements TextureTransformer {
graphics.drawImage(ImageUtil.crop(leftImage, 0, 0, (6 * factor), (6 * factor)), 0, 0, null);
graphics.drawImage(ImageUtil.crop(rightImage, 0, 0, (6 * factor), (6 * factor)), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, chest.bedrockName()), newImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, chest.bedrockName()), newImage, "png");
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.block;
package org.geysermc.pack.converter.type.texture.transformer.type.block;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -50,7 +51,7 @@ public class ChestFrontTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
for (ChestData chest : CHESTS) {
Texture texture = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, chest.javaName()));
Texture texture = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, chest.javaName()));
if (texture == null) {
continue;
}
@@ -68,7 +69,7 @@ public class ChestFrontTransformer implements TextureTransformer {
graphics.drawImage(ImageUtil.crop(fromImage, (14 * factor), (34 * factor), (14 * factor), (9 * factor)), 0, (5 * factor), null);
graphics.drawImage(ImageUtil.crop(fromImage , factor, factor, (2 * factor), (4 * factor)), (6 * factor), (3 * factor), null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, chest.bedrockName()), newImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, chest.bedrockName()), newImage, "png");
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.block;
package org.geysermc.pack.converter.type.texture.transformer.type.block;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -53,7 +54,7 @@ public class ChestNormalTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
for (String variant : VARIANTS) {
Texture texture = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, CHEST_PATH + "/" + variant + ".png"));
Texture texture = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, CHEST_PATH + "/" + variant + ".png"));
if (texture == null) {
continue;
}
@@ -86,7 +87,7 @@ public class ChestNormalTransformer implements TextureTransformer {
graphics.drawImage(ImageUtil.crop(chestImage, 0, 0, (6 * factor), (6 * factor)), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, CHEST_PATH + "/" + variant + ".png"), newChestImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, CHEST_PATH + "/" + variant + ".png"), newChestImage, "png");
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.block;
package org.geysermc.pack.converter.type.texture.transformer.type.block;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -49,7 +50,7 @@ public class ChestSideTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
for (ChestData chest : CHESTS) {
Texture texture = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, chest.javaName()));
Texture texture = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, chest.javaName()));
if (texture == null) {
continue;
}
@@ -66,7 +67,7 @@ public class ChestSideTransformer implements TextureTransformer {
graphics.drawImage(ImageUtil.crop(fromImage, (28 * factor), (14 * factor), (14 * factor), (5 * factor)), 0, 0, null);
graphics.drawImage(ImageUtil.crop(fromImage, (28 * factor), (34 * factor), (14 * factor), (9 * factor)), 0, (5 * factor), null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, chest.bedrockName()), newImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, chest.bedrockName()), newImage, "png");
}
}

View File

@@ -24,17 +24,18 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.block;
package org.geysermc.pack.converter.type.texture.transformer.type.block;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.*;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
@@ -42,7 +43,7 @@ import java.io.IOException;
public class ConduitTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
Texture baseTexture = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, "entity/conduit/base.png"));
Texture baseTexture = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "entity/conduit/base.png"));
if (baseTexture != null) {
BufferedImage baseImage = this.readImage(baseTexture);
@@ -53,10 +54,10 @@ public class ConduitTransformer implements TextureTransformer {
Graphics g = bedrockBaseImage.getGraphics();
g.drawImage(ImageUtil.crop(baseImage, 0, 0, (int) (24 * scale), (int) (12 * scale)), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "blocks/conduit_base.png"), bedrockBaseImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "blocks/conduit_base.png"), bedrockBaseImage, "png");
}
Texture eyeTexture = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, "entity/conduit/closed_eye.png"));
Texture eyeTexture = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "entity/conduit/closed_eye.png"));
if (eyeTexture != null) {
BufferedImage image = this.readImage(eyeTexture);
@@ -67,10 +68,10 @@ public class ConduitTransformer implements TextureTransformer {
Graphics g = bedrockImage.getGraphics();
g.drawImage(ImageUtil.crop(image, 0, 0, (int) (8 * scale), (int) (8 * scale)), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "blocks/conduit_closed.png"), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "blocks/conduit_closed.png"), bedrockImage, "png");
}
Texture eyeOpenTexture = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, "entity/conduit/open_eye.png"));
Texture eyeOpenTexture = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "entity/conduit/open_eye.png"));
if (eyeOpenTexture != null) {
BufferedImage image = this.readImage(eyeOpenTexture);
@@ -81,7 +82,7 @@ public class ConduitTransformer implements TextureTransformer {
Graphics g = bedrockImage.getGraphics();
g.drawImage(ImageUtil.crop(image, 0, 0, (int) (8 * scale), (int) (8 * scale)), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "blocks/conduit_open.png"), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "blocks/conduit_open.png"), bedrockImage, "png");
}
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.block;
package org.geysermc.pack.converter.type.texture.transformer.type.block;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -55,7 +56,7 @@ public class LiquidTransformer implements TextureTransformer {
int minWidth = liquid.minWidth();
boolean grayscale = liquid.grayscale();
Texture texture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, javaName));
Texture texture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, javaName));
if (texture == null) {
continue;
}
@@ -70,7 +71,7 @@ public class LiquidTransformer implements TextureTransformer {
liquidImage = ImageUtil.ensureMinWidth(liquidImage, minWidth);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, bedrockName), liquidImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, bedrockName), liquidImage, "png");
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.block;
package org.geysermc.pack.converter.type.texture.transformer.type.block;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -47,22 +48,22 @@ public class TallSeagrassTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
Texture javaTop = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, JAVA_TOP));
Texture javaTop = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, JAVA_TOP));
if (javaTop != null) {
BufferedImage javaImage = this.readImage(javaTop);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, BEDROCK_TOP.formatted("a")), javaImage, "png");
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, BEDROCK_TOP.formatted("b")), ImageUtil.flip(javaImage, true, false), "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, BEDROCK_TOP.formatted("a")), javaImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, BEDROCK_TOP.formatted("b")), ImageUtil.flip(javaImage, true, false), "png");
}
Texture javaBottom = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, JAVA_BOTTOM));
Texture javaBottom = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, JAVA_BOTTOM));
if (javaBottom != null) {
BufferedImage javaImage = this.readImage(javaBottom);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, BEDROCK_BOTTOM.formatted("a")), javaImage, "png");
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, BEDROCK_BOTTOM.formatted("b")), ImageUtil.flip(javaImage, true, false), "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, BEDROCK_BOTTOM.formatted("a")), javaImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, BEDROCK_BOTTOM.formatted("b")), ImageUtil.flip(javaImage, true, false), "png");
}
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.entity;
package org.geysermc.pack.converter.type.texture.transformer.type.entity;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -46,7 +47,7 @@ public class ArrowTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
Texture texture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, INPUT));
Texture texture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, INPUT));
if (texture == null) {
return;
}
@@ -76,6 +77,6 @@ public class ArrowTransformer implements TextureTransformer {
graphics.drawImage(fromImage, 0, 10 * factor, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, OUTPUT), newArrowImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, OUTPUT), newArrowImage, "png");
}
}

View File

@@ -24,17 +24,18 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.entity;
package org.geysermc.pack.converter.type.texture.transformer.type.entity;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.*;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
@@ -44,7 +45,7 @@ public class DolphinTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
Texture javaTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, LOCATION));
Texture javaTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, LOCATION));
if (javaTexture == null) return;
BufferedImage javaImage = this.readImage(javaTexture);
@@ -590,6 +591,6 @@ public class DolphinTransformer implements TextureTransformer {
null
);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, LOCATION), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, LOCATION), bedrockImage, "png");
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.entity;
package org.geysermc.pack.converter.type.texture.transformer.type.entity;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -47,8 +48,8 @@ public class DrownedTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
Texture drownedTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, String.format(TEXTURE_PATH, DROWNED_TEXTURE)));
Texture outerLayerTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, String.format(TEXTURE_PATH, DROWNED_OUTER_LAYER_TEXTURE)));
Texture drownedTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, String.format(TEXTURE_PATH, DROWNED_TEXTURE)));
Texture outerLayerTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, String.format(TEXTURE_PATH, DROWNED_OUTER_LAYER_TEXTURE)));
if (drownedTexture == null || outerLayerTexture == null) {
return;
@@ -71,6 +72,6 @@ public class DrownedTransformer implements TextureTransformer {
graphics.drawImage(ImageUtil.crop(overlayImage, (int) (16 * factor), (int) (48 * factor), (int) (16 * factor), (int) (16 * factor)), 0, (int) (48 * factor), null);
graphics.drawImage(ImageUtil.crop(overlayImage, (int) (32 * factor), (int) (48 * factor), (int) (16 * factor), (int) (16 * factor)), (int) (48 * factor), (int) (48 * factor), null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, String.format(TEXTURE_PATH, DROWNED_TEXTURE)), newImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, String.format(TEXTURE_PATH, DROWNED_TEXTURE)), newImage, "png");
}
}

View File

@@ -24,22 +24,21 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.entity;
package org.geysermc.pack.converter.type.texture.transformer.type.entity;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.*;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@AutoService(TextureTransformer.class)
@@ -52,11 +51,11 @@ public class PaintingTransformer implements TextureTransformer {
public void transform(@NotNull TransformContext context) throws IOException {
// Let's check if we actually need to convert anything.
boolean isAnyPresent = PAINTING_DATA.keySet().stream().anyMatch(key -> {
return context.isTexturePresent(Key.key(Key.MINECRAFT_NAMESPACE, JAVA_LOCATION.formatted(key)));
return context.isTexturePresent(KeyUtil.key(Key.MINECRAFT_NAMESPACE, JAVA_LOCATION.formatted(key)));
});
// We don't map this like the others because bedrock is difficult, so it isn't in PAINTING_DATA.
isAnyPresent = isAnyPresent || context.isTexturePresent(Key.key(Key.MINECRAFT_NAMESPACE, JAVA_LOCATION.formatted("back")));
isAnyPresent = isAnyPresent || context.isTexturePresent(KeyUtil.key(Key.MINECRAFT_NAMESPACE, JAVA_LOCATION.formatted("back")));
if (!isAnyPresent) return; // we have nothing to convert, skip this
@@ -65,7 +64,7 @@ public class PaintingTransformer implements TextureTransformer {
Map<String, Float> scales = new HashMap<>();
PAINTING_DATA.forEach((key, value) -> {
Texture javaTexture = context.pollOrPeekVanilla(Key.key(Key.MINECRAFT_NAMESPACE, JAVA_LOCATION.formatted(key)));
Texture javaTexture = context.pollOrPeekVanilla(KeyUtil.key(Key.MINECRAFT_NAMESPACE, JAVA_LOCATION.formatted(key)));
if (javaTexture == null) {
scales.put(key, 1f);
return;
@@ -83,7 +82,7 @@ public class PaintingTransformer implements TextureTransformer {
});
// Again, bedrock being difficult with back.
Texture backTexture = context.pollOrPeekVanilla(Key.key(Key.MINECRAFT_NAMESPACE, JAVA_LOCATION.formatted("back")));
Texture backTexture = context.pollOrPeekVanilla(KeyUtil.key(Key.MINECRAFT_NAMESPACE, JAVA_LOCATION.formatted("back")));
if (backTexture != null) {
BufferedImage image = this.readImage(backTexture);
@@ -128,7 +127,7 @@ public class PaintingTransformer implements TextureTransformer {
}
}
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "painting/kz.png"), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "painting/kz.png"), bedrockImage, "png");
}
// BedrockX and BedrockY determine where the image is in kz.png (Bedrock painting atlas)

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.entity;
package org.geysermc.pack.converter.type.texture.transformer.type.entity;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -47,9 +48,9 @@ public class SheepTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
Key sheepKey = Key.key(Key.MINECRAFT_NAMESPACE, SHEEP);
Key woolKey = Key.key(Key.MINECRAFT_NAMESPACE, SHEEP_WOOL);
Key undercoatKey = Key.key(Key.MINECRAFT_NAMESPACE, SHEEP_UNDERCOAT);
Key sheepKey = KeyUtil.key(Key.MINECRAFT_NAMESPACE, SHEEP);
Key woolKey = KeyUtil.key(Key.MINECRAFT_NAMESPACE, SHEEP_WOOL);
Key undercoatKey = KeyUtil.key(Key.MINECRAFT_NAMESPACE, SHEEP_UNDERCOAT);
if (
!context.isTexturePresent(sheepKey) &&
@@ -93,6 +94,6 @@ public class SheepTransformer implements TextureTransformer {
g.drawImage(sheepWoolImage, 0, sheepImage.getHeight(), null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, SHEEP), newImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, SHEEP), newImage, "png");
}
}

View File

@@ -24,12 +24,13 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.entity;
package org.geysermc.pack.converter.type.texture.transformer.type.entity;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -69,7 +70,7 @@ public class VillagerTransformer implements TextureTransformer {
for (String entity : ENTITIES) {
for (String profession : VILLAGER_PROFESSIONS) {
String texturePath = String.format(TEXTURE_PATH, entity, profession);
Texture texture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, texturePath));
Texture texture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, texturePath));
if (texture == null) {
continue;
}
@@ -91,7 +92,7 @@ public class VillagerTransformer implements TextureTransformer {
}
}
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, texturePath), newImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, texturePath), newImage, "png");
}
}
}

View File

@@ -24,17 +24,18 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.entity;
package org.geysermc.pack.converter.type.texture.transformer.type.entity;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.*;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.List;
@@ -53,7 +54,7 @@ public class ZombieTransformer implements TextureTransformer {
public void transform(@NotNull TransformContext context) throws IOException {
for (ZombieData zombieData : ZOMBIES) {
String zombie = zombieData.name();
Key path = Key.key(Key.MINECRAFT_NAMESPACE, TEXTURE_PATH.formatted(zombie));
Key path = KeyUtil.key(Key.MINECRAFT_NAMESPACE, TEXTURE_PATH.formatted(zombie));
Texture texture = context.poll(path);
if (texture == null) continue;
@@ -70,7 +71,7 @@ public class ZombieTransformer implements TextureTransformer {
context.offer(path, bedrockImage, "png");
if (zombieData.mobHead()) {
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, MOB_HEAD_PATH.formatted(zombie)), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, MOB_HEAD_PATH.formatted(zombie)), bedrockImage, "png");
}
}
}

View File

@@ -24,16 +24,17 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.item;
package org.geysermc.pack.converter.type.texture.transformer.type.item;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.*;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.List;
@@ -67,8 +68,8 @@ public class BundleTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
for (String color : COLORS) {
Texture backTexture = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, FRONT.formatted(color)));
Texture frontTexture = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, BACK.formatted(color)));
Texture backTexture = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, FRONT.formatted(color)));
Texture frontTexture = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, BACK.formatted(color)));
if (backTexture == null || frontTexture == null) continue; // TODO: If one is missing, pull from vanilla pack
BufferedImage backImage = this.readImage(backTexture);
@@ -81,7 +82,7 @@ public class BundleTransformer implements TextureTransformer {
g.drawImage(backImage, 0, 0, null);
g.drawImage(frontImage, 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, BEDROCK_OUTPUT.formatted(color)), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, BEDROCK_OUTPUT.formatted(color)), bedrockImage, "png");
}
}
}

View File

@@ -24,13 +24,14 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.particle;
package org.geysermc.pack.converter.type.texture.transformer.type.particle;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.geysermc.pack.converter.util.Spritesheet;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -75,10 +76,10 @@ public class BaseParticleTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
// Create a grayscale bubble image
Texture bubbleTexture = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, PATH + "/bubble.png"));
Texture bubbleTexture = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, PATH + "/bubble.png"));
if (bubbleTexture != null) {
BufferedImage bubble = this.readImage(bubbleTexture);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, PATH + "/bubble_gray.png"), ImageUtil.grayscale(bubble), "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, PATH + "/bubble_gray.png"), ImageUtil.grayscale(bubble), "png");
}
this.createSpritesheet(context);
@@ -169,7 +170,7 @@ public class BaseParticleTransformer implements TextureTransformer {
context.debug(String.format("Creating particle spritesheet %s", OUTPUT));
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, PATH + "/" + OUTPUT), vanillaSprite, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, PATH + "/" + OUTPUT), vanillaSprite, "png");
}
interface TextureData {
@@ -181,7 +182,7 @@ public class BaseParticleTransformer implements TextureTransformer {
@NotNull
public Key textureKey(int atlas) {
return Key.key(Key.MINECRAFT_NAMESPACE, PATH + "/" + javaName + "_" + atlas + ".png");
return KeyUtil.key(Key.MINECRAFT_NAMESPACE, PATH + "/" + javaName + "_" + atlas + ".png");
}
@Override
@@ -207,7 +208,7 @@ public class BaseParticleTransformer implements TextureTransformer {
continue;
}
Texture texture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, textureName + ".png"));
Texture texture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, textureName + ".png"));
if (texture == null) {
continue;
}

View File

@@ -24,10 +24,10 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.particle;
package org.geysermc.pack.converter.type.texture.transformer.type.particle;
import com.google.auto.service.AutoService;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
@AutoService(TextureTransformer.class)
public class CampfireParticleTransformer extends SpritesheetParticleTransformer {

View File

@@ -24,10 +24,10 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.particle;
package org.geysermc.pack.converter.type.texture.transformer.type.particle;
import com.google.auto.service.AutoService;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
@AutoService(TextureTransformer.class)
public class SculkChargeParticleTransformer extends SpritesheetParticleTransformer {

View File

@@ -24,10 +24,10 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.particle;
package org.geysermc.pack.converter.type.texture.transformer.type.particle;
import com.google.auto.service.AutoService;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
@AutoService(TextureTransformer.class)
public class SculkChargePopParticleTransformer extends SpritesheetParticleTransformer {

View File

@@ -24,10 +24,10 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.particle;
package org.geysermc.pack.converter.type.texture.transformer.type.particle;
import com.google.auto.service.AutoService;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
@AutoService(TextureTransformer.class)
public class SculkSoulParticleTransformer extends SpritesheetParticleTransformer {

View File

@@ -24,10 +24,10 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.particle;
package org.geysermc.pack.converter.type.texture.transformer.type.particle;
import com.google.auto.service.AutoService;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
@AutoService(TextureTransformer.class)
public class SonicExplosionParticleTransformer extends SpritesheetParticleTransformer {

View File

@@ -24,10 +24,10 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.particle;
package org.geysermc.pack.converter.type.texture.transformer.type.particle;
import com.google.auto.service.AutoService;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
@AutoService(TextureTransformer.class)
public class SoulParticleTransformer extends SpritesheetParticleTransformer {

View File

@@ -24,12 +24,13 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.particle;
package org.geysermc.pack.converter.type.texture.transformer.type.particle;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.geysermc.pack.converter.util.Spritesheet;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
@@ -59,7 +60,7 @@ public class SpritesheetParticleTransformer implements TextureTransformer {
BitSet occupiedSectors = new BitSet(this.atlasCount);
Spritesheet spritesheet = new Spritesheet();
for (int i = 0; i < this.atlasCount; i++) {
Texture texture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, String.format(this.javaPath, i)));
Texture texture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, String.format(this.javaPath, i)));
if (texture == null) {
continue;
}
@@ -95,6 +96,6 @@ public class SpritesheetParticleTransformer implements TextureTransformer {
context.debug(String.format("Creating particle spritesheet %s", this.bedrockPath));
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, this.bedrockPath), vanillaSprite, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, this.bedrockPath), vanillaSprite, "png");
}
}

View File

@@ -24,26 +24,35 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.ui;
package org.geysermc.pack.converter.type.texture.transformer.type.ui;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.font.*;
import team.unnamed.creative.font.BitMapFontProvider;
import team.unnamed.creative.font.Font;
import team.unnamed.creative.font.FontProvider;
import team.unnamed.creative.font.ReferenceFontProvider;
import team.unnamed.creative.font.SpaceFontProvider;
import team.unnamed.creative.font.UnihexFontProvider;
import team.unnamed.creative.texture.Texture;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HexFormat;
import java.util.List;
import java.util.Map;
@AutoService(TextureTransformer.class)
public class FontTransformer implements TextureTransformer {
@@ -69,7 +78,7 @@ public class FontTransformer implements TextureTransformer {
// Currently, only the default font is converted, custom fonts are not supported on bedrock
for (Font font : context.javaResourcePack().fonts()) {
if (!font.key().equals(Key.key(Key.MINECRAFT_NAMESPACE, "default"))) continue;
if (!font.key().equals(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "default"))) continue;
for (FontProvider fontProvider : font.providers()) {
unicodeFontData.addAll(handleFont(context, fontProvider));
@@ -77,15 +86,15 @@ public class FontTransformer implements TextureTransformer {
}
// if we have no data, don't stop yet, we may still want to generate some stuff from vanilla
if (unicodeFontData.isEmpty()) {
if (unicodeFontData.isEmpty() && context.vanillaPack().isPresent()) {
if (
!context.isTexturePresent(Key.key(Key.MINECRAFT_NAMESPACE, "font/ascii.png")) &&
!context.isTexturePresent(Key.key(Key.MINECRAFT_NAMESPACE, "font/accented.png")) &&
!context.isTexturePresent(Key.key(Key.MINECRAFT_NAMESPACE, "font/nonlatin_european.png"))
!context.isTexturePresent(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "font/ascii.png")) &&
!context.isTexturePresent(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "font/accented.png")) &&
!context.isTexturePresent(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "font/nonlatin_european.png"))
) return;
for (Font font : context.vanillaPack().fonts()) {
if (!font.key().equals(Key.key(Key.MINECRAFT_NAMESPACE, "default"))) continue;
for (Font font : context.vanillaPack().get().fonts()) {
if (!font.key().equals(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "default"))) continue;
for (FontProvider fontProvider : font.providers()) {
unicodeFontData.addAll(handleFont(context, fontProvider));
@@ -225,8 +234,8 @@ public class FontTransformer implements TextureTransformer {
} else if (fontProvider instanceof ReferenceFontProvider referenceFontProvider) {
// Refers to other fonts, so we need to read those
Font font = context.javaResourcePack().font(referenceFontProvider.id());
if (font == null) { // Just maybe, the vanilla files are used
font = context.vanillaPack().font(referenceFontProvider.id());
if (font == null && context.vanillaPack().isPresent()) { // Just maybe, the vanilla files are used
font = context.vanillaPack().get().font(referenceFontProvider.id());
}
if (font == null) {
@@ -247,30 +256,30 @@ public class FontTransformer implements TextureTransformer {
private void transformDefault8(@NotNull TransformContext context) throws IOException {
// Don't attempt to write default8 if we have no data to pull from, otherwise it's vanilla to vanilla
if (
!context.isTexturePresent(Key.key(Key.MINECRAFT_NAMESPACE, "font/ascii.png")) &&
!context.isTexturePresent(Key.key(Key.MINECRAFT_NAMESPACE, "font/accented.png")) &&
!context.isTexturePresent(Key.key(Key.MINECRAFT_NAMESPACE, "font/nonlatin_european.png"))
!context.isTexturePresent(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "font/ascii.png")) &&
!context.isTexturePresent(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "font/accented.png")) &&
!context.isTexturePresent(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "font/nonlatin_european.png"))
) return;
// Store the java images to prevent constant image reading
Map<String, BufferedImage> imgs = new HashMap<>();
Map<String, Integer> scales = new HashMap<>();
Texture ascii = context.peekOrVanilla(Key.key(Key.MINECRAFT_NAMESPACE, "font/ascii.png"));
Texture ascii = context.peekOrVanilla(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "font/ascii.png"));
if (ascii != null) {
BufferedImage image = this.readImage(ascii);
imgs.put("ascii", image);
scales.put("ascii", image.getWidth() / 128);
}
Texture accented = context.peekOrVanilla(Key.key(Key.MINECRAFT_NAMESPACE, "font/accented.png"));
Texture accented = context.peekOrVanilla(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "font/accented.png"));
if (accented != null) {
BufferedImage image = this.readImage(accented);
imgs.put("accented", image);
scales.put("accented", image.getWidth() / 144);
}
Texture nonlatin_european = context.peekOrVanilla(Key.key(Key.MINECRAFT_NAMESPACE, "font/nonlatin_european.png"));
Texture nonlatin_european = context.peekOrVanilla(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "font/nonlatin_european.png"));
if (nonlatin_european != null) {
BufferedImage image = this.readImage(nonlatin_european);
imgs.put("nonlatin_european", image);

View File

@@ -24,17 +24,18 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.ui;
package org.geysermc.pack.converter.type.texture.transformer.type.ui;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.*;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
@@ -45,7 +46,7 @@ public class HotbarTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
Texture javaHotbarTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, HOTBAR));
Texture javaHotbarTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, HOTBAR));
if (javaHotbarTexture == null) return;
BufferedImage javaHotbarImage = readImage(javaHotbarTexture);
@@ -61,7 +62,7 @@ public class HotbarTransformer implements TextureTransformer {
g.drawImage(ImageUtil.crop(javaHotbarImage, 0, 0, scale, height), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "ui/hotbar_start_cap.png"), bedrockStartHotbar, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "ui/hotbar_start_cap.png"), bedrockStartHotbar, "png");
for (int i = 0; i <= 8; i++) {
BufferedImage bedrockHotbarPart = new BufferedImage(scale * 20, height, BufferedImage.TYPE_INT_ARGB);
@@ -70,7 +71,7 @@ public class HotbarTransformer implements TextureTransformer {
graphics.drawImage(ImageUtil.crop(javaHotbarImage, (i * 20 * scale) + scale, 0, scale * 20, height), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "ui/hotbar_" + i + ".png"), bedrockHotbarPart, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "ui/hotbar_" + i + ".png"), bedrockHotbarPart, "png");
}
// The texture is 1 wide, so 1 * scale = scale
@@ -80,9 +81,9 @@ public class HotbarTransformer implements TextureTransformer {
g.drawImage(ImageUtil.crop(javaHotbarImage, javaHotbarImage.getWidth() - scale, 0, scale, height), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "ui/hotbar_end_cap.png"), bedrockEndHotbar, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "ui/hotbar_end_cap.png"), bedrockEndHotbar, "png");
Texture javaHotbarSelectionTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, HOTBAR_SELECTION));
Texture javaHotbarSelectionTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, HOTBAR_SELECTION));
if (javaHotbarSelectionTexture == null) return;
BufferedImage javaHotbarSelectionImage = readImage(javaHotbarSelectionTexture);
@@ -99,6 +100,6 @@ public class HotbarTransformer implements TextureTransformer {
g.drawImage(ImageUtil.flip(ImageUtil.crop(javaHotbarSelectionImage, 0, 0, size, selectionScale), false, true), 0, size - selectionScale, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "ui/selected_hotbar_slot.png"), bedrockHotbarSelection, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "ui/selected_hotbar_slot.png"), bedrockHotbarSelection, "png");
}
}

View File

@@ -24,20 +24,20 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.ui;
package org.geysermc.pack.converter.type.texture.transformer.type.ui;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.*;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@AutoService(TextureTransformer.class)
@@ -52,7 +52,7 @@ public class LocatorTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
for (Map.Entry<String, String> mapping : DOT_MAPPING.entrySet()) {
Texture dotTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, mapping.getKey()));
Texture dotTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, mapping.getKey()));
if (dotTexture == null) continue;
BufferedImage dotImage = this.readImage(dotTexture);
@@ -71,10 +71,10 @@ public class LocatorTransformer implements TextureTransformer {
// Also accounts for the scale a resource pack may offer
g.drawImage(ImageUtil.crop(dotImage, scale, scale, bedrockSize, bedrockSize), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, mapping.getValue()), bedrockDotImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, mapping.getValue()), bedrockDotImage, "png");
}
Texture upArrowTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/locator_bar_arrow_up.png"));
Texture upArrowTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/locator_bar_arrow_up.png"));
if (upArrowTexture != null) {
BufferedImage javaImage = this.readImage(upArrowTexture);
@@ -86,10 +86,10 @@ public class LocatorTransformer implements TextureTransformer {
g.drawImage(ImageUtil.crop(javaImage, 0, 1, javaImage.getWidth(), scale * 4), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "ui/locator_arrow_up.png"), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "ui/locator_arrow_up.png"), bedrockImage, "png");
}
Texture downArrowTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/locator_bar_arrow_down.png"));
Texture downArrowTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/locator_bar_arrow_down.png"));
if (downArrowTexture != null) {
BufferedImage javaImage = this.readImage(downArrowTexture);
@@ -101,10 +101,10 @@ public class LocatorTransformer implements TextureTransformer {
g.drawImage(ImageUtil.crop(javaImage, 0, 0, javaImage.getWidth(), scale * 4), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "ui/locator_arrow_down.png"), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "ui/locator_arrow_down.png"), bedrockImage, "png");
}
Texture bgTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/locator_bar_background.png"));
Texture bgTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/locator_bar_background.png"));
if (bgTexture != null) {
BufferedImage javaImage = this.readImage(bgTexture);
@@ -122,7 +122,7 @@ public class LocatorTransformer implements TextureTransformer {
g.drawImage(ImageUtil.crop(javaImage, javaImage.getWidth() - (scale * 5), 0, scale * 5, javaImage.getHeight()), (scale * 182) - scale * 5, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "ui/locator_bg.png"), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "ui/locator_bg.png"), bedrockImage, "png");
}
}
}

View File

@@ -24,17 +24,18 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.ui;
package org.geysermc.pack.converter.type.texture.transformer.type.ui;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.*;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.List;
@@ -62,7 +63,7 @@ public class SignTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
for (SignData signData : SIGNS) {
Texture javaTexture = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, JAVA_LOCATION.formatted(signData.name)));
Texture javaTexture = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, JAVA_LOCATION.formatted(signData.name)));
if (javaTexture == null) continue;
BufferedImage javaImage = this.readImage(javaTexture);
@@ -75,7 +76,7 @@ public class SignTransformer implements TextureTransformer {
g.drawImage(ImageUtil.crop(javaImage, (int) (2 * scale), (int) (2 * scale), (int) (scale * 24), (int) (scale * 12)), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, BEDROCK_LOCATION.formatted(signData.bedrockName)), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, BEDROCK_LOCATION.formatted(signData.bedrockName)), bedrockImage, "png");
}
}

View File

@@ -24,17 +24,18 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.ui;
package org.geysermc.pack.converter.type.texture.transformer.type.ui;
import com.google.auto.service.AutoService;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.ImageUtil;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.*;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
@@ -42,7 +43,7 @@ import java.io.IOException;
public class TitleTransformer implements TextureTransformer {
@Override
public void transform(@NotNull TransformContext context) throws IOException {
Texture javaTexture = context.poll(Key.key(Key.MINECRAFT_NAMESPACE, "gui/title/minecraft.png"));
Texture javaTexture = context.poll(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "gui/title/minecraft.png"));
if (javaTexture == null) return;
BufferedImage javaImage = this.readImage(javaTexture);
@@ -56,6 +57,6 @@ public class TitleTransformer implements TextureTransformer {
g.drawImage(ImageUtil.resize(ImageUtil.crop(javaImage, 0, 0, javaImage.getWidth(), javaImage.getHeight() - ((int) (scale * 79))), 1937, 333), 0, 0, null);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "ui/title.png"), bedrockImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "ui/title.png"), bedrockImage, "png");
}
}

View File

@@ -24,22 +24,20 @@
*
*/
package org.geysermc.pack.converter.converter.texture.transformer.type.ui;
package org.geysermc.pack.converter.type.texture.transformer.type.ui;
import com.google.auto.service.AutoService;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import net.kyori.adventure.key.Key;
import org.geysermc.pack.converter.converter.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.TransformContext;
import org.geysermc.pack.converter.type.texture.transformer.TextureTransformer;
import org.geysermc.pack.converter.type.texture.transformer.TransformContext;
import org.geysermc.pack.converter.util.KeyUtil;
import org.jetbrains.annotations.NotNull;
import team.unnamed.creative.texture.Texture;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
// This isn't really a "transformer", we just include some files if an certain UI elements are present
// in order to get it to appear correctly, the mappings here are still done in textures.json or elsewhere
@@ -47,29 +45,26 @@ import java.nio.charset.StandardCharsets;
// Credit to Bedrock Tweaks, wouldn't know how to do this without their packs
@AutoService(TextureTransformer.class)
public class UISizeTransformer implements TextureTransformer {
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.create();
private static final JsonArray FULLBARSLICE = new JsonArray();
@Override
public void transform(@NotNull TransformContext context) throws IOException {
Texture emptyXp = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/experience_bar_background.png"));
Texture emptyXp = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/experience_bar_background.png"));
if (emptyXp != null) {
writeUiJson(context, this.readImage(emptyXp), "experiencebarempty", FULLBARSLICE);
// Since we have the full image, we *don't* want this
BufferedImage nubImage = new BufferedImage(11, 5, BufferedImage.TYPE_INT_ARGB);
context.offer(Key.key(Key.MINECRAFT_NAMESPACE, "ui/experiencenub.png"), nubImage, "png");
context.offer(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "ui/experiencenub.png"), nubImage, "png");
}
Texture fullXp = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/experience_bar_progress.png"));
Texture fullXp = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/experience_bar_progress.png"));
if (fullXp != null) {
writeUiJson(context, this.readImage(fullXp), "experiencebarfull", FULLBARSLICE);
}
Texture locatorBg = context.peek(Key.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/locator_bar_background.png"));
Texture locatorBg = context.peek(KeyUtil.key(Key.MINECRAFT_NAMESPACE, "gui/sprites/hud/locator_bar_background.png"));
if (locatorBg != null) {
BufferedImage image = this.readImage(locatorBg);
@@ -97,7 +92,7 @@ public class UISizeTransformer implements TextureTransformer {
baseSize.add(customHeight);
rootObject.add("base_size", baseSize);
context.bedrockResourcePack().addExtraFile(GSON.toJson(rootObject).getBytes(StandardCharsets.UTF_8), "textures/ui/%s.json".formatted(jsonName));
context.bedrockResourcePack().addExtraFile(rootObject, "textures/ui/%s.json".formatted(jsonName));
}
static {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
* 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
@@ -24,15 +24,22 @@
*
*/
package org.geysermc.pack.converter.converter;
package org.geysermc.pack.converter.util;
import org.geysermc.pack.converter.data.BaseConversionData;
import net.kyori.adventure.key.Key;
import org.jetbrains.annotations.NotNull;
public abstract class BaseConverter implements Converter<BaseConversionData> {
@SuppressWarnings("PatternValidation")
public final class KeyUtil {
@Override
public BaseConversionData createConversionData(@NotNull ConversionDataCreationContext context) {
return new BaseConversionData(context.inputDirectory(), context.outputDirectory(), context.vanillaResourcePack());
private KeyUtil() {
}
public static @NotNull Key key(final @NotNull String string) {
return Key.key(string);
}
public static @NotNull Key key(final @NotNull String namespace, final @NotNull String value) {
return Key.key(namespace, value);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
* Copyright (c) 2025-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
@@ -24,22 +24,31 @@
*
*/
package org.geysermc.pack.converter.converter;
package org.geysermc.pack.converter.util;
import java.util.List;
import java.util.ServiceLoader;
import org.jetbrains.annotations.NotNull;
public class Converters {
public interface LogListenerHelper {
public static List<? extends Converter<?>> defaultConverters() {
return defaultConverters(false);
LogListener logListener();
default void debug(@NotNull String message) {
logListener().debug(message);
}
public static List<? extends Converter<?>> defaultConverters(boolean experimental) {
return ServiceLoader.load(Converter.class).stream()
.map(ServiceLoader.Provider::get)
.map(c -> (Converter<?>)c)
.filter(converter -> experimental || !converter.isExperimental())
.toList();
default void info(@NotNull String message) {
logListener().info(message);
}
default void warn(@NotNull String message) {
logListener().warn(message);
}
default void error(@NotNull String message) {
logListener().error(message);
}
default void error(@NotNull String message, @NotNull Throwable exception) {
logListener().error(message, exception);
}
}

View File

@@ -26,12 +26,16 @@
package org.geysermc.pack.converter.util;
import com.google.gson.*;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.Getter;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.file.PathUtils;
import org.geysermc.pack.converter.converter.texture.transformer.type.OverlayTransformer;
import org.geysermc.pack.converter.converter.texture.transformer.type.entity.SheepTransformer;
import org.geysermc.pack.converter.type.texture.transformer.type.OverlayTransformer;
import org.geysermc.pack.converter.type.texture.transformer.type.entity.SheepTransformer;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
@@ -40,7 +44,10 @@ import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
public final class VanillaPackProvider {

View File

@@ -28,6 +28,7 @@ package org.geysermc.pack.bedrock.resource;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import org.geysermc.pack.bedrock.resource.attachables.Attachables;
import org.geysermc.pack.bedrock.resource.models.entity.ModelEntity;
import org.geysermc.pack.bedrock.resource.render_controllers.RenderControllers;
@@ -44,9 +45,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -91,6 +92,16 @@ public class BedrockResourcePack {
this.terrainTexture = terrainTexture;
}
/**
* Get the directory of the resource pack.
*
* @return the directory of the resource pack
*/
@NotNull
public Path directory() {
return directory;
}
/**
* Get the manifest of the resource pack.
*
@@ -500,6 +511,16 @@ public class BedrockResourcePack {
this.extraFiles.put(location, bytes);
}
/**
* Add an extra JSON file to the resource pack.
*
* @param element the contents of the file
* @param location the location of the file
*/
public void addExtraFile(@NotNull JsonElement element, @NotNull String location) {
addExtraFile(GSON.toJson(element).getBytes(StandardCharsets.UTF_8), location);
}
/**
* Exports the resource pack to the specified directory.
*