From e7f30f4d43ff4636e0e6bed5e87b25a65cd35bf8 Mon Sep 17 00:00:00 2001 From: KingRainbow44 Date: Wed, 31 May 2023 22:20:01 -0400 Subject: [PATCH] Require plugins to specify an API version and match with the server --- plugin-schema.json | 127 +++++++++--------- .../emu/grasscutter/plugin/PluginConfig.java | 4 +- .../emu/grasscutter/plugin/PluginManager.java | 48 ++++--- src/main/resources/languages/en-US.json | 6 +- src/main/resources/languages/es-ES.json | 6 +- src/main/resources/languages/fr-FR.json | 6 +- src/main/resources/languages/it-IT.json | 6 +- src/main/resources/languages/ja-JP.json | 6 +- src/main/resources/languages/ko-KR.json | 6 +- src/main/resources/languages/pl-PL.json | 6 +- src/main/resources/languages/ro-RO.json | 6 +- src/main/resources/languages/ru-RU.json | 6 +- src/main/resources/languages/zh-CN.json | 6 +- src/main/resources/languages/zh-TW.json | 6 +- 14 files changed, 153 insertions(+), 92 deletions(-) diff --git a/plugin-schema.json b/plugin-schema.json index fad282da0..fe0a5f8d4 100644 --- a/plugin-schema.json +++ b/plugin-schema.json @@ -1,63 +1,68 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON schema for a Grasscutter Plugin", - "type": "object", - "additionalProperties": true, - "definitions": { - "plugin-name": { - "type": "string", - "pattern": "^[A-Za-z\\d_.-]+$" - } - }, - "required": [ - "name", - "description", - "mainClass" - ], - "properties": { - "name": { - "description": "The unique name of plugin.", - "$ref": "#/definitions/plugin-name" - }, - "mainClass": { - "description": "The plugin's initial class file.", - "type": "string", - "pattern": "^(?!org\\.bukkit\\.)([a-zA-Z_$][a-zA-Z\\d_$]*\\.)*[a-zA-Z_$][a-zA-Z\\d_$]*$" - }, - "version": { - "description": "A plugin revision identifier.", - "type": [ - "string", - "number" - ] - }, - "description": { - "description": "Human readable plugin summary.", - "type": "string" - }, - "author": { - "description": "The plugin author.", - "type": "string" - }, - "authors": { - "description": "The plugin contributors.", - "type": "array", - "items": { - "type": "string" - } - }, - "website": { - "title": "Website", - "description": "The URL to the plugin's site", - "type": "string", - "format": "uri" - }, - "loadAfter": { - "description": "Plugins to load before this plugin.", - "type": "array", - "items": { - "type": "string" - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "JSON schema for a Grasscutter Plugin", + "type": "object", + "additionalProperties": true, + "definitions": { + "plugin-name": { + "type": "string", + "pattern": "^[A-Za-z\\d_.-]+$" } -} \ No newline at end of file + }, + "required": [ + "name", + "description", + "mainClass", + "api" + ], + "properties": { + "name": { + "description": "The unique name of plugin.", + "$ref": "#/definitions/plugin-name" + }, + "mainClass": { + "description": "The plugin's initial class file.", + "type": "string", + "pattern": "^(?!org\\.bukkit\\.)([a-zA-Z_$][a-zA-Z\\d_$]*\\.)*[a-zA-Z_$][a-zA-Z\\d_$]*$" + }, + "version": { + "description": "A plugin revision identifier.", + "type": [ + "string", + "number" + ] + }, + "api": { + "description": "The API revision the plugin is using.", + "type": "number" + }, + "description": { + "description": "Human readable plugin summary.", + "type": "string" + }, + "author": { + "description": "The plugin author.", + "type": "string" + }, + "authors": { + "description": "The plugin contributors.", + "type": "array", + "items": { + "type": "string" + } + }, + "website": { + "title": "Website", + "description": "The URL to the plugin's site", + "type": "string", + "format": "uri" + }, + "loadAfter": { + "description": "Plugins to load before this plugin.", + "type": "array", + "items": { + "type": "string" + } + } + } +} diff --git a/src/main/java/emu/grasscutter/plugin/PluginConfig.java b/src/main/java/emu/grasscutter/plugin/PluginConfig.java index bdda5aa84..e9a0f95fa 100644 --- a/src/main/java/emu/grasscutter/plugin/PluginConfig.java +++ b/src/main/java/emu/grasscutter/plugin/PluginConfig.java @@ -4,6 +4,7 @@ package emu.grasscutter.plugin; public final class PluginConfig { public String name, description, version; public String mainClass; + public Integer api; public String[] authors; public String[] loadAfter; @@ -14,6 +15,7 @@ public final class PluginConfig { */ @SuppressWarnings("BooleanMethodIsAlwaysInverted") public boolean validate() { - return name != null && description != null && mainClass != null; + return name != null && description != null && + mainClass != null && api != null; } } diff --git a/src/main/java/emu/grasscutter/plugin/PluginManager.java b/src/main/java/emu/grasscutter/plugin/PluginManager.java index 3ad68ea84..51e3413e6 100644 --- a/src/main/java/emu/grasscutter/plugin/PluginManager.java +++ b/src/main/java/emu/grasscutter/plugin/PluginManager.java @@ -1,28 +1,28 @@ package emu.grasscutter.plugin; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.server.event.*; +import emu.grasscutter.utils.*; +import lombok.*; + +import javax.annotation.Nullable; +import java.io.*; +import java.lang.reflect.Method; +import java.net.*; +import java.util.*; +import java.util.jar.*; + import static emu.grasscutter.utils.lang.Language.translate; -import emu.grasscutter.Grasscutter; -import emu.grasscutter.server.event.Event; -import emu.grasscutter.server.event.EventHandler; -import emu.grasscutter.utils.FileUtils; -import emu.grasscutter.utils.JsonUtils; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.InputStreamReader; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.*; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import javax.annotation.Nullable; -import lombok.AllArgsConstructor; -import lombok.Getter; - /** Manages the server's plugins and the event system. */ public final class PluginManager { + /* + * This should only be changed when a breaking change is made to the plugin API. + * A 'breaking change' is something which changes the existing logic of the API. + */ + @SuppressWarnings("FieldCanBeLocal") + private static int API_VERSION = 2; + /* All loaded plugins. */ private final Map plugins = new LinkedHashMap<>(); /* All currently registered listeners per plugin. */ @@ -78,6 +78,16 @@ public final class PluginManager { // Create a plugin config instance from the config file. PluginConfig pluginConfig = JsonUtils.loadToClass(fileReader, PluginConfig.class); + // Check the plugin's API version. + if (pluginConfig.api == null) { + Grasscutter.getLogger().warn(translate("plugin.invalid_api.not_present", plugin.getName())); + return; + } else if (pluginConfig.api != API_VERSION) { + Grasscutter.getLogger().warn(translate("plugin.invalid_api.lower", + plugin.getName(), pluginConfig.api, API_VERSION)); + return; + } + // Check if the plugin config is valid. if (!pluginConfig.validate()) { Grasscutter.getLogger().warn(translate("plugin.invalid_config", plugin.getName())); diff --git a/src/main/resources/languages/en-US.json b/src/main/resources/languages/en-US.json index 09b84b835..ec190e15c 100644 --- a/src/main/resources/languages/en-US.json +++ b/src/main/resources/languages/en-US.json @@ -460,6 +460,10 @@ "enabling_plugin": "Enabling plugin: %s", "enabling_failed": "Failed to enable plugin: %s", "disabling_plugin": "Disabling plugin: %s", - "disabling_failed": "Failed to disable plugin: %s" + "disabling_failed": "Failed to disable plugin: %s", + "invalid_api": { + "not_present": "Plugin %s does not specify an API version.", + "lower": "Plugin %s is using API version %s, while the server is using API version %s." + } } } diff --git a/src/main/resources/languages/es-ES.json b/src/main/resources/languages/es-ES.json index 5b58dfc19..7b45839ef 100644 --- a/src/main/resources/languages/es-ES.json +++ b/src/main/resources/languages/es-ES.json @@ -460,6 +460,10 @@ "enabling_plugin": "Activando plugin: %s", "enabling_failed": "Error al activar el plugin: %s", "disabling_plugin": "Desactivando el plugin: %s", - "disabling_failed": "Error al desactivar el plugin: %s" + "disabling_failed": "Error al desactivar el plugin: %s", + "invalid_api": { + "not_present": "🇺🇸Plugin %s does not specify an API version.", + "lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s." + } } } diff --git a/src/main/resources/languages/fr-FR.json b/src/main/resources/languages/fr-FR.json index 25908ec07..4fc11c732 100644 --- a/src/main/resources/languages/fr-FR.json +++ b/src/main/resources/languages/fr-FR.json @@ -460,6 +460,10 @@ "enabling_plugin": "Activation du plugin %s", "enabling_failed": "Impossible d'activer le plugin %s", "disabling_plugin": "Désactivation du plugin %s", - "disabling_failed": "Impossible de désactiver le plugin %s" + "disabling_failed": "Impossible de désactiver le plugin %s", + "invalid_api": { + "not_present": "🇺🇸Plugin %s does not specify an API version.", + "lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s." + } } } diff --git a/src/main/resources/languages/it-IT.json b/src/main/resources/languages/it-IT.json index 5907cfced..737df9418 100644 --- a/src/main/resources/languages/it-IT.json +++ b/src/main/resources/languages/it-IT.json @@ -460,6 +460,10 @@ "enabling_plugin": "Abilitazione plug-in: %s", "enabling_failed": "Impossibile abilitare il plug-in: %s", "disabling_plugin": "Disabilitazione plug-in: %s", - "disabling_failed": "Impossibile disabilitare il plug-in: %s" + "disabling_failed": "Impossibile disabilitare il plug-in: %s", + "invalid_api": { + "not_present": "🇺🇸Plugin %s does not specify an API version.", + "lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s." + } } } diff --git a/src/main/resources/languages/ja-JP.json b/src/main/resources/languages/ja-JP.json index d73a5ab35..01f35e248 100644 --- a/src/main/resources/languages/ja-JP.json +++ b/src/main/resources/languages/ja-JP.json @@ -460,6 +460,10 @@ "enabling_plugin": "プラグインを有効にしています: %s", "enabling_failed": "プラグインの有効化に失敗しました: %s", "disabling_plugin": "プラグインを無効にしています: %s", - "disabling_failed": "プラグインの無効化に失敗しました: %s" + "disabling_failed": "プラグインの無効化に失敗しました: %s", + "invalid_api": { + "not_present": "🇺🇸Plugin %s does not specify an API version.", + "lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s." + } } } diff --git a/src/main/resources/languages/ko-KR.json b/src/main/resources/languages/ko-KR.json index 4cc31aaa3..cd188ce0c 100644 --- a/src/main/resources/languages/ko-KR.json +++ b/src/main/resources/languages/ko-KR.json @@ -460,6 +460,10 @@ "enabling_plugin": "플러그인을 활성화했습니다: %s", "enabling_failed": "플러그인을 활성화하는데 실패했습니다: %s", "disabling_plugin": "플러그인을 비활성화했습니다: %s", - "disabling_failed": "플러그인을 비활성화하는데 실패했습니다: %s" + "disabling_failed": "플러그인을 비활성화하는데 실패했습니다: %s", + "invalid_api": { + "not_present": "🇺🇸Plugin %s does not specify an API version.", + "lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s." + } } } diff --git a/src/main/resources/languages/pl-PL.json b/src/main/resources/languages/pl-PL.json index 6171e56d9..9f56296d1 100644 --- a/src/main/resources/languages/pl-PL.json +++ b/src/main/resources/languages/pl-PL.json @@ -460,6 +460,10 @@ "enabling_plugin": "Włączanie pluginu: %s", "enabling_failed": "Nie udało się załączyć pluginu: %s", "disabling_plugin": "Wyłączanie pluginu: %s", - "disabling_failed": "Nie udało się wyłączyć pluginu: %s" + "disabling_failed": "Nie udało się wyłączyć pluginu: %s", + "invalid_api": { + "not_present": "🇺🇸Plugin %s does not specify an API version.", + "lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s." + } } } diff --git a/src/main/resources/languages/ro-RO.json b/src/main/resources/languages/ro-RO.json index da2a665c4..3c5fb5222 100644 --- a/src/main/resources/languages/ro-RO.json +++ b/src/main/resources/languages/ro-RO.json @@ -460,6 +460,10 @@ "enabling_plugin": "🇺🇸Enabling plugin: %s", "enabling_failed": "🇺🇸Failed to enable plugin: %s", "disabling_plugin": "🇺🇸Disabling plugin: %s", - "disabling_failed": "🇺🇸Failed to disable plugin: %s" + "disabling_failed": "🇺🇸Failed to disable plugin: %s", + "invalid_api": { + "not_present": "🇺🇸Plugin %s does not specify an API version.", + "lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s." + } } } diff --git a/src/main/resources/languages/ru-RU.json b/src/main/resources/languages/ru-RU.json index 7945eef18..12d4c2047 100644 --- a/src/main/resources/languages/ru-RU.json +++ b/src/main/resources/languages/ru-RU.json @@ -460,6 +460,10 @@ "enabling_plugin": "Включаем Плагин: %s", "enabling_failed": "Ошибка включения Плагина: %s", "disabling_plugin": "Отключаем Плагин: %s", - "disabling_failed": "Ошибка отключения Плагина: %s" + "disabling_failed": "Ошибка отключения Плагина: %s", + "invalid_api": { + "not_present": "🇺🇸Plugin %s does not specify an API version.", + "lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s." + } } } diff --git a/src/main/resources/languages/zh-CN.json b/src/main/resources/languages/zh-CN.json index 5e9e22a03..16661b658 100644 --- a/src/main/resources/languages/zh-CN.json +++ b/src/main/resources/languages/zh-CN.json @@ -460,6 +460,10 @@ "enabling_plugin": "启用插件:%s", "enabling_failed": "无法启用插件:%s", "disabling_plugin": "正在禁用插件:%s", - "disabling_failed": "无法禁用插件:%s" + "disabling_failed": "无法禁用插件:%s", + "invalid_api": { + "not_present": "🇺🇸Plugin %s does not specify an API version.", + "lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s." + } } } diff --git a/src/main/resources/languages/zh-TW.json b/src/main/resources/languages/zh-TW.json index 36c379070..c215845c3 100644 --- a/src/main/resources/languages/zh-TW.json +++ b/src/main/resources/languages/zh-TW.json @@ -460,6 +460,10 @@ "enabling_plugin": "🇺🇸Enabling plugin: %s", "enabling_failed": "🇺🇸Failed to enable plugin: %s", "disabling_plugin": "🇺🇸Disabling plugin: %s", - "disabling_failed": "🇺🇸Failed to disable plugin: %s" + "disabling_failed": "🇺🇸Failed to disable plugin: %s", + "invalid_api": { + "not_present": "🇺🇸Plugin %s does not specify an API version.", + "lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s." + } } }