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

More final tweaks

This commit is contained in:
Eclipse
2025-10-16 14:07:22 +00:00
parent 2cc85d6c91
commit 3296b5a59e
12 changed files with 94 additions and 73 deletions

View File

@@ -8,14 +8,11 @@ import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.core.HolderLookup;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import org.apache.commons.io.IOUtils;
import org.geysermc.rainbow.CodecUtil;
import org.geysermc.rainbow.RainbowIO;
import org.geysermc.rainbow.mapping.PackSerializer;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Objects;
@@ -31,26 +28,17 @@ public class MinecraftPackSerializer implements PackSerializer {
@Override
public <T> CompletableFuture<?> saveJson(Codec<T> codec, T object, Path path) {
DynamicOps<JsonElement> ops = RegistryOps.create(JsonOps.INSTANCE, registries);
return CompletableFuture.runAsync(() -> {
try {
CodecUtil.trySaveJson(codec, object, path.resolveSibling(path.getFileName() + ".json"), ops);
} catch (IOException exception) {
// TODO log
}
}, Util.backgroundExecutor().forName("PackSerializer-saveJson"));
return CompletableFuture.runAsync(() -> RainbowIO.safeIO(() -> CodecUtil.trySaveJson(codec, object, path.resolveSibling(path.getFileName() + ".json"), ops)),
Util.backgroundExecutor().forName("PackSerializer-saveJson"));
}
@Override
public CompletableFuture<?> saveTexture(byte[] texture, Path path) {
return CompletableFuture.runAsync(() -> {
try {
return CompletableFuture.runAsync(() -> RainbowIO.safeIO(() -> {
CodecUtil.ensureDirectoryExists(path.getParent());
try (OutputStream outputTexture = new FileOutputStream(path.toFile())) {
outputTexture.write(texture);
}
} catch (IOException exception) {
// TODO log
}
}, Util.backgroundExecutor().forName("PackSerializer-saveTexture"));
}), Util.backgroundExecutor().forName("PackSerializer-saveTexture"));
}
}

View File

