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
Expand Up @@ -4,11 +4,10 @@
import joptsimple.OptionSet;
import joptsimple.OptionSpec;

import net.fabricmc.loader.api.FabricLoader;

import net.ornithemc.osl.branding.api.BrandingContext;
import net.ornithemc.osl.branding.api.BrandingPatchEvents;
import net.ornithemc.osl.branding.api.Operation;
import net.ornithemc.osl.core.impl.util.MinecraftVersion;
import net.ornithemc.osl.entrypoints.api.client.ClientModInitializer;
import net.ornithemc.osl.entrypoints.api.launch.LaunchEvents;
import net.ornithemc.osl.entrypoints.api.launch.OptionsConsumer;
Expand All @@ -21,7 +20,7 @@ public class BrandingPatchImpl implements ClientModInitializer {

public static String getGameVersion() {
if (gameVersion == null) {
gameVersion = FabricLoader.getInstance().getRawGameVersion();
gameVersion = MinecraftVersion.resolve().gameVersion();

if (gameVersion.charAt(0) == 'a') {
gameVersion = "Alpha v" + gameVersion.substring(1);
Expand Down
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,77 @@
package net.ornithemc.osl.core.impl.util;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.SemanticVersion;
import net.fabricmc.loader.api.Version;
import net.fabricmc.loader.api.VersionParsingException;
import net.fabricmc.loader.impl.game.minecraft.McVersionLookup;

public final class MinecraftVersion {

private static MinecraftVersion INSTANCE;

public static MinecraftVersion resolve() {
if (INSTANCE == null) {
Optional<ModContainer> mod = FabricLoader.getInstance().getModContainer("minecraft");

if (!mod.isPresent()) {
throw new IllegalStateException("Minecraft not loaded!");
}

ModContainer minecraft = mod.get();
String gameVersion = FabricLoader.getInstance().getRawGameVersion();
SemanticVersion semanticVersion = resolveSemanticVersion(minecraft.getMetadata().getVersion());

INSTANCE = new MinecraftVersion(gameVersion, semanticVersion);
}

return INSTANCE;
}

private final String gameVersion;
private final SemanticVersion semanticVersion;

// semantic versions for other game versions
private final Map<String, SemanticVersion> semanticVersions = new HashMap<>();

private MinecraftVersion(String gameVersion, SemanticVersion semanticVersion) {
this.gameVersion = gameVersion;
this.semanticVersion = semanticVersion;
}

public String gameVersion() {
return this.gameVersion;
}

public String semanticVersion() {
return this.semanticVersion.toString();
}

public int compareTo(String gameVersion) {
return this.semanticVersion.compareTo((Version) this.semanticVersions.computeIfAbsent(gameVersion, MinecraftVersion::resolveSemanticVersion));
}

private static SemanticVersion resolveSemanticVersion(Version gameVersion) {
if (gameVersion instanceof SemanticVersion) {
return (SemanticVersion) gameVersion;
} else {
return resolveSemanticVersion(gameVersion.toString());
}
}

private static SemanticVersion resolveSemanticVersion(String gameVersion) {
String releaseVersion = McVersionLookup.getRelease(gameVersion);
String normalizedVersion = McVersionLookup.normalizeVersion(gameVersion, releaseVersion);

try {
return SemanticVersion.parse(normalizedVersion);
} catch (VersionParsingException e) {
throw new IllegalStateException("Unable to parse Minecraft version " + gameVersion, e);
}
}
}
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", "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
Loading
Loading