Can somebody please explain AssetManager for config files?

All I want to do is correctly create a configuration file, and people, as well as the documentation, tells me to use asset manager, yet the documentation is so confusing and I can’t get anywhere. First, I assume I need to inject PluginContainer because plugin in plugin.getAsset doesn’t exist. I’m not even sure if I’m doing that correctly because the examples for injection are horribly confusing… this is the best I could figure out:

@Inject
private PluginContainer plugin;

After I do that, I can use plugin.getAsset. Now I cannot figure out if it wants me to do this first:

    Asset asset = plugin.getAsset("myfile.txt").get();

or

    if (Files.notExists(configPath)) {
                plugin.getAsset("default.conf").copyToFile(configPath);
     }

because it says Asset asset = plugin.getAsset(“myfile.txt”).get(); is if you have the file, which I don’t. It also says the second piece of code can create one if you use the PluginContainer and that code, yet it doesn’t explain properly how to use PluginContainer.

If it’s not clear, I haven’t programmed much Java, so if this is common sense, I apologize. Could somebody please give me a better explanation or at least a full example that isn’t cut into a billion pieces like the documentation. Thank you.

EDIT: I forgot to mention, on the second piece of code, I cannot use plugin.getAsset(“default.conf”).copyToFile(configPath); because it creates an error saying it needs a “=” like what it does with Asset Asset =.

Okay, so there are a couple questions here.

First, you can inject your PluginContainer as you’ve shown just fine (if that’s within your @Plugin class), but you don’t have to. You can get the container for any plugin using Sponge.getPluginManager.getPlugin(id) (which returns an Optional<PluginContainer>, which is why it’s recommended to have in injected for you. What I personally do is inject it into the constructor of my @Plugin class (like this) - so you have a couple of options to work with.

Onto the AssetManager. The largest thing to know about how the AssetManager works is that it’s just a basic tool for getting a resource from your plugin (see ClassLoader#getResource). An asset is simply a wrapper around the URL returned by this method and provides a couple utility functions for working with them, but it is not in itself a file. The AssetManger is more particular, however, in that it can only read files from your assets folder - specifically assets/pluginid, which go in the resources folder (src/main/resources).

So, to answer your first question, should you check if the file does not exist first? Yes, however it can be done for you. The Files#copy method will throw an exception if the file exists and you have not indicated you wish to replace it. Thankfully, the Asset#copyToFile method allows you to specify if you would like to overwrite the file or only copy it if it does not exist (which is what you want for a default config). You should be able to accomplish what you need with:

plugin.getAsset("default.conf").get().copyToFile(configPath, false, true);
// where "default.conf" is the name of the file in your assets folder
// configPath is the path to the destination of the file
// 'false' states that an existing file should be overwritten
// `true` states that it should only be copied if the file does not exist.

With your final message, remember that getAsset returns an Optional<Asset>, not the asset itself. You need to call .get() first before calling any other methods (or however else you’d like to work with it). I simply call .get() because if I’m missing an asset then I have other issues xD

With that being said, here are the two main answer to your questions:

  • An Asset is a file that is stored in your jar under resources/assets/pluginid.
  • You need to check (directly or indirectly) if the file exists and handle that appropriately. You can do that yourself or delegate it to the copyToFile method; your choice.

If you’d like an example, you can take a look at the ActiveTime configuration (which has been a bit encapsulated) and it’s assets folder.

Thanks for the reply, I’m gonna look over this and try to figure this out after work tomorrow because this is still a lot to take in. I’ll see what I can solve and reply tomorrow. Thanks again.

Wait, will the configuration file created be editable in the server’s config folder? I’m doing some more research, but I’m not completely sure where it would put this file. It’s kinda sounding like it puts it in the jar file itself, which as far as I understand, won’t be editable by anybody other than the developers.

I want something that IS editable by a server manager, because eventually it’ll be connecting to a MySQL database, and they would need to put their info in the config file.

Correct, because they’re two separate files. The asset manager holds your default configuration file, which is then copied into the actual configuration file if it doesn’t exist. This allows you to setup default values automatically without setting a bunch of things through Configurate directly.

Huh, now it all makes sense. I had the wrong idea. So plugin.getAsset(“default.conf”) gets the URL to the default config (That needs to be created manually) and .copyToFile(configPath) copies the content to the actual read config file in the config folder on the server, right?

Now, I assume Configuring Plugins — Sponge 7.2.0 documentation actually creates the configuration file in the config folder? Now where do I learn how to manually structure the ASSET config file so the READ config file is readable when the content is copied? I would read the documentation further, but it’s worded so horribly.

I don’t use many of those configuration options for precisely that reason; I find I’m able to do what I need to easier on my own and it ends up clearer as well. Here’s how you’d go about setting up an asset:

PluginContainer container; //Should be stored in your @Plugin class
Path directory; //Easiest to inject this with @ConfigDir

Path path = directory.resolve("config.conf");
if (Files.notExists(path)) {
    Asset asset = container.getAsset("default-config.conf").get();
    asset.copyToFile(path); //this *creates* the file as well
}

//from there, normal configuration stuff
HoconConfigurationLoader loader = HoconConfigurationLoader.builder()
    .setPath(path)
    .build();
CommentedConfigurationNode root = loader.load();

Alternatively, you can also just get the asset and then copy it only if the actual config file isn’t present. There is a performance difference between the two, but the readability might be worth it to you (and during startup it’s not as important anyways).

container.getAsset("default-config.conf").get().copyToFile(path, false, true);
// override is false, onlyIfAbsent is true

Basically, all this is doing is creating your config file with a preset you’ve created that’s stored in the jar file. There’s no trickery or anything special going on with it; just a file copy.

That all seems simple, but what I mean by structure is, how do I create variables in the default config? That way the variables are readable when they are copied.

It’s just a configuration file, there’s nothing special you have to do for it as far as copying is concerned. You can take a look the crates.conf asset from TeslaCrate as an example.

If your question is more on how to load data saved on a file, then what you’re after is Configurate. Configurate is the library used by Sponge to transform a text file into structure of meaningful objects that can be used. It supports a variety of different formats, but HOCON (a superset of JSON) is the most common and is what I use personally.

The first part of your reply answers my question. Thank you for all your help, I really appreciate it.

1 Like

Here’s something you may find useful. It’s a utility class I wrote a while back to help make it easier on me for working with default configs and what not. Note, this doesn’t use the Asset manager or any pre-existing file at all, instead it’s based on Configurate’s ObjectMapper, and instead generates the file when the plugin starts ( or loads it, if it exists already ).