@@ -8,6 +8,7 @@ import net.minecraft.util.RandomSource;
import net.minecraft.util.StringUtil;
import org.geysermc.rainbow.CodecUtil;
import org.geysermc.rainbow.Rainbow;
import org.geysermc.rainbow.RainbowIO;
import org.geysermc.rainbow.client.mixin.SplashRendererAccessor;
import org.geysermc.rainbow.client.render.MinecraftGeometryRenderer;
import org.geysermc.rainbow.pack.BedrockItem;
@@ -24,7 +25,7 @@ import java.util.function.Consumer;
public final class PackManager {
private static final List<String> PACK_SUMMARY_COMMENTS = List.of("Use the custom item API v2 build!", "bugrock moment", "RORY",
"use !!plshelp", "rm -rf --no-preserve-root /*", "welcome to the internet!", "beep beep. boop boop?", "FROG", "it is frog day", "it is cat day!",
"use !!plshelp", "*message was deleted*", "welcome to the internet!", "beep beep. boop boop?", "FROG", "it is frog day", "it is cat day!",
"eclipse will hear about this.", "you must now say the word 'frog' in the #general channel", "You Just Lost The Game", "you are now breathing manually",
"you are now blinking manually", "you're eligible for a free hug token! <3", "don't mind me!", "hissss", "Gayser and Floodgayte, my favourite plugins.",
"meow", "we'll be done here soon™", "got anything else to say?", "we're done now!", "this will be fixed by v6053", "expect it to be done within 180 business days!",
@@ -69,11 +70,7 @@ public final class PackManager {
public boolean finish() {
currentPack.map(pack -> {
try {
Files.writeString(getExportPath().orElseThrow().resolve(REPORT_FILE), createPackSummary(pack));
} catch (IOException exception) {
// TODO log
}
RainbowIO.safeIO(() -> Files.writeString(getExportPath().orElseThrow().resolve(REPORT_FILE), createPackSummary(pack)));
return pack.save();
}).ifPresent(CompletableFuture::join);
boolean wasPresent = currentPack.isPresent();

View File

@@ -23,6 +23,7 @@ import net.minecraft.util.ProblemReporter;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.equipment.EquipmentAsset;
import org.geysermc.rainbow.Rainbow;
import org.geysermc.rainbow.RainbowIO;
import org.geysermc.rainbow.mapping.AssetResolver;
import org.geysermc.rainbow.mapping.PackSerializer;
import org.geysermc.rainbow.pack.BedrockPack;
@@ -125,7 +126,7 @@ public abstract class RainbowModelProvider extends FabricModelProvider {
try {
output.writeIfNeeded(path, texture, HashCode.fromBytes(texture));
} catch (IOException exception) {
LOGGER.error("Failed to save file to {}", path, exception);
LOGGER.error("Failed to save texture to {}", path, exception);
}
}, Util.backgroundExecutor().forName("PackSerializer-saveTexture"));
}
@@ -153,12 +154,11 @@ public abstract class RainbowModelProvider extends FabricModelProvider {
public Optional<ResolvedModel> getResolvedModel(ResourceLocation location) {
return resolvedModelCache.computeIfAbsent(location, key -> Optional.ofNullable(models.get(location))
.map(instance -> BlockModel.fromStream(new StringReader(instance.get().toString())))
.or(() -> {
.or(() -> RainbowIO.safeIO(() -> {
try (BufferedReader reader = resourceManager.openAsReader(location.withPrefix("models/").withSuffix(".json"))) {
return Optional.of(BlockModel.fromStream(reader));
} catch (IOException ignored) {}
return Optional.empty();
})
return BlockModel.fromStream(reader);
}
}))
.map(model -> new ResolvedModel() {
@Override
public @NotNull UnbakedModel wrapped() {

View File

@@ -14,8 +14,7 @@ public class Rainbow {
return ResourceLocation.fromNamespaceAndPath(MOD_ID, path);
}
// TODO rename remove file
public static String fileSafeResourceLocation(ResourceLocation location) {
public static String safeResourceLocation(ResourceLocation location) {
return location.toString().replace(':', '.').replace('/', '_');
}

View File

@@ -0,0 +1,51 @@
package org.geysermc.rainbow;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
import java.io.IOException;
import java.util.Optional;
public final class RainbowIO {
private static final Logger LOGGER = LogUtils.getLogger();
private RainbowIO() {}
public static <T> T safeIO(IOSupplier<T> supplier, T defaultValue) {
try {
return supplier.get();
} catch (IOException exception) {
LOGGER.error("Failed to perform IO operation!", exception);
return defaultValue;
}
}
public static <T> Optional<T> safeIO(IOSupplier<T> supplier) {
try {
return Optional.ofNullable(supplier.get());
} catch (IOException exception) {
LOGGER.error("Failed to perform IO operation!", exception);
return Optional.empty();
}
}
public static void safeIO(IORunnable runnable) {
try {
runnable.run();
} catch (IOException exception) {
LOGGER.error("Failed to perform IO operation!", exception);
}
}
@FunctionalInterface
public interface IOSupplier<T> {
T get() throws IOException;
}
@FunctionalInterface
public interface IORunnable {
void run() throws IOException;
}
}

View File

@@ -61,7 +61,7 @@ public record GeyserBaseDefinition(ResourceLocation bedrockIdentifier, Optional<
}
public String textureName() {
return bedrockOptions.icon.orElse(Rainbow.fileSafeResourceLocation(bedrockIdentifier));
return bedrockOptions.icon.orElse(Rainbow.safeResourceLocation(bedrockIdentifier));
}
public record BedrockOptions(Optional<String> icon, boolean allowOffhand, boolean displayHandheld, int protectionValue, List<ResourceLocation> tags) {

View File

@@ -71,9 +71,9 @@ public class BedrockItemMapper {
}
public static void tryMapStack(ItemStack stack, int customModelData, ProblemReporter reporter, PackContext context) {
// TODO Improve this, use resouce log in problemreporter
ItemModel.Unbaked vanillaModel = context.assetResolver().getClientItem(stack.get(DataComponents.ITEM_MODEL)).map(ClientItem::model).orElseThrow();
ProblemReporter childReporter = reporter.forChild(() -> "item model " + vanillaModel + " with custom model data " + customModelData + " ");
ResourceLocation itemModel = stack.get(DataComponents.ITEM_MODEL);
ItemModel.Unbaked vanillaModel = context.assetResolver().getClientItem(itemModel).map(ClientItem::model).orElseThrow();
ProblemReporter childReporter = reporter.forChild(() -> "item model " + itemModel + " with custom model data " + customModelData + " ");
if (vanillaModel instanceof RangeSelectItemModel.Unbaked(RangeSelectItemModelProperty property, float scale, List<RangeSelectItemModel.Entry> entries, Optional<ItemModel.Unbaked> fallback)) {
// WHY, Mojang?
if (property instanceof net.minecraft.client.renderer.item.properties.numeric.CustomModelDataProperty(int index)) {

View File

@@ -44,7 +44,7 @@ public record BedrockGeometryContext(Optional<Supplier<StitchedGeometry>> geomet
// This check should probably be done differently (actually check if the model is 2D or 3D)
ResourceLocation modelLocation = ResourceLocation.parse(model.debugName());
String safeIdentifier = Rainbow.fileSafeResourceLocation(bedrockIdentifier);
String safeIdentifier = Rainbow.safeResourceLocation(bedrockIdentifier);
geometry = Optional.of(Suppliers.memoize(() -> {
StitchedTextures stitchedTextures = StitchedTextures.stitchModelTextures(textures, context);

View File

@@ -2,7 +2,6 @@ package org.geysermc.rainbow.mapping.geometry;
import com.mojang.blaze3d.platform.NativeImage;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.TextureSlots;
import net.minecraft.client.renderer.texture.SpriteContents;
import net.minecraft.client.renderer.texture.SpriteLoader;
@@ -12,12 +11,12 @@ import net.minecraft.client.resources.model.Material;
import net.minecraft.data.AtlasIds;
import net.minecraft.resources.ResourceLocation;
import org.geysermc.rainbow.Rainbow;
import org.geysermc.rainbow.RainbowIO;
import org.geysermc.rainbow.mapping.PackContext;
import org.geysermc.rainbow.mixin.SpriteContentsAccessor;
import org.geysermc.rainbow.mixin.SpriteLoaderAccessor;
import org.geysermc.rainbow.mixin.TextureSlotsAccessor;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
@@ -50,20 +49,20 @@ public record StitchedTextures(Map<String, TextureAtlasSprite> sprites, Supplier
// Atlas ID doesn't matter much here, but BLOCKS is the most appropriate
// Not sure if 1024 should be the max supported texture size, but it seems to work
SpriteLoader spriteLoader = new SpriteLoader(AtlasIds.BLOCKS, 1024, 16, 16);
List<SpriteContents> sprites = textures.distinct().map(texture -> readSpriteContents(texture, context)).toList();
List<SpriteContents> sprites = textures.distinct()
.map(texture -> readSpriteContents(texture, context))
.<SpriteContents>mapMulti(Optional::ifPresent)
.toList();
return ((SpriteLoaderAccessor) spriteLoader).invokeStitch(sprites, 0, Util.backgroundExecutor());
}
private static SpriteContents readSpriteContents(ResourceLocation location, PackContext context) {
// TODO decorate path util
// TODO don't use ResourceManager
// TODO IO is on main thread here?
private static Optional<SpriteContents> readSpriteContents(ResourceLocation location, PackContext context) {
return RainbowIO.safeIO(() -> {
try (InputStream textureStream = context.assetResolver().openAsset(Rainbow.decorateTextureLocation(location))) {
NativeImage texture = NativeImage.read(textureStream);
return new SpriteContents(location, new FrameSize(texture.getWidth(), texture.getHeight()), texture);
} catch (IOException exception) {
throw new RuntimeException(exception);
}
});
}
private static NativeImage stitchTextureAtlas(SpriteLoader.Preparations preparations) {

View File

@@ -32,7 +32,7 @@ public record BedrockItem(ResourceLocation identifier, String textureName, Bedro
CompletableFuture.allOf(attachableTextures.stream().map(textureSaver).toArray(CompletableFuture[]::new)),
stitchedGeometry.map(StitchedGeometry::geometry).map(geometry -> geometry.save(serializer, geometryDirectory)).orElse(noop()),
stitchedGeometry.map(StitchedGeometry::stitchedTextures).map(textureSaver).orElse(noop()),
geometryContext.animation().map(context -> context.animation().save(serializer, animationDirectory, Rainbow.fileSafeResourceLocation(identifier))).orElse(noop())
geometryContext.animation().map(context -> context.animation().save(serializer, animationDirectory, Rainbow.safeResourceLocation(identifier))).orElse(noop())
);
})
);

View File

@@ -13,6 +13,7 @@ import net.minecraft.world.item.component.CustomModelData;
import org.geysermc.rainbow.CodecUtil;
import org.geysermc.rainbow.PackConstants;
import org.geysermc.rainbow.Rainbow;
import org.geysermc.rainbow.RainbowIO;
import org.geysermc.rainbow.image.NativeImageUtil;
import org.geysermc.rainbow.mapping.AssetResolver;
import org.geysermc.rainbow.mapping.BedrockItemMapper;
@@ -131,22 +132,12 @@ public class BedrockPack {
Function<TextureHolder, CompletableFuture<?>> textureSaver = texture -> {
ResourceLocation textureLocation = Rainbow.decorateTextureLocation(texture.location());
return texture.supplier()
.flatMap(image -> {
try {
return Optional.of(NativeImageUtil.writeToByteArray(image.get()));
} catch (IOException exception) {
// TODO log
return Optional.empty();
}
})
.or(() -> {
.flatMap(image -> RainbowIO.safeIO(() -> NativeImageUtil.writeToByteArray(image.get())))
.or(() -> RainbowIO.safeIO(() -> {
try (InputStream textureStream = context.assetResolver().openAsset(textureLocation)) {
return Optional.of(textureStream.readAllBytes());
} catch (IOException exception) {
// TODO log
return Optional.empty();
return textureStream.readAllBytes();
}
})
}))
.map(bytes -> serializer.saveTexture(bytes, paths.packRoot().resolve(textureLocation.getPath())))
.orElse(CompletableFuture.completedFuture(null));
};
@@ -156,11 +147,7 @@ public class BedrockPack {
}
if (paths.zipOutput().isPresent()) {
try {
CodecUtil.tryZipDirectory(paths.packRoot(), paths.zipOutput().get());
} catch (IOException exception) {
// TODO log
}
RainbowIO.safeIO(() -> CodecUtil.tryZipDirectory(paths.packRoot(), paths.zipOutput().get()));
}
if (reporter instanceof AutoCloseable closeable) {

View File

@@ -37,7 +37,7 @@ public record BedrockAttachable(BedrockVersion formatVersion, AttachableInfo inf
public CompletableFuture<?> save(PackSerializer serializer, Path attachablesDirectory) {
// Get a safe attachable path by using Geyser's way of getting icons
return serializer.saveJson(CODEC, this, attachablesDirectory.resolve(Rainbow.fileSafeResourceLocation(info.identifier) + ".json"));
return serializer.saveJson(CODEC, this, attachablesDirectory.resolve(Rainbow.safeResourceLocation(info.identifier) + ".json"));
}
public static Builder builder(ResourceLocation identifier) {