Skip to content

Commit

Permalink
refactor: jar plugin reloading
Browse files Browse the repository at this point in the history
  • Loading branch information
smartcmd committed Jul 14, 2024
1 parent ddc55e2 commit f8baa1b
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 61 deletions.
15 changes: 5 additions & 10 deletions Allay-API/src/main/java/org/allaymc/api/plugin/Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.allaymc.api.command.CommandRegistry;
import org.allaymc.api.i18n.I18n;
import org.allaymc.api.scheduler.Scheduler;
Expand All @@ -15,6 +16,7 @@
*/
@Getter
@Setter
@Slf4j
public abstract class Plugin implements TaskCreator {

protected PluginContainer pluginContainer;
Expand Down Expand Up @@ -50,18 +52,11 @@ public boolean isReloadable() {
}

/**
* Reload the plugin and load it again.
* This is different from simply calling onEnable()/onDisable().
* The default implementation of this method calls the onLoad() method to simulate the entire process of the plugin being read and enabled.
* <p>
* We do not allow this method to be overridden because for a plugin with good code quality, there should be no need to override this method.
* When the plugin reloading, call
*/
public final void reload() {
public void reload() {
if (!isReloadable()) throw new UnsupportedOperationException("This plugin is not a reloadable plugin!");
onDisable();
onUnload();
onLoad();
onEnable();
else log.warn("Plugin {} is marked as reloadable but do nothing in reload() method!", pluginContainer.descriptor().getName());
}

public Server getServer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ public boolean isReloadable() {
return true;
}

@Override
public void reload() {
onDisable();
onUnload();
onLoad();
onEnable();
}

protected void tryCallJsFunction(String onLoad) {
var func = jsExport.getMember(onLoad);
if (func != null && func.canExecute())
Expand Down
35 changes: 8 additions & 27 deletions docs/plugin_development/about_plugin_reloading.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,12 @@ Considering the above reasons, for a Jar plugin, we default that it cannot be ho
users to mark their plugins as "reloadable", which is achieved by overriding the `Plugin.isReloadable()` method and
returning `true`.

Please note that overriding the `Plugin.isReloadable()` method and returning `true` does not mean your plugin supports
hot reload. You still need to ensure that your code supports hot reload. Before introducing how to make your plugin
support hot reload, let's first see what the `Plugin.reload()` method does:

```java
public final void reload() {
if (!isReloadable()) throw new UnsupportedOperationException("This plugin is not a reloadable plugin!");
onDisable();
onUnload();
onLoad();
onEnable();
}
```

The `Plugin.reload()` method simulates the complete process of uninstalling and reloading the plugin. There used to be a
wonderful operation in the Nukkit community years ago. Since Nukkit does not lock plugin files for writing, users modify
Keep in mind that overriding the method 'Plugin.isReloadable()' and returning 'true' doesn't mean that your plugin supports hot reloading.
You still need to make sure your code supports hot reloading.
You'll need to override the 'Plugin.reload() method, and the details will be introduced later.
If you only override the method 'Plugin.isReloadable() without overwriting 'Plugin.reload()', the console will output a warning

There used to be a wonderful operation in the Nukkit community years ago. Since Nukkit does not lock plugin files for writing, users modify
the code, recompile it, replace the original jar file with the new one, and call the plugin's `reload()` method. The new
code miraculously takes effect. I admit that this is indeed very convenient (in fact, I have done it myself), but in
Allay, Jar plugins are not allowed to do so.
Expand All @@ -42,17 +32,8 @@ Remember, for Jar plugins, calling the `reload()` method **does not mean it is a

## Making Your Plugin Support Hot Reload

**Ensure that operations related to plugin functionality are in the `onEnable()` method**, such as:

- Registering listeners
- Registering scheduled tasks
- Registering commands
- ...

**Ensure that all content registered by the plugin is unregistered in the `onDisable()` method**.

Even if the above is followed, we still cannot guarantee that your plugin can be hot reloaded correctly. **For Jar
plugins, the best way is not to support hot reload**, as restarting the server does not take much time.
- Override the method 'Plugin.isReloadable() and return 'true'.
- Override the method 'Plugin.reload()'. For simple plug-ins, the most common action is to re-read the plug-in configuration file.

## Hot Reload for JavaScript Plugins

Expand Down
31 changes: 7 additions & 24 deletions docs/plugin_development/about_plugin_reloading.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,22 @@ comments: true
- 不编写插件卸载逻辑,导致插件卸载后此插件注册的监听器,命令,定时任务依旧可用
- ...

结合以上原因,对于一个Jar插件,我们默认其不能热重载。尽管如此,我们依然允许用户将其插件标记为”可热重载的“,这通过覆写方法`Plugin.isReloadable()`并返回`true`实现
结合以上原因,对于一个Jar插件,我们默认其不能热重载。尽管如此,我们依然允许用户将其插件标记为”可热重载的“,
这通过覆写方法`Plugin.isReloadable()`并返回`true`实现

请记住,覆写了方法`Plugin.isReloadable()`并返回`true`之后并不代表你的插件就支持热重载。你仍需要确保你的代码支持热重载。
在介绍如何让你的插件支持热重载之前,我们先来看看`Plugin.reload()`方法会做什么:

```java
public final void reload() {
if (!isReloadable()) throw new UnsupportedOperationException("This plugin is not a reloadable plugin!");
onDisable();
onUnload();
onLoad();
onEnable();
}
```

`Plugin.reload()`方法会模拟插件完全卸载再加载的全过程。
你需要覆写方法`Plugin.reload()`并在此方法内完成热重载工作,具体细节将在稍后说明
若仅仅覆写了方法`Plugin.isReloadable()`而没有覆写`Plugin.reload()`,控制台将输出一段警告

早些年在Nukkit圈有一种奇妙的操作,由于Nukkit不会对插件文件加写锁,用户修改代码并重新编译后 用新的jar文件覆盖原来的jar文件,
并调用插件的`reload()`方法,新的代码会奇迹般的奏效。我承认这确实很方便(事实上我也干过),但是在Allay中Jar插件不允许这么干。

切记,对于Jar插件,调用`reload()`方法**并不代表它真的被从文件重新加载了**

## 让你的插件支持热重载

**确保与插件功能有关的操作位于`onEnable()`方法**,例如:

- 注册监听器
- 注册定时任务
- 注册命令
- ...

**确保在`onDisable()`方法解注册了插件注册的所有内容**

即使遵守了以上内容,我们仍然不能保证你的插件可以正确热重载。**对于Jar插件,最好的办法就是不支持热重载**,重新开服并不需要耗费多少时间。
- 覆写方法`Plugin.isReloadable()`并返回`true`
- 覆写方法`Plugin.reload()`。对于简单的插件,最常见的操作就是重新读取插件配置文件。

## JavaScript插件的热重载

Expand Down

0 comments on commit f8baa1b

Please sign in to comment.