This is a very simple Java tool for games and other apps to manage resources.
The current version is 1.1
. This version is compatible with Java 17 and above.
The artifact can be installed from my Maven repository.
// Kotlin DSL:
repositories {
// Add my repository
maven { url = uri("https://maven.shadew.net/") }
}
dependencies {
// Add the artifact
implementation("dev.runefox:rms:1.1")
}
// Groovy DSL:
repositories {
// Add my repository
maven { url "https://maven.shadew.net/" }
}
dependencies {
// Add the artifact
implementation "dev.runefox:rms:1.1"
}
<repositories>
<!-- Add my repository -->
<repository>
<id>Runefox Maven</id>
<url>https://maven.shadew.net/</url>
</repository>
</repositories>
<dependencies>
<!-- Add the artifact -->
<dependency>
<groupId>dev.runefox</groupId>
<artifactId>rms</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
There are 3 core components to the system:
ResourceManager
Resource
ResourceType
You need at the very least a ResourceManager
, which is a concrete class you can simply instantiate:
ResourceManager mgr = new ResourceManager(path, "namespace");
There are 2 main things you need to create the resource manager:
- A
Path
, to the root directory that contains all your resources. - A "standard namespace", which is the main namespace of your application. This is usually one identifier, like
rms
.
There is a special static method on ResourceManager
that allows you to get a Path
for a resource folder on the class path. This method uses the ClassLoader.getResource
method, which needs a resource file in order to work. So, to get the resources from your jar file, you need to define some empty file in the root directory of your resources folder, and call ResourceManager.classpath("/.resources_root")
if it's named .resources_root
. The returned path is then the parent directory of that file, possibly inside of a jar file.
ResourceManager mgr = new ResourceManager(ResourceManager.classpath(".resources_root"), "namespace");
Whenever you're done with your resources, or need to reload all of them, call ResourceManager.dispose
.
The next step is to create a Resource
implementation. Resource
is an interface you can implement and it just has one method: dispose
. This method is called by the ResourceManager
upon ResourceManager.dispose
. Usually, this method does nothing, but if your resource acquires some native resources (like off-heap memory) that must be cleaned up, then this is the place to do that. This is a resource that just contains a string:
public class ContentResource implements Resource {
private final String content;
public ContentResource(String content) {
this.content = content;
}
public String content() {
return content;
}
@Override
public void dispose() {
// Not applicable
}
}
Once you've got a Resource
implementation, you have to define a way this resource is loaded. The ResourceManager
assumes this resource to be in a certain folder, so all you have to do is locate it inside of that folder, read the files that must be read and turn them into your resource. Typically, the resource is in a file with the same name as the name of the resource. Resources may have path names, so you can group similar resources of the same type into a subfolder.
The loading of resources from the filesystem is done by a ResourceType
. The ResourceType
interface has two methods: load
and createFallback
. First load
is called to locate and load the resource, and if that fails, createFallback
should be able to create some kind of fallback in case the resource fails loading. For above example we could create the following resource type:
public class ContentResourceType implements ResourceType<ContentResource> {
@Override
public ContentResource load(ResourceManager res, String ns, String name, Path directory) throws Exception {
// The `directory` parameter already accounts for the namespace, so you don't need to incorporate the
// namespace into locating the resource.
try (BufferedReader in = Files.newBufferedReader(directory.resolve(name + ".txt"), StandardCharsets.UTF_8)) {
StringWriter writer = new StringWriter();
in.transferTo(writer);
return new ContentResource(writer.toString());
}
}
@Override
public ContentResource createFallback(ResourceManager res, String ns, String name) {
// The fallback should be created from memory, it should not load from a file.
return new ContentResource("[Failed loading.]");
}
}
You may, however, encounter resources made of text in this specific structure quite often. There are 3 common interfaces that extend ResourceType
which ease the loading of single-file resources (which are resources of just one file which has the name of the resource):
FileResourceType
is a type for loading binary files: it provides you with anInputStream
that streams the contents of the file.TextResourceType
is a type for loading text files: it provides you with aReader<