Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package net.ornithemc.osl.core.api.util;

public enum Unit {

INSTANCE

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package net.ornithemc.osl.core.api.util.function;

import java.io.IOException;

public interface IOSupplier<T> {

T get() throws IOException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package net.ornithemc.osl.core.impl.util;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

public final class Util {

public static <V> CompletableFuture<List<V>> sequence(List<? extends CompletableFuture<? extends V>> futures) {
List<V> results = new ArrayList<>(futures.size());

CompletableFuture<?>[] sequence = new CompletableFuture[futures.size()];
CompletableFuture<Void> failure = new CompletableFuture<>();

futures.forEach(future -> {
int i = results.size();
results.add(null);

sequence[i] = future.whenComplete((result, exception) -> {
if (exception != null) {
failure.completeExceptionally(exception);
} else {
results.set(i, result);
}
});
});

return CompletableFuture.allOf(sequence).applyToEither(failure, v -> results);
}
}
203 changes: 202 additions & 1 deletion libraries/resource-loader/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,204 @@
# Resource Loader API

The Resource Loader API allows mods to load their own resources into the game. The mod's resources are wrapped in a "built-in resource pack" which allows the game to find them.
The Resource Loader API allows mods to load their own resources into the game. On top of that, it provides its own access layer for resource packs and resource management.

## Loading Your Mod's Resources

Mods do not need to manually load their resources into the game. OSL wraps each mod's resources in a resource pack and loads all those resource packs in under the `fabric-mod-resources` pack. You can check that this is working by opening the Resource Packs screen and checking that `fabric-mod-resources` appears in the selected packs list.

## Resource Loader Events

The API provides several events for both the client and server, for initializing resource management and listening for resource reloads. Event listeners for these events should be registered in your mod's entrypoint.

```java
package com.example;

import net.ornithemc.osl.entrypoints.api.client.ClientModInitializer;
import net.ornithemc.osl.resource.loader.api.client.ClientResourceLoaderEvents;

public class ExampleInitializer implements ClientModInitializer {

@Override
public void initClient() {
ClientResourceLoaderEvents.INIT_RESOURCE_PACK_REPOSITORY.register(packRepository -> {
// register resource pack repository sources here
});
ClientResourceLoaderEvents.INIT_RESOURCE_MANAGER.register(resourceManager -> {
// register resource reloaders and reload listeners here
});
ClientResourceLoaderEvents.START_RESOURCE_PACKS_RELOAD.register(() -> {
// this code is run before the resource pack repository is reloaded
});
ClientResourceLoaderEvents.START_RESOURCE_RELOAD.register(() -> {
// this code is run before a resource reload is started
});
}
}
```

## Bundled Mod Resource Packs

The API provides a way for mods to provide multiple bundled resource packs. You can do this by using sub-directories in your project resources. This is useful if you want to separate client assets from server data, or if you want to organize resources into bundles for specific feature sets or project modules.

To ensure these resources are loaded properly, register them in your mod's entrypoint.

```java
package com.example;

import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;

import net.ornithemc.osl.entrypoints.api.client.ClientModInitializer;
import net.ornithemc.osl.resource.loader.api.resource.repository.ResourcePackRepository;

public class ExampleInitializer implements ClientModInitializer {

private static final ModContainer MOD = FabricLoader.getInstance().getModContainer("example").get()

@Override
public void initClient() {
ResourcePackRepository.registerBundledModResources("cookie-assets", MOD, "client/cookies/")
}
}
```

## Resource Pack Repositories

The client and server each have their own resource pack repositories. You can add a custom source to load in custom resource packs.

```java
package com.example;

import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;

import net.ornithemc.osl.entrypoints.api.client.ClientModInitializer;
import net.ornithemc.osl.resource.loader.api.client.ClientResourceLoaderEvents;

public class ExampleInitializer implements ClientModInitializer {

private static final ModContainer MOD = FabricLoader.getInstance().getModContainer("example").get()

@Override
public void initClient() {
ClientResourceLoaderEvents.INIT_RESOURCE_PACK_REPOSITORY.register(packRepository -> {
packRepository.addSource(new ExampleRepositorySource());
});
}
}
```

```java
package com.example;

import java.util.function.Consumer;

import net.ornithemc.osl.resource.loader.api.resource.pack.ResourcePack;
import net.ornithemc.osl.resource.loader.api.resource.repository.ResourcePackRepository;

public class ExampleRepositorySource implements ResourcePackRepository.Source {

@Override
public void loadResourcePacks(Consumer<ResourcePack> consumer) {
// Load your custom resource packs here.
// These can be virtual resource packs, or packs loaded
// from a special file or directory.
...
}
}
```


## Resource Management

The client and server each has their own resource manager. You can use this to access resources and register resource reloaders or reload listeners.


```java
ResourceManager resourceManager = ResourceManager.client();

// Get resource as an InputStream using a direct String path.
// Use this to access Vanilla resources in versions before 1.6.
// For custom resources you add, it is recommended to lay them
// out in namespaced directories like Vanilla in 1.6 and later.
resourceManager.getResource("/path/to/resource");

// Get resource as an Optional<Resource> using a namespaced location.
// Use this to access Vanilla resources in versions 1.6 and later.
// It is recommended to lay out custom resources in namespaced
// directories for improved compatibility and extra features.
resourceManager.getResource(NamespacedIdentifiers.from("example", "path/to/resource"));

// Get all resources as a Map<NamespacedIdentifier, Resource>
// in the specified directory that match the specified filter.
// NOTE: the specified directory is not resolved from the root
// of the resource packs, but from the namespaced directories!
// For example: a file at /directory/to/resources/example.json
// will NOT be found, but
// a file at `/assets/example/directory/to/resources/example.json
// WILL be found.`
resourceManager.findResources("directory/to/resources/", location -> location.path().endsWith(".json"));
```

```java
package com.example;

import net.ornithemc.osl.entrypoints.api.client.ClientModInitializer;
import net.ornithemc.osl.resource.loader.api.client.ClientResourceLoaderEvents;

public class ExampleInitializer implements ClientModInitializer {

@Override
public void initClient() {
ClientResourceLoaderEvents.INIT_RESOURCE_MANAGER.register(resourceManager -> {
resourceManager.addReloader(new CookiesManager());
resourceManager.addReloader(new ExampleReloadListener());
});
}
}
```

```java
package com.example;

import net.ornithemc.osl.resource.loader.api.resource.manager.ResourceManager;
import net.ornithemc.osl.resource.loader.api.resource.reload.SimpleResourceReloader;

public class CookiesManager implements SimpleResourceReloader<Cookies> {

private Cookies cookies;

@Override
public Cookies reloadResources(ResourceManager manager) {
// Load resources from the resource manager here, parse them
// as necessary, and package them up. This code may not run
// on the main game thread, so it is imperative that you do
// not modify the world or render state here!
Cookies cookies = ...
return cookies;
}

@Override
public void applyResources(Cookies cookies, ResourceManager manager) {
// Use the resources you loaded, and parsed above to modify
// the world or render state as necessary.
this.cookies = cookies;
}
}
```

```java
package com.example;

import net.ornithemc.osl.resource.loader.api.resource.manager.ResourceManager;
import net.ornithemc.osl.resource.loader.api.resource.reload.ResourceReloadListener;

public class ExampleReloadListener implements ResourceReloadListener {

@Override
public void resourcesReloaded(ResourceManager manager) {
//
...
}
}
```
4 changes: 4 additions & 0 deletions libraries/resource-loader/build.gradle
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
setUpLibrary(project)

dependencies {
implementation 'com.google.code.gson:gson:2.8.0'
}
2 changes: 1 addition & 1 deletion libraries/resource-loader/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ library_name = Resource Loader
library_description = Resource loading API and events.
library_version = 0.6.1

osl_dependencies = core:>=0.7.0
osl_dependencies = core:>=0.7.0,executors:>=0.1.0,text-components:>=0.1.0-
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
environment = client
min_mc_version = 1.8.2-pre5
max_mc_version = 1.12.2
minecraft_dependency = >=1.8.2-rc.5 <=1.12.2

minecraft_version = 1.12.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package net.ornithemc.osl.resource.loader.impl;

import java.util.function.Consumer;

import net.minecraft.client.resource.pack.ResourcePacks;

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.adapter.WrappedResourcePack;

public class ClientResourcePacks implements ResourcePackRepository.Source {

private final ResourcePacks resourcePacks;

public ClientResourcePacks(ResourcePacks resourcePacks) {
this.resourcePacks = resourcePacks;
}

@Override
public void loadResourcePacks(Consumer<ResourcePackSummary> consumer) {
ResourcePack pack = new WrappedResourcePack(this.resourcePacks.defaultPack);
ResourcePackSummary summary = ResourcePackSummary.create(
pack,
true,
false,
PackPosition.BOTTOM
);

consumer.accept(summary);

if (this.resourcePacks.getServerPack() != null) {
ResourcePack serverPack = new WrappedResourcePack(this.resourcePacks.getServerPack());
ResourcePackSummary serverSummary = ResourcePackSummary.create(
serverPack,
true,
true,
PackPosition.TOP
);

if (serverSummary != null) {
consumer.accept(serverSummary);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package net.ornithemc.osl.resource.loader.impl.adapter;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import net.minecraft.client.resource.metadata.ResourceMetadataSerializerRegistry;
import net.minecraft.resource.Identifier;

import net.ornithemc.osl.core.api.util.NamespacedIdentifier;
import net.ornithemc.osl.resource.loader.api.resource.Resource;
import net.ornithemc.osl.resource.loader.api.resource.pack.ResourcePack;

class Adapters {

static Identifier identifier(NamespacedIdentifier id) {
return id instanceof Identifier ? (Identifier) id : new Identifier(id.namespace(), id.identifier());
}

static net.minecraft.client.resource.Resource resource(ResourceMetadataSerializerRegistry metadataSerializers, Resource resource) throws IOException {
return new ResourceAdapter(metadataSerializers, resource);
}

static net.minecraft.client.resource.Resource resource(ResourceMetadataSerializerRegistry metadataSerializers, NamespacedIdentifier location, Optional<Resource> resource) throws IOException {
if (resource.isPresent()) {
return resource(metadataSerializers, resource.get());
}

throw new FileNotFoundException(location.toString());
}

static List<net.minecraft.client.resource.Resource> resources(ResourceMetadataSerializerRegistry metadataSerializers, List<Resource> resources) throws IOException {
List<net.minecraft.client.resource.Resource> rs = new ArrayList<>();

for (Resource resource : resources) {
rs.add(resource(metadataSerializers, resource));
}

return Collections.unmodifiableList(rs);
}

static net.minecraft.client.resource.pack.ResourcePack resourcePack(ResourcePack pack) {
if (pack instanceof WrappedResourcePack) {
return ((WrappedResourcePack) pack).pack;
} else {
return new ResourcePackAdapter(pack);
}
}

static ResourcePack resourcePack(net.minecraft.client.resource.pack.ResourcePack pack) {
if (pack instanceof ResourcePackAdapter) {
return ((ResourcePackAdapter) pack).pack;
} else {
return new WrappedResourcePack(pack);
}
}

static List<net.minecraft.client.resource.pack.ResourcePack> packs(List<ResourcePack> packs) {
return packs.stream().map(Adapters::resourcePack).collect(Collectors.toList());
}

static List<ResourcePack> resourcePacks(List<net.minecraft.client.resource.pack.ResourcePack> packs) {
return packs.stream().map(Adapters::resourcePack).collect(Collectors.toList());
}
}
Loading
Loading