From 8b269ff8efc5adce65e1531ddaebd7d3713f14e8 Mon Sep 17 00:00:00 2001 From: Space Walker Date: Mon, 28 Sep 2026 18:55:22 +0200 Subject: [PATCH 1/2] [resource loader] implement DirectoryPackSource --- .../repository/DirectoryPackSource.java | 55 ++++++++++++++++++- .../repository/ResourcePackDetector.java | 45 +++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 libraries/resource-loader/src/main/java/net/ornithemc/osl/resource/loader/impl/resource/repository/ResourcePackDetector.java diff --git a/libraries/resource-loader/src/main/java/net/ornithemc/osl/resource/loader/impl/resource/repository/DirectoryPackSource.java b/libraries/resource-loader/src/main/java/net/ornithemc/osl/resource/loader/impl/resource/repository/DirectoryPackSource.java index 57c840ba8..497e1e2b4 100644 --- a/libraries/resource-loader/src/main/java/net/ornithemc/osl/resource/loader/impl/resource/repository/DirectoryPackSource.java +++ b/libraries/resource-loader/src/main/java/net/ornithemc/osl/resource/loader/impl/resource/repository/DirectoryPackSource.java @@ -1,10 +1,17 @@ package net.ornithemc.osl.resource.loader.impl.resource.repository; +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.function.BiConsumer; import java.util.function.Consumer; +import net.ornithemc.osl.resource.loader.api.resource.pack.PackPosition; +import net.ornithemc.osl.resource.loader.api.resource.pack.ResourcePack; import net.ornithemc.osl.resource.loader.api.resource.repository.ResourcePackRepository; import net.ornithemc.osl.resource.loader.api.resource.repository.ResourcePackSummary; +import net.ornithemc.osl.resource.loader.impl.ResourceLoader; public class DirectoryPackSource implements ResourcePackRepository.Source { @@ -12,8 +19,54 @@ public static String packId(Path file) { return "file/" + file.getFileName(); } + private final Path directory; + private ResourcePackDetector detector; + + public DirectoryPackSource(Path dir) { + this(dir, new ResourcePackDetector()); + } + + public DirectoryPackSource(Path dir, ResourcePackDetector detector) { + this.directory = dir; + this.detector = detector; + } + @Override public void loadResourcePacks(Consumer consumer) { - // TODO + try { + Files.createDirectories(this.directory); + + discoverResourcePacks(this.directory, this.detector, (file, pack) -> { + ResourcePackSummary summary = ResourcePackSummary.create( + pack, + packId(file), + false, + false, + PackPosition.TOP + ); + + if (summary != null) { + consumer.accept(summary); + } + }); + } catch (IOException e) { + ResourceLoader.LOGGER.warn("Unable to load packs from {}", this.directory.getFileName()); + } + } + + private static void discoverResourcePacks(Path directory, ResourcePackDetector detector, BiConsumer consumer) throws IOException { + try (DirectoryStream dir = Files.newDirectoryStream(directory)) { + for (Path file : dir) { + try { + ResourcePack pack = detector.detectResourcePack(file); + + if (pack != null) { + consumer.accept(file, pack); + } + } catch (IOException e) { + ResourceLoader.LOGGER.warn("Unable to read properties from {}, ignoring", file.getFileName()); + } + } + } } } diff --git a/libraries/resource-loader/src/main/java/net/ornithemc/osl/resource/loader/impl/resource/repository/ResourcePackDetector.java b/libraries/resource-loader/src/main/java/net/ornithemc/osl/resource/loader/impl/resource/repository/ResourcePackDetector.java new file mode 100644 index 000000000..ed7614a6e --- /dev/null +++ b/libraries/resource-loader/src/main/java/net/ornithemc/osl/resource/loader/impl/resource/repository/ResourcePackDetector.java @@ -0,0 +1,45 @@ +package net.ornithemc.osl.resource.loader.impl.resource.repository; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; + +import net.ornithemc.osl.resource.loader.api.resource.pack.ResourcePack; +import net.ornithemc.osl.resource.loader.impl.resource.pack.DirectoryResourcePack; +import net.ornithemc.osl.resource.loader.impl.resource.pack.ZippedResourcePack; + +public class ResourcePackDetector { + + public ResourcePack detectResourcePack(Path file) throws IOException { + BasicFileAttributes attributes = Files.readAttributes(file, BasicFileAttributes.class); + + if (attributes.isDirectory()) { + if (this.isValidDirectory(file)) { + return this.createDirectoryPack(file); + } + } else if (attributes.isRegularFile()) { + if (this.isValidZip(file)) { + return this.createZippedPack(file); + } + } + + return null; + } + + protected boolean isValidDirectory(Path file) { + return Files.exists(file.resolve(ResourcePack.METADATA_FILE)); + } + + protected boolean isValidZip(Path file) { + return file.getFileName().toString().endsWith(".zip"); + } + + protected ResourcePack createDirectoryPack(Path file) { + return new DirectoryResourcePack(file); + } + + protected ResourcePack createZippedPack(Path file) { + return new ZippedResourcePack(file); + } +} From 6bae80b69e2fa7e7670a3e5f321236c541d6bd9c Mon Sep 17 00:00:00 2001 From: Space Walker Date: Tue, 29 Sep 2026 10:06:53 +0200 Subject: [PATCH 2/2] [resource loader] add texturepacks/ support to early alpha --- .../loader/impl/DefaultTexturePack.java | 47 +------ .../osl/resource/loader/impl/TexturePack.java | 119 ++++++++++++++++++ .../loader/impl/TexturePackDetector.java | 25 ++++ .../loader/impl/access/GameOptionsAccess.java | 9 ++ .../impl/mixin/client/GameOptionsMixin.java | 75 +++++++++++ .../impl/mixin/client/MinecraftMixin.java | 31 ++++- .../resources/osl.resource-loader.mixins.json | 4 +- 7 files changed, 265 insertions(+), 45 deletions(-) create mode 100644 libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/TexturePack.java create mode 100644 libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/TexturePackDetector.java create mode 100644 libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/access/GameOptionsAccess.java create mode 100644 libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/mixin/client/GameOptionsMixin.java diff --git a/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/DefaultTexturePack.java b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/DefaultTexturePack.java index 70cd5048e..a56831015 100644 --- a/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/DefaultTexturePack.java +++ b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/DefaultTexturePack.java @@ -1,54 +1,17 @@ package net.ornithemc.osl.resource.loader.impl; -import java.io.InputStream; -import java.util.Collections; -import java.util.Map; -import java.util.Set; - import net.fabricmc.loader.api.FabricLoader; -import net.ornithemc.osl.core.api.util.NamespacedIdentifier; -import net.ornithemc.osl.core.api.util.function.IOSupplier; -import net.ornithemc.osl.resource.loader.api.resource.ResourceType; -import net.ornithemc.osl.resource.loader.api.resource.pack.ResourceConsumer; -import net.ornithemc.osl.resource.loader.impl.resource.pack.ModContainerResourcePack; -import net.ornithemc.osl.resource.loader.impl.resource.pack.ResourcePacks; -import net.ornithemc.osl.text.api.TextComponents; +import net.ornithemc.osl.resource.loader.impl.resource.pack.ModResourcePack; -public class DefaultTexturePack extends ModContainerResourcePack { +public class DefaultTexturePack extends TexturePack { public DefaultTexturePack() { - super(FabricLoader.getInstance().getModContainer("minecraft").get(), ".", "Default"); - } - - @Override - protected IOSupplier generateMetadataFile() { - return ResourcePacks.generateMetadataFile( - TextComponents.literal("The default look of Minecraft") - ); - } - - @Override - protected IOSupplier generateIconFile() { - return ModContainerResourcePack.getIcon(this.mod); - } - - @Override - protected Map> findNamespaces() { - return Collections.emptyMap(); - } - - @Override - public boolean hasResource(ResourceType type, NamespacedIdentifier location) { - return false; - } - - @Override - public IOSupplier getResource(ResourceType type, NamespacedIdentifier location) { - return null; + super(new ModResourcePack(FabricLoader.getInstance().getModContainer("minecraft").get(), ".", "Default")); } @Override - public void findResources(ResourceType type, String namespace, String directory, ResourceConsumer consumer) { + void initMetadata() { + this.descriptionLine1 = "The default look of Minecraft"; } } diff --git a/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/TexturePack.java b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/TexturePack.java new file mode 100644 index 000000000..3e309dd20 --- /dev/null +++ b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/TexturePack.java @@ -0,0 +1,119 @@ +package net.ornithemc.osl.resource.loader.impl; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import net.ornithemc.osl.core.api.util.NamespacedIdentifier; +import net.ornithemc.osl.core.api.util.function.IOSupplier; +import net.ornithemc.osl.resource.loader.api.resource.ResourceType; +import net.ornithemc.osl.resource.loader.api.resource.pack.AbstractResourcePack; +import net.ornithemc.osl.resource.loader.api.resource.pack.ResourceConsumer; +import net.ornithemc.osl.resource.loader.api.resource.pack.ResourcePack; +import net.ornithemc.osl.resource.loader.impl.resource.pack.ResourcePacks; +import net.ornithemc.osl.text.api.TextComponents; + +public class TexturePack extends AbstractResourcePack { + + public static final String METADATA_FILE = "pack.txt"; + + private final ResourcePack delegate; + private final IOSupplier metadata; + + public String descriptionLine1 = ""; + public String descriptionLine2 = ""; + + public TexturePack(ResourcePack delegate) { + this.delegate = delegate; + this.metadata = this.createMetadata(); + } + + private IOSupplier createMetadata() { + this.initMetadata(); + + String description = ""; + if (this.descriptionLine1 != null) { + description += this.descriptionLine1; + if (!this.descriptionLine2.isEmpty()) { + description += "\n" + this.descriptionLine2; + } + } + + return ResourcePacks.generateMetadataFile( + TextComponents.literal(description) + ); + } + + void initMetadata() { + try (BufferedReader br = new BufferedReader(new InputStreamReader(this.getResource(METADATA_FILE)))) { + this.descriptionLine1 = this.trimDescriptionLine(br.readLine()); + this.descriptionLine2 = this.trimDescriptionLine(br.readLine()); + } catch (IOException e) { + ResourceLoader.LOGGER.warn("Unable to read metadata from texture pack " + this.getName(), e); + } + } + + String trimDescriptionLine(String line) { + if (line.length() > 34) { + line = line.substring(0, 34); + } + + return line; + } + + @Override + public String getName() { + return this.delegate.getName(); + } + + @Override + public boolean hasResource(String path) { + if (ResourcePack.METADATA_FILE.equals(path)) { + return true; + } + + return this.delegate.hasResource(path); + } + + @Override + public InputStream getResource(String path) throws IOException { + if (ResourcePack.METADATA_FILE.equals(path)) { + return this.metadata.get(); + } + + return this.delegate.getResource(path); + } + + @Override + protected Map> findNamespaces() { + return Collections.emptyMap(); + } + + @Override + public boolean hasResource(ResourceType type, NamespacedIdentifier location) { + return false; + } + + @Override + public IOSupplier getResource(ResourceType type, NamespacedIdentifier location) { + return null; + } + + @Override + public void findResources(ResourceType type, String namespace, String directory, ResourceConsumer consumer) { + } + + @Override + public void open() { + this.delegate.open(); + } + + @Override + public void close() { + this.delegate.close(); + } +} diff --git a/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/TexturePackDetector.java b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/TexturePackDetector.java new file mode 100644 index 000000000..adb9310af --- /dev/null +++ b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/TexturePackDetector.java @@ -0,0 +1,25 @@ +package net.ornithemc.osl.resource.loader.impl; + +import java.nio.file.Files; +import java.nio.file.Path; + +import net.ornithemc.osl.resource.loader.api.resource.pack.ResourcePack; +import net.ornithemc.osl.resource.loader.impl.resource.repository.ResourcePackDetector; + +public class TexturePackDetector extends ResourcePackDetector { + + @Override + protected boolean isValidDirectory(Path file) { + return Files.exists(file.resolve(TexturePack.METADATA_FILE)); + } + + @Override + protected ResourcePack createDirectoryPack(Path file) { + return new TexturePack(super.createDirectoryPack(file)); + } + + @Override + protected ResourcePack createZippedPack(Path file) { + return new TexturePack(super.createZippedPack(file)); + } +} diff --git a/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/access/GameOptionsAccess.java b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/access/GameOptionsAccess.java new file mode 100644 index 000000000..d699b1020 --- /dev/null +++ b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/access/GameOptionsAccess.java @@ -0,0 +1,9 @@ +package net.ornithemc.osl.resource.loader.impl.access; + +public interface GameOptionsAccess { + + String osl$resource_loader$getSkin(); + + void osl$resource_loader$setSkin(String skin); + +} diff --git a/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/mixin/client/GameOptionsMixin.java b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/mixin/client/GameOptionsMixin.java new file mode 100644 index 000000000..ffdf2759b --- /dev/null +++ b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/mixin/client/GameOptionsMixin.java @@ -0,0 +1,75 @@ +package net.ornithemc.osl.resource.loader.impl.mixin.client; + +import java.io.PrintWriter; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.llamalad7.mixinextras.sugar.Local; + +import net.minecraft.client.options.GameOptions; + +import net.ornithemc.osl.resource.loader.impl.access.GameOptionsAccess; + +@Mixin(GameOptions.class) +public class GameOptionsMixin implements GameOptionsAccess { + + private static final String SKIN_OPTION = "skin"; + private static final String SKIN_DEFAULT_VALUE = "Default"; + + @Unique + private String skin; + + @Inject( + method = "*", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/options/GameOptions;load()V" + ) + ) + private void osl$resource_loader$initSkinOption(CallbackInfo ci) { + this.skin = SKIN_DEFAULT_VALUE; + } + + @Inject( + method = "load", + at = @At( + value = "INVOKE", + target = "Ljava/lang/String;split(Ljava/lang/String;)[Ljava/lang/String;", + shift = Shift.BY, + by = 2 + ) + ) + private void osl$resource_loader$loadSkinOption(CallbackInfo ci, @Local String[] option) { + if (option[0].equals(SKIN_OPTION)) { + this.skin = option[1]; + } + } + + @Inject( + method = "save", + at = @At( + value = "INVOKE", + target = "Ljava/io/PrintWriter;println(Ljava/lang/String;)V", + ordinal = 10, // after fancyGraphics option + shift = Shift.AFTER + ) + ) + private void osl$resource_loader$skinSkinOption(CallbackInfo ci, @Local PrintWriter writer) { + writer.println(String.format("%s:%s", SKIN_OPTION, this.skin)); + } + + @Override + public String osl$resource_loader$getSkin() { + return this.skin; + } + + @Override + public void osl$resource_loader$setSkin(String skin) { + this.skin = skin; + } +} diff --git a/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/mixin/client/MinecraftMixin.java b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/mixin/client/MinecraftMixin.java index 2180b9700..875e81fde 100644 --- a/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/mixin/client/MinecraftMixin.java +++ b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/java/net/ornithemc/osl/resource/loader/impl/mixin/client/MinecraftMixin.java @@ -1,28 +1,43 @@ package net.ornithemc.osl.resource.loader.impl.mixin.client; +import java.io.File; +import java.nio.file.Path; +import java.util.Collections; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import net.minecraft.client.Minecraft; +import net.minecraft.client.options.GameOptions; import net.minecraft.client.render.texture.TextureManager; import net.minecraft.client.render.world.WorldRenderer; import net.ornithemc.osl.resource.loader.api.client.MinecraftExtension; +import net.ornithemc.osl.resource.loader.api.resource.pack.ResourcePack; import net.ornithemc.osl.resource.loader.api.resource.reload.ResourceReloader; +import net.ornithemc.osl.resource.loader.api.resource.repository.ResourcePackSummary; import net.ornithemc.osl.resource.loader.impl.ClientResourcePacks; import net.ornithemc.osl.resource.loader.impl.ResourceLoader; +import net.ornithemc.osl.resource.loader.impl.TexturePack; +import net.ornithemc.osl.resource.loader.impl.TexturePackDetector; +import net.ornithemc.osl.resource.loader.impl.access.GameOptionsAccess; import net.ornithemc.osl.resource.loader.impl.resource.manager.SimpleReloadableResourceManager; import net.ornithemc.osl.resource.loader.impl.resource.repository.BundledModResourcePacks; +import net.ornithemc.osl.resource.loader.impl.resource.repository.DirectoryPackSource; import net.ornithemc.osl.resource.loader.impl.resource.repository.SimpleResourcePackRepository; @Mixin(Minecraft.class) public class MinecraftMixin implements MinecraftExtension { + @Shadow + private File gameDir; + @Shadow + private GameOptions options; @Shadow private TextureManager textureManager; @Shadow @@ -41,6 +56,8 @@ public class MinecraftMixin implements MinecraftExtension { ) ) private void osl$resource_loader$initPackRepository(CallbackInfo ci) { + Path dir = this.gameDir.toPath(); + this.packRepository = SimpleResourcePackRepository.client(); this.packRepository.reset(); @@ -48,10 +65,20 @@ public class MinecraftMixin implements MinecraftExtension { this.packRepository.addSource(new ClientResourcePacks()); this.packRepository.addSource(new BundledModResourcePacks()); -// this.packRepository.addSource(new DirectoryResourcePackSource()); // TODO + this.packRepository.addSource(new DirectoryPackSource(dir.resolve("texturepacks"), new TexturePackDetector())); this.packRepository.init(); this.packRepository.reload(); + + String skin = ((GameOptionsAccess) this.options).osl$resource_loader$getSkin(); + + for (ResourcePackSummary summary : this.packRepository.getAvailablePacks()) { + ResourcePack pack = summary.open(); + + if (pack instanceof TexturePack && pack.getName().equals(skin)) { + this.packRepository.setSelectedPacks(Collections.singleton(summary.getId())); + } + } } @Inject( diff --git a/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/resources/osl.resource-loader.mixins.json b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/resources/osl.resource-loader.mixins.json index bbcf178a1..f2a28e063 100644 --- a/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/resources/osl.resource-loader.mixins.json +++ b/libraries/resource-loader/resource-loader-mca1.0.1_01-mca1.2.1_01/src/main/resources/osl.resource-loader.mixins.json @@ -6,12 +6,14 @@ "mixins": [ ], "client": [ + "client.GameOptionsMixin", "client.MinecraftMixin", "client.TextureManagerMixin" ], "server": [ ], "injectors": { - "defaultRequire": 1 + "defaultRequire": 1, + "maxShiftBy": 2 } }