CloudburstMC / Nukkit

Cloudburst Nukkit - Nuclear-Powered Minecraft: Bedrock Edition Server Software
https://cloudburstmc.org
GNU General Public License v3.0
1.21k stars 419 forks source link

dataFolder is not honored during reloadConfig #865

Open ZizzyZizzy opened 5 years ago

ZizzyZizzy commented 5 years ago

Expected Behavior

All plugin functions functions should honor dataFolder.

Actual Behavior

Even though the plugin itself is reporting the correct dataFolder, either utils.Config or plugin.PluginBase.reloadConfig is getting the folder based on the location from which the plugin .jar was loaded.

Steps to Reproduce

Create a plugin that loads "other" plugins from a different folder.

After loading the plugin, use reflection to alter dataFolder. In this case, I'm altering the dataFolder from a shared folder where the plugin.jar is located to the server's normal /plugin/ folder.

However, one of the Nukkit config related functions is not honoring the dataFolder, but others are. For example, when the custom plugin loader first loads the "other" plugin, the plugin's data folder and config.yml ARE created as expected and in the correct location. When getConfig is called, suddenly the dataFolder has reverted to the location from where the plugin.jar was loaded.

NOTE: This works perfectly with Spigot using almost the exact same code.

                Plugin plugin = pluginManager.loadPlugin(pluginFile);
                if(setDataFolder(plugin, newDataFolder)) {
                    plugin.onLoad();
                    pluginManager.enablePlugin(plugin);
                }

    private boolean setDataFolder(Plugin plugin, String dataFolderPath) {
        try {
            if(plugin instanceof PluginBase) {
                PluginBase jpl = (PluginBase) plugin;
                File dataFolder = new File(dataFolderPath);
                Field field = PluginBase.class.getDeclaredField("dataFolder");
                field.setAccessible(true);
                field.set(jpl, dataFolder);
                File newDataFolder = plugin.getDataFolder();
                return true;
            }
        } catch (IllegalArgumentException | IllegalAccessException exception) {
            getLogger().log(LogLevel.CRITICAL, " Could not set Data folder!");
            return false;
        }
        return false;
    }

Debug information

NOTE: [MY PLUGIN] output below is in the FIRST line in the MyTestPlugin's onEnable function, proving that when the plugin is enabled the dataFolder has been changed as expected.

Enabling MyTestPlugin v1.1.0
[MY PLUGIN] Data folder = /home/multicraft/servers/server5/plugins/MyTestPlugin
ERROR Could not create Config /home/multicraft/shared/MyTestPlugin/config.yml
java.io.IOException: No such file or directory
at java.io.UnixFileSystem.createFileExclusively(Native Method) ~[?:1.8.0_221]
at java.io.File.createNewFile(File.java:1012) ~[?:1.8.0_221]
at cn.nukkit.utils.Config.load(Config.java:135) [Nukkit.jar:?]
at cn.nukkit.utils.Config.<init>(Config.java:98) [Nukkit.jar:?]
at cn.nukkit.utils.Config.<init>(Config.java:85) [Nukkit.jar:?]
at cn.nukkit.utils.Config.<init>(Config.java:81) [Nukkit.jar:?]
at cn.nukkit.plugin.PluginBase.reloadConfig(PluginBase.java:239) [Nukkit.jar:?]
at cn.nukkit.plugin.PluginBase.getConfig(PluginBase.java:218) [Nukkit.jar:?]
at com.mysite.testplugin.MyTestPlugin.onEnable(MyTestPlugin.java:58) [MyTestPlugin_v1.1.0.jar:?]
at cn.nukkit.plugin.PluginBase.setEnabled(PluginBase.java:89) [Nukkit.jar:?]
at cn.nukkit.plugin.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:117) [Nukkit.jar:?]
at cn.nukkit.plugin.PluginManager.enablePlugin(PluginManager.java:439) [Nukkit.jar:?]
at com.mysite.pluginloader.CustomPluginLoader.loadPlugins(CustomPluginLoader.java:131) [CustomPluginLoader.jar:?]
at com.mysite.pluginloader.CustomPluginLoader.onEnable(CustomPluginLoader.java:46) [CustomPluginLoader.jar:?]
at cn.nukkit.plugin.PluginBase.setEnabled(PluginBase.java:89) [Nukkit.jar:?]

Checklist:

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/79666788-datafolder-is-not-honored-during-reloadconfig?utm_campaign=plugin&utm_content=tracker%2F80651332&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F80651332&utm_medium=issues&utm_source=github).
ZizzyZizzy commented 5 years ago

I'm not 100% sure, but this looks like it could be the culprit:

    public boolean load(String file, int type, ConfigSection defaultMap) {
        this.correct = true;
        this.type = type;
        this.file = new File(file);
        if (!this.file.exists()) {
            try {
                this.file.getParentFile().mkdirs();
                this.file.createNewFile();
            } catch (IOException var7) {
                MainLogger.getLogger().error("Could not create Config " + this.file.toString(), var7);
            }

In particular, this line:

            this.file.getParentFile().mkdirs();

If you chase the rabbit down the rabbit hole, there is no mention of dataFolder. It appears to just use the path relative to the location of the loaded plugin.

Somewhere between saveDefaultConfig and getConfig, the dataFolder is either ignored or reverted to the location of the loaded plugin.

Here are the first three lines of the test plugin's onEnable function:

this.getServer().getLogger().info("[MY PLUGIN] Data folder = " + this.getDataFolder());
this.saveDefaultConfig();
pluginConfig = getConfig();

The call to saveDefaultConfig DOES work properly! The default config.yml is placed in the plugin's data folder under the /plugins/ folder of the server as expected. getConfig seems to be where it breaks.

ZizzyZizzy commented 5 years ago

After a bunch of debugging, I found the cause and a work-around.

I was changing the plugin dataFolder with reflection after the plugin was loaded, which was sufficient for how Spigot loads plugins. Nukkit looks similar but does things a little differently.

When the plugin was actually loaded by Nukkit, the configFile had already been set by the init function (PluginBase.class:79) using a dataFolder path that was set to the folder where the plugin .jar was loaded. That means setting only dataFolder after the plugin loads is not sufficient for Nukkit.

this.configFile = new File(this.dataFolder, "config.yml");

The work-around was to set both the dataFolder and configFile using reflection. That fixed my problem, but may not be a 100% solution.